diff options
Diffstat (limited to 'tests/sys/fs/tarfs/mktar.c')
| -rw-r--r-- | tests/sys/fs/tarfs/mktar.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/tests/sys/fs/tarfs/mktar.c b/tests/sys/fs/tarfs/mktar.c new file mode 100644 index 000000000000..b4ec271c73a1 --- /dev/null +++ b/tests/sys/fs/tarfs/mktar.c @@ -0,0 +1,273 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Klara, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/stat.h> +#include <sys/wait.h> + +#include <err.h> +#include <fcntl.h> +#include <paths.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#define PROGNAME "mktar" + +#define SUBDIRNAME "directory" +#define NORMALFILENAME "file" +#define SPARSEFILENAME "sparse_file" +#define HARDLINKNAME "hard_link" +#define SHORTLINKNAME "short_link" +#define LONGLINKNAME "long_link" + +static bool opt_g; +static bool opt_v; + +static void verbose(const char *fmt, ...) +{ + va_list ap; + + if (!opt_v) + return; + fprintf(stderr, "%s: ", PROGNAME); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void +mknormalfile(const char *filename, mode_t mode) +{ + char buf[512]; + ssize_t res; + int fd; + + if ((fd = open(filename, O_RDWR|O_CREAT|O_EXCL, mode)) < 0) + err(1, "%s", filename); + for (unsigned int i = 0; i < sizeof(buf); i++) + buf[i] = 32 + i % 64; + res = write(fd, buf, sizeof(buf)); + if (res < 0) + err(1, "%s", filename); + if (res != sizeof(buf)) + errx(1, "%s: short write", filename); + close(fd); +} + +static void +mksparsefile(const char *filename, mode_t mode) +{ + char buf[511]; + ssize_t res; + int fd; + + if ((fd = open(filename, O_RDWR|O_CREAT|O_EXCL, mode)) < 0) + err(1, "%s", filename); + for (unsigned int i = 33; i <= 126; i++) { + memset(buf, i, sizeof(buf)); + if (lseek(fd, 1048576LU * (i - 32), SEEK_SET) < 0) + err(1, "%s", filename); + res = write(fd, buf, sizeof(buf)); + if (res < 0) + err(1, "%s", filename); + if (res != sizeof(buf)) + errx(1, "%s: short write", filename); + } + close(fd); +} + +static char * +mklonglinktarget(const char *dirname, const char *filename) +{ + char *piece, *target; + + if (asprintf(&piece, "%1$s/../%1$s/../%1$s/../%1$s/../", dirname) < 0) + err(1, "asprintf()"); + if (asprintf(&target, "%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%2$s", piece, filename) < 0) + err(1, "asprintf()"); + free(piece); + return target; +} + +static void +mktar(void) +{ + char *linktarget; + + /* create a subdirectory */ + verbose("mkdir %s", SUBDIRNAME); + if (mkdir(SUBDIRNAME, 0755) != 0) + err(1, "%s", SUBDIRNAME); + + /* create a normal file */ + verbose("creating %s", NORMALFILENAME); + mknormalfile(NORMALFILENAME, 0644); + + /* create a sparse file */ + verbose("creating %s", SPARSEFILENAME); + mksparsefile(SPARSEFILENAME, 0644); + chflags(SPARSEFILENAME, UF_NODUMP); + + /* create a hard link */ + verbose("link %s %s", SPARSEFILENAME, HARDLINKNAME); + if (link(SPARSEFILENAME, HARDLINKNAME) != 0) + err(1, "%s", HARDLINKNAME); + + /* create a symbolic link with a short target */ + verbose("symlink %s %s", SPARSEFILENAME, SHORTLINKNAME); + if (symlink(SPARSEFILENAME, SHORTLINKNAME) != 0) + err(1, "%s", SHORTLINKNAME); + + /* create a symbolic link with a long target */ + linktarget = mklonglinktarget(SUBDIRNAME, SPARSEFILENAME); + verbose("symlink %s %s", linktarget, LONGLINKNAME); + if (symlink(linktarget, LONGLINKNAME) != 0) + err(1, "%s", LONGLINKNAME); + free(linktarget); +} + +static void +usage(void) +{ + + fprintf(stderr, "usage: %s [-gv] tarfile\n", PROGNAME); + exit(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ + const char *tarfilename; + char *dirname; + int opt, wstatus; + pid_t pid; + + while ((opt = getopt(argc, argv, "gv")) != -1) + switch (opt) { + case 'g': + opt_g = true; + break; + case 'v': + opt_v = true; + break; + default: + usage(); + } + + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + tarfilename = *argv; + + if (asprintf(&dirname, "%s%s.XXXXXXXX", _PATH_TMP, PROGNAME) < 0) + err(1, "asprintf()"); + if (mkdtemp(dirname) == NULL) + err(1, "%s", dirname); + verbose("mkdir %s", dirname); + + /* fork a child to create the files */ + if ((pid = fork()) < 0) + err(1, "fork()"); + if (pid == 0) { + verbose("cd %s", dirname); + if (chdir(dirname) != 0) + err(1, "%s", dirname); + verbose("umask 022"); + umask(022); + mktar(); + verbose("cd -"); + exit(0); + } + if (waitpid(pid, &wstatus, 0) < 0) + err(1, "waitpid()"); + if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) + errx(1, "child failed"); + + /* fork a child to create the tarball */ + if ((pid = fork()) < 0) + err(1, "fork()"); + if (pid == 0) { + verbose("creating tarball"); + execlp(opt_g ? "gtar" : "tar", + "tar", + "-c", + "-f", tarfilename, + "-C", dirname, + "--posix", + "--zstd", +#if 0 + "--options", "zstd:frame-per-file", +#endif + "./" SUBDIRNAME "/../" NORMALFILENAME, + "./" SPARSEFILENAME, + "./" HARDLINKNAME, + "./" SHORTLINKNAME, + "./" SUBDIRNAME, + "./" LONGLINKNAME, + NULL); + err(1, "execlp()"); + } + if (waitpid(pid, &wstatus, 0) < 0) + err(1, "waitpid()"); + if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) + errx(1, "child failed"); + + /* fork a child to delete everything */ + if ((pid = fork()) < 0) + err(1, "fork()"); + if (pid == 0) { + verbose("cd %s", dirname); + if (chdir(dirname) != 0) + err(1, "%s", dirname); + verbose("rm %s", LONGLINKNAME); + (void)unlink(LONGLINKNAME); + verbose("rm %s", SHORTLINKNAME); + (void)unlink(SHORTLINKNAME); + verbose("rm %s", HARDLINKNAME); + (void)unlink(HARDLINKNAME); + verbose("rm %s", SPARSEFILENAME); + (void)unlink(SPARSEFILENAME); + verbose("rmdir %s", SUBDIRNAME); + (void)rmdir(SUBDIRNAME); + verbose("cd -"); + exit(0); + } + if (waitpid(pid, &wstatus, 0) < 0) + err(1, "waitpid()"); + if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) + errx(1, "child failed"); + verbose("rmdir %s", dirname); + (void)rmdir(dirname); + + exit(0); +} |
