summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTerence Honles <terence@honles.com>2012-05-07 15:17:14 -0700
committerTerence Honles <terence@honles.com>2012-05-07 15:17:28 -0700
commit2fbfe661a605b0878443558d4a080a10cf53ba20 (patch)
treefbf7776931a7902505e84d9dfb7a038e0c667a0d
parent5e9a1154cbda06c8ff39c80dcbd1fd70744140c5 (diff)
parent2f0e5b5f1a5ee11a958acc8acdc47b187a76ec1b (diff)
downloadfusepy-2fbfe661a605b0878443558d4a080a10cf53ba20.tar.gz
bumping major version due to paths and metadata as unicode nowv2.0
-rw-r--r--README.rst29
-rwxr-xr-xexamples/context.py26
-rwxr-xr-xexamples/loopback.py49
-rwxr-xr-xexamples/memory.py77
-rwxr-xr-xexamples/memory3.py128
-rwxr-xr-xexamples/sftp.py54
-rw-r--r--fuse.py12
-rwxr-xr-xsetup.py5
8 files changed, 132 insertions, 248 deletions
diff --git a/README.rst b/README.rst
index beff1d8..cbdd977 100644
--- a/README.rst
+++ b/README.rst
@@ -4,16 +4,13 @@ fusepy
``fusepy`` is a Python module that provides a simple interface to FUSE_ and
MacFUSE_. It's just one file and is implemented using ctypes.
-The official version of ``fusepy`` is hosted on `Google Code`_, but was split
-into 3 seperate files: fuse24.py, fuse.py, and fuse3.py. These versions were
-for <Python2.5, <Python3.x, and Python3.x respectively. Unfortunately they were
-not all maintained, and installing the package in Python3 did not work.
+The original version of ``fusepy`` was hosted on `Google Code`_, but is now
+`officially hosted on GitHub`_.
-This repo mereges bits from all 3 files and combines them into one file. The
-file is written in 2x syntax, but trying to pay attention to bytes and other
-changes 3x would care about. The only incompatible changes between 2x and 3x
-are the change in syntax for number literals and exceptions. These issues are
-fixed using the 2to3 tool when installing the package, or runnning::
+``fusepy`` is written in 2x syntax, but trying to pay attention to bytes and
+other changes 3x would care about. The only incompatible changes between 2x and
+3x are the change in syntax for number literals and exceptions. These issues
+are fixed using the 2to3 tool when installing the package, or runnning::
2to3 -f numliterals -f except -w fuse.py
@@ -39,9 +36,13 @@ fusepy requires FUSE 2.6 (or later) and runs on:
.. _FUSE: http://fuse.sourceforge.net/
.. _MacFUSE: http://code.google.com/p/macfuse/
.. _`Google Code`: http://code.google.com/p/fusepy/
-.. _memory: http://github.com/terencehonles/fusepy/tree/master/examples/memory.py
-.. _loopback: http://github.com/terencehonles/fusepy/tree/master/examples/loopback.py
-.. _context: http://github.com/terencehonles/fusepy/tree/master/examples/context.py
-.. _sftp: http://github.com/terencehonles/fusepy/tree/master/examples/sftp.py
+
+.. _officially hosted on GitHub: source_
.. _download: https://github.com/terencehonles/fusepy/zipball/master
-.. _source: http://github.com/terencehonles/fusepy/tree/master/
+.. _source: http://github.com/terencehonles/fusepy
+
+.. examples
+.. _memory: http://github.com/terencehonles/fusepy/blob/master/examples/memory.py
+.. _loopback: http://github.com/terencehonles/fusepy/blob/master/examples/loopback.py
+.. _context: http://github.com/terencehonles/fusepy/blob/master/examples/context.py
+.. _sftp: http://github.com/terencehonles/fusepy/blob/master/examples/sftp.py
diff --git a/examples/context.py b/examples/context.py
index 2609aa0..aa8ab76 100755
--- a/examples/context.py
+++ b/examples/context.py
@@ -9,8 +9,8 @@ from fuse import FUSE, FuseOSError, Operations, LoggingMixIn, fuse_get_context
class Context(LoggingMixIn, Operations):
- """Example filesystem to demonstrate fuse_get_context()"""
-
+ 'Example filesystem to demonstrate fuse_get_context()'
+
def getattr(self, path, fh=None):
uid, gid, pid = fuse_get_context()
if path == '/':
@@ -28,17 +28,20 @@ class Context(LoggingMixIn, Operations):
raise FuseOSError(ENOENT)
st['st_ctime'] = st['st_mtime'] = st['st_atime'] = time()
return st
-
+
def read(self, path, size, offset, fh):
uid, gid, pid = fuse_get_context()
+ encoded = lambda x: ('%s\n' % x).encode('utf-8')
+
if path == '/uid':
- return '%s\n' % uid
+ return encoded(uid)
elif path == '/gid':
- return '%s\n' % gid
+ return encoded(gid)
elif path == '/pid':
- return '%s\n' % pid
- return ''
-
+ return encoded(pid)
+
+ raise RuntimeError('unexpected path: %r' % path)
+
def readdir(self, path, fh):
return ['.', '..', 'uid', 'gid', 'pid']
@@ -54,8 +57,9 @@ class Context(LoggingMixIn, Operations):
statfs = None
-if __name__ == "__main__":
+if __name__ == '__main__':
if len(argv) != 2:
- print 'usage: %s <mountpoint>' % argv[0]
+ print('usage: %s <mountpoint>' % argv[0])
exit(1)
- fuse = FUSE(Context(), argv[1], foreground=True) \ No newline at end of file
+
+ fuse = FUSE(Context(), argv[1], foreground=True, ro=True)
diff --git a/examples/loopback.py b/examples/loopback.py
index 5ce16ed..a7b8275 100755
--- a/examples/loopback.py
+++ b/examples/loopback.py
@@ -12,87 +12,88 @@ import os
from fuse import FUSE, FuseOSError, Operations, LoggingMixIn
-class Loopback(LoggingMixIn, Operations):
+class Loopback(LoggingMixIn, Operations):
def __init__(self, root):
self.root = realpath(root)
self.rwlock = Lock()
-
+
def __call__(self, op, path, *args):
return super(Loopback, self).__call__(op, self.root + path, *args)
-
+
def access(self, path, mode):
if not os.access(path, mode):
raise FuseOSError(EACCES)
-
+
chmod = os.chmod
chown = os.chown
-
+
def create(self, path, mode):
return os.open(path, os.O_WRONLY | os.O_CREAT, mode)
-
+
def flush(self, path, fh):
return os.fsync(fh)
def fsync(self, path, datasync, fh):
return os.fsync(fh)
-
+
def getattr(self, path, fh=None):
st = os.lstat(path)
return dict((key, getattr(st, key)) for key in ('st_atime', 'st_ctime',
'st_gid', 'st_mode', 'st_mtime', 'st_nlink', 'st_size', 'st_uid'))
-
+
getxattr = None
-
+
def link(self, target, source):
return os.link(source, target)
-
+
listxattr = None
mkdir = os.mkdir
mknod = os.mknod
open = os.open
-
+
def read(self, path, size, offset, fh):
with self.rwlock:
os.lseek(fh, offset, 0)
return os.read(fh, size)
-
+
def readdir(self, path, fh):
return ['.', '..'] + os.listdir(path)
readlink = os.readlink
-
+
def release(self, path, fh):
return os.close(fh)
-
+
def rename(self, old, new):
return os.rename(old, self.root + new)
-
+
rmdir = os.rmdir
-
+
def statfs(self, path):
stv = os.statvfs(path)
return dict((key, getattr(stv, key)) for key in ('f_bavail', 'f_bfree',
'f_blocks', 'f_bsize', 'f_favail', 'f_ffree', 'f_files', 'f_flag',
'f_frsize', 'f_namemax'))
-
+
def symlink(self, target, source):
return os.symlink(source, target)
-
+
def truncate(self, path, length, fh=None):
with open(path, 'r+') as f:
f.truncate(length)
-
+
unlink = os.unlink
utimens = os.utime
-
+
def write(self, path, data, offset, fh):
with self.rwlock:
os.lseek(fh, offset, 0)
return os.write(fh, data)
-
-if __name__ == "__main__":
+
+if __name__ == '__main__':
if len(argv) != 3:
- print 'usage: %s <root> <mountpoint>' % argv[0]
+ print('usage: %s <root> <mountpoint>' % argv[0])
exit(1)
- fuse = FUSE(Loopback(argv[1]), argv[2], foreground=True) \ No newline at end of file
+
+ fuse = FUSE(Loopback(argv[1]), argv[2], foreground=True)
diff --git a/examples/memory.py b/examples/memory.py
index 246b305..a103185 100755
--- a/examples/memory.py
+++ b/examples/memory.py
@@ -1,5 +1,7 @@
#!/usr/bin/env python
+import logging
+
from collections import defaultdict
from errno import ENOENT
from stat import S_IFDIR, S_IFLNK, S_IFREG
@@ -8,18 +10,20 @@ from time import time
from fuse import FUSE, FuseOSError, Operations, LoggingMixIn
+if not hasattr(__builtins__, 'bytes'):
+ bytes = str
class Memory(LoggingMixIn, Operations):
- """Example memory filesystem. Supports only one level of files."""
-
+ 'Example memory filesystem. Supports only one level of files.'
+
def __init__(self):
self.files = {}
- self.data = defaultdict(str)
+ self.data = defaultdict(bytes)
self.fd = 0
now = time()
self.files['/'] = dict(st_mode=(S_IFDIR | 0755), st_ctime=now,
- st_mtime=now, st_atime=now, st_nlink=2)
-
+ st_mtime=now, st_atime=now, st_nlink=2)
+
def chmod(self, path, mode):
self.files[path]['st_mode'] &= 0770000
self.files[path]['st_mode'] |= mode
@@ -28,96 +32,105 @@ class Memory(LoggingMixIn, Operations):
def chown(self, path, uid, gid):
self.files[path]['st_uid'] = uid
self.files[path]['st_gid'] = gid
-
+
def create(self, path, mode):
self.files[path] = dict(st_mode=(S_IFREG | mode), st_nlink=1,
- st_size=0, st_ctime=time(), st_mtime=time(), st_atime=time())
+ st_size=0, st_ctime=time(), st_mtime=time(),
+ st_atime=time())
+
self.fd += 1
return self.fd
-
+
def getattr(self, path, fh=None):
if path not in self.files:
raise FuseOSError(ENOENT)
- st = self.files[path]
- return st
-
+
+ return self.files[path]
+
def getxattr(self, path, name, position=0):
attrs = self.files[path].get('attrs', {})
+
try:
return attrs[name]
except KeyError:
return '' # Should return ENOATTR
-
+
def listxattr(self, path):
attrs = self.files[path].get('attrs', {})
return attrs.keys()
-
+
def mkdir(self, path, mode):
self.files[path] = dict(st_mode=(S_IFDIR | mode), st_nlink=2,
- st_size=0, st_ctime=time(), st_mtime=time(), st_atime=time())
+ st_size=0, st_ctime=time(), st_mtime=time(),
+ st_atime=time())
+
self.files['/']['st_nlink'] += 1
-
+
def open(self, path, flags):
self.fd += 1
return self.fd
-
+
def read(self, path, size, offset, fh):
return self.data[path][offset:offset + size]
-
+
def readdir(self, path, fh):
return ['.', '..'] + [x[1:] for x in self.files if x != '/']
-
+
def readlink(self, path):
return self.data[path]
-
+
def removexattr(self, path, name):
attrs = self.files[path].get('attrs', {})
+
try:
del attrs[name]
except KeyError:
pass # Should return ENOATTR
-
+
def rename(self, old, new):
self.files[new] = self.files.pop(old)
-
+
def rmdir(self, path):
self.files.pop(path)
self.files['/']['st_nlink'] -= 1
-
+
def setxattr(self, path, name, value, options, position=0):
# Ignore options
attrs = self.files[path].setdefault('attrs', {})
attrs[name] = value
-
+
def statfs(self, path):
return dict(f_bsize=512, f_blocks=4096, f_bavail=2048)
-
+
def symlink(self, target, source):
self.files[target] = dict(st_mode=(S_IFLNK | 0777), st_nlink=1,
- st_size=len(source))
+ st_size=len(source))
+
self.data[target] = source
-
+
def truncate(self, path, length, fh=None):
self.data[path] = self.data[path][:length]
self.files[path]['st_size'] = length
-
+
def unlink(self, path):
self.files.pop(path)
-
+
def utimens(self, path, times=None):
now = time()
atime, mtime = times if times else (now, now)
self.files[path]['st_atime'] = atime
self.files[path]['st_mtime'] = mtime
-
+
def write(self, path, data, offset, fh):
self.data[path] = self.data[path][:offset] + data
self.files[path]['st_size'] = len(self.data[path])
return len(data)
-if __name__ == "__main__":
+if __name__ == '__main__':
if len(argv) != 2:
- print 'usage: %s <mountpoint>' % argv[0]
+ print('usage: %s <mountpoint>' % argv[0])
exit(1)
- fuse = FUSE(Memory(), argv[1], foreground=True) \ No newline at end of file
+
+ logging.getLogger().setLevel(logging.DEBUG)
+ fuse = FUSE(Memory(), argv[1], foreground=True)
diff --git a/examples/memory3.py b/examples/memory3.py
deleted file mode 100755
index e5cbad7..0000000
--- a/examples/memory3.py
+++ /dev/null
@@ -1,128 +0,0 @@
-#!/usr/bin/env python
-
-from fuse3 import FUSE, Operations, LoggingMixIn
-
-from collections import defaultdict
-from errno import ENOENT
-from stat import S_IFDIR, S_IFLNK, S_IFREG
-from sys import argv, exit
-from time import time
-
-import logging
-
-
-class Memory(LoggingMixIn, Operations):
- """Example memory filesystem. Supports only one level of files."""
-
- def __init__(self):
- self.files = {}
- self.data = defaultdict(bytearray)
- self.fd = 0
- now = time()
- self.files['/'] = dict(st_mode=(S_IFDIR | 0o755), st_ctime=now,
- st_mtime=now, st_atime=now, st_nlink=2)
-
- def chmod(self, path, mode):
- self.files[path]['st_mode'] &= 0o770000
- self.files[path]['st_mode'] |= mode
- return 0
-
- def chown(self, path, uid, gid):
- self.files[path]['st_uid'] = uid
- self.files[path]['st_gid'] = gid
-
- def create(self, path, mode):
- self.files[path] = dict(st_mode=(S_IFREG | mode), st_nlink=1,
- st_size=0, st_ctime=time(), st_mtime=time(), st_atime=time())
- self.fd += 1
- return self.fd
-
- def getattr(self, path, fh=None):
- if path not in self.files:
- raise OSError(ENOENT, '')
- st = self.files[path]
- return st
-
- def getxattr(self, path, name, position=0):
- attrs = self.files[path].get('attrs', {})
- try:
- return attrs[name]
- except KeyError:
- return '' # Should return ENOATTR
-
- def listxattr(self, path):
- attrs = self.files[path].get('attrs', {})
- return attrs.keys()
-
- def mkdir(self, path, mode):
- self.files[path] = dict(st_mode=(S_IFDIR | mode), st_nlink=2,
- st_size=0, st_ctime=time(), st_mtime=time(), st_atime=time())
- self.files['/']['st_nlink'] += 1
-
- def open(self, path, flags):
- self.fd += 1
- return self.fd
-
- def read(self, path, size, offset, fh):
- return bytes(self.data[path][offset:offset + size])
-
- def readdir(self, path, fh):
- return ['.', '..'] + [x[1:] for x in self.files if x != '/']
-
- def readlink(self, path):
- return self.data[path].decode('utf-8')
-
- def removexattr(self, path, name):
- attrs = self.files[path].get('attrs', {})
- try:
- del attrs[name]
- except KeyError:
- pass # Should return ENOATTR
-
- def rename(self, old, new):
- self.files[new] = self.files.pop(old)
-
- def rmdir(self, path):
- self.files.pop(path)
- self.files['/']['st_nlink'] -= 1
-
- def setxattr(self, path, name, value, options, position=0):
- # Ignore options
- attrs = self.files[path].setdefault('attrs', {})
- attrs[name] = value
-
- def statfs(self, path):
- return dict(f_bsize=512, f_blocks=4096, f_bavail=2048)
-
- def symlink(self, target, source):
- source = source.encode('utf-8')
- self.files[target] = dict(st_mode=(S_IFLNK | 0o777), st_nlink=1,
- st_size=len(source))
- self.data[target] = bytearray(source)
-
- def truncate(self, path, length, fh=None):
- del self.data[path][length:]
- self.files[path]['st_size'] = length
-
- def unlink(self, path):
- self.files.pop(path)
-
- def utimens(self, path, times=None):
- now = time()
- atime, mtime = times if times else (now, now)
- self.files[path]['st_atime'] = atime
- self.files[path]['st_mtime'] = mtime
-
- def write(self, path, data, offset, fh):
- del self.data[path][offset:]
- self.data[path].extend(data)
- self.files[path]['st_size'] = len(self.data[path])
- return len(data)
-
-
-if __name__ == "__main__":
- if len(argv) != 2:
- print('usage: %s <mountpoint>' % argv[0])
- exit(1)
- logging.getLogger().setLevel(logging.DEBUG)
- fuse = FUSE(Memory(), argv[1], foreground=True) \ No newline at end of file
diff --git a/examples/sftp.py b/examples/sftp.py
index 019fb29..6f512bd 100755
--- a/examples/sftp.py
+++ b/examples/sftp.py
@@ -5,44 +5,26 @@ from time import time
from paramiko import SSHClient
-from fuse import FUSE, Operations
+from fuse import FUSE, Operations, LoggingMixIn
-class SFTP(Operations):
- """A simple SFTP filesystem. Requires paramiko:
- http://www.lag.net/paramiko/
-
- You need to be able to login to remote host without entering a password.
- """
+class SFTP(LoggingMixIn, Operations):
+ '''
+ A simple SFTP filesystem. Requires paramiko: http://www.lag.net/paramiko/
+
+ You need to be able to login to remote host without entering a password.
+ '''
+
def __init__(self, host, path='.'):
self.client = SSHClient()
self.client.load_system_host_keys()
self.client.connect(host)
self.sftp = self.client.open_sftp()
self.root = path
-
- def __del__(self):
- self.sftp.close()
- self.client.close()
-
- def __call__(self, op, path, *args):
- print '->', op, path, args[0] if args else ''
- ret = '[Unhandled Exception]'
- try:
- ret = getattr(self, op)(self.root + path, *args)
- return ret
- except OSError, e:
- ret = str(e)
- raise
- except IOError, e:
- ret = str(e)
- raise OSError(*e.args)
- finally:
- print '<-', op
-
+
def chmod(self, path, mode):
return self.sftp.chmod(path, mode)
-
+
def chown(self, path, uid, gid):
return self.sftp.chown(path, uid, gid)
@@ -52,6 +34,10 @@ class SFTP(Operations):
f.close()
return 0
+ def destroy(self, path):
+ self.sftp.close()
+ self.client.close()
+
def getattr(self, path, fh=None):
st = self.sftp.lstat(path)
return dict((key, getattr(st, key)) for key in ('st_atime', 'st_gid',
@@ -68,7 +54,8 @@ class SFTP(Operations):
return buf
def readdir(self, path, fh):
- return ['.', '..'] + [name.encode('utf-8') for name in self.sftp.listdir(path)]
+ return ['.', '..'] + [name.encode('utf-8')
+ for name in self.sftp.listdir(path)]
def readlink(self, path):
return self.sftp.readlink(path)
@@ -97,10 +84,11 @@ class SFTP(Operations):
f.write(data)
f.close()
return len(data)
-
-if __name__ == "__main__":
+
+if __name__ == '__main__':
if len(argv) != 3:
- print 'usage: %s <host> <mountpoint>' % argv[0]
+ print('usage: %s <host> <mountpoint>' % argv[0])
exit(1)
- fuse = FUSE(SFTP(argv[1]), argv[2], foreground=True, nothreads=True) \ No newline at end of file
+
+ fuse = FUSE(SFTP(argv[1]), argv[2], foreground=True, nothreads=True)
diff --git a/fuse.py b/fuse.py
index 2cea0c6..3b91740 100644
--- a/fuse.py
+++ b/fuse.py
@@ -1,4 +1,5 @@
-# Copyright (c) 2008 Giorgos Verigakis <verigak@gmail.com>
+# Copyright (c) 2012 Terence Honles <terence@honles.com> (maintainer)
+# Copyright (c) 2008 Giorgos Verigakis <verigak@gmail.com> (author)
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -40,7 +41,9 @@ except ImportError:
newfunc.keywords = keywords
return newfunc
-if not hasattr(__builtins__, 'basestring'):
+try:
+ basestring
+except NameError:
basestring = str
class c_timespec(Structure):
@@ -543,10 +546,9 @@ class FUSE(object):
return retsize
def listxattr(self, path, namebuf, size):
- ret = '\x00'.join(self.operations('listxattr', path) or '') \
- .encode(self.encoding)
+ attrs = self.operations('listxattr', path.decode(self.encoding)) or ''
- buf = create_string_buffer(ret)
+ buf = create_string_buffer('\x00'.join(attrs).encode(self.encoding))
bufsize = len(buf)
if namebuf:
if bufsize > size: return -ERANGE
diff --git a/setup.py b/setup.py
index 326df55..e849f49 100755
--- a/setup.py
+++ b/setup.py
@@ -13,14 +13,17 @@ with open('README') as readme:
setup(
name = 'fusepy',
- version = '1.2',
+ version = '2.0',
description = 'Simple ctypes bindings for FUSE',
long_description = documentation,
author = 'Giorgos Verigakis',
author_email = 'verigak@gmail.com',
+ maintainer = 'Terence Honles',
+ maintainer_email = 'terence@honles.com',
license = 'ISC',
py_modules=['fuse'],
+ url = 'http://github.com/terencehonles/fusepy',
use_2to3 = True,
# only use the following fixers (everything else is already compatible)