1 /* $OpenBSD: fileio.c,v 1.103 2016/07/28 21:40:25 tedu Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * POSIX fileio.c 7 */ 8 9 #include <sys/queue.h> 10 #include <sys/resource.h> 11 #include <sys/stat.h> 12 #include <sys/time.h> 13 #include <sys/types.h> 14 #include <sys/wait.h> 15 #include <dirent.h> 16 #include <errno.h> 17 #include <fcntl.h> 18 #include <limits.h> 19 #include <pwd.h> 20 #include <signal.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #include "def.h" 27 #include "kbd.h" 28 #include "pathnames.h" 29 30 static char *bkuplocation(const char *); 31 static int bkupleavetmp(const char *); 32 33 static char *bkupdir; 34 static int leavetmp = 0; /* 1 = leave any '~' files in tmp dir */ 35 36 /* 37 * Open a file for reading. 38 */ 39 int 40 ffropen(FILE ** ffp, const char *fn, struct buffer *bp) 41 { 42 if ((*ffp = fopen(fn, "r")) == NULL) { 43 if (errno == ENOENT) 44 return (FIOFNF); 45 return (FIOERR); 46 } 47 48 /* If 'fn' is a directory open it with dired. */ 49 if (fisdir(fn) == TRUE) 50 return (FIODIR); 51 52 ffstat(*ffp, bp); 53 54 return (FIOSUC); 55 } 56 57 /* 58 * Update stat/dirty info 59 */ 60 void 61 ffstat(FILE *ffp, struct buffer *bp) 62 { 63 struct stat sb; 64 65 if (bp && fstat(fileno(ffp), &sb) == 0) { 66 /* set highorder bit to make sure this isn't all zero */ 67 bp->b_fi.fi_mode = sb.st_mode | 0x8000; 68 bp->b_fi.fi_uid = sb.st_uid; 69 bp->b_fi.fi_gid = sb.st_gid; 70 bp->b_fi.fi_mtime = sb.st_mtimespec; 71 /* Clear the ignore flag */ 72 bp->b_flag &= ~(BFIGNDIRTY | BFDIRTY); 73 } 74 } 75 76 /* 77 * Update the status/dirty info. If there is an error, 78 * there's not a lot we can do. 79 */ 80 int 81 fupdstat(struct buffer *bp) 82 { 83 FILE *ffp; 84 85 if ((ffp = fopen(bp->b_fname, "r")) == NULL) { 86 if (errno == ENOENT) 87 return (FIOFNF); 88 return (FIOERR); 89 } 90 ffstat(ffp, bp); 91 (void)ffclose(ffp, bp); 92 return (FIOSUC); 93 } 94 95 /* 96 * Open a file for writing. 97 */ 98 int 99 ffwopen(FILE ** ffp, const char *fn, struct buffer *bp) 100 { 101 int fd; 102 mode_t fmode = DEFFILEMODE; 103 104 if (bp && bp->b_fi.fi_mode) 105 fmode = bp->b_fi.fi_mode & 07777; 106 107 fd = open(fn, O_RDWR | O_CREAT | O_TRUNC, fmode); 108 if (fd == -1) { 109 ffp = NULL; 110 dobeep(); 111 ewprintf("Cannot open file for writing : %s", strerror(errno)); 112 return (FIOERR); 113 } 114 115 if ((*ffp = fdopen(fd, "w")) == NULL) { 116 dobeep(); 117 ewprintf("Cannot open file for writing : %s", strerror(errno)); 118 close(fd); 119 return (FIOERR); 120 } 121 122 /* 123 * If we have file information, use it. We don't bother to check for 124 * errors, because there's no a lot we can do about it. Certainly 125 * trying to change ownership will fail if we aren't root. That's 126 * probably OK. If we don't have info, no need to get it, since any 127 * future writes will do the same thing. 128 */ 129 if (bp && bp->b_fi.fi_mode) { 130 fchmod(fd, bp->b_fi.fi_mode & 07777); 131 fchown(fd, bp->b_fi.fi_uid, bp->b_fi.fi_gid); 132 } 133 return (FIOSUC); 134 } 135 136 /* 137 * Close a file. 138 */ 139 /* ARGSUSED */ 140 int 141 ffclose(FILE *ffp, struct buffer *bp) 142 { 143 if (fclose(ffp) == 0) 144 return (FIOSUC); 145 return (FIOERR); 146 } 147 148 /* 149 * Write a buffer to the already opened file. bp points to the 150 * buffer. Return the status. 151 */ 152 int 153 ffputbuf(FILE *ffp, struct buffer *bp) 154 { 155 struct line *lp, *lpend; 156 157 lpend = bp->b_headp; 158 for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) { 159 if (fwrite(ltext(lp), 1, llength(lp), ffp) != llength(lp)) { 160 dobeep(); 161 ewprintf("Write I/O error"); 162 return (FIOERR); 163 } 164 if (lforw(lp) != lpend) /* no implied \n on last line */ 165 putc('\n', ffp); 166 } 167 /* 168 * XXX should be variable controlled (once we have variables) 169 */ 170 if (llength(lback(lpend)) != 0) { 171 if (eyorn("No newline at end of file, add one") == TRUE) { 172 lnewline_at(lback(lpend), llength(lback(lpend))); 173 putc('\n', ffp); 174 } 175 } 176 return (FIOSUC); 177 } 178 179 /* 180 * Read a line from a file, and store the bytes 181 * in the supplied buffer. Stop on end of file or end of 182 * line. When FIOEOF is returned, there is a valid line 183 * of data without the normally implied \n. 184 * If the line length exceeds nbuf, FIOLONG is returned. 185 */ 186 int 187 ffgetline(FILE *ffp, char *buf, int nbuf, int *nbytes) 188 { 189 int c, i; 190 191 i = 0; 192 while ((c = getc(ffp)) != EOF && c != '\n') { 193 buf[i++] = c; 194 if (i >= nbuf) 195 return (FIOLONG); 196 } 197 if (c == EOF && ferror(ffp) != FALSE) { 198 dobeep(); 199 ewprintf("File read error"); 200 return (FIOERR); 201 } 202 *nbytes = i; 203 return (c == EOF ? FIOEOF : FIOSUC); 204 } 205 206 /* 207 * Make a backup copy of "fname". On Unix the backup has the same 208 * name as the original file, with a "~" on the end; this seems to 209 * be newest of the new-speak. The error handling is all in "file.c". 210 * We do a copy instead of a rename since otherwise another process 211 * with an open fd will get the backup, not the new file. This is 212 * a problem when using mg with things like crontab and vipw. 213 */ 214 int 215 fbackupfile(const char *fn) 216 { 217 struct stat sb; 218 int from, to, serrno; 219 ssize_t nread; 220 char buf[BUFSIZ]; 221 char *nname, *tname, *bkpth; 222 223 if (stat(fn, &sb) == -1) { 224 dobeep(); 225 ewprintf("Can't stat %s : %s", fn, strerror(errno)); 226 return (FALSE); 227 } 228 229 if ((bkpth = bkuplocation(fn)) == NULL) 230 return (FALSE); 231 232 if (asprintf(&nname, "%s~", bkpth) == -1) { 233 dobeep(); 234 ewprintf("Can't allocate backup file name : %s", strerror(errno)); 235 free(bkpth); 236 return (ABORT); 237 } 238 if (asprintf(&tname, "%s.XXXXXXXXXX", bkpth) == -1) { 239 dobeep(); 240 ewprintf("Can't allocate temp file name : %s", strerror(errno)); 241 free(bkpth); 242 free(nname); 243 return (ABORT); 244 } 245 free(bkpth); 246 247 if ((from = open(fn, O_RDONLY)) == -1) { 248 free(nname); 249 free(tname); 250 return (FALSE); 251 } 252 to = mkstemp(tname); 253 if (to == -1) { 254 serrno = errno; 255 close(from); 256 free(nname); 257 free(tname); 258 errno = serrno; 259 return (FALSE); 260 } 261 while ((nread = read(from, buf, sizeof(buf))) > 0) { 262 if (write(to, buf, (size_t)nread) != nread) { 263 nread = -1; 264 break; 265 } 266 } 267 serrno = errno; 268 (void) fchmod(to, (sb.st_mode & 0777)); 269 270 /* copy the mtime to the backupfile */ 271 struct timespec new_times[2]; 272 new_times[0] = sb.st_atim; 273 new_times[1] = sb.st_mtim; 274 futimens(to, new_times); 275 276 close(from); 277 close(to); 278 if (nread == -1) { 279 if (unlink(tname) == -1) 280 ewprintf("Can't unlink temp : %s", strerror(errno)); 281 } else { 282 if (rename(tname, nname) == -1) { 283 ewprintf("Can't rename temp : %s", strerror(errno)); 284 (void) unlink(tname); 285 nread = -1; 286 } 287 } 288 free(nname); 289 free(tname); 290 errno = serrno; 291 292 return (nread == -1 ? FALSE : TRUE); 293 } 294 295 /* 296 * Convert "fn" to a canonicalized absolute filename, replacing 297 * a leading ~/ with the user's home dir, following symlinks, and 298 * remove all occurrences of /./ and /../ 299 */ 300 char * 301 adjustname(const char *fn, int slashslash) 302 { 303 static char fnb[PATH_MAX]; 304 const char *cp, *ep = NULL; 305 char *path; 306 307 if (slashslash == TRUE) { 308 cp = fn + strlen(fn) - 1; 309 for (; cp >= fn; cp--) { 310 if (ep && (*cp == '/')) { 311 fn = ep; 312 break; 313 } 314 if (*cp == '/' || *cp == '~') 315 ep = cp; 316 else 317 ep = NULL; 318 } 319 } 320 if ((path = expandtilde(fn)) == NULL) 321 return (NULL); 322 323 if (realpath(path, fnb) == NULL) 324 (void)strlcpy(fnb, path, sizeof(fnb)); 325 326 free(path); 327 return (fnb); 328 } 329 330 /* 331 * Find a startup file for the user and return its name. As a service 332 * to other pieces of code that may want to find a startup file (like 333 * the terminal driver in particular), accepts a suffix to be appended 334 * to the startup file name. 335 */ 336 char * 337 startupfile(char *suffix) 338 { 339 static char file[NFILEN]; 340 char *home; 341 int ret; 342 343 if ((home = getenv("HOME")) == NULL || *home == '\0') 344 goto nohome; 345 346 if (suffix == NULL) { 347 ret = snprintf(file, sizeof(file), _PATH_MG_STARTUP, home); 348 if (ret < 0 || ret >= sizeof(file)) 349 return (NULL); 350 } else { 351 ret = snprintf(file, sizeof(file), _PATH_MG_TERM, home, suffix); 352 if (ret < 0 || ret >= sizeof(file)) 353 return (NULL); 354 } 355 356 if (access(file, R_OK) == 0) 357 return (file); 358 nohome: 359 #ifdef STARTUPFILE 360 if (suffix == NULL) { 361 ret = snprintf(file, sizeof(file), "%s", STARTUPFILE); 362 if (ret < 0 || ret >= sizeof(file)) 363 return (NULL); 364 } else { 365 ret = snprintf(file, sizeof(file), "%s%s", STARTUPFILE, 366 suffix); 367 if (ret < 0 || ret >= sizeof(file)) 368 return (NULL); 369 } 370 371 if (access(file, R_OK) == 0) 372 return (file); 373 #endif /* STARTUPFILE */ 374 return (NULL); 375 } 376 377 int 378 copy(char *frname, char *toname) 379 { 380 int ifd, ofd; 381 char buf[BUFSIZ]; 382 mode_t fmode = DEFFILEMODE; /* XXX?? */ 383 struct stat orig; 384 ssize_t sr; 385 386 if ((ifd = open(frname, O_RDONLY)) == -1) 387 return (FALSE); 388 if (fstat(ifd, &orig) == -1) { 389 dobeep(); 390 ewprintf("fstat: %s", strerror(errno)); 391 close(ifd); 392 return (FALSE); 393 } 394 395 if ((ofd = open(toname, O_WRONLY|O_CREAT|O_TRUNC, fmode)) == -1) { 396 close(ifd); 397 return (FALSE); 398 } 399 while ((sr = read(ifd, buf, sizeof(buf))) > 0) { 400 if (write(ofd, buf, (size_t)sr) != sr) { 401 ewprintf("write error : %s", strerror(errno)); 402 break; 403 } 404 } 405 if (fchmod(ofd, orig.st_mode) == -1) 406 ewprintf("Cannot set original mode : %s", strerror(errno)); 407 408 if (sr == -1) { 409 ewprintf("Read error : %s", strerror(errno)); 410 close(ifd); 411 close(ofd); 412 return (FALSE); 413 } 414 /* 415 * It is "normal" for this to fail since we can't guarantee that 416 * we will be running as root. 417 */ 418 if (fchown(ofd, orig.st_uid, orig.st_gid) && errno != EPERM) 419 ewprintf("Cannot set owner : %s", strerror(errno)); 420 421 (void) close(ifd); 422 (void) close(ofd); 423 424 return (TRUE); 425 } 426 427 /* 428 * return list of file names that match the name in buf. 429 */ 430 struct list * 431 make_file_list(char *buf) 432 { 433 char *dir, *file, *cp; 434 size_t len, preflen; 435 int ret; 436 DIR *dirp; 437 struct dirent *dent; 438 struct list *last, *current; 439 char fl_name[NFILEN + 2]; 440 char prefixx[NFILEN + 1]; 441 442 /* 443 * We need three different strings: 444 445 * dir - the name of the directory containing what the user typed. 446 * Must be a real unix file name, e.g. no ~user, etc.. 447 * Must not end in /. 448 * prefix - the portion of what the user typed that is before the 449 * names we are going to find in the directory. Must have a 450 * trailing / if the user typed it. 451 * names from the directory - We open dir, and return prefix 452 * concatenated with names. 453 */ 454 455 /* first we get a directory name we can look up */ 456 /* 457 * Names ending in . are potentially odd, because adjustname will 458 * treat foo/bar/.. as a foo/, whereas we are 459 * interested in names starting with .. 460 */ 461 len = strlen(buf); 462 if (len && buf[len - 1] == '.') { 463 buf[len - 1] = 'x'; 464 dir = adjustname(buf, TRUE); 465 buf[len - 1] = '.'; 466 } else 467 dir = adjustname(buf, TRUE); 468 if (dir == NULL) 469 return (NULL); 470 /* 471 * If the user typed a trailing / or the empty string 472 * he wants us to use his file spec as a directory name. 473 */ 474 if (len && buf[len - 1] != '/') { 475 file = strrchr(dir, '/'); 476 if (file) { 477 *file = '\0'; 478 if (*dir == '\0') 479 dir = "/"; 480 } else 481 return (NULL); 482 } 483 /* Now we get the prefix of the name the user typed. */ 484 if (strlcpy(prefixx, buf, sizeof(prefixx)) >= sizeof(prefixx)) 485 return (NULL); 486 cp = strrchr(prefixx, '/'); 487 if (cp == NULL) 488 prefixx[0] = '\0'; 489 else 490 cp[1] = '\0'; 491 492 preflen = strlen(prefixx); 493 /* cp is the tail of buf that really needs to be compared. */ 494 cp = buf + preflen; 495 len = strlen(cp); 496 497 /* 498 * Now make sure that file names will fit in the buffers allocated. 499 * SV files are fairly short. For BSD, something more general would 500 * be required. 501 */ 502 if (preflen > NFILEN - MAXNAMLEN) 503 return (NULL); 504 505 /* loop over the specified directory, making up the list of files */ 506 507 /* 508 * Note that it is worth our time to filter out names that don't 509 * match, even though our caller is going to do so again, and to 510 * avoid doing the stat if completion is being done, because stat'ing 511 * every file in the directory is relatively expensive. 512 */ 513 514 dirp = opendir(dir); 515 if (dirp == NULL) 516 return (NULL); 517 last = NULL; 518 519 while ((dent = readdir(dirp)) != NULL) { 520 int isdir; 521 if (strncmp(cp, dent->d_name, len) != 0) 522 continue; 523 isdir = 0; 524 if (dent->d_type == DT_DIR) { 525 isdir = 1; 526 } else if (dent->d_type == DT_LNK || 527 dent->d_type == DT_UNKNOWN) { 528 struct stat statbuf; 529 530 if (fstatat(dirfd(dirp), dent->d_name, &statbuf, 0) < 0) 531 continue; 532 if (S_ISDIR(statbuf.st_mode)) 533 isdir = 1; 534 } 535 536 if ((current = malloc(sizeof(struct list))) == NULL) { 537 free_file_list(last); 538 closedir(dirp); 539 return (NULL); 540 } 541 ret = snprintf(fl_name, sizeof(fl_name), 542 "%s%s%s", prefixx, dent->d_name, isdir ? "/" : ""); 543 if (ret < 0 || ret >= sizeof(fl_name)) { 544 free(current); 545 continue; 546 } 547 current->l_next = last; 548 current->l_name = strdup(fl_name); 549 last = current; 550 } 551 closedir(dirp); 552 553 return (last); 554 } 555 556 /* 557 * Test if a supplied filename refers to a directory 558 * Returns ABORT on error, TRUE if directory. FALSE otherwise 559 */ 560 int 561 fisdir(const char *fname) 562 { 563 struct stat statbuf; 564 565 if (stat(fname, &statbuf) != 0) 566 return (ABORT); 567 568 if (S_ISDIR(statbuf.st_mode)) 569 return (TRUE); 570 571 return (FALSE); 572 } 573 574 /* 575 * Check the mtime of the supplied filename. 576 * Return TRUE if last mtime matches, FALSE if not, 577 * If the stat fails, return TRUE and try the save anyway 578 */ 579 int 580 fchecktime(struct buffer *bp) 581 { 582 struct stat sb; 583 584 if (stat(bp->b_fname, &sb) == -1) 585 return (TRUE); 586 587 if (bp->b_fi.fi_mtime.tv_sec != sb.st_mtimespec.tv_sec || 588 bp->b_fi.fi_mtime.tv_nsec != sb.st_mtimespec.tv_nsec) 589 return (FALSE); 590 591 return (TRUE); 592 593 } 594 595 /* 596 * Location of backup file. This function creates the correct path. 597 */ 598 static char * 599 bkuplocation(const char *fn) 600 { 601 struct stat sb; 602 char *ret; 603 604 if (bkupdir != NULL && (stat(bkupdir, &sb) == 0) && 605 S_ISDIR(sb.st_mode) && !bkupleavetmp(fn)) { 606 char fname[NFILEN]; 607 const char *c; 608 int i = 0, len; 609 610 c = fn; 611 len = strlen(bkupdir); 612 613 while (*c != '\0') { 614 /* Make sure we don't go over combined: 615 * strlen(bkupdir + '/' + fname + '\0') 616 */ 617 if (i >= NFILEN - len - 1) 618 return (NULL); 619 if (*c == '/') { 620 fname[i] = '!'; 621 } else if (*c == '!') { 622 if (i >= NFILEN - len - 2) 623 return (NULL); 624 fname[i++] = '!'; 625 fname[i] = '!'; 626 } else 627 fname[i] = *c; 628 i++; 629 c++; 630 } 631 fname[i] = '\0'; 632 if (asprintf(&ret, "%s/%s", bkupdir, fname) == -1) 633 return (NULL); 634 635 } else if ((ret = strndup(fn, NFILEN)) == NULL) 636 return (NULL); 637 638 return (ret); 639 } 640 641 int 642 backuptohomedir(int f, int n) 643 { 644 const char *c = _PATH_MG_DIR; 645 char *p; 646 647 if (bkupdir == NULL) { 648 p = adjustname(c, TRUE); 649 bkupdir = strndup(p, NFILEN); 650 if (bkupdir == NULL) 651 return(FALSE); 652 653 if (mkdir(bkupdir, 0700) == -1 && errno != EEXIST) { 654 free(bkupdir); 655 bkupdir = NULL; 656 } 657 } else { 658 free(bkupdir); 659 bkupdir = NULL; 660 } 661 662 return (TRUE); 663 } 664 665 /* 666 * For applications that use mg as the editor and have a desire to keep 667 * '~' files in /tmp, toggle the location: /tmp | ~/.mg.d 668 */ 669 int 670 toggleleavetmp(int f, int n) 671 { 672 leavetmp = !leavetmp; 673 674 return (TRUE); 675 } 676 677 /* 678 * Returns TRUE if fn is located in the temp directory and we want to save 679 * those backups there. 680 */ 681 int 682 bkupleavetmp(const char *fn) 683 { 684 if (!leavetmp) 685 return(FALSE); 686 687 if (strncmp(fn, "/tmp", 4) == 0) 688 return (TRUE); 689 690 return (FALSE); 691 } 692 693 /* 694 * Expand file names beginning with '~' if appropriate: 695 * 1, if ./~fn exists, continue without expanding tilde. 696 * 2, else, if username 'fn' exists, expand tilde with home directory path. 697 * 3, otherwise, continue and create new buffer called ~fn. 698 */ 699 char * 700 expandtilde(const char *fn) 701 { 702 struct passwd *pw; 703 struct stat statbuf; 704 const char *cp; 705 char user[LOGIN_NAME_MAX], path[NFILEN]; 706 char *un, *ret; 707 size_t ulen, plen; 708 709 path[0] = '\0'; 710 711 if (fn[0] != '~' || stat(fn, &statbuf) == 0) { 712 if ((ret = strndup(fn, NFILEN)) == NULL) 713 return (NULL); 714 return(ret); 715 } 716 cp = strchr(fn, '/'); 717 if (cp == NULL) 718 cp = fn + strlen(fn); /* point to the NUL byte */ 719 ulen = cp - &fn[1]; 720 if (ulen >= sizeof(user)) { 721 if ((ret = strndup(fn, NFILEN)) == NULL) 722 return (NULL); 723 return(ret); 724 } 725 if (ulen == 0) { /* ~/ or ~ */ 726 if ((un = getlogin()) != NULL) 727 (void)strlcpy(user, un, sizeof(user)); 728 else 729 user[0] = '\0'; 730 } else { /* ~user/ or ~user */ 731 memcpy(user, &fn[1], ulen); 732 user[ulen] = '\0'; 733 } 734 pw = getpwnam(user); 735 if (pw != NULL) { 736 plen = strlcpy(path, pw->pw_dir, sizeof(path)); 737 if (plen == 0 || path[plen - 1] != '/') { 738 if (strlcat(path, "/", sizeof(path)) >= sizeof(path)) { 739 dobeep(); 740 ewprintf("Path too long"); 741 return (NULL); 742 } 743 } 744 fn = cp; 745 if (*fn == '/') 746 fn++; 747 } 748 if (strlcat(path, fn, sizeof(path)) >= sizeof(path)) { 749 dobeep(); 750 ewprintf("Path too long"); 751 return (NULL); 752 } 753 if ((ret = strndup(path, NFILEN)) == NULL) 754 return (NULL); 755 756 return (ret); 757 } 758