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
verbose(const char * fmt,...)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
tarsum(FILE * in,const char * ifn,FILE * out,const char * ofn)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
usage(void)82 usage(void)
83 {
84 fprintf(stderr, "usage: tarsum [-v] [-o output] [input]\n");
85 exit(1);
86 }
87
88 int
main(int argc,char * argv[])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