1 /*- 2 * Copyright (c) 2024 Klara, Inc. 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * This program reads a tarball from stdin, recalculates the checksums of 7 * all ustar records within it, and writes the result to stdout. 8 */ 9 10 #include <err.h> 11 #include <stdarg.h> 12 #include <stdbool.h> 13 #include <stdint.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <unistd.h> 18 19 static bool opt_v; 20 21 static int 22 verbose(const char *fmt, ...) 23 { 24 va_list ap; 25 int ret; 26 27 if (!opt_v) 28 return (0); 29 va_start(ap, fmt); 30 ret = vfprintf(stderr, fmt, ap); 31 va_end(ap); 32 return (ret); 33 } 34 35 static void 36 tarsum(FILE *in, const char *ifn, FILE *out, const char *ofn) 37 { 38 union { 39 uint8_t bytes[512]; 40 struct { 41 uint8_t prelude[148]; 42 char checksum[8]; 43 uint8_t interlude[101]; 44 char magic[6]; 45 char version[2]; 46 char postlude[]; 47 }; 48 } ustar; 49 unsigned long sum; 50 off_t offset = 0; 51 ssize_t ret; 52 size_t i; 53 54 for (;;) { 55 if ((ret = fread(&ustar, 1, sizeof(ustar), in)) < 0) 56 err(1, "%s", ifn); 57 else if (ret == 0) 58 break; 59 else if ((size_t)ret < sizeof(ustar)) 60 errx(1, "%s: Short read", ifn); 61 if (strcmp(ustar.magic, "ustar") == 0 && 62 ustar.version[0] == '0' && ustar.version[1] == '0') { 63 verbose("header found at offset %#lx\n", offset); 64 verbose("current checksum %.*s\n", 65 (int)sizeof(ustar.checksum), ustar.checksum); 66 memset(ustar.checksum, ' ', sizeof(ustar.checksum)); 67 for (sum = i = 0; i < sizeof(ustar); i++) 68 sum += ustar.bytes[i]; 69 verbose("calculated checksum %#lo\n", sum); 70 sprintf(ustar.checksum, "%#lo", sum); 71 } 72 if ((ret = fwrite(&ustar, 1, sizeof(ustar), out)) < 0) 73 err(1, "%s", ofn); 74 else if ((size_t)ret < sizeof(ustar)) 75 errx(1, "%s: Short write", ofn); 76 offset += sizeof(ustar); 77 } 78 verbose("%lu bytes processed\n", offset); 79 } 80 81 static void 82 usage(void) 83 { 84 fprintf(stderr, "usage: tarsum [-v] [-o output] [input]\n"); 85 exit(1); 86 } 87 88 int 89 main(int argc, char *argv[]) 90 { 91 const char *ifn, *ofn = NULL; 92 FILE *in, *out; 93 int opt; 94 95 while ((opt = getopt(argc, argv, "o:v")) != -1) { 96 switch (opt) { 97 case 'o': 98 ofn = optarg; 99 break; 100 case 'v': 101 opt_v = true; 102 break; 103 default: 104 usage(); 105 } 106 } 107 argc -= optind; 108 argv += optind; 109 if (argc == 0 || strcmp(*argv, "-") == 0) { 110 ifn = "stdin"; 111 in = stdin; 112 } else if (argc == 1) { 113 ifn = *argv; 114 if ((in = fopen(ifn, "rb")) == NULL) 115 err(1, "%s", ifn); 116 } else { 117 usage(); 118 } 119 if (ofn == NULL || strcmp(ofn, "-") == 0) { 120 ofn = "stdout"; 121 out = stdout; 122 } else { 123 if ((out = fopen(ofn, "wb")) == NULL) 124 err(1, "%s", ofn); 125 } 126 tarsum(in, ifn, out, ofn); 127 return (0); 128 } 129