diff options
author | bryan newbold <bnewbold@snark.mit.edu> | 2008-07-29 02:32:16 -0400 |
---|---|---|
committer | bryan newbold <bnewbold@snark.mit.edu> | 2008-07-29 02:32:16 -0400 |
commit | c56c18320506152db25277f49c05733c73a1043a (patch) | |
tree | 81ab207f882fb8d96236fb5be5a95a6d89c14785 | |
download | pynsfs-c56c18320506152db25277f49c05733c73a1043a.tar.gz pynsfs-c56c18320506152db25277f49c05733c73a1043a.zip |
initialization of repo
-rw-r--r-- | first_try.py | 269 | ||||
-rw-r--r-- | nsfs.py | 401 |
2 files changed, 670 insertions, 0 deletions
diff --git a/first_try.py b/first_try.py new file mode 100644 index 0000000..04a831e --- /dev/null +++ b/first_try.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python + +# July 2008: Bryan Newbold <bryannewbold.com> +# Based on xmp.py + +import os, sys +from errno import * +from stat import * +import fcntl +# pull in some spaghetti to make this stuff work without fuse-py being installed +try: + import _find_fuse_parts +except ImportError: + pass +import fuse +from fuse import Fuse + + +if not hasattr(fuse, '__version__'): + raise RuntimeError, \ + "your fuse-py doesn't know of fuse.__version__, probably it's too old." + +fuse.fuse_python_api = (0, 2) + +fuse.feature_assert('stateful_files', 'has_init') + + +def flag2mode(flags): + md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'} + m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)] + + if flags | os.O_APPEND: + m = m.replace('w', 'a', 1) + + return m + + +class PyNamespace(Fuse): + + def __init__(self, *args, **kw): + + Fuse.__init__(self, *args, **kw) + + # do stuff to set up your filesystem here, if you want + #import thread + #thread.start_new_thread(self.mythread, ()) + #self.root = '/' + +# def mythread(self): +# +# """ +# The beauty of the FUSE python implementation is that with the python interp +# running in foreground, you can have threads +# """ +# print "mythread: started" +# while 1: +# time.sleep(120) +# print "mythread: ticking" + + def getattr(self, path): + return locals()[path].__repr__ + + def readlink(self, path): + return str(path) + + def readdir(self, path, offset): + for e in os.listdir("." + path): + yield fuse.Direntry(e) + + def unlink(self, path): + del locals()[path] + + def rmdir(self, path): + del locals()[path] + + def symlink(self, path, path1): + locals()[path1] = locals()[path] + + def rename(self, path, path1): + locals()[path1] = locals()[path] + del locals()[path] + + def link(self, path, path1): + locals()[path1] = locals()[path] + + def chmod(self, path, mode): + return -EPFNOSUPPORT + + def chown(self, path, user, group): + return -EPFNOSUPPORT + + def truncate(self, path, len): + return -EPFNOSUPPORT + + def mknod(self, path, mode, dev): + return -EPFNOSUPPORT + + def mkdir(self, path, mode): + return -EPFNOSUPPORT + + def utime(self, path, times): + return -EPFNOSUPPORT + +# The following utimens method would do the same as the above utime method. +# We can't make it better though as the Python stdlib doesn't know of +# subsecond preciseness in acces/modify times. +# +# def utimens(self, path, ts_acc, ts_mod): +# os.utime("." + path, (ts_acc.tv_sec, ts_mod.tv_sec)) + + def access(self, path, mode): + if not os.access("." + path, mode): + return -EACCES + +# This is how we could add stub extended attribute handlers... +# (We can't have ones which aptly delegate requests to the underlying fs +# because Python lacks a standard xattr interface.) +# +# def getxattr(self, path, name, size): +# val = name.swapcase() + '@' + path +# if size == 0: +# # We are asked for size of the value. +# return len(val) +# return val +# +# def listxattr(self, path, size): +# # We use the "user" namespace to please XFS utils +# aa = ["user." + a for a in ("foo", "bar")] +# if size == 0: +# # We are asked for size of the attr list, ie. joint size of attrs +# # plus null separators. +# return len("".join(aa)) + len(aa) +# return aa + + def statfs(self): + """ + Should return an object with statvfs attributes (f_bsize, f_frsize...). + Eg., the return value of os.statvfs() is such a thing (since py 2.2). + If you are not reusing an existing statvfs object, start with + fuse.StatVFS(), and define the attributes. + + To provide usable information (ie., you want sensible df(1) + output, you are suggested to specify the following attributes: + + - f_bsize - preferred size of file blocks, in bytes + - f_frsize - fundamental size of file blcoks, in bytes + [if you have no idea, use the same as blocksize] + - f_blocks - total number of blocks in the filesystem + - f_bfree - number of free blocks + - f_files - total number of file inodes + - f_ffree - nunber of free file inodes + """ + rval = os.statvfs(".") + rval[3] = len(locals()) + rval[5] = len(locals()) + return rval + + def fsinit(self): + #os.chdir(self.root) + pass + + class PyNamespaceFile(object): + + def __init__(self, path, flags, *mode): + self.fobject = locals()[path] + self.fstr = StringIO(str(self.fobject)) + + def read(self, length, offset): + return self.fstr.read(length) + + def write(self, buf, offset): + self.fstr.seek(offset) + self.fstr.write(buf) + return len(buf) + + def release(self, flags): + self.fstr.close() + + def _fflush(self): + if 'w' in self.fstr.mode or 'a' in self.fstr.mode: + self.fstr.flush() + + def fsync(self, isfsyncfile): + pass + + def flush(self): + pass + + def fgetattr(self): + pass + + def ftruncate(self, len): + self.fstr.truncate(len) + + def lock(self, cmd, owner, **kw): + # The code here is much rather just a demonstration of the locking + # API than something which actually was seen to be useful. + + # Advisory file locking is pretty messy in Unix, and the Python + # interface to this doesn't make it better. + # We can't do fcntl(2)/F_GETLK from Python in a platfrom independent + # way. The following implementation *might* work under Linux. + # + # if cmd == fcntl.F_GETLK: + # import struct + # + # lockdata = struct.pack('hhQQi', kw['l_type'], os.SEEK_SET, + # kw['l_start'], kw['l_len'], kw['l_pid']) + # ld2 = fcntl.fcntl(self.fd, fcntl.F_GETLK, lockdata) + # flockfields = ('l_type', 'l_whence', 'l_start', 'l_len', 'l_pid') + # uld2 = struct.unpack('hhQQi', ld2) + # res = {} + # for i in xrange(len(uld2)): + # res[flockfields[i]] = uld2[i] + # + # return fuse.Flock(**res) + + # Convert fcntl-ish lock parameters to Python's weird + # lockf(3)/flock(2) medley locking API... + return -EOPNOTSUPP + #op = { fcntl.F_UNLCK : fcntl.LOCK_UN, + # fcntl.F_RDLCK : fcntl.LOCK_SH, + # fcntl.F_WRLCK : fcntl.LOCK_EX }[kw['l_type']] + #if cmd == fcntl.F_GETLK: + # return -EOPNOTSUPP + #elif cmd == fcntl.F_SETLK: + # if op != fcntl.LOCK_UN: + # op |= fcntl.LOCK_NB + #elif cmd == fcntl.F_SETLKW: + # pass + #else: + # return -EINVAL + + #fcntl.lockf(self.fd, op, kw['l_start'], kw['l_len']) + + + def main(self, *a, **kw): + + self.file_class = self.PyNamespaceFile + + return Fuse.main(self, *a, **kw) + + +def main(): + + usage = """ +Presents a python namespace as a filesystem for aesthetic reasons +""" + Fuse.fusage + + server = PyNamespace(version="%prog " + fuse.__version__, + usage=usage, + dash_s_do='setsingle') + + #server.parser.add_option(mountopt="root", metavar="PATH", default='/', + # help="mirror filesystem from under PATH [default: %default]") + #server.parse(values=server, errex=1) + + #try: + # if server.fuse_args.mount_expected(): + # os.chdir(server.root) + #except OSError: + # print >> sys.stderr, "can't enter root of underlying filesystem" + # sys.exit(1) + + server.main() + + +if __name__ == '__main__': + main() @@ -0,0 +1,401 @@ +#!/usr/bin/python + +from time import time +from StringIO import StringIO + +import stat # for file properties +import os # for filesystem modes (O_RDONLY, etc) +import errno # for error number codes (ENOENT, etc) + # - note: these must be returned as negatives + + +# pull in some spaghetti to make this stuff work without fuse-py being installed +try: + import _find_fuse_parts +except ImportError: + pass +import fuse +from fuse import Fuse + +if not hasattr(fuse, '__version__'): + raise RuntimeError, \ + "your fuse-py doesn't know of fuse.__version__, probably it's too old." + +fuse.fuse_python_api = (0, 2) + +class DefaultStat(fuse.Stat): + def __init__(self): + self.st_mode = 0 + self.st_ino = 0 + self.st_dev = 0 + self.st_nlink = 0 + self.st_uid = 0 + self.st_gid = 0 + self.st_size = 0 + self.st_atime = 0 + self.st_mtime = 0 + self.st_ctime = 0 + +class DefaultStatVfs(fuse.StatVfs): + def __init__(self): + self.f_bavail = 0 + self.f_bfree = 0 + self.f_blocks = 0 + self.f_bsize = 0 + self.f_favail = 0 + self.f_ffree = 0 + self.f_files = 0 + self.f_flag = 0 + self.f_frsize = 0 + self.f_namemax = 0 + self.n_fields = 0 + self.n_sequence_fields = 0 + self.n_unamed_fields = 0 + +class NamespaceFS(Fuse): + """Generates a file system from a python namespace, for kicks. + """ + + def __init__(self, *args, **kw): + Fuse.__init__(self, *args, **kw) + + self.context = self.GetContext() + #self.file_class = NsfsFile + print '*** init complete' + + def getattr(self, path): + """ + - st_mode (protection bits) + - st_ino (inode number) + - st_dev (device) + - st_nlink (number of hard links) + - st_uid (user ID of owner) + - st_gid (group ID of owner) + - st_size (size of file, in bytes) + - st_atime (time of most recent access) + - st_mtime (time of most recent content modification) + - st_ctime (platform dependent; time of most recent metadata change on + Unix, or the time of creation on Windows). + """ + print '*** getattr', path + + st = DefaultStat() + #st.st_uid = self.context['uid'] + #st.st_gid = self.context['gid'] + + if path == '/': + # root directory + st.st_mode = stat.S_IFDIR | 0755 + st.st_nlink = 2 + return st + + # we want this in the form 'module/submod/stuff' + l = path.strip('/').split('/') + + # recursively check the namespace + try: + l[0] = globals()[l[0]] + if len(l) > 1: + thing = reduce(getattr, l) + else: + thing = l[0] + except: + return -errno.ENOENT # nope, not there + + # for now, only modules are directories + if isinstance(thing, type(fuse)): + st.st_mode = stat.S_IFDIR | 0755 + st.st_nlink = 2 + else: + st.st_mode = stat.S_IFREG | 0444 + st.st_size = len(str(thing)) # how big? + st.st_nlink = 1 + return st + + def readdir(self, path, offset): + print '*** readdir', path, offset + if path == '/': + retl = globals().keys() + else: + # we want this in the form 'module/submod/stuff' + l = path.strip('/').split('/') + + # recursively check the namespace + try: + print '* trying ', l + l[0] = globals()[l[0]] + if len(l) == 1: + retl = l[0].__dict__.keys() + else: + retl = reduce(getattr, l).__dict__.keys() + except: + raise IOError(-errno.ENOENT) # nope, not there + + retl.insert(0, '..') + retl.insert(0, '.') + for r in retl: + yield fuse.Direntry(r) + + def open ( self, path, flags ): + print '*** open', path, flags + + # we want this in the form 'module/submod/stuff' + l = path.strip('/').split('/') + + # recursively check the namespace + try: + print '* trying ', l + l[0] = globals()[l[0]] + if len(l) == 1: + thing = l[0] + else: + thing = reduce(getattr, l) + except: + raise IOError(-errno.ENOENT) # nope, not there + + # only allow read unless it's a string + writeok = False + if isinstance(thing, type(".")): + writeok = True + accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR + if (flags & accmode) and writeok: + return + elif (flags & accmode) != os.O_RDONLY: + return -errno.EACCES + + def read ( self, path, length, offset ): + print '*** read', path, length, offset + # we want this in the form 'module/submod/stuff' + l = path.strip('/').split('/') + + # recursively check the namespace + try: + print '* trying ', l + l[0] = globals()[l[0]] + if len(l) == 1: + thing = l[0] + else: + thing = reduce(getattr, l) + except: + raise IOError(-errno.ENOENT) # nope, not there + + tstr = str(thing) + slen = len(tstr) + if offset < tstr: + if offset + length > slen: + size = slen - offset + buf = tstr[offset:offset+length] + else: + buf = '' + return buf + + def statfs ( self ): + print '*** statfs' + #return -errno.ENOSYS + fst = DefaultStatVfs() + fst.f_namemax = 256 + return fst + + def link ( self, targetPath, linkPath ): + print '*** link', targetPath, linkPath + t = targetPath.strip('/').split('/') + + # recursively check the namespace; target can't be / for now + try: + print '* trying ', t + t[0] = globals()[t[0]] + if len(t) == 1: + target = t[0] + else: + target = reduce(getattr, t) + except: + return -errno.ENOENT # nope, not there + + l = linkPath.strip('/').split('/') + if (len(l) == 1) and (l[0] != '') and not (l[0] in globals().keys()): + # easy, good to go + globals()[l[0]] = target + print '* trying easy ', l[0], target + return + + # otherwise... + # recursively check the namespace; link can't be / + try: + print '* trying ', l + l[0] = globals()[l[0]] + if len(l) == 1: + raise IOError(-errno.ENOENT) # can't do root or existing + elif len(l) == 2: + lp = l[0] + else: + lp = reduce(getattr, l[:-1]) # want the parent of the link + except: + return -errno.ENOENT # nope, not there + + # if that all worked out with no errors... + lp.__setattr__(l[-1], target) + + def unlink ( self, path ): + print '*** unlink', path + + l = path.strip('/').split('/') + if (len(l) == 1) and (l[0] != '') and (l[0] in globals().keys()): + # easy, good to go + del globals()[l[0]] + return + + try: + print '* trying ', l + l[0] = globals()[l[0]] + if len(l) == 1: + return -errno.ENOENT # something is wrong, shouldn't get here + elif len(l) == 2: + lp = l[0] + else: + lp = reduce(getattr, l[:-1]) # want the parent of the link + except: + return -errno.ENOENT # nope, not there + lp.__delattr__(l[-1]) + + def write ( self, path, buf, offset ): + print '*** write', path, buf, offset + # we want this in the form 'module/submod/stuff' + l = path.strip('/').split('/') + + # recursively check the namespace + try: + print '* trying ', l + l[0] = globals()[l[0]] + if len(l) == 1: + thing = l[0] + else: + thing = reduce(getattr, l) + except: + print '* ACTUALLY CAN\'T FIND' + raise IOError(-errno.ENOENT) # nope, not there + + if not isinstance(thing, type(".")): + print '* WRONG TYPE' + raise IOError(-errno.ENOENT) # wrong file type + + # Kind of a slow way to go? + sfile = StringIO(thing) + sfile.seek(offset) + sfile.write(buf) + sfile.flush() + thing = sfile.getvalue() + sfile.close() + + # recursively check the namespace + l = path.strip('/').split('/') + #try: + if len(l) == 1: + globals()[l[0]] = thing + return len(buf) + l[0] = globals()[l[0]] + tp = reduce(getattr, l[:-1]) + tp.__setattr__(l[-1], thing) + print '* TRIED ', thing + return len(buf) + #except: # raise IOError(-errno.ENOENT) # nope, not there + + def release ( self, path, flags ): + print '*** release', path, flags + return -errno.ENOSYS + + def fsync ( self, path, isFsyncFile ): + print '*** fsync', path, isFsyncFile + return -errno.ENOSYS + + def mknod ( self, path, mode, dev ): + print '*** mknod', path, oct(mode), dev + return -errno.ENOSYS + + def rmdir ( self, path ): + print '*** rmdir', path + return -errno.ENOSYS + + def getdir(self, path): + print '*** getdir', path + return self.readdir(path, 0) + + def mythread ( self ): + print '*** mythread' + return -errno.ENOSYS + + def chmod ( self, path, mode ): + print '*** chmod', path, oct(mode) + return -errno.ENOSYS + + def chown ( self, path, uid, gid ): + print '*** chown', path, uid, gid + return -errno.ENOSYS + + def mkdir ( self, path, mode ): + print '*** mkdir', path, oct(mode) + return -errno.ENOSYS + + def readlink ( self, path ): + print '*** readlink', path + return -errno.ENOSYS + + def rename ( self, oldPath, newPath ): + print '*** rename', oldPath, newPath + globals()[path1] = globals()[path] + del globals()[path] + #return -errno.ENOSYS + + def symlink ( self, targetPath, linkPath ): + print '*** symlink', targetPath, linkPath + return -errno.ENOSYS + + def truncate ( self, path, size ): + print '*** truncate', path, size + return -errno.ENOSYS + + def utime ( self, path, times ): + print '*** utime', path, times + return -errno.ENOSYS + +# This isn't actually used yet +class NsfsFile(object): + + def init(self, path, flags, mode=None): + + # if mode != None: NEW FILE + + # we want this in the form 'module/submod/stuff' + l = path.strip('/').split('/') + + # recursively check the namespace + try: + l[0] = globals()[l[0]] + self.thing = reduce(getattr, l) + except: + return -errno.ENOENT # nope, not there + self.tstr = str(self.thing) + self.tfile = StringIO(self.tfile) + + def read(self, len, offset): + #return -errno.ENOSYS + return read(self.tfile, len, offset) + + def release(self, flags): + self.tfile.close() + +def main(): + usage=""" +Experimental Python Namespace Filesystem + +""" + Fuse.fusage + server = NamespaceFS(version="%prog " + fuse.__version__, + usage=usage, + dash_s_do='setsingle') + server.parse(errex=1) + server.flags = 0 + server.multithreaded = 0 + print "let's go!" + server.main() + +if __name__ == '__main__': + main() |