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 /* This currently doesn't work with "." */
391 if (strcmp(path, ".") == 0) {
392 ERROR("\".\" not supported");
394 const char *dir = strrchr(path, '/');
399 if (check_netatalk_dirs(dir) != NULL)
400 return FTW_SKIP_SUBTREE;
403 * If we are in case (2) above, we need to append the
404 * source name to the target name.
406 if (type != FILE_TO_FILE) {
408 * Need to remember the roots of traversals to create
409 * correct pathnames. If there's a directory being
410 * copied to a non-existent directory, e.g.
411 * cp -R a/dir noexist
412 * the resulting path name should be noexist/foo, not
413 * noexist/dir/foo (where foo is a file in dir), which
414 * is the case where the target exists.
416 * Also, check for "..". This is for correct path
417 * concatenation for paths ending in "..", e.g.
419 * Paths ending in ".." are changed to ".". This is
420 * tricky, but seems the easiest way to fix the problem.
423 * Since the first level MUST be FTS_ROOTLEVEL, base
424 * is always initialized.
426 if (ftw->level == 0) {
427 if (type != DIR_TO_DNE) {
430 if (strcmp(&path[base], "..") == 0)
437 nlen = strlen(path) - base;
438 target_mid = to.target_end;
439 if (*p != '/' && target_mid[-1] != '/')
442 if (target_mid - to.p_path + nlen >= PATH_MAX) {
443 SLOG("%s%s: name too long (not copied)", to.p_path, p);
447 (void)strncat(target_mid, p, nlen);
448 to.p_end = target_mid + nlen;
450 STRIP_TRAILING_SLASH(to);
453 /* Not an error but need to remember it happened */
454 if (stat(to.p_path, &to_stat) == -1)
457 if (to_stat.st_dev == statp->st_dev &&
458 to_stat.st_ino == statp->st_ino) {
459 SLOG("%s and %s are identical (not copied).", to.p_path, path);
461 if (S_ISDIR(statp->st_mode))
462 /* without using glibc extension FTW_ACTIONRETVAL cant handle this */
463 return FTW_SKIP_SUBTREE;
466 if (!S_ISDIR(statp->st_mode) &&
467 S_ISDIR(to_stat.st_mode)) {
468 SLOG("cannot overwrite directory %s with "
477 /* Convert basename to appropiate volume encoding */
478 if (dvolume.volinfo.v_path) {
479 if ((convert_dots_encoding(&svolume, &dvolume, to.p_path, MAXPATHLEN)) == -1) {
480 SLOG("Error converting name for %s", to.p_path);
486 switch (statp->st_mode & S_IFMT) {
488 if (ftw_copy_link(ftw, path, statp, !dne))
493 SLOG("%s is a directory", path);
498 * If the directory doesn't exist, create the new
499 * one with the from file mode plus owner RWX bits,
500 * modified by the umask. Trade-off between being
501 * able to write the directory (if from directory is
502 * 555) and not causing a permissions race. If the
503 * umask blocks owner writes, we fail..
506 if (mkdir(to.p_path, statp->st_mode | S_IRWXU) < 0)
507 ERROR("mkdir: %s: %s", to.p_path, strerror(errno));
508 } else if (!S_ISDIR(to_stat.st_mode)) {
510 ERROR("%s", to.p_path);
513 /* Create ad dir and copy ".Parent" */
514 if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
516 /* Create ".AppleDouble" dir */
517 mode_t omask = umask(0);
518 bstring addir = bfromcstr(to.p_path);
519 bcatcstr(addir, "/.AppleDouble");
520 mkdir(cfrombstr(addir), 02777);
523 if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
524 /* copy ".Parent" file */
525 if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
526 SLOG("Error copying adouble for %s -> %s", path, to.p_path);
532 /* Get CNID of Parent and add new childir to CNID database */
534 if ((did = cnid_for_path(&dvolume, to.p_path, &pdid)) == CNID_INVALID) {
535 SLOG("Error resolving CNID for %s", to.p_path);
542 if (lstat(to.p_path, &st) != 0) {
546 ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
547 if (ad_open_metadata(to.p_path, ADFLAGS_DIR, O_RDWR | O_CREAT, &ad) != 0) {
548 ERROR("Error opening adouble for: %s", to.p_path);
550 ad_setid( &ad, st.st_dev, st.st_ino, did, pdid, dvolume.db_stamp);
551 ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
552 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
553 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
554 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
555 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
557 ad_close_metadata(&ad);
563 if (setfile(statp, -1))
566 if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0)
574 SLOG("%s is a device file (not copied).", path);
577 SLOG("%s is a socket (not copied).", path);
580 SLOG("%s is a FIFO (not copied).", path);
583 if (ftw_copy_file(ftw, path, statp, dne))
586 if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
588 mode_t omask = umask(0);
589 if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
591 if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
592 SLOG("Error copying adouble for %s -> %s", path, to.p_path);
598 /* Get CNID of Parent and add new childir to CNID database */
601 if ((cnid = cnid_for_path(&dvolume, to.p_path, &did)) == CNID_INVALID) {
602 SLOG("Error resolving CNID for %s", to.p_path);
609 if (lstat(to.p_path, &st) != 0) {
613 ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
614 if (ad_open_metadata(to.p_path, 0, O_RDWR | O_CREAT, &ad) != 0) {
615 ERROR("Error opening adouble for: %s", to.p_path);
617 ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp);
618 ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
619 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
620 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
621 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
622 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
624 ad_close_metadata(&ad);
630 (void)printf("%s -> %s\n", path, to.p_path);
635 /* Memory strategy threshold, in pages: if physmem is larger then this, use a large buffer */
636 #define PHYSPAGES_THRESHOLD (32*1024)
638 /* Maximum buffer size in bytes - do not allow it to grow larger than this */
639 #define BUFSIZE_MAX (2*1024*1024)
641 /* Small (default) buffer size in bytes. It's inefficient for this to be smaller than MAXPHYS */
642 #define MAXPHYS (64 * 1024)
643 #define BUFSIZE_SMALL (MAXPHYS)
645 static int ftw_copy_file(const struct FTW *entp,
647 const struct stat *sp,
650 static char *buf = NULL;
651 static size_t bufsize;
655 int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
659 if ((from_fd = open(spath, O_RDONLY, 0)) == -1) {
660 SLOG("%s: %s", spath, strerror(errno));
665 * If the file exists and we're interactive, verify with the user.
666 * If the file DNE, set the mode to be the from file, minus setuid
667 * bits, modified by the umask; arguably wrong, but it makes copying
668 * executables work right and it's been that way forever. (The
669 * other choice is 666 or'ed with the execute bits on the from file
670 * modified by the umask.)
673 #define YESNO "(y/n [n]) "
676 printf("%s not overwritten\n", to.p_path);
677 (void)close(from_fd);
680 (void)fprintf(stderr, "overwrite %s? %s",
682 checkch = ch = getchar();
683 while (ch != '\n' && ch != EOF)
685 if (checkch != 'y' && checkch != 'Y') {
686 (void)close(from_fd);
687 (void)fprintf(stderr, "not overwritten\n");
693 /* remove existing destination file name,
694 * create a new file */
695 (void)unlink(to.p_path);
696 (void)dvolume.volume.vfs->vfs_deletefile(&dvolume.volume, -1, to.p_path);
697 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
698 sp->st_mode & ~(S_ISUID | S_ISGID));
700 /* overwrite existing destination file name */
701 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
704 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
705 sp->st_mode & ~(S_ISUID | S_ISGID));
709 SLOG("%s: %s", to.p_path, strerror(errno));
710 (void)close(from_fd);
717 * Mmap and write if less than 8M (the limit is so we don't totally
718 * trash memory on big files. This is really a minor hack, but it
719 * wins some CPU back.
720 * Some filesystems, such as smbnetfs, don't support mmap,
721 * so this is a best-effort attempt.
724 if (S_ISREG(sp->st_mode) && sp->st_size > 0 &&
725 sp->st_size <= 8 * 1024 * 1024 &&
726 (p = mmap(NULL, (size_t)sp->st_size, PROT_READ,
727 MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
729 for (bufp = p, wresid = sp->st_size; ;
730 bufp += wcount, wresid -= (size_t)wcount) {
731 wcount = write(to_fd, bufp, wresid);
735 if (wcount >= (ssize_t)wresid)
738 if (wcount != (ssize_t)wresid) {
739 SLOG("%s: %s", to.p_path, strerror(errno));
742 /* Some systems don't unmap on close(2). */
743 if (munmap(p, sp->st_size) < 0) {
744 SLOG("%s: %s", spath, strerror(errno));
750 * Note that buf and bufsize are static. If
751 * malloc() fails, it will fail at the start
752 * and not copy only some files.
754 if (sysconf(_SC_PHYS_PAGES) >
756 bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
758 bufsize = BUFSIZE_SMALL;
759 buf = malloc(bufsize);
761 ERROR("Not enough memory");
765 while ((rcount = read(from_fd, buf, bufsize)) > 0) {
766 for (bufp = buf, wresid = rcount; ;
767 bufp += wcount, wresid -= wcount) {
768 wcount = write(to_fd, bufp, wresid);
772 if (wcount >= (ssize_t)wresid)
775 if (wcount != (ssize_t)wresid) {
776 SLOG("%s: %s", to.p_path, strerror(errno));
782 SLOG("%s: %s", spath, strerror(errno));
788 * Don't remove the target even after an error. The target might
789 * not be a regular file, or its attributes might be important,
790 * or its contents might be irreplaceable. It would only be safe
791 * to remove it if we created it and its length is 0.
794 if (pflag && setfile(sp, to_fd))
796 if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
799 SLOG("%s: %s", to.p_path, strerror(errno));
803 (void)close(from_fd);
808 static int ftw_copy_link(const struct FTW *p,
810 const struct stat *sstp,
814 char llink[PATH_MAX];
816 if ((len = readlink(spath, llink, sizeof(llink) - 1)) == -1) {
817 SLOG("readlink: %s: %s", spath, strerror(errno));
821 if (exists && unlink(to.p_path)) {
822 SLOG("unlink: %s: %s", to.p_path, strerror(errno));
825 if (symlink(llink, to.p_path)) {
826 SLOG("symlink: %s: %s", llink, strerror(errno));
829 return (pflag ? setfile(sstp, -1) : 0);
832 static int setfile(const struct stat *fs, int fd)
834 static struct timeval tv[2];
836 int rval, gotstat, islink, fdval;
841 islink = !fdval && S_ISLNK(fs->st_mode);
842 mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
844 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
845 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
846 if (utimes(to.p_path, tv)) {
847 SLOG("utimes: %s", to.p_path);
850 if (fdval ? fstat(fd, &ts) :
851 (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
855 ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
856 S_IRWXU | S_IRWXG | S_IRWXO;
859 * Changing the ownership probably won't succeed, unless we're root
860 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
861 * the mode; current BSD behavior is to remove all setuid bits on
862 * chown. If chown fails, lose setuid/setgid bits.
864 if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
865 if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
866 (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
867 chown(to.p_path, fs->st_uid, fs->st_gid))) {
868 if (errno != EPERM) {
869 SLOG("chown: %s: %s", to.p_path, strerror(errno));
872 mode &= ~(S_ISUID | S_ISGID);
875 if (!gotstat || mode != ts.st_mode)
876 if (fdval ? fchmod(fd, mode) : chmod(to.p_path, mode)) {
877 SLOG("chmod: %s: %s", to.p_path, strerror(errno));
882 if (!gotstat || fs->st_flags != ts.st_flags)
884 fchflags(fd, fs->st_flags) :
885 (islink ? lchflags(to.p_path, fs->st_flags) :
886 chflags(to.p_path, fs->st_flags))) {
887 SLOG("chflags: %s: %s", to.p_path, strerror(errno));
895 static int preserve_fd_acls(int source_fd, int dest_fd)
900 int acl_supported = 0, ret, trivial;
902 ret = fpathconf(source_fd, _PC_ACL_NFS4);
905 acl_type = ACL_TYPE_NFS4;
906 } else if (ret < 0 && errno != EINVAL) {
907 warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
910 if (acl_supported == 0) {
911 ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
914 acl_type = ACL_TYPE_ACCESS;
915 } else if (ret < 0 && errno != EINVAL) {
916 warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
921 if (acl_supported == 0)
924 acl = acl_get_fd_np(source_fd, acl_type);
926 warn("failed to get acl entries while setting %s", to.p_path);
929 if (acl_is_trivial_np(acl, &trivial)) {
930 warn("acl_is_trivial() failed for %s", to.p_path);
938 if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
939 warn("failed to set acl entries for %s", to.p_path);
948 static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
951 acl_t (*aclgetf)(const char *, acl_type_t);
952 int (*aclsetf)(const char *, acl_type_t, acl_t);
956 int acl_supported = 0, ret, trivial;
958 ret = pathconf(source_dir, _PC_ACL_NFS4);
961 acl_type = ACL_TYPE_NFS4;
962 } else if (ret < 0 && errno != EINVAL) {
963 warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
966 if (acl_supported == 0) {
967 ret = pathconf(source_dir, _PC_ACL_EXTENDED);
970 acl_type = ACL_TYPE_ACCESS;
971 } else if (ret < 0 && errno != EINVAL) {
972 warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
977 if (acl_supported == 0)
981 * If the file is a link we will not follow it
983 if (S_ISLNK(fs->st_mode)) {
984 aclgetf = acl_get_link_np;
985 aclsetf = acl_set_link_np;
987 aclgetf = acl_get_file;
988 aclsetf = acl_set_file;
990 if (acl_type == ACL_TYPE_ACCESS) {
992 * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
993 * size ACL will be returned. So it is not safe to simply
994 * check the pointer to see if the default ACL is present.
996 acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
998 warn("failed to get default acl entries on %s",
1002 aclp = &acl->ats_acl;
1003 if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
1004 ACL_TYPE_DEFAULT, acl) < 0) {
1005 warn("failed to set default acl entries on %s",
1012 acl = aclgetf(source_dir, acl_type);
1014 warn("failed to get acl entries on %s", source_dir);
1017 if (acl_is_trivial_np(acl, &trivial)) {
1018 warn("acl_is_trivial() failed on %s", source_dir);
1026 if (aclsetf(dest_dir, acl_type, acl) < 0) {
1027 warn("failed to set acl entries on %s", dest_dir);