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