Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Silbe <sascha-pgp@silbe.org>2010-06-03 13:40:04 (GMT)
committer Sascha Silbe <sascha-pgp@silbe.org>2010-06-03 13:40:04 (GMT)
commitada4ccb107ca6cee061402624afb5fb9fc15e803 (patch)
treea81959d5bba6c1f4f5e4ebadbdcf3322ee209fcb
parentc1959a1c2040f640ff86518a4ad992a0f2fafcff (diff)
Put files in / and symlinks in /by-id, rather than the other way round.
FUSE doesn't like files getting replaced by symlinks upon creation, so we need to put the "files" in the place where the user can create new entries.
-rwxr-xr-xdatastore-fuse.py229
1 files changed, 126 insertions, 103 deletions
diff --git a/datastore-fuse.py b/datastore-fuse.py
index 4638f5f..1b45664 100755
--- a/datastore-fuse.py
+++ b/datastore-fuse.py
@@ -32,14 +32,14 @@ DS_DBUS_PATH = "/org/laptop/sugar/DataStore"
class DataStoreObjectStat(fuse.Stat):
# pylint: disable-msg=R0902,R0903
- def __init__(self, parent, metadata, size):
+ def __init__(self, filesystem, metadata, size):
fuse.Stat.__init__(self, st_mode=stat.S_IFREG | 0750, st_nlink=1,
st_uid=os.getuid(), st_gid=os.getgid(), st_size=size,
st_mtime=self._parse_time(metadata.get('timestamp', '')))
self.st_ctime = self.st_mtime
self.st_atime = self.st_mtime
self.metadata = metadata
- self.parent = parent
+ self._filesystem = filesystem
self.object_id = metadata['uid']
def _parse_time(self, timestamp):
@@ -49,10 +49,10 @@ class DataStoreObjectStat(fuse.Stat):
return 0
def should_truncate(self):
- return self.parent.should_truncate(self.object_id)
+ return self._filesystem.should_truncate(self.object_id)
def reset_truncate(self):
- return self.parent.reset_truncate(self.object_id)
+ return self._filesystem.reset_truncate(self.object_id)
class Symlink(fuse.Stat):
@@ -105,16 +105,11 @@ class Directory(fuse.Stat):
def remove(self, name_):
raise IOError(errno.EACCES, os.strerror(errno.EACCES))
- def truncate(self, name_):
- raise IOError(errno.EACCES, os.strerror(errno.EACCES))
-
class ByTitleDirectory(Directory):
def __init__(self, filesystem, root):
self.root = root
Directory.__init__(self, filesystem, 0750)
- self._object_id_to_title_name = {}
- self._title_name_to_object_id = {}
def readdir(self, offset):
Directory.readdir(self, offset)
@@ -122,83 +117,42 @@ class ByTitleDirectory(Directory):
for entry in self._filesystem.find({},
{'metadata': ['title', 'uid', 'timestamp']}):
- name = self._object_id_to_title_name.get(entry['uid'])
- if not name:
- name = self._generate_title_name(entry)
- self._add_title_name(name, entry)
-
+ name = self._filesystem.lookup_title_name(entry['uid'])
yield fuse.Direntry(name)
+ def getxattr(self, name, attribute):
+ object_id = self._filesystem.resolve_title_name(name)
+ metadata = self._filesystem.get_metadata(object_id)
+ if attribute in metadata:
+ return metadata[attribute]
+
+ Directory.getxattr(self, object_id, attribute)
+
+ def listxattr(self, name):
+ object_id = self._filesystem.resolve_title_name(name)
+ metadata = self._filesystem.get_metadata(object_id)
+ return [str(name) for name in metadata.keys()]
+
def lookup(self, name):
- object_id = self._resolve_title_name(name)
- return Symlink(self._filesystem, 'by-id/' + str(object_id))
+ object_id = self._filesystem.resolve_title_name(name)
+ metadata = self._filesystem.get_metadata(object_id)
+ size = self._filesystem.get_data_size(object_id)
+ return DataStoreObjectStat(self._filesystem, metadata, size)
def mknod(self, name):
- if name in self._title_name_to_object_id:
+ if self._filesystem.try_resolve_title_name(name):
raise IOError(errno.EEXIST, os.strerror(errno.EEXIST))
- metadata = {'title': name}
- object_id = self._filesystem.create_new(metadata, '')
- metadata['uid'] = object_id
- self._add_title_name(name, metadata)
+ object_id = self._filesystem.create_new(name, '')
def remove(self, name):
- if name in ['.', '..']:
- raise IOError(errno.EACCES, os.strerror(errno.EACCES))
-
- object_id = self._resolve_title_name(name)
- self.root.by_id_directory.remove(object_id)
- self._remove_title_name_by_object_id(object_id)
-
- def _resolve_title_name(self, name):
- try:
- return self._title_name_to_object_id[name]
-
- except KeyError:
- raise IOError(errno.ENOENT, os.strerror(errno.ENOENT))
-
- def _add_title_name(self, name, metadata):
- self._object_id_to_title_name[metadata['uid']] = name
- self._title_name_to_object_id[name] = metadata['uid']
- return name
-
- @trace()
- def _generate_title_name(self, metadata):
- title = metadata.get('title')
- try:
- mtime = float(metadata['timestamp'])
- except (KeyError, ValueError):
- mtime = time.time()
-
- time_human = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(mtime))
- name = '%s - %s' % (title, time_human)
- name = self._safe_name(name)
- current_name = name
- counter = 1
- while current_name in self._title_name_to_object_id:
- counter += 1
- current_name = '%s %d' % (name, counter)
-
- return current_name
-
- def _remove_title_name_by_object_id(self, object_id):
- name = self._object_id_to_title_name.pop(object_id, None)
- if name:
- del self._title_name_to_object_id[name]
-
- def _remove_title_name_by_name(self, name):
- object_id = self._title_name_to_object_id.pop(name, None)
- if object_id:
- del self._object_id_to_title_name[object_id]
-
- def _safe_name(self, name):
- return name.replace('/', '_')
+ object_id = self._filesystem.resolve_title_name(name)
+ self._filesystem.remove_entry(object_id)
class ByIdDirectory(Directory):
def __init__(self, filesystem):
Directory.__init__(self, filesystem, 0550)
- self._truncate_object_ids = set()
def getxattr(self, object_id, attribute):
metadata = self._filesystem.get_metadata(object_id)
@@ -212,9 +166,8 @@ class ByIdDirectory(Directory):
return [str(name) for name in metadata.keys()]
def lookup(self, object_id):
- metadata = self._filesystem.get_metadata(object_id)
- size = self._get_size(object_id)
- return DataStoreObjectStat(self, metadata, size)
+ name = self._filesystem.lookup_title_name(object_id)
+ return Symlink(self._filesystem, '../' + name)
def readdir(self, offset):
Directory.readdir(self, offset)
@@ -225,25 +178,6 @@ class ByIdDirectory(Directory):
def remove(self, object_id):
self._filesystem.remove_entry(object_id)
- def truncate(self, object_id):
- self._truncate_object_ids.add(object_id)
-
- def should_truncate(self, object_id):
- return object_id in self._truncate_object_ids
-
- def reset_truncate(self, object_id):
- self._truncate_object_ids.discard(object_id)
-
- def _get_size(self, object_id):
- file_name = self._filesystem.get_data(object_id)
- if not file_name:
- return 0
-
- try:
- return os.stat(file_name).st_size
- finally:
- os.remove(file_name)
-
# TODO
class ByTagsDirectory(Directory):
@@ -390,8 +324,8 @@ class DataStoreFile(object):
@trace()
def fgetattr(self):
-# return os.fstat(self._file.fileno())
- return self._filesystem.getattr(self._path)
+ return os.fstat(self._file.fileno())
+# return self._filesystem.getattr(self._path)
@trace()
def ftruncate(self, length):
@@ -411,6 +345,9 @@ class DataStoreFS(fuse.Fuse):
DataStoreFile.__init__(self_file, self_fs, *args, **kwargs)
self_fs.file_class = WrappedDataStoreFile
+ self_fs._truncate_object_ids = set()
+ self_fs._object_id_to_title_name = {}
+ self_fs._title_name_to_object_id = {}
fuse.Fuse.__init__(self_fs, *args, **kw)
@@ -435,20 +372,24 @@ class DataStoreFS(fuse.Fuse):
directory = self.getattr(directory_name)
return getattr(directory, action)(file_name, *args)
- def readdir(self, path, offset):
+ def readdir(self, path, offset=None):
return self.getattr(path).readdir(offset)
def readlink(self, path):
return self._delegate(path, 'readlink')
- def mknod(self, path, mode_, dev_):
+ def mknod(self, path, mode_=None, dev_=None):
# called by FUSE for open(O_CREAT) before instantiating the file
return self._delegate(path, 'mknod')
def truncate(self, path, mode_=None, dev_=None):
# Documented to be called by FUSE when opening files with O_TRUNC,
# unless -o o_trunc_atomic is passed as a CLI option
- self._delegate(path, 'truncate')
+ entry = self.getattr(path)
+ if isinstance(entry, Directory):
+ raise IOError(errno.EISDIR, os.strerror(errno.EISDIR))
+
+ self._truncate_object_ids.add(entry.object_id)
def unlink(self, path):
self._delegate(path, 'remove')
@@ -507,6 +448,12 @@ class DataStoreFS(fuse.Fuse):
return attribute_names
+ def should_truncate(self, object_id):
+ return object_id in self._truncate_object_ids
+
+ def reset_truncate(self, object_id):
+ self._truncate_object_ids.discard(object_id)
+
def find(self, metadata, options):
mess = metadata.copy()
mess.update(options)
@@ -520,23 +467,99 @@ class DataStoreFS(fuse.Fuse):
return self._data_store.get_properties(object_id, timeout=-1,
byte_arrays=True)
- def create_new(self, metadata, path):
- return self._data_store.create(metadata, path, False, timeout=-1,
+ def create_new(self, name, path):
+ metadata = {'title': name}
+ object_id = self._data_store.create(metadata, path, False, timeout=-1,
byte_arrays=True)
+ self._add_title_name(name, object_id)
- def remove_data(self, object_id):
- return self._data_store.delete(object_id)
+ def remove_entry(self, object_id):
+ self._data_store.delete(object_id)
+ self._remove_title_name_by_object_id(object_id)
+ self._truncate_object_ids.discard(object_id)
def get_data(self, object_id):
return self._data_store.get_filename(object_id, timeout=-1,
byte_arrays=True)
+ def get_data_size(self, object_id):
+ file_name = self.get_data(object_id)
+ if not file_name:
+ return 0
+
+ try:
+ return os.stat(file_name).st_size
+ finally:
+ os.remove(file_name)
+
@trace()
def write_data(self, object_id, file_name):
metadata = self.get_metadata(object_id)
return self._data_store.update(object_id, metadata, file_name, False,
timeout=-1, byte_arrays=True)
+ def resolve_title_name(self, name):
+ if name not in self._title_name_to_object_id:
+ # FIXME: Hack to fill self._title_name_to_object_id. To be
+ # replaced by parsing the name and doing a specific search.
+ list(self.readdir('/'))
+
+ try:
+ return self._title_name_to_object_id[name]
+
+ except KeyError:
+ raise IOError(errno.ENOENT, os.strerror(errno.ENOENT))
+
+ def try_resolve_title_name(self, name):
+ return self._title_name_to_object_id.get(name)
+
+ def lookup_title_name(self, object_id):
+ name = self._object_id_to_title_name.get(object_id)
+ if name:
+ return name
+
+ metadata = self.get_metadata(object_id)
+ name = self._generate_title_name(metadata)
+ self._add_title_name(name, object_id)
+ return name
+
+ def _add_title_name(self, name, object_id):
+ self._object_id_to_title_name[object_id] = name
+ self._title_name_to_object_id[name] = object_id
+ return name
+
+ @trace()
+ def _generate_title_name(self, metadata):
+ title = metadata.get('title')
+ try:
+ mtime = float(metadata['timestamp'])
+ except (KeyError, ValueError):
+ mtime = time.time()
+
+ time_human = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(mtime))
+ name = '%s - %s' % (title, time_human)
+ name = self._safe_name(name)
+ current_name = name
+ counter = 1
+ while current_name in self._title_name_to_object_id:
+ counter += 1
+ current_name = '%s %d' % (name, counter)
+
+ return current_name
+
+ def _remove_title_name_by_object_id(self, object_id):
+ name = self._object_id_to_title_name.pop(object_id, None)
+ if name:
+ del self._title_name_to_object_id[name]
+
+ def _remove_title_name_by_name(self, name):
+ object_id = self._title_name_to_object_id.pop(name, None)
+ if object_id:
+ del self._object_id_to_title_name[object_id]
+
+ def _safe_name(self, name):
+ return name.replace('/', '_')
+
def main():
usage = __doc__ + fuse.Fuse.fusage