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