1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 import os
18 import stat
19 import tarfile
20 import time
21
22 from libxyz.exceptions import XYZRuntimeError
23 from libxyz.core.utils import ustring
24 from libxyz.vfs import types as vfstypes
25 from libxyz.vfs import vfsobj
26 from libxyz.vfs import util
27 from libxyz.vfs import mode
28 from libxyz.ui import BlockEntries
29
31 """
32 Tar archive interface
33 """
34
36 if self.root:
37 return a
38 else:
39 return b()
40
41
42
43 get_name = lambda self, x: os.path.basename(x.name.rstrip(os.path.sep))
44 get_path = lambda self, x: os.path.join(self.ext_path, x.lstrip(os.sep))
45 get_int_path = lambda self, x: x.lstrip(os.sep)
46
47 file_type_map = {
48 lambda obj: obj.isfile(): vfstypes.VFSTypeFile(),
49 lambda obj: obj.isdir(): vfstypes.VFSTypeDir(),
50 lambda obj: obj.issym(): vfstypes.VFSTypeLink(),
51 lambda obj: obj.ischr(): vfstypes.VFSTypeChar(),
52 lambda obj: obj.isblk(): vfstypes.VFSTypeBlock(),
53 lambda obj: obj.isfifo(): vfstypes.VFSTypeFifo(),
54 }
55
57 self.tarobj = None
58 self.members = None
59
60 super(TarVFSObject, self).__init__(*args, **kwargs)
61
62
63
65 """
66 Directory tree walker
67 @return: tuple (parent, dir, objects) where:
68 parent - parent dir *VFSObject instance
69 dir - current dir TarVFSObject instance
70 objects - BlockEntries of TarVFSObject objects
71 """
72
73 dirs, files = [], []
74 self._open_archive()
75
76 for x in self.members.values():
77 if self.in_dir(self.path, x.name):
78 if x.isdir():
79 dirs.append(x)
80 else:
81 files.append(x)
82
83 dirs.sort(cmp=lambda x, y: cmp(self.get_name(x),
84 self.get_name(y)))
85 files.sort(cmp=lambda x, y: cmp(self.get_name(x),
86 self.get_name(y)))
87
88 if self.path == os.sep:
89 _parent = self.xyz.vfs.get_parent(self.parent.full_path, self.enc)
90 else:
91 _parent = self.xyz.vfs.dispatch(
92 self.get_path(os.path.dirname(self.path)), self.enc)
93 _parent.name = ".."
94
95 return [
96 _parent,
97 self,
98 BlockEntries(self.xyz, dirs + files,
99 lambda x: self.get_path(x.name))]
100
101
102
103 - def copy(self, path, existcb=None, errorcb=None, save_attrs=True,
104 follow_links=False, cancel=None):
105
106 env = {
107 'override': 'abort',
108 'error': 'abort'
109 }
110
111 tarobj = self._open_archive()
112
113 try:
114 if self.is_dir():
115 f = self._copy_dir
116 else:
117 f = self._copy_file
118
119 f(self.path, path, existcb, errorcb,
120 save_attrs, follow_links, env, cancel, tarobj=tarobj)
121 except XYZRuntimeError:
122
123 return False
124 else:
125 return True
126
127
128
129 - def open(self, mode='r'):
130 """
131 Open self object in provided mode
132 """
133
134 if self.fileobj:
135 return self
136 else:
137 self.fileobj = self.tarobj.extractfile(
138 self.get_int_path(self.path))
139
140 return self
141
142
143
145 """
146 Close self object
147 """
148
149 if self.fileobj is None:
150 return
151 else:
152 try:
153 self.fileobj.close()
154 finally:
155 self.fileobj = None
156
157
158
159 - def read(self, bytes=None):
160 """
161 Read bytes from self object
162 """
163
164 return self.fileobj.read(bytes)
165
166
167
169 """
170 Tell file position
171 """
172
173 return self.fileobj.tell()
174
175
176
177 - def seek(self, offset, whence=0):
178 """
179 Perform seek() on object
180 """
181
182 return self.fileobj.seek(offset, whence)
183
184
185
187 if self.path == os.sep:
188 self.root = True
189 else:
190 self.root = False
191
192 (self.tarobj, self.members) = self._find_cached()
193
194 if self.root:
195 self.obj = None
196 else:
197 self.obj = self._init_obj()
198
199 self.ftype = self._find_type()
200 self.vtype = self.ftype.vtype
201
202 self._set_attributes()
203
204 self.attributes = (
205 (_(u"Name"), ustring(self.name)),
206 (_(u"Type"), ustring(self.ftype)),
207 (_(u"Modification time"), ustring(time.ctime(self.mtime))),
208 (_(u"Size in bytes"), ustring(self.size)),
209 (_(u"Owner"), ustring(self.uid)),
210 (_(u"Group"), ustring(self.gid)),
211 (_(u"Access mode"), ustring(self.mode)),
212 (_(u"Type-specific data"), self.data),
213 )
214
215
216
218 """
219 Normalize path
220 """
221
222 if path.startswith(os.sep):
223 return path.lstrip(os.sep)
224 else:
225 return path
226
227
228
230 return "<TarVFSObject object: %s>" % self.path
231
232
233
235 """
236 Filter only those archive entries which exist in the same
237 directory level
238 """
239
240 if e.startswith(d.lstrip(os.sep)) and \
241 len(util.split_path(e)) == (len(util.split_path(d)) + 1):
242 return True
243 else:
244 return False
245
246
247
249 """
250 Find out file type
251 """
252
253 if self.root:
254 return self.parent.ftype
255
256 for k, v in self.file_type_map.iteritems():
257 if k(self.obj):
258 return v
259
260 return vfstypes.VFSTypeUnknown()
261
262
263
265 """
266 Set file attibutes
267 """
268
269 def set_link_attributes():
270 """
271 Set appropriate soft link attibutes
272 """
273
274 self.info = ""
275 self.visual = "-> %s" % self.obj.linkname or ""
276
277
278
279 self.name = self.either(self.parent.name, lambda: self.name)
280 self.mtime = self.either(self.parent.mtime, lambda: self.obj.mtime)
281 self.size = self.either(self.parent.size, lambda: self.obj.size)
282 self.uid = self.either(self.parent.uid, lambda: self.obj.uid)
283 self.gid = self.either(self.parent.gid, lambda: self.obj.gid)
284 self.mode = mode.Mode(self.either(self.parent.mode.raw,
285 lambda: self.obj.mode), self.ftype)
286 self.visual = "%s%s" % (self.vtype, self.name)
287
288 self.info = "%s %s" % (util.format_size(self.size), self.mode)
289
290 if self.is_link():
291 set_link_attributes()
292 elif self.is_file():
293 _mode = stat.S_IMODE(self.mode.raw)
294
295
296 if _mode & 0111:
297 self.vtype = "*"
298 self.visual = "*%s" % self.name
299
300
301
303 self._open_archive()
304 path = (altpath or self.path).lstrip(os.sep)
305
306 try:
307 obj = self.members[path]
308 except KeyError:
309 obj = self.members(path + os.sep)
310
311 return obj
312
313
314
316 if self.tarobj is None:
317 _mode = "r"
318
319 if self.driver == "gztar":
320 _mode = "r:gz"
321 elif self.driver == "bz2tar":
322 _mode = "r:bz2"
323
324 self.tarobj = tarfile.open(fileobj=self.parent.open(), mode=_mode)
325 self.members = self._init_members(self.tarobj)
326
327 self.xyz.vfs.set_cache(self.parent.full_path, (self.tarobj,
328 self.members))
329
330 return self.tarobj
331
332
333
334 - def _copy_file(self, src, dst, existcb, errorcb, save_attrs,
335 follow_links, env, cancel=None, tarobj=None):
336 """
337 File-to-file copy
338 """
339
340 obj = self._init_obj(src)
341
342 if os.path.exists(dst) and os.path.isdir(dst):
343 dstto = os.path.join(dst, os.path.basename(src))
344 else:
345 dstto = dst
346
347 if os.path.exists(dstto):
348 if env['override'] not in ('override all', 'skip all'):
349 if existcb:
350 try:
351 env['override'] = existcb(
352 self.xyz.vfs.dispatch(dstto))
353 except Exception:
354 env['override'] = 'abort'
355
356 if env['override'] == 'abort':
357 raise XYZRuntimeError()
358 elif env['override'] in ('skip', 'skip all'):
359 return False
360
361 try:
362 if not follow_links and obj.issym():
363 os.symlink(obj.linkname, dstto)
364 else:
365 if obj.issym():
366 objdir = os.path.dirname(obj.name)
367 src = os.path.join(objdir, obj.linkname)
368
369 self._do_copy(src, dstto, save_attrs, tarobj, obj)
370
371 return True
372 except Exception, e:
373 if env['error'] != 'skip all':
374 if errorcb:
375 try:
376 env['error'] = errorcb(
377 self.xyz.vfs.dispatch(self.full_path), str(e))
378 except Exception:
379 env['error'] = 'abort'
380
381 if env['error'] == 'abort':
382 raise XYZRuntimeError()
383
384 return False
385
386
387
388 - def _copy_dir(self, src, dst, existcb, errorcb, save_attrs,
389 follow_links, env, cancel=None, tarobj=None):
390 """
391 Dir-to-dir copy
392 """
393
394 obj = self._init_obj(src)
395
396 if os.path.exists(dst) and os.path.isdir(dst) and \
397 os.path.basename(src) != os.path.basename(dst):
398 dst = os.path.join(dst, os.path.basename(src))
399
400 if not follow_links and obj.issym():
401 os.symlink(obj.linkname, dst)
402
403 return True
404
405 if obj.isdir() and not os.path.exists(dst):
406 os.makedirs(dst)
407
408 files = [x for x in tarobj.getmembers() if
409 self.in_dir(obj.name, x.name)]
410
411 for f in files:
412 if cancel is not None and cancel.isSet():
413 raise StopIteration()
414
415 srcobj = f.name
416 dstobj = os.path.join(dst, self.get_name(f))
417
418 if self._init_obj(srcobj).isdir():
419 fun = self._copy_dir
420 else:
421 fun = self._copy_file
422
423 fun(srcobj, dstobj, existcb, errorcb, save_attrs,
424 follow_links, env, cancel, tarobj)
425
426 if obj.isdir() and save_attrs:
427 self._copystat(obj, dst)
428
429
430
431 - def _do_copy(self, src, dst, save_attrs, tarobj, obj):
432 """
433 Copy file from inside archive
434 """
435
436 fsrc = tarobj.extractfile(self._normalize(src))
437
438 fdst = open(dst, "w")
439
440 while True:
441 block = fsrc.read(4096)
442
443
444 if block == '':
445 break
446
447 fdst.write(block)
448
449 fsrc.close()
450 fdst.close()
451
452 if save_attrs:
453 self._copystat(obj, dst)
454
455
456
458 try:
459 os.chown(dst, obj.uid, obj.gid)
460 except Exception, e:
461 xyzlog.warning(_(u"Unable to chown %s: %s") %
462 (ustring(dst), unicode(e)))
463
464 try:
465 os.chmod(dst, obj.mode)
466 except Exception, e:
467 xyzlog.warning(_(u"Unable to chmod %s: %s") %
468 (ustring(dst), unicode(e)))
469
470
471
473 """
474 Find cached data
475 """
476
477 return self.xyz.vfs.get_cache(self.parent.full_path) or (None, None)
478
479
480
482 """
483 Init members from archive contents
484 """
485
486 members = {}
487
488 for name in tarobj.getnames():
489 data = util.split_path(name)
490 current = []
491
492 for obj in data:
493 current.append(obj)
494
495 path = os.path.join(*current)
496
497 if path not in members:
498 try:
499 item = tarobj.getmember(path)
500 except KeyError:
501 try:
502 item = tarobj.getmember(path + os.sep)
503 except KeyError:
504
505 item = self._make_dummy_dir(path)
506
507 members[path] = item
508
509 return members
510
511
512
514 """
515 Make dummy directory object
516 """
517
518 uid = os.getuid()
519 gid = os.getgid()
520
521 obj = tarfile.TarInfo(name)
522 obj.type = tarfile.DIRTYPE
523 obj.mode = 0755
524 obj.uid = uid
525 obj.gid = gid
526 obj.mtime = int(time.time())
527 obj.uname = util.get_user(uid)
528 obj.gname = util.get_group(gid)
529
530 return obj
531