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>
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>
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 const char *dir = strrchr(path, '/');
394 if (check_netatalk_dirs(dir) != NULL)
395 return FTW_SKIP_SUBTREE;
398 * If we are in case (2) above, we need to append the
399 * source name to the target name.
401 if (type != FILE_TO_FILE) {
403 * Need to remember the roots of traversals to create
404 * correct pathnames. If there's a directory being
405 * copied to a non-existent directory, e.g.
406 * cp -R a/dir noexist
407 * the resulting path name should be noexist/foo, not
408 * noexist/dir/foo (where foo is a file in dir), which
409 * is the case where the target exists.
411 * Also, check for "..". This is for correct path
412 * concatenation for paths ending in "..", e.g.
414 * Paths ending in ".." are changed to ".". This is
415 * tricky, but seems the easiest way to fix the problem.
418 * Since the first level MUST be FTS_ROOTLEVEL, base
419 * is always initialized.
421 if (ftw->level == 0) {
422 if (type != DIR_TO_DNE) {
425 if (strcmp(&path[base], "..") == 0)
432 nlen = strlen(path) - base;
433 target_mid = to.target_end;
434 if (*p != '/' && target_mid[-1] != '/')
437 if (target_mid - to.p_path + nlen >= PATH_MAX) {
438 SLOG("%s%s: name too long (not copied)", to.p_path, p);
442 (void)strncat(target_mid, p, nlen);
443 to.p_end = target_mid + nlen;
445 STRIP_TRAILING_SLASH(to);
448 /* Not an error but need to remember it happened */
449 if (stat(to.p_path, &to_stat) == -1)
452 if (to_stat.st_dev == statp->st_dev &&
453 to_stat.st_ino == statp->st_ino) {
454 SLOG("%s and %s are identical (not copied).", to.p_path, path);
456 if (S_ISDIR(statp->st_mode))
457 /* without using glibc extension FTW_ACTIONRETVAL cant handle this */
458 return FTW_SKIP_SUBTREE;
461 if (!S_ISDIR(statp->st_mode) &&
462 S_ISDIR(to_stat.st_mode)) {
463 SLOG("cannot overwrite directory %s with "
472 /* Convert basename to appropiate volume encoding */
473 if (dvolume.volinfo.v_path) {
474 if ((convert_dots_encoding(&svolume, &dvolume, to.p_path, MAXPATHLEN)) == -1) {
475 SLOG("Error converting name for %s", to.p_path);
481 switch (statp->st_mode & S_IFMT) {
483 if (ftw_copy_link(ftw, path, statp, !dne))
488 SLOG("%s is a directory", path);
493 * If the directory doesn't exist, create the new
494 * one with the from file mode plus owner RWX bits,
495 * modified by the umask. Trade-off between being
496 * able to write the directory (if from directory is
497 * 555) and not causing a permissions race. If the
498 * umask blocks owner writes, we fail..
501 if (mkdir(to.p_path, statp->st_mode | S_IRWXU) < 0)
502 ERROR("mkdir: %s: %s", to.p_path, strerror(errno));
503 } else if (!S_ISDIR(to_stat.st_mode)) {
505 ERROR("%s", to.p_path);
508 /* Create ad dir and copy ".Parent" */
509 if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
511 /* Create ".AppleDouble" dir */
512 mode_t omask = umask(0);
513 bstring addir = bfromcstr(to.p_path);
514 bcatcstr(addir, "/.AppleDouble");
515 mkdir(cfrombstr(addir), 02777);
518 if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
519 /* copy ".Parent" file */
520 if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
521 SLOG("Error copying adouble for %s -> %s", path, to.p_path);
527 /* Get CNID of Parent and add new childir to CNID database */
529 did = cnid_for_path(&dvolume, to.p_path, &pdid);
533 if (stat(to.p_path, &st) != 0) {
537 ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
538 if (ad_open_metadata(to.p_path, ADFLAGS_DIR, O_RDWR | O_CREAT, &ad) != 0) {
539 ERROR("Error opening adouble for: %s", to.p_path);
541 ad_setid( &ad, st.st_dev, st.st_ino, did, pdid, dvolume.db_stamp);
542 ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
543 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
544 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
545 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
546 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
548 ad_close_metadata(&ad);
554 if (setfile(statp, -1))
557 if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0)
565 SLOG("%s is a device file (not copied).", path);
568 SLOG("%s is a socket (not copied).", path);
571 SLOG("%s is a FIFO (not copied).", path);
574 if (ftw_copy_file(ftw, path, statp, dne))
577 if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
579 mode_t omask = umask(0);
580 if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
582 if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
583 SLOG("Error copying adouble for %s -> %s", path, to.p_path);
589 /* Get CNID of Parent and add new childir to CNID database */
591 cnid_t cnid = cnid_for_path(&dvolume, to.p_path, &did);
595 if (stat(to.p_path, &st) != 0) {
599 ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
600 if (ad_open_metadata(to.p_path, 0, O_RDWR | O_CREAT, &ad) != 0) {
601 ERROR("Error opening adouble for: %s", to.p_path);
603 ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp);
604 ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
605 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
606 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
607 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
608 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
610 ad_close_metadata(&ad);
616 (void)printf("%s -> %s\n", path, to.p_path);
621 /* Memory strategy threshold, in pages: if physmem is larger then this, use a large buffer */
622 #define PHYSPAGES_THRESHOLD (32*1024)
624 /* Maximum buffer size in bytes - do not allow it to grow larger than this */
625 #define BUFSIZE_MAX (2*1024*1024)
627 /* Small (default) buffer size in bytes. It's inefficient for this to be smaller than MAXPHYS */
628 #define MAXPHYS (64 * 1024)
629 #define BUFSIZE_SMALL (MAXPHYS)
631 static int ftw_copy_file(const struct FTW *entp,
633 const struct stat *sp,
636 static char *buf = NULL;
637 static size_t bufsize;
641 int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
645 if ((from_fd = open(spath, O_RDONLY, 0)) == -1) {
646 SLOG("%s: %s", spath, strerror(errno));
651 * If the file exists and we're interactive, verify with the user.
652 * If the file DNE, set the mode to be the from file, minus setuid
653 * bits, modified by the umask; arguably wrong, but it makes copying
654 * executables work right and it's been that way forever. (The
655 * other choice is 666 or'ed with the execute bits on the from file
656 * modified by the umask.)
659 #define YESNO "(y/n [n]) "
662 printf("%s not overwritten\n", to.p_path);
663 (void)close(from_fd);
666 (void)fprintf(stderr, "overwrite %s? %s",
668 checkch = ch = getchar();
669 while (ch != '\n' && ch != EOF)
671 if (checkch != 'y' && checkch != 'Y') {
672 (void)close(from_fd);
673 (void)fprintf(stderr, "not overwritten\n");
679 /* remove existing destination file name,
680 * create a new file */
681 (void)unlink(to.p_path);
682 (void)dvolume.volume.vfs->vfs_deletefile(&dvolume.volume, -1, to.p_path);
683 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
684 sp->st_mode & ~(S_ISUID | S_ISGID));
686 /* overwrite existing destination file name */
687 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
690 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
691 sp->st_mode & ~(S_ISUID | S_ISGID));
695 SLOG("%s: %s", to.p_path, strerror(errno));
696 (void)close(from_fd);
703 * Mmap and write if less than 8M (the limit is so we don't totally
704 * trash memory on big files. This is really a minor hack, but it
705 * wins some CPU back.
706 * Some filesystems, such as smbnetfs, don't support mmap,
707 * so this is a best-effort attempt.
710 if (S_ISREG(sp->st_mode) && sp->st_size > 0 &&
711 sp->st_size <= 8 * 1024 * 1024 &&
712 (p = mmap(NULL, (size_t)sp->st_size, PROT_READ,
713 MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
715 for (bufp = p, wresid = sp->st_size; ;
716 bufp += wcount, wresid -= (size_t)wcount) {
717 wcount = write(to_fd, bufp, wresid);
721 if (wcount >= (ssize_t)wresid)
724 if (wcount != (ssize_t)wresid) {
725 SLOG("%s: %s", to.p_path, strerror(errno));
728 /* Some systems don't unmap on close(2). */
729 if (munmap(p, sp->st_size) < 0) {
730 SLOG("%s: %s", spath, strerror(errno));
736 * Note that buf and bufsize are static. If
737 * malloc() fails, it will fail at the start
738 * and not copy only some files.
740 if (sysconf(_SC_PHYS_PAGES) >
742 bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
744 bufsize = BUFSIZE_SMALL;
745 buf = malloc(bufsize);
747 ERROR("Not enough memory");
751 while ((rcount = read(from_fd, buf, bufsize)) > 0) {
752 for (bufp = buf, wresid = rcount; ;
753 bufp += wcount, wresid -= wcount) {
754 wcount = write(to_fd, bufp, wresid);
758 if (wcount >= (ssize_t)wresid)
761 if (wcount != (ssize_t)wresid) {
762 SLOG("%s: %s", to.p_path, strerror(errno));
768 SLOG("%s: %s", spath, strerror(errno));
774 * Don't remove the target even after an error. The target might
775 * not be a regular file, or its attributes might be important,
776 * or its contents might be irreplaceable. It would only be safe
777 * to remove it if we created it and its length is 0.
780 if (pflag && setfile(sp, to_fd))
782 if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
785 SLOG("%s: %s", to.p_path, strerror(errno));
789 (void)close(from_fd);
794 static int ftw_copy_link(const struct FTW *p,
796 const struct stat *sstp,
800 char llink[PATH_MAX];
802 if ((len = readlink(spath, llink, sizeof(llink) - 1)) == -1) {
803 SLOG("readlink: %s: %s", spath, strerror(errno));
807 if (exists && unlink(to.p_path)) {
808 SLOG("unlink: %s: %s", to.p_path, strerror(errno));
811 if (symlink(llink, to.p_path)) {
812 SLOG("symlink: %s: %s", llink, strerror(errno));
815 return (pflag ? setfile(sstp, -1) : 0);
818 static int setfile(const struct stat *fs, int fd)
820 static struct timeval tv[2];
822 int rval, gotstat, islink, fdval;
827 islink = !fdval && S_ISLNK(fs->st_mode);
828 mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
830 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
831 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
832 if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) {
833 SLOG("%sutimes: %s", islink ? "l" : "", to.p_path);
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);
934 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);