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/volinfo.h>
71 #include <atalk/bstrlib.h>
72 #include <atalk/bstradd.h>
73 #include <atalk/queue.h>
77 #define STRIP_TRAILING_SLASH(p) { \
78 while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \
82 static char emptystring[] = "";
84 PATH_T to = { to.p_path, emptystring, "" };
85 enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
87 int fflag, iflag, nflag, pflag, vflag;
90 cnid_t ppdid, pdid, did; /* current dir CNID and parent did*/
92 static afpvol_t svolume, dvolume;
95 static volatile sig_atomic_t alarmed;
96 static int badcp, rval;
97 static int ftw_options = FTW_MOUNT | FTW_PHYS | FTW_ACTIONRETVAL;
99 static char *netatalk_dirs[] = {
106 /* Forward declarations */
107 static int copy(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
108 static int ftw_copy_file(const struct FTW *, const char *, const struct stat *, int);
109 static int ftw_copy_link(const struct FTW *, const char *, const struct stat *, int);
110 static int setfile(const struct stat *, int);
111 static int preserve_dir_acls(const struct stat *, char *, char *);
112 static int preserve_fd_acls(int, int);
115 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
116 Returns pointer to name or NULL.
118 static const char *check_netatalk_dirs(const char *name)
122 for (c=0; netatalk_dirs[c]; c++) {
123 if ((strcmp(name, netatalk_dirs[c])) == 0)
124 return netatalk_dirs[c];
129 static void upfunc(void)
137 catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
140 static void sig_handler(int signo)
146 static void set_signal(void)
150 sv.sa_handler = sig_handler;
151 sv.sa_flags = SA_RESTART;
152 sigemptyset(&sv.sa_mask);
153 if (sigaction(SIGTERM, &sv, NULL) < 0)
154 ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
156 if (sigaction(SIGINT, &sv, NULL) < 0)
157 ERROR("error in sigaction(SIGINT): %s", strerror(errno));
159 memset(&sv, 0, sizeof(struct sigaction));
160 sv.sa_handler = SIG_IGN;
161 sigemptyset(&sv.sa_mask);
163 if (sigaction(SIGABRT, &sv, NULL) < 0)
164 ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
166 if (sigaction(SIGHUP, &sv, NULL) < 0)
167 ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
169 if (sigaction(SIGQUIT, &sv, NULL) < 0)
170 ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
173 static void usage_cp(void)
176 "Usage: ad cp [-R] [-aipvf] <source_file> <target_file>\n"
177 " ad cp [-R] [-aipvfx] <source_file [source_file ...]> <target_directory>\n\n"
178 "In the first synopsis form, the cp utility copies the contents of the source_file to the\n"
179 "target_file. In the second synopsis form, the contents of each named source_file is copied to the\n"
180 "destination target_directory. The names of the files themselves are not changed. If cp detects an\n"
181 "attempt to copy a file to itself, the copy will fail.\n\n"
182 "Netatalk AFP volumes are detected by means of their \".AppleDesktop\" directory\n"
183 "which is located in their volume root. When a copy targetting an AFP volume\n"
184 "is detected, its CNID database daemon is connected and all copies will also\n"
185 "go through the CNID database.\n"
186 "AppleDouble files are also copied and created as needed when the target is\n"
188 "The following options are available:\n\n"
189 " -a Archive mode. Same as -Rp.\n\n"
190 " -f For each existing destination pathname, remove it and create a new\n"
191 " file, without prompting for confirmation regardless of its permis-\n"
192 " sions. (The -f option overrides any previous -i or -n options.)\n\n"
193 " -i Cause cp to write a prompt to the standard error output before\n"
194 " copying a file that would overwrite an existing file. If the\n"
195 " response from the standard input begins with the character 'y' or\n"
196 " 'Y', the file copy is attempted. (The -i option overrides any pre-\n"
197 " vious -f or -n options.)\n\n"
198 " -n Do not overwrite an existing file. (The -n option overrides any\n"
199 " previous -f or -i options.)\n\n"
200 " -p Cause cp to preserve the following attributes of each source file\n"
201 " in the copy: modification time, access time, file flags, file mode,\n"
202 " user ID, and group ID, as allowed by permissions.\n"
203 " If the user ID and group ID cannot be preserved, no error message\n"
204 " is displayed and the exit value is not altered.\n\n"
205 " -R If source_file designates a directory, cp copies the directory and\n"
206 " the entire subtree connected at that point.If the source_file\n"
207 " ends in a /, the contents of the directory are copied rather than\n"
208 " the directory itself.\n\n"
209 " -v Cause cp to be verbose, showing files as they are copied.\n\n"
210 " -x File system mount points are not traversed.\n\n"
215 int ad_cp(int argc, char *argv[])
217 struct stat to_stat, tmp_stat;
218 int r, ch, have_trailing_slash;
225 ppdid = pdid = htonl(1);
228 while ((ch = getopt(argc, argv, "afinpRvx")) != -1)
256 ftw_options |= FTW_MOUNT;
271 /* Save the target base in "to". */
272 target = argv[--argc];
273 if ((strlcpy(to.p_path, target, PATH_MAX)) >= PATH_MAX)
274 ERROR("%s: name too long", target);
276 to.p_end = to.p_path + strlen(to.p_path);
277 if (to.p_path == to.p_end) {
281 have_trailing_slash = (to.p_end[-1] == '/');
282 if (have_trailing_slash)
283 STRIP_TRAILING_SLASH(to);
284 to.target_end = to.p_end;
286 /* Set end of argument list */
290 * Cp has two distinct cases:
292 * cp [-R] source target
293 * cp [-R] source1 ... sourceN directory
295 * In both cases, source can be either a file or a directory.
297 * In (1), the target becomes a copy of the source. That is, if the
298 * source is a file, the target will be a file, and likewise for
301 * In (2), the real target is not directory, but "directory/source".
303 r = stat(to.p_path, &to_stat);
304 if (r == -1 && errno != ENOENT)
305 ERROR("%s", to.p_path);
306 if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
308 * Case (1). Target is not a directory.
311 ERROR("%s is not a directory", to.p_path);
314 * Need to detect the case:
316 * Where dir is a directory and foo does not exist, where
317 * we want pathname concatenations turned on but not for
318 * the initial mkdir().
321 lstat(*argv, &tmp_stat);
323 if (S_ISDIR(tmp_stat.st_mode) && Rflag)
330 if (have_trailing_slash && type == FILE_TO_FILE) {
332 ERROR("directory %s does not exist", to.p_path);
334 ERROR("%s is not a directory", to.p_path);
338 * Case (2). Target is a directory.
343 * Keep an inverted copy of the umask, for use in correcting
344 * permissions on created directories when not using -p.
350 /* Inhereting perms in ad_mkdir etc requires this */
354 /* Load .volinfo file for destination*/
355 openvol(to.p_path, &dvolume);
357 for (int i = 0; argv[i] != NULL; i++) {
358 /* Load .volinfo file for source */
359 openvol(argv[i], &svolume);
361 if (nftw(argv[i], copy, upfunc, 20, ftw_options) == -1) {
365 SLOG("Error: %s: %s", argv[i], strerror(errno));
374 static int copy(const char *path,
375 const struct stat *statp,
390 const char *dir = strrchr(path, '/');
395 if (check_netatalk_dirs(dir) != NULL)
396 return FTW_SKIP_SUBTREE;
399 * If we are in case (2) above, we need to append the
400 * source name to the target name.
402 if (type != FILE_TO_FILE) {
404 * Need to remember the roots of traversals to create
405 * correct pathnames. If there's a directory being
406 * copied to a non-existent directory, e.g.
407 * cp -R a/dir noexist
408 * the resulting path name should be noexist/foo, not
409 * noexist/dir/foo (where foo is a file in dir), which
410 * is the case where the target exists.
412 * Also, check for "..". This is for correct path
413 * concatenation for paths ending in "..", e.g.
415 * Paths ending in ".." are changed to ".". This is
416 * tricky, but seems the easiest way to fix the problem.
419 * Since the first level MUST be FTS_ROOTLEVEL, base
420 * is always initialized.
422 if (ftw->level == 0) {
423 if (type != DIR_TO_DNE) {
426 if (strcmp(&path[base], "..") == 0)
433 nlen = strlen(path) - base;
434 target_mid = to.target_end;
435 if (*p != '/' && target_mid[-1] != '/')
438 if (target_mid - to.p_path + nlen >= PATH_MAX) {
439 SLOG("%s%s: name too long (not copied)", to.p_path, p);
443 (void)strncat(target_mid, p, nlen);
444 to.p_end = target_mid + nlen;
446 STRIP_TRAILING_SLASH(to);
449 /* Not an error but need to remember it happened */
450 if (stat(to.p_path, &to_stat) == -1)
453 if (to_stat.st_dev == statp->st_dev &&
454 to_stat.st_ino == statp->st_ino) {
455 SLOG("%s and %s are identical (not copied).", to.p_path, path);
457 if (S_ISDIR(statp->st_mode))
458 /* without using glibc extension FTW_ACTIONRETVAL cant handle this */
459 return FTW_SKIP_SUBTREE;
462 if (!S_ISDIR(statp->st_mode) &&
463 S_ISDIR(to_stat.st_mode)) {
464 SLOG("cannot overwrite directory %s with "
473 /* Convert basename to appropiate volume encoding */
474 if (dvolume.volinfo.v_path) {
475 if ((convert_dots_encoding(&svolume, &dvolume, to.p_path, MAXPATHLEN)) == -1) {
476 SLOG("Error converting name for %s", to.p_path);
482 switch (statp->st_mode & S_IFMT) {
484 if (ftw_copy_link(ftw, path, statp, !dne))
489 SLOG("%s is a directory", path);
494 * If the directory doesn't exist, create the new
495 * one with the from file mode plus owner RWX bits,
496 * modified by the umask. Trade-off between being
497 * able to write the directory (if from directory is
498 * 555) and not causing a permissions race. If the
499 * umask blocks owner writes, we fail..
502 if (mkdir(to.p_path, statp->st_mode | S_IRWXU) < 0)
503 ERROR("mkdir: %s: %s", to.p_path, strerror(errno));
504 } else if (!S_ISDIR(to_stat.st_mode)) {
506 ERROR("%s", to.p_path);
509 /* Create ad dir and copy ".Parent" */
510 if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
512 /* Create ".AppleDouble" dir */
513 mode_t omask = umask(0);
514 bstring addir = bfromcstr(to.p_path);
515 bcatcstr(addir, "/.AppleDouble");
516 mkdir(cfrombstr(addir), 02777);
519 if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
520 /* copy ".Parent" file */
521 if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
522 SLOG("Error copying adouble for %s -> %s", path, to.p_path);
528 /* Get CNID of Parent and add new childir to CNID database */
530 did = cnid_for_path(&dvolume, to.p_path, &pdid);
534 if (stat(to.p_path, &st) != 0) {
538 ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
539 if (ad_open_metadata(to.p_path, ADFLAGS_DIR, O_RDWR | O_CREAT, &ad) != 0) {
540 ERROR("Error opening adouble for: %s", to.p_path);
542 ad_setid( &ad, st.st_dev, st.st_ino, did, pdid, dvolume.db_stamp);
543 ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
544 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
545 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
546 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
547 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
549 ad_close_metadata(&ad);
555 if (setfile(statp, -1))
558 if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0)
566 SLOG("%s is a device file (not copied).", path);
569 SLOG("%s is a socket (not copied).", path);
572 SLOG("%s is a FIFO (not copied).", path);
575 if (ftw_copy_file(ftw, path, statp, dne))
578 if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
580 mode_t omask = umask(0);
581 if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
583 if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
584 SLOG("Error copying adouble for %s -> %s", path, to.p_path);
590 /* Get CNID of Parent and add new childir to CNID database */
592 cnid_t cnid = cnid_for_path(&dvolume, to.p_path, &did);
596 if (stat(to.p_path, &st) != 0) {
600 ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
601 if (ad_open_metadata(to.p_path, 0, O_RDWR | O_CREAT, &ad) != 0) {
602 ERROR("Error opening adouble for: %s", to.p_path);
604 ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp);
605 ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
606 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
607 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
608 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
609 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
611 ad_close_metadata(&ad);
617 (void)printf("%s -> %s\n", path, to.p_path);
622 /* Memory strategy threshold, in pages: if physmem is larger then this, use a large buffer */
623 #define PHYSPAGES_THRESHOLD (32*1024)
625 /* Maximum buffer size in bytes - do not allow it to grow larger than this */
626 #define BUFSIZE_MAX (2*1024*1024)
628 /* Small (default) buffer size in bytes. It's inefficient for this to be smaller than MAXPHYS */
629 #define MAXPHYS (64 * 1024)
630 #define BUFSIZE_SMALL (MAXPHYS)
632 static int ftw_copy_file(const struct FTW *entp,
634 const struct stat *sp,
637 static char *buf = NULL;
638 static size_t bufsize;
642 int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
646 if ((from_fd = open(spath, O_RDONLY, 0)) == -1) {
647 SLOG("%s: %s", spath, strerror(errno));
652 * If the file exists and we're interactive, verify with the user.
653 * If the file DNE, set the mode to be the from file, minus setuid
654 * bits, modified by the umask; arguably wrong, but it makes copying
655 * executables work right and it's been that way forever. (The
656 * other choice is 666 or'ed with the execute bits on the from file
657 * modified by the umask.)
660 #define YESNO "(y/n [n]) "
663 printf("%s not overwritten\n", to.p_path);
664 (void)close(from_fd);
667 (void)fprintf(stderr, "overwrite %s? %s",
669 checkch = ch = getchar();
670 while (ch != '\n' && ch != EOF)
672 if (checkch != 'y' && checkch != 'Y') {
673 (void)close(from_fd);
674 (void)fprintf(stderr, "not overwritten\n");
680 /* remove existing destination file name,
681 * create a new file */
682 (void)unlink(to.p_path);
683 (void)dvolume.volume.vfs->vfs_deletefile(&dvolume.volume, -1, to.p_path);
684 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
685 sp->st_mode & ~(S_ISUID | S_ISGID));
687 /* overwrite existing destination file name */
688 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
691 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
692 sp->st_mode & ~(S_ISUID | S_ISGID));
696 SLOG("%s: %s", to.p_path, strerror(errno));
697 (void)close(from_fd);
704 * Mmap and write if less than 8M (the limit is so we don't totally
705 * trash memory on big files. This is really a minor hack, but it
706 * wins some CPU back.
707 * Some filesystems, such as smbnetfs, don't support mmap,
708 * so this is a best-effort attempt.
711 if (S_ISREG(sp->st_mode) && sp->st_size > 0 &&
712 sp->st_size <= 8 * 1024 * 1024 &&
713 (p = mmap(NULL, (size_t)sp->st_size, PROT_READ,
714 MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
716 for (bufp = p, wresid = sp->st_size; ;
717 bufp += wcount, wresid -= (size_t)wcount) {
718 wcount = write(to_fd, bufp, wresid);
722 if (wcount >= (ssize_t)wresid)
725 if (wcount != (ssize_t)wresid) {
726 SLOG("%s: %s", to.p_path, strerror(errno));
729 /* Some systems don't unmap on close(2). */
730 if (munmap(p, sp->st_size) < 0) {
731 SLOG("%s: %s", spath, strerror(errno));
737 * Note that buf and bufsize are static. If
738 * malloc() fails, it will fail at the start
739 * and not copy only some files.
741 if (sysconf(_SC_PHYS_PAGES) >
743 bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
745 bufsize = BUFSIZE_SMALL;
746 buf = malloc(bufsize);
748 ERROR("Not enough memory");
752 while ((rcount = read(from_fd, buf, bufsize)) > 0) {
753 for (bufp = buf, wresid = rcount; ;
754 bufp += wcount, wresid -= wcount) {
755 wcount = write(to_fd, bufp, wresid);
759 if (wcount >= (ssize_t)wresid)
762 if (wcount != (ssize_t)wresid) {
763 SLOG("%s: %s", to.p_path, strerror(errno));
769 SLOG("%s: %s", spath, strerror(errno));
775 * Don't remove the target even after an error. The target might
776 * not be a regular file, or its attributes might be important,
777 * or its contents might be irreplaceable. It would only be safe
778 * to remove it if we created it and its length is 0.
781 if (pflag && setfile(sp, to_fd))
783 if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
786 SLOG("%s: %s", to.p_path, strerror(errno));
790 (void)close(from_fd);
795 static int ftw_copy_link(const struct FTW *p,
797 const struct stat *sstp,
801 char llink[PATH_MAX];
803 if ((len = readlink(spath, llink, sizeof(llink) - 1)) == -1) {
804 SLOG("readlink: %s: %s", spath, strerror(errno));
808 if (exists && unlink(to.p_path)) {
809 SLOG("unlink: %s: %s", to.p_path, strerror(errno));
812 if (symlink(llink, to.p_path)) {
813 SLOG("symlink: %s: %s", llink, strerror(errno));
816 return (pflag ? setfile(sstp, -1) : 0);
819 static int setfile(const struct stat *fs, int fd)
821 static struct timeval tv[2];
823 int rval, gotstat, islink, fdval;
828 islink = !fdval && S_ISLNK(fs->st_mode);
829 mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
831 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
832 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
833 if (utimes(to.p_path, tv)) {
834 SLOG("utimes: %s", to.p_path);
837 if (fdval ? fstat(fd, &ts) :
838 (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
842 ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
843 S_IRWXU | S_IRWXG | S_IRWXO;
846 * Changing the ownership probably won't succeed, unless we're root
847 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
848 * the mode; current BSD behavior is to remove all setuid bits on
849 * chown. If chown fails, lose setuid/setgid bits.
851 if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
852 if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
853 (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
854 chown(to.p_path, fs->st_uid, fs->st_gid))) {
855 if (errno != EPERM) {
856 SLOG("chown: %s: %s", to.p_path, strerror(errno));
859 mode &= ~(S_ISUID | S_ISGID);
862 if (!gotstat || mode != ts.st_mode)
863 if (fdval ? fchmod(fd, mode) : chmod(to.p_path, mode)) {
864 SLOG("chmod: %s: %s", to.p_path, strerror(errno));
869 if (!gotstat || fs->st_flags != ts.st_flags)
871 fchflags(fd, fs->st_flags) :
872 (islink ? lchflags(to.p_path, fs->st_flags) :
873 chflags(to.p_path, fs->st_flags))) {
874 SLOG("chflags: %s: %s", to.p_path, strerror(errno));
882 static int preserve_fd_acls(int source_fd, int dest_fd)
887 int acl_supported = 0, ret, trivial;
889 ret = fpathconf(source_fd, _PC_ACL_NFS4);
892 acl_type = ACL_TYPE_NFS4;
893 } else if (ret < 0 && errno != EINVAL) {
894 warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
897 if (acl_supported == 0) {
898 ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
901 acl_type = ACL_TYPE_ACCESS;
902 } else if (ret < 0 && errno != EINVAL) {
903 warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
908 if (acl_supported == 0)
911 acl = acl_get_fd_np(source_fd, acl_type);
913 warn("failed to get acl entries while setting %s", to.p_path);
916 if (acl_is_trivial_np(acl, &trivial)) {
917 warn("acl_is_trivial() failed for %s", to.p_path);
925 if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
926 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)
938 acl_t (*aclgetf)(const char *, acl_type_t);
939 int (*aclsetf)(const char *, acl_type_t, acl_t);
943 int acl_supported = 0, ret, trivial;
945 ret = pathconf(source_dir, _PC_ACL_NFS4);
948 acl_type = ACL_TYPE_NFS4;
949 } else if (ret < 0 && errno != EINVAL) {
950 warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
953 if (acl_supported == 0) {
954 ret = pathconf(source_dir, _PC_ACL_EXTENDED);
957 acl_type = ACL_TYPE_ACCESS;
958 } else if (ret < 0 && errno != EINVAL) {
959 warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
964 if (acl_supported == 0)
968 * If the file is a link we will not follow it
970 if (S_ISLNK(fs->st_mode)) {
971 aclgetf = acl_get_link_np;
972 aclsetf = acl_set_link_np;
974 aclgetf = acl_get_file;
975 aclsetf = acl_set_file;
977 if (acl_type == ACL_TYPE_ACCESS) {
979 * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
980 * size ACL will be returned. So it is not safe to simply
981 * check the pointer to see if the default ACL is present.
983 acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
985 warn("failed to get default acl entries on %s",
989 aclp = &acl->ats_acl;
990 if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
991 ACL_TYPE_DEFAULT, acl) < 0) {
992 warn("failed to set default acl entries on %s",
999 acl = aclgetf(source_dir, acl_type);
1001 warn("failed to get acl entries on %s", source_dir);
1004 if (acl_is_trivial_np(acl, &trivial)) {
1005 warn("acl_is_trivial() failed on %s", source_dir);
1013 if (aclsetf(dest_dir, acl_type, acl) < 0) {
1014 warn("failed to set acl entries on %s", dest_dir);