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