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