# Copyright 2001-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # The format for a tbz2/xpak: # # tbz2: tar.bz2 + xpak + (xpak_offset) + "STOP" # xpak: "XPAKPACK" + (index_len) + (data_len) + index + data + "XPAKSTOP" # index: (pathname_len) + pathname + (data_offset) + (data_len) # index entries are concatenated end-to-end. # data: concatenated data chunks, end-to-end. # # [tarball]XPAKPACKIIIIDDDD[index][data]XPAKSTOPOOOOSTOP # # (integer) == encodeint(integer) ===> 4 characters (big-endian copy) # '+' means concatenate the fields ===> All chunks are strings __all__ = ['addtolist', 'decodeint', 'encodeint', 'getboth', 'getindex', 'getindex_mem', 'getitem', 'listindex', 'searchindex', 'tbz2', 'xpak_mem', 'xpak', 'xpand', 'xsplit', 'xsplit_mem'] import array import errno import shutil import sys import portage from portage import os from portage import normalize_path from portage import _encodings from portage import _unicode_decode from portage import _unicode_encode def addtolist(mylist, curdir): """(list, dir) --- Takes an array(list) and appends all files from dir down the directory tree. Returns nothing. list is modified.""" curdir = normalize_path(_unicode_decode(curdir, encoding=_encodings['fs'], errors='strict')) for parent, dirs, files in os.walk(curdir): parent = _unicode_decode(parent, encoding=_encodings['fs'], errors='strict') if parent != curdir: mylist.append(parent[len(curdir) + 1:] + os.sep) for x in dirs: try: _unicode_decode(x, encoding=_encodings['fs'], errors='strict') except UnicodeDecodeError: dirs.remove(x) for x in files: try: x = _unicode_decode(x, encoding=_encodings['fs'], errors='strict') except UnicodeDecodeError: continue mylist.append(os.path.join(parent, x)[len(curdir) + 1:]) def encodeint(myint): """Takes a 4 byte integer and converts it into a string of 4 characters. Returns the characters in a string.""" a = array.array('B') a.append((myint >> 24 ) & 0xff) a.append((myint >> 16 ) & 0xff) a.append((myint >> 8 ) & 0xff) a.append(myint & 0xff) return a.tostring() def decodeint(mystring): """Takes a 4 byte string and converts it into a 4 byte integer. Returns an integer.""" if sys.hexversion < 0x3000000: mystring = [ord(x) for x in mystring] myint = 0 myint += mystring[3] myint += mystring[2] << 8 myint += mystring[1] << 16 myint += mystring[0] << 24 return myint def xpak(rootdir,outfile=None): """(rootdir,outfile) -- creates an xpak segment of the directory 'rootdir' and under the name 'outfile' if it is specified. Otherwise it returns the xpak segment.""" mylist=[] addtolist(mylist, rootdir) mylist.sort() mydata = {} for x in mylist: if x == 'CONTENTS': # CONTENTS is generated during the merge process. continue x = _unicode_encode(x, encoding=_encodings['fs'], errors='strict') mydata[x] = open(os.path.join(rootdir, x), 'rb').read() xpak_segment = xpak_mem(mydata) if outfile: outf = open(_unicode_encode(outfile, encoding=_encodings['fs'], errors='strict'), 'wb') outf.write(xpak_segment) outf.close() else: return xpak_segment def xpak_mem(mydata): """Create an xpack segement from a map object.""" mydata_encoded = {} for k, v in mydata.items(): k = _unicode_encode(k, encoding=_encodings['repo.content'], errors='backslashreplace') v = _unicode_encode(v, encoding=_encodings['repo.content'], errors='backslashreplace') mydata_encoded[k] = v mydata = mydata_encoded del mydata_encoded indexglob = b'' indexpos=0 dataglob = b'' datapos=0 for x, newglob in mydata.items(): mydatasize=len(newglob) indexglob=indexglob+encodeint(len(x))+x+encodeint(datapos)+encodeint(mydatasize) indexpos=indexpos+4+len(x)+4+4 dataglob=dataglob+newglob datapos=datapos+mydatasize return b'XPAKPACK' \ + encodeint(len(indexglob)) \ + encodeint(len(dataglob)) \ + indexglob \ + dataglob \ + b'XPAKSTOP' def xsplit(infile): """(infile) -- Splits the infile into two files. 'infile.index' contains the index segment. 'infile.dat' contails the data segment.""" infile = _unicode_decode(infile, encoding=_encodings['fs'], errors='strict') myfile = open(_unicode_encode(infile, encoding=_encodings['fs'], errors='strict'), 'rb') mydat=myfile.read() myfile.close() splits = xsplit_mem(mydat) if not splits: return False myfile = open(_unicode_encode(infile + '.index', encoding=_encodings['fs'], errors='strict'), 'wb') myfile.write(splits[0]) myfile.close() myfile = open(_unicode_encode(infile + '.dat', encoding=_encodings['fs'], errors='strict'), 'wb') myfile.write(splits[1]) myfile.close() return True def xsplit_mem(mydat): if mydat[0:8] != b'XPAKPACK': return None if mydat[-8:] != b'XPAKSTOP': return None indexsize=decodeint(mydat[8:12]) return (mydat[16:indexsize+16], mydat[indexsize+16:-8]) def getindex(infile): """(infile) -- grabs the index segment from the infile and returns it.""" myfile = open(_unicode_encode(infile, encoding=_encodings['fs'], errors='strict'), 'rb') myheader=myfile.read(16) if myheader[0:8] != b'XPAKPACK': myfile.close() return indexsize=decodeint(myheader[8:12]) myindex=myfile.read(indexsize) myfile.close() return myindex def getboth(infile): """(infile) -- grabs the index and data segments from the infile. Returns an array [indexSegment,dataSegment]""" myfile = open(_unicode_encode(infile, encoding=_encodings['fs'], errors='strict'), 'rb') myheader=myfile.read(16) if myheader[0:8] != b'XPAKPACK': myfile.close() return indexsize=decodeint(myheader[8:12]) datasize=decodeint(myheader[12:16]) myindex=myfile.read(indexsize) mydata=myfile.read(datasize) myfile.close() return myindex, mydata def listindex(myindex): """Print to the terminal the filenames listed in the indexglob passed in.""" for x in getindex_mem(myindex): print(x) def getindex_mem(myindex): """Returns the filenames listed in the indexglob passed in.""" myindexlen=len(myindex) startpos=0 myret=[] while ((startpos+8) 1: tmp_fname = "%s.%d" % (self.file, os.getpid()) shutil.copyfile(self.file, tmp_fname) try: portage.util.apply_stat_permissions(self.file, self.filestat) except portage.exception.OperationNotPermitted: pass os.rename(tmp_fname, self.file) myfile = open(_unicode_encode(self.file, encoding=_encodings['fs'], errors='strict'), 'ab+') if not myfile: raise IOError myfile.seek(-self.xpaksize,2) # 0,2 or -0,2 just mean EOF. myfile.truncate() myfile.write(xpdata+encodeint(len(xpdata)) + b'STOP') myfile.flush() myfile.close() return 1 def cleanup(self, datadir): datadir_split = os.path.split(datadir) if len(datadir_split) >= 2 and len(datadir_split[1]) > 0: # This is potentially dangerous, # thus the above sanity check. try: shutil.rmtree(datadir) except OSError as oe: if oe.errno == errno.ENOENT: pass else: raise oe def scan(self): """Scans the tbz2 to locate the xpak segment and setup internal values. This function is called by relevant functions already.""" try: mystat=os.stat(self.file) if self.filestat: changed=0 if mystat.st_size != self.filestat.st_size \ or mystat.st_mtime != self.filestat.st_mtime \ or mystat.st_ctime != self.filestat.st_ctime: changed = True if not changed: return 1 self.filestat=mystat a = open(_unicode_encode(self.file, encoding=_encodings['fs'], errors='strict'), 'rb') a.seek(-16,2) trailer=a.read() self.infosize=0 self.xpaksize=0 if trailer[-4:] != b'STOP': a.close() return 0 if trailer[0:8] != b'XPAKSTOP': a.close() return 0 self.infosize=decodeint(trailer[8:12]) self.xpaksize=self.infosize+8 a.seek(-(self.xpaksize),2) header=a.read(16) if header[0:8] != b'XPAKPACK': a.close() return 0 self.indexsize=decodeint(header[8:12]) self.datasize=decodeint(header[12:16]) self.indexpos=a.tell() self.index=a.read(self.indexsize) self.datapos=a.tell() a.close() return 2 except SystemExit as e: raise except: return 0 def filelist(self): """Return an array of each file listed in the index.""" if not self.scan(): return None return getindex_mem(self.index) def getfile(self,myfile,mydefault=None): """Finds 'myfile' in the data segment and returns it.""" if not self.scan(): return None myresult=searchindex(self.index,myfile) if not myresult: return mydefault a = open(_unicode_encode(self.file, encoding=_encodings['fs'], errors='strict'), 'rb') a.seek(self.datapos+myresult[0],0) myreturn=a.read(myresult[1]) a.close() return myreturn def getelements(self,myfile): """A split/array representation of tbz2.getfile()""" mydat=self.getfile(myfile) if not mydat: return [] return mydat.split() def unpackinfo(self,mydest): """Unpacks all the files from the dataSegment into 'mydest'.""" if not self.scan(): return 0 try: origdir=os.getcwd() except SystemExit as e: raise except: os.chdir("/") origdir="/" a = open(_unicode_encode(self.file, encoding=_encodings['fs'], errors='strict'), 'rb') if not os.path.exists(mydest): os.makedirs(mydest) os.chdir(mydest) startpos=0 while ((startpos+8)