1*118f3e42Sjsg /* $OpenBSD: cp.c,v 1.11 2024/10/14 08:26:48 jsg Exp $ */ 2e580cfdfStedu /* $NetBSD: cp.c,v 1.14 1995/09/07 06:14:51 jtc Exp $ */ 3*118f3e42Sjsg /* $NetBSD: utils.c,v 1.6 1997/02/26 14:40:51 cgd Exp $ */ 4e580cfdfStedu 5e580cfdfStedu /* 6e580cfdfStedu * Copyright (c) 1988, 1993, 1994 7e580cfdfStedu * The Regents of the University of California. All rights reserved. 8e580cfdfStedu * 9e580cfdfStedu * This code is derived from software contributed to Berkeley by 10e580cfdfStedu * David Hitz of Auspex Systems Inc. 11e580cfdfStedu * 12e580cfdfStedu * Redistribution and use in source and binary forms, with or without 13e580cfdfStedu * modification, are permitted provided that the following conditions 14e580cfdfStedu * are met: 15e580cfdfStedu * 1. Redistributions of source code must retain the above copyright 16e580cfdfStedu * notice, this list of conditions and the following disclaimer. 17e580cfdfStedu * 2. Redistributions in binary form must reproduce the above copyright 18e580cfdfStedu * notice, this list of conditions and the following disclaimer in the 19e580cfdfStedu * documentation and/or other materials provided with the distribution. 20e580cfdfStedu * 3. Neither the name of the University nor the names of its contributors 21e580cfdfStedu * may be used to endorse or promote products derived from this software 22e580cfdfStedu * without specific prior written permission. 23e580cfdfStedu * 24e580cfdfStedu * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25e580cfdfStedu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26e580cfdfStedu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27e580cfdfStedu * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28e580cfdfStedu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29e580cfdfStedu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30e580cfdfStedu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31e580cfdfStedu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32e580cfdfStedu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33e580cfdfStedu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34e580cfdfStedu * SUCH DAMAGE. 35e580cfdfStedu */ 36e580cfdfStedu 37e580cfdfStedu /* 38e580cfdfStedu * Cp copies source files to target files. 39e580cfdfStedu * 40e580cfdfStedu * The global PATH_T structure "to" always contains the path to the 41e580cfdfStedu * current target file. Since fts(3) does not change directories, 42e580cfdfStedu * this path can be either absolute or dot-relative. 43e580cfdfStedu * 44e580cfdfStedu * The basic algorithm is to initialize "to" and use fts(3) to traverse 45e580cfdfStedu * the file hierarchy rooted in the argument list. A trivial case is the 46e580cfdfStedu * case of 'cp file1 file2'. The more interesting case is the case of 47e580cfdfStedu * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the 48e580cfdfStedu * path (relative to the root of the traversal) is appended to dir (stored 49e580cfdfStedu * in "to") to form the final target path. 50e580cfdfStedu */ 51e580cfdfStedu 52e580cfdfStedu #include <sys/types.h> 53e580cfdfStedu #include <sys/stat.h> 54e580cfdfStedu #include <sys/mman.h> 55e580cfdfStedu #include <sys/time.h> 56e580cfdfStedu 57e580cfdfStedu #include <dirent.h> 58e580cfdfStedu #include <err.h> 59e580cfdfStedu #include <errno.h> 60e580cfdfStedu #include <fcntl.h> 61e580cfdfStedu #include <fts.h> 62e580cfdfStedu #include <stdio.h> 63e580cfdfStedu #include <stdlib.h> 64e580cfdfStedu #include <string.h> 65e580cfdfStedu #include <unistd.h> 66e580cfdfStedu #include <limits.h> 67e580cfdfStedu 68e580cfdfStedu #define fts_dne(_x) (_x->fts_pointer != NULL) 69e580cfdfStedu 70e580cfdfStedu typedef struct { 71e580cfdfStedu char *p_end; /* pointer to NULL at end of path */ 72e580cfdfStedu char *target_end; /* pointer to end of target base */ 73e580cfdfStedu char p_path[PATH_MAX]; /* pointer to the start of a path */ 74e580cfdfStedu } PATH_T; 75e580cfdfStedu 76e580cfdfStedu static PATH_T to = { to.p_path, "" }; 77e580cfdfStedu 78e580cfdfStedu static int copy_fifo(struct stat *, int); 79e580cfdfStedu static int copy_file(FTSENT *, int); 80e580cfdfStedu static int copy_link(FTSENT *, int); 81e580cfdfStedu static int copy_special(struct stat *, int); 82e580cfdfStedu static int setfile(struct stat *, int); 83e580cfdfStedu 84e580cfdfStedu 85e580cfdfStedu extern char *__progname; 86e580cfdfStedu 87e580cfdfStedu static uid_t myuid; 886733f734Stedu static int fflag, iflag; 89e580cfdfStedu static mode_t myumask; 90e580cfdfStedu 91e580cfdfStedu enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; 92e580cfdfStedu 93e580cfdfStedu static int copy(char *[], enum op, int); 94e580cfdfStedu static char *find_last_component(char *); 95e580cfdfStedu 96e580cfdfStedu static void __dead 97e580cfdfStedu usage(void) 98e580cfdfStedu { 99e580cfdfStedu (void)fprintf(stderr, 100e580cfdfStedu "usage: %s [-fip] [-R [-H | -L | -P]] source target\n", __progname); 101e580cfdfStedu (void)fprintf(stderr, 102e580cfdfStedu " %s [-fip] [-R [-H | -L | -P]] source ... directory\n", 103e580cfdfStedu __progname); 104e580cfdfStedu exit(1); 105e580cfdfStedu } 106e580cfdfStedu 107e580cfdfStedu int 108e580cfdfStedu cpmain(int argc, char *argv[]) 109e580cfdfStedu { 110e580cfdfStedu struct stat to_stat, tmp_stat; 111e580cfdfStedu enum op type; 11265f8a631Stedu int fts_options, r; 113e580cfdfStedu char *target; 114e580cfdfStedu 115e580cfdfStedu fts_options = FTS_NOCHDIR | FTS_PHYSICAL; 116e580cfdfStedu 117e580cfdfStedu myuid = getuid(); 118e580cfdfStedu 119e580cfdfStedu /* Copy the umask for explicit mode setting. */ 120e580cfdfStedu myumask = umask(0); 121e580cfdfStedu (void)umask(myumask); 122e580cfdfStedu 123e580cfdfStedu /* Save the target base in "to". */ 124e580cfdfStedu target = argv[--argc]; 125e580cfdfStedu if (strlcpy(to.p_path, target, sizeof to.p_path) >= sizeof(to.p_path)) 126e580cfdfStedu errx(1, "%s: name too long", target); 127e580cfdfStedu to.p_end = to.p_path + strlen(to.p_path); 128e580cfdfStedu if (to.p_path == to.p_end) { 129e580cfdfStedu *to.p_end++ = '.'; 130e580cfdfStedu *to.p_end = '\0'; 131e580cfdfStedu } 132e580cfdfStedu to.target_end = to.p_end; 133e580cfdfStedu 134e580cfdfStedu /* Set end of argument list for fts(3). */ 135e580cfdfStedu argv[argc] = NULL; 136e580cfdfStedu 137e580cfdfStedu /* 138e580cfdfStedu * Cp has two distinct cases: 139e580cfdfStedu * 140e580cfdfStedu * cp [-R] source target 141e580cfdfStedu * cp [-R] source1 ... sourceN directory 142e580cfdfStedu * 143e580cfdfStedu * In both cases, source can be either a file or a directory. 144e580cfdfStedu * 145e580cfdfStedu * In (1), the target becomes a copy of the source. That is, if the 146e580cfdfStedu * source is a file, the target will be a file, and likewise for 147e580cfdfStedu * directories. 148e580cfdfStedu * 149e580cfdfStedu * In (2), the real target is not directory, but "directory/source". 150e580cfdfStedu */ 151e580cfdfStedu r = stat(to.p_path, &to_stat); 152e580cfdfStedu if (r == -1 && errno != ENOENT) 153e580cfdfStedu err(1, "%s", to.p_path); 154e580cfdfStedu if (r == -1 || !S_ISDIR(to_stat.st_mode)) { 155e580cfdfStedu /* 156e580cfdfStedu * Case (1). Target is not a directory. 157e580cfdfStedu */ 158e580cfdfStedu if (argc > 1) 159e580cfdfStedu usage(); 160e580cfdfStedu /* 161e580cfdfStedu * Need to detect the case: 162e580cfdfStedu * cp -R dir foo 163e580cfdfStedu * Where dir is a directory and foo does not exist, where 164e580cfdfStedu * we want pathname concatenations turned on but not for 165e580cfdfStedu * the initial mkdir(). 166e580cfdfStedu */ 167e580cfdfStedu if (r == -1) { 168e580cfdfStedu lstat(*argv, &tmp_stat); 169e580cfdfStedu 1706733f734Stedu if (S_ISDIR(tmp_stat.st_mode)) 171e580cfdfStedu type = DIR_TO_DNE; 172e580cfdfStedu else 173e580cfdfStedu type = FILE_TO_FILE; 174e580cfdfStedu } else 175e580cfdfStedu type = FILE_TO_FILE; 176e580cfdfStedu } else { 177e580cfdfStedu /* 178e580cfdfStedu * Case (2). Target is a directory. 179e580cfdfStedu */ 180e580cfdfStedu type = FILE_TO_DIR; 181e580cfdfStedu } 182e580cfdfStedu 183e580cfdfStedu return (copy(argv, type, fts_options)); 184e580cfdfStedu } 185e580cfdfStedu 186e580cfdfStedu static char * 187e580cfdfStedu find_last_component(char *path) 188e580cfdfStedu { 189e580cfdfStedu char *p; 190e580cfdfStedu 191e580cfdfStedu if ((p = strrchr(path, '/')) == NULL) 192e580cfdfStedu p = path; 193e580cfdfStedu else { 194e580cfdfStedu /* Special case foo/ */ 195e580cfdfStedu if (!*(p+1)) { 196e580cfdfStedu while ((p >= path) && *p == '/') 197e580cfdfStedu p--; 198e580cfdfStedu 199e580cfdfStedu while ((p >= path) && *p != '/') 200e580cfdfStedu p--; 201e580cfdfStedu } 202e580cfdfStedu 203e580cfdfStedu p++; 204e580cfdfStedu } 205e580cfdfStedu 206e580cfdfStedu return (p); 207e580cfdfStedu } 208e580cfdfStedu 209e580cfdfStedu static int 210e580cfdfStedu copy(char *argv[], enum op type, int fts_options) 211e580cfdfStedu { 212e580cfdfStedu struct stat to_stat; 213e580cfdfStedu FTS *ftsp; 214e580cfdfStedu FTSENT *curr; 215e580cfdfStedu int base, nlen, rval; 216e580cfdfStedu char *p, *target_mid; 217e580cfdfStedu base = 0; 218e580cfdfStedu 219e580cfdfStedu if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL) 220e580cfdfStedu err(1, NULL); 221e580cfdfStedu for (rval = 0; (curr = fts_read(ftsp)) != NULL;) { 222e580cfdfStedu switch (curr->fts_info) { 223e580cfdfStedu case FTS_NS: 224e580cfdfStedu case FTS_DNR: 225e580cfdfStedu case FTS_ERR: 226e580cfdfStedu warnx("%s: %s", 227e580cfdfStedu curr->fts_path, strerror(curr->fts_errno)); 228e580cfdfStedu rval = 1; 229e580cfdfStedu continue; 230e580cfdfStedu case FTS_DC: 231e580cfdfStedu warnx("%s: directory causes a cycle", curr->fts_path); 232e580cfdfStedu rval = 1; 233e580cfdfStedu continue; 234e580cfdfStedu } 235e580cfdfStedu 236e580cfdfStedu /* 237e580cfdfStedu * If we are in case (2) or (3) above, we need to append the 238e580cfdfStedu * source name to the target name. 239e580cfdfStedu */ 240e580cfdfStedu if (type != FILE_TO_FILE) { 241e580cfdfStedu /* 242e580cfdfStedu * Need to remember the roots of traversals to create 243e580cfdfStedu * correct pathnames. If there's a directory being 244e580cfdfStedu * copied to a non-existent directory, e.g. 245e580cfdfStedu * cp -R a/dir noexist 246e580cfdfStedu * the resulting path name should be noexist/foo, not 247e580cfdfStedu * noexist/dir/foo (where foo is a file in dir), which 248e580cfdfStedu * is the case where the target exists. 249e580cfdfStedu * 250e580cfdfStedu * Also, check for "..". This is for correct path 251e580cfdfStedu * concatenation for paths ending in "..", e.g. 252e580cfdfStedu * cp -R .. /tmp 253e580cfdfStedu * Paths ending in ".." are changed to ".". This is 254e580cfdfStedu * tricky, but seems the easiest way to fix the problem. 255e580cfdfStedu * 256e580cfdfStedu * XXX 257e580cfdfStedu * Since the first level MUST be FTS_ROOTLEVEL, base 258e580cfdfStedu * is always initialized. 259e580cfdfStedu */ 260e580cfdfStedu if (curr->fts_level == FTS_ROOTLEVEL) { 261e580cfdfStedu if (type != DIR_TO_DNE) { 262e580cfdfStedu p = find_last_component(curr->fts_path); 263e580cfdfStedu base = p - curr->fts_path; 264e580cfdfStedu 265e580cfdfStedu if (!strcmp(&curr->fts_path[base], 266e580cfdfStedu "..")) 267e580cfdfStedu base += 1; 268e580cfdfStedu } else 269e580cfdfStedu base = curr->fts_pathlen; 270e580cfdfStedu } 271e580cfdfStedu 272e580cfdfStedu p = &curr->fts_path[base]; 273e580cfdfStedu nlen = curr->fts_pathlen - base; 274e580cfdfStedu target_mid = to.target_end; 275e580cfdfStedu if (*p != '/' && target_mid[-1] != '/') 276e580cfdfStedu *target_mid++ = '/'; 277e580cfdfStedu *target_mid = '\0'; 278e580cfdfStedu if (target_mid - to.p_path + nlen >= PATH_MAX) { 279e580cfdfStedu warnx("%s%s: name too long (not copied)", 280e580cfdfStedu to.p_path, p); 281e580cfdfStedu rval = 1; 282e580cfdfStedu continue; 283e580cfdfStedu } 284e580cfdfStedu (void)strncat(target_mid, p, nlen); 285e580cfdfStedu to.p_end = target_mid + nlen; 286e580cfdfStedu *to.p_end = '\0'; 287e580cfdfStedu } 288e580cfdfStedu 289e580cfdfStedu /* Not an error but need to remember it happened */ 290e580cfdfStedu if (stat(to.p_path, &to_stat) == -1) { 291e580cfdfStedu if (curr->fts_info == FTS_DP) 292e580cfdfStedu continue; 293e580cfdfStedu /* 294e580cfdfStedu * We use fts_pointer as a boolean to indicate that 295e580cfdfStedu * we created this directory ourselves. We'll use 296e580cfdfStedu * this later on via the fts_dne macro to decide 297e580cfdfStedu * whether or not to set the directory mode during 298e580cfdfStedu * the post-order pass. 299e580cfdfStedu */ 300e580cfdfStedu curr->fts_pointer = (void *)1; 301e580cfdfStedu } else { 302e580cfdfStedu /* 303e580cfdfStedu * Set directory mode/user/times on the post-order 304e580cfdfStedu * pass. We can't do this earlier because the mode 305e580cfdfStedu * may not allow us write permission. Furthermore, 306e580cfdfStedu * if we set the times during the pre-order pass, 307e580cfdfStedu * they will get changed later when the directory 308e580cfdfStedu * is populated. 309e580cfdfStedu */ 310e580cfdfStedu if (curr->fts_info == FTS_DP) { 311e580cfdfStedu if (!S_ISDIR(to_stat.st_mode)) 312e580cfdfStedu continue; 313e580cfdfStedu /* 314e580cfdfStedu * If not -p and directory didn't exist, set 315e580cfdfStedu * it to be the same as the from directory, 316e580cfdfStedu * unmodified by the umask; arguably wrong, 317e580cfdfStedu * but it's been that way forever. 318e580cfdfStedu */ 3196733f734Stedu if (setfile(curr->fts_statp, -1)) 320e580cfdfStedu rval = 1; 321e580cfdfStedu else if (fts_dne(curr)) 322e580cfdfStedu (void)chmod(to.p_path, 323e580cfdfStedu curr->fts_statp->st_mode); 324e580cfdfStedu continue; 325e580cfdfStedu } 326e580cfdfStedu if (to_stat.st_dev == curr->fts_statp->st_dev && 327e580cfdfStedu to_stat.st_ino == curr->fts_statp->st_ino) { 328e580cfdfStedu warnx("%s and %s are identical (not copied).", 329e580cfdfStedu to.p_path, curr->fts_path); 330e580cfdfStedu rval = 1; 331e580cfdfStedu if (S_ISDIR(curr->fts_statp->st_mode)) 332e580cfdfStedu (void)fts_set(ftsp, curr, FTS_SKIP); 333e580cfdfStedu continue; 334e580cfdfStedu } 335e580cfdfStedu if (!S_ISDIR(curr->fts_statp->st_mode) && 336e580cfdfStedu S_ISDIR(to_stat.st_mode)) { 337e580cfdfStedu warnx("cannot overwrite directory %s with non-directory %s", 338e580cfdfStedu to.p_path, curr->fts_path); 339e580cfdfStedu rval = 1; 340e580cfdfStedu continue; 341e580cfdfStedu } 342e580cfdfStedu } 343e580cfdfStedu 344e580cfdfStedu switch (curr->fts_statp->st_mode & S_IFMT) { 345e580cfdfStedu case S_IFLNK: 346e580cfdfStedu if (copy_link(curr, !fts_dne(curr))) 347e580cfdfStedu rval = 1; 348e580cfdfStedu break; 349e580cfdfStedu case S_IFDIR: 350e580cfdfStedu /* 351e580cfdfStedu * If the directory doesn't exist, create the new 352e580cfdfStedu * one with the from file mode plus owner RWX bits, 353e580cfdfStedu * modified by the umask. Trade-off between being 354e580cfdfStedu * able to write the directory (if from directory is 355e580cfdfStedu * 555) and not causing a permissions race. If the 356e580cfdfStedu * umask blocks owner writes, we fail.. 357e580cfdfStedu */ 358e580cfdfStedu if (fts_dne(curr)) { 359e580cfdfStedu if (mkdir(to.p_path, 3603aaa63ebSderaadt curr->fts_statp->st_mode | S_IRWXU) == -1) 361e580cfdfStedu err(1, "%s", to.p_path); 362e580cfdfStedu } else if (!S_ISDIR(to_stat.st_mode)) 363e580cfdfStedu errc(1, ENOTDIR, "%s", to.p_path); 364e580cfdfStedu break; 365e580cfdfStedu case S_IFBLK: 366e580cfdfStedu case S_IFCHR: 367e580cfdfStedu if (copy_special(curr->fts_statp, !fts_dne(curr))) 368e580cfdfStedu rval = 1; 369e580cfdfStedu break; 370e580cfdfStedu case S_IFIFO: 371e580cfdfStedu if (copy_fifo(curr->fts_statp, !fts_dne(curr))) 372e580cfdfStedu rval = 1; 373e580cfdfStedu break; 374e580cfdfStedu case S_IFSOCK: 375e580cfdfStedu warnc(EOPNOTSUPP, "%s", curr->fts_path); 376e580cfdfStedu break; 377e580cfdfStedu default: 378e580cfdfStedu if (copy_file(curr, fts_dne(curr))) 379e580cfdfStedu rval = 1; 380e580cfdfStedu break; 381e580cfdfStedu } 382e580cfdfStedu } 383e580cfdfStedu if (errno) 384e580cfdfStedu err(1, "fts_read"); 385e580cfdfStedu (void)fts_close(ftsp); 386e580cfdfStedu return (rval); 387e580cfdfStedu } 388e580cfdfStedu 3891a0afcdeSderaadt #define _MAXBSIZE (64 * 1024) 3901a0afcdeSderaadt 391e580cfdfStedu static int 392e580cfdfStedu copy_file(FTSENT *entp, int dne) 393e580cfdfStedu { 394e580cfdfStedu static char *buf; 395e580cfdfStedu static char *zeroes; 396c642ab92Schl struct stat *fs; 397e580cfdfStedu int ch, checkch, from_fd, rcount, rval, to_fd, wcount; 3981a0afcdeSderaadt const size_t buflen = _MAXBSIZE; 399e580cfdfStedu #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 400e580cfdfStedu char *p; 401e580cfdfStedu #endif 402e580cfdfStedu 403e580cfdfStedu if (!buf) { 4041a0afcdeSderaadt buf = malloc(buflen); 405e580cfdfStedu if (!buf) 406e580cfdfStedu err(1, "malloc"); 407e580cfdfStedu } 408e580cfdfStedu if (!zeroes) { 4091a0afcdeSderaadt zeroes = calloc(1, buflen); 410e580cfdfStedu if (!zeroes) 411e580cfdfStedu err(1, "calloc"); 412e580cfdfStedu } 413e580cfdfStedu 414b7041c07Sderaadt if ((from_fd = open(entp->fts_path, O_RDONLY)) == -1) { 415e580cfdfStedu warn("%s", entp->fts_path); 416e580cfdfStedu return (1); 417e580cfdfStedu } 418e580cfdfStedu 419e580cfdfStedu fs = entp->fts_statp; 420e580cfdfStedu 421e580cfdfStedu /* 422e580cfdfStedu * In -f (force) mode, we always unlink the destination first 423e580cfdfStedu * if it exists. Note that -i and -f are mutually exclusive. 424e580cfdfStedu */ 425e580cfdfStedu if (!dne && fflag) 426e580cfdfStedu (void)unlink(to.p_path); 427e580cfdfStedu 428e580cfdfStedu /* 429e580cfdfStedu * If the file exists and we're interactive, verify with the user. 430e580cfdfStedu * If the file DNE, set the mode to be the from file, minus setuid 431e580cfdfStedu * bits, modified by the umask; arguably wrong, but it makes copying 432e580cfdfStedu * executables work right and it's been that way forever. (The 433e580cfdfStedu * other choice is 666 or'ed with the execute bits on the from file 434e580cfdfStedu * modified by the umask.) 435e580cfdfStedu */ 436e580cfdfStedu if (!dne && !fflag) { 437e580cfdfStedu if (iflag) { 438e580cfdfStedu (void)fprintf(stderr, "overwrite %s? ", to.p_path); 439e580cfdfStedu checkch = ch = getchar(); 440e580cfdfStedu while (ch != '\n' && ch != EOF) 441e580cfdfStedu ch = getchar(); 442e580cfdfStedu if (checkch != 'y' && checkch != 'Y') { 443e580cfdfStedu (void)close(from_fd); 444e580cfdfStedu return (0); 445e580cfdfStedu } 446e580cfdfStedu } 447b7041c07Sderaadt to_fd = open(to.p_path, O_WRONLY | O_TRUNC); 448e580cfdfStedu } else 449e580cfdfStedu to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 450e580cfdfStedu fs->st_mode & ~(S_ISTXT | S_ISUID | S_ISGID)); 451e580cfdfStedu 452e580cfdfStedu if (to_fd == -1) { 453e580cfdfStedu warn("%s", to.p_path); 454e580cfdfStedu (void)close(from_fd); 455e580cfdfStedu return (1); 456e580cfdfStedu } 457e580cfdfStedu 458e580cfdfStedu rval = 0; 459e580cfdfStedu 460e580cfdfStedu /* 461e580cfdfStedu * Mmap and write if less than 8M (the limit is so we don't totally 462e580cfdfStedu * trash memory on big files. This is really a minor hack, but it 463e580cfdfStedu * wins some CPU back. 464e580cfdfStedu */ 465e580cfdfStedu #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 466e580cfdfStedu /* XXX broken for 0-size mmap */ 467e580cfdfStedu if (fs->st_size <= 8 * 1048576) { 468e580cfdfStedu if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, 469e580cfdfStedu MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) { 470e580cfdfStedu warn("mmap: %s", entp->fts_path); 471e580cfdfStedu rval = 1; 472e580cfdfStedu } else { 473e580cfdfStedu madvise(p, fs->st_size, MADV_SEQUENTIAL); 474e580cfdfStedu if (write(to_fd, p, fs->st_size) != fs->st_size) { 475e580cfdfStedu warn("%s", to.p_path); 476e580cfdfStedu rval = 1; 477e580cfdfStedu } 478e580cfdfStedu /* Some systems don't unmap on close(2). */ 4793aaa63ebSderaadt if (munmap(p, fs->st_size) == -1) { 480e580cfdfStedu warn("%s", entp->fts_path); 481e580cfdfStedu rval = 1; 482e580cfdfStedu } 483e580cfdfStedu } 484e580cfdfStedu } else 485e580cfdfStedu #endif 486e580cfdfStedu { 487e580cfdfStedu int skipholes = 0; 488e580cfdfStedu struct stat tosb; 489e580cfdfStedu if (!fstat(to_fd, &tosb) && S_ISREG(tosb.st_mode)) 490e580cfdfStedu skipholes = 1; 4911a0afcdeSderaadt while ((rcount = read(from_fd, buf, buflen)) > 0) { 492e580cfdfStedu if (skipholes && memcmp(buf, zeroes, rcount) == 0) 493e580cfdfStedu wcount = lseek(to_fd, rcount, SEEK_CUR) == -1 ? -1 : rcount; 494e580cfdfStedu else 495e580cfdfStedu wcount = write(to_fd, buf, rcount); 496e580cfdfStedu if (rcount != wcount || wcount == -1) { 497e580cfdfStedu warn("%s", to.p_path); 498e580cfdfStedu rval = 1; 499e580cfdfStedu break; 500e580cfdfStedu } 501e580cfdfStedu } 502e580cfdfStedu if (skipholes && rcount >= 0) 503e580cfdfStedu rcount = ftruncate(to_fd, lseek(to_fd, 0, SEEK_CUR)); 5043aaa63ebSderaadt if (rcount == -1) { 505e580cfdfStedu warn("%s", entp->fts_path); 506e580cfdfStedu rval = 1; 507e580cfdfStedu } 508e580cfdfStedu } 509e580cfdfStedu 510e580cfdfStedu if (rval == 1) { 511e580cfdfStedu (void)close(from_fd); 512e580cfdfStedu (void)close(to_fd); 513e580cfdfStedu return (1); 514e580cfdfStedu } 515e580cfdfStedu 5166733f734Stedu if (setfile(fs, to_fd)) 517e580cfdfStedu rval = 1; 518e580cfdfStedu (void)close(from_fd); 519e580cfdfStedu if (close(to_fd)) { 520e580cfdfStedu warn("%s", to.p_path); 521e580cfdfStedu rval = 1; 522e580cfdfStedu } 523e580cfdfStedu return (rval); 524e580cfdfStedu } 525e580cfdfStedu 526e580cfdfStedu static int 527e580cfdfStedu copy_link(FTSENT *p, int exists) 528e580cfdfStedu { 529e580cfdfStedu int len; 530e580cfdfStedu char linkname[PATH_MAX]; 531e580cfdfStedu 532e580cfdfStedu if ((len = readlink(p->fts_path, linkname, sizeof(linkname)-1)) == -1) { 533e580cfdfStedu warn("readlink: %s", p->fts_path); 534e580cfdfStedu return (1); 535e580cfdfStedu } 536e580cfdfStedu linkname[len] = '\0'; 537e580cfdfStedu if (exists && unlink(to.p_path)) { 538e580cfdfStedu warn("unlink: %s", to.p_path); 539e580cfdfStedu return (1); 540e580cfdfStedu } 541e580cfdfStedu if (symlink(linkname, to.p_path)) { 542e580cfdfStedu warn("symlink: %s", linkname); 543e580cfdfStedu return (1); 544e580cfdfStedu } 545595d7346Sguenther return (setfile(p->fts_statp, -1)); 546e580cfdfStedu } 547e580cfdfStedu 548e580cfdfStedu static int 549e580cfdfStedu copy_fifo(struct stat *from_stat, int exists) 550e580cfdfStedu { 551e580cfdfStedu if (exists && unlink(to.p_path)) { 552e580cfdfStedu warn("unlink: %s", to.p_path); 553e580cfdfStedu return (1); 554e580cfdfStedu } 555e580cfdfStedu if (mkfifo(to.p_path, from_stat->st_mode)) { 556e580cfdfStedu warn("mkfifo: %s", to.p_path); 557e580cfdfStedu return (1); 558e580cfdfStedu } 5596733f734Stedu return (setfile(from_stat, -1)); 560e580cfdfStedu } 561e580cfdfStedu 562e580cfdfStedu static int 563e580cfdfStedu copy_special(struct stat *from_stat, int exists) 564e580cfdfStedu { 565e580cfdfStedu if (exists && unlink(to.p_path)) { 566e580cfdfStedu warn("unlink: %s", to.p_path); 567e580cfdfStedu return (1); 568e580cfdfStedu } 569e580cfdfStedu if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 570e580cfdfStedu warn("mknod: %s", to.p_path); 571e580cfdfStedu return (1); 572e580cfdfStedu } 5736733f734Stedu return (setfile(from_stat, -1)); 574e580cfdfStedu } 575e580cfdfStedu 576e580cfdfStedu 577e580cfdfStedu static int 578e580cfdfStedu setfile(struct stat *fs, int fd) 579e580cfdfStedu { 580e580cfdfStedu struct timespec ts[2]; 581e580cfdfStedu int rval; 582e580cfdfStedu 583e580cfdfStedu rval = 0; 584e580cfdfStedu fs->st_mode &= S_ISTXT | S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; 585e580cfdfStedu 586e580cfdfStedu ts[0] = fs->st_atim; 587e580cfdfStedu ts[1] = fs->st_mtim; 588e580cfdfStedu if (fd >= 0 ? futimens(fd, ts) : 589e580cfdfStedu utimensat(AT_FDCWD, to.p_path, ts, AT_SYMLINK_NOFOLLOW)) { 590e580cfdfStedu warn("update times: %s", to.p_path); 591e580cfdfStedu rval = 1; 592e580cfdfStedu } 593e580cfdfStedu /* 594e580cfdfStedu * Changing the ownership probably won't succeed, unless we're root 595e580cfdfStedu * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 596e580cfdfStedu * the mode; current BSD behavior is to remove all setuid bits on 597e580cfdfStedu * chown. If chown fails, lose setuid/setgid bits. 598e580cfdfStedu */ 599e580cfdfStedu if (fd >= 0 ? fchown(fd, fs->st_uid, fs->st_gid) : 600e580cfdfStedu lchown(to.p_path, fs->st_uid, fs->st_gid)) { 601e580cfdfStedu if (errno != EPERM) { 602e580cfdfStedu warn("chown: %s", to.p_path); 603e580cfdfStedu rval = 1; 604e580cfdfStedu } 605e580cfdfStedu fs->st_mode &= ~(S_ISTXT | S_ISUID | S_ISGID); 606e580cfdfStedu } 607e580cfdfStedu if (fd >= 0 ? fchmod(fd, fs->st_mode) : 608e580cfdfStedu fchmodat(AT_FDCWD, to.p_path, fs->st_mode, AT_SYMLINK_NOFOLLOW)) { 609e580cfdfStedu warn("chmod: %s", to.p_path); 610e580cfdfStedu rval = 1; 611e580cfdfStedu } 612e580cfdfStedu 613e580cfdfStedu /* 614e580cfdfStedu * XXX 615e580cfdfStedu * NFS doesn't support chflags; ignore errors unless there's reason 616e580cfdfStedu * to believe we're losing bits. (Note, this still won't be right 617e580cfdfStedu * if the server supports flags and we were trying to *remove* flags 618e580cfdfStedu * on a file that we copied, i.e., that we didn't create.) 619e580cfdfStedu */ 620e580cfdfStedu errno = 0; 621e580cfdfStedu if (fd >= 0 ? fchflags(fd, fs->st_flags) : 622e580cfdfStedu chflagsat(AT_FDCWD, to.p_path, fs->st_flags, AT_SYMLINK_NOFOLLOW)) 623e580cfdfStedu if (errno != EOPNOTSUPP || fs->st_flags != 0) { 624e580cfdfStedu warn("chflags: %s", to.p_path); 625e580cfdfStedu rval = 1; 626e580cfdfStedu } 627e580cfdfStedu return (rval); 628e580cfdfStedu } 629