def _create_header(info, format_, encoding, errors): """Return a header block. info is a dictionary with file information, format must be one of the *_FORMAT constants. """ parts = [ tarfile.stn(info.get("name", ""), 100, encoding, errors), tarfile.itn(info.get("mode", 0), 8, format_), tarfile.itn(info.get("uid", 0), 8, format_), tarfile.itn(info.get("gid", 0), 8, format_), tarfile.itn(info.get("size", 0), 12, format_), tarfile.itn(info.get("mtime", 0), 12, format_), b" " * 8, # checksum field info.get("type", tarfile.REGTYPE), tarfile.stn(info.get("linkname", ""), 100, encoding, errors), info.get("magic", tarfile.POSIX_MAGIC), tarfile.stn(info.get("uname", ""), 32, encoding, errors), tarfile.stn(info.get("gname", ""), 32, encoding, errors), tarfile.itn(info.get("devmajor", 0), 8, format_), tarfile.itn(info.get("devminor", 0), 8, format_), tarfile.stn(info.get("prefix", ""), 155, encoding, errors) ] buf = struct.pack("%ds" % tarfile.BLOCKSIZE, b"".join(parts)) chksum = tarfile.calc_chksums(buf[-tarfile.BLOCKSIZE:])[0] buf = buf[:-364] + bytes("%06o\0" % chksum, "ascii") + buf[-357:] return buf
def parse_tar_header_simple(header_data): header = { 'size': tarfile.nti(header_data[size_offset:size_offset + size_len]), 'chksum': tarfile.nti(header_data[checksum_offset:checksum_offset + checksum_len]) } if header['chksum'] not in tarfile.calc_chksums(header_data): raise tarfile.InvalidHeaderError("chksum not match") return header
def main(): parser = argparse.ArgumentParser(description="The MinLin distro build system.") parser.add_argument("--build-dir", help="The build directory.", required=True) parser.add_argument("-o", dest="outfile", required=True) args = parser.parse_args() root_dir = Path(args.build_dir) shutil.rmtree(root_dir, ignore_errors=True) os.makedirs(root_dir, exist_ok=True) packages = get_packages() for pkg in packages.values(): pkg.build() build_package(root_dir, pkg) tar = tarfile.open(args.outfile, "w") tar.add(root_dir, "/", recursive=True) tar.close() # Add symlinks. tar_data = bytes() for src, dst in all_symlinks.items(): src = src.lstrip("/") dst = dst.lstrip("/") fmt = f"100s{8+8+8}x12s12s8sc100s8s{32+32+8+8+155+12}x" magic = b"ustar \x00" size = b"00000000000\0" time_modified = b"00000000000\0" type_ = b"2" # symbolic link header = struct.pack(fmt, src.encode("ascii"), size, time_modified, b"", type_, dst.encode("ascii"), magic) from tarfile import calc_chksums checksum = bytes("%06o\0" % calc_chksums(header)[0], "ascii") header = struct.pack(fmt, src.encode("ascii"), size, time_modified, checksum, type_, dst.encode("ascii"), magic) tar_data += header tar_data += open(args.outfile, "rb").read() open(args.outfile, "wb").write(tar_data)
def parse_tar_header(header_data): header = dict() tar_header = struct.unpack( '=100s8s8s8s12s12s8sc100s6s2s32s32s8s8s155s12x', header_data) header['name'] = tar_header[0] header['mode'] = tar_header[1] header['uid'] = tar_header[2] header['gid'] = tar_header[3] header['size'] = tarfile.nti(tar_header[4]) header['mtime'] = tar_header[5] header['chksum'] = tarfile.nti(tar_header[6]) header['typeflag'] = tar_header[7] header['linkname'] = tar_header[8] header['magic'] = tar_header[9] header['version'] = tar_header[10] header['uname'] = tar_header[11] header['gname'] = tar_header[12] header['devmajor'] = tar_header[13] header['devminor'] = tar_header[14] header['prefix'] = tar_header[15] if header['chksum'] not in tarfile.calc_chksums(header_data): raise tarfile.InvalidHeaderError("chksum not match") return header
def write_archivefile(data): # Intercept writes to the archive file and rewrite any headers matching our criteria for block_idx in range( (len(data) + tarfile.BLOCKSIZE - 1) // tarfile.BLOCKSIZE): block = data[block_idx * tarfile.BLOCKSIZE:(block_idx + 1) * tarfile.BLOCKSIZE] cur_offset = archive_props["offset"] archive_props["offset"] += len(block) if cur_offset != archive_props[ "next_header_offset"]: continue try: tarinfo = out_archive.tarinfo.frombuf( block, out_archive.encoding, out_archive.errors) except tarfile.EOFHeaderError: # type: ignore[attr-defined] break block_count = (tarinfo.size + tarfile.BLOCKSIZE - 1) // tarfile.BLOCKSIZE archive_props[ "next_header_offset"] = archive_props[ "offset"] + block_count * tarfile.BLOCKSIZE if tarinfo.devmajor != 0 or tarinfo.devmajor != 0 or tarinfo.type in ( tarfile.CHRTYPE, tarfile.BLKTYPE): continue # Erase major/minor fields for non-device files as other tar-archive producing tools do. if not isinstance(data, bytearray): data = bytearray(data) DEVNUMBERS_OFFSET = 329 DEVNUMBERS_LENGTH = 2 * 8 DEVNUMBERS_RANGE = slice( block_idx * tarfile.BLOCKSIZE + DEVNUMBERS_OFFSET, block_idx * tarfile.BLOCKSIZE + DEVNUMBERS_OFFSET + DEVNUMBERS_LENGTH, ) CHECKSUM_OFFSET = 148 CHECKSUM_LENGTH = 8 CHECKSUM_RANGE = slice( block_idx * tarfile.BLOCKSIZE + CHECKSUM_OFFSET, block_idx * tarfile.BLOCKSIZE + CHECKSUM_OFFSET + CHECKSUM_LENGTH, ) data[ DEVNUMBERS_RANGE] = b"\0" * DEVNUMBERS_LENGTH # Recompute checksum, but whipe checksum field (with spaces) before doing so data[CHECKSUM_RANGE] = b" " * CHECKSUM_LENGTH chksum, *_ = tarfile.calc_chksums( # type: ignore[attr-defined] data[block_idx * tarfile.BLOCKSIZE:(block_idx + 1) * tarfile.BLOCKSIZE]) data[CHECKSUM_RANGE] = b"%06o\0 " % (chksum, ) return original_write(data)
def update_event(self, inp=-1): self.set_output_val(0, tarfile.calc_chksums(self.input(0)))