1/*
2 * Copyright (c) 2010, Frank Lahm <franklahm@googlemail.com>
3 * Copyright (c) 1988, 1993, 1994
4 * The Regents of the University of California.  All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * David Hitz of Auspex Systems Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34/*
35 * Cp copies source files to target files.
36 *
37 * The global PATH_T structure "to" always contains the path to the
38 * current target file.  Since fts(3) does not change directories,
39 * this path can be either absolute or dot-relative.
40 *
41 * The basic algorithm is to initialize "to" and use fts(3) to traverse
42 * the file hierarchy rooted in the argument list.  A trivial case is the
43 * case of 'cp file1 file2'.  The more interesting case is the case of
44 * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
45 * path (relative to the root of the traversal) is appended to dir (stored
46 * in "to") to form the final target path.
47 */
48
49#ifdef HAVE_CONFIG_H
50#include "config.h"
51#endif /* HAVE_CONFIG_H */
52
53#include <sys/types.h>
54#include <sys/stat.h>
55#include <errno.h>
56#include <limits.h>
57#include <signal.h>
58#include <stdio.h>
59#include <stdlib.h>
60#include <string.h>
61#include <unistd.h>
62#include <libgen.h>
63
64#include <atalk/ftw.h>
65#include <atalk/adouble.h>
66#include <atalk/vfs.h>
67#include <atalk/util.h>
68#include <atalk/unix.h>
69#include <atalk/volume.h>
70#include <atalk/volinfo.h>
71#include <atalk/bstrlib.h>
72#include <atalk/bstradd.h>
73#include <atalk/queue.h>
74
75#include "ad.h"
76
77#define STRIP_TRAILING_SLASH(p) {                                   \
78        while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')  \
79            *--(p).p_end = 0;                                       \
80    }
81
82static char emptystring[] = "";
83
84PATH_T to = { to.p_path, emptystring, "" };
85enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
86
87int fflag, iflag, nflag, pflag, vflag;
88mode_t mask;
89
90cnid_t ppdid, pdid, did; /* current dir CNID and parent did*/
91
92static afpvol_t svolume, dvolume;
93static enum op type;
94static int Rflag;
95static volatile sig_atomic_t alarmed;
96static int badcp, rval;
97static int ftw_options = FTW_MOUNT | FTW_PHYS | FTW_ACTIONRETVAL;
98
99static char           *netatalk_dirs[] = {
100    ".AppleDouble",
101    ".AppleDB",
102    ".AppleDesktop",
103    NULL
104};
105
106/* Forward declarations */
107static int copy(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
108static int ftw_copy_file(const struct FTW *, const char *, const struct stat *, int);
109static int ftw_copy_link(const struct FTW *, const char *, const struct stat *, int);
110static int setfile(const struct stat *, int);
111static int preserve_dir_acls(const struct stat *, char *, char *);
112static int preserve_fd_acls(int, int);
113
114/*
115  Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
116  Returns pointer to name or NULL.
117*/
118static const char *check_netatalk_dirs(const char *name)
119{
120    int c;
121
122    for (c=0; netatalk_dirs[c]; c++) {
123        if ((strcmp(name, netatalk_dirs[c])) == 0)
124            return netatalk_dirs[c];
125    }
126    return NULL;
127}
128
129static void upfunc(void)
130{
131    did = pdid;
132    pdid = ppdid;
133}
134
135/*
136  SIGNAL handling:
137  catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
138*/
139
140static void sig_handler(int signo)
141{
142    alarmed = 1;
143    return;
144}
145
146static void set_signal(void)
147{
148    struct sigaction sv;
149
150    sv.sa_handler = sig_handler;
151    sv.sa_flags = SA_RESTART;
152    sigemptyset(&sv.sa_mask);
153    if (sigaction(SIGTERM, &sv, NULL) < 0)
154        ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
155
156    if (sigaction(SIGINT, &sv, NULL) < 0)
157        ERROR("error in sigaction(SIGINT): %s", strerror(errno));
158
159    memset(&sv, 0, sizeof(struct sigaction));
160    sv.sa_handler = SIG_IGN;
161    sigemptyset(&sv.sa_mask);
162
163    if (sigaction(SIGABRT, &sv, NULL) < 0)
164        ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
165
166    if (sigaction(SIGHUP, &sv, NULL) < 0)
167        ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
168
169    if (sigaction(SIGQUIT, &sv, NULL) < 0)
170        ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
171}
172
173static void usage_cp(void)
174{
175    printf(
176        "Usage: ad cp [-R] [-aipvf] <source_file> <target_file>\n"
177        "       ad cp [-R] [-aipvfx] <source_file [source_file ...]> <target_directory>\n\n"
178        "In the first synopsis form, the cp utility copies the contents of the source_file to the\n"
179        "target_file.  In the second synopsis form, the contents of each named source_file is copied to the\n"
180        "destination target_directory.  The names of the files themselves are not changed.  If cp detects an\n"
181        "attempt to copy a file to itself, the copy will fail.\n\n"
182        "Netatalk AFP volumes are detected by means of their \".AppleDesktop\" directory\n"
183        "which is located in their volume root. When a copy targetting an AFP volume\n"
184        "is detected, its CNID database daemon is connected and all copies will also\n"
185        "go through the CNID database.\n"
186        "AppleDouble files are also copied and created as needed when the target is\n"
187        "an AFP volume.\n\n"
188        "The following options are available:\n\n"
189        "     -a    Archive mode.  Same as -Rp.\n\n"
190        "     -f    For each existing destination pathname, remove it and create a new\n"
191        "           file, without prompting for confirmation regardless of its permis-\n"
192        "           sions.  (The -f option overrides any previous -i or -n options.)\n\n"
193        "     -i    Cause cp to write a prompt to the standard error output before\n"
194        "           copying a file that would overwrite an existing file.  If the\n"
195        "           response from the standard input begins with the character 'y' or\n"
196        "           'Y', the file copy is attempted.  (The -i option overrides any pre-\n"
197        "           vious -f or -n options.)\n\n"
198        "     -n    Do not overwrite an existing file.  (The -n option overrides any\n"
199        "           previous -f or -i options.)\n\n"
200        "     -p    Cause cp to preserve the following attributes of each source file\n"
201        "           in the copy: modification time, access time, file flags, file mode,\n"
202        "           user ID, and group ID, as allowed by permissions.\n"
203        "           If the user ID and group ID cannot be preserved, no error message\n"
204        "           is displayed and the exit value is not altered.\n\n"
205        "     -R    If source_file designates a directory, cp copies the directory and\n"
206        "           the entire subtree connected at that point.If the source_file\n"
207        "           ends in a /, the contents of the directory are copied rather than\n"
208        "           the directory itself.\n\n"
209        "     -v    Cause cp to be verbose, showing files as they are copied.\n\n"
210        "     -x    File system mount points are not traversed.\n\n"
211        );
212    exit(EXIT_FAILURE);
213}
214
215int ad_cp(int argc, char *argv[])
216{
217    struct stat to_stat, tmp_stat;
218    int r, ch, have_trailing_slash;
219    char *target;
220#if 0
221    afpvol_t srcvol;
222    afpvol_t dstvol;
223#endif
224
225    ppdid = pdid = htonl(1);
226    did = htonl(2);
227
228    while ((ch = getopt(argc, argv, "afinpRvx")) != -1)
229        switch (ch) {
230        case 'a':
231            pflag = 1;
232            Rflag = 1;
233            break;
234        case 'f':
235            fflag = 1;
236            iflag = nflag = 0;
237            break;
238        case 'i':
239            iflag = 1;
240            fflag = nflag = 0;
241            break;
242        case 'n':
243            nflag = 1;
244            fflag = iflag = 0;
245            break;
246        case 'p':
247            pflag = 1;
248            break;
249        case 'R':
250            Rflag = 1;
251            break;
252        case 'v':
253            vflag = 1;
254            break;
255        case 'x':
256            ftw_options |= FTW_MOUNT;
257            break;
258        default:
259            usage_cp();
260            break;
261        }
262    argc -= optind;
263    argv += optind;
264
265    if (argc < 2)
266        usage_cp();
267
268    set_signal();
269    cnid_init();
270
271    /* Save the target base in "to". */
272    target = argv[--argc];
273    if ((strlcpy(to.p_path, target, PATH_MAX)) >= PATH_MAX)
274        ERROR("%s: name too long", target);
275
276    to.p_end = to.p_path + strlen(to.p_path);
277    if (to.p_path == to.p_end) {
278        *to.p_end++ = '.';
279        *to.p_end = 0;
280    }
281    have_trailing_slash = (to.p_end[-1] == '/');
282    if (have_trailing_slash)
283        STRIP_TRAILING_SLASH(to);
284    to.target_end = to.p_end;
285
286    /* Set end of argument list */
287    argv[argc] = NULL;
288
289    /*
290     * Cp has two distinct cases:
291     *
292     * cp [-R] source target
293     * cp [-R] source1 ... sourceN directory
294     *
295     * In both cases, source can be either a file or a directory.
296     *
297     * In (1), the target becomes a copy of the source. That is, if the
298     * source is a file, the target will be a file, and likewise for
299     * directories.
300     *
301     * In (2), the real target is not directory, but "directory/source".
302     */
303    r = stat(to.p_path, &to_stat);
304    if (r == -1 && errno != ENOENT)
305        ERROR("%s", to.p_path);
306    if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
307        /*
308         * Case (1).  Target is not a directory.
309         */
310        if (argc > 1)
311            ERROR("%s is not a directory", to.p_path);
312
313        /*
314         * Need to detect the case:
315         *cp -R dir foo
316         * Where dir is a directory and foo does not exist, where
317         * we want pathname concatenations turned on but not for
318         * the initial mkdir().
319         */
320        if (r == -1) {
321            lstat(*argv, &tmp_stat);
322
323            if (S_ISDIR(tmp_stat.st_mode) && Rflag)
324                type = DIR_TO_DNE;
325            else
326                type = FILE_TO_FILE;
327        } else
328            type = FILE_TO_FILE;
329
330        if (have_trailing_slash && type == FILE_TO_FILE) {
331            if (r == -1)
332                ERROR("directory %s does not exist", to.p_path);
333            else
334                ERROR("%s is not a directory", to.p_path);
335        }
336    } else
337        /*
338         * Case (2).  Target is a directory.
339         */
340        type = FILE_TO_DIR;
341
342    /*
343     * Keep an inverted copy of the umask, for use in correcting
344     * permissions on created directories when not using -p.
345     */
346    mask = ~umask(0777);
347    umask(~mask);
348
349#if 0
350    /* Inhereting perms in ad_mkdir etc requires this */
351    ad_setfuid(0);
352#endif
353
354    /* Load .volinfo file for destination*/
355    openvol(to.p_path, &dvolume);
356
357    for (int i = 0; argv[i] != NULL; i++) {
358        /* Load .volinfo file for source */
359        openvol(argv[i], &svolume);
360
361        if (nftw(argv[i], copy, upfunc, 20, ftw_options) == -1) {
362            if (alarmed) {
363                SLOG("...break");
364            } else {
365                SLOG("Error: %s: %s", argv[i], strerror(errno));
366            }
367            closevol(&svolume);
368            closevol(&dvolume);
369        }
370    }
371    return rval;
372}
373
374static int copy(const char *path,
375                const struct stat *statp,
376                int tflag,
377                struct FTW *ftw)
378{
379    static int base = 0;
380
381    struct stat to_stat;
382    int dne;
383    size_t nlen;
384    const char *p;
385    char *target_mid;
386
387    if (alarmed)
388        return -1;
389
390    /* This currently doesn't work with "." */
391    if (strcmp(path, ".") == 0) {
392        ERROR("\".\" not supported");
393    }
394    const char *dir = strrchr(path, '/');
395    if (dir == NULL)
396        dir = path;
397    else
398        dir++;
399    if (check_netatalk_dirs(dir) != NULL)
400        return FTW_SKIP_SUBTREE;
401
402    /*
403     * If we are in case (2) above, we need to append the
404     * source name to the target name.
405     */
406    if (type != FILE_TO_FILE) {
407        /*
408         * Need to remember the roots of traversals to create
409         * correct pathnames.  If there's a directory being
410         * copied to a non-existent directory, e.g.
411         *     cp -R a/dir noexist
412         * the resulting path name should be noexist/foo, not
413         * noexist/dir/foo (where foo is a file in dir), which
414         * is the case where the target exists.
415         *
416         * Also, check for "..".  This is for correct path
417         * concatenation for paths ending in "..", e.g.
418         *     cp -R .. /tmp
419         * Paths ending in ".." are changed to ".".  This is
420         * tricky, but seems the easiest way to fix the problem.
421         *
422         * XXX
423         * Since the first level MUST be FTS_ROOTLEVEL, base
424         * is always initialized.
425         */
426        if (ftw->level == 0) {
427            if (type != DIR_TO_DNE) {
428                base = ftw->base;
429
430                if (strcmp(&path[base], "..") == 0)
431                    base += 1;
432            } else
433                base = strlen(path);
434        }
435
436        p = &path[base];
437        nlen = strlen(path) - base;
438        target_mid = to.target_end;
439        if (*p != '/' && target_mid[-1] != '/')
440            *target_mid++ = '/';
441        *target_mid = 0;
442        if (target_mid - to.p_path + nlen >= PATH_MAX) {
443            SLOG("%s%s: name too long (not copied)", to.p_path, p);
444            badcp = rval = 1;
445            return 0;
446        }
447        (void)strncat(target_mid, p, nlen);
448        to.p_end = target_mid + nlen;
449        *to.p_end = 0;
450        STRIP_TRAILING_SLASH(to);
451    }
452
453    /* Not an error but need to remember it happened */
454    if (stat(to.p_path, &to_stat) == -1)
455        dne = 1;
456    else {
457        if (to_stat.st_dev == statp->st_dev &&
458            to_stat.st_ino == statp->st_ino) {
459            SLOG("%s and %s are identical (not copied).", to.p_path, path);
460            badcp = rval = 1;
461            if (S_ISDIR(statp->st_mode))
462                /* without using glibc extension FTW_ACTIONRETVAL cant handle this */
463                return FTW_SKIP_SUBTREE;
464            return 0;
465        }
466        if (!S_ISDIR(statp->st_mode) &&
467            S_ISDIR(to_stat.st_mode)) {
468            SLOG("cannot overwrite directory %s with "
469                 "non-directory %s",
470                 to.p_path, path);
471            badcp = rval = 1;
472            return 0;
473        }
474        dne = 0;
475    }
476
477    /* Convert basename to appropiate volume encoding */
478    if (dvolume.volinfo.v_path) {
479        if ((convert_dots_encoding(&svolume, &dvolume, to.p_path, MAXPATHLEN)) == -1) {
480            SLOG("Error converting name for %s", to.p_path);
481            badcp = rval = 1;
482            return -1;
483        }
484    }
485
486    switch (statp->st_mode & S_IFMT) {
487    case S_IFLNK:
488        if (ftw_copy_link(ftw, path, statp, !dne))
489            badcp = rval = 1;
490        break;
491    case S_IFDIR:
492        if (!Rflag) {
493            SLOG("%s is a directory", path);
494            badcp = rval = 1;
495            return -1;
496        }
497        /*
498         * If the directory doesn't exist, create the new
499         * one with the from file mode plus owner RWX bits,
500         * modified by the umask.  Trade-off between being
501         * able to write the directory (if from directory is
502         * 555) and not causing a permissions race.  If the
503         * umask blocks owner writes, we fail..
504         */
505        if (dne) {
506            if (mkdir(to.p_path, statp->st_mode | S_IRWXU) < 0)
507                ERROR("mkdir: %s: %s", to.p_path, strerror(errno));
508        } else if (!S_ISDIR(to_stat.st_mode)) {
509            errno = ENOTDIR;
510            ERROR("%s", to.p_path);
511        }
512
513        /* Create ad dir and copy ".Parent" */
514        if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
515
516            /* Create ".AppleDouble" dir */
517            mode_t omask = umask(0);
518            bstring addir = bfromcstr(to.p_path);
519            bcatcstr(addir, "/.AppleDouble");
520            mkdir(cfrombstr(addir), 02777);
521            bdestroy(addir);
522
523            if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
524                /* copy ".Parent" file */
525                if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
526                    SLOG("Error copying adouble for %s -> %s", path, to.p_path);
527                    badcp = rval = 1;
528                    break;
529                }
530            }
531
532            /* Get CNID of Parent and add new childir to CNID database */
533            ppdid = pdid;
534            if ((did = cnid_for_path(&dvolume, to.p_path, &pdid)) == CNID_INVALID) {
535                SLOG("Error resolving CNID for %s", to.p_path);
536                badcp = rval = 1;
537                return -1;
538            }
539
540            struct adouble ad;
541            struct stat st;
542            if (lstat(to.p_path, &st) != 0) {
543                badcp = rval = 1;
544                break;
545            }
546            ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
547            if (ad_open_metadata(to.p_path, ADFLAGS_DIR, O_RDWR | O_CREAT, &ad) != 0) {
548                ERROR("Error opening adouble for: %s", to.p_path);
549            }
550            ad_setid( &ad, st.st_dev, st.st_ino, did, pdid, dvolume.db_stamp);
551            ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
552            ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
553            ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
554            ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
555            ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
556            ad_flush(&ad);
557            ad_close_metadata(&ad);
558
559            umask(omask);
560        }
561
562        if (pflag) {
563            if (setfile(statp, -1))
564                rval = 1;
565#if 0
566            if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0)
567                rval = 1;
568#endif
569        }
570        break;
571
572    case S_IFBLK:
573    case S_IFCHR:
574        SLOG("%s is a device file (not copied).", path);
575        break;
576    case S_IFSOCK:
577        SLOG("%s is a socket (not copied).", path);
578        break;
579    case S_IFIFO:
580        SLOG("%s is a FIFO (not copied).", path);
581        break;
582    default:
583        if (ftw_copy_file(ftw, path, statp, dne))
584            badcp = rval = 1;
585
586        if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
587
588            mode_t omask = umask(0);
589            if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
590                /* copy ad-file */
591                if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
592                    SLOG("Error copying adouble for %s -> %s", path, to.p_path);
593                    badcp = rval = 1;
594                    break;
595                }
596            }
597
598            /* Get CNID of Parent and add new childir to CNID database */
599            pdid = did;
600            cnid_t cnid;
601            if ((cnid = cnid_for_path(&dvolume, to.p_path, &did)) == CNID_INVALID) {
602                SLOG("Error resolving CNID for %s", to.p_path);
603                badcp = rval = 1;
604                return -1;
605            }
606
607            struct adouble ad;
608            struct stat st;
609            if (lstat(to.p_path, &st) != 0) {
610                badcp = rval = 1;
611                break;
612            }
613            ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
614            if (ad_open_metadata(to.p_path, 0, O_RDWR | O_CREAT, &ad) != 0) {
615                ERROR("Error opening adouble for: %s", to.p_path);
616            }
617            ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp);
618            ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
619            ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
620            ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
621            ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
622            ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
623            ad_flush(&ad);
624            ad_close_metadata(&ad);
625            umask(omask);
626        }
627        break;
628    }
629    if (vflag && !badcp)
630        (void)printf("%s -> %s\n", path, to.p_path);
631
632    return 0;
633}
634
635/* Memory strategy threshold, in pages: if physmem is larger then this, use a large buffer */
636#define PHYSPAGES_THRESHOLD (32*1024)
637
638/* Maximum buffer size in bytes - do not allow it to grow larger than this */
639#define BUFSIZE_MAX (2*1024*1024)
640
641/* Small (default) buffer size in bytes. It's inefficient for this to be smaller than MAXPHYS */
642#define MAXPHYS (64 * 1024)
643#define BUFSIZE_SMALL (MAXPHYS)
644
645static int ftw_copy_file(const struct FTW *entp,
646                         const char *spath,
647                         const struct stat *sp,
648                         int dne)
649{
650    static char *buf = NULL;
651    static size_t bufsize;
652    ssize_t wcount;
653    size_t wresid;
654    off_t wtotal;
655    int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
656    char *bufp;
657    char *p;
658
659    if ((from_fd = open(spath, O_RDONLY, 0)) == -1) {
660        SLOG("%s: %s", spath, strerror(errno));
661        return (1);
662    }
663
664    /*
665     * If the file exists and we're interactive, verify with the user.
666     * If the file DNE, set the mode to be the from file, minus setuid
667     * bits, modified by the umask; arguably wrong, but it makes copying
668     * executables work right and it's been that way forever.  (The
669     * other choice is 666 or'ed with the execute bits on the from file
670     * modified by the umask.)
671     */
672    if (!dne) {
673#define YESNO "(y/n [n]) "
674        if (nflag) {
675            if (vflag)
676                printf("%s not overwritten\n", to.p_path);
677            (void)close(from_fd);
678            return (0);
679        } else if (iflag) {
680            (void)fprintf(stderr, "overwrite %s? %s",
681                          to.p_path, YESNO);
682            checkch = ch = getchar();
683            while (ch != '\n' && ch != EOF)
684                ch = getchar();
685            if (checkch != 'y' && checkch != 'Y') {
686                (void)close(from_fd);
687                (void)fprintf(stderr, "not overwritten\n");
688                return (1);
689            }
690        }
691
692        if (fflag) {
693            /* remove existing destination file name,
694             * create a new file  */
695            (void)unlink(to.p_path);
696            (void)dvolume.volume.vfs->vfs_deletefile(&dvolume.volume, -1, to.p_path);
697            to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
698                         sp->st_mode & ~(S_ISUID | S_ISGID));
699        } else {
700            /* overwrite existing destination file name */
701            to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
702        }
703    } else {
704        to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
705                     sp->st_mode & ~(S_ISUID | S_ISGID));
706    }
707
708    if (to_fd == -1) {
709        SLOG("%s: %s", to.p_path, strerror(errno));
710        (void)close(from_fd);
711        return (1);
712    }
713
714    rval = 0;
715
716    /*
717     * Mmap and write if less than 8M (the limit is so we don't totally
718     * trash memory on big files.  This is really a minor hack, but it
719     * wins some CPU back.
720     * Some filesystems, such as smbnetfs, don't support mmap,
721     * so this is a best-effort attempt.
722     */
723
724    if (S_ISREG(sp->st_mode) && sp->st_size > 0 &&
725        sp->st_size <= 8 * 1024 * 1024 &&
726        (p = mmap(NULL, (size_t)sp->st_size, PROT_READ,
727                  MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
728        wtotal = 0;
729        for (bufp = p, wresid = sp->st_size; ;
730             bufp += wcount, wresid -= (size_t)wcount) {
731            wcount = write(to_fd, bufp, wresid);
732            if (wcount <= 0)
733                break;
734            wtotal += wcount;
735            if (wcount >= (ssize_t)wresid)
736                break;
737        }
738        if (wcount != (ssize_t)wresid) {
739            SLOG("%s: %s", to.p_path, strerror(errno));
740            rval = 1;
741        }
742        /* Some systems don't unmap on close(2). */
743        if (munmap(p, sp->st_size) < 0) {
744            SLOG("%s: %s", spath, strerror(errno));
745            rval = 1;
746        }
747    } else {
748        if (buf == NULL) {
749            /*
750             * Note that buf and bufsize are static. If
751             * malloc() fails, it will fail at the start
752             * and not copy only some files.
753             */
754            if (sysconf(_SC_PHYS_PAGES) >
755                PHYSPAGES_THRESHOLD)
756                bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
757            else
758                bufsize = BUFSIZE_SMALL;
759            buf = malloc(bufsize);
760            if (buf == NULL)
761                ERROR("Not enough memory");
762
763        }
764        wtotal = 0;
765        while ((rcount = read(from_fd, buf, bufsize)) > 0) {
766            for (bufp = buf, wresid = rcount; ;
767                 bufp += wcount, wresid -= wcount) {
768                wcount = write(to_fd, bufp, wresid);
769                if (wcount <= 0)
770                    break;
771                wtotal += wcount;
772                if (wcount >= (ssize_t)wresid)
773                    break;
774            }
775            if (wcount != (ssize_t)wresid) {
776                SLOG("%s: %s", to.p_path, strerror(errno));
777                rval = 1;
778                break;
779            }
780        }
781        if (rcount < 0) {
782            SLOG("%s: %s", spath, strerror(errno));
783            rval = 1;
784        }
785    }
786
787    /*
788     * Don't remove the target even after an error.  The target might
789     * not be a regular file, or its attributes might be important,
790     * or its contents might be irreplaceable.  It would only be safe
791     * to remove it if we created it and its length is 0.
792     */
793
794    if (pflag && setfile(sp, to_fd))
795        rval = 1;
796    if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
797        rval = 1;
798    if (close(to_fd)) {
799        SLOG("%s: %s", to.p_path, strerror(errno));
800        rval = 1;
801    }
802
803    (void)close(from_fd);
804
805    return (rval);
806}
807
808static int ftw_copy_link(const struct FTW *p,
809                         const char *spath,
810                         const struct stat *sstp,
811                         int exists)
812{
813    int len;
814    char llink[PATH_MAX];
815
816    if ((len = readlink(spath, llink, sizeof(llink) - 1)) == -1) {
817        SLOG("readlink: %s: %s", spath, strerror(errno));
818        return (1);
819    }
820    llink[len] = '\0';
821    if (exists && unlink(to.p_path)) {
822        SLOG("unlink: %s: %s", to.p_path, strerror(errno));
823        return (1);
824    }
825    if (symlink(llink, to.p_path)) {
826        SLOG("symlink: %s: %s", llink, strerror(errno));
827        return (1);
828    }
829    return (pflag ? setfile(sstp, -1) : 0);
830}
831
832static int setfile(const struct stat *fs, int fd)
833{
834    static struct timeval tv[2];
835    struct stat ts;
836    int rval, gotstat, islink, fdval;
837    mode_t mode;
838
839    rval = 0;
840    fdval = fd != -1;
841    islink = !fdval && S_ISLNK(fs->st_mode);
842    mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
843
844#if defined(__FreeBSD__)
845    TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
846    TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
847#else
848    TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
849    TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
850#endif
851
852    if (utimes(to.p_path, tv)) {
853        SLOG("utimes: %s", to.p_path);
854        rval = 1;
855    }
856    if (fdval ? fstat(fd, &ts) :
857        (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
858        gotstat = 0;
859    else {
860        gotstat = 1;
861        ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
862            S_IRWXU | S_IRWXG | S_IRWXO;
863    }
864    /*
865     * Changing the ownership probably won't succeed, unless we're root
866     * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
867     * the mode; current BSD behavior is to remove all setuid bits on
868     * chown.  If chown fails, lose setuid/setgid bits.
869     */
870    if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
871        if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
872            (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
873             chown(to.p_path, fs->st_uid, fs->st_gid))) {
874            if (errno != EPERM) {
875                SLOG("chown: %s: %s", to.p_path, strerror(errno));
876                rval = 1;
877            }
878            mode &= ~(S_ISUID | S_ISGID);
879        }
880
881    if (!gotstat || mode != ts.st_mode)
882        if (fdval ? fchmod(fd, mode) : chmod(to.p_path, mode)) {
883            SLOG("chmod: %s: %s", to.p_path, strerror(errno));
884            rval = 1;
885        }
886
887#ifdef HAVE_ST_FLAGS
888    if (!gotstat || fs->st_flags != ts.st_flags)
889        if (fdval ?
890            fchflags(fd, fs->st_flags) :
891            (islink ? lchflags(to.p_path, fs->st_flags) :
892             chflags(to.p_path, fs->st_flags))) {
893            SLOG("chflags: %s: %s", to.p_path, strerror(errno));
894            rval = 1;
895        }
896#endif
897
898    return (rval);
899}
900
901static int preserve_fd_acls(int source_fd, int dest_fd)
902{
903#if 0
904    acl_t acl;
905    acl_type_t acl_type;
906    int acl_supported = 0, ret, trivial;
907
908    ret = fpathconf(source_fd, _PC_ACL_NFS4);
909    if (ret > 0 ) {
910        acl_supported = 1;
911        acl_type = ACL_TYPE_NFS4;
912    } else if (ret < 0 && errno != EINVAL) {
913        warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
914        return (1);
915    }
916    if (acl_supported == 0) {
917        ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
918        if (ret > 0 ) {
919            acl_supported = 1;
920            acl_type = ACL_TYPE_ACCESS;
921        } else if (ret < 0 && errno != EINVAL) {
922            warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
923                 to.p_path);
924            return (1);
925        }
926    }
927    if (acl_supported == 0)
928        return (0);
929
930    acl = acl_get_fd_np(source_fd, acl_type);
931    if (acl == NULL) {
932        warn("failed to get acl entries while setting %s", to.p_path);
933        return (1);
934    }
935    if (acl_is_trivial_np(acl, &trivial)) {
936        warn("acl_is_trivial() failed for %s", to.p_path);
937        acl_free(acl);
938        return (1);
939    }
940    if (trivial) {
941        acl_free(acl);
942        return (0);
943    }
944    if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
945        warn("failed to set acl entries for %s", to.p_path);
946        acl_free(acl);
947        return (1);
948    }
949    acl_free(acl);
950#endif
951    return (0);
952}
953
954static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
955{
956#if 0
957    acl_t (*aclgetf)(const char *, acl_type_t);
958    int (*aclsetf)(const char *, acl_type_t, acl_t);
959    struct acl *aclp;
960    acl_t acl;
961    acl_type_t acl_type;
962    int acl_supported = 0, ret, trivial;
963
964    ret = pathconf(source_dir, _PC_ACL_NFS4);
965    if (ret > 0) {
966        acl_supported = 1;
967        acl_type = ACL_TYPE_NFS4;
968    } else if (ret < 0 && errno != EINVAL) {
969        warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
970        return (1);
971    }
972    if (acl_supported == 0) {
973        ret = pathconf(source_dir, _PC_ACL_EXTENDED);
974        if (ret > 0) {
975            acl_supported = 1;
976            acl_type = ACL_TYPE_ACCESS;
977        } else if (ret < 0 && errno != EINVAL) {
978            warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
979                 source_dir);
980            return (1);
981        }
982    }
983    if (acl_supported == 0)
984        return (0);
985
986    /*
987     * If the file is a link we will not follow it
988     */
989    if (S_ISLNK(fs->st_mode)) {
990        aclgetf = acl_get_link_np;
991        aclsetf = acl_set_link_np;
992    } else {
993        aclgetf = acl_get_file;
994        aclsetf = acl_set_file;
995    }
996    if (acl_type == ACL_TYPE_ACCESS) {
997        /*
998         * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
999         * size ACL will be returned. So it is not safe to simply
1000         * check the pointer to see if the default ACL is present.
1001         */
1002        acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
1003        if (acl == NULL) {
1004            warn("failed to get default acl entries on %s",
1005                 source_dir);
1006            return (1);
1007        }
1008        aclp = &acl->ats_acl;
1009        if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
1010                                          ACL_TYPE_DEFAULT, acl) < 0) {
1011            warn("failed to set default acl entries on %s",
1012                 dest_dir);
1013            acl_free(acl);
1014            return (1);
1015        }
1016        acl_free(acl);
1017    }
1018    acl = aclgetf(source_dir, acl_type);
1019    if (acl == NULL) {
1020        warn("failed to get acl entries on %s", source_dir);
1021        return (1);
1022    }
1023    if (acl_is_trivial_np(acl, &trivial)) {
1024        warn("acl_is_trivial() failed on %s", source_dir);
1025        acl_free(acl);
1026        return (1);
1027    }
1028    if (trivial) {
1029        acl_free(acl);
1030        return (0);
1031    }
1032    if (aclsetf(dest_dir, acl_type, acl) < 0) {
1033        warn("failed to set acl entries on %s", dest_dir);
1034        acl_free(acl);
1035        return (1);
1036    }
1037    acl_free(acl);
1038#endif
1039    return (0);
1040}
1041