]> arthur.barton.de Git - netatalk.git/blob - bin/ad/ad_cp.c
Convert names for all fs-objects and proper signal handling
[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, lflag, 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 [-P]] [-pvf] <source_file> <target_file>\n"
176         "Usage: ad cp [-R [-P]] [-pvfx] <source_file [source_file ...]> <target_directory>\n"
177         );
178     exit(EXIT_FAILURE);
179 }
180
181 int ad_cp(int argc, char *argv[])
182 {
183     struct stat to_stat, tmp_stat;
184     int r, ch, have_trailing_slash;
185     char *target;
186 #if 0
187     afpvol_t srcvol;
188     afpvol_t dstvol;
189 #endif
190
191     ppdid = pdid = htonl(1);
192     did = htonl(2);
193
194     while ((ch = getopt(argc, argv, "Rafilnpvx")) != -1)
195         switch (ch) {
196         case 'R':
197             Rflag = 1;
198             break;
199         case 'a':
200             pflag = 1;
201             Rflag = 1;
202             break;
203         case 'f':
204             fflag = 1;
205             iflag = nflag = 0;
206             break;
207         case 'i':
208             iflag = 1;
209             fflag = nflag = 0;
210             break;
211         case 'l':
212             lflag = 1;
213             break;
214         case 'n':
215             nflag = 1;
216             fflag = iflag = 0;
217             break;
218         case 'p':
219             pflag = 1;
220             break;
221         case 'v':
222             vflag = 1;
223             break;
224         case 'x':
225             ftw_options |= FTW_MOUNT;
226             break;
227         default:
228             usage_cp();
229             break;
230         }
231     argc -= optind;
232     argv += optind;
233
234     if (argc < 2)
235         usage_cp();
236
237     set_signal();
238     cnid_init();
239
240     /* Save the target base in "to". */
241     target = argv[--argc];
242     if ((strlcpy(to.p_path, target, PATH_MAX)) >= PATH_MAX)
243         ERROR("%s: name too long", target);
244
245     to.p_end = to.p_path + strlen(to.p_path);
246     if (to.p_path == to.p_end) {
247         *to.p_end++ = '.';
248         *to.p_end = 0;
249     }
250     have_trailing_slash = (to.p_end[-1] == '/');
251     if (have_trailing_slash)
252         STRIP_TRAILING_SLASH(to);
253     to.target_end = to.p_end;
254
255     /* Set end of argument list */
256     argv[argc] = NULL;
257
258     /*
259      * Cp has two distinct cases:
260      *
261      * cp [-R] source target
262      * cp [-R] source1 ... sourceN directory
263      *
264      * In both cases, source can be either a file or a directory.
265      *
266      * In (1), the target becomes a copy of the source. That is, if the
267      * source is a file, the target will be a file, and likewise for
268      * directories.
269      *
270      * In (2), the real target is not directory, but "directory/source".
271      */
272     r = stat(to.p_path, &to_stat);
273     if (r == -1 && errno != ENOENT)
274         ERROR("%s", to.p_path);
275     if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
276         /*
277          * Case (1).  Target is not a directory.
278          */
279         if (argc > 1)
280             ERROR("%s is not a directory", to.p_path);
281
282         /*
283          * Need to detect the case:
284          *cp -R dir foo
285          * Where dir is a directory and foo does not exist, where
286          * we want pathname concatenations turned on but not for
287          * the initial mkdir().
288          */
289         if (r == -1) {
290             lstat(*argv, &tmp_stat);
291
292             if (S_ISDIR(tmp_stat.st_mode) && Rflag)
293                 type = DIR_TO_DNE;
294             else
295                 type = FILE_TO_FILE;
296         } else
297             type = FILE_TO_FILE;
298
299         if (have_trailing_slash && type == FILE_TO_FILE) {
300             if (r == -1)
301                 ERROR("directory %s does not exist", to.p_path);
302             else
303                 ERROR("%s is not a directory", to.p_path);
304         }
305     } else
306         /*
307          * Case (2).  Target is a directory.
308          */
309         type = FILE_TO_DIR;
310
311     /*
312      * Keep an inverted copy of the umask, for use in correcting
313      * permissions on created directories when not using -p.
314      */
315     mask = ~umask(0777);
316     umask(~mask);
317
318 #if 0
319     /* Inhereting perms in ad_mkdir etc requires this */
320     ad_setfuid(0);
321 #endif
322
323     /* Load .volinfo file for destination*/
324     openvol(to.p_path, &dvolume);
325
326     for (int i = 0; argv[i] != NULL; i++) { 
327         /* Load .volinfo file for source */
328         openvol(argv[i], &svolume);
329
330         if (nftw(argv[i], copy, upfunc, 20, ftw_options) == -1) {
331             if (alarmed) {
332                 SLOG("...break");
333             } else {
334                 SLOG("Error: %s: %s", argv[i], strerror(errno));
335             }
336             closevol(&svolume);
337             closevol(&dvolume);
338         }
339     }
340     return rval;
341 }
342
343 static int copy(const char *path,
344                 const struct stat *statp,
345                 int tflag,
346                 struct FTW *ftw)
347 {
348     static int base = 0;
349
350     struct stat to_stat;
351     int dne;
352     size_t nlen;
353     const char *p;
354     char *target_mid;
355
356     if (alarmed)
357         return -1;
358
359     const char *dir = strrchr(path, '/');
360     if (dir == NULL)
361         dir = path;
362     else
363         dir++;
364     if (check_netatalk_dirs(dir) != NULL)
365         return FTW_SKIP_SUBTREE;
366
367     /*
368      * If we are in case (2) above, we need to append the
369      * source name to the target name.
370      */
371     if (type != FILE_TO_FILE) {
372         /*
373          * Need to remember the roots of traversals to create
374          * correct pathnames.  If there's a directory being
375          * copied to a non-existent directory, e.g.
376          *     cp -R a/dir noexist
377          * the resulting path name should be noexist/foo, not
378          * noexist/dir/foo (where foo is a file in dir), which
379          * is the case where the target exists.
380          *
381          * Also, check for "..".  This is for correct path
382          * concatenation for paths ending in "..", e.g.
383          *     cp -R .. /tmp
384          * Paths ending in ".." are changed to ".".  This is
385          * tricky, but seems the easiest way to fix the problem.
386          *
387          * XXX
388          * Since the first level MUST be FTS_ROOTLEVEL, base
389          * is always initialized.
390          */
391         if (ftw->level == 0) {
392             if (type != DIR_TO_DNE) {
393                 base = ftw->base;
394
395                 if (strcmp(&path[base], "..") == 0)
396                     base += 1;
397             } else
398                 base = strlen(path);
399         }
400
401         p = &path[base];
402         nlen = strlen(path) - base;
403         target_mid = to.target_end;
404         if (*p != '/' && target_mid[-1] != '/')
405             *target_mid++ = '/';
406         *target_mid = 0;
407         if (target_mid - to.p_path + nlen >= PATH_MAX) {
408             SLOG("%s%s: name too long (not copied)", to.p_path, p);
409             badcp = rval = 1;
410             return 0;
411         }
412         (void)strncat(target_mid, p, nlen);
413         to.p_end = target_mid + nlen;
414         *to.p_end = 0;
415         STRIP_TRAILING_SLASH(to);
416     }
417
418     /* Not an error but need to remember it happened */
419     if (stat(to.p_path, &to_stat) == -1)
420         dne = 1;
421     else {
422         if (to_stat.st_dev == statp->st_dev &&
423             to_stat.st_ino == statp->st_ino) {
424             SLOG("%s and %s are identical (not copied).", to.p_path, path);
425             badcp = rval = 1;
426             if (S_ISDIR(statp->st_mode))
427                 /* without using glibc extension FTW_ACTIONRETVAL cant handle this */
428                 return FTW_SKIP_SUBTREE;
429             return 0;
430         }
431         if (!S_ISDIR(statp->st_mode) &&
432             S_ISDIR(to_stat.st_mode)) {
433             SLOG("cannot overwrite directory %s with "
434                 "non-directory %s",
435                 to.p_path, path);
436                 badcp = rval = 1;
437                 return 0;
438         }
439         dne = 0;
440     }
441
442     /* Convert basename to appropiate volume encoding */
443     if (dvolume.volinfo.v_path) {
444         if ((convert_dots_encoding(&svolume, &dvolume, to.p_path, MAXPATHLEN)) == -1) {
445             SLOG("Error converting name for %s", to.p_path);
446             badcp = rval = 1;
447             return -1;
448         }
449     }
450
451     switch (statp->st_mode & S_IFMT) {
452     case S_IFLNK:
453         if (ftw_copy_link(ftw, path, statp, !dne))
454             badcp = rval = 1;
455         break;
456     case S_IFDIR:
457         if (!Rflag) {
458             SLOG("%s is a directory", path);
459             badcp = rval = 1;
460             return -1;
461         }
462         /*
463          * If the directory doesn't exist, create the new
464          * one with the from file mode plus owner RWX bits,
465          * modified by the umask.  Trade-off between being
466          * able to write the directory (if from directory is
467          * 555) and not causing a permissions race.  If the
468          * umask blocks owner writes, we fail..
469          */
470         if (dne) {
471             if (mkdir(to.p_path, statp->st_mode | S_IRWXU) < 0)
472                 ERROR("mkdir: %s: %s", to.p_path, strerror(errno));
473         } else if (!S_ISDIR(to_stat.st_mode)) {
474             errno = ENOTDIR;
475             ERROR("%s", to.p_path);
476         }
477
478         /* Create ad dir and copy ".Parent" */
479         if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
480
481             /* Create ".AppleDouble" dir */
482             mode_t omask = umask(0);
483             bstring addir = bfromcstr(to.p_path);
484             bcatcstr(addir, "/.AppleDouble");
485             mkdir(cfrombstr(addir), 02777);
486             bdestroy(addir);
487
488             if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
489                 /* copy ".Parent" file */
490                 SLOG("Copying adouble for %s -> %s", path, to.p_path);
491                 if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
492                     SLOG("Error copying adouble for %s -> %s", path, to.p_path);
493                     badcp = rval = 1;
494                     break;
495                 }
496             }
497
498             /* Get CNID of Parent and add new childir to CNID database */
499             ppdid = pdid;
500             did = cnid_for_path(&dvolume.volinfo, &dvolume.volume, to.p_path, &pdid);
501
502             struct adouble ad;
503             struct stat st;
504             if (stat(to.p_path, &st) != 0) {
505                 badcp = rval = 1;
506                 break;
507             }
508             ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
509             if (ad_open_metadata(to.p_path, ADFLAGS_DIR, O_RDWR | O_CREAT, &ad) != 0) {
510                 ERROR("Error opening adouble for: %s", to.p_path);
511             }
512             SLOG("Setting CNID %u for %s", ntohl(did), to.p_path);
513             ad_setid( &ad, st.st_dev, st.st_ino, did, pdid, dvolume.db_stamp);
514             ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
515             ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
516             ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
517             ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
518             ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
519             ad_flush(&ad);
520             ad_close_metadata(&ad);
521
522             umask(omask);
523         }
524
525         if (pflag) {
526             if (setfile(statp, -1))
527                 rval = 1;
528 #if 0
529             if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0)
530                 rval = 1;
531 #endif
532         }
533         break;
534
535     case S_IFBLK:
536     case S_IFCHR:
537         SLOG("%s is a device file (not copied).", path);
538         break;
539     case S_IFSOCK:
540         SLOG("%s is a socket (not copied).", path);
541         break;
542     case S_IFIFO:
543         SLOG("%s is a FIFO (not copied).", path);
544         break;
545     default:
546         if (ftw_copy_file(ftw, path, statp, dne))
547             badcp = rval = 1;
548
549         if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
550
551             mode_t omask = umask(0);
552             if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
553                 /* copy ad-file */
554                 if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
555                     SLOG("Error copying adouble for %s -> %s", path, to.p_path);
556                     badcp = rval = 1;
557                     break;
558                 }
559             }
560
561             /* Get CNID of Parent and add new childir to CNID database */
562             pdid = did;
563             cnid_t cnid = cnid_for_path(&dvolume.volinfo, &dvolume.volume, to.p_path, &did);
564
565             struct adouble ad;
566             struct stat st;
567             if (stat(to.p_path, &st) != 0) {
568                 badcp = rval = 1;
569                 break;
570             }
571             ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
572             if (ad_open_metadata(to.p_path, 0, O_RDWR | O_CREAT, &ad) != 0) {
573                 ERROR("Error opening adouble for: %s", to.p_path);
574             }
575             SLOG("setid: DID: %u, CNID: %u, %s", ntohl(did), ntohl(cnid), to.p_path);
576             ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp);
577             ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
578             ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
579             ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
580             ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
581             ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
582             ad_flush(&ad);
583             ad_close_metadata(&ad);
584             umask(omask);
585         }
586         break;
587     }
588     if (vflag && !badcp)
589         (void)printf("%s -> %s\n", path, to.p_path);
590
591     return 0;
592 }
593
594 /* Memory strategy threshold, in pages: if physmem is larger then this, use a large buffer */
595 #define PHYSPAGES_THRESHOLD (32*1024)
596
597 /* Maximum buffer size in bytes - do not allow it to grow larger than this */
598 #define BUFSIZE_MAX (2*1024*1024)
599
600 /* Small (default) buffer size in bytes. It's inefficient for this to be smaller than MAXPHYS */
601 #define MAXPHYS (64 * 1024)
602 #define BUFSIZE_SMALL (MAXPHYS)
603
604 static int ftw_copy_file(const struct FTW *entp,
605                          const char *spath,
606                          const struct stat *sp,
607                          int dne)
608 {
609     static char *buf = NULL;
610     static size_t bufsize;
611     ssize_t wcount;
612     size_t wresid;
613     off_t wtotal;
614     int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
615     char *bufp;
616     char *p;
617
618     if ((from_fd = open(spath, O_RDONLY, 0)) == -1) {
619         SLOG("%s: %s", spath, strerror(errno));
620         return (1);
621     }
622
623     /*
624      * If the file exists and we're interactive, verify with the user.
625      * If the file DNE, set the mode to be the from file, minus setuid
626      * bits, modified by the umask; arguably wrong, but it makes copying
627      * executables work right and it's been that way forever.  (The
628      * other choice is 666 or'ed with the execute bits on the from file
629      * modified by the umask.)
630      */
631     if (!dne) {
632 #define YESNO "(y/n [n]) "
633         if (nflag) {
634             if (vflag)
635                 printf("%s not overwritten\n", to.p_path);
636             (void)close(from_fd);
637             return (0);
638         } else if (iflag) {
639             (void)fprintf(stderr, "overwrite %s? %s", 
640                           to.p_path, YESNO);
641             checkch = ch = getchar();
642             while (ch != '\n' && ch != EOF)
643                 ch = getchar();
644             if (checkch != 'y' && checkch != 'Y') {
645                 (void)close(from_fd);
646                 (void)fprintf(stderr, "not overwritten\n");
647                 return (1);
648             }
649         }
650         
651         if (fflag) {
652             /* remove existing destination file name, 
653              * create a new file  */
654             (void)unlink(to.p_path);
655             (void)dvolume.volume.vfs->vfs_deletefile(&dvolume.volume, -1, to.p_path);
656             if (!lflag)
657                 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
658                              sp->st_mode & ~(S_ISUID | S_ISGID));
659         } else {
660             if (!lflag)
661                 /* overwrite existing destination file name */
662                 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
663         }
664     } else {
665         if (!lflag)
666             to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
667                          sp->st_mode & ~(S_ISUID | S_ISGID));
668     }
669     
670     if (to_fd == -1) {
671         SLOG("%s: %s", to.p_path, strerror(errno));
672         (void)close(from_fd);
673         return (1);
674     }
675
676     rval = 0;
677
678     if (!lflag) {
679         /*
680          * Mmap and write if less than 8M (the limit is so we don't totally
681          * trash memory on big files.  This is really a minor hack, but it
682          * wins some CPU back.
683          * Some filesystems, such as smbnetfs, don't support mmap,
684          * so this is a best-effort attempt.
685          */
686
687         if (S_ISREG(sp->st_mode) && sp->st_size > 0 &&
688             sp->st_size <= 8 * 1024 * 1024 &&
689             (p = mmap(NULL, (size_t)sp->st_size, PROT_READ,
690                       MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
691             wtotal = 0;
692             for (bufp = p, wresid = sp->st_size; ;
693                  bufp += wcount, wresid -= (size_t)wcount) {
694                 wcount = write(to_fd, bufp, wresid);
695                 if (wcount <= 0)
696                     break;
697                 wtotal += wcount;
698                 if (wcount >= (ssize_t)wresid)
699                     break;
700             }
701             if (wcount != (ssize_t)wresid) {
702                 SLOG("%s: %s", to.p_path, strerror(errno));
703                 rval = 1;
704             }
705             /* Some systems don't unmap on close(2). */
706             if (munmap(p, sp->st_size) < 0) {
707                 SLOG("%s: %s", spath, strerror(errno));
708                 rval = 1;
709             }
710         } else {
711             if (buf == NULL) {
712                 /*
713                  * Note that buf and bufsize are static. If
714                  * malloc() fails, it will fail at the start
715                  * and not copy only some files. 
716                  */ 
717                 if (sysconf(_SC_PHYS_PAGES) > 
718                     PHYSPAGES_THRESHOLD)
719                     bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
720                 else
721                     bufsize = BUFSIZE_SMALL;
722                 buf = malloc(bufsize);
723                 if (buf == NULL)
724                     ERROR("Not enough memory");
725
726             }
727             wtotal = 0;
728             while ((rcount = read(from_fd, buf, bufsize)) > 0) {
729                 for (bufp = buf, wresid = rcount; ;
730                      bufp += wcount, wresid -= wcount) {
731                     wcount = write(to_fd, bufp, wresid);
732                     if (wcount <= 0)
733                         break;
734                     wtotal += wcount;
735                     if (wcount >= (ssize_t)wresid)
736                         break;
737                 }
738                 if (wcount != (ssize_t)wresid) {
739                     SLOG("%s: %s", to.p_path, strerror(errno));
740                     rval = 1;
741                     break;
742                 }
743             }
744             if (rcount < 0) {
745                 SLOG("%s: %s", spath, strerror(errno));
746                 rval = 1;
747             }
748         }
749     } else {
750         if (link(spath, to.p_path)) {
751             SLOG("%s", to.p_path);
752             rval = 1;
753         }
754     }
755     
756     /*
757      * Don't remove the target even after an error.  The target might
758      * not be a regular file, or its attributes might be important,
759      * or its contents might be irreplaceable.  It would only be safe
760      * to remove it if we created it and its length is 0.
761      */
762
763     if (!lflag) {
764         if (pflag && setfile(sp, to_fd))
765             rval = 1;
766         if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
767             rval = 1;
768         if (close(to_fd)) {
769             SLOG("%s: %s", to.p_path, strerror(errno));
770             rval = 1;
771         }
772     }
773
774     (void)close(from_fd);
775
776     return (rval);
777 }
778
779 static int ftw_copy_link(const struct FTW *p,
780                          const char *spath,
781                          const struct stat *sstp,
782                          int exists)
783 {
784     int len;
785     char llink[PATH_MAX];
786
787     if ((len = readlink(spath, llink, sizeof(llink) - 1)) == -1) {
788         SLOG("readlink: %s: %s", spath, strerror(errno));
789         return (1);
790     }
791     llink[len] = '\0';
792     if (exists && unlink(to.p_path)) {
793         SLOG("unlink: %s: %s", to.p_path, strerror(errno));
794         return (1);
795     }
796     if (symlink(llink, to.p_path)) {
797         SLOG("symlink: %s: %s", llink, strerror(errno));
798         return (1);
799     }
800     return (pflag ? setfile(sstp, -1) : 0);
801 }
802
803 static int setfile(const struct stat *fs, int fd)
804 {
805     static struct timeval tv[2];
806     struct stat ts;
807     int rval, gotstat, islink, fdval;
808     mode_t mode;
809
810     rval = 0;
811     fdval = fd != -1;
812     islink = !fdval && S_ISLNK(fs->st_mode);
813     mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
814
815     TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
816     TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
817     if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) {
818         SLOG("%sutimes: %s", islink ? "l" : "", to.p_path);
819         rval = 1;
820     }
821     if (fdval ? fstat(fd, &ts) :
822         (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
823         gotstat = 0;
824     else {
825         gotstat = 1;
826         ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
827             S_IRWXU | S_IRWXG | S_IRWXO;
828     }
829     /*
830      * Changing the ownership probably won't succeed, unless we're root
831      * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
832      * the mode; current BSD behavior is to remove all setuid bits on
833      * chown.  If chown fails, lose setuid/setgid bits.
834      */
835     if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
836         if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
837             (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
838              chown(to.p_path, fs->st_uid, fs->st_gid))) {
839             if (errno != EPERM) {
840                 SLOG("chown: %s: %s", to.p_path, strerror(errno));
841                 rval = 1;
842             }
843             mode &= ~(S_ISUID | S_ISGID);
844         }
845
846     if (!gotstat || mode != ts.st_mode)
847         if (fdval ? fchmod(fd, mode) : chmod(to.p_path, mode)) {
848             SLOG("chmod: %s: %s", to.p_path, strerror(errno));
849             rval = 1;
850         }
851
852 #ifdef HAVE_ST_FLAGS
853     if (!gotstat || fs->st_flags != ts.st_flags)
854         if (fdval ?
855             fchflags(fd, fs->st_flags) :
856             (islink ? lchflags(to.p_path, fs->st_flags) :
857              chflags(to.p_path, fs->st_flags))) {
858             SLOG("chflags: %s: %s", to.p_path, strerror(errno));
859             rval = 1;
860         }
861 #endif
862
863     return (rval);
864 }
865
866 static int preserve_fd_acls(int source_fd, int dest_fd)
867 {
868 #if 0
869     acl_t acl;
870     acl_type_t acl_type;
871     int acl_supported = 0, ret, trivial;
872
873     ret = fpathconf(source_fd, _PC_ACL_NFS4);
874     if (ret > 0 ) {
875         acl_supported = 1;
876         acl_type = ACL_TYPE_NFS4;
877     } else if (ret < 0 && errno != EINVAL) {
878         warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
879         return (1);
880     }
881     if (acl_supported == 0) {
882         ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
883         if (ret > 0 ) {
884             acl_supported = 1;
885             acl_type = ACL_TYPE_ACCESS;
886         } else if (ret < 0 && errno != EINVAL) {
887             warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
888                  to.p_path);
889             return (1);
890         }
891     }
892     if (acl_supported == 0)
893         return (0);
894
895     acl = acl_get_fd_np(source_fd, acl_type);
896     if (acl == NULL) {
897         warn("failed to get acl entries while setting %s", to.p_path);
898         return (1);
899     }
900     if (acl_is_trivial_np(acl, &trivial)) {
901         warn("acl_is_trivial() failed for %s", to.p_path);
902         acl_free(acl);
903         return (1);
904     }
905     if (trivial) {
906         acl_free(acl);
907         return (0);
908     }
909     if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
910         warn("failed to set acl entries for %s", to.p_path);
911         acl_free(acl);
912         return (1);
913     }
914     acl_free(acl);
915 #endif
916     return (0);
917 }
918
919 static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
920 {
921 #if 0
922     acl_t (*aclgetf)(const char *, acl_type_t);
923     int (*aclsetf)(const char *, acl_type_t, acl_t);
924     struct acl *aclp;
925     acl_t acl;
926     acl_type_t acl_type;
927     int acl_supported = 0, ret, trivial;
928
929     ret = pathconf(source_dir, _PC_ACL_NFS4);
930     if (ret > 0) {
931         acl_supported = 1;
932         acl_type = ACL_TYPE_NFS4;
933     } else if (ret < 0 && errno != EINVAL) {
934         warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
935         return (1);
936     }
937     if (acl_supported == 0) {
938         ret = pathconf(source_dir, _PC_ACL_EXTENDED);
939         if (ret > 0) {
940             acl_supported = 1;
941             acl_type = ACL_TYPE_ACCESS;
942         } else if (ret < 0 && errno != EINVAL) {
943             warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
944                  source_dir);
945             return (1);
946         }
947     }
948     if (acl_supported == 0)
949         return (0);
950
951     /*
952      * If the file is a link we will not follow it
953      */
954     if (S_ISLNK(fs->st_mode)) {
955         aclgetf = acl_get_link_np;
956         aclsetf = acl_set_link_np;
957     } else {
958         aclgetf = acl_get_file;
959         aclsetf = acl_set_file;
960     }
961     if (acl_type == ACL_TYPE_ACCESS) {
962         /*
963          * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
964          * size ACL will be returned. So it is not safe to simply
965          * check the pointer to see if the default ACL is present.
966          */
967         acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
968         if (acl == NULL) {
969             warn("failed to get default acl entries on %s",
970                  source_dir);
971             return (1);
972         }
973         aclp = &acl->ats_acl;
974         if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
975                                           ACL_TYPE_DEFAULT, acl) < 0) {
976             warn("failed to set default acl entries on %s",
977                  dest_dir);
978             acl_free(acl);
979             return (1);
980         }
981         acl_free(acl);
982     }
983     acl = aclgetf(source_dir, acl_type);
984     if (acl == NULL) {
985         warn("failed to get acl entries on %s", source_dir);
986         return (1);
987     }
988     if (acl_is_trivial_np(acl, &trivial)) {
989         warn("acl_is_trivial() failed on %s", source_dir);
990         acl_free(acl);
991         return (1);
992     }
993     if (trivial) {
994         acl_free(acl);
995         return (0);
996     }
997     if (aclsetf(dest_dir, acl_type, acl) < 0) {
998         warn("failed to set acl entries on %s", dest_dir);
999         acl_free(acl);
1000         return (1);
1001     }
1002     acl_free(acl);
1003 #endif
1004     return (0);
1005 }