xref: /freebsd/tests/sys/fs/tarfs/mktar.c (revision 2af158ae)
169d94f4cSDag-Erling Smørgrav /*-
269d94f4cSDag-Erling Smørgrav  * SPDX-License-Identifier: BSD-2-Clause
369d94f4cSDag-Erling Smørgrav  *
469d94f4cSDag-Erling Smørgrav  * Copyright (c) 2023 Klara, Inc.
569d94f4cSDag-Erling Smørgrav  *
669d94f4cSDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
769d94f4cSDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
869d94f4cSDag-Erling Smørgrav  * are met:
969d94f4cSDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
1069d94f4cSDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer.
1169d94f4cSDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
1269d94f4cSDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
1369d94f4cSDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
1469d94f4cSDag-Erling Smørgrav  *
1569d94f4cSDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1669d94f4cSDag-Erling Smørgrav  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1769d94f4cSDag-Erling Smørgrav  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1869d94f4cSDag-Erling Smørgrav  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1969d94f4cSDag-Erling Smørgrav  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2069d94f4cSDag-Erling Smørgrav  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2169d94f4cSDag-Erling Smørgrav  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2269d94f4cSDag-Erling Smørgrav  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2369d94f4cSDag-Erling Smørgrav  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2469d94f4cSDag-Erling Smørgrav  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2569d94f4cSDag-Erling Smørgrav  * SUCH DAMAGE.
2669d94f4cSDag-Erling Smørgrav  */
2769d94f4cSDag-Erling Smørgrav 
2869d94f4cSDag-Erling Smørgrav #include <sys/stat.h>
2969d94f4cSDag-Erling Smørgrav #include <sys/wait.h>
3069d94f4cSDag-Erling Smørgrav 
3169d94f4cSDag-Erling Smørgrav #include <err.h>
3269d94f4cSDag-Erling Smørgrav #include <fcntl.h>
3369d94f4cSDag-Erling Smørgrav #include <paths.h>
3469d94f4cSDag-Erling Smørgrav #include <stdarg.h>
3569d94f4cSDag-Erling Smørgrav #include <stdbool.h>
3669d94f4cSDag-Erling Smørgrav #include <stdlib.h>
3769d94f4cSDag-Erling Smørgrav #include <stdio.h>
3869d94f4cSDag-Erling Smørgrav #include <string.h>
3969d94f4cSDag-Erling Smørgrav #include <unistd.h>
4069d94f4cSDag-Erling Smørgrav 
4169d94f4cSDag-Erling Smørgrav #define PROGNAME	"mktar"
4269d94f4cSDag-Erling Smørgrav 
4369d94f4cSDag-Erling Smørgrav #define SUBDIRNAME	"directory"
44ce6a0c77SDag-Erling Smørgrav #define NORMALFILENAME	"file"
4569d94f4cSDag-Erling Smørgrav #define SPARSEFILENAME	"sparse_file"
4669d94f4cSDag-Erling Smørgrav #define HARDLINKNAME	"hard_link"
4769d94f4cSDag-Erling Smørgrav #define SHORTLINKNAME	"short_link"
4869d94f4cSDag-Erling Smørgrav #define LONGLINKNAME	"long_link"
4969d94f4cSDag-Erling Smørgrav 
506cb78fa4SDag-Erling Smørgrav static bool opt_g;
5169d94f4cSDag-Erling Smørgrav static bool opt_v;
5269d94f4cSDag-Erling Smørgrav 
verbose(const char * fmt,...)5369d94f4cSDag-Erling Smørgrav static void verbose(const char *fmt, ...)
5469d94f4cSDag-Erling Smørgrav {
5569d94f4cSDag-Erling Smørgrav 	va_list ap;
5669d94f4cSDag-Erling Smørgrav 
5769d94f4cSDag-Erling Smørgrav 	if (!opt_v)
5869d94f4cSDag-Erling Smørgrav 		return;
5969d94f4cSDag-Erling Smørgrav 	fprintf(stderr, "%s: ", PROGNAME);
6069d94f4cSDag-Erling Smørgrav 	va_start(ap, fmt);
6169d94f4cSDag-Erling Smørgrav 	vfprintf(stderr, fmt, ap);
6269d94f4cSDag-Erling Smørgrav 	va_end(ap);
6369d94f4cSDag-Erling Smørgrav 	fprintf(stderr, "\n");
6469d94f4cSDag-Erling Smørgrav }
6569d94f4cSDag-Erling Smørgrav 
6669d94f4cSDag-Erling Smørgrav static void
mknormalfile(const char * filename,mode_t mode)67ce6a0c77SDag-Erling Smørgrav mknormalfile(const char *filename, mode_t mode)
68ce6a0c77SDag-Erling Smørgrav {
69ce6a0c77SDag-Erling Smørgrav 	char buf[512];
70ce6a0c77SDag-Erling Smørgrav 	ssize_t res;
71ce6a0c77SDag-Erling Smørgrav 	int fd;
72ce6a0c77SDag-Erling Smørgrav 
73ce6a0c77SDag-Erling Smørgrav 	if ((fd = open(filename, O_RDWR|O_CREAT|O_EXCL, mode)) < 0)
74ce6a0c77SDag-Erling Smørgrav 		err(1, "%s", filename);
75ce6a0c77SDag-Erling Smørgrav 	for (unsigned int i = 0; i < sizeof(buf); i++)
76ce6a0c77SDag-Erling Smørgrav 		buf[i] = 32 + i % 64;
77ce6a0c77SDag-Erling Smørgrav 	res = write(fd, buf, sizeof(buf));
78ce6a0c77SDag-Erling Smørgrav 	if (res < 0)
79ce6a0c77SDag-Erling Smørgrav 		err(1, "%s", filename);
80ce6a0c77SDag-Erling Smørgrav 	if (res != sizeof(buf))
81ce6a0c77SDag-Erling Smørgrav 		errx(1, "%s: short write", filename);
82ce6a0c77SDag-Erling Smørgrav 	close(fd);
83ce6a0c77SDag-Erling Smørgrav }
84ce6a0c77SDag-Erling Smørgrav 
85ce6a0c77SDag-Erling Smørgrav static void
mksparsefile(const char * filename,mode_t mode)8669d94f4cSDag-Erling Smørgrav mksparsefile(const char *filename, mode_t mode)
8769d94f4cSDag-Erling Smørgrav {
8869d94f4cSDag-Erling Smørgrav 	char buf[511];
8969d94f4cSDag-Erling Smørgrav 	ssize_t res;
9069d94f4cSDag-Erling Smørgrav 	int fd;
9169d94f4cSDag-Erling Smørgrav 
92ce6a0c77SDag-Erling Smørgrav 	if ((fd = open(filename, O_RDWR|O_CREAT|O_EXCL, mode)) < 0)
9369d94f4cSDag-Erling Smørgrav 		err(1, "%s", filename);
9469d94f4cSDag-Erling Smørgrav 	for (unsigned int i = 33; i <= 126; i++) {
9569d94f4cSDag-Erling Smørgrav 		memset(buf, i, sizeof(buf));
9669d94f4cSDag-Erling Smørgrav 		if (lseek(fd, 1048576LU * (i - 32), SEEK_SET) < 0)
9769d94f4cSDag-Erling Smørgrav 			err(1, "%s", filename);
9869d94f4cSDag-Erling Smørgrav 		res = write(fd, buf, sizeof(buf));
9969d94f4cSDag-Erling Smørgrav 		if (res < 0)
10069d94f4cSDag-Erling Smørgrav 			err(1, "%s", filename);
10169d94f4cSDag-Erling Smørgrav 		if (res != sizeof(buf))
10269d94f4cSDag-Erling Smørgrav 			errx(1, "%s: short write", filename);
10369d94f4cSDag-Erling Smørgrav 	}
10469d94f4cSDag-Erling Smørgrav 	close(fd);
10569d94f4cSDag-Erling Smørgrav }
10669d94f4cSDag-Erling Smørgrav 
10769d94f4cSDag-Erling Smørgrav static char *
mklonglinktarget(const char * dirname,const char * filename)10869d94f4cSDag-Erling Smørgrav mklonglinktarget(const char *dirname, const char *filename)
10969d94f4cSDag-Erling Smørgrav {
11069d94f4cSDag-Erling Smørgrav 	char *piece, *target;
11169d94f4cSDag-Erling Smørgrav 
11269d94f4cSDag-Erling Smørgrav 	if (asprintf(&piece, "%1$s/../%1$s/../%1$s/../%1$s/../", dirname) < 0)
11369d94f4cSDag-Erling Smørgrav 		err(1, "asprintf()");
11469d94f4cSDag-Erling Smørgrav 	if (asprintf(&target, "%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%2$s", piece, filename) < 0)
11569d94f4cSDag-Erling Smørgrav 		err(1, "asprintf()");
11669d94f4cSDag-Erling Smørgrav 	free(piece);
11769d94f4cSDag-Erling Smørgrav 	return target;
11869d94f4cSDag-Erling Smørgrav }
11969d94f4cSDag-Erling Smørgrav 
12069d94f4cSDag-Erling Smørgrav static void
mktar(void)12169d94f4cSDag-Erling Smørgrav mktar(void)
12269d94f4cSDag-Erling Smørgrav {
12369d94f4cSDag-Erling Smørgrav 	char *linktarget;
12469d94f4cSDag-Erling Smørgrav 
12569d94f4cSDag-Erling Smørgrav 	/* create a subdirectory */
12669d94f4cSDag-Erling Smørgrav 	verbose("mkdir %s", SUBDIRNAME);
12769d94f4cSDag-Erling Smørgrav 	if (mkdir(SUBDIRNAME, 0755) != 0)
12869d94f4cSDag-Erling Smørgrav 		err(1, "%s", SUBDIRNAME);
12969d94f4cSDag-Erling Smørgrav 
130ce6a0c77SDag-Erling Smørgrav 	/* create a normal file */
131ce6a0c77SDag-Erling Smørgrav 	verbose("creating %s", NORMALFILENAME);
132ce6a0c77SDag-Erling Smørgrav 	mknormalfile(NORMALFILENAME, 0644);
133ce6a0c77SDag-Erling Smørgrav 
13469d94f4cSDag-Erling Smørgrav 	/* create a sparse file */
13569d94f4cSDag-Erling Smørgrav 	verbose("creating %s", SPARSEFILENAME);
13669d94f4cSDag-Erling Smørgrav 	mksparsefile(SPARSEFILENAME, 0644);
13769d94f4cSDag-Erling Smørgrav 	chflags(SPARSEFILENAME, UF_NODUMP);
13869d94f4cSDag-Erling Smørgrav 
13969d94f4cSDag-Erling Smørgrav 	/* create a hard link */
14069d94f4cSDag-Erling Smørgrav 	verbose("link %s %s", SPARSEFILENAME, HARDLINKNAME);
14169d94f4cSDag-Erling Smørgrav 	if (link(SPARSEFILENAME, HARDLINKNAME) != 0)
14269d94f4cSDag-Erling Smørgrav 		err(1, "%s", HARDLINKNAME);
14369d94f4cSDag-Erling Smørgrav 
14469d94f4cSDag-Erling Smørgrav 	/* create a symbolic link with a short target */
14569d94f4cSDag-Erling Smørgrav 	verbose("symlink %s %s", SPARSEFILENAME, SHORTLINKNAME);
14669d94f4cSDag-Erling Smørgrav 	if (symlink(SPARSEFILENAME, SHORTLINKNAME) != 0)
14769d94f4cSDag-Erling Smørgrav 		err(1, "%s", SHORTLINKNAME);
14869d94f4cSDag-Erling Smørgrav 
14969d94f4cSDag-Erling Smørgrav 	/* create a symbolic link with a long target */
15069d94f4cSDag-Erling Smørgrav 	linktarget = mklonglinktarget(SUBDIRNAME, SPARSEFILENAME);
15169d94f4cSDag-Erling Smørgrav 	verbose("symlink %s %s", linktarget, LONGLINKNAME);
15269d94f4cSDag-Erling Smørgrav 	if (symlink(linktarget, LONGLINKNAME) != 0)
15369d94f4cSDag-Erling Smørgrav 		err(1, "%s", LONGLINKNAME);
15469d94f4cSDag-Erling Smørgrav 	free(linktarget);
15569d94f4cSDag-Erling Smørgrav }
15669d94f4cSDag-Erling Smørgrav 
15769d94f4cSDag-Erling Smørgrav static void
usage(void)15869d94f4cSDag-Erling Smørgrav usage(void)
15969d94f4cSDag-Erling Smørgrav {
16069d94f4cSDag-Erling Smørgrav 
1616cb78fa4SDag-Erling Smørgrav 	fprintf(stderr, "usage: %s [-gv] tarfile\n", PROGNAME);
16269d94f4cSDag-Erling Smørgrav 	exit(EXIT_FAILURE);
16369d94f4cSDag-Erling Smørgrav }
16469d94f4cSDag-Erling Smørgrav 
16569d94f4cSDag-Erling Smørgrav int
main(int argc,char * argv[])16669d94f4cSDag-Erling Smørgrav main(int argc, char *argv[])
16769d94f4cSDag-Erling Smørgrav {
16869d94f4cSDag-Erling Smørgrav 	const char *tarfilename;
16969d94f4cSDag-Erling Smørgrav 	char *dirname;
17069d94f4cSDag-Erling Smørgrav 	int opt, wstatus;
17169d94f4cSDag-Erling Smørgrav 	pid_t pid;
17269d94f4cSDag-Erling Smørgrav 
1736cb78fa4SDag-Erling Smørgrav 	while ((opt = getopt(argc, argv, "gv")) != -1)
17469d94f4cSDag-Erling Smørgrav 		switch (opt) {
1756cb78fa4SDag-Erling Smørgrav 		case 'g':
1766cb78fa4SDag-Erling Smørgrav 			opt_g = true;
1772af158aeSEd Maste 			break;
17869d94f4cSDag-Erling Smørgrav 		case 'v':
17969d94f4cSDag-Erling Smørgrav 			opt_v = true;
18069d94f4cSDag-Erling Smørgrav 			break;
18169d94f4cSDag-Erling Smørgrav 		default:
18269d94f4cSDag-Erling Smørgrav 			usage();
18369d94f4cSDag-Erling Smørgrav 		}
18469d94f4cSDag-Erling Smørgrav 
18569d94f4cSDag-Erling Smørgrav 	argc -= optind;
18669d94f4cSDag-Erling Smørgrav 	argv += optind;
18769d94f4cSDag-Erling Smørgrav 
18869d94f4cSDag-Erling Smørgrav 	if (argc != 1)
18969d94f4cSDag-Erling Smørgrav 		usage();
19069d94f4cSDag-Erling Smørgrav 	tarfilename = *argv;
19169d94f4cSDag-Erling Smørgrav 
19269d94f4cSDag-Erling Smørgrav 	if (asprintf(&dirname, "%s%s.XXXXXXXX", _PATH_TMP, PROGNAME) < 0)
19369d94f4cSDag-Erling Smørgrav 		err(1, "asprintf()");
19469d94f4cSDag-Erling Smørgrav 	if (mkdtemp(dirname) == NULL)
19569d94f4cSDag-Erling Smørgrav 		err(1, "%s", dirname);
19669d94f4cSDag-Erling Smørgrav 	verbose("mkdir %s", dirname);
19769d94f4cSDag-Erling Smørgrav 
19869d94f4cSDag-Erling Smørgrav 	/* fork a child to create the files */
19969d94f4cSDag-Erling Smørgrav 	if ((pid = fork()) < 0)
20069d94f4cSDag-Erling Smørgrav 		err(1, "fork()");
20169d94f4cSDag-Erling Smørgrav 	if (pid == 0) {
20269d94f4cSDag-Erling Smørgrav 		verbose("cd %s", dirname);
20369d94f4cSDag-Erling Smørgrav 		if (chdir(dirname) != 0)
20469d94f4cSDag-Erling Smørgrav 			err(1, "%s", dirname);
20569d94f4cSDag-Erling Smørgrav 		verbose("umask 022");
20669d94f4cSDag-Erling Smørgrav 		umask(022);
20769d94f4cSDag-Erling Smørgrav 		mktar();
20869d94f4cSDag-Erling Smørgrav 		verbose("cd -");
20969d94f4cSDag-Erling Smørgrav 		exit(0);
21069d94f4cSDag-Erling Smørgrav 	}
21169d94f4cSDag-Erling Smørgrav 	if (waitpid(pid, &wstatus, 0) < 0)
21269d94f4cSDag-Erling Smørgrav 		err(1, "waitpid()");
21369d94f4cSDag-Erling Smørgrav 	if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
21469d94f4cSDag-Erling Smørgrav 		errx(1, "child failed");
21569d94f4cSDag-Erling Smørgrav 
21669d94f4cSDag-Erling Smørgrav 	/* fork a child to create the tarball */
21769d94f4cSDag-Erling Smørgrav 	if ((pid = fork()) < 0)
21869d94f4cSDag-Erling Smørgrav 		err(1, "fork()");
21969d94f4cSDag-Erling Smørgrav 	if (pid == 0) {
22069d94f4cSDag-Erling Smørgrav 		verbose("creating tarball");
2216cb78fa4SDag-Erling Smørgrav 		execlp(opt_g ? "gtar" : "tar",
2226cb78fa4SDag-Erling Smørgrav 		    "tar",
22369d94f4cSDag-Erling Smørgrav 		    "-c",
22469d94f4cSDag-Erling Smørgrav 		    "-f", tarfilename,
22569d94f4cSDag-Erling Smørgrav 		    "-C", dirname,
2266cb78fa4SDag-Erling Smørgrav 		    "--posix",
22769d94f4cSDag-Erling Smørgrav 		    "--zstd",
22869d94f4cSDag-Erling Smørgrav #if 0
22969d94f4cSDag-Erling Smørgrav 		    "--options", "zstd:frame-per-file",
23069d94f4cSDag-Erling Smørgrav #endif
231ef184e98SDag-Erling Smørgrav 		    "./" SUBDIRNAME "/../" NORMALFILENAME,
232ce6a0c77SDag-Erling Smørgrav 		    "./" SPARSEFILENAME,
233ce6a0c77SDag-Erling Smørgrav 		    "./" HARDLINKNAME,
234ce6a0c77SDag-Erling Smørgrav 		    "./" SHORTLINKNAME,
235ce6a0c77SDag-Erling Smørgrav 		    "./" SUBDIRNAME,
236ce6a0c77SDag-Erling Smørgrav 		    "./" LONGLINKNAME,
23769d94f4cSDag-Erling Smørgrav 		    NULL);
23869d94f4cSDag-Erling Smørgrav 		err(1, "execlp()");
23969d94f4cSDag-Erling Smørgrav 	}
24069d94f4cSDag-Erling Smørgrav 	if (waitpid(pid, &wstatus, 0) < 0)
24169d94f4cSDag-Erling Smørgrav 		err(1, "waitpid()");
24269d94f4cSDag-Erling Smørgrav 	if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
24369d94f4cSDag-Erling Smørgrav 		errx(1, "child failed");
24469d94f4cSDag-Erling Smørgrav 
24569d94f4cSDag-Erling Smørgrav 	/* fork a child to delete everything */
24669d94f4cSDag-Erling Smørgrav 	if ((pid = fork()) < 0)
24769d94f4cSDag-Erling Smørgrav 		err(1, "fork()");
24869d94f4cSDag-Erling Smørgrav 	if (pid == 0) {
24969d94f4cSDag-Erling Smørgrav 		verbose("cd %s", dirname);
25069d94f4cSDag-Erling Smørgrav 		if (chdir(dirname) != 0)
25169d94f4cSDag-Erling Smørgrav 			err(1, "%s", dirname);
25269d94f4cSDag-Erling Smørgrav 		verbose("rm %s", LONGLINKNAME);
25369d94f4cSDag-Erling Smørgrav 		(void)unlink(LONGLINKNAME);
25469d94f4cSDag-Erling Smørgrav 		verbose("rm %s", SHORTLINKNAME);
25569d94f4cSDag-Erling Smørgrav 		(void)unlink(SHORTLINKNAME);
25669d94f4cSDag-Erling Smørgrav 		verbose("rm %s", HARDLINKNAME);
25769d94f4cSDag-Erling Smørgrav 		(void)unlink(HARDLINKNAME);
25869d94f4cSDag-Erling Smørgrav 		verbose("rm %s", SPARSEFILENAME);
25969d94f4cSDag-Erling Smørgrav 		(void)unlink(SPARSEFILENAME);
260ce6a0c77SDag-Erling Smørgrav 		verbose("rmdir %s", SUBDIRNAME);
26169d94f4cSDag-Erling Smørgrav 		(void)rmdir(SUBDIRNAME);
26269d94f4cSDag-Erling Smørgrav 		verbose("cd -");
26369d94f4cSDag-Erling Smørgrav 		exit(0);
26469d94f4cSDag-Erling Smørgrav 	}
26569d94f4cSDag-Erling Smørgrav 	if (waitpid(pid, &wstatus, 0) < 0)
26669d94f4cSDag-Erling Smørgrav 		err(1, "waitpid()");
26769d94f4cSDag-Erling Smørgrav 	if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
26869d94f4cSDag-Erling Smørgrav 		errx(1, "child failed");
26969d94f4cSDag-Erling Smørgrav 	verbose("rmdir %s", dirname);
27069d94f4cSDag-Erling Smørgrav 	(void)rmdir(dirname);
27169d94f4cSDag-Erling Smørgrav 
27269d94f4cSDag-Erling Smørgrav 	exit(0);
27369d94f4cSDag-Erling Smørgrav }
274