]> arthur.barton.de Git - netatalk.git/blob - bin/ad/ad_cp.c
Portability fixes
[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             did = cnid_for_path(&dvolume, to.p_path, &pdid);
531
532             struct adouble ad;
533             struct stat st;
534             if (stat(to.p_path, &st) != 0) {
535                 badcp = rval = 1;
536                 break;
537             }
538             ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
539             if (ad_open_metadata(to.p_path, ADFLAGS_DIR, O_RDWR | O_CREAT, &ad) != 0) {
540                 ERROR("Error opening adouble for: %s", to.p_path);
541             }
542             ad_setid( &ad, st.st_dev, st.st_ino, did, pdid, dvolume.db_stamp);
543             ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
544             ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
545             ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
546             ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
547             ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
548             ad_flush(&ad);
549             ad_close_metadata(&ad);
550
551             umask(omask);
552         }
553
554         if (pflag) {
555             if (setfile(statp, -1))
556                 rval = 1;
557 #if 0
558             if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0)
559                 rval = 1;
560 #endif
561         }
562         break;
563
564     case S_IFBLK:
565     case S_IFCHR:
566         SLOG("%s is a device file (not copied).", path);
567         break;
568     case S_IFSOCK:
569         SLOG("%s is a socket (not copied).", path);
570         break;
571     case S_IFIFO:
572         SLOG("%s is a FIFO (not copied).", path);
573         break;
574     default:
575         if (ftw_copy_file(ftw, path, statp, dne))
576             badcp = rval = 1;
577
578         if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
579
580             mode_t omask = umask(0);
581             if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
582                 /* copy ad-file */
583                 if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
584                     SLOG("Error copying adouble for %s -> %s", path, to.p_path);
585                     badcp = rval = 1;
586                     break;
587                 }
588             }
589
590             /* Get CNID of Parent and add new childir to CNID database */
591             pdid = did;
592             cnid_t cnid = cnid_for_path(&dvolume, to.p_path, &did);
593
594             struct adouble ad;
595             struct stat st;
596             if (stat(to.p_path, &st) != 0) {
597                 badcp = rval = 1;
598                 break;
599             }
600             ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
601             if (ad_open_metadata(to.p_path, 0, O_RDWR | O_CREAT, &ad) != 0) {
602                 ERROR("Error opening adouble for: %s", to.p_path);
603             }
604             ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp);
605             ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
606             ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
607             ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
608             ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
609             ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
610             ad_flush(&ad);
611             ad_close_metadata(&ad);
612             umask(omask);
613         }
614         break;
615     }
616     if (vflag && !badcp)
617         (void)printf("%s -> %s\n", path, to.p_path);
618
619     return 0;
620 }
621
622 /* Memory strategy threshold, in pages: if physmem is larger then this, use a large buffer */
623 #define PHYSPAGES_THRESHOLD (32*1024)
624
625 /* Maximum buffer size in bytes - do not allow it to grow larger than this */
626 #define BUFSIZE_MAX (2*1024*1024)
627
628 /* Small (default) buffer size in bytes. It's inefficient for this to be smaller than MAXPHYS */
629 #define MAXPHYS (64 * 1024)
630 #define BUFSIZE_SMALL (MAXPHYS)
631
632 static int ftw_copy_file(const struct FTW *entp,
633                          const char *spath,
634                          const struct stat *sp,
635                          int dne)
636 {
637     static char *buf = NULL;
638     static size_t bufsize;
639     ssize_t wcount;
640     size_t wresid;
641     off_t wtotal;
642     int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
643     char *bufp;
644     char *p;
645
646     if ((from_fd = open(spath, O_RDONLY, 0)) == -1) {
647         SLOG("%s: %s", spath, strerror(errno));
648         return (1);
649     }
650
651     /*
652      * If the file exists and we're interactive, verify with the user.
653      * If the file DNE, set the mode to be the from file, minus setuid
654      * bits, modified by the umask; arguably wrong, but it makes copying
655      * executables work right and it's been that way forever.  (The
656      * other choice is 666 or'ed with the execute bits on the from file
657      * modified by the umask.)
658      */
659     if (!dne) {
660 #define YESNO "(y/n [n]) "
661         if (nflag) {
662             if (vflag)
663                 printf("%s not overwritten\n", to.p_path);
664             (void)close(from_fd);
665             return (0);
666         } else if (iflag) {
667             (void)fprintf(stderr, "overwrite %s? %s",
668                           to.p_path, YESNO);
669             checkch = ch = getchar();
670             while (ch != '\n' && ch != EOF)
671                 ch = getchar();
672             if (checkch != 'y' && checkch != 'Y') {
673                 (void)close(from_fd);
674                 (void)fprintf(stderr, "not overwritten\n");
675                 return (1);
676             }
677         }
678
679         if (fflag) {
680             /* remove existing destination file name,
681              * create a new file  */
682             (void)unlink(to.p_path);
683             (void)dvolume.volume.vfs->vfs_deletefile(&dvolume.volume, -1, to.p_path);
684             to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
685                          sp->st_mode & ~(S_ISUID | S_ISGID));
686         } else {
687             /* overwrite existing destination file name */
688             to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
689         }
690     } else {
691         to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
692                      sp->st_mode & ~(S_ISUID | S_ISGID));
693     }
694
695     if (to_fd == -1) {
696         SLOG("%s: %s", to.p_path, strerror(errno));
697         (void)close(from_fd);
698         return (1);
699     }
700
701     rval = 0;
702
703     /*
704      * Mmap and write if less than 8M (the limit is so we don't totally
705      * trash memory on big files.  This is really a minor hack, but it
706      * wins some CPU back.
707      * Some filesystems, such as smbnetfs, don't support mmap,
708      * so this is a best-effort attempt.
709      */
710
711     if (S_ISREG(sp->st_mode) && sp->st_size > 0 &&
712         sp->st_size <= 8 * 1024 * 1024 &&
713         (p = mmap(NULL, (size_t)sp->st_size, PROT_READ,
714                   MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
715         wtotal = 0;
716         for (bufp = p, wresid = sp->st_size; ;
717              bufp += wcount, wresid -= (size_t)wcount) {
718             wcount = write(to_fd, bufp, wresid);
719             if (wcount <= 0)
720                 break;
721             wtotal += wcount;
722             if (wcount >= (ssize_t)wresid)
723                 break;
724         }
725         if (wcount != (ssize_t)wresid) {
726             SLOG("%s: %s", to.p_path, strerror(errno));
727             rval = 1;
728         }
729         /* Some systems don't unmap on close(2). */
730         if (munmap(p, sp->st_size) < 0) {
731             SLOG("%s: %s", spath, strerror(errno));
732             rval = 1;
733         }
734     } else {
735         if (buf == NULL) {
736             /*
737              * Note that buf and bufsize are static. If
738              * malloc() fails, it will fail at the start
739              * and not copy only some files.
740              */
741             if (sysconf(_SC_PHYS_PAGES) >
742                 PHYSPAGES_THRESHOLD)
743                 bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
744             else
745                 bufsize = BUFSIZE_SMALL;
746             buf = malloc(bufsize);
747             if (buf == NULL)
748                 ERROR("Not enough memory");
749
750         }
751         wtotal = 0;
752         while ((rcount = read(from_fd, buf, bufsize)) > 0) {
753             for (bufp = buf, wresid = rcount; ;
754                  bufp += wcount, wresid -= wcount) {
755                 wcount = write(to_fd, bufp, wresid);
756                 if (wcount <= 0)
757                     break;
758                 wtotal += wcount;
759                 if (wcount >= (ssize_t)wresid)
760                     break;
761             }
762             if (wcount != (ssize_t)wresid) {
763                 SLOG("%s: %s", to.p_path, strerror(errno));
764                 rval = 1;
765                 break;
766             }
767         }
768         if (rcount < 0) {
769             SLOG("%s: %s", spath, strerror(errno));
770             rval = 1;
771         }
772     }
773
774     /*
775      * Don't remove the target even after an error.  The target might
776      * not be a regular file, or its attributes might be important,
777      * or its contents might be irreplaceable.  It would only be safe
778      * to remove it if we created it and its length is 0.
779      */
780
781     if (pflag && setfile(sp, to_fd))
782         rval = 1;
783     if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
784         rval = 1;
785     if (close(to_fd)) {
786         SLOG("%s: %s", to.p_path, strerror(errno));
787         rval = 1;
788     }
789
790     (void)close(from_fd);
791
792     return (rval);
793 }
794
795 static int ftw_copy_link(const struct FTW *p,
796                          const char *spath,
797                          const struct stat *sstp,
798                          int exists)
799 {
800     int len;
801     char llink[PATH_MAX];
802
803     if ((len = readlink(spath, llink, sizeof(llink) - 1)) == -1) {
804         SLOG("readlink: %s: %s", spath, strerror(errno));
805         return (1);
806     }
807     llink[len] = '\0';
808     if (exists && unlink(to.p_path)) {
809         SLOG("unlink: %s: %s", to.p_path, strerror(errno));
810         return (1);
811     }
812     if (symlink(llink, to.p_path)) {
813         SLOG("symlink: %s: %s", llink, strerror(errno));
814         return (1);
815     }
816     return (pflag ? setfile(sstp, -1) : 0);
817 }
818
819 static int setfile(const struct stat *fs, int fd)
820 {
821     static struct timeval tv[2];
822     struct stat ts;
823     int rval, gotstat, islink, fdval;
824     mode_t mode;
825
826     rval = 0;
827     fdval = fd != -1;
828     islink = !fdval && S_ISLNK(fs->st_mode);
829     mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
830
831     TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
832     TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
833     if (utimes(to.p_path, tv)) {
834         SLOG("utimes: %s", to.p_path);
835         rval = 1;
836     }
837     if (fdval ? fstat(fd, &ts) :
838         (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
839         gotstat = 0;
840     else {
841         gotstat = 1;
842         ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
843             S_IRWXU | S_IRWXG | S_IRWXO;
844     }
845     /*
846      * Changing the ownership probably won't succeed, unless we're root
847      * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
848      * the mode; current BSD behavior is to remove all setuid bits on
849      * chown.  If chown fails, lose setuid/setgid bits.
850      */
851     if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
852         if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
853             (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
854              chown(to.p_path, fs->st_uid, fs->st_gid))) {
855             if (errno != EPERM) {
856                 SLOG("chown: %s: %s", to.p_path, strerror(errno));
857                 rval = 1;
858             }
859             mode &= ~(S_ISUID | S_ISGID);
860         }
861
862     if (!gotstat || mode != ts.st_mode)
863         if (fdval ? fchmod(fd, mode) : chmod(to.p_path, mode)) {
864             SLOG("chmod: %s: %s", to.p_path, strerror(errno));
865             rval = 1;
866         }
867
868 #ifdef HAVE_ST_FLAGS
869     if (!gotstat || fs->st_flags != ts.st_flags)
870         if (fdval ?
871             fchflags(fd, fs->st_flags) :
872             (islink ? lchflags(to.p_path, fs->st_flags) :
873              chflags(to.p_path, fs->st_flags))) {
874             SLOG("chflags: %s: %s", to.p_path, strerror(errno));
875             rval = 1;
876         }
877 #endif
878
879     return (rval);
880 }
881
882 static int preserve_fd_acls(int source_fd, int dest_fd)
883 {
884 #if 0
885     acl_t acl;
886     acl_type_t acl_type;
887     int acl_supported = 0, ret, trivial;
888
889     ret = fpathconf(source_fd, _PC_ACL_NFS4);
890     if (ret > 0 ) {
891         acl_supported = 1;
892         acl_type = ACL_TYPE_NFS4;
893     } else if (ret < 0 && errno != EINVAL) {
894         warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
895         return (1);
896     }
897     if (acl_supported == 0) {
898         ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
899         if (ret > 0 ) {
900             acl_supported = 1;
901             acl_type = ACL_TYPE_ACCESS;
902         } else if (ret < 0 && errno != EINVAL) {
903             warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
904                  to.p_path);
905             return (1);
906         }
907     }
908     if (acl_supported == 0)
909         return (0);
910
911     acl = acl_get_fd_np(source_fd, acl_type);
912     if (acl == NULL) {
913         warn("failed to get acl entries while setting %s", to.p_path);
914         return (1);
915     }
916     if (acl_is_trivial_np(acl, &trivial)) {
917         warn("acl_is_trivial() failed for %s", to.p_path);
918         acl_free(acl);
919         return (1);
920     }
921     if (trivial) {
922         acl_free(acl);
923         return (0);
924     }
925     if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
926         warn("failed to set acl entries for %s", to.p_path);
927         acl_free(acl);
928         return (1);
929     }
930     acl_free(acl);
931 #endif
932     return (0);
933 }
934
935 static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
936 {
937 #if 0
938     acl_t (*aclgetf)(const char *, acl_type_t);
939     int (*aclsetf)(const char *, acl_type_t, acl_t);
940     struct acl *aclp;
941     acl_t acl;
942     acl_type_t acl_type;
943     int acl_supported = 0, ret, trivial;
944
945     ret = pathconf(source_dir, _PC_ACL_NFS4);
946     if (ret > 0) {
947         acl_supported = 1;
948         acl_type = ACL_TYPE_NFS4;
949     } else if (ret < 0 && errno != EINVAL) {
950         warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
951         return (1);
952     }
953     if (acl_supported == 0) {
954         ret = pathconf(source_dir, _PC_ACL_EXTENDED);
955         if (ret > 0) {
956             acl_supported = 1;
957             acl_type = ACL_TYPE_ACCESS;
958         } else if (ret < 0 && errno != EINVAL) {
959             warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
960                  source_dir);
961             return (1);
962         }
963     }
964     if (acl_supported == 0)
965         return (0);
966
967     /*
968      * If the file is a link we will not follow it
969      */
970     if (S_ISLNK(fs->st_mode)) {
971         aclgetf = acl_get_link_np;
972         aclsetf = acl_set_link_np;
973     } else {
974         aclgetf = acl_get_file;
975         aclsetf = acl_set_file;
976     }
977     if (acl_type == ACL_TYPE_ACCESS) {
978         /*
979          * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
980          * size ACL will be returned. So it is not safe to simply
981          * check the pointer to see if the default ACL is present.
982          */
983         acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
984         if (acl == NULL) {
985             warn("failed to get default acl entries on %s",
986                  source_dir);
987             return (1);
988         }
989         aclp = &acl->ats_acl;
990         if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
991                                           ACL_TYPE_DEFAULT, acl) < 0) {
992             warn("failed to set default acl entries on %s",
993                  dest_dir);
994             acl_free(acl);
995             return (1);
996         }
997         acl_free(acl);
998     }
999     acl = aclgetf(source_dir, acl_type);
1000     if (acl == NULL) {
1001         warn("failed to get acl entries on %s", source_dir);
1002         return (1);
1003     }
1004     if (acl_is_trivial_np(acl, &trivial)) {
1005         warn("acl_is_trivial() failed on %s", source_dir);
1006         acl_free(acl);
1007         return (1);
1008     }
1009     if (trivial) {
1010         acl_free(acl);
1011         return (0);
1012     }
1013     if (aclsetf(dest_dir, acl_type, acl) < 0) {
1014         warn("failed to set acl entries on %s", dest_dir);
1015         acl_free(acl);
1016         return (1);
1017     }
1018     acl_free(acl);
1019 #endif
1020     return (0);
1021 }