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 static char *netatalk_dirs[] = {
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);
114 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
115 Returns pointer to name or NULL.
117 static const char *check_netatalk_dirs(const char *name)
121 for (c=0; netatalk_dirs[c]; c++) {
122 if ((strcmp(name, netatalk_dirs[c])) == 0)
123 return netatalk_dirs[c];
128 static void upfunc(void)
136 catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
139 static void sig_handler(int signo)
145 static void set_signal(void)
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));
155 if (sigaction(SIGINT, &sv, NULL) < 0)
156 ERROR("error in sigaction(SIGINT): %s", strerror(errno));
158 memset(&sv, 0, sizeof(struct sigaction));
159 sv.sa_handler = SIG_IGN;
160 sigemptyset(&sv.sa_mask);
162 if (sigaction(SIGABRT, &sv, NULL) < 0)
163 ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
165 if (sigaction(SIGHUP, &sv, NULL) < 0)
166 ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
168 if (sigaction(SIGQUIT, &sv, NULL) < 0)
169 ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
172 static void usage_cp(void)
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"
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"
214 int ad_cp(int argc, char *argv[])
216 struct stat to_stat, tmp_stat;
217 int r, ch, have_trailing_slash;
224 ppdid = pdid = htonl(1);
227 while ((ch = getopt(argc, argv, "afinpRvx")) != -1)
255 ftw_options |= FTW_MOUNT;
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);
275 to.p_end = to.p_path + strlen(to.p_path);
276 if (to.p_path == to.p_end) {
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;
285 /* Set end of argument list */
289 * Cp has two distinct cases:
291 * cp [-R] source target
292 * cp [-R] source1 ... sourceN directory
294 * In both cases, source can be either a file or a directory.
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
300 * In (2), the real target is not directory, but "directory/source".
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)) {
307 * Case (1). Target is not a directory.
310 ERROR("%s is not a directory", to.p_path);
313 * Need to detect the case:
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().
320 lstat(*argv, &tmp_stat);
322 if (S_ISDIR(tmp_stat.st_mode) && Rflag)
329 if (have_trailing_slash && type == FILE_TO_FILE) {
331 ERROR("directory %s does not exist", to.p_path);
333 ERROR("%s is not a directory", to.p_path);
337 * Case (2). Target is a directory.
342 * Keep an inverted copy of the umask, for use in correcting
343 * permissions on created directories when not using -p.
349 /* Inhereting perms in ad_mkdir etc requires this */
353 /* Load .volinfo file for destination*/
354 openvol(to.p_path, &dvolume);
356 for (int i = 0; argv[i] != NULL; i++) {
357 /* Load .volinfo file for source */
358 openvol(argv[i], &svolume);
360 if (nftw(argv[i], copy, upfunc, 20, ftw_options) == -1) {
364 SLOG("Error: %s: %s", argv[i], strerror(errno));
373 static int copy(const char *path,
374 const struct stat *statp,
389 /* This currently doesn't work with "." */
390 if (strcmp(path, ".") == 0) {
391 ERROR("\".\" not supported");
393 const char *dir = strrchr(path, '/');
398 if (check_netatalk_dirs(dir) != NULL)
399 return FTW_SKIP_SUBTREE;
402 * If we are in case (2) above, we need to append the
403 * source name to the target name.
405 if (type != FILE_TO_FILE) {
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.
415 * Also, check for "..". This is for correct path
416 * concatenation for paths ending in "..", e.g.
418 * Paths ending in ".." are changed to ".". This is
419 * tricky, but seems the easiest way to fix the problem.
422 * Since the first level MUST be FTS_ROOTLEVEL, base
423 * is always initialized.
425 if (ftw->level == 0) {
426 if (type != DIR_TO_DNE) {
429 if (strcmp(&path[base], "..") == 0)
436 nlen = strlen(path) - base;
437 target_mid = to.target_end;
438 if (*p != '/' && target_mid[-1] != '/')
441 if (target_mid - to.p_path + nlen >= PATH_MAX) {
442 SLOG("%s%s: name too long (not copied)", to.p_path, p);
446 (void)strncat(target_mid, p, nlen);
447 to.p_end = target_mid + nlen;
449 STRIP_TRAILING_SLASH(to);
452 /* Not an error but need to remember it happened */
453 if (stat(to.p_path, &to_stat) == -1)
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);
460 if (S_ISDIR(statp->st_mode))
461 /* without using glibc extension FTW_ACTIONRETVAL cant handle this */
462 return FTW_SKIP_SUBTREE;
465 if (!S_ISDIR(statp->st_mode) &&
466 S_ISDIR(to_stat.st_mode)) {
467 SLOG("cannot overwrite directory %s with "
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);
485 switch (statp->st_mode & S_IFMT) {
487 if (ftw_copy_link(ftw, path, statp, !dne))
492 SLOG("%s is a directory", path);
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..
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)) {
509 ERROR("%s", to.p_path);
512 /* Create ad dir and copy ".Parent" */
513 if (dvolume.vol->v_path && dvolume.vol->v_adouble == AD_VERSION2) {
515 /* Create ".AppleDouble" dir */
516 mode_t omask = umask(0);
517 bstring addir = bfromcstr(to.p_path);
518 bcatcstr(addir, "/.AppleDouble");
519 mkdir(cfrombstr(addir), 02777);
522 if (svolume.vol->v_path && svolume.vol->v_adouble == AD_VERSION2) {
523 /* copy ".Parent" file */
524 if (dvolume.vol->vfs->vfs_copyfile(dvolume.vol, -1, path, to.p_path)) {
525 SLOG("Error copying adouble for %s -> %s", path, to.p_path);
531 /* Get CNID of Parent and add new childir to CNID database */
533 if ((did = cnid_for_path(&dvolume, to.p_path, &pdid)) == CNID_INVALID) {
534 SLOG("Error resolving CNID for %s", to.p_path);
541 if (lstat(to.p_path, &st) != 0) {
545 ad_init(&ad, dvolume.vol);
546 if (ad_open(&ad, to.p_path, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) != 0) {
547 ERROR("Error opening adouble for: %s", to.p_path);
549 ad_setid( &ad, st.st_dev, st.st_ino, did, pdid, dvolume.db_stamp);
550 ad_setname(&ad, utompath(dvolume.vol, basename(to.p_path)));
551 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
552 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
553 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
554 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
556 ad_close(&ad, ADFLAGS_HF);
562 if (setfile(statp, -1))
565 if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0)
573 SLOG("%s is a device file (not copied).", path);
576 SLOG("%s is a socket (not copied).", path);
579 SLOG("%s is a FIFO (not copied).", path);
582 if (ftw_copy_file(ftw, path, statp, dne))
585 if (dvolume.vol->v_path && dvolume.vol->v_adouble == AD_VERSION2) {
587 mode_t omask = umask(0);
588 if (svolume.vol->v_path && svolume.vol->v_adouble == AD_VERSION2) {
590 if (dvolume.vol->vfs->vfs_copyfile(dvolume.vol, -1, path, to.p_path)) {
591 SLOG("Error copying adouble for %s -> %s", path, to.p_path);
597 /* Get CNID of Parent and add new childir to CNID database */
600 if ((cnid = cnid_for_path(&dvolume, to.p_path, &did)) == CNID_INVALID) {
601 SLOG("Error resolving CNID for %s", to.p_path);
608 if (lstat(to.p_path, &st) != 0) {
612 ad_init(&ad, dvolume.vol);
613 if (ad_open(&ad, to.p_path, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) != 0) {
614 ERROR("Error opening adouble for: %s", to.p_path);
616 ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp);
617 ad_setname(&ad, utompath(dvolume.vol, basename(to.p_path)));
618 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
619 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
620 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
621 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
623 ad_close(&ad, ADFLAGS_HF);
629 (void)printf("%s -> %s\n", path, to.p_path);
634 /* Memory strategy threshold, in pages: if physmem is larger then this, use a large buffer */
635 #define PHYSPAGES_THRESHOLD (32*1024)
637 /* Maximum buffer size in bytes - do not allow it to grow larger than this */
638 #define BUFSIZE_MAX (2*1024*1024)
640 /* Small (default) buffer size in bytes. It's inefficient for this to be smaller than MAXPHYS */
641 #define MAXPHYS (64 * 1024)
642 #define BUFSIZE_SMALL (MAXPHYS)
644 static int ftw_copy_file(const struct FTW *entp,
646 const struct stat *sp,
649 static char *buf = NULL;
650 static size_t bufsize;
654 int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
658 if ((from_fd = open(spath, O_RDONLY, 0)) == -1) {
659 SLOG("%s: %s", spath, strerror(errno));
664 * If the file exists and we're interactive, verify with the user.
665 * If the file DNE, set the mode to be the from file, minus setuid
666 * bits, modified by the umask; arguably wrong, but it makes copying
667 * executables work right and it's been that way forever. (The
668 * other choice is 666 or'ed with the execute bits on the from file
669 * modified by the umask.)
672 #define YESNO "(y/n [n]) "
675 printf("%s not overwritten\n", to.p_path);
676 (void)close(from_fd);
679 (void)fprintf(stderr, "overwrite %s? %s",
681 checkch = ch = getchar();
682 while (ch != '\n' && ch != EOF)
684 if (checkch != 'y' && checkch != 'Y') {
685 (void)close(from_fd);
686 (void)fprintf(stderr, "not overwritten\n");
692 /* remove existing destination file name,
693 * create a new file */
694 (void)unlink(to.p_path);
695 (void)dvolume.vol->vfs->vfs_deletefile(dvolume.vol, -1, to.p_path);
696 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
697 sp->st_mode & ~(S_ISUID | S_ISGID));
699 /* overwrite existing destination file name */
700 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
703 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
704 sp->st_mode & ~(S_ISUID | S_ISGID));
708 SLOG("%s: %s", to.p_path, strerror(errno));
709 (void)close(from_fd);
716 * Mmap and write if less than 8M (the limit is so we don't totally
717 * trash memory on big files. This is really a minor hack, but it
718 * wins some CPU back.
719 * Some filesystems, such as smbnetfs, don't support mmap,
720 * so this is a best-effort attempt.
723 if (S_ISREG(sp->st_mode) && sp->st_size > 0 &&
724 sp->st_size <= 8 * 1024 * 1024 &&
725 (p = mmap(NULL, (size_t)sp->st_size, PROT_READ,
726 MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
728 for (bufp = p, wresid = sp->st_size; ;
729 bufp += wcount, wresid -= (size_t)wcount) {
730 wcount = write(to_fd, bufp, wresid);
734 if (wcount >= (ssize_t)wresid)
737 if (wcount != (ssize_t)wresid) {
738 SLOG("%s: %s", to.p_path, strerror(errno));
741 /* Some systems don't unmap on close(2). */
742 if (munmap(p, sp->st_size) < 0) {
743 SLOG("%s: %s", spath, strerror(errno));
749 * Note that buf and bufsize are static. If
750 * malloc() fails, it will fail at the start
751 * and not copy only some files.
753 if (sysconf(_SC_PHYS_PAGES) >
755 bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
757 bufsize = BUFSIZE_SMALL;
758 buf = malloc(bufsize);
760 ERROR("Not enough memory");
764 while ((rcount = read(from_fd, buf, bufsize)) > 0) {
765 for (bufp = buf, wresid = rcount; ;
766 bufp += wcount, wresid -= wcount) {
767 wcount = write(to_fd, bufp, wresid);
771 if (wcount >= (ssize_t)wresid)
774 if (wcount != (ssize_t)wresid) {
775 SLOG("%s: %s", to.p_path, strerror(errno));
781 SLOG("%s: %s", spath, strerror(errno));
787 * Don't remove the target even after an error. The target might
788 * not be a regular file, or its attributes might be important,
789 * or its contents might be irreplaceable. It would only be safe
790 * to remove it if we created it and its length is 0.
793 if (pflag && setfile(sp, to_fd))
795 if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
798 SLOG("%s: %s", to.p_path, strerror(errno));
802 (void)close(from_fd);
807 static int ftw_copy_link(const struct FTW *p,
809 const struct stat *sstp,
813 char llink[PATH_MAX];
815 if ((len = readlink(spath, llink, sizeof(llink) - 1)) == -1) {
816 SLOG("readlink: %s: %s", spath, strerror(errno));
820 if (exists && unlink(to.p_path)) {
821 SLOG("unlink: %s: %s", to.p_path, strerror(errno));
824 if (symlink(llink, to.p_path)) {
825 SLOG("symlink: %s: %s", llink, strerror(errno));
828 return (pflag ? setfile(sstp, -1) : 0);
831 static int setfile(const struct stat *fs, int fd)
833 static struct timeval tv[2];
835 int rval, gotstat, islink, fdval;
840 islink = !fdval && S_ISLNK(fs->st_mode);
841 mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
843 #if defined(__FreeBSD__)
844 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
845 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
847 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
848 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
851 if (utimes(to.p_path, tv)) {
852 SLOG("utimes: %s", to.p_path);
855 if (fdval ? fstat(fd, &ts) :
856 (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
860 ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
861 S_IRWXU | S_IRWXG | S_IRWXO;
864 * Changing the ownership probably won't succeed, unless we're root
865 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
866 * the mode; current BSD behavior is to remove all setuid bits on
867 * chown. If chown fails, lose setuid/setgid bits.
869 if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
870 if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
871 (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
872 chown(to.p_path, fs->st_uid, fs->st_gid))) {
873 if (errno != EPERM) {
874 SLOG("chown: %s: %s", to.p_path, strerror(errno));
877 mode &= ~(S_ISUID | S_ISGID);
880 if (!gotstat || mode != ts.st_mode)
881 if (fdval ? fchmod(fd, mode) : chmod(to.p_path, mode)) {
882 SLOG("chmod: %s: %s", to.p_path, strerror(errno));
887 if (!gotstat || fs->st_flags != ts.st_flags)
889 fchflags(fd, fs->st_flags) :
890 (islink ? lchflags(to.p_path, fs->st_flags) :
891 chflags(to.p_path, fs->st_flags))) {
892 SLOG("chflags: %s: %s", to.p_path, strerror(errno));
900 static int preserve_fd_acls(int source_fd, int dest_fd)
905 int acl_supported = 0, ret, trivial;
907 ret = fpathconf(source_fd, _PC_ACL_NFS4);
910 acl_type = ACL_TYPE_NFS4;
911 } else if (ret < 0 && errno != EINVAL) {
912 warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
915 if (acl_supported == 0) {
916 ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
919 acl_type = ACL_TYPE_ACCESS;
920 } else if (ret < 0 && errno != EINVAL) {
921 warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
926 if (acl_supported == 0)
929 acl = acl_get_fd_np(source_fd, acl_type);
931 warn("failed to get acl entries while setting %s", to.p_path);
934 if (acl_is_trivial_np(acl, &trivial)) {
935 warn("acl_is_trivial() failed for %s", to.p_path);
943 if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
944 warn("failed to set acl entries for %s", to.p_path);
953 static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
956 acl_t (*aclgetf)(const char *, acl_type_t);
957 int (*aclsetf)(const char *, acl_type_t, acl_t);
961 int acl_supported = 0, ret, trivial;
963 ret = pathconf(source_dir, _PC_ACL_NFS4);
966 acl_type = ACL_TYPE_NFS4;
967 } else if (ret < 0 && errno != EINVAL) {
968 warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
971 if (acl_supported == 0) {
972 ret = pathconf(source_dir, _PC_ACL_EXTENDED);
975 acl_type = ACL_TYPE_ACCESS;
976 } else if (ret < 0 && errno != EINVAL) {
977 warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
982 if (acl_supported == 0)
986 * If the file is a link we will not follow it
988 if (S_ISLNK(fs->st_mode)) {
989 aclgetf = acl_get_link_np;
990 aclsetf = acl_set_link_np;
992 aclgetf = acl_get_file;
993 aclsetf = acl_set_file;
995 if (acl_type == ACL_TYPE_ACCESS) {
997 * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
998 * size ACL will be returned. So it is not safe to simply
999 * check the pointer to see if the default ACL is present.
1001 acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
1003 warn("failed to get default acl entries on %s",
1007 aclp = &acl->ats_acl;
1008 if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
1009 ACL_TYPE_DEFAULT, acl) < 0) {
1010 warn("failed to set default acl entries on %s",
1017 acl = aclgetf(source_dir, acl_type);
1019 warn("failed to get acl entries on %s", source_dir);
1022 if (acl_is_trivial_np(acl, &trivial)) {
1023 warn("acl_is_trivial() failed on %s", source_dir);
1031 if (aclsetf(dest_dir, acl_type, acl) < 0) {
1032 warn("failed to set acl entries on %s", dest_dir);