diff options
Diffstat (limited to 'fs/contrib/tahoelafs/__init__.py')
-rw-r--r-- | fs/contrib/tahoelafs/__init__.py | 830 |
1 files changed, 415 insertions, 415 deletions
diff --git a/fs/contrib/tahoelafs/__init__.py b/fs/contrib/tahoelafs/__init__.py index c01dd5e..91858bd 100644 --- a/fs/contrib/tahoelafs/__init__.py +++ b/fs/contrib/tahoelafs/__init__.py @@ -1,415 +1,415 @@ -'''
-fs.contrib.tahoelafs
-====================
-
-This modules provides a PyFilesystem interface to the Tahoe Least Authority
-File System. Tahoe-LAFS is a distributed, encrypted, fault-tolerant storage
-system:
-
- http://tahoe-lafs.org/
-
-You will need access to a Tahoe-LAFS "web api" service.
-
-Example (it will use publicly available (but slow) Tahoe-LAFS cloud)::
-
- from fs.contrib.tahoelafs import TahoeLAFS, Connection
- dircap = TahoeLAFS.createdircap(webapi='http://insecure.tahoe-lafs.org')
- print "Your dircap (unique key to your storage directory) is", dircap
- print "Keep it safe!"
- fs = TahoeLAFS(dircap, autorun=False, webapi='http://insecure.tahoe-lafs.org')
- f = fs.open("foo.txt", "a")
- f.write('bar!')
- f.close()
- print "Now visit %s and enjoy :-)" % fs.getpathurl('foo.txt')
-
-When any problem occurred, you can turn on internal debugging messages::
-
- import logging
- l = logging.getLogger()
- l.setLevel(logging.DEBUG)
- l.addHandler(logging.StreamHandler(sys.stdout))
-
- ... your Python code using TahoeLAFS ...
-
-TODO:
-
- * unicode support
- * try network errors / bad happiness
- * exceptions
- * tests
- * sanitize all path types (., /)
- * support for extra large file uploads (poster module)
- * Possibility to block write until upload done (Tahoe mailing list)
- * Report something sane when Tahoe crashed/unavailable
- * solve failed unit tests (makedir_winner, ...)
- * file times
- * docs & author
- * python3 support
- * remove creating blank files (depends on FileUploadManager)
-
-TODO (Not TahoeLAFS specific tasks):
- * RemoteFileBuffer on the fly buffering support
- * RemoteFileBuffer unit tests
- * RemoteFileBuffer submit to trunk
- * Implement FileUploadManager + faking isfile/exists of just processing file
- * pyfilesystem docs is outdated (rename, movedir, ...)
-
-'''
-
-
-import stat as statinfo
-
-import logging
-from logging import DEBUG, INFO, ERROR, CRITICAL
-
-import fs
-import fs.errors as errors
-from fs.path import abspath, relpath, normpath, dirname, pathjoin
-from fs.base import FS, NullFile
-from fs import _thread_synchronize_default, SEEK_END
-from fs.remote import CacheFSMixin, RemoteFileBuffer
-from fs.base import fnmatch, NoDefaultMeta
-
-from util import TahoeUtil
-from connection import Connection
-
-from six import b
-
-logger = fs.getLogger('fs.tahoelafs')
-
-def _fix_path(func):
- """Method decorator for automatically normalising paths."""
- def wrapper(self, *args, **kwds):
- if len(args):
- args = list(args)
- args[0] = _fixpath(args[0])
- return func(self, *args, **kwds)
- return wrapper
-
-
-def _fixpath(path):
- """Normalize the given path."""
- return abspath(normpath(path))
-
-
-
-class _TahoeLAFS(FS):
- """FS providing raw access to a Tahoe-LAFS Filesystem.
-
- This class implements all the details of interacting with a Tahoe-backed
- filesystem, but you probably don't want to use it in practice. Use the
- TahoeLAFS class instead, which has some internal caching to improve
- performance.
- """
-
- _meta = { 'virtual' : False,
- 'read_only' : False,
- 'unicode_paths' : True,
- 'case_insensitive_paths' : False,
- 'network' : True
- }
-
-
- def __init__(self, dircap, largefilesize=10*1024*1024, webapi='http://127.0.0.1:3456'):
- '''Creates instance of TahoeLAFS.
-
- :param dircap: special hash allowing user to work with TahoeLAFS directory.
- :param largefilesize: - Create placeholder file for files larger than this treshold.
- Uploading and processing of large files can last extremely long (many hours),
- so placing this placeholder can help you to remember that upload is processing.
- Setting this to None will skip creating placeholder files for any uploads.
- '''
- self.dircap = dircap if not dircap.endswith('/') else dircap[:-1]
- self.largefilesize = largefilesize
- self.connection = Connection(webapi)
- self.tahoeutil = TahoeUtil(webapi)
- super(_TahoeLAFS, self).__init__(thread_synchronize=_thread_synchronize_default)
-
- def __str__(self):
- return "<TahoeLAFS: %s>" % self.dircap
-
- @classmethod
- def createdircap(cls, webapi='http://127.0.0.1:3456'):
- return TahoeUtil(webapi).createdircap()
-
- def getmeta(self,meta_name,default=NoDefaultMeta):
- if meta_name == "read_only":
- return self.dircap.startswith('URI:DIR2-RO')
- return super(_TahoeLAFS,self).getmeta(meta_name,default)
-
- @_fix_path
- def open(self, path, mode='r', **kwargs):
- self._log(INFO, 'Opening file %s in mode %s' % (path, mode))
- newfile = False
- if not self.exists(path):
- if 'w' in mode or 'a' in mode:
- newfile = True
- else:
- self._log(DEBUG, "File %s not found while opening for reads" % path)
- raise errors.ResourceNotFoundError(path)
- elif self.isdir(path):
- self._log(DEBUG, "Path %s is directory, not a file" % path)
- raise errors.ResourceInvalidError(path)
- elif 'w' in mode:
- newfile = True
-
- if newfile:
- self._log(DEBUG, 'Creating empty file %s' % path)
- if self.getmeta("read_only"):
- raise errors.UnsupportedError('read only filesystem')
- self.setcontents(path, b(''))
- handler = NullFile()
- else:
- self._log(DEBUG, 'Opening existing file %s for reading' % path)
- handler = self.getrange(path,0)
-
- return RemoteFileBuffer(self, path, mode, handler,
- write_on_flush=False)
-
- @_fix_path
- def desc(self, path):
- try:
- return self.getinfo(path)
- except:
- return ''
-
- @_fix_path
- def exists(self, path):
- try:
- self.getinfo(path)
- self._log(DEBUG, "Path %s exists" % path)
- return True
- except errors.ResourceNotFoundError:
- self._log(DEBUG, "Path %s does not exists" % path)
- return False
- except errors.ResourceInvalidError:
- self._log(DEBUG, "Path %s does not exists, probably misspelled URI" % path)
- return False
-
- @_fix_path
- def getsize(self, path):
- try:
- size = self.getinfo(path)['size']
- self._log(DEBUG, "Size of %s is %d" % (path, size))
- return size
- except errors.ResourceNotFoundError:
- return 0
-
- @_fix_path
- def isfile(self, path):
- try:
- isfile = (self.getinfo(path)['type'] == 'filenode')
- except errors.ResourceNotFoundError:
- #isfile = not path.endswith('/')
- isfile = False
- self._log(DEBUG, "Path %s is file: %d" % (path, isfile))
- return isfile
-
- @_fix_path
- def isdir(self, path):
- try:
- isdir = (self.getinfo(path)['type'] == 'dirnode')
- except errors.ResourceNotFoundError:
- isdir = False
- self._log(DEBUG, "Path %s is directory: %d" % (path, isdir))
- return isdir
-
-
- def listdir(self, *args, **kwargs):
- return [ item[0] for item in self.listdirinfo(*args, **kwargs) ]
-
- def listdirinfo(self, *args, **kwds):
- return list(self.ilistdirinfo(*args,**kwds))
-
- def ilistdir(self, *args, **kwds):
- for item in self.ilistdirinfo(*args,**kwds):
- yield item[0]
-
- @_fix_path
- def ilistdirinfo(self, path="/", wildcard=None, full=False, absolute=False,
- dirs_only=False, files_only=False):
- self._log(DEBUG, "Listing directory (listdirinfo) %s" % path)
-
- if dirs_only and files_only:
- raise ValueError("dirs_only and files_only can not both be True")
-
- for item in self.tahoeutil.list(self.dircap, path):
- if dirs_only and item['type'] == 'filenode':
- continue
- elif files_only and item['type'] == 'dirnode':
- continue
-
- if wildcard is not None:
- if isinstance(wildcard,basestring):
- if not fnmatch.fnmatch(item['name'], wildcard):
- continue
- else:
- if not wildcard(item['name']):
- continue
-
- if full:
- item_path = relpath(pathjoin(path, item['name']))
- elif absolute:
- item_path = abspath(pathjoin(path, item['name']))
- else:
- item_path = item['name']
-
- yield (item_path, item)
-
- @_fix_path
- def remove(self, path):
- self._log(INFO, 'Removing file %s' % path)
- if self.getmeta("read_only"):
- raise errors.UnsupportedError('read only filesystem')
-
- if not self.isfile(path):
- if not self.isdir(path):
- raise errors.ResourceNotFoundError(path)
- raise errors.ResourceInvalidError(path)
-
- try:
- self.tahoeutil.unlink(self.dircap, path)
- except Exception, e:
- raise errors.ResourceInvalidError(path)
-
- @_fix_path
- def removedir(self, path, recursive=False, force=False):
- self._log(INFO, "Removing directory %s" % path)
- if self.getmeta("read_only"):
- raise errors.UnsupportedError('read only filesystem')
- if not self.isdir(path):
- if not self.isfile(path):
- raise errors.ResourceNotFoundError(path)
- raise errors.ResourceInvalidError(path)
- if not force and self.listdir(path):
- raise errors.DirectoryNotEmptyError(path)
-
- self.tahoeutil.unlink(self.dircap, path)
-
- if recursive and path != '/':
- try:
- self.removedir(dirname(path), recursive=True)
- except errors.DirectoryNotEmptyError:
- pass
-
- @_fix_path
- def makedir(self, path, recursive=False, allow_recreate=False):
- self._log(INFO, "Creating directory %s" % path)
- if self.getmeta("read_only"):
- raise errors.UnsupportedError('read only filesystem')
- if self.exists(path):
- if not self.isdir(path):
- raise errors.ResourceInvalidError(path)
- if not allow_recreate:
- raise errors.DestinationExistsError(path)
- if not recursive and not self.exists(dirname(path)):
- raise errors.ParentDirectoryMissingError(path)
- self.tahoeutil.mkdir(self.dircap, path)
-
- def movedir(self, src, dst, overwrite=False):
- self.move(src, dst, overwrite=overwrite)
-
- def move(self, src, dst, overwrite=False):
- self._log(INFO, "Moving file from %s to %s" % (src, dst))
- if self.getmeta("read_only"):
- raise errors.UnsupportedError('read only filesystem')
- src = _fixpath(src)
- dst = _fixpath(dst)
- if not self.exists(dirname(dst)):
- raise errors.ParentDirectoryMissingError(dst)
- if not overwrite and self.exists(dst):
- raise errors.DestinationExistsError(dst)
- self.tahoeutil.move(self.dircap, src, dst)
-
- def rename(self, src, dst):
- self.move(src, dst)
-
- def copy(self, src, dst, overwrite=False, chunk_size=16384):
- if self.getmeta("read_only"):
- raise errors.UnsupportedError('read only filesystem')
- # FIXME: this is out of date; how to do native tahoe copy?
- # FIXME: Workaround because isfile() not exists on _TahoeLAFS
- FS.copy(self, src, dst, overwrite, chunk_size)
-
- def copydir(self, src, dst, overwrite=False, ignore_errors=False, chunk_size=16384):
- if self.getmeta("read_only"):
- raise errors.UnsupportedError('read only filesystem')
- # FIXME: this is out of date; how to do native tahoe copy?
- # FIXME: Workaround because isfile() not exists on _TahoeLAFS
- FS.copydir(self, src, dst, overwrite, ignore_errors, chunk_size)
-
-
- def _log(self, level, message):
- if not logger.isEnabledFor(level): return
- logger.log(level, u'(%d) %s' % (id(self),
- unicode(message).encode('ASCII', 'replace')))
-
- @_fix_path
- def getpathurl(self, path, allow_none=False, webapi=None):
- '''
- Retrieve URL where the file/directory is stored
- '''
- if webapi == None:
- webapi = self.connection.webapi
- self._log(DEBUG, "Retrieving URL for %s over %s" % (path, webapi))
- path = self.tahoeutil.fixwinpath(path, False)
- return u"%s/uri/%s%s" % (webapi, self.dircap, path)
-
- @_fix_path
- def getrange(self, path, offset, length=None):
- return self.connection.get(u'/uri/%s%s' % (self.dircap, path),
- offset=offset, length=length)
-
- @_fix_path
- def setcontents(self, path, file, chunk_size=64*1024):
- self._log(INFO, 'Uploading file %s' % path)
- size=None
-
- if self.getmeta("read_only"):
- raise errors.UnsupportedError('read only filesystem')
-
- # Workaround for large files:
- # First create zero file placeholder, then
- # upload final content.
- if self.largefilesize != None and getattr(file, 'read', None):
- # As 'file' can be also a string, need to check,
- # if 'file' looks like duck. Sorry, file.
- file.seek(0, SEEK_END)
- size = file.tell()
- file.seek(0)
-
- if size > self.largefilesize:
- self.connection.put(u'/uri/%s%s' % (self.dircap, path),
- "PyFilesystem.TahoeLAFS: Upload started, final size %d" % size)
-
- self.connection.put(u'/uri/%s%s' % (self.dircap, path), file, size=size)
-
- @_fix_path
- def getinfo(self, path):
- self._log(INFO, 'Reading meta for %s' % path)
- info = self.tahoeutil.info(self.dircap, path)
- #import datetime
- #info['created_time'] = datetime.datetime.now()
- #info['modified_time'] = datetime.datetime.now()
- #info['accessed_time'] = datetime.datetime.now()
- if info['type'] == 'filenode':
- info["st_mode"] = 0x700 | statinfo.S_IFREG
- elif info['type'] == 'dirnode':
- info["st_mode"] = 0x700 | statinfo.S_IFDIR
- return info
-
-
-
-class TahoeLAFS(CacheFSMixin,_TahoeLAFS):
- """FS providing cached access to a Tahoe Filesystem.
-
- This class is the preferred means to access a Tahoe filesystem. It
- maintains an internal cache of recently-accessed metadata to speed
- up operations.
- """
-
- def __init__(self, *args, **kwds):
- kwds.setdefault("cache_timeout",60)
- super(TahoeLAFS,self).__init__(*args,**kwds)
-
-
+''' +fs.contrib.tahoelafs +==================== + +This modules provides a PyFilesystem interface to the Tahoe Least Authority +File System. Tahoe-LAFS is a distributed, encrypted, fault-tolerant storage +system: + + http://tahoe-lafs.org/ + +You will need access to a Tahoe-LAFS "web api" service. + +Example (it will use publicly available (but slow) Tahoe-LAFS cloud):: + + from fs.contrib.tahoelafs import TahoeLAFS, Connection + dircap = TahoeLAFS.createdircap(webapi='http://insecure.tahoe-lafs.org') + print "Your dircap (unique key to your storage directory) is", dircap + print "Keep it safe!" + fs = TahoeLAFS(dircap, autorun=False, webapi='http://insecure.tahoe-lafs.org') + f = fs.open("foo.txt", "a") + f.write('bar!') + f.close() + print "Now visit %s and enjoy :-)" % fs.getpathurl('foo.txt') + +When any problem occurred, you can turn on internal debugging messages:: + + import logging + l = logging.getLogger() + l.setLevel(logging.DEBUG) + l.addHandler(logging.StreamHandler(sys.stdout)) + + ... your Python code using TahoeLAFS ... + +TODO: + + * unicode support + * try network errors / bad happiness + * exceptions + * tests + * sanitize all path types (., /) + * support for extra large file uploads (poster module) + * Possibility to block write until upload done (Tahoe mailing list) + * Report something sane when Tahoe crashed/unavailable + * solve failed unit tests (makedir_winner, ...) + * file times + * docs & author + * python3 support + * remove creating blank files (depends on FileUploadManager) + +TODO (Not TahoeLAFS specific tasks): + * RemoteFileBuffer on the fly buffering support + * RemoteFileBuffer unit tests + * RemoteFileBuffer submit to trunk + * Implement FileUploadManager + faking isfile/exists of just processing file + * pyfilesystem docs is outdated (rename, movedir, ...) + +''' + + +import stat as statinfo + +import logging +from logging import DEBUG, INFO, ERROR, CRITICAL + +import fs +import fs.errors as errors +from fs.path import abspath, relpath, normpath, dirname, pathjoin +from fs.base import FS, NullFile +from fs import _thread_synchronize_default, SEEK_END +from fs.remote import CacheFSMixin, RemoteFileBuffer +from fs.base import fnmatch, NoDefaultMeta + +from util import TahoeUtil +from connection import Connection + +from six import b + +logger = fs.getLogger('fs.tahoelafs') + +def _fix_path(func): + """Method decorator for automatically normalising paths.""" + def wrapper(self, *args, **kwds): + if len(args): + args = list(args) + args[0] = _fixpath(args[0]) + return func(self, *args, **kwds) + return wrapper + + +def _fixpath(path): + """Normalize the given path.""" + return abspath(normpath(path)) + + + +class _TahoeLAFS(FS): + """FS providing raw access to a Tahoe-LAFS Filesystem. + + This class implements all the details of interacting with a Tahoe-backed + filesystem, but you probably don't want to use it in practice. Use the + TahoeLAFS class instead, which has some internal caching to improve + performance. + """ + + _meta = { 'virtual' : False, + 'read_only' : False, + 'unicode_paths' : True, + 'case_insensitive_paths' : False, + 'network' : True + } + + + def __init__(self, dircap, largefilesize=10*1024*1024, webapi='http://127.0.0.1:3456'): + '''Creates instance of TahoeLAFS. + + :param dircap: special hash allowing user to work with TahoeLAFS directory. + :param largefilesize: - Create placeholder file for files larger than this treshold. + Uploading and processing of large files can last extremely long (many hours), + so placing this placeholder can help you to remember that upload is processing. + Setting this to None will skip creating placeholder files for any uploads. + ''' + self.dircap = dircap if not dircap.endswith('/') else dircap[:-1] + self.largefilesize = largefilesize + self.connection = Connection(webapi) + self.tahoeutil = TahoeUtil(webapi) + super(_TahoeLAFS, self).__init__(thread_synchronize=_thread_synchronize_default) + + def __str__(self): + return "<TahoeLAFS: %s>" % self.dircap + + @classmethod + def createdircap(cls, webapi='http://127.0.0.1:3456'): + return TahoeUtil(webapi).createdircap() + + def getmeta(self,meta_name,default=NoDefaultMeta): + if meta_name == "read_only": + return self.dircap.startswith('URI:DIR2-RO') + return super(_TahoeLAFS,self).getmeta(meta_name,default) + + @_fix_path + def open(self, path, mode='r', **kwargs): + self._log(INFO, 'Opening file %s in mode %s' % (path, mode)) + newfile = False + if not self.exists(path): + if 'w' in mode or 'a' in mode: + newfile = True + else: + self._log(DEBUG, "File %s not found while opening for reads" % path) + raise errors.ResourceNotFoundError(path) + elif self.isdir(path): + self._log(DEBUG, "Path %s is directory, not a file" % path) + raise errors.ResourceInvalidError(path) + elif 'w' in mode: + newfile = True + + if newfile: + self._log(DEBUG, 'Creating empty file %s' % path) + if self.getmeta("read_only"): + raise errors.UnsupportedError('read only filesystem') + self.setcontents(path, b('')) + handler = NullFile() + else: + self._log(DEBUG, 'Opening existing file %s for reading' % path) + handler = self.getrange(path,0) + + return RemoteFileBuffer(self, path, mode, handler, + write_on_flush=False) + + @_fix_path + def desc(self, path): + try: + return self.getinfo(path) + except: + return '' + + @_fix_path + def exists(self, path): + try: + self.getinfo(path) + self._log(DEBUG, "Path %s exists" % path) + return True + except errors.ResourceNotFoundError: + self._log(DEBUG, "Path %s does not exists" % path) + return False + except errors.ResourceInvalidError: + self._log(DEBUG, "Path %s does not exists, probably misspelled URI" % path) + return False + + @_fix_path + def getsize(self, path): + try: + size = self.getinfo(path)['size'] + self._log(DEBUG, "Size of %s is %d" % (path, size)) + return size + except errors.ResourceNotFoundError: + return 0 + + @_fix_path + def isfile(self, path): + try: + isfile = (self.getinfo(path)['type'] == 'filenode') + except errors.ResourceNotFoundError: + #isfile = not path.endswith('/') + isfile = False + self._log(DEBUG, "Path %s is file: %d" % (path, isfile)) + return isfile + + @_fix_path + def isdir(self, path): + try: + isdir = (self.getinfo(path)['type'] == 'dirnode') + except errors.ResourceNotFoundError: + isdir = False + self._log(DEBUG, "Path %s is directory: %d" % (path, isdir)) + return isdir + + + def listdir(self, *args, **kwargs): + return [ item[0] for item in self.listdirinfo(*args, **kwargs) ] + + def listdirinfo(self, *args, **kwds): + return list(self.ilistdirinfo(*args,**kwds)) + + def ilistdir(self, *args, **kwds): + for item in self.ilistdirinfo(*args,**kwds): + yield item[0] + + @_fix_path + def ilistdirinfo(self, path="/", wildcard=None, full=False, absolute=False, + dirs_only=False, files_only=False): + self._log(DEBUG, "Listing directory (listdirinfo) %s" % path) + + if dirs_only and files_only: + raise ValueError("dirs_only and files_only can not both be True") + + for item in self.tahoeutil.list(self.dircap, path): + if dirs_only and item['type'] == 'filenode': + continue + elif files_only and item['type'] == 'dirnode': + continue + + if wildcard is not None: + if isinstance(wildcard,basestring): + if not fnmatch.fnmatch(item['name'], wildcard): + continue + else: + if not wildcard(item['name']): + continue + + if full: + item_path = relpath(pathjoin(path, item['name'])) + elif absolute: + item_path = abspath(pathjoin(path, item['name'])) + else: + item_path = item['name'] + + yield (item_path, item) + + @_fix_path + def remove(self, path): + self._log(INFO, 'Removing file %s' % path) + if self.getmeta("read_only"): + raise errors.UnsupportedError('read only filesystem') + + if not self.isfile(path): + if not self.isdir(path): + raise errors.ResourceNotFoundError(path) + raise errors.ResourceInvalidError(path) + + try: + self.tahoeutil.unlink(self.dircap, path) + except Exception, e: + raise errors.ResourceInvalidError(path) + + @_fix_path + def removedir(self, path, recursive=False, force=False): + self._log(INFO, "Removing directory %s" % path) + if self.getmeta("read_only"): + raise errors.UnsupportedError('read only filesystem') + if not self.isdir(path): + if not self.isfile(path): + raise errors.ResourceNotFoundError(path) + raise errors.ResourceInvalidError(path) + if not force and self.listdir(path): + raise errors.DirectoryNotEmptyError(path) + + self.tahoeutil.unlink(self.dircap, path) + + if recursive and path != '/': + try: + self.removedir(dirname(path), recursive=True) + except errors.DirectoryNotEmptyError: + pass + + @_fix_path + def makedir(self, path, recursive=False, allow_recreate=False): + self._log(INFO, "Creating directory %s" % path) + if self.getmeta("read_only"): + raise errors.UnsupportedError('read only filesystem') + if self.exists(path): + if not self.isdir(path): + raise errors.ResourceInvalidError(path) + if not allow_recreate: + raise errors.DestinationExistsError(path) + if not recursive and not self.exists(dirname(path)): + raise errors.ParentDirectoryMissingError(path) + self.tahoeutil.mkdir(self.dircap, path) + + def movedir(self, src, dst, overwrite=False): + self.move(src, dst, overwrite=overwrite) + + def move(self, src, dst, overwrite=False): + self._log(INFO, "Moving file from %s to %s" % (src, dst)) + if self.getmeta("read_only"): + raise errors.UnsupportedError('read only filesystem') + src = _fixpath(src) + dst = _fixpath(dst) + if not self.exists(dirname(dst)): + raise errors.ParentDirectoryMissingError(dst) + if not overwrite and self.exists(dst): + raise errors.DestinationExistsError(dst) + self.tahoeutil.move(self.dircap, src, dst) + + def rename(self, src, dst): + self.move(src, dst) + + def copy(self, src, dst, overwrite=False, chunk_size=16384): + if self.getmeta("read_only"): + raise errors.UnsupportedError('read only filesystem') + # FIXME: this is out of date; how to do native tahoe copy? + # FIXME: Workaround because isfile() not exists on _TahoeLAFS + FS.copy(self, src, dst, overwrite, chunk_size) + + def copydir(self, src, dst, overwrite=False, ignore_errors=False, chunk_size=16384): + if self.getmeta("read_only"): + raise errors.UnsupportedError('read only filesystem') + # FIXME: this is out of date; how to do native tahoe copy? + # FIXME: Workaround because isfile() not exists on _TahoeLAFS + FS.copydir(self, src, dst, overwrite, ignore_errors, chunk_size) + + + def _log(self, level, message): + if not logger.isEnabledFor(level): return + logger.log(level, u'(%d) %s' % (id(self), + unicode(message).encode('ASCII', 'replace'))) + + @_fix_path + def getpathurl(self, path, allow_none=False, webapi=None): + ''' + Retrieve URL where the file/directory is stored + ''' + if webapi == None: + webapi = self.connection.webapi + self._log(DEBUG, "Retrieving URL for %s over %s" % (path, webapi)) + path = self.tahoeutil.fixwinpath(path, False) + return u"%s/uri/%s%s" % (webapi, self.dircap, path) + + @_fix_path + def getrange(self, path, offset, length=None): + return self.connection.get(u'/uri/%s%s' % (self.dircap, path), + offset=offset, length=length) + + @_fix_path + def setcontents(self, path, file, chunk_size=64*1024): + self._log(INFO, 'Uploading file %s' % path) + size=None + + if self.getmeta("read_only"): + raise errors.UnsupportedError('read only filesystem') + + # Workaround for large files: + # First create zero file placeholder, then + # upload final content. + if self.largefilesize != None and getattr(file, 'read', None): + # As 'file' can be also a string, need to check, + # if 'file' looks like duck. Sorry, file. + file.seek(0, SEEK_END) + size = file.tell() + file.seek(0) + + if size > self.largefilesize: + self.connection.put(u'/uri/%s%s' % (self.dircap, path), + "PyFilesystem.TahoeLAFS: Upload started, final size %d" % size) + + self.connection.put(u'/uri/%s%s' % (self.dircap, path), file, size=size) + + @_fix_path + def getinfo(self, path): + self._log(INFO, 'Reading meta for %s' % path) + info = self.tahoeutil.info(self.dircap, path) + #import datetime + #info['created_time'] = datetime.datetime.now() + #info['modified_time'] = datetime.datetime.now() + #info['accessed_time'] = datetime.datetime.now() + if info['type'] == 'filenode': + info["st_mode"] = 0x700 | statinfo.S_IFREG + elif info['type'] == 'dirnode': + info["st_mode"] = 0x700 | statinfo.S_IFDIR + return info + + + +class TahoeLAFS(CacheFSMixin,_TahoeLAFS): + """FS providing cached access to a Tahoe Filesystem. + + This class is the preferred means to access a Tahoe filesystem. It + maintains an internal cache of recently-accessed metadata to speed + up operations. + """ + + def __init__(self, *args, **kwds): + kwds.setdefault("cache_timeout",60) + super(TahoeLAFS,self).__init__(*args,**kwds) + + |