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