|
2652
|
1 """File System Proxy.
|
|
|
2
|
|
|
3 Provide an OS-neutral view on a file system, locally or remotely.
|
|
|
4 The functionality is geared towards implementing some sort of
|
|
|
5 rdist-like utility between a Mac and a UNIX system.
|
|
|
6
|
|
|
7 The module defines three classes:
|
|
|
8
|
|
|
9 FSProxyLocal -- used for local access
|
|
|
10 FSProxyServer -- used on the server side of remote access
|
|
|
11 FSProxyClient -- used on the client side of remote access
|
|
|
12
|
|
|
13 The remote classes are instantiated with an IP address and an optional
|
|
|
14 verbosity flag.
|
|
|
15 """
|
|
|
16
|
|
|
17 import server
|
|
|
18 import client
|
|
|
19 import md5
|
|
|
20 import os
|
|
|
21 import fnmatch
|
|
|
22 from stat import *
|
|
|
23 import time
|
|
|
24 import fnmatch
|
|
|
25
|
|
|
26 if os.name == 'mac':
|
|
|
27 import macfs
|
|
|
28 maxnamelen = 31
|
|
|
29 else:
|
|
|
30 macfs = None
|
|
|
31 maxnamelen = 255
|
|
|
32
|
|
|
33 skipnames = (os.curdir, os.pardir)
|
|
|
34
|
|
|
35
|
|
|
36 class FSProxyLocal:
|
|
|
37
|
|
|
38 def __init__(self):
|
|
|
39 self._dirstack = []
|
|
|
40 self._ignore = ['*.pyc'] + self._readignore()
|
|
|
41
|
|
|
42 def _close(self):
|
|
|
43 while self._dirstack:
|
|
|
44 self.back()
|
|
|
45
|
|
|
46 def _readignore(self):
|
|
|
47 file = self._hide('ignore')
|
|
|
48 try:
|
|
|
49 f = open(file)
|
|
|
50 except IOError:
|
|
|
51 file = self._hide('synctree.ignorefiles')
|
|
|
52 try:
|
|
|
53 f = open(file)
|
|
|
54 except IOError:
|
|
|
55 return []
|
|
|
56 ignore = []
|
|
|
57 while 1:
|
|
|
58 line = f.readline()
|
|
|
59 if not line: break
|
|
|
60 if line[-1] == '\n': line = line[:-1]
|
|
|
61 ignore.append(line)
|
|
|
62 f.close()
|
|
|
63 return ignore
|
|
|
64
|
|
|
65 def _hidden(self, name):
|
|
|
66 if os.name == 'mac':
|
|
|
67 return name[0] == '(' and name[-1] == ')'
|
|
|
68 else:
|
|
|
69 return name[0] == '.'
|
|
|
70
|
|
|
71 def _hide(self, name):
|
|
|
72 if os.name == 'mac':
|
|
|
73 return '(%s)' % name
|
|
|
74 else:
|
|
|
75 return '.%s' % name
|
|
|
76
|
|
|
77 def visible(self, name):
|
|
|
78 if len(name) > maxnamelen: return 0
|
|
|
79 if name[-1] == '~': return 0
|
|
|
80 if name in skipnames: return 0
|
|
|
81 if self._hidden(name): return 0
|
|
|
82 head, tail = os.path.split(name)
|
|
|
83 if head or not tail: return 0
|
|
|
84 if macfs:
|
|
|
85 if os.path.exists(name) and not os.path.isdir(name):
|
|
|
86 try:
|
|
|
87 fs = macfs.FSSpec(name)
|
|
|
88 c, t = fs.GetCreatorType()
|
|
|
89 if t != 'TEXT': return 0
|
|
|
90 except macfs.error, msg:
|
|
|
91 print "***", name, msg
|
|
|
92 return 0
|
|
|
93 else:
|
|
|
94 if os.path.islink(name): return 0
|
|
|
95 if '\0' in open(name, 'rb').read(512): return 0
|
|
|
96 for ign in self._ignore:
|
|
|
97 if fnmatch.fnmatch(name, ign): return 0
|
|
|
98 return 1
|
|
|
99
|
|
|
100 def check(self, name):
|
|
|
101 if not self.visible(name):
|
|
|
102 raise os.error, "protected name %s" % repr(name)
|
|
|
103
|
|
|
104 def checkfile(self, name):
|
|
|
105 self.check(name)
|
|
|
106 if not os.path.isfile(name):
|
|
|
107 raise os.error, "not a plain file %s" % repr(name)
|
|
|
108
|
|
|
109 def pwd(self):
|
|
|
110 return os.getcwd()
|
|
|
111
|
|
|
112 def cd(self, name):
|
|
|
113 self.check(name)
|
|
|
114 save = os.getcwd(), self._ignore
|
|
|
115 os.chdir(name)
|
|
|
116 self._dirstack.append(save)
|
|
|
117 self._ignore = self._ignore + self._readignore()
|
|
|
118
|
|
|
119 def back(self):
|
|
|
120 if not self._dirstack:
|
|
|
121 raise os.error, "empty directory stack"
|
|
|
122 dir, ignore = self._dirstack[-1]
|
|
|
123 os.chdir(dir)
|
|
|
124 del self._dirstack[-1]
|
|
|
125 self._ignore = ignore
|
|
|
126
|
|
|
127 def _filter(self, files, pat = None):
|
|
|
128 if pat:
|
|
|
129 def keep(name, pat = pat):
|
|
|
130 return fnmatch.fnmatch(name, pat)
|
|
|
131 files = filter(keep, files)
|
|
|
132 files = filter(self.visible, files)
|
|
|
133 files.sort()
|
|
|
134 return files
|
|
|
135
|
|
|
136 def list(self, pat = None):
|
|
|
137 files = os.listdir(os.curdir)
|
|
|
138 return self._filter(files, pat)
|
|
|
139
|
|
|
140 def listfiles(self, pat = None):
|
|
|
141 files = os.listdir(os.curdir)
|
|
|
142 files = filter(os.path.isfile, files)
|
|
|
143 return self._filter(files, pat)
|
|
|
144
|
|
|
145 def listsubdirs(self, pat = None):
|
|
|
146 files = os.listdir(os.curdir)
|
|
|
147 files = filter(os.path.isdir, files)
|
|
|
148 return self._filter(files, pat)
|
|
|
149
|
|
|
150 def exists(self, name):
|
|
|
151 return self.visible(name) and os.path.exists(name)
|
|
|
152
|
|
|
153 def isdir(self, name):
|
|
|
154 return self.visible(name) and os.path.isdir(name)
|
|
|
155
|
|
|
156 def islink(self, name):
|
|
|
157 return self.visible(name) and os.path.islink(name)
|
|
|
158
|
|
|
159 def isfile(self, name):
|
|
|
160 return self.visible(name) and os.path.isfile(name)
|
|
|
161
|
|
|
162 def sum(self, name):
|
|
|
163 self.checkfile(name)
|
|
|
164 BUFFERSIZE = 1024*8
|
|
|
165 f = open(name)
|
|
|
166 sum = md5.new()
|
|
|
167 while 1:
|
|
|
168 buffer = f.read(BUFFERSIZE)
|
|
|
169 if not buffer:
|
|
|
170 break
|
|
|
171 sum.update(buffer)
|
|
|
172 return sum.digest()
|
|
|
173
|
|
|
174 def size(self, name):
|
|
|
175 self.checkfile(name)
|
|
|
176 return os.stat(name)[ST_SIZE]
|
|
|
177
|
|
|
178 def mtime(self, name):
|
|
|
179 self.checkfile(name)
|
|
|
180 return time.localtime(os.stat(name)[ST_MTIME])
|
|
|
181
|
|
|
182 def stat(self, name):
|
|
|
183 self.checkfile(name)
|
|
|
184 size = os.stat(name)[ST_SIZE]
|
|
|
185 mtime = time.localtime(os.stat(name)[ST_MTIME])
|
|
|
186 return size, mtime
|
|
|
187
|
|
|
188 def info(self, name):
|
|
|
189 sum = self.sum(name)
|
|
|
190 size = os.stat(name)[ST_SIZE]
|
|
|
191 mtime = time.localtime(os.stat(name)[ST_MTIME])
|
|
|
192 return sum, size, mtime
|
|
|
193
|
|
|
194 def _list(self, function, list):
|
|
|
195 if list is None:
|
|
|
196 list = self.listfiles()
|
|
|
197 res = []
|
|
|
198 for name in list:
|
|
|
199 try:
|
|
|
200 res.append((name, function(name)))
|
|
|
201 except (os.error, IOError):
|
|
|
202 res.append((name, None))
|
|
|
203 return res
|
|
|
204
|
|
|
205 def sumlist(self, list = None):
|
|
|
206 return self._list(self.sum, list)
|
|
|
207
|
|
|
208 def statlist(self, list = None):
|
|
|
209 return self._list(self.stat, list)
|
|
|
210
|
|
|
211 def mtimelist(self, list = None):
|
|
|
212 return self._list(self.mtime, list)
|
|
|
213
|
|
|
214 def sizelist(self, list = None):
|
|
|
215 return self._list(self.size, list)
|
|
|
216
|
|
|
217 def infolist(self, list = None):
|
|
|
218 return self._list(self.info, list)
|
|
|
219
|
|
|
220 def _dict(self, function, list):
|
|
|
221 if list is None:
|
|
|
222 list = self.listfiles()
|
|
|
223 dict = {}
|
|
|
224 for name in list:
|
|
|
225 try:
|
|
|
226 dict[name] = function(name)
|
|
|
227 except (os.error, IOError):
|
|
|
228 pass
|
|
|
229 return dict
|
|
|
230
|
|
|
231 def sumdict(self, list = None):
|
|
|
232 return self.dict(self.sum, list)
|
|
|
233
|
|
|
234 def sizedict(self, list = None):
|
|
|
235 return self.dict(self.size, list)
|
|
|
236
|
|
|
237 def mtimedict(self, list = None):
|
|
|
238 return self.dict(self.mtime, list)
|
|
|
239
|
|
|
240 def statdict(self, list = None):
|
|
|
241 return self.dict(self.stat, list)
|
|
|
242
|
|
|
243 def infodict(self, list = None):
|
|
|
244 return self._dict(self.info, list)
|
|
|
245
|
|
|
246 def read(self, name, offset = 0, length = -1):
|
|
|
247 self.checkfile(name)
|
|
|
248 f = open(name)
|
|
|
249 f.seek(offset)
|
|
|
250 if length == 0:
|
|
|
251 data = ''
|
|
|
252 elif length < 0:
|
|
|
253 data = f.read()
|
|
|
254 else:
|
|
|
255 data = f.read(length)
|
|
|
256 f.close()
|
|
|
257 return data
|
|
|
258
|
|
|
259 def create(self, name):
|
|
|
260 self.check(name)
|
|
|
261 if os.path.exists(name):
|
|
|
262 self.checkfile(name)
|
|
|
263 bname = name + '~'
|
|
|
264 try:
|
|
|
265 os.unlink(bname)
|
|
|
266 except os.error:
|
|
|
267 pass
|
|
|
268 os.rename(name, bname)
|
|
|
269 f = open(name, 'w')
|
|
|
270 f.close()
|
|
|
271
|
|
|
272 def write(self, name, data, offset = 0):
|
|
|
273 self.checkfile(name)
|
|
|
274 f = open(name, 'r+')
|
|
|
275 f.seek(offset)
|
|
|
276 f.write(data)
|
|
|
277 f.close()
|
|
|
278
|
|
|
279 def mkdir(self, name):
|
|
|
280 self.check(name)
|
|
|
281 os.mkdir(name, 0777)
|
|
|
282
|
|
|
283 def rmdir(self, name):
|
|
|
284 self.check(name)
|
|
|
285 os.rmdir(name)
|
|
|
286
|
|
|
287
|
|
|
288 class FSProxyServer(FSProxyLocal, server.Server):
|
|
|
289
|
|
|
290 def __init__(self, address, verbose = server.VERBOSE):
|
|
|
291 FSProxyLocal.__init__(self)
|
|
|
292 server.Server.__init__(self, address, verbose)
|
|
|
293
|
|
|
294 def _close(self):
|
|
|
295 server.Server._close(self)
|
|
|
296 FSProxyLocal._close(self)
|
|
|
297
|
|
|
298 def _serve(self):
|
|
|
299 server.Server._serve(self)
|
|
|
300 # Retreat into start directory
|
|
|
301 while self._dirstack: self.back()
|
|
|
302
|
|
|
303
|
|
|
304 class FSProxyClient(client.Client):
|
|
|
305
|
|
|
306 def __init__(self, address, verbose = client.VERBOSE):
|
|
|
307 client.Client.__init__(self, address, verbose)
|
|
|
308
|
|
|
309
|
|
|
310 def test():
|
|
|
311 import string
|
|
|
312 import sys
|
|
|
313 if sys.argv[1:]:
|
|
|
314 port = string.atoi(sys.argv[1])
|
|
|
315 else:
|
|
|
316 port = 4127
|
|
|
317 proxy = FSProxyServer(('', port))
|
|
|
318 proxy._serverloop()
|
|
|
319
|
|
|
320
|
|
|
321 if __name__ == '__main__':
|
|
|
322 test()
|