class CouchFSDocument(fuse.Fuse): def __init__(self, mountpoint, uri=None, *args, **kwargs): fuse.Fuse.__init__(self, *args, **kwargs) db_uri, doc_id = uri.rsplit('/', 1) self.doc_id = unquote(doc_id) self.db = Database(db_uri) def get_dirs(self): dirs = {} attachments = self.db[self.doc_id].get('_attachments', {}).keys() for att in attachments: parents = [u''] for name in att.split('/'): filenames = dirs.setdefault(u'/'.join(parents[1:]), set()) if name != COUCHFS_DIRECTORY_PLACEHOLDER: filenames.add(name) parents.append(name) return dirs def readdir(self, path, offset): path = _normalize_path(path) for r in '.', '..': yield fuse.Direntry(r) for name in self.get_dirs().get(path, []): yield fuse.Direntry(name.encode('utf-8')) def getattr(self, path): path = _normalize_path(path) try: st = CouchStat() if path == '' or path in self.get_dirs().keys(): st.st_mode = stat.S_IFDIR | 0775 st.st_nlink = 2 else: att = self.db[self.doc_id].get('_attachments', {}) data = att[path] st.st_mode = stat.S_IFREG | 0664 st.st_nlink = 1 st.st_size = data['length'] return st except (KeyError, ResourceNotFound): return -errno.ENOENT def open(self, path, flags): path = _normalize_path(path) try: #data = self.db.get_attachment(self.db[self.doc_id], path.split('/')[-1]) #att = self.db[self.doc_id].get('_attachments', {}) #data = att[path.split('/')[-1]] parts = path.rsplit(u'/', 1) if len(parts) == 1: dirname, filename = u'', parts[0] else: dirname, filename = parts if filename in self.get_dirs()[dirname]: return 0 return -errno.ENOENT except (KeyError, ResourceNotFound): return -errno.ENOENT #accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR #if (flags & accmode) != os.O_RDONLY: # return -errno.EACCES def read(self, path, size, offset): path = _normalize_path(path) try: data = self.db.get_attachment(self.db[self.doc_id], path) if isinstance(data, cStringIO.InputType): data = data.getvalue() elif data is None: data = "" slen = len(data) if offset < slen: if offset + size > slen: size = slen - offset buf = data[offset:offset+size] else: buf = '' return buf except (KeyError, ResourceNotFound): pass return -errno.ENOENT def write(self, path, buf, offset): path = _normalize_path(path) try: data = self.db.get_attachment(self.db[self.doc_id], path) if data is None: data = "" elif isinstance(data, cStringIO.InputType): data = data.getvalue() data = data[0:offset] + buf + data[offset+len(buf):] self.db.put_attachment(self.db[self.doc_id], data, filename=path) return len(buf) except (KeyError, ResourceNotFound): pass return -errno.ENOENT def mknod(self, path, mode, dev): path = _normalize_path(path) self.db.put_attachment(self.db[self.doc_id], u'', filename=path) def unlink(self, path): path = _normalize_path(path) parts = path.rsplit(u'/', 1) if len(parts) == 1: dirname, filename = u'', parts[0] else: dirname, filename = parts self.db.delete_attachment(self.db[self.doc_id], path) if filename != COUCHFS_DIRECTORY_PLACEHOLDER and len(self.get_dirs().get(dirname, [])) == 0: print "putting to:", u'%s/%s' % (dirname, COUCHFS_DIRECTORY_PLACEHOLDER) self.db.put_attachment(self.db[self.doc_id], u'', filename=u'%s/%s' % (dirname, COUCHFS_DIRECTORY_PLACEHOLDER)) def truncate(self, path, size): path = _normalize_path(path) self.db.put_attachment(self.db[self.doc_id], u'', filename=path) return 0 def utime(self, path, times): return 0 def mkdir(self, path, mode): path = _normalize_path(path) self.db.put_attachment(self.db[self.doc_id], u'', filename=u'%s/%s' % (path, COUCHFS_DIRECTORY_PLACEHOLDER)) return 0 def rmdir(self, path): path = _normalize_path(path) self.db.delete_attachment(self.db[self.doc_id], u'%s/%s' % (path, COUCHFS_DIRECTORY_PLACEHOLDER)) return 0 def rename(self, pathfrom, pathto): pathfrom, pathto = _normalize_path(pathfrom), _normalize_path(pathto) data = self.db.get_attachment(self.db[self.doc_id], pathfrom) if isinstance(data, cStringIO.InputType): data = data.getvalue() elif data is None: data = "" self.db.put_attachment(self.db[self.doc_id], data, filename=pathto) self.db.delete_attachment(self.db[self.doc_id], pathfrom) return 0 def fsync(self, path, isfsyncfile): return 0 def statfs(self): """ Should return a tuple with the following 6 elements: - blocksize - size of file blocks, in bytes - totalblocks - total number of blocks in the filesystem - freeblocks - number of free blocks - availblocks - number of blocks available to non-superuser - totalfiles - total number of file inodes - freefiles - nunber of free file inodes Feel free to set any of the above values to 0, which tells the kernel that the info is not available. """ st = fuse.StatVfs() block_size = 1024 blocks = 1024 * 1024 blocks_free = blocks blocks_avail = blocks_free files = 0 files_free = 0 st.f_bsize = block_size st.f_frsize = block_size st.f_blocks = blocks st.f_bfree = blocks_free st.f_bavail = blocks_avail st.f_files = files st.f_ffree = files_free return st
class CouchFSDocument(fuse.Fuse): def __init__(self, mountpoint, uri=None, *args, **kwargs): fuse.Fuse.__init__(self, *args, **kwargs) db_uri, doc_id = uri.rsplit('/', 1) self.doc_id = unquote(doc_id) self.db = Database(db_uri) def get_dirs(self): dirs = {} attachments = self.db[self.doc_id].get('_attachments', {}).keys() for att in attachments: parents = [u''] for name in att.split('/'): filenames = dirs.setdefault(u'/'.join(parents[1:]), set()) if name != COUCHFS_DIRECTORY_PLACEHOLDER: filenames.add(name) parents.append(name) return dirs def readdir(self, path, offset): path = _normalize_path(path) for r in '.', '..': yield fuse.Direntry(r) for name in self.get_dirs().get(path, []): yield fuse.Direntry(name.encode('utf-8')) def getattr(self, path): path = _normalize_path(path) try: st = CouchStat() if path == '' or path in self.get_dirs().keys(): st.st_mode = stat.S_IFDIR | 0775 st.st_nlink = 2 else: att = self.db[self.doc_id].get('_attachments', {}) data = att[path] st.st_mode = stat.S_IFREG | 0664 st.st_nlink = 1 st.st_size = data['length'] return st except (KeyError, ResourceNotFound): return -errno.ENOENT def open(self, path, flags): path = _normalize_path(path) try: #data = self.db.get_attachment(self.db[self.doc_id], path.split('/')[-1]) #att = self.db[self.doc_id].get('_attachments', {}) #data = att[path.split('/')[-1]] parts = path.rsplit(u'/', 1) if len(parts) == 1: dirname, filename = u'', parts[0] else: dirname, filename = parts if filename in self.get_dirs()[dirname]: return 0 return -errno.ENOENT except (KeyError, ResourceNotFound): return -errno.ENOENT #accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR #if (flags & accmode) != os.O_RDONLY: # return -errno.EACCES def read(self, path, size, offset): path = _normalize_path(path) try: data = self.db.get_attachment(self.db[self.doc_id], path) if isinstance(data, cStringIO.InputType): data = data.getvalue() elif data is None: data = "" slen = len(data) if offset < slen: if offset + size > slen: size = slen - offset buf = data[offset:offset + size] else: buf = '' return buf except (KeyError, ResourceNotFound): pass return -errno.ENOENT def write(self, path, buf, offset): path = _normalize_path(path) try: data = self.db.get_attachment(self.db[self.doc_id], path) if data is None: data = "" elif isinstance(data, cStringIO.InputType): data = data.getvalue() data = data[0:offset] + buf + data[offset + len(buf):] self.db.put_attachment(self.db[self.doc_id], data, filename=path) return len(buf) except (KeyError, ResourceNotFound): pass return -errno.ENOENT def mknod(self, path, mode, dev): path = _normalize_path(path) self.db.put_attachment(self.db[self.doc_id], u'', filename=path) def unlink(self, path): path = _normalize_path(path) parts = path.rsplit(u'/', 1) if len(parts) == 1: dirname, filename = u'', parts[0] else: dirname, filename = parts self.db.delete_attachment(self.db[self.doc_id], path) if filename != COUCHFS_DIRECTORY_PLACEHOLDER and len( self.get_dirs().get(dirname, [])) == 0: print "putting to:", u'%s/%s' % (dirname, COUCHFS_DIRECTORY_PLACEHOLDER) self.db.put_attachment(self.db[self.doc_id], u'', filename=u'%s/%s' % (dirname, COUCHFS_DIRECTORY_PLACEHOLDER)) def truncate(self, path, size): path = _normalize_path(path) self.db.put_attachment(self.db[self.doc_id], u'', filename=path) return 0 def utime(self, path, times): return 0 def mkdir(self, path, mode): path = _normalize_path(path) self.db.put_attachment(self.db[self.doc_id], u'', filename=u'%s/%s' % (path, COUCHFS_DIRECTORY_PLACEHOLDER)) return 0 def rmdir(self, path): path = _normalize_path(path) self.db.delete_attachment( self.db[self.doc_id], u'%s/%s' % (path, COUCHFS_DIRECTORY_PLACEHOLDER)) return 0 def rename(self, pathfrom, pathto): pathfrom, pathto = _normalize_path(pathfrom), _normalize_path(pathto) data = self.db.get_attachment(self.db[self.doc_id], pathfrom) if isinstance(data, cStringIO.InputType): data = data.getvalue() elif data is None: data = "" self.db.put_attachment(self.db[self.doc_id], data, filename=pathto) self.db.delete_attachment(self.db[self.doc_id], pathfrom) return 0 def fsync(self, path, isfsyncfile): return 0 def statfs(self): """ Should return a tuple with the following 6 elements: - blocksize - size of file blocks, in bytes - totalblocks - total number of blocks in the filesystem - freeblocks - number of free blocks - availblocks - number of blocks available to non-superuser - totalfiles - total number of file inodes - freefiles - nunber of free file inodes Feel free to set any of the above values to 0, which tells the kernel that the info is not available. """ st = fuse.StatVfs() block_size = 1024 blocks = 1024 * 1024 blocks_free = blocks blocks_avail = blocks_free files = 0 files_free = 0 st.f_bsize = block_size st.f_frsize = block_size st.f_blocks = blocks st.f_bfree = blocks_free st.f_bavail = blocks_avail st.f_files = files st.f_ffree = files_free return st