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