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.
6 * This code is derived from software contributed to Berkeley by
7 * David Hitz of Auspex Systems Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
35 * Cp copies source files to target files.
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.
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.
51 #endif /* HAVE_CONFIG_H */
53 #include <sys/types.h>
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>
76 #define STRIP_TRAILING_SLASH(p) { \
77 while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \
81 static char emptystring[] = "";
83 PATH_T to = { to.p_path, emptystring, "" };
84 enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
86 int fflag, iflag, nflag, pflag, vflag;
89 cnid_t ppdid, pdid, did; /* current dir CNID and parent did*/
91 static afpvol_t svolume, dvolume;
94 static volatile sig_atomic_t alarmed;
95 static int badcp, rval;
96 static int ftw_options = FTW_MOUNT | FTW_PHYS | FTW_ACTIONRETVAL;
98 /* Forward declarations */
99 static int copy(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
100 static int ftw_copy_file(const struct FTW *, const char *, const struct stat *, int);
101 static int ftw_copy_link(const struct FTW *, const char *, const struct stat *, int);
102 static int setfile(const struct stat *, int);
103 // static int preserve_dir_acls(const struct stat *, char *, char *);
104 static int preserve_fd_acls(int, int);
106 static void upfunc(void)
114 catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
117 static void sig_handler(int signo)
123 static void set_signal(void)
127 sv.sa_handler = sig_handler;
128 sv.sa_flags = SA_RESTART;
129 sigemptyset(&sv.sa_mask);
130 if (sigaction(SIGTERM, &sv, NULL) < 0)
131 ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
133 if (sigaction(SIGINT, &sv, NULL) < 0)
134 ERROR("error in sigaction(SIGINT): %s", strerror(errno));
136 memset(&sv, 0, sizeof(struct sigaction));
137 sv.sa_handler = SIG_IGN;
138 sigemptyset(&sv.sa_mask);
140 if (sigaction(SIGABRT, &sv, NULL) < 0)
141 ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
143 if (sigaction(SIGHUP, &sv, NULL) < 0)
144 ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
146 if (sigaction(SIGQUIT, &sv, NULL) < 0)
147 ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
150 static void usage_cp(void)
153 "Usage: ad cp [-R] [-aipvf] <source_file> <target_file>\n"
154 " ad cp [-R] [-aipvfx] <source_file [source_file ...]> <target_directory>\n\n"
155 "In the first synopsis form, the cp utility copies the contents of the source_file to the\n"
156 "target_file. In the second synopsis form, the contents of each named source_file is copied to the\n"
157 "destination target_directory. The names of the files themselves are not changed. If cp detects an\n"
158 "attempt to copy a file to itself, the copy will fail.\n\n"
159 "Netatalk AFP volumes are detected by means of their \".AppleDesktop\" directory\n"
160 "which is located in their volume root. When a copy targetting an AFP volume\n"
161 "is detected, its CNID database daemon is connected and all copies will also\n"
162 "go through the CNID database.\n"
163 "AppleDouble files are also copied and created as needed when the target is\n"
165 "The following options are available:\n\n"
166 " -a Archive mode. Same as -Rp.\n\n"
167 " -f For each existing destination pathname, remove it and create a new\n"
168 " file, without prompting for confirmation regardless of its permis-\n"
169 " sions. (The -f option overrides any previous -i or -n options.)\n\n"
170 " -i Cause cp to write a prompt to the standard error output before\n"
171 " copying a file that would overwrite an existing file. If the\n"
172 " response from the standard input begins with the character 'y' or\n"
173 " 'Y', the file copy is attempted. (The -i option overrides any pre-\n"
174 " vious -f or -n options.)\n\n"
175 " -n Do not overwrite an existing file. (The -n option overrides any\n"
176 " previous -f or -i options.)\n\n"
177 " -p Cause cp to preserve the following attributes of each source file\n"
178 " in the copy: modification time, access time, file flags, file mode,\n"
179 " user ID, and group ID, as allowed by permissions.\n"
180 " If the user ID and group ID cannot be preserved, no error message\n"
181 " is displayed and the exit value is not altered.\n\n"
182 " -R If source_file designates a directory, cp copies the directory and\n"
183 " the entire subtree connected at that point.If the source_file\n"
184 " ends in a /, the contents of the directory are copied rather than\n"
185 " the directory itself.\n\n"
186 " -v Cause cp to be verbose, showing files as they are copied.\n\n"
187 " -x File system mount points are not traversed.\n\n"
192 int ad_cp(int argc, char *argv[], AFPObj *obj)
194 struct stat to_stat, tmp_stat;
195 int r, ch, have_trailing_slash;
202 ppdid = pdid = htonl(1);
205 while ((ch = getopt(argc, argv, "afinpRvx")) != -1)
233 ftw_options |= FTW_MOUNT;
248 /* Save the target base in "to". */
249 target = argv[--argc];
250 if ((strlcpy(to.p_path, target, PATH_MAX)) >= PATH_MAX)
251 ERROR("%s: name too long", target);
253 to.p_end = to.p_path + strlen(to.p_path);
254 if (to.p_path == to.p_end) {
258 have_trailing_slash = (to.p_end[-1] == '/');
259 if (have_trailing_slash)
260 STRIP_TRAILING_SLASH(to);
261 to.target_end = to.p_end;
263 /* Set end of argument list */
267 * Cp has two distinct cases:
269 * cp [-R] source target
270 * cp [-R] source1 ... sourceN directory
272 * In both cases, source can be either a file or a directory.
274 * In (1), the target becomes a copy of the source. That is, if the
275 * source is a file, the target will be a file, and likewise for
278 * In (2), the real target is not directory, but "directory/source".
280 r = stat(to.p_path, &to_stat);
281 if (r == -1 && errno != ENOENT)
282 ERROR("%s", to.p_path);
283 if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
285 * Case (1). Target is not a directory.
288 ERROR("%s is not a directory", to.p_path);
291 * Need to detect the case:
293 * Where dir is a directory and foo does not exist, where
294 * we want pathname concatenations turned on but not for
295 * the initial mkdir().
298 lstat(*argv, &tmp_stat);
300 if (S_ISDIR(tmp_stat.st_mode) && Rflag)
307 if (have_trailing_slash && type == FILE_TO_FILE) {
309 ERROR("directory %s does not exist", to.p_path);
311 ERROR("%s is not a directory", to.p_path);
315 * Case (2). Target is a directory.
320 * Keep an inverted copy of the umask, for use in correcting
321 * permissions on created directories when not using -p.
327 /* Inhereting perms in ad_mkdir etc requires this */
331 /* Load .volinfo file for destination*/
332 openvol(obj, to.p_path, &dvolume);
334 for (int i = 0; argv[i] != NULL; i++) {
335 /* Load .volinfo file for source */
336 openvol(obj, argv[i], &svolume);
338 if (nftw(argv[i], copy, upfunc, 20, ftw_options) == -1) {
342 SLOG("Error: %s: %s", argv[i], strerror(errno));
351 static int copy(const char *path,
352 const struct stat *statp,
367 /* This currently doesn't work with "." */
368 if (strcmp(path, ".") == 0) {
369 ERROR("\".\" not supported");
371 const char *dir = strrchr(path, '/');
376 if (!dvolume.vol->vfs->vfs_validupath(dvolume.vol, dir))
377 return FTW_SKIP_SUBTREE;
380 * If we are in case (2) above, we need to append the
381 * source name to the target name.
383 if (type != FILE_TO_FILE) {
385 * Need to remember the roots of traversals to create
386 * correct pathnames. If there's a directory being
387 * copied to a non-existent directory, e.g.
388 * cp -R a/dir noexist
389 * the resulting path name should be noexist/foo, not
390 * noexist/dir/foo (where foo is a file in dir), which
391 * is the case where the target exists.
393 * Also, check for "..". This is for correct path
394 * concatenation for paths ending in "..", e.g.
396 * Paths ending in ".." are changed to ".". This is
397 * tricky, but seems the easiest way to fix the problem.
400 * Since the first level MUST be FTS_ROOTLEVEL, base
401 * is always initialized.
403 if (ftw->level == 0) {
404 if (type != DIR_TO_DNE) {
407 if (strcmp(&path[base], "..") == 0)
414 nlen = strlen(path) - base;
415 target_mid = to.target_end;
416 if (*p != '/' && target_mid[-1] != '/')
419 if (target_mid - to.p_path + nlen >= PATH_MAX) {
420 SLOG("%s%s: name too long (not copied)", to.p_path, p);
424 (void)strncat(target_mid, p, nlen);
425 to.p_end = target_mid + nlen;
427 STRIP_TRAILING_SLASH(to);
430 /* Not an error but need to remember it happened */
431 if (stat(to.p_path, &to_stat) == -1)
434 if (to_stat.st_dev == statp->st_dev &&
435 to_stat.st_ino == statp->st_ino) {
436 SLOG("%s and %s are identical (not copied).", to.p_path, path);
438 if (S_ISDIR(statp->st_mode))
439 /* without using glibc extension FTW_ACTIONRETVAL cant handle this */
440 return FTW_SKIP_SUBTREE;
443 if (!S_ISDIR(statp->st_mode) &&
444 S_ISDIR(to_stat.st_mode)) {
445 SLOG("cannot overwrite directory %s with "
454 /* Convert basename to appropiate volume encoding */
455 if (dvolume.vol->v_path) {
456 if ((convert_dots_encoding(&svolume, &dvolume, to.p_path, MAXPATHLEN)) == -1) {
457 SLOG("Error converting name for %s", to.p_path);
463 switch (statp->st_mode & S_IFMT) {
465 if (ftw_copy_link(ftw, path, statp, !dne))
470 SLOG("%s is a directory", path);
475 * If the directory doesn't exist, create the new
476 * one with the from file mode plus owner RWX bits,
477 * modified by the umask. Trade-off between being
478 * able to write the directory (if from directory is
479 * 555) and not causing a permissions race. If the
480 * umask blocks owner writes, we fail..
483 if (mkdir(to.p_path, statp->st_mode | S_IRWXU) < 0)
484 ERROR("mkdir: %s: %s", to.p_path, strerror(errno));
485 } else if (!S_ISDIR(to_stat.st_mode)) {
487 ERROR("%s", to.p_path);
490 /* Create ad dir and copy ".Parent" */
491 if (dvolume.vol->v_path && ADVOL_V2_OR_EA(dvolume.vol->v_adouble)) {
492 mode_t omask = umask(0);
493 if (dvolume.vol->v_adouble == AD_VERSION2) {
494 /* Create ".AppleDouble" dir */
495 bstring addir = bfromcstr(to.p_path);
496 bcatcstr(addir, "/.AppleDouble");
497 mkdir(cfrombstr(addir), 02777);
501 if (svolume.vol->v_path && ADVOL_V2_OR_EA(svolume.vol->v_adouble)) {
502 /* copy metadata file */
503 if (dvolume.vol->vfs->vfs_copyfile(dvolume.vol, -1, path, to.p_path)) {
504 SLOG("Error copying adouble for %s -> %s", path, to.p_path);
510 /* Get CNID of Parent and add new childir to CNID database */
512 if ((did = cnid_for_path(&dvolume, to.p_path, &pdid)) == CNID_INVALID) {
513 SLOG("Error resolving CNID for %s", to.p_path);
520 if (lstat(to.p_path, &st) != 0) {
524 ad_init(&ad, dvolume.vol);
525 if (ad_open(&ad, to.p_path, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) != 0) {
526 ERROR("Error opening adouble for: %s", to.p_path);
528 ad_setid( &ad, st.st_dev, st.st_ino, did, pdid, dvolume.db_stamp);
529 if (dvolume.vol->v_adouble == AD_VERSION2)
530 ad_setname(&ad, utompath(dvolume.vol, basename(to.p_path)));
531 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
532 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
533 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
534 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
536 ad_close(&ad, ADFLAGS_HF);
542 if (setfile(statp, -1))
545 if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0)
553 SLOG("%s is a device file (not copied).", path);
556 SLOG("%s is a socket (not copied).", path);
559 SLOG("%s is a FIFO (not copied).", path);
562 if (ftw_copy_file(ftw, path, statp, dne))
565 if (dvolume.vol->v_path && ADVOL_V2_OR_EA(dvolume.vol->v_adouble)) {
567 mode_t omask = umask(0);
568 if (svolume.vol->v_path && ADVOL_V2_OR_EA(svolume.vol->v_adouble)) {
570 if (dvolume.vol->vfs->vfs_copyfile(dvolume.vol, -1, path, to.p_path)) {
571 SLOG("Error copying adouble for %s -> %s", path, to.p_path);
577 /* Get CNID of Parent and add new childir to CNID database */
580 if ((cnid = cnid_for_path(&dvolume, to.p_path, &did)) == CNID_INVALID) {
581 SLOG("Error resolving CNID for %s", to.p_path);
588 if (lstat(to.p_path, &st) != 0) {
592 ad_init(&ad, dvolume.vol);
593 if (ad_open(&ad, to.p_path, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) != 0) {
594 ERROR("Error opening adouble for: %s", to.p_path);
596 ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp);
597 if (dvolume.vol->v_adouble == AD_VERSION2)
598 ad_setname(&ad, utompath(dvolume.vol, basename(to.p_path)));
599 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
600 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
601 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
602 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
604 ad_close(&ad, ADFLAGS_HF);
610 (void)printf("%s -> %s\n", path, to.p_path);
615 /* Memory strategy threshold, in pages: if physmem is larger then this, use a large buffer */
616 #define PHYSPAGES_THRESHOLD (32*1024)
618 /* Maximum buffer size in bytes - do not allow it to grow larger than this */
619 #define BUFSIZE_MAX (2*1024*1024)
621 /* Small (default) buffer size in bytes. It's inefficient for this to be smaller than MAXPHYS */
622 #define MAXPHYS (64 * 1024)
623 #define BUFSIZE_SMALL (MAXPHYS)
625 static int ftw_copy_file(const struct FTW *entp,
627 const struct stat *sp,
630 static char *buf = NULL;
631 static size_t bufsize;
635 int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
639 if ((from_fd = open(spath, O_RDONLY, 0)) == -1) {
640 SLOG("%s: %s", spath, strerror(errno));
645 * If the file exists and we're interactive, verify with the user.
646 * If the file DNE, set the mode to be the from file, minus setuid
647 * bits, modified by the umask; arguably wrong, but it makes copying
648 * executables work right and it's been that way forever. (The
649 * other choice is 666 or'ed with the execute bits on the from file
650 * modified by the umask.)
653 #define YESNO "(y/n [n]) "
656 printf("%s not overwritten\n", to.p_path);
657 (void)close(from_fd);
660 (void)fprintf(stderr, "overwrite %s? %s",
662 checkch = ch = getchar();
663 while (ch != '\n' && ch != EOF)
665 if (checkch != 'y' && checkch != 'Y') {
666 (void)close(from_fd);
667 (void)fprintf(stderr, "not overwritten\n");
673 /* remove existing destination file name,
674 * create a new file */
675 (void)unlink(to.p_path);
676 (void)dvolume.vol->vfs->vfs_deletefile(dvolume.vol, -1, to.p_path);
677 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
678 sp->st_mode & ~(S_ISUID | S_ISGID));
680 /* overwrite existing destination file name */
681 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
684 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
685 sp->st_mode & ~(S_ISUID | S_ISGID));
689 SLOG("%s: %s", to.p_path, strerror(errno));
690 (void)close(from_fd);
697 * Mmap and write if less than 8M (the limit is so we don't totally
698 * trash memory on big files. This is really a minor hack, but it
699 * wins some CPU back.
700 * Some filesystems, such as smbnetfs, don't support mmap,
701 * so this is a best-effort attempt.
704 if (S_ISREG(sp->st_mode) && sp->st_size > 0 &&
705 sp->st_size <= 8 * 1024 * 1024 &&
706 (p = mmap(NULL, (size_t)sp->st_size, PROT_READ,
707 MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
709 for (bufp = p, wresid = sp->st_size; ;
710 bufp += wcount, wresid -= (size_t)wcount) {
711 wcount = write(to_fd, bufp, wresid);
715 if (wcount >= (ssize_t)wresid)
718 if (wcount != (ssize_t)wresid) {
719 SLOG("%s: %s", to.p_path, strerror(errno));
722 /* Some systems don't unmap on close(2). */
723 if (munmap(p, sp->st_size) < 0) {
724 SLOG("%s: %s", spath, strerror(errno));
730 * Note that buf and bufsize are static. If
731 * malloc() fails, it will fail at the start
732 * and not copy only some files.
734 if (sysconf(_SC_PHYS_PAGES) >
736 bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
738 bufsize = BUFSIZE_SMALL;
739 buf = malloc(bufsize);
741 ERROR("Not enough memory");
745 while ((rcount = read(from_fd, buf, bufsize)) > 0) {
746 for (bufp = buf, wresid = rcount; ;
747 bufp += wcount, wresid -= wcount) {
748 wcount = write(to_fd, bufp, wresid);
752 if (wcount >= (ssize_t)wresid)
755 if (wcount != (ssize_t)wresid) {
756 SLOG("%s: %s", to.p_path, strerror(errno));
762 SLOG("%s: %s", spath, strerror(errno));
768 * Don't remove the target even after an error. The target might
769 * not be a regular file, or its attributes might be important,
770 * or its contents might be irreplaceable. It would only be safe
771 * to remove it if we created it and its length is 0.
774 if (pflag && setfile(sp, to_fd))
776 if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
779 SLOG("%s: %s", to.p_path, strerror(errno));
783 (void)close(from_fd);
788 static int ftw_copy_link(const struct FTW *p,
790 const struct stat *sstp,
794 char llink[PATH_MAX];
796 if ((len = readlink(spath, llink, sizeof(llink) - 1)) == -1) {
797 SLOG("readlink: %s: %s", spath, strerror(errno));
801 if (exists && unlink(to.p_path)) {
802 SLOG("unlink: %s: %s", to.p_path, strerror(errno));
805 if (symlink(llink, to.p_path)) {
806 SLOG("symlink: %s: %s", llink, strerror(errno));
809 return (pflag ? setfile(sstp, -1) : 0);
812 static int setfile(const struct stat *fs, int fd)
814 static struct timeval tv[2];
816 int rval, gotstat, islink, fdval;
821 islink = !fdval && S_ISLNK(fs->st_mode);
822 mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
824 #if defined(__FreeBSD__)
825 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
826 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
828 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
829 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
832 if (utimes(to.p_path, tv)) {
833 SLOG("utimes: %s", to.p_path);
836 if (fdval ? fstat(fd, &ts) :
837 (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
841 ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
842 S_IRWXU | S_IRWXG | S_IRWXO;
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.
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));
858 mode &= ~(S_ISUID | S_ISGID);
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));
868 if (!gotstat || fs->st_flags != ts.st_flags)
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));
881 static int preserve_fd_acls(int source_fd, int dest_fd)
886 int acl_supported = 0, ret, trivial;
888 ret = fpathconf(source_fd, _PC_ACL_NFS4);
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);
896 if (acl_supported == 0) {
897 ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
900 acl_type = ACL_TYPE_ACCESS;
901 } else if (ret < 0 && errno != EINVAL) {
902 warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
907 if (acl_supported == 0)
910 acl = acl_get_fd_np(source_fd, acl_type);
912 warn("failed to get acl entries while setting %s", to.p_path);
915 if (acl_is_trivial_np(acl, &trivial)) {
916 warn("acl_is_trivial() failed for %s", to.p_path);
924 if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
925 warn("failed to set acl entries for %s", to.p_path);
935 static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
937 acl_t (*aclgetf)(const char *, acl_type_t);
938 int (*aclsetf)(const char *, acl_type_t, acl_t);
942 int acl_supported = 0, ret, trivial;
944 ret = pathconf(source_dir, _PC_ACL_NFS4);
947 acl_type = ACL_TYPE_NFS4;
948 } else if (ret < 0 && errno != EINVAL) {
949 warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
952 if (acl_supported == 0) {
953 ret = pathconf(source_dir, _PC_ACL_EXTENDED);
956 acl_type = ACL_TYPE_ACCESS;
957 } else if (ret < 0 && errno != EINVAL) {
958 warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
963 if (acl_supported == 0)
967 * If the file is a link we will not follow it
969 if (S_ISLNK(fs->st_mode)) {
970 aclgetf = acl_get_link_np;
971 aclsetf = acl_set_link_np;
973 aclgetf = acl_get_file;
974 aclsetf = acl_set_file;
976 if (acl_type == ACL_TYPE_ACCESS) {
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.
982 acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
984 warn("failed to get default acl entries on %s",
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",
998 acl = aclgetf(source_dir, acl_type);
1000 warn("failed to get acl entries on %s", source_dir);
1003 if (acl_is_trivial_np(acl, &trivial)) {
1004 warn("acl_is_trivial() failed on %s", source_dir);
1012 if (aclsetf(dest_dir, acl_type, acl) < 0) {
1013 warn("failed to set acl entries on %s", dest_dir);