blob: 510ac7683859952b12c474f1585d2b82319f7478 [file] [log] [blame]
Guido van Rossum07a272d1995-04-10 11:40:52 +00001"""File System Proxy.
2
3Provide an OS-neutral view on a file system, locally or remotely.
4The functionality is geared towards implementing some sort of
5rdist-like utility between a Mac and a UNIX system.
6
7The module defines three classes:
8
9FSProxyLocal -- used for local access
10FSProxyServer -- used on the server side of remote access
11FSProxyClient -- used on the client side of remote access
12
13The remote classes are instantiated with an IP address and an optional
14verbosity flag.
15"""
16
17import server
18import client
19import md5
20import os
21import fnmatch
22from stat import *
23import time
24import fnmatch
25
26if os.name == 'mac':
Tim Peterse6ddc8b2004-07-18 05:56:09 +000027 import macfs
28 maxnamelen = 31
Guido van Rossum07a272d1995-04-10 11:40:52 +000029else:
Tim Peterse6ddc8b2004-07-18 05:56:09 +000030 macfs = None
31 maxnamelen = 255
Guido van Rossum07a272d1995-04-10 11:40:52 +000032
33skipnames = (os.curdir, os.pardir)
34
35
36class FSProxyLocal:
Tim Peterse6ddc8b2004-07-18 05:56:09 +000037
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
Guido van Rossumb940e112007-01-10 16:19:56 +000090 except macfs.error as msg:
Collin Winter6f2df4d2007-07-17 20:59:35 +000091 print("***", name, msg)
Tim Peterse6ddc8b2004-07-18 05:56:09 +000092 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):
Collin Winter6f2df4d2007-07-17 20:59:35 +0000102 raise os.error("protected name %s" % repr(name))
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000103
104 def checkfile(self, name):
105 self.check(name)
106 if not os.path.isfile(name):
Collin Winter6f2df4d2007-07-17 20:59:35 +0000107 raise os.error("not a plain file %s" % repr(name))
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000108
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:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000121 raise os.error("empty directory stack")
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000122 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)
Collin Winter6f2df4d2007-07-17 20:59:35 +0000131 files = list(filter(keep, files))
132 files = list(filter(self.visible, files))
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000133 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)
Collin Winter6f2df4d2007-07-17 20:59:35 +0000142 files = list(filter(os.path.isfile, files))
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000143 return self._filter(files, pat)
144
145 def listsubdirs(self, pat = None):
146 files = os.listdir(os.curdir)
Collin Winter6f2df4d2007-07-17 20:59:35 +0000147 files = list(filter(os.path.isdir, files))
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000148 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)
Collin Winter6f2df4d2007-07-17 20:59:35 +0000281 os.mkdir(name, 0o777)
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000282
283 def rmdir(self, name):
284 self.check(name)
285 os.rmdir(name)
Guido van Rossum07a272d1995-04-10 11:40:52 +0000286
287
288class FSProxyServer(FSProxyLocal, server.Server):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000289
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()
Guido van Rossum07a272d1995-04-10 11:40:52 +0000302
303
304class FSProxyClient(client.Client):
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000305
306 def __init__(self, address, verbose = client.VERBOSE):
307 client.Client.__init__(self, address, verbose)
Guido van Rossum07a272d1995-04-10 11:40:52 +0000308
309
310def test():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000311 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()
Guido van Rossum07a272d1995-04-10 11:40:52 +0000319
320
321if __name__ == '__main__':
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000322 test()