From 667f937c00a63badda794bca5dec48c68f981a1c Mon Sep 17 00:00:00 2001 From: Thomas Forgione Date: Tue, 23 Jul 2024 14:40:20 +0200 Subject: [PATCH] Finished tar --- tar.py | 55 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/tar.py b/tar.py index 0394bcc..48c8dcf 100644 --- a/tar.py +++ b/tar.py @@ -1,3 +1,4 @@ +import builtins from flask import Response import functools import os @@ -5,6 +6,12 @@ import os # 4MiB chunks CHUNK_SIZE = 4_194_304 +# ASCII value for space +SPACE = ord(' ') + +# ASCII value for zero +ZERO = ord('0') + # Specification de l'entête du fichier # Numéro Nom Début Taille Description # 1 name 0 100 Nom du fichier @@ -19,45 +26,47 @@ CHUNK_SIZE = 4_194_304 def header_chunk(filename: str, filepath: str) -> bytes: + + # Returns the octal representation without the initial + def oct(i: int) -> str: + return builtins.oct(i)[2:] + stat = os.stat(filepath) - buffer = bytearray() + buffer = bytearray(512) # Field 1: filename on 100 bytes - buffer += filename.encode('ascii') - buffer += b'\x00' * (100 - len(filename)) + buffer[0:len(filename)] = filename.encode('ascii') - # Field 2: mode, on 8 bytes - # TODO we put 777 for test - buffer += '0000777'.encode('ascii') + b'\x00' + # Field 2: mode, on 8 bytes, octal, last byte must be \x00, so we set only the first 7 bytes + buffer[100:107] = oct(stat.st_mode).rjust(7, '0').encode('ascii') - # Field 3: owner, we put 1000, default user - buffer += '0001000'.encode('ascii') + b'\x00' + # Field 3: owner, on 8 bytes, octal, last byte must be \x00, so we set only the first 7 bytes + buffer[108:115] = oct(stat.st_uid).rjust(7, '0').encode('ascii') - # Field 4: group, we put 1000 - buffer += '0001000'.encode('ascii') + b'\x00' + # Field 4: group, on 8 bytes, octal, last byte must be \x00, so we set only the first 7 bytes + buffer[116:123] = oct(stat.st_gid).rjust(7, '0').encode('ascii') - # Field 5: file size in bytes in ascii - buffer += oct(stat.st_size)[2:].rjust(11, '0').encode('ascii') + b'\x00' + # Field 5: file size in bytes, on 12 bytes, octal, last byte must be \x00, so we set only the first 11 bytes + buffer[124:135] = oct(stat.st_size).rjust(11, '0').encode('ascii') - # Field 6: last modified, zeros for now - # buffer += '00000000000'.encode('ascii') + b'\x00' - buffer += oct(1721728914)[2:].rjust(11, '0').encode('ascii') + b'\x00' + # Field 6: last modified, on 12 bytes, octal, last byte must be \x00, so we set only the first 11 bytes + buffer[136:147] = oct(int(stat.st_mtime)).rjust(11, '0').encode('ascii') - # Field 7: checksum, we put spaces and we will edit it at the end - buffer += ' '.encode('ascii') + # Field 7: checksum, we fill it at the end # Field 8: type flag, 0 because we only have regular files - buffer += b'0' + buffer[156] = ZERO # Field 9: linkname, \x00s because we only have regular files - buffer += b'\x00' * 100 # POSIX 1003.1-1990: 255 empty bytes - buffer += b'\x00' * 255 - # Compute the checksum - checksum = oct(functools.reduce(lambda x, y: x + y, buffer))[2:].rjust(6, '0').encode('ascii') + b'\x00 ' - buffer[148:156] = checksum + # Compute the checksum: we start at 248 which are the 8 fields of checksum filled with spaces (32 * 8) + checksum = oct(functools.reduce(lambda x, y: x + y, buffer, 256)).rjust(6, '0').encode('ascii') + buffer[148:154] = checksum + + # Don't ask me why, but the checksum must end with b'\x00 ', so we skip the \x00 and write the space + buffer[155] = SPACE return bytes(buffer)