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 50static bool opt_g; 51static bool opt_v; 52 53static 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 66static void 67mknormalfile(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 85static void 86mksparsefile(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 107static char * 108mklonglinktarget(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 120static void 121mktar(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 157static void 158usage(void) 159{ 160 161 fprintf(stderr, "usage: %s [-gv] tarfile\n", PROGNAME); 162 exit(EXIT_FAILURE); 163} 164 165int 166main(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