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 if ((did = cnid_for_path(&dvolume, to.p_path, &pdid)) == CNID_INVALID) {
531 SLOG("Error resolving CNID for %s", to.p_path);
538 if (lstat(to.p_path, &st) != 0) {
542 ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
543 if (ad_open_metadata(to.p_path, ADFLAGS_DIR, O_RDWR | O_CREAT, &ad) != 0) {
544 ERROR("Error opening adouble for: %s", to.p_path);
546 ad_setid( &ad, st.st_dev, st.st_ino, did, pdid, dvolume.db_stamp);
547 ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
548 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
549 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
550 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
551 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
553 ad_close_metadata(&ad);
559 if (setfile(statp, -1))
562 if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0)
570 SLOG("%s is a device file (not copied).", path);
573 SLOG("%s is a socket (not copied).", path);
576 SLOG("%s is a FIFO (not copied).", path);
579 if (ftw_copy_file(ftw, path, statp, dne))
582 if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
584 mode_t omask = umask(0);
585 if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
587 if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
588 SLOG("Error copying adouble for %s -> %s", path, to.p_path);
594 /* Get CNID of Parent and add new childir to CNID database */
597 if ((cnid = cnid_for_path(&dvolume, to.p_path, &did)) == CNID_INVALID) {
598 SLOG("Error resolving CNID for %s", to.p_path);
605 if (lstat(to.p_path, &st) != 0) {
609 ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
610 if (ad_open_metadata(to.p_path, 0, O_RDWR | O_CREAT, &ad) != 0) {
611 ERROR("Error opening adouble for: %s", to.p_path);
613 ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp);
614 ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
615 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
616 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
617 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
618 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
620 ad_close_metadata(&ad);
626 (void)printf("%s -> %s\n", path, to.p_path);
631 /* Memory strategy threshold, in pages: if physmem is larger then this, use a large buffer */
632 #define PHYSPAGES_THRESHOLD (32*1024)
634 /* Maximum buffer size in bytes - do not allow it to grow larger than this */
635 #define BUFSIZE_MAX (2*1024*1024)
637 /* Small (default) buffer size in bytes. It's inefficient for this to be smaller than MAXPHYS */
638 #define MAXPHYS (64 * 1024)
639 #define BUFSIZE_SMALL (MAXPHYS)
641 static int ftw_copy_file(const struct FTW *entp,
643 const struct stat *sp,
646 static char *buf = NULL;
647 static size_t bufsize;
651 int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
655 if ((from_fd = open(spath, O_RDONLY, 0)) == -1) {
656 SLOG("%s: %s", spath, strerror(errno));
661 * If the file exists and we're interactive, verify with the user.
662 * If the file DNE, set the mode to be the from file, minus setuid
663 * bits, modified by the umask; arguably wrong, but it makes copying
664 * executables work right and it's been that way forever. (The
665 * other choice is 666 or'ed with the execute bits on the from file
666 * modified by the umask.)
669 #define YESNO "(y/n [n]) "
672 printf("%s not overwritten\n", to.p_path);
673 (void)close(from_fd);
676 (void)fprintf(stderr, "overwrite %s? %s",
678 checkch = ch = getchar();
679 while (ch != '\n' && ch != EOF)
681 if (checkch != 'y' && checkch != 'Y') {
682 (void)close(from_fd);
683 (void)fprintf(stderr, "not overwritten\n");
689 /* remove existing destination file name,
690 * create a new file */
691 (void)unlink(to.p_path);
692 (void)dvolume.volume.vfs->vfs_deletefile(&dvolume.volume, -1, to.p_path);
693 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
694 sp->st_mode & ~(S_ISUID | S_ISGID));
696 /* overwrite existing destination file name */
697 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
700 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
701 sp->st_mode & ~(S_ISUID | S_ISGID));
705 SLOG("%s: %s", to.p_path, strerror(errno));
706 (void)close(from_fd);
713 * Mmap and write if less than 8M (the limit is so we don't totally
714 * trash memory on big files. This is really a minor hack, but it
715 * wins some CPU back.
716 * Some filesystems, such as smbnetfs, don't support mmap,
717 * so this is a best-effort attempt.
720 if (S_ISREG(sp->st_mode) && sp->st_size > 0 &&
721 sp->st_size <= 8 * 1024 * 1024 &&
722 (p = mmap(NULL, (size_t)sp->st_size, PROT_READ,
723 MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
725 for (bufp = p, wresid = sp->st_size; ;
726 bufp += wcount, wresid -= (size_t)wcount) {
727 wcount = write(to_fd, bufp, wresid);
731 if (wcount >= (ssize_t)wresid)
734 if (wcount != (ssize_t)wresid) {
735 SLOG("%s: %s", to.p_path, strerror(errno));
738 /* Some systems don't unmap on close(2). */
739 if (munmap(p, sp->st_size) < 0) {
740 SLOG("%s: %s", spath, strerror(errno));
746 * Note that buf and bufsize are static. If
747 * malloc() fails, it will fail at the start
748 * and not copy only some files.
750 if (sysconf(_SC_PHYS_PAGES) >
752 bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
754 bufsize = BUFSIZE_SMALL;
755 buf = malloc(bufsize);
757 ERROR("Not enough memory");
761 while ((rcount = read(from_fd, buf, bufsize)) > 0) {
762 for (bufp = buf, wresid = rcount; ;
763 bufp += wcount, wresid -= wcount) {
764 wcount = write(to_fd, bufp, wresid);
768 if (wcount >= (ssize_t)wresid)
771 if (wcount != (ssize_t)wresid) {
772 SLOG("%s: %s", to.p_path, strerror(errno));
778 SLOG("%s: %s", spath, strerror(errno));
784 * Don't remove the target even after an error. The target might
785 * not be a regular file, or its attributes might be important,
786 * or its contents might be irreplaceable. It would only be safe
787 * to remove it if we created it and its length is 0.
790 if (pflag && setfile(sp, to_fd))
792 if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
795 SLOG("%s: %s", to.p_path, strerror(errno));
799 (void)close(from_fd);
804 static int ftw_copy_link(const struct FTW *p,
806 const struct stat *sstp,
810 char llink[PATH_MAX];
812 if ((len = readlink(spath, llink, sizeof(llink) - 1)) == -1) {
813 SLOG("readlink: %s: %s", spath, strerror(errno));
817 if (exists && unlink(to.p_path)) {
818 SLOG("unlink: %s: %s", to.p_path, strerror(errno));
821 if (symlink(llink, to.p_path)) {
822 SLOG("symlink: %s: %s", llink, strerror(errno));
825 return (pflag ? setfile(sstp, -1) : 0);
828 static int setfile(const struct stat *fs, int fd)
830 static struct timeval tv[2];
832 int rval, gotstat, islink, fdval;
837 islink = !fdval && S_ISLNK(fs->st_mode);
838 mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
840 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
841 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
842 if (utimes(to.p_path, tv)) {
843 SLOG("utimes: %s", to.p_path);
846 if (fdval ? fstat(fd, &ts) :
847 (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
851 ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
852 S_IRWXU | S_IRWXG | S_IRWXO;
855 * Changing the ownership probably won't succeed, unless we're root
856 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
857 * the mode; current BSD behavior is to remove all setuid bits on
858 * chown. If chown fails, lose setuid/setgid bits.
860 if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
861 if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
862 (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
863 chown(to.p_path, fs->st_uid, fs->st_gid))) {
864 if (errno != EPERM) {
865 SLOG("chown: %s: %s", to.p_path, strerror(errno));
868 mode &= ~(S_ISUID | S_ISGID);
871 if (!gotstat || mode != ts.st_mode)
872 if (fdval ? fchmod(fd, mode) : chmod(to.p_path, mode)) {
873 SLOG("chmod: %s: %s", to.p_path, strerror(errno));
878 if (!gotstat || fs->st_flags != ts.st_flags)
880 fchflags(fd, fs->st_flags) :
881 (islink ? lchflags(to.p_path, fs->st_flags) :
882 chflags(to.p_path, fs->st_flags))) {
883 SLOG("chflags: %s: %s", to.p_path, strerror(errno));
891 static int preserve_fd_acls(int source_fd, int dest_fd)
896 int acl_supported = 0, ret, trivial;
898 ret = fpathconf(source_fd, _PC_ACL_NFS4);
901 acl_type = ACL_TYPE_NFS4;
902 } else if (ret < 0 && errno != EINVAL) {
903 warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
906 if (acl_supported == 0) {
907 ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
910 acl_type = ACL_TYPE_ACCESS;
911 } else if (ret < 0 && errno != EINVAL) {
912 warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
917 if (acl_supported == 0)
920 acl = acl_get_fd_np(source_fd, acl_type);
922 warn("failed to get acl entries while setting %s", to.p_path);
925 if (acl_is_trivial_np(acl, &trivial)) {
926 warn("acl_is_trivial() failed for %s", to.p_path);
934 if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
935 warn("failed to set acl entries for %s", to.p_path);
944 static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
947 acl_t (*aclgetf)(const char *, acl_type_t);
948 int (*aclsetf)(const char *, acl_type_t, acl_t);
952 int acl_supported = 0, ret, trivial;
954 ret = pathconf(source_dir, _PC_ACL_NFS4);
957 acl_type = ACL_TYPE_NFS4;
958 } else if (ret < 0 && errno != EINVAL) {
959 warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
962 if (acl_supported == 0) {
963 ret = pathconf(source_dir, _PC_ACL_EXTENDED);
966 acl_type = ACL_TYPE_ACCESS;
967 } else if (ret < 0 && errno != EINVAL) {
968 warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
973 if (acl_supported == 0)
977 * If the file is a link we will not follow it
979 if (S_ISLNK(fs->st_mode)) {
980 aclgetf = acl_get_link_np;
981 aclsetf = acl_set_link_np;
983 aclgetf = acl_get_file;
984 aclsetf = acl_set_file;
986 if (acl_type == ACL_TYPE_ACCESS) {
988 * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
989 * size ACL will be returned. So it is not safe to simply
990 * check the pointer to see if the default ACL is present.
992 acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
994 warn("failed to get default acl entries on %s",
998 aclp = &acl->ats_acl;
999 if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
1000 ACL_TYPE_DEFAULT, acl) < 0) {
1001 warn("failed to set default acl entries on %s",
1008 acl = aclgetf(source_dir, acl_type);
1010 warn("failed to get acl entries on %s", source_dir);
1013 if (acl_is_trivial_np(acl, &trivial)) {
1014 warn("acl_is_trivial() failed on %s", source_dir);
1022 if (aclsetf(dest_dir, acl_type, acl) < 0) {
1023 warn("failed to set acl entries on %s", dest_dir);