pax-tar.c (11537B)
1 /* mk/pax-tar.c - data-tar writer for the UNOS apk driver. 2 * 3 * Usage: pax-tar [--no-checksum] <stagedir> <out.tar> 4 * 5 * Writes a deterministic tar of <stagedir>: entries sorted by name, 6 * uid/gid 0, user/group root. Default mode adds a per-file SHA1 in an 7 * `APK-TOOLS.checksum.SHA1` pax extended-header record ahead of every 8 * regular file (the apk v2 data-segment layout `abuild-tar` produces); 9 * `--no-checksum` skips those records (control and signature segments). 10 * 11 * Only regular files, directories and symlinks are accepted; anything else 12 * is a loud error. SHA1 is implemented below from FIPS 180-4 (no crypto 13 * dependency); its output is verified against the system sha1sum during 14 * driver testing. File mtimes come from SOURCE_DATE_EPOCH when set, else 15 * from the filesystem. 16 */ 17 #define _POSIX_C_SOURCE 200809L 18 #include <dirent.h> 19 #include <stdint.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <sys/stat.h> 24 #include <unistd.h> 25 26 /* ---------------- SHA1 (FIPS 180-4) ---------------- */ 27 28 typedef struct { 29 uint32_t h[5]; 30 uint64_t len; /* total message length in bytes */ 31 uint8_t buf[64]; 32 size_t buflen; 33 } sha1_t; 34 35 static uint32_t rol(uint32_t v, int n) { return (v << n) | (v >> (32 - n)); } 36 37 static void sha1_block(sha1_t *c, const uint8_t *p) { 38 uint32_t w[80]; 39 for (int i = 0; i < 16; i++) 40 w[i] = ((uint32_t)p[4 * i] << 24) | ((uint32_t)p[4 * i + 1] << 16) | 41 ((uint32_t)p[4 * i + 2] << 8) | p[4 * i + 3]; 42 for (int i = 16; i < 80; i++) 43 w[i] = rol(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); 44 uint32_t a = c->h[0], b = c->h[1], v = c->h[2], d = c->h[3], e = c->h[4]; 45 for (int i = 0; i < 80; i++) { 46 uint32_t f, k; 47 if (i < 20) { 48 f = (b & v) | (~b & d); 49 k = 0x5a827999; 50 } else if (i < 40) { 51 f = b ^ v ^ d; 52 k = 0x6ed9eba1; 53 } else if (i < 60) { 54 f = (b & v) | (b & d) | (v & d); 55 k = 0x8f1bbcdc; 56 } else { 57 f = b ^ v ^ d; 58 k = 0xca62c1d6; 59 } 60 uint32_t t = rol(a, 5) + f + e + k + w[i]; 61 e = d; 62 d = v; 63 v = rol(b, 30); 64 b = a; 65 a = t; 66 } 67 c->h[0] += a; 68 c->h[1] += b; 69 c->h[2] += v; 70 c->h[3] += d; 71 c->h[4] += e; 72 } 73 74 static void sha1_init(sha1_t *c) { 75 c->h[0] = 0x67452301; 76 c->h[1] = 0xefcdab89; 77 c->h[2] = 0x98badcfe; 78 c->h[3] = 0x10325476; 79 c->h[4] = 0xc3d2e1f0; 80 c->len = 0; 81 c->buflen = 0; 82 } 83 84 static void sha1_update(sha1_t *c, const uint8_t *data, size_t n) { 85 c->len += n; 86 while (n > 0) { 87 size_t take = 64 - c->buflen; 88 if (take > n) 89 take = n; 90 memcpy(c->buf + c->buflen, data, take); 91 c->buflen += take; 92 data += take; 93 n -= take; 94 if (c->buflen == 64) { 95 sha1_block(c, c->buf); 96 c->buflen = 0; 97 } 98 } 99 } 100 101 static void sha1_final(sha1_t *c, uint8_t out[20]) { 102 uint64_t bits = c->len * 8; 103 uint8_t one = 0x80; 104 sha1_update(c, &one, 1); 105 uint8_t zero = 0; 106 while (c->buflen != 56) 107 sha1_update(c, &zero, 1); 108 uint8_t lenbuf[8]; 109 for (int i = 0; i < 8; i++) 110 lenbuf[i] = (uint8_t)(bits >> (56 - 8 * i)); 111 /* feed length directly: buffer is at 56, one block completes it */ 112 memcpy(c->buf + 56, lenbuf, 8); 113 sha1_block(c, c->buf); 114 for (int i = 0; i < 5; i++) { 115 out[4 * i] = (uint8_t)(c->h[i] >> 24); 116 out[4 * i + 1] = (uint8_t)(c->h[i] >> 16); 117 out[4 * i + 2] = (uint8_t)(c->h[i] >> 8); 118 out[4 * i + 3] = (uint8_t)c->h[i]; 119 } 120 } 121 122 static void sha1_buf_hex(const uint8_t *data, size_t n, char out[41]) { 123 sha1_t c; 124 sha1_init(&c); 125 sha1_update(&c, data, n); 126 uint8_t digest[20]; 127 sha1_final(&c, digest); 128 for (int i = 0; i < 20; i++) 129 sprintf(out + 2 * i, "%02x", digest[i]); 130 out[40] = '\0'; 131 } 132 133 static void sha1_file_hex(const char *path, char out[41]) { 134 FILE *f = fopen(path, "rb"); 135 if (!f) { 136 perror(path); 137 exit(1); 138 } 139 sha1_t c; 140 sha1_init(&c); 141 uint8_t buf[65536]; 142 size_t n; 143 while ((n = fread(buf, 1, sizeof buf, f)) > 0) 144 sha1_update(&c, buf, n); 145 if (ferror(f)) { 146 perror(path); 147 exit(1); 148 } 149 fclose(f); 150 uint8_t digest[20]; 151 sha1_final(&c, digest); 152 for (int i = 0; i < 20; i++) 153 sprintf(out + 2 * i, "%02x", digest[i]); 154 out[40] = '\0'; 155 } 156 157 /* ---------------- tar writer ---------------- */ 158 159 typedef struct { 160 char *arc; /* archive name, no leading ./ */ 161 char *full; /* filesystem path */ 162 struct stat st; 163 } entry_t; 164 165 static entry_t *entries; 166 static size_t nentries, capentries; 167 168 static void push_entry(const char *arc, const char *full, struct stat *st) { 169 if (nentries == capentries) { 170 capentries = capentries ? capentries * 2 : 256; 171 entries = realloc(entries, capentries * sizeof *entries); 172 if (!entries) { 173 perror("realloc"); 174 exit(1); 175 } 176 } 177 entries[nentries].arc = strdup(arc); 178 entries[nentries].full = strdup(full); 179 entries[nentries].st = *st; 180 nentries++; 181 } 182 183 static void walk(const char *full, const char *arc) { 184 DIR *d = opendir(full); 185 if (!d) { 186 perror(full); 187 exit(1); 188 } 189 struct dirent *de; 190 while ((de = readdir(d))) { 191 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) 192 continue; 193 char sub_full[8192], sub_arc[8192]; 194 snprintf(sub_full, sizeof sub_full, "%s/%s", full, de->d_name); 195 if (arc[0]) 196 snprintf(sub_arc, sizeof sub_arc, "%s/%s", arc, de->d_name); 197 else 198 snprintf(sub_arc, sizeof sub_arc, "%s", de->d_name); 199 struct stat st; 200 if (lstat(sub_full, &st) != 0) { 201 perror(sub_full); 202 exit(1); 203 } 204 if (S_ISDIR(st.st_mode)) 205 walk(sub_full, sub_arc); 206 push_entry(sub_arc, sub_full, &st); 207 } 208 closedir(d); 209 } 210 211 static int cmp_entry(const void *a, const void *b) { 212 return strcmp(((const entry_t *)a)->arc, ((const entry_t *)b)->arc); 213 } 214 215 static void put_oct(char *dst, size_t w, uint64_t v) { 216 /* w includes the trailing NUL; value left-padded with '0' */ 217 dst[w - 1] = '\0'; 218 for (size_t i = w - 1; i > 0;) { 219 i--; 220 dst[i] = (char)('0' + (v & 7)); 221 v >>= 3; 222 } 223 } 224 225 static void write_zeros(FILE *out, size_t n) { 226 static const char z[512]; 227 while (n > 0) { 228 size_t take = n > sizeof z ? sizeof z : n; 229 if (fwrite(z, 1, take, out) != take) { 230 perror("fwrite"); 231 exit(1); 232 } 233 n -= take; 234 } 235 } 236 237 /* Split name into ustar name/prefix at a '/' boundary. Dies if impossible. */ 238 static void split_name(const char *arc, char *name, char *prefix) { 239 if (strlen(arc) <= 100) { 240 strcpy(name, arc); 241 prefix[0] = '\0'; 242 return; 243 } 244 size_t len = strlen(arc); 245 /* latest '/' that leaves <=155 prefix bytes and <=100 name bytes */ 246 size_t cut = 0; 247 for (size_t i = 0; i < len; i++) { 248 if (arc[i] == '/' && i <= 155 && len - (i + 1) <= 100) 249 cut = i; 250 } 251 if (!cut) { 252 fprintf(stderr, "pax-tar: name too long: %s\n", arc); 253 exit(1); 254 } 255 memcpy(prefix, arc, cut); 256 prefix[cut] = '\0'; 257 strcpy(name, arc + cut + 1); 258 } 259 260 static void write_header(FILE *out, const char *arc, mode_t mode, 261 uint64_t size, uint64_t mtime, char type, 262 const char *linkname) { 263 char blk[512]; 264 char name[100], prefix[155]; 265 memset(blk, 0, sizeof blk); 266 split_name(arc, name, prefix); 267 memcpy(blk + 0, name, strlen(name)); 268 put_oct(blk + 100, 8, (uint64_t)mode & 07777); 269 put_oct(blk + 108, 8, 0); 270 put_oct(blk + 116, 8, 0); 271 put_oct(blk + 124, 12, size); 272 put_oct(blk + 136, 12, mtime); 273 memset(blk + 148, ' ', 8); 274 blk[156] = type; 275 if (linkname) 276 memcpy(blk + 157, linkname, strlen(linkname)); 277 memcpy(blk + 257, "ustar", 5); 278 blk[262] = '\0'; 279 memcpy(blk + 263, "00", 2); 280 memcpy(blk + 265, "root", 4); 281 memcpy(blk + 297, "root", 4); 282 memcpy(blk + 345, prefix, strlen(prefix)); 283 unsigned sum = 0; 284 for (int i = 0; i < 512; i++) 285 sum += (unsigned char)blk[i]; 286 char chk[8]; 287 snprintf(chk, sizeof chk, "%06o", sum); 288 memcpy(blk + 148, chk, 6); 289 blk[154] = '\0'; 290 blk[155] = ' '; 291 if (fwrite(blk, 1, sizeof blk, out) != sizeof blk) { 292 perror("fwrite"); 293 exit(1); 294 } 295 } 296 297 /* pax extended-header record: "<len> <keyword>=<value>\n" */ 298 static void write_pax_record(FILE *out, const char *keyword, 299 const char *value, uint64_t mtime) { 300 size_t kv = strlen(keyword) + 1 + strlen(value) + 1; /* "k=v\n" */ 301 size_t len = kv + 2; /* first guess for digits + space */ 302 char digits[32]; 303 for (;;) { 304 snprintf(digits, sizeof digits, "%zu", len); 305 size_t need = kv + strlen(digits) + 1; 306 if (need == len) 307 break; 308 len = need; 309 } 310 char *rec = malloc(len + 1); 311 snprintf(rec, len + 1, "%s %s=%s\n", digits, keyword, value); 312 /* extended-header header block uses typeflag 'x'; name content-free */ 313 write_header(out, "pax-header", 0, len, mtime, 'x', NULL); 314 if (fwrite(rec, 1, len, out) != len) { 315 perror("fwrite"); 316 exit(1); 317 } 318 free(rec); 319 write_zeros(out, (512 - (len % 512)) % 512); 320 } 321 322 static uint64_t now_or_epoch(void) { 323 const char *e = getenv("SOURCE_DATE_EPOCH"); 324 if (e && e[0]) 325 return (uint64_t)atoll(e); 326 return 0; /* 0 = keep each file's own mtime */ 327 } 328 329 int main(int argc, char **argv) { 330 int checksums = 1; 331 if (argc == 4 && !strcmp(argv[1], "--no-checksum")) { 332 checksums = 0; 333 argv++; 334 argc--; 335 } 336 if (argc != 3) { 337 fprintf(stderr, "usage: pax-tar [--no-checksum] <stagedir> <out.tar>\n"); 338 return 1; 339 } 340 uint64_t epoch = now_or_epoch(); 341 walk(argv[1], ""); 342 qsort(entries, nentries, sizeof *entries, cmp_entry); 343 344 FILE *out = fopen(argv[2], "wb"); 345 if (!out) { 346 perror(argv[2]); 347 return 1; 348 } 349 for (size_t i = 0; i < nentries; i++) { 350 entry_t *e = &entries[i]; 351 uint64_t mtime = 352 epoch ? epoch : (uint64_t)e->st.st_mtime; 353 if (S_ISDIR(e->st.st_mode)) { 354 char *slash = malloc(strlen(e->arc) + 2); 355 sprintf(slash, "%s/", e->arc); 356 write_header(out, slash, e->st.st_mode, 0, mtime, '5', NULL); 357 free(slash); 358 } else if (S_ISREG(e->st.st_mode)) { 359 char hex[41]; 360 uint64_t fsize = (uint64_t)e->st.st_size; 361 if (checksums) { 362 sha1_file_hex(e->full, hex); 363 write_pax_record(out, "APK-TOOLS.checksum.SHA1", hex, mtime); 364 } 365 write_header(out, e->arc, e->st.st_mode, fsize, 366 mtime, '0', NULL); 367 FILE *f = fopen(e->full, "rb"); 368 if (!f) { 369 perror(e->full); 370 return 1; 371 } 372 char buf[65536]; 373 size_t n; 374 uint64_t left = fsize; 375 while (left > 0 && 376 (n = fread(buf, 1, left > sizeof buf ? sizeof buf : left, f)) > 377 0) { 378 if (fwrite(buf, 1, n, out) != n) { 379 perror("fwrite"); 380 return 1; 381 } 382 left -= n; 383 } 384 fclose(f); 385 if (left != 0) { 386 fprintf(stderr, "pax-tar: short read: %s\n", e->full); 387 return 1; 388 } 389 write_zeros(out, (512 - (fsize % 512)) % 512); 390 } else if (S_ISLNK(e->st.st_mode)) { 391 char target[1024]; 392 ssize_t n = readlink(e->full, target, sizeof target - 1); 393 if (n < 0) { 394 perror(e->full); 395 return 1; 396 } 397 target[n] = '\0'; 398 if (checksums) { 399 /* abuild-tar hashes the link target string for symlinks */ 400 char hex[41]; 401 sha1_buf_hex((uint8_t *)target, (size_t)n, hex); 402 write_pax_record(out, "APK-TOOLS.checksum.SHA1", hex, mtime); 403 } 404 write_header(out, e->arc, e->st.st_mode, 0, mtime, '2', target); 405 } else { 406 fprintf(stderr, "pax-tar: unsupported file type: %s\n", e->full); 407 return 1; 408 } 409 } 410 write_zeros(out, 1024); /* end-of-tar */ 411 if (fclose(out) != 0) { 412 perror("fclose"); 413 return 1; 414 } 415 return 0; 416 }