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