]> arthur.barton.de Git - netatalk.git/blob - bin/ad/ad_cp.c
Merge master
[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/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
82 static char emptystring[] = "";
83
84 PATH_T to = { to.p_path, emptystring, "" };
85 enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
86
87 int fflag, iflag, nflag, pflag, vflag;
88 mode_t mask;
89
90 cnid_t ppdid, pdid, did; /* current dir CNID and parent did*/
91
92 static afpvol_t svolume, dvolume;
93 static enum op type;
94 static int Rflag;
95 static volatile sig_atomic_t alarmed;
96 static int badcp, rval;
97 static int ftw_options = FTW_MOUNT | FTW_PHYS | FTW_ACTIONRETVAL;
98
99 static char           *netatalk_dirs[] = {
100     ".AppleDouble",
101     ".AppleDB",
102     ".AppleDesktop",
103     NULL
104 };
105
106 /* Forward declarations */
107 static int copy(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
108 static int ftw_copy_file(const struct FTW *, const char *, const struct stat *, int);
109 static int ftw_copy_link(const struct FTW *, const char *, const struct stat *, int);
110 static int setfile(const struct stat *, int);
111 static int preserve_dir_acls(const struct stat *, char *, char *);
112 static 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 */
118 static 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
129 static 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
140 static void sig_handler(int signo)
141 {
142     alarmed = 1;
143     return;
144 }
145
146 static 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
173 static 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
215 int 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
374 static 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(&ad, to.p_path, ADFLAGS_HF | ADFLAGS_DIR, O_RDWR | O_CREAT, 0666) != 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(&ad, to.p_path, ADFLAGS_HF, O_RDWR | O_CREAT) != 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
645 static 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
808 static 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
832 static 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     TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
845     TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
846     if (utimes(to.p_path, tv)) {
847         SLOG("utimes: %s", to.p_path);
848         rval = 1;
849     }
850     if (fdval ? fstat(fd, &ts) :
851         (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
852         gotstat = 0;
853     else {
854         gotstat = 1;
855         ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
856             S_IRWXU | S_IRWXG | S_IRWXO;
857     }
858     /*
859      * Changing the ownership probably won't succeed, unless we're root
860      * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
861      * the mode; current BSD behavior is to remove all setuid bits on
862      * chown.  If chown fails, lose setuid/setgid bits.
863      */
864     if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
865         if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
866             (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
867              chown(to.p_path, fs->st_uid, fs->st_gid))) {
868             if (errno != EPERM) {
869                 SLOG("chown: %s: %s", to.p_path, strerror(errno));
870                 rval = 1;
871             }
872             mode &= ~(S_ISUID | S_ISGID);
873         }
874
875     if (!gotstat || mode != ts.st_mode)
876         if (fdval ? fchmod(fd, mode) : chmod(to.p_path, mode)) {
877             SLOG("chmod: %s: %s", to.p_path, strerror(errno));
878             rval = 1;
879         }
880
881 #ifdef HAVE_ST_FLAGS
882     if (!gotstat || fs->st_flags != ts.st_flags)
883         if (fdval ?
884             fchflags(fd, fs->st_flags) :
885             (islink ? lchflags(to.p_path, fs->st_flags) :
886              chflags(to.p_path, fs->st_flags))) {
887             SLOG("chflags: %s: %s", to.p_path, strerror(errno));
888             rval = 1;
889         }
890 #endif
891
892     return (rval);
893 }
894
895 static int preserve_fd_acls(int source_fd, int dest_fd)
896 {
897 #if 0
898     acl_t acl;
899     acl_type_t acl_type;
900     int acl_supported = 0, ret, trivial;
901
902     ret = fpathconf(source_fd, _PC_ACL_NFS4);
903     if (ret > 0 ) {
904         acl_supported = 1;
905         acl_type = ACL_TYPE_NFS4;
906     } else if (ret < 0 && errno != EINVAL) {
907         warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
908         return (1);
909     }
910     if (acl_supported == 0) {
911         ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
912         if (ret > 0 ) {
913             acl_supported = 1;
914             acl_type = ACL_TYPE_ACCESS;
915         } else if (ret < 0 && errno != EINVAL) {
916             warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
917                  to.p_path);
918             return (1);
919         }
920     }
921     if (acl_supported == 0)
922         return (0);
923
924     acl = acl_get_fd_np(source_fd, acl_type);
925     if (acl == NULL) {
926         warn("failed to get acl entries while setting %s", to.p_path);
927         return (1);
928     }
929     if (acl_is_trivial_np(acl, &trivial)) {
930         warn("acl_is_trivial() failed for %s", to.p_path);
931         acl_free(acl);
932         return (1);
933     }
934     if (trivial) {
935         acl_free(acl);
936         return (0);
937     }
938     if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
939         warn("failed to set acl entries for %s", to.p_path);
940         acl_free(acl);
941         return (1);
942     }
943     acl_free(acl);
944 #endif
945     return (0);
946 }
947
948 static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
949 {
950 #if 0
951     acl_t (*aclgetf)(const char *, acl_type_t);
952     int (*aclsetf)(const char *, acl_type_t, acl_t);
953     struct acl *aclp;
954     acl_t acl;
955     acl_type_t acl_type;
956     int acl_supported = 0, ret, trivial;
957
958     ret = pathconf(source_dir, _PC_ACL_NFS4);
959     if (ret > 0) {
960         acl_supported = 1;
961         acl_type = ACL_TYPE_NFS4;
962     } else if (ret < 0 && errno != EINVAL) {
963         warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
964         return (1);
965     }
966     if (acl_supported == 0) {
967         ret = pathconf(source_dir, _PC_ACL_EXTENDED);
968         if (ret > 0) {
969             acl_supported = 1;
970             acl_type = ACL_TYPE_ACCESS;
971         } else if (ret < 0 && errno != EINVAL) {
972             warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
973                  source_dir);
974             return (1);
975         }
976     }
977     if (acl_supported == 0)
978         return (0);
979
980     /*
981      * If the file is a link we will not follow it
982      */
983     if (S_ISLNK(fs->st_mode)) {
984         aclgetf = acl_get_link_np;
985         aclsetf = acl_set_link_np;
986     } else {
987         aclgetf = acl_get_file;
988         aclsetf = acl_set_file;
989     }
990     if (acl_type == ACL_TYPE_ACCESS) {
991         /*
992          * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
993          * size ACL will be returned. So it is not safe to simply
994          * check the pointer to see if the default ACL is present.
995          */
996         acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
997         if (acl == NULL) {
998             warn("failed to get default acl entries on %s",
999                  source_dir);
1000             return (1);
1001         }
1002         aclp = &acl->ats_acl;
1003         if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
1004                                           ACL_TYPE_DEFAULT, acl) < 0) {
1005             warn("failed to set default acl entries on %s",
1006                  dest_dir);
1007             acl_free(acl);
1008             return (1);
1009         }
1010         acl_free(acl);
1011     }
1012     acl = aclgetf(source_dir, acl_type);
1013     if (acl == NULL) {
1014         warn("failed to get acl entries on %s", source_dir);
1015         return (1);
1016     }
1017     if (acl_is_trivial_np(acl, &trivial)) {
1018         warn("acl_is_trivial() failed on %s", source_dir);
1019         acl_free(acl);
1020         return (1);
1021     }
1022     if (trivial) {
1023         acl_free(acl);
1024         return (0);
1025     }
1026     if (aclsetf(dest_dir, acl_type, acl) < 0) {
1027         warn("failed to set acl entries on %s", dest_dir);
1028         acl_free(acl);
1029         return (1);
1030     }
1031     acl_free(acl);
1032 #endif
1033     return (0);
1034 }