def test_deletion(): meta1 = "apple" meta2 = "microsoft" meta3 = "linux" T = BlobTree(strdict()) T.create_subtree("/sub", meta1) T.create_subtree("/sub/sub", meta2) T.create_data("/sub/data", meta3) T.set_data("/sub/data", "some data") assert "sub" in T.list_dir("/") T.unlink("/sub") assert not "sub" in T.list_dir("/")
class KVFS: """A Key-Value-File-System This class is initialized with a key value store and implements a file system on top of it, providing methods like mkdir, create, read, write, ... In a failure case IOError gets raised. Some features like permissions or hardlinks are not yet supported.""" def __init__(self, kv_store): self._bt = BlobTree(kv_store) m = _MetaData() m['st_mode'] = m['st_mode'] | stat.S_IFDIR self.root_meta = m def getattr(self, path): """returns the attributes of the object at `path`.""" if path == "/": return self.root_meta try: return _MetaData(self._bt.get_meta_data(path)) except KeyError: _raise_io(errno.ENOENT, path) def setattr(self, path, attr): """sets the attributes of the object at `path`.""" if path == "/": _raise_io(errno.EPERM, path) try: self._bt.set_meta_data(path, attr) except (KeyError, IndexError): _raise_io(errno.ENOENT, path) def create(self, path): """create a file""" if self._bt.exists(path): _raise_io(errno.EEXIST, path) m = _MetaData() m['st_mode'] = m['st_mode'] | stat.S_IFREG m['st_size'] = 0 self._bt.create_data(path, str(m)) def mkdir(self, path): """creates a directory""" if self._bt.exists(path): _raise_io(errno.EEXIST, path) m = _MetaData() m['st_mode'] = m['st_mode'] | stat.S_IFDIR self._bt.create_subtree(path, str(m)) def readdir(self, path): """read contents of a directory""" try: files = self._bt.list_dir(path) except KeyError: _raise_io(errno.ENOENT, path) yield '.' yield '..' for f in files: yield f def readlink(self, path): """resolves a symbolic link""" # TODO recursive? meta = self.getattr(path) try: return meta['symlink'] except KeyError: _raise_io(errno.ENOLINK, path) def symlink(self, target, name): """create a symbolic link target->name""" if self._bt.exists(target): _raise_io(errno.EEXIST, target) m = _MetaData() # use attributes to save target and link property m['symlink'] = name m['st_mode'] = m['st_mode'] | stat.S_IFLNK self._bt.create_data(target, str(m)) def remove(self, path): """removes a file or directory""" try: self._bt.unlink(path) except KeyError: _raise_io(errno.ENOENT, path) def rename(self, old, new): """rename a file (note that directories may change)""" try: self._bt.rename(old, new) except KeyError: _raise_io(errno.ENOENT, old) def link(self, target, name): """create a hardlink""" self._bt.create_data(target, self.getattr(name)) self._bt.set_data(target, self._bt.get_data(name)) # FIXME this is a copy, not a hardlink! # Subsequent changes won't be applied. # A transparent link blob type would be needed, # but that should rather be called a symlink. def _get_data(self, path): """get data from path or raise IOERROR""" try: return self._bt.get_data(path) except KeyError: _raise_io(errno.ENOENT, path) except TypeError: _raise_io(errno.EISDIR, path) def read(self, path, length=2000000000, offset=0): """read data from a file""" data = self._get_data(path) return data[offset:offset + length] def write(self, path, buf, offset=0): """write data to a file""" meta = self.getattr(path) if offset == 0 and len(buf) >= meta['st_size']: data = buf else: data = self._get_data(path) data = data[:offset] + buf + data[offset + len(buf):] meta['st_mtime'] = time.time() meta['st_size'] = len(data) self._bt.set_data(path, data, str(meta)) def flush(self, path="/"): """clear all buffers, finish all pending operations""" self._bt.flush() def truncate(self, path, length): """truncate file to given length""" data = self._get_data(path) self._bt.set_data(path, data[:length])
class KVFS: """A Key-Value-File-System This class is initialized with a key value store and implements a file system on top of it, providing methods like mkdir, create, read, write, ... In a failure case IOError gets raised. Some features like permissions or hardlinks are not yet supported.""" def __init__(self, kv_store): self._bt = BlobTree(kv_store) m = _MetaData() m['st_mode'] = m['st_mode'] | stat.S_IFDIR self.root_meta = m def getattr(self, path): """returns the attributes of the object at `path`.""" if path == "/": return self.root_meta try: return _MetaData(self._bt.get_meta_data(path)) except KeyError: _raise_io(errno.ENOENT, path) def setattr(self, path, attr): """sets the attributes of the object at `path`.""" if path == "/": _raise_io(errno.EPERM, path) try: self._bt.set_meta_data(path, attr) except (KeyError, IndexError): _raise_io(errno.ENOENT, path) def create(self, path): """create a file""" if self._bt.exists(path): _raise_io(errno.EEXIST, path) m = _MetaData() m['st_mode'] = m['st_mode'] | stat.S_IFREG m['st_size'] = 0 self._bt.create_data(path, str(m)) def mkdir(self, path): """creates a directory""" if self._bt.exists(path): _raise_io(errno.EEXIST, path) m = _MetaData() m['st_mode'] = m['st_mode'] | stat.S_IFDIR self._bt.create_subtree(path, str(m)) def readdir(self, path): """read contents of a directory""" try: files = self._bt.list_dir(path) except KeyError: _raise_io(errno.ENOENT, path) yield '.' yield '..' for f in files: yield f def readlink(self, path): """resolves a symbolic link""" # TODO recursive? meta = self.getattr(path) try: return meta['symlink'] except KeyError: _raise_io(errno.ENOLINK, path) def symlink(self, target, name): """create a symbolic link target->name""" if self._bt.exists(target): _raise_io(errno.EEXIST, target) m = _MetaData() # use attributes to save target and link property m['symlink'] = name m['st_mode'] = m['st_mode'] | stat.S_IFLNK self._bt.create_data(target, str(m)) def remove(self, path): """removes a file or directory""" try: self._bt.unlink(path) except KeyError: _raise_io(errno.ENOENT, path) def rename(self, old, new): """rename a file (note that directories may change)""" try: self._bt.rename(old, new) except KeyError: _raise_io(errno.ENOENT, old) def link(self, target, name): """create a hardlink""" self._bt.create_data(target, self.getattr(name)) self._bt.set_data(target, self._bt.get_data(name)) # FIXME this is a copy, not a hardlink! # Subsequent changes won't be applied. # A transparent link blob type would be needed, # but that should rather be called a symlink. def _get_data(self, path): """get data from path or raise IOERROR""" try: return self._bt.get_data(path) except KeyError: _raise_io(errno.ENOENT, path) except TypeError: _raise_io(errno.EISDIR, path) def read(self, path, length=2000000000, offset=0): """read data from a file""" data = self._get_data(path) return data[offset:offset+length] def write(self, path, buf, offset=0): """write data to a file""" meta = self.getattr(path) if offset==0 and len(buf) >= meta['st_size']: data = buf else: data = self._get_data(path) data = data[:offset] + buf + data[offset+len(buf):] meta['st_mtime'] = time.time() meta['st_size'] = len(data) self._bt.set_data(path, data, str(meta)) def flush(self, path="/"): """clear all buffers, finish all pending operations""" self._bt.flush() def truncate(self, path, length): """truncate file to given length""" data = self._get_data(path) self._bt.set_data(path, data[:length])