Package libxyz :: Package vfs :: Module tar
[hide private]
[frames] | no frames]

Source Code for Module libxyz.vfs.tar

  1  #-*- coding: utf8 -* 
  2  # 
  3  # Max E. Kuznecov ~syhpoon <syhpoon@syhpoon.name> 2008-2009 
  4  # 
  5  # This file is part of XYZCommander. 
  6  # XYZCommander is free software: you can redistribute it and/or modify 
  7  # it under the terms of the GNU Lesser Public License as published by 
  8  # the Free Software Foundation, either version 3 of the License, or 
  9  # (at your option) any later version. 
 10  # XYZCommander is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 13  # GNU Lesser Public License for more details. 
 14  # You should have received a copy of the GNU Lesser Public License 
 15  # along with XYZCommander. If not, see <http://www.gnu.org/licenses/>. 
 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   
29 -class TarVFSObject(vfsobj.VFSObject):
30 """ 31 Tar archive interface 32 """ 33
34 - def either(self, a, b):
35 if self.root: 36 return a 37 else: 38 return b()
39 40 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 41 42 get_name = lambda self, x: os.path.basename(x.name.rstrip(os.path.sep)) 43 get_path = lambda self, x: os.path.join(self.ext_path, x.lstrip(os.sep)) 44 45 file_type_map = { 46 lambda obj: obj.isfile(): vfstypes.VFSTypeFile(), 47 lambda obj: obj.isdir(): vfstypes.VFSTypeDir(), 48 lambda obj: obj.issym(): vfstypes.VFSTypeLink(), 49 lambda obj: obj.ischr(): vfstypes.VFSTypeChar(), 50 lambda obj: obj.isblk(): vfstypes.VFSTypeBlock(), 51 lambda obj: obj.isfifo(): vfstypes.VFSTypeFifo(), 52 } 53
54 - def __init__(self, *args, **kwargs):
55 self.tarobj = kwargs.get('tarobj', None) 56 57 super(TarVFSObject, self).__init__(*args, **kwargs)
58 59 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 60
61 - def walk(self):
62 """ 63 Directory tree walker 64 @return: tuple (parent, dir, dirs, files) where: 65 parent - parent dir *VFSObject instance 66 dir - current dir TarVFSObject instance 67 dirs - list of TarVFSObject objects of directories 68 files - list of TarVFSObject objects of files 69 """ 70 71 tarobj = self._open_archive() 72 entries = tarobj.getmembers() 73 74 _dirs = [x for x in entries if x.isdir() and 75 self.in_dir(self.path, x.name)] 76 _files = [x for x in entries if not x.isdir() and 77 self.in_dir(self.path, x.name)] 78 79 _dirs.sort(cmp=lambda x, y: cmp(self.get_name(x), 80 self.get_name(y))) 81 _files.sort(cmp=lambda x, y: cmp(self.get_name(x), 82 self.get_name(y))) 83 84 if self.path == os.sep: 85 _parent = self.xyz.vfs.get_parent(self.parent.path, self.enc) 86 else: 87 _parent = self.xyz.vfs.dispatch( 88 self.get_path(os.path.dirname(self.path)), self.enc) 89 _parent.name = ".." 90 91 return [ 92 _parent, 93 self, 94 [self.xyz.vfs.dispatch(self.get_path(x.name), self.enc) 95 for x in _dirs], 96 [self.xyz.vfs.dispatch(self.get_path(x.name), self.enc) 97 for x in _files], 98 ]
99 100 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 101
102 - def copy(self, path, existcb=None, errorcb=None, save_attrs=True, 103 follow_links=False, cancel=None):
104 105 env = { 106 'override': 'abort', 107 'error': 'abort' 108 } 109 110 tarobj = self._open_archive() 111 112 try: 113 if self.is_dir(): 114 f = self._copy_dir 115 else: 116 f = self._copy_file 117 118 f(self.path, path, existcb, errorcb, 119 save_attrs, follow_links, env, cancel, tarobj=tarobj) 120 except XYZRuntimeError: 121 # Aborted 122 return False 123 else: 124 return True
125 126 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 127
128 - def _prepare(self):
129 if self.path == os.sep: 130 self.root = True 131 else: 132 self.root = False 133 134 self.tarobj = self.kwargs.get("tarobj", None) 135 136 if self.root: 137 self.obj = None 138 else: 139 self.obj = self._init_obj() 140 141 self.ftype = self._find_type() 142 self.vtype = self.ftype.vtype 143 144 self._set_attributes() 145 146 self.attributes = ( 147 (_(u"Name"), ustring(self.name)), 148 (_(u"Type"), ustring(self.ftype)), 149 (_(u"Modification time"), ustring(time.ctime(self.mtime))), 150 (_(u"Size in bytes"), ustring(self.size)), 151 (_(u"Owner"), ustring(self.uid)), 152 (_(u"Group"), ustring(self.gid)), 153 (_(u"Access mode"), ustring(self.mode)), 154 (_(u"Type-specific data"), self.data), 155 )
156 157 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 158
159 - def _normalize(self, path):
160 """ 161 Normalize path 162 """ 163 164 if path.startswith(os.sep): 165 return path.lstrip(os.sep) 166 else: 167 return path
168 169 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 170
171 - def __str__(self):
172 return "<TarVFSObject object: %s>" % self.path
173 174 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 175
176 - def in_dir(self, d, e):
177 """ 178 Filter only those archive entries which exist in the same 179 directory level 180 """ 181 182 if e.startswith(d.lstrip(os.sep)) and \ 183 len(util.split_path(e)) == (len(util.split_path(d)) + 1): 184 return True 185 else: 186 return False
187 188 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 189
190 - def _find_type(self):
191 """ 192 Find out file type 193 """ 194 195 if self.root: 196 return self.parent.ftype 197 198 for k, v in self.file_type_map.iteritems(): 199 if k(self.obj): 200 return v 201 202 return vfstypes.VFSTypeUnknown()
203 204 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 205
206 - def _set_attributes(self):
207 """ 208 Set file attibutes 209 """ 210 211 def set_link_attributes(): 212 """ 213 Set appropriate soft link attibutes 214 """ 215 216 self.info = "" 217 self.visual = "-> %s" % self.obj.linkname or ""
218 219 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 220 221 self.name = self.either(self.parent.name, lambda: self.name) 222 self.mtime = self.either(self.parent.mtime, lambda: self.obj.mtime) 223 self.size = self.either(self.parent.size, lambda: self.obj.size) 224 self.uid = self.either(self.parent.uid, lambda: self.obj.uid) 225 self.gid = self.either(self.parent.gid, lambda: self.obj.gid) 226 self.mode = mode.Mode(self.either(self.parent.mode.raw, 227 lambda: self.obj.mode), self.ftype) 228 self.visual = "%s%s" % (self.vtype, self.name) 229 230 self.info = "%s %s" % (util.format_size(self.size), self.mode) 231 232 if self.is_link(): 233 set_link_attributes() 234 elif self.is_file(): 235 _mode = stat.S_IMODE(self.mode.raw) 236 237 # Executable 238 if _mode & 0111: 239 self.vtype = "*" 240 self.visual = "*%s" % self.name
241 242 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 243
244 - def _init_obj(self, altpath=None):
245 tarobj = self._open_archive() 246 path = (altpath or self.path).lstrip(os.sep) 247 248 try: 249 obj = tarobj.getmember(path) 250 except KeyError: 251 obj = tarobj.getmember(path + os.sep) 252 253 return obj
254 255 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 256
257 - def _open_archive(self):
258 if self.tarobj is None: 259 _mode = "r" 260 261 if self.driver == "gztar": 262 _mode = "r:gz" 263 elif self.driver == "bz2tar": 264 _mode = "r:bz2" 265 266 self.tarobj = tarfile.open(self.parent.path, mode=_mode) 267 self.xyz.vfs.set_cache(self.parent.path, {'tarobj': self.tarobj}) 268 269 return self.tarobj
270 271 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 272
273 - def _copy_file(self, src, dst, existcb, errorcb, save_attrs, 274 follow_links, env, cancel=None, tarobj=None):
275 """ 276 File-to-file copy 277 """ 278 279 obj = self._init_obj(src) 280 281 if os.path.exists(dst) and os.path.isdir(dst): 282 dstto = os.path.join(dst, os.path.basename(src)) 283 else: 284 dstto = dst 285 286 if os.path.exists(dstto): 287 if env['override'] not in ('override all', 'skip all'): 288 if existcb: 289 try: 290 env['override'] = existcb( 291 self.xyz.vfs.dispatch(dstto)) 292 except Exception: 293 env['override'] = 'abort' 294 295 if env['override'] == 'abort': 296 raise XYZRuntimeError() 297 elif env['override'] in ('skip', 'skip all'): 298 return False 299 300 try: 301 if not follow_links and obj.issym(): 302 os.symlink(obj.linkname, dstto) 303 else: 304 if obj.issym(): 305 objdir = os.path.dirname(obj.name) 306 src = os.path.join(objdir, obj.linkname) 307 308 self._do_copy(src, dstto, save_attrs, tarobj, obj) 309 310 return True 311 except Exception, e: 312 if env['error'] != 'skip all': 313 if errorcb: 314 try: 315 env['error'] = errorcb( 316 self.xyz.vfs.dispatch(self.full_path), str(e)) 317 except Exception: 318 env['error'] = 'abort' 319 320 if env['error'] == 'abort': 321 raise XYZRuntimeError() 322 323 return False
324 325 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 326
327 - def _copy_dir(self, src, dst, existcb, errorcb, save_attrs, 328 follow_links, env, cancel=None, tarobj=None):
329 """ 330 Dir-to-dir copy 331 """ 332 333 obj = self._init_obj(src) 334 335 if os.path.exists(dst) and os.path.isdir(dst) and \ 336 os.path.basename(src) != os.path.basename(dst): 337 dst = os.path.join(dst, os.path.basename(src)) 338 339 if not follow_links and obj.issym(): 340 os.symlink(obj.linkname, dst) 341 342 return True 343 344 if obj.isdir() and not os.path.exists(dst): 345 os.makedirs(dst) 346 347 files = [x for x in tarobj.getmembers() if 348 self.in_dir(obj.name, x.name)] 349 350 for f in files: 351 if cancel is not None and cancel.isSet(): 352 raise StopIteration() 353 354 srcobj = f.name 355 dstobj = os.path.join(dst, self.get_name(f)) 356 357 if self._init_obj(srcobj).isdir(): 358 fun = self._copy_dir 359 else: 360 fun = self._copy_file 361 362 fun(srcobj, dstobj, existcb, errorcb, save_attrs, 363 follow_links, env, cancel, tarobj) 364 365 if obj.isdir() and save_attrs: 366 self._copystat(obj, dst)
367 368 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 369
370 - def _do_copy(self, src, dst, save_attrs, tarobj, obj):
371 """ 372 Copy file from inside archive 373 """ 374 375 fsrc = tarobj.extractfile(self._normalize(src)) 376 377 fdst = open(dst, "w") 378 379 while True: 380 block = fsrc.read(4096) 381 382 # EOF 383 if block == '': 384 break 385 386 fdst.write(block) 387 388 fsrc.close() 389 fdst.close() 390 391 if save_attrs: 392 self._copystat(obj, dst)
393 394 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 395
396 - def _copystat(self, obj, dst):
397 try: 398 os.chown(dst, obj.uid, obj.gid) 399 except Exception, e: 400 xyzlog.warning(_(u"Unable to chown %s: %s") % 401 (ustring(dst), ustring(str(e)))) 402 403 try: 404 os.chmod(dst, obj.mode) 405 except Exception, e: 406 xyzlog.warning(_(u"Unable to chmod %s: %s") % 407 (ustring(dst), ustring(str(e))))
408