root/trunk/source/pyhesiodfs/pyHesiodFS.py

Revision 159, 6.1 kB (checked in by broder, 15 years ago)

Make pyHesiodFS cache mounted filesystems on a per-user basis

  • Property svn:executable set to *
Line 
1 #!/usr/bin/python2.5
2
3 #    pyHesiodFS:
4 #    Copyright (C) 2007  Quentin Smith <quentin@mit.edu>
5 #    "Hello World" pyFUSE example:
6 #    Copyright (C) 2006  Andrew Straw  <strawman@astraw.com>
7 #
8 #    This program can be distributed under the terms of the GNU LGPL.
9 #    See the file COPYING.
10 #
11
12 import sys, os, stat, errno
13 from syslog import *
14 import fuse
15 from fuse import Fuse
16
17 import hesiod
18
19 try:
20     from collections import defaultdict
21 except ImportError:
22     class defaultdict(dict):
23         """
24         A dictionary that automatically will fill in keys that don't exist
25         with the result from some default value factory
26        
27         Based on the collections.defaultdict object in Python 2.5
28         """
29        
30         def __init__(self, default_factory):
31             self.default_factory = default_factory
32             super(defaultdict, self).__init__()
33        
34         def __getitem__(self, y):
35             if y not in self:
36                 self[y] = self.default_factory()
37             return super(defaultdict, self).__getitem__(y)
38        
39         def __str__(self):
40             print 'defaultdict(%s, %s)' % (self.default_factory,
41                                            super(defaultdict, self).__str__())
42        
43 new_fuse = hasattr(fuse, '__version__')
44
45 fuse.fuse_python_api = (0, 2)
46
47 hello_path = '/README.txt'
48 hello_str = """This is the pyhesiodfs FUSE autmounter. To access a Hesiod filsys, just access
49 %(mountpoint)s/name.
50
51 If you're using the Finder, try pressing Cmd+Shift+G and then entering
52 %(mountpoint)s/name"""
53
54 if not hasattr(fuse, 'Stat'):
55     fuse.Stat = object
56
57 class MyStat(fuse.Stat):
58     def __init__(self):
59         self.st_mode = 0
60         self.st_ino = 0
61         self.st_dev = 0
62         self.st_nlink = 0
63         self.st_uid = 0
64         self.st_gid = 0
65         self.st_size = 0
66         self.st_atime = 0
67         self.st_mtime = 0
68         self.st_ctime = 0
69
70     def toTuple(self):
71         return (self.st_mode, self.st_ino, self.st_dev, self.st_nlink,
72                 self.st_uid, self.st_gid, self.st_size, self.st_atime,
73                 self.st_mtime, self.st_ctime)
74
75 class PyHesiodFS(Fuse):
76
77     def __init__(self, *args, **kwargs):
78         Fuse.__init__(self, *args, **kwargs)
79        
80         openlog('pyhesiodfs', 0, LOG_DAEMON)
81        
82         try:
83             self.fuse_args.add("allow_other", True)
84         except AttributeError:
85             self.allow_other = 1
86
87         if sys.platform == 'darwin':
88             self.fuse_args.add("noappledouble", True)
89             self.fuse_args.add("noapplexattr", True)
90             self.fuse_args.add("volname", "MIT")
91             self.fuse_args.add("fsname", "pyHesiodFS")
92         self.mounts = defaultdict(dict)
93    
94     def _user(self):
95         return fuse.FuseGetContext()['uid']
96    
97     def getattr(self, path):
98         st = MyStat()
99         if path == '/':
100             st.st_mode = stat.S_IFDIR | 0755
101             st.st_nlink = 2
102         elif path == hello_path:
103             st.st_mode = stat.S_IFREG | 0444
104             st.st_nlink = 1
105             st.st_size = len(hello_str)
106         elif '/' not in path[1:]:
107             if self.findLocker(path[1:]):
108                 st.st_mode = stat.S_IFLNK | 0777
109                 st.st_nlink = 1
110                 st.st_size = len(self.findLocker(path[1:]))
111             else:
112                 return -errno.ENOENT
113         else:
114             return -errno.ENOENT
115         if new_fuse:
116             return st
117         else:
118             return st.toTuple()
119
120     def getCachedLockers(self):
121         return self.mounts[self._user()].keys()
122
123     def findLocker(self, name):
124         """Lookup a locker in hesiod and return its path"""
125         if name in self.mounts[self._user()]:
126             return self.mounts[self._user()][name]
127         else:
128             try:
129                 filsys = hesiod.FilsysLookup(name)
130             except IOError, e:
131                 if e.errno in (errno.ENOENT, errno.EMSGSIZE):
132                     raise IOError(errno.ENOENT, os.strerror(errno.ENOENT))
133                 else:
134                     raise IOError(errno.EIO, os.strerror(errno.EIO))
135             # FIXME check if the first locker is valid
136             if len(filsys.filsys) >= 1:
137                 pointers = filsys.filsys
138                 pointer = pointers[0]
139                 if pointer['type'] != 'AFS' and pointer['type'] != 'LOC':
140                     syslog(LOG_NOTICE, "Unknown locker type "+pointer['type']+" for locker "+name+" ("+repr(pointer)+" )")
141                     return None
142                 else:
143                     self.mounts[self._user()][name] = pointer['location']
144                     syslog(LOG_INFO, "Mounting "+name+" on "+pointer['location'])
145                     return pointer['location']
146             else:
147                 syslog(LOG_WARNING, "Couldn't find filsys for "+name)
148                 return None
149
150     def getdir(self, path):
151         return [(i, 0) for i in (['.', '..', hello_path[1:]] + self.getCachedLockers())]
152
153     def readdir(self, path, offset):
154         for (r, zero) in self.getdir(path):
155             yield fuse.Direntry(r)
156            
157     def readlink(self, path):
158         return self.findLocker(path[1:])
159
160     def open(self, path, flags):
161         if path != hello_path:
162             return -errno.ENOENT
163         accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
164         if (flags & accmode) != os.O_RDONLY:
165             return -errno.EACCES
166
167     def read(self, path, size, offset):
168         if path != hello_path:
169             return -errno.ENOENT
170         slen = len(hello_str)
171         if offset < slen:
172             if offset + size > slen:
173                 size = slen - offset
174             buf = hello_str[offset:offset+size]
175         else:
176             buf = ''
177         return buf
178
179 def main():
180     global hello_str
181     try:
182         usage = Fuse.fusage
183         server = PyHesiodFS(version="%prog " + fuse.__version__,
184                             usage=usage,
185                             dash_s_do='setsingle')
186         server.parse(errex=1)
187     except AttributeError:
188         usage="""
189 pyHesiodFS [mountpath] [options]
190
191 """
192         if sys.argv[1] == '-f':
193             sys.argv.pop(1)
194         server = PyHesiodFS()
195
196     hello_str = hello_str % {'mountpoint': server.parse(errex=1).mountpoint}
197     server.main()
198
199 if __name__ == '__main__':
200     main()
Note: See TracBrowser for help on using the browser.