tar.c (3841B)
1 #include "tar.h" 2 3 #include <stdio.h> 4 #include <string.h> 5 6 #define TAR_BLOCK 512u 7 #define TAR_NAME_OFF 0u 8 #define TAR_MODE_OFF 100u 9 #define TAR_UID_OFF 108u 10 #define TAR_GID_OFF 116u 11 #define TAR_SIZE_OFF 124u 12 #define TAR_MTIME_OFF 136u 13 #define TAR_CHKSUM_OFF 148u 14 #define TAR_TYPE_OFF 156u 15 #define TAR_MAGIC_OFF 257u 16 #define TAR_CHKSUM_LEN 8u 17 #define TAR_DEFAULT_MODE 0644u 18 19 /* Round `n` up to the next multiple of TAR_BLOCK. */ 20 static size_t tar_round(size_t n) { 21 return (n + (TAR_BLOCK - 1u)) & ~(size_t)(TAR_BLOCK - 1u); 22 } 23 24 static unsigned tar_checksum(const uint8_t* hdr) { 25 unsigned sum = 0; 26 size_t i; 27 for (i = 0; i < TAR_BLOCK; ++i) { 28 /* The checksum field itself is treated as spaces. */ 29 if (i >= TAR_CHKSUM_OFF && i < TAR_CHKSUM_OFF + TAR_CHKSUM_LEN) { 30 sum += (unsigned)' '; 31 } else { 32 sum += hdr[i]; 33 } 34 } 35 return sum; 36 } 37 38 static int tar_write(KitWriter* out, const void* data, size_t n) { 39 return kit_writer_write(out, data, n) == KIT_OK ? DIST_OK : DIST_ERR; 40 } 41 42 int dist_tar_append(KitWriter* out, const char* name, const uint8_t* data, 43 size_t size) { 44 uint8_t hdr[TAR_BLOCK]; 45 size_t namelen = strlen(name); 46 unsigned sum; 47 static const uint8_t zero[TAR_BLOCK] = {0}; 48 size_t pad; 49 50 if (namelen >= DIST_PATH_MAX) return DIST_ERR; 51 52 memset(hdr, 0, sizeof hdr); 53 memcpy(hdr + TAR_NAME_OFF, name, namelen); 54 snprintf((char*)hdr + TAR_MODE_OFF, 8, "%07o", TAR_DEFAULT_MODE); 55 snprintf((char*)hdr + TAR_UID_OFF, 8, "%07o", 0u); 56 snprintf((char*)hdr + TAR_GID_OFF, 8, "%07o", 0u); 57 snprintf((char*)hdr + TAR_SIZE_OFF, 12, "%011o", (unsigned)size); 58 snprintf((char*)hdr + TAR_MTIME_OFF, 12, "%011o", 0u); 59 hdr[TAR_TYPE_OFF] = '0'; 60 memcpy(hdr + TAR_MAGIC_OFF, "ustar", 5); /* trailing NUL already zeroed */ 61 hdr[TAR_MAGIC_OFF + 6] = '0'; 62 hdr[TAR_MAGIC_OFF + 7] = '0'; 63 64 sum = tar_checksum(hdr); 65 /* "%06o\0 " — six octal digits, NUL, then a space. */ 66 snprintf((char*)hdr + TAR_CHKSUM_OFF, 7, "%06o", sum); 67 hdr[TAR_CHKSUM_OFF + 7] = ' '; 68 69 if (tar_write(out, hdr, TAR_BLOCK) != DIST_OK) return DIST_ERR; 70 if (size && tar_write(out, data, size) != DIST_OK) return DIST_ERR; 71 pad = tar_round(size) - size; 72 if (pad && tar_write(out, zero, pad) != DIST_OK) return DIST_ERR; 73 return DIST_OK; 74 } 75 76 int dist_tar_finish(KitWriter* out) { 77 static const uint8_t zero[TAR_BLOCK] = {0}; 78 if (tar_write(out, zero, TAR_BLOCK) != DIST_OK) return DIST_ERR; 79 if (tar_write(out, zero, TAR_BLOCK) != DIST_OK) return DIST_ERR; 80 return DIST_OK; 81 } 82 83 /* Parse an octal field of `len` bytes (space/NUL terminated). */ 84 static size_t tar_parse_octal(const uint8_t* field, size_t len) { 85 size_t v = 0; 86 size_t i; 87 for (i = 0; i < len; ++i) { 88 uint8_t c = field[i]; 89 if (c == ' ' || c == '\0') break; 90 if (c < '0' || c > '7') break; 91 v = v * 8u + (size_t)(c - '0'); 92 } 93 return v; 94 } 95 96 static int tar_block_is_zero(const uint8_t* b) { 97 size_t i; 98 for (i = 0; i < TAR_BLOCK; ++i) { 99 if (b[i]) return 0; 100 } 101 return 1; 102 } 103 104 int dist_tar_iter(const uint8_t* data, size_t len, DistTarEntry* out, 105 size_t cap, size_t* count) { 106 size_t off = 0; 107 size_t n = 0; 108 while (off + TAR_BLOCK <= len) { 109 const uint8_t* hdr = data + off; 110 size_t size; 111 if (tar_block_is_zero(hdr)) break; /* end-of-archive marker */ 112 if (tar_checksum(hdr) != 113 tar_parse_octal(hdr + TAR_CHKSUM_OFF, TAR_CHKSUM_LEN)) { 114 return DIST_ERR; 115 } 116 if (n >= cap) return DIST_ERR; 117 size = tar_parse_octal(hdr + TAR_SIZE_OFF, 12); 118 if (off + TAR_BLOCK + size > len) return DIST_ERR; /* truncated */ 119 memcpy(out[n].name, hdr + TAR_NAME_OFF, DIST_PATH_MAX); 120 out[n].name[DIST_PATH_MAX] = '\0'; 121 out[n].data = data + off + TAR_BLOCK; 122 out[n].size = size; 123 ++n; 124 off += TAR_BLOCK + tar_round(size); 125 } 126 *count = n; 127 return DIST_OK; 128 }