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