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