1 /* $OpenBSD: xinstall.c,v 1.65 2016/05/13 17:51:15 jmc Exp $ */ 2 /* $NetBSD: xinstall.c,v 1.9 1995/12/20 10:25:17 jonathan Exp $ */ 3 4 /* 5 * Copyright (c) 1987, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> /* MAXBSIZE */ 34 #include <sys/wait.h> 35 #include <sys/mman.h> 36 #include <sys/stat.h> 37 38 #include <ctype.h> 39 #include <err.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <grp.h> 43 #include <paths.h> 44 #include <pwd.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <limits.h> 50 #include <utime.h> 51 #include <libgen.h> 52 53 #include "pathnames.h" 54 55 #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 56 57 #define DIRECTORY 0x01 /* Tell install it's a directory. */ 58 #define SETFLAGS 0x02 /* Tell install to set flags. */ 59 #define USEFSYNC 0x04 /* Tell install to use fsync(2). */ 60 #define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) 61 #define BACKUP_SUFFIX ".old" 62 63 struct passwd *pp; 64 struct group *gp; 65 int dobackup, docompare, dodest, dodir, dopreserve, dostrip, safecopy; 66 int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 67 char pathbuf[PATH_MAX], tempfile[PATH_MAX]; 68 char *suffix = BACKUP_SUFFIX; 69 uid_t uid; 70 gid_t gid; 71 72 void copy(int, char *, int, char *, off_t, int); 73 int compare(int, const char *, off_t, int, const char *, off_t); 74 void install(char *, char *, u_long, u_int); 75 void install_dir(char *, int); 76 void strip(char *); 77 void usage(void); 78 int create_newfile(char *, struct stat *); 79 int create_tempfile(char *, char *, size_t); 80 int file_write(int, char *, size_t, int *, int *, int); 81 void file_flush(int, int); 82 83 int 84 main(int argc, char *argv[]) 85 { 86 struct stat from_sb, to_sb; 87 void *set; 88 u_int32_t fset; 89 u_int iflags; 90 int ch, no_target; 91 char *flags, *to_name, *group = NULL, *owner = NULL; 92 93 iflags = 0; 94 while ((ch = getopt(argc, argv, "B:bCcDdFf:g:m:o:pSs")) != -1) 95 switch(ch) { 96 case 'C': 97 docompare = 1; 98 break; 99 case 'B': 100 suffix = optarg; 101 /* fall through; -B implies -b */ 102 case 'b': 103 dobackup = 1; 104 break; 105 case 'c': 106 /* For backwards compatibility. */ 107 break; 108 case 'F': 109 iflags |= USEFSYNC; 110 break; 111 case 'f': 112 flags = optarg; 113 if (strtofflags(&flags, &fset, NULL)) 114 errx(1, "%s: invalid flag", flags); 115 iflags |= SETFLAGS; 116 break; 117 case 'g': 118 group = optarg; 119 break; 120 case 'm': 121 if (!(set = setmode(optarg))) 122 errx(1, "%s: invalid file mode", optarg); 123 mode = getmode(set, 0); 124 free(set); 125 break; 126 case 'o': 127 owner = optarg; 128 break; 129 case 'p': 130 docompare = dopreserve = 1; 131 break; 132 case 'S': 133 safecopy = 1; 134 break; 135 case 's': 136 dostrip = 1; 137 break; 138 case 'D': 139 dodest = 1; 140 break; 141 case 'd': 142 dodir = 1; 143 break; 144 case '?': 145 default: 146 usage(); 147 } 148 argc -= optind; 149 argv += optind; 150 151 /* some options make no sense when creating directories */ 152 if ((safecopy || docompare || dostrip) && dodir) 153 usage(); 154 155 /* must have at least two arguments, except when creating directories */ 156 if (argc < 2 && !dodir) 157 usage(); 158 159 /* need to make a temp copy so we can compare stripped version */ 160 if (docompare && dostrip) 161 safecopy = 1; 162 163 /* get group and owner id's */ 164 if (group && !(gp = getgrnam(group)) && !isdigit((unsigned char)*group)) 165 errx(1, "unknown group %s", group); 166 gid = (group) ? ((gp) ? gp->gr_gid : (gid_t)strtoul(group, NULL, 10)) : (gid_t)-1; 167 if (owner && !(pp = getpwnam(owner)) && !isdigit((unsigned char)*owner)) 168 errx(1, "unknown user %s", owner); 169 uid = (owner) ? ((pp) ? pp->pw_uid : (uid_t)strtoul(owner, NULL, 10)) : (uid_t)-1; 170 171 if (dodir) { 172 for (; *argv != NULL; ++argv) 173 install_dir(*argv, mode); 174 exit(0); 175 /* NOTREACHED */ 176 } 177 178 if (dodest) { 179 char *dest = dirname(argv[argc - 1]); 180 if (dest == NULL) 181 errx(1, "cannot determine dirname"); 182 /* 183 * When -D is passed, do not chmod the directory with the mode set for 184 * the target file. If more restrictive permissions are required then 185 * '-d -m' ought to be used instead. 186 */ 187 install_dir(dest, 0755); 188 } 189 190 no_target = stat(to_name = argv[argc - 1], &to_sb); 191 if (!no_target && S_ISDIR(to_sb.st_mode)) { 192 for (; *argv != to_name; ++argv) 193 install(*argv, to_name, fset, iflags | DIRECTORY); 194 exit(0); 195 /* NOTREACHED */ 196 } 197 198 /* can't do file1 file2 directory/file */ 199 if (argc != 2) 200 errx(1, "Target: %s", argv[argc-1]); 201 202 if (!no_target) { 203 if (stat(*argv, &from_sb)) 204 err(1, "%s", *argv); 205 if (!S_ISREG(to_sb.st_mode)) 206 errc(1, EFTYPE, "%s", to_name); 207 if (to_sb.st_dev == from_sb.st_dev && 208 to_sb.st_ino == from_sb.st_ino) 209 errx(1, "%s and %s are the same file", *argv, to_name); 210 } 211 install(*argv, to_name, fset, iflags); 212 exit(0); 213 /* NOTREACHED */ 214 } 215 216 /* 217 * install -- 218 * build a path name and install the file 219 */ 220 void 221 install(char *from_name, char *to_name, u_long fset, u_int flags) 222 { 223 struct stat from_sb, to_sb; 224 struct timespec ts[2]; 225 int devnull, from_fd, to_fd, serrno, files_match = 0; 226 char *p; 227 228 (void)memset((void *)&from_sb, 0, sizeof(from_sb)); 229 (void)memset((void *)&to_sb, 0, sizeof(to_sb)); 230 231 /* If try to install NULL file to a directory, fails. */ 232 if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { 233 if (stat(from_name, &from_sb)) 234 err(1, "%s", from_name); 235 if (!S_ISREG(from_sb.st_mode)) 236 errc(1, EFTYPE, "%s", from_name); 237 /* Build the target path. */ 238 if (flags & DIRECTORY) { 239 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", 240 to_name, 241 (p = strrchr(from_name, '/')) ? ++p : from_name); 242 to_name = pathbuf; 243 } 244 devnull = 0; 245 } else { 246 devnull = 1; 247 } 248 249 if (stat(to_name, &to_sb) == 0) { 250 /* Only compare against regular files. */ 251 if (docompare && !S_ISREG(to_sb.st_mode)) { 252 docompare = 0; 253 warnc(EFTYPE, "%s", to_name); 254 } 255 } else if (docompare) { 256 /* File does not exist so silently ignore compare flag. */ 257 docompare = 0; 258 } 259 260 if (!devnull) { 261 if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) 262 err(1, "%s", from_name); 263 } 264 265 if (safecopy) { 266 to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile)); 267 if (to_fd < 0) 268 err(1, "%s", tempfile); 269 } else if (docompare && !dostrip) { 270 if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) 271 err(1, "%s", to_name); 272 } else { 273 if ((to_fd = create_newfile(to_name, &to_sb)) < 0) 274 err(1, "%s", to_name); 275 } 276 277 if (!devnull) { 278 if (docompare && !safecopy) { 279 files_match = !(compare(from_fd, from_name, 280 from_sb.st_size, to_fd, 281 to_name, to_sb.st_size)); 282 283 /* Truncate "to" file for copy unless we match */ 284 if (!files_match) { 285 (void)close(to_fd); 286 if ((to_fd = create_newfile(to_name, &to_sb)) < 0) 287 err(1, "%s", to_name); 288 } 289 } 290 if (!files_match) 291 copy(from_fd, from_name, to_fd, 292 safecopy ? tempfile : to_name, from_sb.st_size, 293 ((off_t)from_sb.st_blocks * S_BLKSIZE < from_sb.st_size)); 294 } 295 296 if (dostrip) { 297 strip(safecopy ? tempfile : to_name); 298 299 /* 300 * Re-open our fd on the target, in case we used a strip 301 * that does not work in-place -- like gnu binutils strip. 302 */ 303 close(to_fd); 304 if ((to_fd = open(safecopy ? tempfile : to_name, O_RDONLY, 305 0)) < 0) 306 err(1, "stripping %s", to_name); 307 } 308 309 /* 310 * Compare the (possibly stripped) temp file to the target. 311 */ 312 if (safecopy && docompare) { 313 int temp_fd = to_fd; 314 struct stat temp_sb; 315 316 /* Re-open to_fd using the real target name. */ 317 if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) 318 err(1, "%s", to_name); 319 320 if (fstat(temp_fd, &temp_sb)) { 321 serrno = errno; 322 (void)unlink(tempfile); 323 errc(1, serrno, "%s", tempfile); 324 } 325 326 if (compare(temp_fd, tempfile, temp_sb.st_size, to_fd, 327 to_name, to_sb.st_size) == 0) { 328 /* 329 * If target has more than one link we need to 330 * replace it in order to snap the extra links. 331 * Need to preserve target file times, though. 332 */ 333 if (to_sb.st_nlink != 1) { 334 ts[0] = to_sb.st_atim; 335 ts[1] = to_sb.st_mtim; 336 futimens(temp_fd, ts); 337 } else { 338 files_match = 1; 339 (void)unlink(tempfile); 340 } 341 } 342 (void)close(to_fd); 343 to_fd = temp_fd; 344 } 345 346 /* 347 * Preserve the timestamp of the source file if necessary. 348 */ 349 if (dopreserve && !files_match) { 350 ts[0] = from_sb.st_atim; 351 ts[1] = from_sb.st_mtim; 352 futimens(to_fd, ts); 353 } 354 355 /* 356 * Set owner, group, mode for target; do the chown first, 357 * chown may lose the setuid bits. 358 */ 359 if ((gid != (gid_t)-1 || uid != (uid_t)-1) && 360 fchown(to_fd, uid, gid)) { 361 serrno = errno; 362 (void)unlink(safecopy ? tempfile : to_name); 363 errx(1, "%s: chown/chgrp: %s", 364 safecopy ? tempfile : to_name, strerror(serrno)); 365 } 366 if (fchmod(to_fd, mode)) { 367 serrno = errno; 368 (void)unlink(safecopy ? tempfile : to_name); 369 errx(1, "%s: chmod: %s", safecopy ? tempfile : to_name, 370 strerror(serrno)); 371 } 372 373 /* 374 * If provided a set of flags, set them, otherwise, preserve the 375 * flags, except for the dump flag. 376 */ 377 if (fchflags(to_fd, 378 flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { 379 if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0) 380 warnx("%s: chflags: %s", 381 safecopy ? tempfile :to_name, strerror(errno)); 382 } 383 384 if (flags & USEFSYNC) 385 fsync(to_fd); 386 (void)close(to_fd); 387 if (!devnull) 388 (void)close(from_fd); 389 390 /* 391 * Move the new file into place if doing a safe copy 392 * and the files are different (or just not compared). 393 */ 394 if (safecopy && !files_match) { 395 /* Try to turn off the immutable bits. */ 396 if (to_sb.st_flags & (NOCHANGEBITS)) 397 (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS)); 398 if (dobackup) { 399 char backup[PATH_MAX]; 400 (void)snprintf(backup, PATH_MAX, "%s%s", to_name, 401 suffix); 402 /* It is ok for the target file not to exist. */ 403 if (rename(to_name, backup) < 0 && errno != ENOENT) { 404 serrno = errno; 405 unlink(tempfile); 406 errx(1, "rename: %s to %s: %s", to_name, 407 backup, strerror(serrno)); 408 } 409 } 410 if (rename(tempfile, to_name) < 0 ) { 411 serrno = errno; 412 unlink(tempfile); 413 errx(1, "rename: %s to %s: %s", tempfile, 414 to_name, strerror(serrno)); 415 } 416 } 417 } 418 419 /* 420 * copy -- 421 * copy from one file to another 422 */ 423 void 424 copy(int from_fd, char *from_name, int to_fd, char *to_name, off_t size, 425 int sparse) 426 { 427 ssize_t nr, nw; 428 int serrno; 429 char *p, buf[MAXBSIZE]; 430 431 if (size == 0) 432 return; 433 434 /* Rewind file descriptors. */ 435 if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1) 436 err(1, "lseek: %s", from_name); 437 if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1) 438 err(1, "lseek: %s", to_name); 439 440 /* 441 * Mmap and write if less than 8M (the limit is so we don't totally 442 * trash memory on big files. This is really a minor hack, but it 443 * wins some CPU back. Sparse files need special treatment. 444 */ 445 if (!sparse && size <= 8 * 1048576) { 446 size_t siz; 447 448 if ((p = mmap(NULL, (size_t)size, PROT_READ, MAP_PRIVATE, 449 from_fd, (off_t)0)) == MAP_FAILED) { 450 serrno = errno; 451 (void)unlink(to_name); 452 errc(1, serrno, "%s", from_name); 453 } 454 madvise(p, size, MADV_SEQUENTIAL); 455 siz = (size_t)size; 456 if ((nw = write(to_fd, p, siz)) != siz) { 457 serrno = errno; 458 (void)unlink(to_name); 459 errx(1, "%s: %s", 460 to_name, strerror(nw > 0 ? EIO : serrno)); 461 } 462 (void) munmap(p, (size_t)size); 463 } else { 464 int sz, rem, isem = 1; 465 struct stat sb; 466 467 /* 468 * Pass the blocksize of the file being written to the write 469 * routine. if the size is zero, use the default S_BLKSIZE. 470 */ 471 if (fstat(to_fd, &sb) != 0 || sb.st_blksize == 0) 472 sz = S_BLKSIZE; 473 else 474 sz = sb.st_blksize; 475 rem = sz; 476 477 while ((nr = read(from_fd, buf, sizeof(buf))) > 0) { 478 if (sparse) 479 nw = file_write(to_fd, buf, nr, &rem, &isem, sz); 480 else 481 nw = write(to_fd, buf, nr); 482 if (nw != nr) { 483 serrno = errno; 484 (void)unlink(to_name); 485 errx(1, "%s: %s", 486 to_name, strerror(nw > 0 ? EIO : serrno)); 487 } 488 } 489 if (sparse) 490 file_flush(to_fd, isem); 491 if (nr != 0) { 492 serrno = errno; 493 (void)unlink(to_name); 494 errc(1, serrno, "%s", from_name); 495 } 496 } 497 } 498 499 /* 500 * compare -- 501 * compare two files; non-zero means files differ 502 */ 503 int 504 compare(int from_fd, const char *from_name, off_t from_len, int to_fd, 505 const char *to_name, off_t to_len) 506 { 507 caddr_t p1, p2; 508 size_t length; 509 off_t from_off, to_off, remainder; 510 int dfound; 511 512 if (from_len == 0 && from_len == to_len) 513 return (0); 514 515 if (from_len != to_len) 516 return (1); 517 518 /* 519 * Compare the two files being careful not to mmap 520 * more than 8M at a time. 521 */ 522 from_off = to_off = (off_t)0; 523 remainder = from_len; 524 do { 525 length = MINIMUM(remainder, 8 * 1048576); 526 remainder -= length; 527 528 if ((p1 = mmap(NULL, length, PROT_READ, MAP_PRIVATE, 529 from_fd, from_off)) == MAP_FAILED) 530 err(1, "%s", from_name); 531 if ((p2 = mmap(NULL, length, PROT_READ, MAP_PRIVATE, 532 to_fd, to_off)) == MAP_FAILED) 533 err(1, "%s", to_name); 534 if (length) { 535 madvise(p1, length, MADV_SEQUENTIAL); 536 madvise(p2, length, MADV_SEQUENTIAL); 537 } 538 539 dfound = memcmp(p1, p2, length); 540 541 (void) munmap(p1, length); 542 (void) munmap(p2, length); 543 544 from_off += length; 545 to_off += length; 546 547 } while (!dfound && remainder > 0); 548 549 return(dfound); 550 } 551 552 /* 553 * strip -- 554 * use strip(1) to strip the target file 555 */ 556 void 557 strip(char *to_name) 558 { 559 int serrno, status; 560 char * volatile path_strip; 561 562 if (issetugid() || (path_strip = getenv("STRIP")) == NULL) 563 path_strip = _PATH_STRIP; 564 565 switch (vfork()) { 566 case -1: 567 serrno = errno; 568 (void)unlink(to_name); 569 errc(1, serrno, "forks"); 570 case 0: 571 execl(path_strip, "strip", "--", to_name, (char *)NULL); 572 warn("%s", path_strip); 573 _exit(1); 574 default: 575 if (wait(&status) == -1 || !WIFEXITED(status)) 576 (void)unlink(to_name); 577 } 578 } 579 580 /* 581 * install_dir -- 582 * build directory hierarchy 583 */ 584 void 585 install_dir(char *path, int mode) 586 { 587 char *p; 588 struct stat sb; 589 int ch; 590 591 for (p = path;; ++p) 592 if (!*p || (p != path && *p == '/')) { 593 ch = *p; 594 *p = '\0'; 595 if (mkdir(path, 0777)) { 596 int mkdir_errno = errno; 597 if (stat(path, &sb)) { 598 /* Not there; use mkdir()s errno */ 599 errc(1, mkdir_errno, "%s", 600 path); 601 /* NOTREACHED */ 602 } 603 if (!S_ISDIR(sb.st_mode)) { 604 /* Is there, but isn't a directory */ 605 errc(1, ENOTDIR, "%s", path); 606 /* NOTREACHED */ 607 } 608 } 609 if (!(*p = ch)) 610 break; 611 } 612 613 if (((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid)) || 614 chmod(path, mode)) { 615 warn("%s", path); 616 } 617 } 618 619 /* 620 * usage -- 621 * print a usage message and die 622 */ 623 void 624 usage(void) 625 { 626 (void)fprintf(stderr, "\ 627 usage: install [-bCcDdFpSs] [-B suffix] [-f flags] [-g group] [-m mode] [-o owner]\n source ... target ...\n"); 628 exit(1); 629 /* NOTREACHED */ 630 } 631 632 /* 633 * create_tempfile -- 634 * create a temporary file based on path and open it 635 */ 636 int 637 create_tempfile(char *path, char *temp, size_t tsize) 638 { 639 char *p; 640 641 strlcpy(temp, path, tsize); 642 if ((p = strrchr(temp, '/')) != NULL) 643 p++; 644 else 645 p = temp; 646 *p = '\0'; 647 strlcat(p, "INS@XXXXXXXXXX", tsize); 648 649 return(mkstemp(temp)); 650 } 651 652 /* 653 * create_newfile -- 654 * create a new file, overwriting an existing one if necessary 655 */ 656 int 657 create_newfile(char *path, struct stat *sbp) 658 { 659 char backup[PATH_MAX]; 660 661 /* 662 * Unlink now... avoid ETXTBSY errors later. Try and turn 663 * off the append/immutable bits -- if we fail, go ahead, 664 * it might work. 665 */ 666 if (sbp->st_flags & (NOCHANGEBITS)) 667 (void)chflags(path, sbp->st_flags & ~(NOCHANGEBITS)); 668 669 if (dobackup) { 670 (void)snprintf(backup, PATH_MAX, "%s%s", path, suffix); 671 /* It is ok for the target file not to exist. */ 672 if (rename(path, backup) < 0 && errno != ENOENT) 673 err(1, "rename: %s to %s (errno %d)", path, backup, errno); 674 } else { 675 if (unlink(path) < 0 && errno != ENOENT) 676 err(1, "%s", path); 677 } 678 679 return(open(path, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR)); 680 } 681 682 /* 683 * file_write() 684 * Write/copy a file (during copy or archive extract). This routine knows 685 * how to copy files with lseek holes in it. (Which are read as file 686 * blocks containing all 0's but do not have any file blocks associated 687 * with the data). Typical examples of these are files created by dbm 688 * variants (.pag files). While the file size of these files are huge, the 689 * actual storage is quite small (the files are sparse). The problem is 690 * the holes read as all zeros so are probably stored on the archive that 691 * way (there is no way to determine if the file block is really a hole, 692 * we only know that a file block of all zero's can be a hole). 693 * At this writing, no major archive format knows how to archive files 694 * with holes. However, on extraction (or during copy, -rw) we have to 695 * deal with these files. Without detecting the holes, the files can 696 * consume a lot of file space if just written to disk. This replacement 697 * for write when passed the basic allocation size of a file system block, 698 * uses lseek whenever it detects the input data is all 0 within that 699 * file block. In more detail, the strategy is as follows: 700 * While the input is all zero keep doing an lseek. Keep track of when we 701 * pass over file block boundaries. Only write when we hit a non zero 702 * input. once we have written a file block, we continue to write it to 703 * the end (we stop looking at the input). When we reach the start of the 704 * next file block, start checking for zero blocks again. Working on file 705 * block boundaries significantly reduces the overhead when copying files 706 * that are NOT very sparse. This overhead (when compared to a write) is 707 * almost below the measurement resolution on many systems. Without it, 708 * files with holes cannot be safely copied. It does has a side effect as 709 * it can put holes into files that did not have them before, but that is 710 * not a problem since the file contents are unchanged (in fact it saves 711 * file space). (Except on paging files for diskless clients. But since we 712 * cannot determine one of those file from here, we ignore them). If this 713 * ever ends up on a system where CTG files are supported and the holes 714 * are not desired, just do a conditional test in those routines that 715 * call file_write() and have it call write() instead. BEFORE CLOSING THE 716 * FILE, make sure to call file_flush() when the last write finishes with 717 * an empty block. A lot of file systems will not create an lseek hole at 718 * the end. In this case we drop a single 0 at the end to force the 719 * trailing 0's in the file. 720 * ---Parameters--- 721 * rem: how many bytes left in this file system block 722 * isempt: have we written to the file block yet (is it empty) 723 * sz: basic file block allocation size 724 * cnt: number of bytes on this write 725 * str: buffer to write 726 * Return: 727 * number of bytes written, -1 on write (or lseek) error. 728 */ 729 730 int 731 file_write(int fd, char *str, size_t cnt, int *rem, int *isempt, int sz) 732 { 733 char *pt; 734 char *end; 735 size_t wcnt; 736 char *st = str; 737 738 /* 739 * while we have data to process 740 */ 741 while (cnt) { 742 if (!*rem) { 743 /* 744 * We are now at the start of file system block again 745 * (or what we think one is...). start looking for 746 * empty blocks again 747 */ 748 *isempt = 1; 749 *rem = sz; 750 } 751 752 /* 753 * only examine up to the end of the current file block or 754 * remaining characters to write, whatever is smaller 755 */ 756 wcnt = MINIMUM(cnt, *rem); 757 cnt -= wcnt; 758 *rem -= wcnt; 759 if (*isempt) { 760 /* 761 * have not written to this block yet, so we keep 762 * looking for zero's 763 */ 764 pt = st; 765 end = st + wcnt; 766 767 /* 768 * look for a zero filled buffer 769 */ 770 while ((pt < end) && (*pt == '\0')) 771 ++pt; 772 773 if (pt == end) { 774 /* 775 * skip, buf is empty so far 776 */ 777 if (lseek(fd, (off_t)wcnt, SEEK_CUR) < 0) { 778 warn("lseek"); 779 return(-1); 780 } 781 st = pt; 782 continue; 783 } 784 /* 785 * drat, the buf is not zero filled 786 */ 787 *isempt = 0; 788 } 789 790 /* 791 * have non-zero data in this file system block, have to write 792 */ 793 if (write(fd, st, wcnt) != wcnt) { 794 warn("write"); 795 return(-1); 796 } 797 st += wcnt; 798 } 799 return(st - str); 800 } 801 802 /* 803 * file_flush() 804 * when the last file block in a file is zero, many file systems will not 805 * let us create a hole at the end. To get the last block with zeros, we 806 * write the last BYTE with a zero (back up one byte and write a zero). 807 */ 808 void 809 file_flush(int fd, int isempt) 810 { 811 static char blnk[] = "\0"; 812 813 /* 814 * silly test, but make sure we are only called when the last block is 815 * filled with all zeros. 816 */ 817 if (!isempt) 818 return; 819 820 /* 821 * move back one byte and write a zero 822 */ 823 if (lseek(fd, (off_t)-1, SEEK_CUR) < 0) { 824 warn("Failed seek on file"); 825 return; 826 } 827 828 if (write(fd, blnk, 1) < 0) 829 warn("Failed write to file"); 830 return; 831 } 832