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