xref: /freebsd/tests/sys/fs/tarfs/tarsum.c (revision e1e636193db45630c7881246d25902e57c43d24e)
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