Changeset View
Changeset View
Standalone View
Standalone View
tests/sys/fs/tarfs/mktar.c
- This file was added.
/*- | |||||
* 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 AUTHORS ``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 AUTHORS 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 SPARSEFILENAME "sparse_file" | |||||
#define HARDLINKNAME "hard_link" | |||||
#define SHORTLINKNAME "short_link" | |||||
#define LONGLINKNAME "long_link" | |||||
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 | |||||
mksparsefile(const char *filename, mode_t mode) | |||||
{ | |||||
char buf[511]; | |||||
ssize_t res; | |||||
int fd; | |||||
if ((fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 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 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 [-v] 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, "v")) != -1) | |||||
switch (opt) { | |||||
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("tar", "tar", | |||||
"-c", | |||||
"-f", tarfilename, | |||||
"-C", dirname, | |||||
"--zstd", | |||||
#if 0 | |||||
"--options", "zstd:frame-per-file", | |||||
#endif | |||||
".", | |||||
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("rm %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); | |||||
} |