xref: /freebsd/tests/sys/fs/tarfs/mktar.c (revision 2af158ae8313b250077f5ba60dc41fbed4e711af)
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 NORMALFILENAME	"file"
45 #define SPARSEFILENAME	"sparse_file"
46 #define HARDLINKNAME	"hard_link"
47 #define SHORTLINKNAME	"short_link"
48 #define LONGLINKNAME	"long_link"
49 
50 static bool opt_g;
51 static bool opt_v;
52 
verbose(const char * fmt,...)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
mknormalfile(const char * filename,mode_t mode)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
mksparsefile(const char * filename,mode_t mode)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 *
mklonglinktarget(const char * dirname,const char * filename)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
mktar(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 normal file */
131 	verbose("creating %s", NORMALFILENAME);
132 	mknormalfile(NORMALFILENAME, 0644);
133 
134 	/* create a sparse file */
135 	verbose("creating %s", SPARSEFILENAME);
136 	mksparsefile(SPARSEFILENAME, 0644);
137 	chflags(SPARSEFILENAME, UF_NODUMP);
138 
139 	/* create a hard link */
140 	verbose("link %s %s", SPARSEFILENAME, HARDLINKNAME);
141 	if (link(SPARSEFILENAME, HARDLINKNAME) != 0)
142 		err(1, "%s", HARDLINKNAME);
143 
144 	/* create a symbolic link with a short target */
145 	verbose("symlink %s %s", SPARSEFILENAME, SHORTLINKNAME);
146 	if (symlink(SPARSEFILENAME, SHORTLINKNAME) != 0)
147 		err(1, "%s", SHORTLINKNAME);
148 
149 	/* create a symbolic link with a long target */
150 	linktarget = mklonglinktarget(SUBDIRNAME, SPARSEFILENAME);
151 	verbose("symlink %s %s", linktarget, LONGLINKNAME);
152 	if (symlink(linktarget, LONGLINKNAME) != 0)
153 		err(1, "%s", LONGLINKNAME);
154 	free(linktarget);
155 }
156 
157 static void
usage(void)158 usage(void)
159 {
160 
161 	fprintf(stderr, "usage: %s [-gv] tarfile\n", PROGNAME);
162 	exit(EXIT_FAILURE);
163 }
164 
165 int
main(int argc,char * argv[])166 main(int argc, char *argv[])
167 {
168 	const char *tarfilename;
169 	char *dirname;
170 	int opt, wstatus;
171 	pid_t pid;
172 
173 	while ((opt = getopt(argc, argv, "gv")) != -1)
174 		switch (opt) {
175 		case 'g':
176 			opt_g = true;
177 			break;
178 		case 'v':
179 			opt_v = true;
180 			break;
181 		default:
182 			usage();
183 		}
184 
185 	argc -= optind;
186 	argv += optind;
187 
188 	if (argc != 1)
189 		usage();
190 	tarfilename = *argv;
191 
192 	if (asprintf(&dirname, "%s%s.XXXXXXXX", _PATH_TMP, PROGNAME) < 0)
193 		err(1, "asprintf()");
194 	if (mkdtemp(dirname) == NULL)
195 		err(1, "%s", dirname);
196 	verbose("mkdir %s", dirname);
197 
198 	/* fork a child to create the files */
199 	if ((pid = fork()) < 0)
200 		err(1, "fork()");
201 	if (pid == 0) {
202 		verbose("cd %s", dirname);
203 		if (chdir(dirname) != 0)
204 			err(1, "%s", dirname);
205 		verbose("umask 022");
206 		umask(022);
207 		mktar();
208 		verbose("cd -");
209 		exit(0);
210 	}
211 	if (waitpid(pid, &wstatus, 0) < 0)
212 		err(1, "waitpid()");
213 	if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
214 		errx(1, "child failed");
215 
216 	/* fork a child to create the tarball */
217 	if ((pid = fork()) < 0)
218 		err(1, "fork()");
219 	if (pid == 0) {
220 		verbose("creating tarball");
221 		execlp(opt_g ? "gtar" : "tar",
222 		    "tar",
223 		    "-c",
224 		    "-f", tarfilename,
225 		    "-C", dirname,
226 		    "--posix",
227 		    "--zstd",
228 #if 0
229 		    "--options", "zstd:frame-per-file",
230 #endif
231 		    "./" SUBDIRNAME "/../" 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", SUBDIRNAME);
261 		(void)rmdir(SUBDIRNAME);
262 		verbose("cd -");
263 		exit(0);
264 	}
265 	if (waitpid(pid, &wstatus, 0) < 0)
266 		err(1, "waitpid()");
267 	if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
268 		errx(1, "child failed");
269 	verbose("rmdir %s", dirname);
270 	(void)rmdir(dirname);
271 
272 	exit(0);
273 }
274