xref: /freebsd/tests/sys/fs/tarfs/mktar.c (revision 9768746b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023 Klara, Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 
31 #include <err.h>
32 #include <fcntl.h>
33 #include <paths.h>
34 #include <stdarg.h>
35 #include <stdbool.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 #define PROGNAME	"mktar"
42 
43 #define SUBDIRNAME	"directory"
44 #define EMPTYDIRNAME	"empty"
45 #define NORMALFILENAME	"file"
46 #define SPARSEFILENAME	"sparse_file"
47 #define HARDLINKNAME	"hard_link"
48 #define SHORTLINKNAME	"short_link"
49 #define LONGLINKNAME	"long_link"
50 
51 static bool opt_v;
52 
53 static void verbose(const char *fmt, ...)
54 {
55 	va_list ap;
56 
57 	if (!opt_v)
58 		return;
59 	fprintf(stderr, "%s: ", PROGNAME);
60 	va_start(ap, fmt);
61 	vfprintf(stderr, fmt, ap);
62 	va_end(ap);
63 	fprintf(stderr, "\n");
64 }
65 
66 static void
67 mknormalfile(const char *filename, mode_t mode)
68 {
69 	char buf[512];
70 	ssize_t res;
71 	int fd;
72 
73 	if ((fd = open(filename, O_RDWR|O_CREAT|O_EXCL, mode)) < 0)
74 		err(1, "%s", filename);
75 	for (unsigned int i = 0; i < sizeof(buf); i++)
76 		buf[i] = 32 + i % 64;
77 	res = write(fd, buf, sizeof(buf));
78 	if (res < 0)
79 		err(1, "%s", filename);
80 	if (res != sizeof(buf))
81 		errx(1, "%s: short write", filename);
82 	close(fd);
83 }
84 
85 static void
86 mksparsefile(const char *filename, mode_t mode)
87 {
88 	char buf[511];
89 	ssize_t res;
90 	int fd;
91 
92 	if ((fd = open(filename, O_RDWR|O_CREAT|O_EXCL, mode)) < 0)
93 		err(1, "%s", filename);
94 	for (unsigned int i = 33; i <= 126; i++) {
95 		memset(buf, i, sizeof(buf));
96 		if (lseek(fd, 1048576LU * (i - 32), SEEK_SET) < 0)
97 			err(1, "%s", filename);
98 		res = write(fd, buf, sizeof(buf));
99 		if (res < 0)
100 			err(1, "%s", filename);
101 		if (res != sizeof(buf))
102 			errx(1, "%s: short write", filename);
103 	}
104 	close(fd);
105 }
106 
107 static char *
108 mklonglinktarget(const char *dirname, const char *filename)
109 {
110 	char *piece, *target;
111 
112 	if (asprintf(&piece, "%1$s/../%1$s/../%1$s/../%1$s/../", dirname) < 0)
113 		err(1, "asprintf()");
114 	if (asprintf(&target, "%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%2$s", piece, filename) < 0)
115 		err(1, "asprintf()");
116 	free(piece);
117 	return target;
118 }
119 
120 static void
121 mktar(void)
122 {
123 	char *linktarget;
124 
125 	/* create a subdirectory */
126 	verbose("mkdir %s", SUBDIRNAME);
127 	if (mkdir(SUBDIRNAME, 0755) != 0)
128 		err(1, "%s", SUBDIRNAME);
129 
130 	/* create a second subdirectory which will remain empty */
131 	verbose("mkdir %s", EMPTYDIRNAME);
132 	if (mkdir(EMPTYDIRNAME, 0755) != 0)
133 		err(1, "%s", EMPTYDIRNAME);
134 
135 	/* create a normal file */
136 	verbose("creating %s", NORMALFILENAME);
137 	mknormalfile(NORMALFILENAME, 0644);
138 
139 	/* create a sparse file */
140 	verbose("creating %s", SPARSEFILENAME);
141 	mksparsefile(SPARSEFILENAME, 0644);
142 	chflags(SPARSEFILENAME, UF_NODUMP);
143 
144 	/* create a hard link */
145 	verbose("link %s %s", SPARSEFILENAME, HARDLINKNAME);
146 	if (link(SPARSEFILENAME, HARDLINKNAME) != 0)
147 		err(1, "%s", HARDLINKNAME);
148 
149 	/* create a symbolic link with a short target */
150 	verbose("symlink %s %s", SPARSEFILENAME, SHORTLINKNAME);
151 	if (symlink(SPARSEFILENAME, SHORTLINKNAME) != 0)
152 		err(1, "%s", SHORTLINKNAME);
153 
154 	/* create a symbolic link with a long target */
155 	linktarget = mklonglinktarget(SUBDIRNAME, SPARSEFILENAME);
156 	verbose("symlink %s %s", linktarget, LONGLINKNAME);
157 	if (symlink(linktarget, LONGLINKNAME) != 0)
158 		err(1, "%s", LONGLINKNAME);
159 	free(linktarget);
160 }
161 
162 static void
163 usage(void)
164 {
165 
166 	fprintf(stderr, "usage: %s [-v] tarfile\n", PROGNAME);
167 	exit(EXIT_FAILURE);
168 }
169 
170 int
171 main(int argc, char *argv[])
172 {
173 	const char *tarfilename;
174 	char *dirname;
175 	int opt, wstatus;
176 	pid_t pid;
177 
178 	while ((opt = getopt(argc, argv, "v")) != -1)
179 		switch (opt) {
180 		case 'v':
181 			opt_v = true;
182 			break;
183 		default:
184 			usage();
185 		}
186 
187 	argc -= optind;
188 	argv += optind;
189 
190 	if (argc != 1)
191 		usage();
192 	tarfilename = *argv;
193 
194 	if (asprintf(&dirname, "%s%s.XXXXXXXX", _PATH_TMP, PROGNAME) < 0)
195 		err(1, "asprintf()");
196 	if (mkdtemp(dirname) == NULL)
197 		err(1, "%s", dirname);
198 	verbose("mkdir %s", dirname);
199 
200 	/* fork a child to create the files */
201 	if ((pid = fork()) < 0)
202 		err(1, "fork()");
203 	if (pid == 0) {
204 		verbose("cd %s", dirname);
205 		if (chdir(dirname) != 0)
206 			err(1, "%s", dirname);
207 		verbose("umask 022");
208 		umask(022);
209 		mktar();
210 		verbose("cd -");
211 		exit(0);
212 	}
213 	if (waitpid(pid, &wstatus, 0) < 0)
214 		err(1, "waitpid()");
215 	if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
216 		errx(1, "child failed");
217 
218 	/* fork a child to create the tarball */
219 	if ((pid = fork()) < 0)
220 		err(1, "fork()");
221 	if (pid == 0) {
222 		verbose("creating tarball");
223 		execlp("tar", "tar",
224 		    "-c",
225 		    "-f", tarfilename,
226 		    "-C", dirname,
227 		    "--zstd",
228 #if 0
229 		    "--options", "zstd:frame-per-file",
230 #endif
231 		    "./" EMPTYDIRNAME "/../" NORMALFILENAME,
232 		    "./" SPARSEFILENAME,
233 		    "./" HARDLINKNAME,
234 		    "./" SHORTLINKNAME,
235 		    "./" SUBDIRNAME,
236 		    "./" LONGLINKNAME,
237 		    NULL);
238 		err(1, "execlp()");
239 	}
240 	if (waitpid(pid, &wstatus, 0) < 0)
241 		err(1, "waitpid()");
242 	if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
243 		errx(1, "child failed");
244 
245 	/* fork a child to delete everything */
246 	if ((pid = fork()) < 0)
247 		err(1, "fork()");
248 	if (pid == 0) {
249 		verbose("cd %s", dirname);
250 		if (chdir(dirname) != 0)
251 			err(1, "%s", dirname);
252 		verbose("rm %s", LONGLINKNAME);
253 		(void)unlink(LONGLINKNAME);
254 		verbose("rm %s", SHORTLINKNAME);
255 		(void)unlink(SHORTLINKNAME);
256 		verbose("rm %s", HARDLINKNAME);
257 		(void)unlink(HARDLINKNAME);
258 		verbose("rm %s", SPARSEFILENAME);
259 		(void)unlink(SPARSEFILENAME);
260 		verbose("rmdir %s", EMPTYDIRNAME);
261 		(void)rmdir(EMPTYDIRNAME);
262 		verbose("rmdir %s", SUBDIRNAME);
263 		(void)rmdir(SUBDIRNAME);
264 		verbose("cd -");
265 		exit(0);
266 	}
267 	if (waitpid(pid, &wstatus, 0) < 0)
268 		err(1, "waitpid()");
269 	if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
270 		errx(1, "child failed");
271 	verbose("rmdir %s", dirname);
272 	(void)rmdir(dirname);
273 
274 	exit(0);
275 }
276