root/trunk/source/pyhesiodfs/pyHesiodFS.py

Revision 162, 7.6 kB (checked in by broder, 15 years ago)

Let users create their own symlinks.

  • 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, time
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 class negcache(dict):
44     """
45     A set-like object that automatically expunges entries after
46     they're been there for a certain amount of time.
47    
48     This only supports add, remove, and __contains__
49     """
50    
51     def __init__(self, cache_time):
52         self.cache_time = cache_time
53    
54     def add(self, obj):
55         self[obj] = time.time()
56    
57     def remove(self, obj):
58         del self[obj]
59    
60     def __contains__(self, k):
61         if super(negcache, self).__contains__(k):
62             if self[k] + self.cache_time > time.time():
63                 return True
64             else:
65                 del self[k]
66         return False
67
68 new_fuse = hasattr(fuse, '__version__')
69
70 fuse.fuse_python_api = (0, 2)
71
72 hello_path = '/README.txt'
73 hello_str = """This is the pyhesiodfs FUSE autmounter. To access a Hesiod filsys, just access
74 %(mountpoint)s/name.
75
76 If you're using the Finder, try pressing Cmd+Shift+G and then entering
77 %(mountpoint)s/name"""
78
79 if not hasattr(fuse, 'Stat'):
80     fuse.Stat = object
81
82 class MyStat(fuse.Stat):
83     def __init__(self):
84         self.st_mode = 0
85         self.st_ino = 0
86         self.st_dev = 0
87         self.st_nlink = 0
88         self.st_uid = 0
89         self.st_gid = 0
90         self.st_size = 0
91         self.st_atime = 0
92         self.st_mtime = 0
93         self.st_ctime = 0
94
95     def toTuple(self):
96         return (self.st_mode, self.st_ino, self.st_dev, self.st_nlink,
97                 self.st_uid, self.st_gid, self.st_size, self.st_atime,
98                 self.st_mtime, self.st_ctime)
99
100 class PyHesiodFS(Fuse):
101
102     def __init__(self, *args, **kwargs):
103         Fuse.__init__(self, *args, **kwargs)
104        
105         openlog('pyhesiodfs', 0, LOG_DAEMON)
106        
107         try:
108             self.fuse_args.add("allow_other", True)
109         except AttributeError:
110             self.allow_other = 1
111
112         if sys.platform == 'darwin':
113             self.fuse_args.add("noappledouble", True)
114             self.fuse_args.add("noapplexattr", True)
115             self.fuse_args.add("volname", "MIT")
116             self.fuse_args.add("fsname", "pyHesiodFS")
117         self.mounts = defaultdict(dict)
118        
119         # Cache deletions for 10 seconds - should give people time to
120         # make a new symlink
121         self.negcache = negcache(10)
122    
123     def _user(self):
124         return fuse.FuseGetContext()['uid']
125    
126     def getattr(self, path):
127         st = MyStat()
128         if path == '/':
129             st.st_mode = stat.S_IFDIR | 0777
130             st.st_nlink = 2
131         elif path == hello_path:
132             st.st_mode = stat.S_IFREG | 0444
133             st.st_nlink = 1
134             st.st_size = len(hello_str)
135         elif '/' not in path[1:]:
136             if path[1:] not in self.negcache and self.findLocker(path[1:]):
137                 st.st_mode = stat.S_IFLNK | 0777
138                 st.st_uid = self._user()
139                 st.st_nlink = 1
140                 st.st_size = len(self.findLocker(path[1:]))
141             else:
142                 return -errno.ENOENT
143         else:
144             return -errno.ENOENT
145         if new_fuse:
146             return st
147         else:
148             return st.toTuple()
149
150     def getCachedLockers(self):
151         return self.mounts[self._user()].keys()
152
153     def findLocker(self, name):
154         """Lookup a locker in hesiod and return its path"""
155         if name in self.mounts[self._user()]:
156             return self.mounts[self._user()][name]
157         else:
158             try:
159                 filsys = hesiod.FilsysLookup(name)
160             except IOError, e:
161                 if e.errno in (errno.ENOENT, errno.EMSGSIZE):
162                     raise IOError(errno.ENOENT, os.strerror(errno.ENOENT))
163                 else:
164                     raise IOError(errno.EIO, os.strerror(errno.EIO))
165             # FIXME check if the first locker is valid
166             if len(filsys.filsys) >= 1:
167                 pointers = filsys.filsys
168                 pointer = pointers[0]
169                 if pointer['type'] != 'AFS' and pointer['type'] != 'LOC':
170                     syslog(LOG_NOTICE, "Unknown locker type "+pointer['type']+" for locker "+name+" ("+repr(pointer)+" )")
171                     return None
172                 else:
173                     self.mounts[self._user()][name] = pointer['location']
174                     syslog(LOG_INFO, "Mounting "+name+" on "+pointer['location'])
175                     return pointer['location']
176             else:
177                 syslog(LOG_WARNING, "Couldn't find filsys for "+name)
178                 return None
179
180     def getdir(self, path):
181         return [(i, 0) for i in (['.', '..', hello_path[1:]] + self.getCachedLockers())]
182
183     def readdir(self, path, offset):
184         for (r, zero) in self.getdir(path):
185             yield fuse.Direntry(r)
186            
187     def readlink(self, path):
188         return self.findLocker(path[1:])
189
190     def open(self, path, flags):
191         if path != hello_path:
192             return -errno.ENOENT
193         accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
194         if (flags & accmode) != os.O_RDONLY:
195             return -errno.EACCES
196
197     def read(self, path, size, offset):
198         if path != hello_path:
199             return -errno.ENOENT
200         slen = len(hello_str)
201         if offset < slen:
202             if offset + size > slen:
203                 size = slen - offset
204             buf = hello_str[offset:offset+size]
205         else:
206             buf = ''
207         return buf
208    
209     def symlink(self, src, path):
210         if path == '/' or path == hello_path:
211             return -errno.EPERM
212         elif '/' not in path[1:]:
213             self.mounts[self._user()][path[1:]] = src
214             self.negcache.remove(path[1:])
215             print self.mounts[self._user()]
216         else:
217             return -errno.EPERM
218    
219     def unlink(self, path):
220         if path == '/' or path == hello_path:
221             return -errno.EPERM
222         elif '/' not in path[1:]:
223             del self.mounts[self._user()][path[1:]]
224             self.negcache.add(path[1:])
225         else:
226             return -errno.EPERM
227
228 def main():
229     global hello_str
230     try:
231         usage = Fuse.fusage
232         server = PyHesiodFS(version="%prog " + fuse.__version__,
233                             usage=usage,
234                             dash_s_do='setsingle')
235         server.parse(errex=1)
236     except AttributeError:
237         usage="""
238 pyHesiodFS [mountpath] [options]
239
240 """
241         if sys.argv[1] == '-f':
242             sys.argv.pop(1)
243         server = PyHesiodFS()
244
245     hello_str = hello_str % {'mountpoint': server.parse(errex=1).mountpoint}
246     server.main()
247
248 if __name__ == '__main__':
249     main()
Note: See TracBrowser for help on using the browser.