]> arthur.barton.de Git - netatalk.git/blob - bin/ad/ad_cp.c
97b30610080ac111b85ab9a5b4139ac1069fed85
[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     const char *dir = strrchr(path, '/');
391     if (dir == NULL)
392         dir = path;
393     else
394         dir++;
395     if (check_netatalk_dirs(dir) != NULL)
396         return FTW_SKIP_SUBTREE;
397
398     /*
399      * If we are in case (2) above, we need to append the
400      * source name to the target name.
401      */
402     if (type != FILE_TO_FILE) {
403         /*
404          * Need to remember the roots of traversals to create
405          * correct pathnames.  If there's a directory being
406          * copied to a non-existent directory, e.g.
407          *     cp -R a/dir noexist
408          * the resulting path name should be noexist/foo, not
409          * noexist/dir/foo (where foo is a file in dir), which
410          * is the case where the target exists.
411          *
412          * Also, check for "..".  This is for correct path
413          * concatenation for paths ending in "..", e.g.
414          *     cp -R .. /tmp
415          * Paths ending in ".." are changed to ".".  This is
416          * tricky, but seems the easiest way to fix the problem.
417          *
418          * XXX
419          * Since the first level MUST be FTS_ROOTLEVEL, base
420          * is always initialized.
421          */
422         if (ftw->level == 0) {
423             if (type != DIR_TO_DNE) {
424                 base = ftw->base;
425
426                 if (strcmp(&path[base], "..") == 0)
427                     base += 1;
428             } else
429                 base = strlen(path);
430         }
431
432         p = &path[base];
433         nlen = strlen(path) - base;
434         target_mid = to.target_end;
435         if (*p != '/' && target_mid[-1] != '/')
436             *target_mid++ = '/';
437         *target_mid = 0;
438         if (target_mid - to.p_path + nlen >= PATH_MAX) {
439             SLOG("%s%s: name too long (not copied)", to.p_path, p);
440             badcp = rval = 1;
441             return 0;
442         }
443         (void)strncat(target_mid, p, nlen);
444         to.p_end = target_mid + nlen;
445         *to.p_end = 0;
446         STRIP_TRAILING_SLASH(to);
447     }
448
449     /* Not an error but need to remember it happened */
450     if (stat(to.p_path, &to_stat) == -1)
451         dne = 1;
452     else {
453         if (to_stat.st_dev == statp->st_dev &&
454             to_stat.st_ino == statp->st_ino) {
455             SLOG("%s and %s are identical (not copied).", to.p_path, path);
456             badcp = rval = 1;
457             if (S_ISDIR(statp->st_mode))
458                 /* without using glibc extension FTW_ACTIONRETVAL cant handle this */
459                 return FTW_SKIP_SUBTREE;
460             return 0;
461         }
462         if (!S_ISDIR(statp->st_mode) &&
463             S_ISDIR(to_stat.st_mode)) {
464             SLOG("cannot overwrite directory %s with "
465                  "non-directory %s",
466                  to.p_path, path);
467             badcp = rval = 1;
468             return 0;
469         }
470         dne = 0;
471     }
472
473     /* Convert basename to appropiate volume encoding */
474     if (dvolume.volinfo.v_path) {
475         if ((convert_dots_encoding(&svolume, &dvolume, to.p_path, MAXPATHLEN)) == -1) {
476             SLOG("Error converting name for %s", to.p_path);
477             badcp = rval = 1;
478             return -1;
479         }
480     }
481
482     switch (statp->st_mode & S_IFMT) {
483     case S_IFLNK:
484         if (ftw_copy_link(ftw, path, statp, !dne))
485             badcp = rval = 1;
486         break;
487     case S_IFDIR:
488         if (!Rflag) {
489             SLOG("%s is a directory", path);
490             badcp = rval = 1;
491             return -1;
492         }
493         /*
494          * If the directory doesn't exist, create the new
495          * one with the from file mode plus owner RWX bits,
496          * modified by the umask.  Trade-off between being
497          * able to write the directory (if from directory is
498          * 555) and not causing a permissions race.  If the
499          * umask blocks owner writes, we fail..
500          */
501         if (dne) {
502             if (mkdir(to.p_path, statp->st_mode | S_IRWXU) < 0)
503                 ERROR("mkdir: %s: %s", to.p_path, strerror(errno));
504         } else if (!S_ISDIR(to_stat.st_mode)) {
505             errno = ENOTDIR;
506             ERROR("%s", to.p_path);
507         }
508
509         /* Create ad dir and copy ".Parent" */
510         if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
511
512             /* Create ".AppleDouble" dir */
513             mode_t omask = umask(0);
514             bstring addir = bfromcstr(to.p_path);
515             bcatcstr(addir, "/.AppleDouble");
516             mkdir(cfrombstr(addir), 02777);
517             bdestroy(addir);
518
519             if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
520                 /* copy ".Parent" file */
521                 if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
522                     SLOG("Error copying adouble for %s -> %s", path, to.p_path);
523                     badcp = rval = 1;
524                     break;
525                 }
526             }
527
528             /* Get CNID of Parent and add new childir to CNID database */
529             ppdid = pdid;
530             if ((did = cnid_for_path(&dvolume, to.p_path, &pdid)) == CNID_INVALID) {
531                 SLOG("Error resolving CNID for %s", to.p_path);
532                 badcp = rval = 1;
533                 return -1;
534             }
535
536             struct adouble ad;
537             struct stat st;
538             if (lstat(to.p_path, &st) != 0) {
539                 badcp = rval = 1;
540                 break;
541             }
542             ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
543             if (ad_open_metadata(to.p_path, ADFLAGS_DIR, O_RDWR | O_CREAT, &ad) != 0) {
544                 ERROR("Error opening adouble for: %s", to.p_path);
545             }
546             ad_setid( &ad, st.st_dev, st.st_ino, did, pdid, dvolume.db_stamp);
547             ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
548             ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
549             ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
550             ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
551             ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
552             ad_flush(&ad);
553             ad_close_metadata(&ad);
554
555             umask(omask);
556         }
557
558         if (pflag) {
559             if (setfile(statp, -1))
560                 rval = 1;
561 #if 0
562             if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0)
563                 rval = 1;
564 #endif
565         }
566         break;
567
568     case S_IFBLK:
569     case S_IFCHR:
570         SLOG("%s is a device file (not copied).", path);
571         break;
572     case S_IFSOCK:
573         SLOG("%s is a socket (not copied).", path);
574         break;
575     case S_IFIFO:
576         SLOG("%s is a FIFO (not copied).", path);
577         break;
578     default:
579         if (ftw_copy_file(ftw, path, statp, dne))
580             badcp = rval = 1;
581
582         if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
583
584             mode_t omask = umask(0);
585             if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
586                 /* copy ad-file */
587                 if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
588                     SLOG("Error copying adouble for %s -> %s", path, to.p_path);
589                     badcp = rval = 1;
590                     break;
591                 }
592             }
593
594             /* Get CNID of Parent and add new childir to CNID database */
595             pdid = did;
596             cnid_t cnid;
597             if ((cnid = cnid_for_path(&dvolume, to.p_path, &did)) == CNID_INVALID) {
598                 SLOG("Error resolving CNID for %s", to.p_path);
599                 badcp = rval = 1;
600                 return -1;
601             }
602
603             struct adouble ad;
604             struct stat st;
605             if (lstat(to.p_path, &st) != 0) {
606                 badcp = rval = 1;
607                 break;
608             }
609             ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
610             if (ad_open_metadata(to.p_path, 0, O_RDWR | O_CREAT, &ad) != 0) {
611                 ERROR("Error opening adouble for: %s", to.p_path);
612             }
613             ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp);
614             ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
615             ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
616             ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
617             ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
618             ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
619             ad_flush(&ad);
620             ad_close_metadata(&ad);
621             umask(omask);
622         }
623         break;
624     }
625     if (vflag && !badcp)
626         (void)printf("%s -> %s\n", path, to.p_path);
627
628     return 0;
629 }
630
631 /* Memory strategy threshold, in pages: if physmem is larger then this, use a large buffer */
632 #define PHYSPAGES_THRESHOLD (32*1024)
633
634 /* Maximum buffer size in bytes - do not allow it to grow larger than this */
635 #define BUFSIZE_MAX (2*1024*1024)
636
637 /* Small (default) buffer size in bytes. It's inefficient for this to be smaller than MAXPHYS */
638 #define MAXPHYS (64 * 1024)
639 #define BUFSIZE_SMALL (MAXPHYS)
640
641 static int ftw_copy_file(const struct FTW *entp,
642                          const char *spath,
643                          const struct stat *sp,
644                          int dne)
645 {
646     static char *buf = NULL;
647     static size_t bufsize;
648     ssize_t wcount;
649     size_t wresid;
650     off_t wtotal;
651     int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
652     char *bufp;
653     char *p;
654
655     if ((from_fd = open(spath, O_RDONLY, 0)) == -1) {
656         SLOG("%s: %s", spath, strerror(errno));
657         return (1);
658     }
659
660     /*
661      * If the file exists and we're interactive, verify with the user.
662      * If the file DNE, set the mode to be the from file, minus setuid
663      * bits, modified by the umask; arguably wrong, but it makes copying
664      * executables work right and it's been that way forever.  (The
665      * other choice is 666 or'ed with the execute bits on the from file
666      * modified by the umask.)
667      */
668     if (!dne) {
669 #define YESNO "(y/n [n]) "
670         if (nflag) {
671             if (vflag)
672                 printf("%s not overwritten\n", to.p_path);
673             (void)close(from_fd);
674             return (0);
675         } else if (iflag) {
676             (void)fprintf(stderr, "overwrite %s? %s",
677                           to.p_path, YESNO);
678             checkch = ch = getchar();
679             while (ch != '\n' && ch != EOF)
680                 ch = getchar();
681             if (checkch != 'y' && checkch != 'Y') {
682                 (void)close(from_fd);
683                 (void)fprintf(stderr, "not overwritten\n");
684                 return (1);
685             }
686         }
687
688         if (fflag) {
689             /* remove existing destination file name,
690              * create a new file  */
691             (void)unlink(to.p_path);
692             (void)dvolume.volume.vfs->vfs_deletefile(&dvolume.volume, -1, to.p_path);
693             to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
694                          sp->st_mode & ~(S_ISUID | S_ISGID));
695         } else {
696             /* overwrite existing destination file name */
697             to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
698         }
699     } else {
700         to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
701                      sp->st_mode & ~(S_ISUID | S_ISGID));
702     }
703
704     if (to_fd == -1) {
705         SLOG("%s: %s", to.p_path, strerror(errno));
706         (void)close(from_fd);
707         return (1);
708     }
709
710     rval = 0;
711
712     /*
713      * Mmap and write if less than 8M (the limit is so we don't totally
714      * trash memory on big files.  This is really a minor hack, but it
715      * wins some CPU back.
716      * Some filesystems, such as smbnetfs, don't support mmap,
717      * so this is a best-effort attempt.
718      */
719
720     if (S_ISREG(sp->st_mode) && sp->st_size > 0 &&
721         sp->st_size <= 8 * 1024 * 1024 &&
722         (p = mmap(NULL, (size_t)sp->st_size, PROT_READ,
723                   MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
724         wtotal = 0;
725         for (bufp = p, wresid = sp->st_size; ;
726              bufp += wcount, wresid -= (size_t)wcount) {
727             wcount = write(to_fd, bufp, wresid);
728             if (wcount <= 0)
729                 break;
730             wtotal += wcount;
731             if (wcount >= (ssize_t)wresid)
732                 break;
733         }
734         if (wcount != (ssize_t)wresid) {
735             SLOG("%s: %s", to.p_path, strerror(errno));
736             rval = 1;
737         }
738         /* Some systems don't unmap on close(2). */
739         if (munmap(p, sp->st_size) < 0) {
740             SLOG("%s: %s", spath, strerror(errno));
741             rval = 1;
742         }
743     } else {
744         if (buf == NULL) {
745             /*
746              * Note that buf and bufsize are static. If
747              * malloc() fails, it will fail at the start
748              * and not copy only some files.
749              */
750             if (sysconf(_SC_PHYS_PAGES) >
751                 PHYSPAGES_THRESHOLD)
752                 bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
753             else
754                 bufsize = BUFSIZE_SMALL;
755             buf = malloc(bufsize);
756             if (buf == NULL)
757                 ERROR("Not enough memory");
758
759         }
760         wtotal = 0;
761         while ((rcount = read(from_fd, buf, bufsize)) > 0) {
762             for (bufp = buf, wresid = rcount; ;
763                  bufp += wcount, wresid -= wcount) {
764                 wcount = write(to_fd, bufp, wresid);
765                 if (wcount <= 0)
766                     break;
767                 wtotal += wcount;
768                 if (wcount >= (ssize_t)wresid)
769                     break;
770             }
771             if (wcount != (ssize_t)wresid) {
772                 SLOG("%s: %s", to.p_path, strerror(errno));
773                 rval = 1;
774                 break;
775             }
776         }
777         if (rcount < 0) {
778             SLOG("%s: %s", spath, strerror(errno));
779             rval = 1;
780         }
781     }
782
783     /*
784      * Don't remove the target even after an error.  The target might
785      * not be a regular file, or its attributes might be important,
786      * or its contents might be irreplaceable.  It would only be safe
787      * to remove it if we created it and its length is 0.
788      */
789
790     if (pflag && setfile(sp, to_fd))
791         rval = 1;
792     if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
793         rval = 1;
794     if (close(to_fd)) {
795         SLOG("%s: %s", to.p_path, strerror(errno));
796         rval = 1;
797     }
798
799     (void)close(from_fd);
800
801     return (rval);
802 }
803
804 static int ftw_copy_link(const struct FTW *p,
805                          const char *spath,
806                          const struct stat *sstp,
807                          int exists)
808 {
809     int len;
810     char llink[PATH_MAX];
811
812     if ((len = readlink(spath, llink, sizeof(llink) - 1)) == -1) {
813         SLOG("readlink: %s: %s", spath, strerror(errno));
814         return (1);
815     }
816     llink[len] = '\0';
817     if (exists && unlink(to.p_path)) {
818         SLOG("unlink: %s: %s", to.p_path, strerror(errno));
819         return (1);
820     }
821     if (symlink(llink, to.p_path)) {
822         SLOG("symlink: %s: %s", llink, strerror(errno));
823         return (1);
824     }
825     return (pflag ? setfile(sstp, -1) : 0);
826 }
827
828 static int setfile(const struct stat *fs, int fd)
829 {
830     static struct timeval tv[2];
831     struct stat ts;
832     int rval, gotstat, islink, fdval;
833     mode_t mode;
834
835     rval = 0;
836     fdval = fd != -1;
837     islink = !fdval && S_ISLNK(fs->st_mode);
838     mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
839
840     TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
841     TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
842     if (utimes(to.p_path, tv)) {
843         SLOG("utimes: %s", to.p_path);
844         rval = 1;
845     }
846     if (fdval ? fstat(fd, &ts) :
847         (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
848         gotstat = 0;
849     else {
850         gotstat = 1;
851         ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
852             S_IRWXU | S_IRWXG | S_IRWXO;
853     }
854     /*
855      * Changing the ownership probably won't succeed, unless we're root
856      * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
857      * the mode; current BSD behavior is to remove all setuid bits on
858      * chown.  If chown fails, lose setuid/setgid bits.
859      */
860     if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
861         if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
862             (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
863              chown(to.p_path, fs->st_uid, fs->st_gid))) {
864             if (errno != EPERM) {
865                 SLOG("chown: %s: %s", to.p_path, strerror(errno));
866                 rval = 1;
867             }
868             mode &= ~(S_ISUID | S_ISGID);
869         }
870
871     if (!gotstat || mode != ts.st_mode)
872         if (fdval ? fchmod(fd, mode) : chmod(to.p_path, mode)) {
873             SLOG("chmod: %s: %s", to.p_path, strerror(errno));
874             rval = 1;
875         }
876
877 #ifdef HAVE_ST_FLAGS
878     if (!gotstat || fs->st_flags != ts.st_flags)
879         if (fdval ?
880             fchflags(fd, fs->st_flags) :
881             (islink ? lchflags(to.p_path, fs->st_flags) :
882              chflags(to.p_path, fs->st_flags))) {
883             SLOG("chflags: %s: %s", to.p_path, strerror(errno));
884             rval = 1;
885         }
886 #endif
887
888     return (rval);
889 }
890
891 static int preserve_fd_acls(int source_fd, int dest_fd)
892 {
893 #if 0
894     acl_t acl;
895     acl_type_t acl_type;
896     int acl_supported = 0, ret, trivial;
897
898     ret = fpathconf(source_fd, _PC_ACL_NFS4);
899     if (ret > 0 ) {
900         acl_supported = 1;
901         acl_type = ACL_TYPE_NFS4;
902     } else if (ret < 0 && errno != EINVAL) {
903         warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
904         return (1);
905     }
906     if (acl_supported == 0) {
907         ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
908         if (ret > 0 ) {
909             acl_supported = 1;
910             acl_type = ACL_TYPE_ACCESS;
911         } else if (ret < 0 && errno != EINVAL) {
912             warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
913                  to.p_path);
914             return (1);
915         }
916     }
917     if (acl_supported == 0)
918         return (0);
919
920     acl = acl_get_fd_np(source_fd, acl_type);
921     if (acl == NULL) {
922         warn("failed to get acl entries while setting %s", to.p_path);
923         return (1);
924     }
925     if (acl_is_trivial_np(acl, &trivial)) {
926         warn("acl_is_trivial() failed for %s", to.p_path);
927         acl_free(acl);
928         return (1);
929     }
930     if (trivial) {
931         acl_free(acl);
932         return (0);
933     }
934     if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
935         warn("failed to set acl entries for %s", to.p_path);
936         acl_free(acl);
937         return (1);
938     }
939     acl_free(acl);
940 #endif
941     return (0);
942 }
943
944 static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
945 {
946 #if 0
947     acl_t (*aclgetf)(const char *, acl_type_t);
948     int (*aclsetf)(const char *, acl_type_t, acl_t);
949     struct acl *aclp;
950     acl_t acl;
951     acl_type_t acl_type;
952     int acl_supported = 0, ret, trivial;
953
954     ret = pathconf(source_dir, _PC_ACL_NFS4);
955     if (ret > 0) {
956         acl_supported = 1;
957         acl_type = ACL_TYPE_NFS4;
958     } else if (ret < 0 && errno != EINVAL) {
959         warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
960         return (1);
961     }
962     if (acl_supported == 0) {
963         ret = pathconf(source_dir, _PC_ACL_EXTENDED);
964         if (ret > 0) {
965             acl_supported = 1;
966             acl_type = ACL_TYPE_ACCESS;
967         } else if (ret < 0 && errno != EINVAL) {
968             warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
969                  source_dir);
970             return (1);
971         }
972     }
973     if (acl_supported == 0)
974         return (0);
975
976     /*
977      * If the file is a link we will not follow it
978      */
979     if (S_ISLNK(fs->st_mode)) {
980         aclgetf = acl_get_link_np;
981         aclsetf = acl_set_link_np;
982     } else {
983         aclgetf = acl_get_file;
984         aclsetf = acl_set_file;
985     }
986     if (acl_type == ACL_TYPE_ACCESS) {
987         /*
988          * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
989          * size ACL will be returned. So it is not safe to simply
990          * check the pointer to see if the default ACL is present.
991          */
992         acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
993         if (acl == NULL) {
994             warn("failed to get default acl entries on %s",
995                  source_dir);
996             return (1);
997         }
998         aclp = &acl->ats_acl;
999         if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
1000                                           ACL_TYPE_DEFAULT, acl) < 0) {
1001             warn("failed to set default acl entries on %s",
1002                  dest_dir);
1003             acl_free(acl);
1004             return (1);
1005         }
1006         acl_free(acl);
1007     }
1008     acl = aclgetf(source_dir, acl_type);
1009     if (acl == NULL) {
1010         warn("failed to get acl entries on %s", source_dir);
1011         return (1);
1012     }
1013     if (acl_is_trivial_np(acl, &trivial)) {
1014         warn("acl_is_trivial() failed on %s", source_dir);
1015         acl_free(acl);
1016         return (1);
1017     }
1018     if (trivial) {
1019         acl_free(acl);
1020         return (0);
1021     }
1022     if (aclsetf(dest_dir, acl_type, acl) < 0) {
1023         warn("failed to set acl entries on %s", dest_dir);
1024         acl_free(acl);
1025         return (1);
1026     }
1027     acl_free(acl);
1028 #endif
1029     return (0);
1030 }