aboutsummaryrefslogtreecommitdiff
path: root/tests/sys/fs/tarfs/mktar.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sys/fs/tarfs/mktar.c')
-rw-r--r--tests/sys/fs/tarfs/mktar.c273
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);
+}