static int fastcopy( const char *from, const char *to, struct stat *sbp ) { struct timeval tval[2]; static u_int blen = MAXPHYS; static char *bp = NULL; mode_t oldmode; int nread, from_fd, to_fd; if ( ( from_fd = open( from, O_RDONLY, 0 ) ) < 0 ) { warn( "fastcopy: open() failed (from): %s", from ); return ( 1 ); } if ( bp == NULL && ( bp = malloc( (size_t) blen ) ) == NULL ) { warnx( "malloc(%u) failed", blen ); return ( 1 ); } while ( ( to_fd = open( to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0 ) ) < 0 ) { if ( errno == EEXIST && unlink( to ) == 0 ) continue; warn( "fastcopy: open() failed (to): %s", to ); (void) close( from_fd ); return ( 1 ); } while ( ( nread = read( from_fd, bp, (size_t) blen ) ) > 0 ) if ( write( to_fd, bp, (size_t) nread ) != nread ) { warn( "fastcopy: write() failed: %s", to ); goto err; } if ( nread < 0 ) { warn( "fastcopy: read() failed: %s", from ); err: if ( unlink( to ) ) warn( "%s: remove", to ); (void) close( from_fd ); (void) close( to_fd ); return ( 1 ); } oldmode = sbp->st_mode & ALLPERMS; if ( fchown( to_fd, sbp->st_uid, sbp->st_gid ) ) { warn( "%s: set owner/group (was: %lu/%lu)", to, (u_long) sbp->st_uid, (u_long) sbp->st_gid ); if ( oldmode & ( S_ISUID | S_ISGID ) ) { warnx( "%s: owner/group changed; clearing suid/sgid (mode was 0%03o)", to, oldmode ); sbp->st_mode &= ~( S_ISUID | S_ISGID ); } } if ( fchmod( to_fd, sbp->st_mode ) ) warn( "%s: set mode (was: 0%03o)", to, oldmode ); preserve_fd_acls( from_fd, to_fd, from, to ); (void) close( from_fd ); errno = 0; if ( fchflags( to_fd, sbp->st_flags ) ) if ( errno != EOPNOTSUPP || sbp->st_flags != 0 ) warn( "%s: set flags (was: 0%07o)", to, sbp->st_flags ); tval[0].tv_sec = sbp->st_atime; tval[1].tv_sec = sbp->st_mtime; tval[0].tv_usec = tval[1].tv_usec = 0; if ( utimes( to, tval ) ) warn( "%s: set times", to ); if ( close( to_fd ) ) { warn( "%s", to ); return ( 1 ); } if ( unlink( from ) ) { warn( "%s: remove", from ); return ( 1 ); } if ( vflg ) printf( "%s -> %s\n", from, to ); return ( 0 ); }
static int fastcopy(const char *from, const char *to, struct stat *sbp) { struct timeval tval[2]; static u_int blen; static char *bp; mode_t oldmode; int nread, from_fd, to_fd; if ((from_fd = open(from, O_RDONLY, 0)) < 0) { warn("%s", from); return (1); } if (blen < sbp->st_blksize) { if (bp != NULL) free(bp); if ((bp = malloc((size_t)sbp->st_blksize)) == NULL) { blen = 0; warnx("malloc failed"); return (1); } blen = sbp->st_blksize; } while ((to_fd = open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) { if (errno == EEXIST && unlink(to) == 0) continue; warn("%s", to); (void)close(from_fd); return (1); } while ((nread = read(from_fd, bp, (size_t)blen)) > 0) if (write(to_fd, bp, (size_t)nread) != nread) { warn("%s", to); goto err; } if (nread < 0) { warn("%s", from); err: if (unlink(to)) warn("%s: remove", to); (void)close(from_fd); (void)close(to_fd); return (1); } oldmode = sbp->st_mode & ALLPERMS; if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { warn("%s: set owner/group (was: %lu/%lu)", to, (u_long)sbp->st_uid, (u_long)sbp->st_gid); if (oldmode & (S_ISUID | S_ISGID)) { warnx( "%s: owner/group changed; clearing suid/sgid (mode was 0%03o)", to, oldmode); sbp->st_mode &= ~(S_ISUID | S_ISGID); } } if (fchmod(to_fd, sbp->st_mode)) warn("%s: set mode (was: 0%03o)", to, oldmode); /* * POSIX 1003.2c states that if _POSIX_ACL_EXTENDED is in effect * for dest_file, then its ACLs shall reflect the ACLs of the * source_file. */ preserve_fd_acls(from_fd, to_fd, from, to); (void)close(from_fd); /* * XXX * NFS doesn't support chflags; ignore errors unless there's reason * to believe we're losing bits. (Note, this still won't be right * if the server supports flags and we were trying to *remove* flags * on a file that we copied, i.e., that we didn't create.) */ errno = 0; if (fchflags(to_fd, (u_long)sbp->st_flags)) if (errno != EOPNOTSUPP || sbp->st_flags != 0) warn("%s: set flags (was: 0%07o)", to, sbp->st_flags); tval[0].tv_sec = sbp->st_atime; tval[1].tv_sec = sbp->st_mtime; tval[0].tv_usec = tval[1].tv_usec = 0; if (utimes(to, tval)) warn("%s: set times", to); if (close(to_fd)) { warn("%s", to); return (1); } if (unlink(from)) { warn("%s: remove", from); return (1); } if (vflg) printf("%s -> %s\n", from, to); return (0); }
int copy_file(const FTSENT *entp, int dne) { static char *buf = NULL; static size_t bufsize; struct stat *fs; ssize_t wcount; size_t wresid; off_t wtotal; int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0; char *bufp; #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED char *p; #endif if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { warn("%s", entp->fts_path); return (1); } fs = entp->fts_statp; /* * If the file exists and we're interactive, verify with the user. * If the file DNE, set the mode to be the from file, minus setuid * bits, modified by the umask; arguably wrong, but it makes copying * executables work right and it's been that way forever. (The * other choice is 666 or'ed with the execute bits on the from file * modified by the umask.) */ if (!dne) { #define YESNO "(y/n [n]) " if (nflag) { if (vflag) printf("%s not overwritten\n", to.p_path); (void)close(from_fd); return (0); } else if (iflag) { (void)fprintf(stderr, "overwrite %s? %s", to.p_path, YESNO); checkch = ch = getchar(); while (ch != '\n' && ch != EOF) ch = getchar(); if (checkch != 'y' && checkch != 'Y') { (void)close(from_fd); (void)fprintf(stderr, "not overwritten\n"); return (1); } } if (fflag) { /* remove existing destination file name, * create a new file */ (void)unlink(to.p_path); if (!lflag) to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, fs->st_mode & ~(S_ISUID | S_ISGID)); } else { if (!lflag) /* overwrite existing destination file name */ to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); } } else { if (!lflag) to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, fs->st_mode & ~(S_ISUID | S_ISGID)); } if (to_fd == -1) { warn("%s", to.p_path); (void)close(from_fd); return (1); } rval = 0; if (!lflag) { /* * Mmap and write if less than 8M (the limit is so we don't totally * trash memory on big files. This is really a minor hack, but it * wins some CPU back. * Some filesystems, such as smbnetfs, don't support mmap, * so this is a best-effort attempt. */ #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED if (S_ISREG(fs->st_mode) && fs->st_size > 0 && fs->st_size <= 8 * 1024 * 1024 && (p = mmap(NULL, (size_t)fs->st_size, PROT_READ, MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) { wtotal = 0; for (bufp = p, wresid = fs->st_size; ; bufp += wcount, wresid -= (size_t)wcount) { wcount = write(to_fd, bufp, wresid); if (wcount <= 0) break; wtotal += wcount; if (info) { info = 0; (void)fprintf(stderr, "%s -> %s %3d%%\n", entp->fts_path, to.p_path, cp_pct(wtotal, fs->st_size)); } if (wcount >= (ssize_t)wresid) break; } if (wcount != (ssize_t)wresid) { warn("%s", to.p_path); rval = 1; } /* Some systems don't unmap on close(2). */ if (munmap(p, fs->st_size) < 0) { warn("%s", entp->fts_path); rval = 1; } } else #endif { if (buf == NULL) { /* * Note that buf and bufsize are static. If * malloc() fails, it will fail at the start * and not copy only some files. */ if (sysconf(_SC_PHYS_PAGES) > PHYSPAGES_THRESHOLD) bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); else bufsize = BUFSIZE_SMALL; buf = malloc(bufsize); if (buf == NULL) err(1, "Not enough memory"); } wtotal = 0; while ((rcount = read(from_fd, buf, bufsize)) > 0) { for (bufp = buf, wresid = rcount; ; bufp += wcount, wresid -= wcount) { wcount = write(to_fd, bufp, wresid); if (wcount <= 0) break; wtotal += wcount; if (info) { info = 0; (void)fprintf(stderr, "%s -> %s %3d%%\n", entp->fts_path, to.p_path, cp_pct(wtotal, fs->st_size)); } if (wcount >= (ssize_t)wresid) break; } if (wcount != (ssize_t)wresid) { warn("%s", to.p_path); rval = 1; break; } } if (rcount < 0) { warn("%s", entp->fts_path); rval = 1; } } } else { if (link(entp->fts_path, to.p_path)) { warn("%s", to.p_path); rval = 1; } } /* * Don't remove the target even after an error. The target might * not be a regular file, or its attributes might be important, * or its contents might be irreplaceable. It would only be safe * to remove it if we created it and its length is 0. */ if (!lflag) { if (pflag && setfile(fs, to_fd)) rval = 1; if (pflag && preserve_fd_acls(from_fd, to_fd) != 0) rval = 1; if (close(to_fd)) { warn("%s", to.p_path); rval = 1; } } (void)close(from_fd); return (rval); }
int copy_file(const FTSENT *entp, int dne) { static char *buf = NULL; static size_t bufsize; struct stat *fs; ssize_t wcount; size_t wresid; off_t wtotal; int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0; char *bufp; #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED char *p; #endif if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { warn("%s", entp->fts_path); return (1); } fs = entp->fts_statp; if (!dne) { #define YESNO "(y/n [n]) " if (nflag) { if (vflag) printf("%s not overwritten\n", to.p_path); (void)close(from_fd); return (1); } else if (iflag) { (void)fprintf(stderr, "overwrite %s? %s", to.p_path, YESNO); checkch = ch = getchar(); while (ch != '\n' && ch != EOF) ch = getchar(); if (checkch != 'y' && checkch != 'Y') { (void)close(from_fd); (void)fprintf(stderr, "not overwritten\n"); return (1); } } if (fflag) { (void)unlink(to.p_path); if (!lflag) to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, fs->st_mode & ~(S_ISUID | S_ISGID)); } else { if (!lflag) to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); } } else { if (!lflag) to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, fs->st_mode & ~(S_ISUID | S_ISGID)); } if (to_fd == -1) { warn("%s", to.p_path); (void)close(from_fd); return (1); } rval = 0; if (!lflag) { #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED if (S_ISREG(fs->st_mode) && fs->st_size > 0 && fs->st_size <= 8 * 1024 * 1024 && (p = mmap(NULL, (size_t)fs->st_size, PROT_READ, MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) { wtotal = 0; for (bufp = p, wresid = fs->st_size; ; bufp += wcount, wresid -= (size_t)wcount) { wcount = write(to_fd, bufp, wresid); if (wcount <= 0) break; wtotal += wcount; if (info) { info = 0; (void)fprintf(stderr, "%s -> %s %3d%%\n", entp->fts_path, to.p_path, cp_pct(wtotal, fs->st_size)); } if (wcount >= (ssize_t)wresid) break; } if (wcount != (ssize_t)wresid) { warn("%s", to.p_path); rval = 1; } if (munmap(p, fs->st_size) < 0) { warn("%s", entp->fts_path); rval = 1; } } else #endif { if (buf == NULL) { if (sysconf(_SC_PHYS_PAGES) > PHYSPAGES_THRESHOLD) bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); else bufsize = BUFSIZE_SMALL; buf = malloc(bufsize); if (buf == NULL) err(1, "Not enough memory"); } wtotal = 0; while ((rcount = read(from_fd, buf, bufsize)) > 0) { for (bufp = buf, wresid = rcount; ; bufp += wcount, wresid -= wcount) { wcount = write(to_fd, bufp, wresid); if (wcount <= 0) break; wtotal += wcount; if (info) { info = 0; (void)fprintf(stderr, "%s -> %s %3d%%\n", entp->fts_path, to.p_path, cp_pct(wtotal, fs->st_size)); } if (wcount >= (ssize_t)wresid) break; } if (wcount != (ssize_t)wresid) { warn("%s", to.p_path); rval = 1; break; } } if (rcount < 0) { warn("%s", entp->fts_path); rval = 1; } } } else { if (link(entp->fts_path, to.p_path)) { warn("%s", to.p_path); rval = 1; } } if (!lflag) { if (pflag && setfile(fs, to_fd)) rval = 1; if (pflag && preserve_fd_acls(from_fd, to_fd) != 0) rval = 1; if (close(to_fd)) { warn("%s", to.p_path); rval = 1; } } (void)close(from_fd); return (rval); }