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