1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 import os
18 import stat
19 import time
20 import pwd
21 import grp
22 import shutil
23 import errno
24
25 from libxyz.exceptions import VFSError
26 from libxyz.exceptions import XYZRuntimeError
27 from libxyz.exceptions import XYZValueError
28 from libxyz.vfs import vfsobj
29 from libxyz.vfs import types
30 from libxyz.vfs import util
31 from libxyz.vfs import mode
32 from libxyz.core.utils import ustring, bstring
33
35 """
36 Local VFS object is used to access local filesystem
37 """
38
39
40
42 """
43 Directory tree walker
44 @return: tuple (parent, dir, dirs, files) where:
45 parent - parent dir LocalVFSObject instance
46 dir - current LocalVFSObject instance
47 dirs - list of LocalVFSObject objects of directories
48 files - list of LocalVFSObject objects of files
49 """
50
51 try:
52 _dir, _dirs, _files = os.walk(self.path).next()
53 except StopIteration:
54 raise XYZRuntimeError(_(u"Unable to walk on %s") %
55 ustring(self.path))
56
57 _dirs.sort()
58 _files.sort()
59 _parent = self.xyz.vfs.get_parent(_dir, self.enc)
60
61 get_path = lambda x: os.path.abspath(os.path.join(self.path, x))
62
63 return [
64 _parent,
65 self,
66 [self.xyz.vfs.dispatch(get_path(x), self.enc) for x in _dirs],
67 [self.xyz.vfs.dispatch(get_path(x), self.enc) for x in _files],
68 ]
69
70
71
72 - def remove(self, recursive=True):
73 """
74 [Recursively] remove object
75 """
76
77 if self.is_dir():
78 if recursive:
79 shutil.rmtree(self.path)
80 else:
81 os.rmdir(self.path)
82 else:
83 os.unlink(self.path)
84
85
86
88 """
89 Create new dir inside object (only valid for directory object types)
90 """
91
92 if not self.is_dir():
93 raise XYZValueError(
94 _(u"Unable to create directory inside %s object type") %
95 self.ftype)
96 else:
97 os.mkdir(os.path.join(self.path, newdir))
98
99
100
101 - def copy(self, path, existcb=None, errorcb=None, save_attrs=True,
102 follow_links=False, cancel=None):
103
104 env = {
105 'override': 'abort',
106 'error': 'abort'
107 }
108
109 try:
110 if self.is_dir():
111 f = self._copy_dir
112 else:
113 f = self._copy_file
114
115 f(self.full_path, path, existcb, errorcb,
116 save_attrs, follow_links, env, cancel)
117 except XYZRuntimeError:
118
119 return False
120 else:
121 return True
122
123
124
125 - def move(self, path, existcb=None, errorcb=None, save_attrs=True,
126 follow_links=False, cancel=None):
127 """
128 Move object
129 """
130
131 def _handle_error(e, obj):
132 if env['error'] != 'skip all':
133 if errorcb:
134 try:
135 env['error'] = errorcb(obj, str(e))
136 except Exception:
137 env['error'] = 'abort'
138
139 if env['error'] == 'abort':
140 raise XYZRuntimeError()
141
142
143
144 def _move_file(src, dst, *args, **kwargs):
145 if os.path.exists(dst) and os.path.isdir(dst):
146 dstto = os.path.join(dst, os.path.basename(src))
147 else:
148 dstto = dst
149
150 if os.path.exists(dstto):
151 if env['override'] not in ('override all', 'skip all'):
152 if existcb:
153 try:
154 env['override'] = existcb(
155 self.xyz.vfs.dispatch(dstto))
156 except Exception:
157 env['override'] = 'abort'
158
159 if env['override'] == 'abort':
160 raise XYZRuntimeError()
161 elif env['override'] in ('skip', 'skip all'):
162 return False
163
164 try:
165 os.rename(src, dstto)
166
167 return True
168 except OSError, e:
169
170 if e.errno == errno.EXDEV:
171 if self._copy_file(src, dst, *args, **kwargs):
172
173 try:
174 os.unlink(src)
175 except Exception, e2:
176 _handle_error(e2, self.xyz.vfs.dispatch(src))
177 else:
178 _handle_error(e, self.xyz.vfs.dispatch(src))
179 except XYZRuntimeError:
180 raise
181 except Exception, e3:
182 _handle_error(e3, self.xyz.vfs.dispatch(src))
183
184
185
186 def _move_dir(src, dst, *args, **kwargs):
187 if os.path.exists(dst) and os.path.isdir(dst) and \
188 os.path.basename(src) != os.path.basename(dst):
189 dst = os.path.join(dst, os.path.basename(src))
190
191 if os.path.isdir(src) and not os.path.exists(dst):
192 os.makedirs(dst)
193
194 files = os.listdir(src)
195
196 cancel = kwargs.get('cancel', None)
197
198 for f in files:
199 if cancel is not None and cancel.isSet():
200 raise StopIteration()
201
202 srcobj = os.path.join(src, f)
203 dstobj = os.path.join(dst, f)
204
205 if os.path.isdir(srcobj):
206 fun = _move_dir
207 else:
208 fun = _move_file
209
210 fun(srcobj, dstobj, *args, **kwargs)
211
212 try:
213 os.rmdir(src)
214 except Exception, e:
215 _handle_error(e, self.xyz.vfs.dispatch(src))
216
217 return True
218
219
220
221 env = {
222 'override': 'abort',
223 'error': 'abort'
224 }
225
226 try:
227 if self.is_dir():
228 fun = _move_dir
229 else:
230 fun = _move_file
231
232 fun(self.full_path, path, existcb, errorcb, save_attrs,
233 follow_links, env, cancel=cancel)
234 except XYZRuntimeError:
235
236 return False
237
238
239
240
242 self.ftype = self._find_type(self.path)
243 self.vtype = self.ftype.vtype
244
245 self._set_attributes()
246
247 _time = lambda x: ustring(time.ctime(x))
248
249 self.attributes = (
250 (_(u"Name"), ustring(self.name)),
251 (_(u"Type"), ustring(self.ftype)),
252 (_(u"Access time"), _time(self.atime)),
253 (_(u"Modification time"), _time(self.mtime)),
254 (_(u"Change time"), _time(self.ctime)),
255 (_(u"Size in bytes"), ustring(self.size)),
256 (_(u"Owner"), ustring(self._uid(self.uid))),
257 (_(u"Group"), ustring(self._gid(self.gid))),
258 (_(u"Access mode"), ustring(self.mode)),
259 (_(u"Inode"), ustring(self.inode)),
260 (_(u"Type-specific data"), self.data),
261 )
262
263
264
266 return "<LocalVFSObject object: %s>" % self.path
267
268
269
270 - def _uid(self, uid):
271 try:
272 _name = pwd.getpwuid(uid).pw_name
273 except (KeyError, TypeError):
274 _name = None
275
276 if _name is not None:
277 return "%s (%s)" % (bstring(uid), _name)
278 else:
279 return bstring(uid)
280
281
282
283 - def _gid(self, gid):
284 try:
285 _name = grp.getgrgid(gid).gr_name
286 except (KeyError, TypeError):
287 _name = None
288
289 if _name is not None:
290 return "%s (%s)" % (bstring(gid), _name)
291 else:
292 return bstring(gid)
293
294
295
297 """
298 Find out file type
299 """
300
301 try:
302 self._stat = os.lstat(path)
303 except OSError, e:
304 raise VFSError(_(u"Unable to stat file %s: %s" % (path, str(e))))
305
306 return util.get_file_type(self._stat.st_mode)
307
308
309
311 """
312 Set file attibutes
313 """
314
315 def set_link_attributes():
316 """
317 Set appropriate soft link attibutes
318 """
319
320 _realpath = os.readlink(self.path)
321 _fullpath = os.path.realpath(self.path)
322
323 if not os.path.exists(_fullpath):
324 self.vtype = "!"
325 else:
326 try:
327 self.data = self.xyz.vfs.dispatch(_fullpath, self.enc)
328 except VFSError, e:
329 xyzlog.error(_(u"Error creating VFS object: %s") %
330 ustring(str(e)))
331 else:
332 if isinstance(self.data.ftype, types.VFSTypeDir):
333 self.vtype = "~"
334 self.info = ""
335 self.visual = "-> %s" % _realpath
336
337
338
339 def set_char_attributes():
340 """
341 Set appropriate character device attibutes
342 """
343
344 _dev = self._stat.st_rdev
345 self.info = "%s, %s %s" % (os.major(_dev), os.minor(_dev),
346 self.mode)
347
348
349
350 self.atime = self._stat.st_atime
351 self.mtime = self._stat.st_mtime
352 self.ctime = self._stat.st_ctime
353 self.size = self._stat.st_size
354 self.uid = self._stat.st_uid
355 self.gid = self._stat.st_gid
356 self.inode = self._stat.st_ino
357 self.mode = mode.Mode(self._stat.st_mode, self.ftype)
358 self.visual = "%s%s" % (self.vtype, self.name)
359 self.info = "%s %s" % (util.format_size(self.size), self.mode)
360
361 if self.is_link():
362 set_link_attributes()
363 elif self.is_char():
364 set_char_attributes()
365 elif self.is_file():
366 _mode = stat.S_IMODE(self.mode.raw)
367
368
369 if _mode & 0111:
370 self.vtype = "*"
371 self.visual = "*%s" % self.name
372
373
374
375 - def _copy_file(self, src, dst, existcb, errorcb, save_attrs,
376 follow_links, env, cancel=None):
377 """
378 File-to-file copy
379 """
380
381 if os.path.exists(dst) and os.path.isdir(dst):
382 dstto = os.path.join(dst, os.path.basename(src))
383 else:
384 dstto = dst
385
386 if os.path.exists(dstto):
387 if env['override'] not in ('override all', 'skip all'):
388 if existcb:
389 try:
390 env['override'] = existcb(
391 self.xyz.vfs.dispatch(dstto))
392 except Exception:
393 env['override'] = 'abort'
394
395 if env['override'] == 'abort':
396 raise XYZRuntimeError()
397 elif env['override'] in ('skip', 'skip all'):
398 return False
399
400 try:
401 if not follow_links and os.path.islink(src):
402 linkto = os.readlink(src)
403 os.symlink(linkto, dstto)
404 else:
405 if save_attrs:
406 fun = shutil.copy2
407 else:
408 fun = shutil.copyfile
409
410 fun(src, dstto)
411
412 return True
413 except Exception, e:
414 if env['error'] != 'skip all':
415 if errorcb:
416 try:
417 env['error'] = errorcb(
418 self.xyz.vfs.dispatch(src), str(e))
419 except Exception:
420 env['error'] = 'abort'
421
422 if env['error'] == 'abort':
423 raise XYZRuntimeError()
424
425 return False
426
427
428
429 - def _copy_dir(self, src, dst, existcb, errorcb, save_attrs,
430 follow_links, env, cancel=None):
431 """
432 Dir-to-dir copy
433 """
434
435 if os.path.exists(dst) and os.path.isdir(dst) and \
436 os.path.basename(src) != os.path.basename(dst):
437 dst = os.path.join(dst, os.path.basename(src))
438
439 if not follow_links and os.path.islink(src):
440 linkto = os.readlink(src)
441 os.symlink(linkto, dst)
442
443 return True
444
445 if os.path.isdir(src) and not os.path.exists(dst):
446 os.makedirs(dst)
447
448 files = os.listdir(src)
449
450 for f in files:
451 if cancel is not None and cancel.isSet():
452 raise StopIteration()
453
454 srcobj = os.path.join(src, f)
455 dstobj = os.path.join(dst, f)
456
457 if os.path.isdir(srcobj):
458 fun = self._copy_dir
459 else:
460 fun = self._copy_file
461
462 fun(srcobj, dstobj, existcb, errorcb, save_attrs,
463 follow_links, env, cancel)
464
465 if os.path.isdir(src) and save_attrs:
466 shutil.copystat(src, dst)
467