2 * Copyright (c) 2010, Frank Lahm <franklahm@googlemail.com>
3 * Copyright (c) 1988, 1993, 1994
4 * The Regents of the University of California. All rights reserved.
6 * This code is derived from software contributed to Berkeley by
7 * David Hitz of Auspex Systems Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * Cp copies source files to target files.
37 * The global PATH_T structure "to" always contains the path to the
38 * current target file. Since fts(3) does not change directories,
39 * this path can be either absolute or dot-relative.
41 * The basic algorithm is to initialize "to" and use fts(3) to traverse
42 * the file hierarchy rooted in the argument list. A trivial case is the
43 * case of 'cp file1 file2'. The more interesting case is the case of
44 * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
45 * path (relative to the root of the traversal) is appended to dir (stored
46 * in "to") to form the final target path.
51 #endif /* HAVE_CONFIG_H */
53 #include <sys/types.h>
64 #include <atalk/ftw.h>
65 #include <atalk/adouble.h>
66 #include <atalk/vfs.h>
67 #include <atalk/util.h>
68 #include <atalk/unix.h>
69 #include <atalk/volume.h>
70 #include <atalk/bstrlib.h>
71 #include <atalk/bstradd.h>
72 #include <atalk/queue.h>
76 #define STRIP_TRAILING_SLASH(p) { \
77 while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \
81 static char emptystring[] = "";
83 PATH_T to = { to.p_path, emptystring, "" };
84 enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
86 int fflag, iflag, nflag, pflag, vflag;
89 cnid_t ppdid, pdid, did; /* current dir CNID and parent did*/
91 static afpvol_t svolume, dvolume;
94 static volatile sig_atomic_t alarmed;
95 static int badcp, rval;
96 static int ftw_options = FTW_MOUNT | FTW_PHYS | FTW_ACTIONRETVAL;
98 static char *netatalk_dirs[] = {
105 /* Forward declarations */
106 static int copy(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
107 static int ftw_copy_file(const struct FTW *, const char *, const struct stat *, int);
108 static int ftw_copy_link(const struct FTW *, const char *, const struct stat *, int);
109 static int setfile(const struct stat *, int);
110 static int preserve_dir_acls(const struct stat *, char *, char *);
111 static int preserve_fd_acls(int, int);
114 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
115 Returns pointer to name or NULL.
117 static const char *check_netatalk_dirs(const char *name)
121 for (c=0; netatalk_dirs[c]; c++) {
122 if ((strcmp(name, netatalk_dirs[c])) == 0)
123 return netatalk_dirs[c];
128 static void upfunc(void)
136 catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
139 static void sig_handler(int signo)
145 static void set_signal(void)
149 sv.sa_handler = sig_handler;
150 sv.sa_flags = SA_RESTART;
151 sigemptyset(&sv.sa_mask);
152 if (sigaction(SIGTERM, &sv, NULL) < 0)
153 ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
155 if (sigaction(SIGINT, &sv, NULL) < 0)
156 ERROR("error in sigaction(SIGINT): %s", strerror(errno));
158 memset(&sv, 0, sizeof(struct sigaction));
159 sv.sa_handler = SIG_IGN;
160 sigemptyset(&sv.sa_mask);
162 if (sigaction(SIGABRT, &sv, NULL) < 0)
163 ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
165 if (sigaction(SIGHUP, &sv, NULL) < 0)
166 ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
168 if (sigaction(SIGQUIT, &sv, NULL) < 0)
169 ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
172 static void usage_cp(void)
175 "Usage: ad cp [-R] [-aipvf] <source_file> <target_file>\n"
176 " ad cp [-R] [-aipvfx] <source_file [source_file ...]> <target_directory>\n\n"
177 "In the first synopsis form, the cp utility copies the contents of the source_file to the\n"
178 "target_file. In the second synopsis form, the contents of each named source_file is copied to the\n"
179 "destination target_directory. The names of the files themselves are not changed. If cp detects an\n"
180 "attempt to copy a file to itself, the copy will fail.\n\n"
181 "Netatalk AFP volumes are detected by means of their \".AppleDesktop\" directory\n"
182 "which is located in their volume root. When a copy targetting an AFP volume\n"
183 "is detected, its CNID database daemon is connected and all copies will also\n"
184 "go through the CNID database.\n"
185 "AppleDouble files are also copied and created as needed when the target is\n"
187 "The following options are available:\n\n"
188 " -a Archive mode. Same as -Rp.\n\n"
189 " -f For each existing destination pathname, remove it and create a new\n"
190 " file, without prompting for confirmation regardless of its permis-\n"
191 " sions. (The -f option overrides any previous -i or -n options.)\n\n"
192 " -i Cause cp to write a prompt to the standard error output before\n"
193 " copying a file that would overwrite an existing file. If the\n"
194 " response from the standard input begins with the character 'y' or\n"
195 " 'Y', the file copy is attempted. (The -i option overrides any pre-\n"
196 " vious -f or -n options.)\n\n"
197 " -n Do not overwrite an existing file. (The -n option overrides any\n"
198 " previous -f or -i options.)\n\n"
199 " -p Cause cp to preserve the following attributes of each source file\n"
200 " in the copy: modification time, access time, file flags, file mode,\n"
201 " user ID, and group ID, as allowed by permissions.\n"
202 " If the user ID and group ID cannot be preserved, no error message\n"
203 " is displayed and the exit value is not altered.\n\n"
204 " -R If source_file designates a directory, cp copies the directory and\n"
205 " the entire subtree connected at that point.If the source_file\n"
206 " ends in a /, the contents of the directory are copied rather than\n"
207 " the directory itself.\n\n"
208 " -v Cause cp to be verbose, showing files as they are copied.\n\n"
209 " -x File system mount points are not traversed.\n\n"
214 int ad_cp(int argc, char *argv[], AFPObj *obj)
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(obj, to.p_path, &dvolume);
356 for (int i = 0; argv[i] != NULL; i++) {
357 /* Load .volinfo file for source */
358 openvol(obj, argv[i], &svolume);
360 if (nftw(argv[i], copy, upfunc, 20, ftw_options) == -1) {
364 SLOG("Error: %s: %s", argv[i], strerror(errno));
373 static int copy(const char *path,
374 const struct stat *statp,
389 /* This currently doesn't work with "." */
390 if (strcmp(path, ".") == 0) {
391 ERROR("\".\" not supported");
393 const char *dir = strrchr(path, '/');
398 if (!dvolume.vol->vfs->vfs_validupath(dvolume.vol, dir))
399 return FTW_SKIP_SUBTREE;
402 * If we are in case (2) above, we need to append the
403 * source name to the target name.
405 if (type != FILE_TO_FILE) {
407 * Need to remember the roots of traversals to create
408 * correct pathnames. If there's a directory being
409 * copied to a non-existent directory, e.g.
410 * cp -R a/dir noexist
411 * the resulting path name should be noexist/foo, not
412 * noexist/dir/foo (where foo is a file in dir), which
413 * is the case where the target exists.
415 * Also, check for "..". This is for correct path
416 * concatenation for paths ending in "..", e.g.
418 * Paths ending in ".." are changed to ".". This is
419 * tricky, but seems the easiest way to fix the problem.
422 * Since the first level MUST be FTS_ROOTLEVEL, base
423 * is always initialized.
425 if (ftw->level == 0) {
426 if (type != DIR_TO_DNE) {
429 if (strcmp(&path[base], "..") == 0)
436 nlen = strlen(path) - base;
437 target_mid = to.target_end;
438 if (*p != '/' && target_mid[-1] != '/')
441 if (target_mid - to.p_path + nlen >= PATH_MAX) {
442 SLOG("%s%s: name too long (not copied)", to.p_path, p);
446 (void)strncat(target_mid, p, nlen);
447 to.p_end = target_mid + nlen;
449 STRIP_TRAILING_SLASH(to);
452 /* Not an error but need to remember it happened */
453 if (stat(to.p_path, &to_stat) == -1)
456 if (to_stat.st_dev == statp->st_dev &&
457 to_stat.st_ino == statp->st_ino) {
458 SLOG("%s and %s are identical (not copied).", to.p_path, path);
460 if (S_ISDIR(statp->st_mode))
461 /* without using glibc extension FTW_ACTIONRETVAL cant handle this */
462 return FTW_SKIP_SUBTREE;
465 if (!S_ISDIR(statp->st_mode) &&
466 S_ISDIR(to_stat.st_mode)) {
467 SLOG("cannot overwrite directory %s with "
476 /* Convert basename to appropiate volume encoding */
477 if (dvolume.vol->v_path) {
478 if ((convert_dots_encoding(&svolume, &dvolume, to.p_path, MAXPATHLEN)) == -1) {
479 SLOG("Error converting name for %s", to.p_path);
485 switch (statp->st_mode & S_IFMT) {
487 if (ftw_copy_link(ftw, path, statp, !dne))
492 SLOG("%s is a directory", path);
497 * If the directory doesn't exist, create the new
498 * one with the from file mode plus owner RWX bits,
499 * modified by the umask. Trade-off between being
500 * able to write the directory (if from directory is
501 * 555) and not causing a permissions race. If the
502 * umask blocks owner writes, we fail..
505 if (mkdir(to.p_path, statp->st_mode | S_IRWXU) < 0)
506 ERROR("mkdir: %s: %s", to.p_path, strerror(errno));
507 } else if (!S_ISDIR(to_stat.st_mode)) {
509 ERROR("%s", to.p_path);
512 /* Create ad dir and copy ".Parent" */
513 if (dvolume.vol->v_path && ADVOL_V2_OR_EA(dvolume.vol->v_adouble)) {
514 mode_t omask = umask(0);
515 if (dvolume.vol->v_adouble == AD_VERSION2) {
516 /* Create ".AppleDouble" dir */
517 bstring addir = bfromcstr(to.p_path);
518 bcatcstr(addir, "/.AppleDouble");
519 mkdir(cfrombstr(addir), 02777);
523 if (svolume.vol->v_path && ADVOL_V2_OR_EA(svolume.vol->v_adouble)) {
524 /* copy metadata file */
525 if (dvolume.vol->vfs->vfs_copyfile(dvolume.vol, -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.vol);
547 if (ad_open(&ad, to.p_path, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) != 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 if (dvolume.vol->v_adouble == AD_VERSION2)
552 ad_setname(&ad, utompath(dvolume.vol, basename(to.p_path)));
553 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
554 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
555 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
556 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
558 ad_close(&ad, ADFLAGS_HF);
564 if (setfile(statp, -1))
567 if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0)
575 SLOG("%s is a device file (not copied).", path);
578 SLOG("%s is a socket (not copied).", path);
581 SLOG("%s is a FIFO (not copied).", path);
584 if (ftw_copy_file(ftw, path, statp, dne))
587 if (dvolume.vol->v_path && ADVOL_V2_OR_EA(dvolume.vol->v_adouble)) {
589 mode_t omask = umask(0);
590 if (svolume.vol->v_path && ADVOL_V2_OR_EA(svolume.vol->v_adouble)) {
592 if (dvolume.vol->vfs->vfs_copyfile(dvolume.vol, -1, path, to.p_path)) {
593 SLOG("Error copying adouble for %s -> %s", path, to.p_path);
599 /* Get CNID of Parent and add new childir to CNID database */
602 if ((cnid = cnid_for_path(&dvolume, to.p_path, &did)) == CNID_INVALID) {
603 SLOG("Error resolving CNID for %s", to.p_path);
610 if (lstat(to.p_path, &st) != 0) {
614 ad_init(&ad, dvolume.vol);
615 if (ad_open(&ad, to.p_path, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) != 0) {
616 ERROR("Error opening adouble for: %s", to.p_path);
618 ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp);
619 if (dvolume.vol->v_adouble == AD_VERSION2)
620 ad_setname(&ad, utompath(dvolume.vol, basename(to.p_path)));
621 ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
622 ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
623 ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
624 ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
626 ad_close(&ad, ADFLAGS_HF);
632 (void)printf("%s -> %s\n", path, to.p_path);
637 /* Memory strategy threshold, in pages: if physmem is larger then this, use a large buffer */
638 #define PHYSPAGES_THRESHOLD (32*1024)
640 /* Maximum buffer size in bytes - do not allow it to grow larger than this */
641 #define BUFSIZE_MAX (2*1024*1024)
643 /* Small (default) buffer size in bytes. It's inefficient for this to be smaller than MAXPHYS */
644 #define MAXPHYS (64 * 1024)
645 #define BUFSIZE_SMALL (MAXPHYS)
647 static int ftw_copy_file(const struct FTW *entp,
649 const struct stat *sp,
652 static char *buf = NULL;
653 static size_t bufsize;
657 int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
661 if ((from_fd = open(spath, O_RDONLY, 0)) == -1) {
662 SLOG("%s: %s", spath, strerror(errno));
667 * If the file exists and we're interactive, verify with the user.
668 * If the file DNE, set the mode to be the from file, minus setuid
669 * bits, modified by the umask; arguably wrong, but it makes copying
670 * executables work right and it's been that way forever. (The
671 * other choice is 666 or'ed with the execute bits on the from file
672 * modified by the umask.)
675 #define YESNO "(y/n [n]) "
678 printf("%s not overwritten\n", to.p_path);
679 (void)close(from_fd);
682 (void)fprintf(stderr, "overwrite %s? %s",
684 checkch = ch = getchar();
685 while (ch != '\n' && ch != EOF)
687 if (checkch != 'y' && checkch != 'Y') {
688 (void)close(from_fd);
689 (void)fprintf(stderr, "not overwritten\n");
695 /* remove existing destination file name,
696 * create a new file */
697 (void)unlink(to.p_path);
698 (void)dvolume.vol->vfs->vfs_deletefile(dvolume.vol, -1, to.p_path);
699 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
700 sp->st_mode & ~(S_ISUID | S_ISGID));
702 /* overwrite existing destination file name */
703 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
706 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
707 sp->st_mode & ~(S_ISUID | S_ISGID));
711 SLOG("%s: %s", to.p_path, strerror(errno));
712 (void)close(from_fd);
719 * Mmap and write if less than 8M (the limit is so we don't totally
720 * trash memory on big files. This is really a minor hack, but it
721 * wins some CPU back.
722 * Some filesystems, such as smbnetfs, don't support mmap,
723 * so this is a best-effort attempt.
726 if (S_ISREG(sp->st_mode) && sp->st_size > 0 &&
727 sp->st_size <= 8 * 1024 * 1024 &&
728 (p = mmap(NULL, (size_t)sp->st_size, PROT_READ,
729 MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
731 for (bufp = p, wresid = sp->st_size; ;
732 bufp += wcount, wresid -= (size_t)wcount) {
733 wcount = write(to_fd, bufp, wresid);
737 if (wcount >= (ssize_t)wresid)
740 if (wcount != (ssize_t)wresid) {
741 SLOG("%s: %s", to.p_path, strerror(errno));
744 /* Some systems don't unmap on close(2). */
745 if (munmap(p, sp->st_size) < 0) {
746 SLOG("%s: %s", spath, strerror(errno));
752 * Note that buf and bufsize are static. If
753 * malloc() fails, it will fail at the start
754 * and not copy only some files.
756 if (sysconf(_SC_PHYS_PAGES) >
758 bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
760 bufsize = BUFSIZE_SMALL;
761 buf = malloc(bufsize);
763 ERROR("Not enough memory");
767 while ((rcount = read(from_fd, buf, bufsize)) > 0) {
768 for (bufp = buf, wresid = rcount; ;
769 bufp += wcount, wresid -= wcount) {
770 wcount = write(to_fd, bufp, wresid);
774 if (wcount >= (ssize_t)wresid)
777 if (wcount != (ssize_t)wresid) {
778 SLOG("%s: %s", to.p_path, strerror(errno));
784 SLOG("%s: %s", spath, strerror(errno));
790 * Don't remove the target even after an error. The target might
791 * not be a regular file, or its attributes might be important,
792 * or its contents might be irreplaceable. It would only be safe
793 * to remove it if we created it and its length is 0.
796 if (pflag && setfile(sp, to_fd))
798 if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
801 SLOG("%s: %s", to.p_path, strerror(errno));
805 (void)close(from_fd);
810 static int ftw_copy_link(const struct FTW *p,
812 const struct stat *sstp,
816 char llink[PATH_MAX];
818 if ((len = readlink(spath, llink, sizeof(llink) - 1)) == -1) {
819 SLOG("readlink: %s: %s", spath, strerror(errno));
823 if (exists && unlink(to.p_path)) {
824 SLOG("unlink: %s: %s", to.p_path, strerror(errno));
827 if (symlink(llink, to.p_path)) {
828 SLOG("symlink: %s: %s", llink, strerror(errno));
831 return (pflag ? setfile(sstp, -1) : 0);
834 static int setfile(const struct stat *fs, int fd)
836 static struct timeval tv[2];
838 int rval, gotstat, islink, fdval;
843 islink = !fdval && S_ISLNK(fs->st_mode);
844 mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
846 #if defined(__FreeBSD__)
847 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
848 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
850 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
851 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
854 if (utimes(to.p_path, tv)) {
855 SLOG("utimes: %s", to.p_path);
858 if (fdval ? fstat(fd, &ts) :
859 (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
863 ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
864 S_IRWXU | S_IRWXG | S_IRWXO;
867 * Changing the ownership probably won't succeed, unless we're root
868 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
869 * the mode; current BSD behavior is to remove all setuid bits on
870 * chown. If chown fails, lose setuid/setgid bits.
872 if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
873 if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
874 (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
875 chown(to.p_path, fs->st_uid, fs->st_gid))) {
876 if (errno != EPERM) {
877 SLOG("chown: %s: %s", to.p_path, strerror(errno));
880 mode &= ~(S_ISUID | S_ISGID);
883 if (!gotstat || mode != ts.st_mode)
884 if (fdval ? fchmod(fd, mode) : chmod(to.p_path, mode)) {
885 SLOG("chmod: %s: %s", to.p_path, strerror(errno));
890 if (!gotstat || fs->st_flags != ts.st_flags)
892 fchflags(fd, fs->st_flags) :
893 (islink ? lchflags(to.p_path, fs->st_flags) :
894 chflags(to.p_path, fs->st_flags))) {
895 SLOG("chflags: %s: %s", to.p_path, strerror(errno));
903 static int preserve_fd_acls(int source_fd, int dest_fd)
908 int acl_supported = 0, ret, trivial;
910 ret = fpathconf(source_fd, _PC_ACL_NFS4);
913 acl_type = ACL_TYPE_NFS4;
914 } else if (ret < 0 && errno != EINVAL) {
915 warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
918 if (acl_supported == 0) {
919 ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
922 acl_type = ACL_TYPE_ACCESS;
923 } else if (ret < 0 && errno != EINVAL) {
924 warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
929 if (acl_supported == 0)
932 acl = acl_get_fd_np(source_fd, acl_type);
934 warn("failed to get acl entries while setting %s", to.p_path);
937 if (acl_is_trivial_np(acl, &trivial)) {
938 warn("acl_is_trivial() failed for %s", to.p_path);
946 if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
947 warn("failed to set acl entries for %s", to.p_path);
956 static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
959 acl_t (*aclgetf)(const char *, acl_type_t);
960 int (*aclsetf)(const char *, acl_type_t, acl_t);
964 int acl_supported = 0, ret, trivial;
966 ret = pathconf(source_dir, _PC_ACL_NFS4);
969 acl_type = ACL_TYPE_NFS4;
970 } else if (ret < 0 && errno != EINVAL) {
971 warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
974 if (acl_supported == 0) {
975 ret = pathconf(source_dir, _PC_ACL_EXTENDED);
978 acl_type = ACL_TYPE_ACCESS;
979 } else if (ret < 0 && errno != EINVAL) {
980 warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
985 if (acl_supported == 0)
989 * If the file is a link we will not follow it
991 if (S_ISLNK(fs->st_mode)) {
992 aclgetf = acl_get_link_np;
993 aclsetf = acl_set_link_np;
995 aclgetf = acl_get_file;
996 aclsetf = acl_set_file;
998 if (acl_type == ACL_TYPE_ACCESS) {
1000 * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
1001 * size ACL will be returned. So it is not safe to simply
1002 * check the pointer to see if the default ACL is present.
1004 acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
1006 warn("failed to get default acl entries on %s",
1010 aclp = &acl->ats_acl;
1011 if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
1012 ACL_TYPE_DEFAULT, acl) < 0) {
1013 warn("failed to set default acl entries on %s",
1020 acl = aclgetf(source_dir, acl_type);
1022 warn("failed to get acl entries on %s", source_dir);
1025 if (acl_is_trivial_np(acl, &trivial)) {
1026 warn("acl_is_trivial() failed on %s", source_dir);
1034 if (aclsetf(dest_dir, acl_type, acl) < 0) {
1035 warn("failed to set acl entries on %s", dest_dir);