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