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 19static bool opt_v; 20 21static int 22verbose(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 35static void 36tarsum(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 81static void 82usage(void) 83{ 84 fprintf(stderr, "usage: tarsum [-v] [-o output] [input]\n"); 85 exit(1); 86} 87 88int 89main(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