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 case 'v': 178 opt_v = true; 179 break; 180 default: 181 usage(); 182 } 183 184 argc -= optind; 185 argv += optind; 186 187 if (argc != 1) 188 usage(); 189 tarfilename = *argv; 190 191 if (asprintf(&dirname, "%s%s.XXXXXXXX", _PATH_TMP, PROGNAME) < 0) 192 err(1, "asprintf()"); 193 if (mkdtemp(dirname) == NULL) 194 err(1, "%s", dirname); 195 verbose("mkdir %s", dirname); 196 197 /* fork a child to create the files */ 198 if ((pid = fork()) < 0) 199 err(1, "fork()"); 200 if (pid == 0) { 201 verbose("cd %s", dirname); 202 if (chdir(dirname) != 0) 203 err(1, "%s", dirname); 204 verbose("umask 022"); 205 umask(022); 206 mktar(); 207 verbose("cd -"); 208 exit(0); 209 } 210 if (waitpid(pid, &wstatus, 0) < 0) 211 err(1, "waitpid()"); 212 if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) 213 errx(1, "child failed"); 214 215 /* fork a child to create the tarball */ 216 if ((pid = fork()) < 0) 217 err(1, "fork()"); 218 if (pid == 0) { 219 verbose("creating tarball"); 220 execlp(opt_g ? "gtar" : "tar", 221 "tar", 222 "-c", 223 "-f", tarfilename, 224 "-C", dirname, 225 "--posix", 226 "--zstd", 227 #if 0 228 "--options", "zstd:frame-per-file", 229 #endif 230 "./" SUBDIRNAME "/../" NORMALFILENAME, 231 "./" SPARSEFILENAME, 232 "./" HARDLINKNAME, 233 "./" SHORTLINKNAME, 234 "./" SUBDIRNAME, 235 "./" LONGLINKNAME, 236 NULL); 237 err(1, "execlp()"); 238 } 239 if (waitpid(pid, &wstatus, 0) < 0) 240 err(1, "waitpid()"); 241 if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) 242 errx(1, "child failed"); 243 244 /* fork a child to delete everything */ 245 if ((pid = fork()) < 0) 246 err(1, "fork()"); 247 if (pid == 0) { 248 verbose("cd %s", dirname); 249 if (chdir(dirname) != 0) 250 err(1, "%s", dirname); 251 verbose("rm %s", LONGLINKNAME); 252 (void)unlink(LONGLINKNAME); 253 verbose("rm %s", SHORTLINKNAME); 254 (void)unlink(SHORTLINKNAME); 255 verbose("rm %s", HARDLINKNAME); 256 (void)unlink(HARDLINKNAME); 257 verbose("rm %s", SPARSEFILENAME); 258 (void)unlink(SPARSEFILENAME); 259 verbose("rmdir %s", SUBDIRNAME); 260 (void)rmdir(SUBDIRNAME); 261 verbose("cd -"); 262 exit(0); 263 } 264 if (waitpid(pid, &wstatus, 0) < 0) 265 err(1, "waitpid()"); 266 if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) 267 errx(1, "child failed"); 268 verbose("rmdir %s", dirname); 269 (void)rmdir(dirname); 270 271 exit(0); 272 } 273