1 /* $OpenBSD: util.c,v 1.46 2021/10/24 21:24:17 deraadt Exp $ */ 2 3 /* 4 * patch - a program to apply diffs to original files 5 * 6 * Copyright 1986, Larry Wall 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following condition is met: 10 * 1. Redistributions of source code must retain the above copyright notice, 11 * this condition and the following disclaimer. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 17 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 * -C option added in 1998, original code by Marc Espie, based on FreeBSD 26 * behaviour 27 */ 28 29 #include <sys/stat.h> 30 31 #include <ctype.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <paths.h> 35 #include <signal.h> 36 #include <stdarg.h> 37 #include <stdlib.h> 38 #include <stdio.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 #include "common.h" 43 #include "util.h" 44 #include "backupfile.h" 45 46 /* Rename a file, copying it if necessary. */ 47 48 int 49 move_file(const char *from, const char *to) 50 { 51 int fromfd; 52 ssize_t i; 53 54 /* to stdout? */ 55 56 if (strEQ(to, "-")) { 57 #ifdef DEBUGGING 58 if (debug & 4) 59 say("Moving %s to stdout.\n", from); 60 #endif 61 fromfd = open(from, O_RDONLY); 62 if (fromfd == -1) 63 pfatal("internal error, can't reopen %s", from); 64 while ((i = read(fromfd, buf, bufsz)) > 0) 65 if (write(STDOUT_FILENO, buf, i) != i) 66 pfatal("write failed"); 67 close(fromfd); 68 return 0; 69 } 70 if (backup_file(to) < 0) { 71 say("Can't backup %s, output is in %s: %s\n", to, from, 72 strerror(errno)); 73 return -1; 74 } 75 #ifdef DEBUGGING 76 if (debug & 4) 77 say("Moving %s to %s.\n", from, to); 78 #endif 79 if (rename(from, to) == -1) { 80 if (errno != EXDEV || copy_file(from, to) < 0) { 81 say("Can't create %s, output is in %s: %s\n", 82 to, from, strerror(errno)); 83 return -1; 84 } 85 } 86 return 0; 87 } 88 89 /* Backup the original file. */ 90 91 int 92 backup_file(const char *orig) 93 { 94 struct stat filestat; 95 char bakname[PATH_MAX], *s, *simplename; 96 dev_t orig_device; 97 ino_t orig_inode; 98 99 if (backup_type == none || stat(orig, &filestat) != 0) 100 return 0; /* nothing to do */ 101 orig_device = filestat.st_dev; 102 orig_inode = filestat.st_ino; 103 104 if (origprae) { 105 if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) || 106 strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname)) 107 fatal("filename %s too long for buffer\n", origprae); 108 } else { 109 if ((s = find_backup_file_name(orig)) == NULL) 110 fatal("out of memory\n"); 111 if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname)) 112 fatal("filename %s too long for buffer\n", s); 113 free(s); 114 } 115 116 if ((simplename = strrchr(bakname, '/')) != NULL) 117 simplename = simplename + 1; 118 else 119 simplename = bakname; 120 121 /* 122 * Find a backup name that is not the same file. Change the 123 * first lowercase char into uppercase; if that isn't 124 * sufficient, chop off the first char and try again. 125 */ 126 while (stat(bakname, &filestat) == 0 && 127 orig_device == filestat.st_dev && orig_inode == filestat.st_ino) { 128 /* Skip initial non-lowercase chars. */ 129 for (s = simplename; *s && !islower((unsigned char)*s); s++) 130 ; 131 if (*s) 132 *s = toupper((unsigned char)*s); 133 else 134 memmove(simplename, simplename + 1, 135 strlen(simplename + 1) + 1); 136 } 137 #ifdef DEBUGGING 138 if (debug & 4) 139 say("Moving %s to %s.\n", orig, bakname); 140 #endif 141 if (rename(orig, bakname) == -1) { 142 if (errno != EXDEV || copy_file(orig, bakname) < 0) 143 return -1; 144 } 145 return 0; 146 } 147 148 /* 149 * Copy a file. 150 */ 151 int 152 copy_file(const char *from, const char *to) 153 { 154 int tofd, fromfd; 155 ssize_t i; 156 157 tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666); 158 if (tofd == -1) 159 return -1; 160 fromfd = open(from, O_RDONLY); 161 if (fromfd == -1) 162 pfatal("internal error, can't reopen %s", from); 163 while ((i = read(fromfd, buf, bufsz)) > 0) 164 if (write(tofd, buf, i) != i) 165 pfatal("write to %s failed", to); 166 close(fromfd); 167 close(tofd); 168 return 0; 169 } 170 171 /* 172 * Allocate a unique area for a string. 173 */ 174 char * 175 savestr(const char *s) 176 { 177 char *rv; 178 179 if (!s) 180 s = "Oops"; 181 rv = strdup(s); 182 if (rv == NULL) { 183 if (using_plan_a) 184 out_of_mem = true; 185 else 186 fatal("out of memory\n"); 187 } 188 return rv; 189 } 190 191 /* 192 * Allocate a unique area for a string. Call fatal if out of memory. 193 */ 194 char * 195 xstrdup(const char *s) 196 { 197 char *rv; 198 199 if (!s) 200 s = "Oops"; 201 rv = strdup(s); 202 if (rv == NULL) 203 fatal("out of memory\n"); 204 return rv; 205 } 206 207 /* 208 * Vanilla terminal output (buffered). 209 */ 210 void 211 say(const char *fmt, ...) 212 { 213 va_list ap; 214 215 va_start(ap, fmt); 216 vfprintf(stdout, fmt, ap); 217 va_end(ap); 218 fflush(stdout); 219 } 220 221 /* 222 * Terminal output, pun intended. 223 */ 224 void 225 fatal(const char *fmt, ...) 226 { 227 va_list ap; 228 229 va_start(ap, fmt); 230 fprintf(stderr, "patch: **** "); 231 vfprintf(stderr, fmt, ap); 232 va_end(ap); 233 my_exit(2); 234 } 235 236 /* 237 * Say something from patch, something from the system, then silence . . . 238 */ 239 void 240 pfatal(const char *fmt, ...) 241 { 242 va_list ap; 243 int errnum = errno; 244 245 fprintf(stderr, "patch: **** "); 246 va_start(ap, fmt); 247 vfprintf(stderr, fmt, ap); 248 va_end(ap); 249 fprintf(stderr, ": %s\n", strerror(errnum)); 250 my_exit(2); 251 } 252 253 /* 254 * Get a response from the user via /dev/tty 255 */ 256 void 257 ask(const char *fmt, ...) 258 { 259 va_list ap; 260 ssize_t nr; 261 static int ttyfd = -1; 262 263 va_start(ap, fmt); 264 vfprintf(stdout, fmt, ap); 265 va_end(ap); 266 fflush(stdout); 267 if (ttyfd < 0) 268 ttyfd = open(_PATH_TTY, O_RDONLY); 269 if (ttyfd >= 0) { 270 if ((nr = read(ttyfd, buf, bufsz)) > 0 && 271 buf[nr - 1] == '\n') 272 buf[nr - 1] = '\0'; 273 } 274 if (ttyfd == -1 || nr <= 0) { 275 /* no tty or error reading, pretend user entered 'return' */ 276 putchar('\n'); 277 buf[0] = '\0'; 278 } 279 } 280 281 /* 282 * How to handle certain events when not in a critical region. 283 */ 284 void 285 set_signals(int reset) 286 { 287 static sig_t hupval, intval; 288 289 if (!reset) { 290 hupval = signal(SIGHUP, SIG_IGN); 291 if (hupval != SIG_IGN) 292 hupval = my_sigexit; 293 intval = signal(SIGINT, SIG_IGN); 294 if (intval != SIG_IGN) 295 intval = my_sigexit; 296 } 297 signal(SIGHUP, hupval); 298 signal(SIGINT, intval); 299 } 300 301 /* 302 * How to handle certain events when in a critical region. 303 */ 304 void 305 ignore_signals(void) 306 { 307 signal(SIGHUP, SIG_IGN); 308 signal(SIGINT, SIG_IGN); 309 } 310 311 /* 312 * Make sure we'll have the directories to create a file. If `striplast' is 313 * true, ignore the last element of `filename'. 314 */ 315 316 void 317 makedirs(const char *filename, bool striplast) 318 { 319 char *tmpbuf; 320 321 if ((tmpbuf = strdup(filename)) == NULL) 322 fatal("out of memory\n"); 323 324 if (striplast) { 325 char *s = strrchr(tmpbuf, '/'); 326 if (s == NULL) { 327 free(tmpbuf); 328 return; /* nothing to be done */ 329 } 330 *s = '\0'; 331 } 332 if (mkpath(tmpbuf) != 0) 333 pfatal("creation of %s failed", tmpbuf); 334 free(tmpbuf); 335 } 336 337 /* 338 * Make filenames more reasonable. 339 */ 340 char * 341 fetchname(const char *at, bool *exists, int strip_leading) 342 { 343 char *fullname, *name, *t; 344 int sleading, tab; 345 struct stat filestat; 346 347 if (at == NULL || *at == '\0') 348 return NULL; 349 while (isspace((unsigned char)*at)) 350 at++; 351 #ifdef DEBUGGING 352 if (debug & 128) 353 say("fetchname %s %d\n", at, strip_leading); 354 #endif 355 /* So files can be created by diffing against /dev/null. */ 356 if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1)) 357 return NULL; 358 name = fullname = t = savestr(at); 359 360 tab = strchr(t, '\t') != NULL; 361 /* Strip off up to `strip_leading' path components and NUL terminate. */ 362 for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') || 363 !isspace((unsigned char)*t)); t++) { 364 if (t[0] == '/' && t[1] != '/' && t[1] != '\0') 365 if (--sleading >= 0) 366 name = t + 1; 367 } 368 *t = '\0'; 369 370 /* 371 * If no -p option was given (957 is the default value!), we were 372 * given a relative pathname, and the leading directories that we 373 * just stripped off all exist, put them back on. 374 */ 375 if (strip_leading == 957 && name != fullname && *fullname != '/') { 376 name[-1] = '\0'; 377 if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) { 378 name[-1] = '/'; 379 name = fullname; 380 } 381 } 382 name = savestr(name); 383 free(fullname); 384 385 *exists = stat(name, &filestat) == 0; 386 return name; 387 } 388 389 void 390 version(void) 391 { 392 fprintf(stderr, "Patch version 2.0-12u8-OpenBSD\n"); 393 my_exit(EXIT_SUCCESS); 394 } 395 396 void 397 my_cleanup(void) 398 { 399 unlink(TMPINNAME); 400 if (!toutkeep) 401 unlink(TMPOUTNAME); 402 if (!trejkeep) 403 unlink(TMPREJNAME); 404 unlink(TMPPATNAME); 405 } 406 407 /* 408 * Exit with cleanup. 409 */ 410 void 411 my_exit(int status) 412 { 413 my_cleanup(); 414 exit(status); 415 } 416 417 /* 418 * Exit with cleanup, from a signal handler. 419 */ 420 void 421 my_sigexit(int signo) 422 { 423 my_cleanup(); 424 _exit(2); 425 } 426