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() |
---|