[275] | 1 | import os |
---|
| 2 | import time |
---|
| 3 | from tarfile import TarInfo, DIRTYPE |
---|
| 4 | from StringIO import StringIO |
---|
| 5 | |
---|
| 6 | # TarballExportContext don't write dirs in tarball and we need to fix this |
---|
| 7 | |
---|
| 8 | #security.declareProtected( ManagePortal, 'writeDataFile' ) |
---|
| 9 | def writeDataFile( self, filename, text, content_type, subdir=None ): |
---|
| 10 | |
---|
| 11 | """ See IExportContext. |
---|
| 12 | """ |
---|
| 13 | mod_time = time.time() |
---|
| 14 | if subdir is not None: |
---|
| 15 | elements = subdir.split('/') |
---|
| 16 | parents = filter(None, elements) |
---|
| 17 | while parents: |
---|
| 18 | dirname = os.path.join(*parents) |
---|
| 19 | try: |
---|
| 20 | self._archive.getmember(dirname+'/') |
---|
| 21 | except KeyError: |
---|
| 22 | info = TarInfo(dirname) |
---|
| 23 | info.size = 0 |
---|
[373] | 24 | info.mode = 509 |
---|
[275] | 25 | info.mtime = mod_time |
---|
| 26 | info.type = DIRTYPE |
---|
| 27 | self._archive.addfile(info, StringIO()) |
---|
| 28 | parents = parents[:-1] |
---|
| 29 | |
---|
| 30 | filename = '/'.join( ( subdir, filename ) ) |
---|
| 31 | |
---|
| 32 | stream = StringIO( text ) |
---|
| 33 | info = TarInfo( filename ) |
---|
| 34 | info.size = len( text ) |
---|
[373] | 35 | info.mode = 436 |
---|
[275] | 36 | info.mtime = mod_time |
---|
| 37 | self._archive.addfile( info, stream ) |
---|
| 38 | |
---|
| 39 | from Products.GenericSetup.context import TarballExportContext |
---|
| 40 | TarballExportContext.writeDataFile = writeDataFile |
---|
| 41 | |
---|
| 42 | from Products.GenericSetup.context import SKIPPED_FILES, SKIPPED_SUFFIXES |
---|
| 43 | |
---|
| 44 | def listDirectory(self, path, skip=SKIPPED_FILES, |
---|
| 45 | skip_suffixes=SKIPPED_SUFFIXES): |
---|
| 46 | |
---|
| 47 | """ See IImportContext. |
---|
| 48 | """ |
---|
| 49 | if path is None: # root is special case: no leading '/' |
---|
| 50 | path = '' |
---|
| 51 | elif path: |
---|
| 52 | if not self.isDirectory(path): |
---|
| 53 | return None |
---|
| 54 | |
---|
| 55 | if not path.endswith('/'): |
---|
| 56 | path = path + '/' |
---|
| 57 | |
---|
| 58 | pfx_len = len(path) |
---|
| 59 | |
---|
| 60 | names = [] |
---|
| 61 | for name in self._archive.getnames(): |
---|
| 62 | if name == path or not name.startswith(path): |
---|
| 63 | continue |
---|
| 64 | name = name[pfx_len:] |
---|
| 65 | if name.count('/') > 1: |
---|
| 66 | continue |
---|
| 67 | if '/' in name and not name.endswith('/'): |
---|
| 68 | continue |
---|
| 69 | if name in skip: |
---|
| 70 | continue |
---|
| 71 | if [s for s in skip_suffixes if name.endswith(s)]: |
---|
| 72 | continue |
---|
| 73 | # directories have trailing '/' character and we need to remove it |
---|
[282] | 74 | name = name.rstrip('/') |
---|
[275] | 75 | names.append(name) |
---|
| 76 | |
---|
| 77 | return names |
---|
| 78 | |
---|
| 79 | from Products.GenericSetup.context import TarballImportContext |
---|
| 80 | TarballImportContext.listDirectory = listDirectory |
---|
[1255] | 81 | |
---|
| 82 | # patch for this bug in tarfile module - http://bugs.python.org/issue1719898 |
---|
| 83 | from tarfile import TarInfo, nts, GNUTYPE_SPARSE, normpath, DIRTYPE |
---|
| 84 | def frombuf(cls, buf): |
---|
| 85 | """Construct a TarInfo object from a 512 byte string buffer. |
---|
| 86 | """ |
---|
| 87 | tarinfo = cls() |
---|
| 88 | tarinfo.name = nts(buf[0:100]) |
---|
| 89 | tarinfo.mode = int(buf[100:108], 8) |
---|
| 90 | tarinfo.uid = int(buf[108:116],8) |
---|
| 91 | tarinfo.gid = int(buf[116:124],8) |
---|
| 92 | |
---|
| 93 | # There are two possible codings for the size field we |
---|
| 94 | # have to discriminate, see comment in tobuf() below. |
---|
| 95 | if buf[124] != chr(0200): |
---|
| 96 | tarinfo.size = long(buf[124:136], 8) |
---|
| 97 | else: |
---|
| 98 | tarinfo.size = 0L |
---|
| 99 | for i in range(11): |
---|
| 100 | tarinfo.size <<= 8 |
---|
| 101 | tarinfo.size += ord(buf[125 + i]) |
---|
| 102 | |
---|
| 103 | tarinfo.mtime = long(buf[136:148], 8) |
---|
| 104 | tarinfo.chksum = int(buf[148:156], 8) |
---|
| 105 | tarinfo.type = buf[156:157] |
---|
| 106 | tarinfo.linkname = nts(buf[157:257]) |
---|
| 107 | tarinfo.uname = nts(buf[265:297]) |
---|
| 108 | tarinfo.gname = nts(buf[297:329]) |
---|
| 109 | try: |
---|
| 110 | tarinfo.devmajor = int(buf[329:337], 8) |
---|
| 111 | tarinfo.devminor = int(buf[337:345], 8) |
---|
| 112 | except ValueError: |
---|
| 113 | tarinfo.devmajor = tarinfo.devmajor = 0 |
---|
| 114 | tarinfo.prefix = buf[345:500] |
---|
| 115 | |
---|
| 116 | # The prefix field is used for filenames > 100 in |
---|
| 117 | # the POSIX standard. |
---|
| 118 | # name = prefix + '/' + name |
---|
| 119 | if tarinfo.type != GNUTYPE_SPARSE: |
---|
| 120 | tarinfo.name = normpath(os.path.join(nts(tarinfo.prefix), tarinfo.name)) |
---|
| 121 | |
---|
| 122 | # Some old tar programs represent a directory as a regular |
---|
| 123 | # file with a trailing slash. |
---|
| 124 | if tarinfo.isreg() and tarinfo.name.endswith("/"): |
---|
| 125 | tarinfo.type = DIRTYPE |
---|
| 126 | |
---|
| 127 | # Directory names should have a '/' at the end. |
---|
| 128 | if tarinfo.isdir(): |
---|
| 129 | tarinfo.name += "/" |
---|
| 130 | return tarinfo |
---|
| 131 | |
---|
| 132 | frombuf = classmethod(frombuf) |
---|
| 133 | TarInfo.frombuf = frombuf |
---|