static int fastcopy(char *from, char *to, struct stat *sbp) { #if defined(__NetBSD__) struct timespec ts[2]; #else struct timeval tval[2]; #endif static blksize_t blen; static char *bp; int nread, from_fd, to_fd; if ((from_fd = open(from, O_RDONLY, 0)) < 0) { warn("%s", from); return (1); } if ((to_fd = open(to, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) { warn("%s", to); (void)close(from_fd); return (1); } if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { warn(NULL); blen = 0; (void)close(from_fd); (void)close(to_fd); return (1); } while ((nread = read(from_fd, bp, blen)) > 0) if (write(to_fd, bp, 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); } if (fcpxattr(from_fd, to_fd) == -1) warn("%s: error copying extended attributes", to); (void)close(from_fd); #ifdef BSD4_4 #if defined(__NetBSD__) ts[0] = sbp->st_atimespec; ts[1] = sbp->st_mtimespec; #else TIMESPEC_TO_TIMEVAL(&tval[0], &sbp->st_atimespec); TIMESPEC_TO_TIMEVAL(&tval[1], &sbp->st_mtimespec); #endif #else tval[0].tv_sec = sbp->st_atime; tval[1].tv_sec = sbp->st_mtime; tval[0].tv_usec = 0; tval[1].tv_usec = 0; #endif #ifdef __SVR4 if (utimes(to, tval)) #else #if defined(__NetBSD__) if (futimens(to_fd, ts)) #else if (futimes(to_fd, tval)) #endif #endif warn("%s: set times", to); if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { if (errno != EPERM) warn("%s: set owner/group", to); sbp->st_mode &= ~(S_ISUID | S_ISGID); } if (fchmod(to_fd, sbp->st_mode)) warn("%s: set mode", to); if (fchflags(to_fd, sbp->st_flags) && (errno != EOPNOTSUPP)) warn("%s: set flags (was: 0%07o)", to, sbp->st_flags); 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(FTSENT *entp, int dne) { static char buf[MAXBSIZE]; struct stat to_stat, *fs; int ch, checkch, from_fd, rcount, rval, to_fd, tolnk, wcount; char *p; off_t ptotal = 0; if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { warn("%s", entp->fts_path); return (1); } to_fd = -1; fs = entp->fts_statp; tolnk = ((Rflag && !(Lflag || Hflag)) || Pflag); /* * 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) { struct stat sb; int sval; if (iflag) { (void)fprintf(stderr, "overwrite %s? ", to.p_path); checkch = ch = getchar(); while (ch != '\n' && ch != EOF) ch = getchar(); if (checkch != 'y' && checkch != 'Y') { (void)close(from_fd); return (0); } } sval = tolnk ? lstat(to.p_path, &sb) : stat(to.p_path, &sb); if (sval == -1) { warn("stat: %s", to.p_path); (void)close(from_fd); return (1); } if (!(tolnk && S_ISLNK(sb.st_mode))) to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); } else to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, fs->st_mode & ~(S_ISUID | S_ISGID)); if (to_fd == -1 && (fflag || tolnk)) { /* * attempt to remove existing destination file name and * create a new file */ (void)unlink(to.p_path); 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 hard linking then simply close the open fds, link and return */ if (lflag) { (void)close(from_fd); (void)close(to_fd); (void)unlink(to.p_path); if (link(entp->fts_path, to.p_path)) { warn("%s", to.p_path); return (1); } return (0); } /* * There's no reason to do anything other than close the file * now if it's empty, so let's not bother. */ if (fs->st_size > 0) { struct finfo fi; fi.from = entp->fts_path; fi.to = to.p_path; fi.size = fs->st_size; /* * 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. */ bool use_read; use_read = true; if (fs->st_size <= MMAP_MAX_SIZE) { size_t fsize = (size_t)fs->st_size; p = mmap(NULL, fsize, PROT_READ, MAP_FILE|MAP_SHARED, from_fd, (off_t)0); if (p != MAP_FAILED) { size_t remainder; use_read = false; (void) madvise(p, (size_t)fs->st_size, MADV_SEQUENTIAL); /* * Write out the data in small chunks to * avoid locking the output file for a * long time if the reading the data from * the source is slow. */ remainder = fsize; do { ssize_t chunk; chunk = (remainder > MMAP_MAX_WRITE) ? MMAP_MAX_WRITE : remainder; if (write(to_fd, &p[fsize - remainder], chunk) != chunk) { warn("%s", to.p_path); rval = 1; break; } remainder -= chunk; ptotal += chunk; if (pinfo) progress(&fi, ptotal); } while (remainder > 0); if (munmap(p, fsize) < 0) { warn("%s", entp->fts_path); rval = 1; } } } if (use_read) { while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { wcount = write(to_fd, buf, (size_t)rcount); if (rcount != wcount || wcount == -1) { warn("%s", to.p_path); rval = 1; break; } ptotal += wcount; if (pinfo) progress(&fi, ptotal); } if (rcount < 0) { warn("%s", entp->fts_path); rval = 1; } } } if (pflag && (fcpxattr(from_fd, to_fd) != 0)) warn("%s: error copying extended attributes", to.p_path); (void)close(from_fd); if (rval == 1) { (void)close(to_fd); return (1); } if (pflag && setfile(fs, to_fd)) rval = 1; /* * If the source was setuid or setgid, lose the bits unless the * copy is owned by the same user and group. */ #define RETAINBITS \ (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) if (!pflag && dne && fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) { if (fstat(to_fd, &to_stat)) { warn("%s", to.p_path); rval = 1; } else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) { warn("%s", to.p_path); rval = 1; } } if (close(to_fd)) { warn("%s", to.p_path); rval = 1; } /* set the mod/access times now after close of the fd */ if (pflag && set_utimes(to.p_path, fs)) { rval = 1; } return (rval); }