root/trunk/source/pyhesiodfs/pyHesiodFS.py

Revision 160, 6.4 kB (checked in by broder, 15 years ago)

Allow users to delete mounts

  • 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 | 0777
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_uid = self._user()
110                 st.st_nlink = 1
111                 st.st_size = len(self.findLocker(path[1:]))
112             else:
113                 return -errno.ENOENT
114         else:
115             return -errno.ENOENT
116         if new_fuse:
117             return st
118         else:
119             return st.toTuple()
120
121     def getCachedLockers(self):
122         return self.mounts[self._user()].keys()
123
124     def findLocker(self, name):
125         """Lookup a locker in hesiod and return its path"""
126         if name in self.mounts[self._user()]:
127             return self.mounts[self._user()][name]
128         else:
129             try:
130                 filsys = hesiod.FilsysLookup(name)
131             except IOError, e:
132                 if e.errno in (errno.ENOENT, errno.EMSGSIZE):
133                     raise IOError(errno.ENOENT, os.strerror(errno.ENOENT))
134                 else:
135                     raise IOError(errno.EIO, os.strerror(errno.EIO))
136             # FIXME check if the first locker is valid
137             if len(filsys.filsys) >= 1:
138                 pointers = filsys.filsys
139                 pointer = pointers[0]
140                 if pointer['type'] != 'AFS' and pointer['type'] != 'LOC':
141                     syslog(LOG_NOTICE, "Unknown locker type "+pointer['type']+" for locker "+name+" ("+repr(pointer)+" )")
142                     return None
143                 else:
144                     self.mounts[self._user()][name] = pointer['location']
145                     syslog(LOG_INFO, "Mounting "+name+" on "+pointer['location'])
146                     return pointer['location']
147             else:
148                 syslog(LOG_WARNING, "Couldn't find filsys for "+name)
149                 return None
150
151     def getdir(self, path):
152         return [(i, 0) for i in (['.', '..', hello_path[1:]] + self.getCachedLockers())]
153
154     def readdir(self, path, offset):
155         for (r, zero) in self.getdir(path):
156             yield fuse.Direntry(r)
157            
158     def readlink(self, path):
159         return self.findLocker(path[1:])
160
161     def open(self, path, flags):
162         if path != hello_path:
163             return -errno.ENOENT
164         accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
165         if (flags & accmode) != os.O_RDONLY:
166             return -errno.EACCES
167
168     def read(self, path, size, offset):
169         if path != hello_path:
170             return -errno.ENOENT
171         slen = len(hello_str)
172         if offset < slen:
173             if offset + size > slen:
174                 size = slen - offset
175             buf = hello_str[offset:offset+size]
176         else:
177             buf = ''
178         return buf
179    
180     def unlink(self, path):
181         if path == '/' or path == hello_path:
182             return -errno.EPERM
183         elif '/' not in path[1:]:
184             del self.mounts[self._user()][path[1:]]
185         else:
186             return -errno.EPERM
187
188 def main():
189     global hello_str
190     try:
191         usage = Fuse.fusage
192         server = PyHesiodFS(version="%prog " + fuse.__version__,
193                             usage=usage,
194                             dash_s_do='setsingle')
195         server.parse(errex=1)
196     except AttributeError:
197         usage="""
198 pyHesiodFS [mountpath] [options]
199
200 """
201         if sys.argv[1] == '-f':
202             sys.argv.pop(1)
203         server = PyHesiodFS()
204
205     hello_str = hello_str % {'mountpoint': server.parse(errex=1).mountpoint}
206     server.main()
207
208 if __name__ == '__main__':
209     main()
Note: See TracBrowser for help on using the browser.