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 SPARSEFILENAME "sparse_file" 45 #define HARDLINKNAME "hard_link" 46 #define SHORTLINKNAME "short_link" 47 #define LONGLINKNAME "long_link" 48 49 static bool opt_v; 50 51 static void verbose(const char *fmt, ...) 52 { 53 va_list ap; 54 55 if (!opt_v) 56 return; 57 fprintf(stderr, "%s: ", PROGNAME); 58 va_start(ap, fmt); 59 vfprintf(stderr, fmt, ap); 60 va_end(ap); 61 fprintf(stderr, "\n"); 62 } 63 64 static void 65 mksparsefile(const char *filename, mode_t mode) 66 { 67 char buf[511]; 68 ssize_t res; 69 int fd; 70 71 if ((fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) 72 err(1, "%s", filename); 73 for (unsigned int i = 33; i <= 126; i++) { 74 memset(buf, i, sizeof(buf)); 75 if (lseek(fd, 1048576LU * (i - 32), SEEK_SET) < 0) 76 err(1, "%s", filename); 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 } 83 close(fd); 84 } 85 86 static char * 87 mklonglinktarget(const char *dirname, const char *filename) 88 { 89 char *piece, *target; 90 91 if (asprintf(&piece, "%1$s/../%1$s/../%1$s/../%1$s/../", dirname) < 0) 92 err(1, "asprintf()"); 93 if (asprintf(&target, "%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%2$s", piece, filename) < 0) 94 err(1, "asprintf()"); 95 free(piece); 96 return target; 97 } 98 99 static void 100 mktar(void) 101 { 102 char *linktarget; 103 104 /* create a subdirectory */ 105 verbose("mkdir %s", SUBDIRNAME); 106 if (mkdir(SUBDIRNAME, 0755) != 0) 107 err(1, "%s", SUBDIRNAME); 108 109 /* create a sparse file */ 110 verbose("creating %s", SPARSEFILENAME); 111 mksparsefile(SPARSEFILENAME, 0644); 112 chflags(SPARSEFILENAME, UF_NODUMP); 113 114 /* create a hard link */ 115 verbose("link %s %s", SPARSEFILENAME, HARDLINKNAME); 116 if (link(SPARSEFILENAME, HARDLINKNAME) != 0) 117 err(1, "%s", HARDLINKNAME); 118 119 /* create a symbolic link with a short target */ 120 verbose("symlink %s %s", SPARSEFILENAME, SHORTLINKNAME); 121 if (symlink(SPARSEFILENAME, SHORTLINKNAME) != 0) 122 err(1, "%s", SHORTLINKNAME); 123 124 /* create a symbolic link with a long target */ 125 linktarget = mklonglinktarget(SUBDIRNAME, SPARSEFILENAME); 126 verbose("symlink %s %s", linktarget, LONGLINKNAME); 127 if (symlink(linktarget, LONGLINKNAME) != 0) 128 err(1, "%s", LONGLINKNAME); 129 free(linktarget); 130 } 131 132 static void 133 usage(void) 134 { 135 136 fprintf(stderr, "usage: %s [-v] tarfile\n", PROGNAME); 137 exit(EXIT_FAILURE); 138 } 139 140 int 141 main(int argc, char *argv[]) 142 { 143 const char *tarfilename; 144 char *dirname; 145 int opt, wstatus; 146 pid_t pid; 147 148 while ((opt = getopt(argc, argv, "v")) != -1) 149 switch (opt) { 150 case 'v': 151 opt_v = true; 152 break; 153 default: 154 usage(); 155 } 156 157 argc -= optind; 158 argv += optind; 159 160 if (argc != 1) 161 usage(); 162 tarfilename = *argv; 163 164 if (asprintf(&dirname, "%s%s.XXXXXXXX", _PATH_TMP, PROGNAME) < 0) 165 err(1, "asprintf()"); 166 if (mkdtemp(dirname) == NULL) 167 err(1, "%s", dirname); 168 verbose("mkdir %s", dirname); 169 170 /* fork a child to create the files */ 171 if ((pid = fork()) < 0) 172 err(1, "fork()"); 173 if (pid == 0) { 174 verbose("cd %s", dirname); 175 if (chdir(dirname) != 0) 176 err(1, "%s", dirname); 177 verbose("umask 022"); 178 umask(022); 179 mktar(); 180 verbose("cd -"); 181 exit(0); 182 } 183 if (waitpid(pid, &wstatus, 0) < 0) 184 err(1, "waitpid()"); 185 if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) 186 errx(1, "child failed"); 187 188 /* fork a child to create the tarball */ 189 if ((pid = fork()) < 0) 190 err(1, "fork()"); 191 if (pid == 0) { 192 verbose("creating tarball"); 193 execlp("tar", "tar", 194 "-c", 195 "-f", tarfilename, 196 "-C", dirname, 197 "--zstd", 198 #if 0 199 "--options", "zstd:frame-per-file", 200 #endif 201 ".", 202 NULL); 203 err(1, "execlp()"); 204 } 205 if (waitpid(pid, &wstatus, 0) < 0) 206 err(1, "waitpid()"); 207 if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) 208 errx(1, "child failed"); 209 210 /* fork a child to delete everything */ 211 if ((pid = fork()) < 0) 212 err(1, "fork()"); 213 if (pid == 0) { 214 verbose("cd %s", dirname); 215 if (chdir(dirname) != 0) 216 err(1, "%s", dirname); 217 verbose("rm %s", LONGLINKNAME); 218 (void)unlink(LONGLINKNAME); 219 verbose("rm %s", SHORTLINKNAME); 220 (void)unlink(SHORTLINKNAME); 221 verbose("rm %s", HARDLINKNAME); 222 (void)unlink(HARDLINKNAME); 223 verbose("rm %s", SPARSEFILENAME); 224 (void)unlink(SPARSEFILENAME); 225 verbose("rm %s", SUBDIRNAME); 226 (void)rmdir(SUBDIRNAME); 227 verbose("cd -"); 228 exit(0); 229 } 230 if (waitpid(pid, &wstatus, 0) < 0) 231 err(1, "waitpid()"); 232 if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) 233 errx(1, "child failed"); 234 verbose("rmdir %s", dirname); 235 (void)rmdir(dirname); 236 237 exit(0); 238 } 239