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