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