1*b6787da4Schristos /* $NetBSD: utils.c,v 1.50 2024/01/15 17:41:06 christos Exp $ */
249f0ad86Scgd
3e40b0636Smycroft /*-
4e40b0636Smycroft * Copyright (c) 1991, 1993, 1994
5e40b0636Smycroft * The Regents of the University of California. All rights reserved.
6e40b0636Smycroft *
7e40b0636Smycroft * Redistribution and use in source and binary forms, with or without
8e40b0636Smycroft * modification, are permitted provided that the following conditions
9e40b0636Smycroft * are met:
10e40b0636Smycroft * 1. Redistributions of source code must retain the above copyright
11e40b0636Smycroft * notice, this list of conditions and the following disclaimer.
12e40b0636Smycroft * 2. Redistributions in binary form must reproduce the above copyright
13e40b0636Smycroft * notice, this list of conditions and the following disclaimer in the
14e40b0636Smycroft * documentation and/or other materials provided with the distribution.
15b5b29542Sagc * 3. Neither the name of the University nor the names of its contributors
16e40b0636Smycroft * may be used to endorse or promote products derived from this software
17e40b0636Smycroft * without specific prior written permission.
18e40b0636Smycroft *
19e40b0636Smycroft * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20e40b0636Smycroft * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21e40b0636Smycroft * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22e40b0636Smycroft * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23e40b0636Smycroft * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24e40b0636Smycroft * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25e40b0636Smycroft * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26e40b0636Smycroft * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27e40b0636Smycroft * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28e40b0636Smycroft * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29e40b0636Smycroft * SUCH DAMAGE.
30e40b0636Smycroft */
31e40b0636Smycroft
32efc20a30Sthorpej #include <sys/cdefs.h>
33e40b0636Smycroft #ifndef lint
3449f0ad86Scgd #if 0
3549f0ad86Scgd static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94";
3649f0ad86Scgd #else
37*b6787da4Schristos __RCSID("$NetBSD: utils.c,v 1.50 2024/01/15 17:41:06 christos Exp $");
3849f0ad86Scgd #endif
39e40b0636Smycroft #endif /* not lint */
40e40b0636Smycroft
419aa2a9c3Schristos #define _ACL_PRIVATE
42633b06feSwiz #include <sys/mman.h>
43e40b0636Smycroft #include <sys/param.h>
44e40b0636Smycroft #include <sys/stat.h>
45e40b0636Smycroft #include <sys/time.h>
46f6a91933Schristos #ifndef SMALL
479aa2a9c3Schristos #include <sys/acl.h>
48f6a91933Schristos #endif
4932c00d62Smanu #include <sys/extattr.h>
50e40b0636Smycroft
51e40b0636Smycroft #include <err.h>
52e40b0636Smycroft #include <errno.h>
53e40b0636Smycroft #include <fcntl.h>
54e40b0636Smycroft #include <fts.h>
551181f404Stron #include <stdbool.h>
56e40b0636Smycroft #include <stdio.h>
57e40b0636Smycroft #include <stdlib.h>
58e40b0636Smycroft #include <string.h>
59e40b0636Smycroft #include <unistd.h>
60e40b0636Smycroft
61e40b0636Smycroft #include "extern.h"
62e40b0636Smycroft
631181f404Stron #define MMAP_MAX_SIZE (8 * 1048576)
641181f404Stron #define MMAP_MAX_WRITE (64 * 1024)
651181f404Stron
66e40b0636Smycroft int
set_utimes(const char * file,struct stat * fs)67633b06feSwiz set_utimes(const char *file, struct stat *fs)
688a99d7cfSwsanchez {
69c1e351a1Senami struct timespec ts[2];
708a99d7cfSwsanchez
71c1e351a1Senami ts[0] = fs->st_atimespec;
72c1e351a1Senami ts[1] = fs->st_mtimespec;
738a99d7cfSwsanchez
74c1e351a1Senami if (lutimens(file, ts)) {
7580c6af72Senami warn("lutimens: %s", file);
768a99d7cfSwsanchez return (1);
778a99d7cfSwsanchez }
788a99d7cfSwsanchez return (0);
798a99d7cfSwsanchez }
808a99d7cfSwsanchez
81eb8f42a5Schristos struct finfo {
82eb8f42a5Schristos const char *from;
83eb8f42a5Schristos const char *to;
849bb33dceSmrg off_t size;
85eb8f42a5Schristos };
86eb8f42a5Schristos
87eb8f42a5Schristos static void
progress(const struct finfo * fi,off_t written)889bb33dceSmrg progress(const struct finfo *fi, off_t written)
89eb8f42a5Schristos {
90eb8f42a5Schristos int pcent = (int)((100.0 * written) / fi->size);
91eb8f42a5Schristos
92eb8f42a5Schristos pinfo = 0;
939bb33dceSmrg (void)fprintf(stderr, "%s => %s %llu/%llu bytes %d%% written\n",
949bb33dceSmrg fi->from, fi->to, (unsigned long long)written,
959bb33dceSmrg (unsigned long long)fi->size, pcent);
96eb8f42a5Schristos }
97eb8f42a5Schristos
988a99d7cfSwsanchez int
copy_file(FTSENT * entp,int dne)99633b06feSwiz copy_file(FTSENT *entp, int dne)
100e40b0636Smycroft {
101e40b0636Smycroft static char buf[MAXBSIZE];
102e40b0636Smycroft struct stat to_stat, *fs;
103471c8a8eSjschauma int ch, checkch, from_fd, rcount, rval, to_fd, tolnk, wcount;
1041181f404Stron char *p;
1059bb33dceSmrg off_t ptotal = 0;
106e40b0636Smycroft
10722057652Sdarcy /* if hard linking then simply link and return */
10822057652Sdarcy if (lflag) {
10922057652Sdarcy (void)unlink(to.p_path);
11022057652Sdarcy if (link(entp->fts_path, to.p_path)) {
11122057652Sdarcy warn("%s", to.p_path);
11222057652Sdarcy return (1);
11322057652Sdarcy }
11422057652Sdarcy return (0);
11522057652Sdarcy }
11622057652Sdarcy
117e40b0636Smycroft if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
118e40b0636Smycroft warn("%s", entp->fts_path);
119e40b0636Smycroft return (1);
120e40b0636Smycroft }
121e40b0636Smycroft
122471c8a8eSjschauma to_fd = -1;
123e40b0636Smycroft fs = entp->fts_statp;
124471c8a8eSjschauma tolnk = ((Rflag && !(Lflag || Hflag)) || Pflag);
125e40b0636Smycroft
126e40b0636Smycroft /*
127e40b0636Smycroft * If the file exists and we're interactive, verify with the user.
128e40b0636Smycroft * If the file DNE, set the mode to be the from file, minus setuid
129e40b0636Smycroft * bits, modified by the umask; arguably wrong, but it makes copying
130e40b0636Smycroft * executables work right and it's been that way forever. (The
131e40b0636Smycroft * other choice is 666 or'ed with the execute bits on the from file
132e40b0636Smycroft * modified by the umask.)
133e40b0636Smycroft */
134e40b0636Smycroft if (!dne) {
135471c8a8eSjschauma struct stat sb;
136471c8a8eSjschauma int sval;
137471c8a8eSjschauma
138e40b0636Smycroft if (iflag) {
139e40b0636Smycroft (void)fprintf(stderr, "overwrite %s? ", to.p_path);
140e40b0636Smycroft checkch = ch = getchar();
141e40b0636Smycroft while (ch != '\n' && ch != EOF)
142e40b0636Smycroft ch = getchar();
143051219a6Smycroft if (checkch != 'y' && checkch != 'Y') {
144e40b0636Smycroft (void)close(from_fd);
145e40b0636Smycroft return (0);
146e40b0636Smycroft }
147e40b0636Smycroft }
148471c8a8eSjschauma
149471c8a8eSjschauma sval = tolnk ?
150471c8a8eSjschauma lstat(to.p_path, &sb) : stat(to.p_path, &sb);
151471c8a8eSjschauma if (sval == -1) {
152471c8a8eSjschauma warn("stat: %s", to.p_path);
15359dc922dSwiz (void)close(from_fd);
154471c8a8eSjschauma return (1);
155471c8a8eSjschauma }
156471c8a8eSjschauma
157471c8a8eSjschauma if (!(tolnk && S_ISLNK(sb.st_mode)))
158e40b0636Smycroft to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
159e40b0636Smycroft } else
160e40b0636Smycroft to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
161e40b0636Smycroft fs->st_mode & ~(S_ISUID | S_ISGID));
162e40b0636Smycroft
163471c8a8eSjschauma if (to_fd == -1 && (fflag || tolnk)) {
1648a99d7cfSwsanchez /*
1658a99d7cfSwsanchez * attempt to remove existing destination file name and
1668a99d7cfSwsanchez * create a new file
1678a99d7cfSwsanchez */
1688a99d7cfSwsanchez (void)unlink(to.p_path);
1698a99d7cfSwsanchez to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
1708a99d7cfSwsanchez fs->st_mode & ~(S_ISUID | S_ISGID));
1718a99d7cfSwsanchez }
1728a99d7cfSwsanchez
173e40b0636Smycroft if (to_fd == -1) {
174e40b0636Smycroft warn("%s", to.p_path);
175e40b0636Smycroft (void)close(from_fd);
176276fd166Ssimonb return (1);
177e40b0636Smycroft }
178e40b0636Smycroft
179e40b0636Smycroft rval = 0;
180e40b0636Smycroft
181e40b0636Smycroft /*
182*b6787da4Schristos * We always copy regular files, even if they appear to be 0-sized
183*b6787da4Schristos * because kernfs and procfs don't return file sizes.
1848a99d7cfSwsanchez */
185*b6787da4Schristos bool need_copy = S_ISREG(fs->st_mode) || fs->st_size > 0;
1867790b321Schristos
187eb8f42a5Schristos struct finfo fi;
188eb8f42a5Schristos
189eb8f42a5Schristos fi.from = entp->fts_path;
190eb8f42a5Schristos fi.to = to.p_path;
1919bb33dceSmrg fi.size = fs->st_size;
192eb8f42a5Schristos
193efab5715Spooka /*
194ec039eb9Schs * Mmap and write if less than 8M (the limit is so
195ec039eb9Schs * we don't totally trash memory on big files).
196ec039eb9Schs * This is really a minor hack, but it wins some CPU back.
197e40b0636Smycroft */
1987790b321Schristos if (S_ISREG(fs->st_mode) && fs->st_size && fs->st_size <= MMAP_MAX_SIZE) {
199509c72efSchristos size_t fsize = (size_t)fs->st_size;
200509c72efSchristos p = mmap(NULL, fsize, PROT_READ, MAP_FILE|MAP_SHARED,
201509c72efSchristos from_fd, (off_t)0);
2021181f404Stron if (p != MAP_FAILED) {
2031181f404Stron size_t remainder;
2041181f404Stron
2057790b321Schristos need_copy = false;
2061181f404Stron
2077790b321Schristos (void) madvise(p, (size_t)fs->st_size, MADV_SEQUENTIAL);
2081181f404Stron
2091181f404Stron /*
2101181f404Stron * Write out the data in small chunks to
2111181f404Stron * avoid locking the output file for a
2121181f404Stron * long time if the reading the data from
2131181f404Stron * the source is slow.
2141181f404Stron */
2151181f404Stron remainder = fsize;
2161181f404Stron do {
2171181f404Stron ssize_t chunk;
2181181f404Stron
2191181f404Stron chunk = (remainder > MMAP_MAX_WRITE) ?
2201181f404Stron MMAP_MAX_WRITE : remainder;
2211181f404Stron if (write(to_fd, &p[fsize - remainder],
2221181f404Stron chunk) != chunk) {
223e40b0636Smycroft warn("%s", to.p_path);
224e40b0636Smycroft rval = 1;
2251181f404Stron break;
226e40b0636Smycroft }
2271181f404Stron remainder -= chunk;
228eb8f42a5Schristos ptotal += chunk;
229eb8f42a5Schristos if (pinfo)
230eb8f42a5Schristos progress(&fi, ptotal);
2311181f404Stron } while (remainder > 0);
2321181f404Stron
233509c72efSchristos if (munmap(p, fsize) < 0) {
234e40b0636Smycroft warn("%s", entp->fts_path);
235e40b0636Smycroft rval = 1;
236e40b0636Smycroft }
237e40b0636Smycroft }
2381181f404Stron }
2391181f404Stron
2407790b321Schristos if (need_copy) {
241e40b0636Smycroft while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
242509c72efSchristos wcount = write(to_fd, buf, (size_t)rcount);
243e40b0636Smycroft if (rcount != wcount || wcount == -1) {
244e40b0636Smycroft warn("%s", to.p_path);
245e40b0636Smycroft rval = 1;
246e40b0636Smycroft break;
247e40b0636Smycroft }
248eb8f42a5Schristos ptotal += wcount;
249eb8f42a5Schristos if (pinfo)
250eb8f42a5Schristos progress(&fi, ptotal);
251e40b0636Smycroft }
252e40b0636Smycroft if (rcount < 0) {
253e40b0636Smycroft warn("%s", entp->fts_path);
254e40b0636Smycroft rval = 1;
255e40b0636Smycroft }
256e40b0636Smycroft }
257e40b0636Smycroft
25832c00d62Smanu if (pflag && (fcpxattr(from_fd, to_fd) != 0))
25932c00d62Smanu warn("%s: error copying extended attributes", to.p_path);
26032c00d62Smanu
261f6a91933Schristos #ifndef SMALL
2629aa2a9c3Schristos if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
2639aa2a9c3Schristos rval = 1;
264f6a91933Schristos #endif
2659aa2a9c3Schristos
266e40b0636Smycroft (void)close(from_fd);
267d8781269Sdarcy
268d8781269Sdarcy if (rval == 1) {
269e40b0636Smycroft (void)close(to_fd);
270e40b0636Smycroft return (1);
271e40b0636Smycroft }
272e40b0636Smycroft
273e40b0636Smycroft if (pflag && setfile(fs, to_fd))
274e40b0636Smycroft rval = 1;
275e40b0636Smycroft /*
276e40b0636Smycroft * If the source was setuid or setgid, lose the bits unless the
277e40b0636Smycroft * copy is owned by the same user and group.
278e40b0636Smycroft */
279e40b0636Smycroft #define RETAINBITS \
280e40b0636Smycroft (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
281eaef8d4aSjld if (!pflag && dne
282eaef8d4aSjld && fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) {
283e40b0636Smycroft if (fstat(to_fd, &to_stat)) {
284e40b0636Smycroft warn("%s", to.p_path);
285e40b0636Smycroft rval = 1;
286e40b0636Smycroft } else if (fs->st_gid == to_stat.st_gid &&
287e40b0636Smycroft fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) {
288e40b0636Smycroft warn("%s", to.p_path);
289e40b0636Smycroft rval = 1;
290e40b0636Smycroft }
2918d68a9dcSthorpej }
292e40b0636Smycroft if (close(to_fd)) {
293e40b0636Smycroft warn("%s", to.p_path);
294e40b0636Smycroft rval = 1;
295e40b0636Smycroft }
2968a99d7cfSwsanchez /* set the mod/access times now after close of the fd */
2978a99d7cfSwsanchez if (pflag && set_utimes(to.p_path, fs)) {
2988a99d7cfSwsanchez rval = 1;
2998a99d7cfSwsanchez }
300e40b0636Smycroft return (rval);
301e40b0636Smycroft }
302e40b0636Smycroft
303e40b0636Smycroft int
copy_link(FTSENT * p,int exists)304633b06feSwiz copy_link(FTSENT *p, int exists)
305e40b0636Smycroft {
306e40b0636Smycroft int len;
307ce7d7575Smycroft char target[MAXPATHLEN];
308e40b0636Smycroft
30932b88027Sprovos if ((len = readlink(p->fts_path, target, sizeof(target)-1)) == -1) {
310e40b0636Smycroft warn("readlink: %s", p->fts_path);
311e40b0636Smycroft return (1);
312e40b0636Smycroft }
313ce7d7575Smycroft target[len] = '\0';
314e40b0636Smycroft if (exists && unlink(to.p_path)) {
315e40b0636Smycroft warn("unlink: %s", to.p_path);
316e40b0636Smycroft return (1);
317e40b0636Smycroft }
318ce7d7575Smycroft if (symlink(target, to.p_path)) {
319ce7d7575Smycroft warn("symlink: %s", target);
320e40b0636Smycroft return (1);
321e40b0636Smycroft }
322fa580a82Senami return (pflag ? setfile(p->fts_statp, 0) : 0);
323e40b0636Smycroft }
324e40b0636Smycroft
325e40b0636Smycroft int
copy_fifo(struct stat * from_stat,int exists)326633b06feSwiz copy_fifo(struct stat *from_stat, int exists)
327e40b0636Smycroft {
328e40b0636Smycroft if (exists && unlink(to.p_path)) {
329e40b0636Smycroft warn("unlink: %s", to.p_path);
330e40b0636Smycroft return (1);
331e40b0636Smycroft }
332e40b0636Smycroft if (mkfifo(to.p_path, from_stat->st_mode)) {
333e40b0636Smycroft warn("mkfifo: %s", to.p_path);
334e40b0636Smycroft return (1);
335e40b0636Smycroft }
336e40b0636Smycroft return (pflag ? setfile(from_stat, 0) : 0);
337e40b0636Smycroft }
338e40b0636Smycroft
339e40b0636Smycroft int
copy_special(struct stat * from_stat,int exists)340633b06feSwiz copy_special(struct stat *from_stat, int exists)
341e40b0636Smycroft {
342e40b0636Smycroft if (exists && unlink(to.p_path)) {
343e40b0636Smycroft warn("unlink: %s", to.p_path);
344e40b0636Smycroft return (1);
345e40b0636Smycroft }
346e40b0636Smycroft if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
347e40b0636Smycroft warn("mknod: %s", to.p_path);
348e40b0636Smycroft return (1);
349e40b0636Smycroft }
350e40b0636Smycroft return (pflag ? setfile(from_stat, 0) : 0);
351e40b0636Smycroft }
352e40b0636Smycroft
353e40b0636Smycroft
3548a99d7cfSwsanchez /*
3558a99d7cfSwsanchez * Function: setfile
3568a99d7cfSwsanchez *
3578a99d7cfSwsanchez * Purpose:
3588a99d7cfSwsanchez * Set the owner/group/permissions for the "to" file to the information
3598a99d7cfSwsanchez * in the stat structure. If fd is zero, also call set_utimes() to set
3608a99d7cfSwsanchez * the mod/access times. If fd is non-zero, the caller must do a utimes
3618a99d7cfSwsanchez * itself after close(fd).
3628a99d7cfSwsanchez */
363e40b0636Smycroft int
setfile(struct stat * fs,int fd)364633b06feSwiz setfile(struct stat *fs, int fd)
365e40b0636Smycroft {
366fa580a82Senami int rval, islink;
367e40b0636Smycroft
368e40b0636Smycroft rval = 0;
369fa580a82Senami islink = S_ISLNK(fs->st_mode);
370e40b0636Smycroft fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
371e40b0636Smycroft
372e40b0636Smycroft /*
373e40b0636Smycroft * Changing the ownership probably won't succeed, unless we're root
374e40b0636Smycroft * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
375e40b0636Smycroft * the mode; current BSD behavior is to remove all setuid bits on
376e40b0636Smycroft * chown. If chown fails, lose setuid/setgid bits.
377e40b0636Smycroft */
378e40b0636Smycroft if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
379fa580a82Senami lchown(to.p_path, fs->st_uid, fs->st_gid)) {
380e40b0636Smycroft if (errno != EPERM) {
381e40b0636Smycroft warn("chown: %s", to.p_path);
382e40b0636Smycroft rval = 1;
383e40b0636Smycroft }
384e40b0636Smycroft fs->st_mode &= ~(S_ISUID | S_ISGID);
385e40b0636Smycroft }
386fa580a82Senami if (fd ? fchmod(fd, fs->st_mode) : lchmod(to.p_path, fs->st_mode)) {
387688ecf2aSmycroft warn("chmod: %s", to.p_path);
388e40b0636Smycroft rval = 1;
389e40b0636Smycroft }
390e40b0636Smycroft
39176ed05e8Selad if (!islink && !Nflag) {
392509c72efSchristos unsigned long fflags = fs->st_flags;
3934f2bfe8bScgd /*
3944f2bfe8bScgd * XXX
395fa580a82Senami * NFS doesn't support chflags; ignore errors unless
396fa580a82Senami * there's reason to believe we're losing bits.
397fa580a82Senami * (Note, this still won't be right if the server
398fa580a82Senami * supports flags and we were trying to *remove* flags
3994f2bfe8bScgd * on a file that we copied, i.e., that we didn't create.)
4004f2bfe8bScgd */
4014f2bfe8bScgd errno = 0;
402509c72efSchristos if ((fd ? fchflags(fd, fflags) :
403509c72efSchristos chflags(to.p_path, fflags)) == -1)
4044f2bfe8bScgd if (errno != EOPNOTSUPP || fs->st_flags != 0) {
405e40b0636Smycroft warn("chflags: %s", to.p_path);
406e40b0636Smycroft rval = 1;
407e40b0636Smycroft }
408fa580a82Senami }
4098a99d7cfSwsanchez /* if fd is non-zero, caller must call set_utimes() after close() */
4108a99d7cfSwsanchez if (fd == 0 && set_utimes(to.p_path, fs))
4118a99d7cfSwsanchez rval = 1;
412e40b0636Smycroft return (rval);
413e40b0636Smycroft }
414e40b0636Smycroft
415f6a91933Schristos #ifndef SMALL
4169aa2a9c3Schristos int
preserve_fd_acls(int source_fd,int dest_fd)4179aa2a9c3Schristos preserve_fd_acls(int source_fd, int dest_fd)
4189aa2a9c3Schristos {
4199aa2a9c3Schristos acl_t acl;
4209aa2a9c3Schristos acl_type_t acl_type;
4219aa2a9c3Schristos int acl_supported = 0, ret, trivial;
4229aa2a9c3Schristos
4239aa2a9c3Schristos ret = fpathconf(source_fd, _PC_ACL_NFS4);
4249aa2a9c3Schristos if (ret > 0 ) {
4259aa2a9c3Schristos acl_supported = 1;
4269aa2a9c3Schristos acl_type = ACL_TYPE_NFS4;
4279aa2a9c3Schristos } else if (ret < 0 && errno != EINVAL) {
4289aa2a9c3Schristos warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
4299aa2a9c3Schristos return (1);
4309aa2a9c3Schristos }
4319aa2a9c3Schristos if (acl_supported == 0) {
4329aa2a9c3Schristos ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
4339aa2a9c3Schristos if (ret > 0 ) {
4349aa2a9c3Schristos acl_supported = 1;
4359aa2a9c3Schristos acl_type = ACL_TYPE_ACCESS;
4369aa2a9c3Schristos } else if (ret < 0 && errno != EINVAL) {
4379aa2a9c3Schristos warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
4389aa2a9c3Schristos to.p_path);
4399aa2a9c3Schristos return (1);
4409aa2a9c3Schristos }
4419aa2a9c3Schristos }
4429aa2a9c3Schristos if (acl_supported == 0)
4439aa2a9c3Schristos return (0);
4449aa2a9c3Schristos
4459aa2a9c3Schristos acl = acl_get_fd_np(source_fd, acl_type);
4469aa2a9c3Schristos if (acl == NULL) {
4479aa2a9c3Schristos warn("failed to get acl entries while setting %s", to.p_path);
4489aa2a9c3Schristos return (1);
4499aa2a9c3Schristos }
4509aa2a9c3Schristos if (acl_is_trivial_np(acl, &trivial)) {
4519aa2a9c3Schristos warn("acl_is_trivial() failed for %s", to.p_path);
4529aa2a9c3Schristos acl_free(acl);
4539aa2a9c3Schristos return (1);
4549aa2a9c3Schristos }
4559aa2a9c3Schristos if (trivial) {
4569aa2a9c3Schristos acl_free(acl);
4579aa2a9c3Schristos return (0);
4589aa2a9c3Schristos }
4599aa2a9c3Schristos if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
4609aa2a9c3Schristos warn("failed to set acl entries for %s", to.p_path);
4619aa2a9c3Schristos acl_free(acl);
4629aa2a9c3Schristos return (1);
4639aa2a9c3Schristos }
4649aa2a9c3Schristos acl_free(acl);
4659aa2a9c3Schristos return (0);
4669aa2a9c3Schristos }
4679aa2a9c3Schristos
4689aa2a9c3Schristos int
preserve_dir_acls(struct stat * fs,char * source_dir,char * dest_dir)4699aa2a9c3Schristos preserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir)
4709aa2a9c3Schristos {
4719aa2a9c3Schristos acl_t (*aclgetf)(const char *, acl_type_t);
4729aa2a9c3Schristos int (*aclsetf)(const char *, acl_type_t, acl_t);
4739aa2a9c3Schristos struct acl *aclp;
4749aa2a9c3Schristos acl_t acl;
4759aa2a9c3Schristos acl_type_t acl_type;
4769aa2a9c3Schristos int acl_supported = 0, ret, trivial;
4779aa2a9c3Schristos
4789aa2a9c3Schristos ret = pathconf(source_dir, _PC_ACL_NFS4);
4799aa2a9c3Schristos if (ret > 0) {
4809aa2a9c3Schristos acl_supported = 1;
4819aa2a9c3Schristos acl_type = ACL_TYPE_NFS4;
4829aa2a9c3Schristos } else if (ret < 0 && errno != EINVAL) {
4839aa2a9c3Schristos warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
4849aa2a9c3Schristos return (1);
4859aa2a9c3Schristos }
4869aa2a9c3Schristos if (acl_supported == 0) {
4879aa2a9c3Schristos ret = pathconf(source_dir, _PC_ACL_EXTENDED);
4889aa2a9c3Schristos if (ret > 0) {
4899aa2a9c3Schristos acl_supported = 1;
4909aa2a9c3Schristos acl_type = ACL_TYPE_ACCESS;
4919aa2a9c3Schristos } else if (ret < 0 && errno != EINVAL) {
4929aa2a9c3Schristos warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
4939aa2a9c3Schristos source_dir);
4949aa2a9c3Schristos return (1);
4959aa2a9c3Schristos }
4969aa2a9c3Schristos }
4979aa2a9c3Schristos if (acl_supported == 0)
4989aa2a9c3Schristos return (0);
4999aa2a9c3Schristos
5009aa2a9c3Schristos /*
5019aa2a9c3Schristos * If the file is a link we will not follow it.
5029aa2a9c3Schristos */
5039aa2a9c3Schristos if (S_ISLNK(fs->st_mode)) {
5049aa2a9c3Schristos aclgetf = acl_get_link_np;
5059aa2a9c3Schristos aclsetf = acl_set_link_np;
5069aa2a9c3Schristos } else {
5079aa2a9c3Schristos aclgetf = acl_get_file;
5089aa2a9c3Schristos aclsetf = acl_set_file;
5099aa2a9c3Schristos }
5109aa2a9c3Schristos if (acl_type == ACL_TYPE_ACCESS) {
5119aa2a9c3Schristos /*
5129aa2a9c3Schristos * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
5139aa2a9c3Schristos * size ACL will be returned. So it is not safe to simply
5149aa2a9c3Schristos * check the pointer to see if the default ACL is present.
5159aa2a9c3Schristos */
5169aa2a9c3Schristos acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
5179aa2a9c3Schristos if (acl == NULL) {
5189aa2a9c3Schristos warn("failed to get default acl entries on %s",
5199aa2a9c3Schristos source_dir);
5209aa2a9c3Schristos return (1);
5219aa2a9c3Schristos }
5229aa2a9c3Schristos aclp = &acl->ats_acl;
5239aa2a9c3Schristos if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
5249aa2a9c3Schristos ACL_TYPE_DEFAULT, acl) < 0) {
5259aa2a9c3Schristos warn("failed to set default acl entries on %s",
5269aa2a9c3Schristos dest_dir);
5279aa2a9c3Schristos acl_free(acl);
5289aa2a9c3Schristos return (1);
5299aa2a9c3Schristos }
5309aa2a9c3Schristos acl_free(acl);
5319aa2a9c3Schristos }
5329aa2a9c3Schristos acl = aclgetf(source_dir, acl_type);
5339aa2a9c3Schristos if (acl == NULL) {
5349aa2a9c3Schristos warn("failed to get acl entries on %s", source_dir);
5359aa2a9c3Schristos return (1);
5369aa2a9c3Schristos }
5379aa2a9c3Schristos if (acl_is_trivial_np(acl, &trivial)) {
5389aa2a9c3Schristos warn("acl_is_trivial() failed on %s", source_dir);
5399aa2a9c3Schristos acl_free(acl);
5409aa2a9c3Schristos return (1);
5419aa2a9c3Schristos }
5429aa2a9c3Schristos if (trivial) {
5439aa2a9c3Schristos acl_free(acl);
5449aa2a9c3Schristos return (0);
5459aa2a9c3Schristos }
5469aa2a9c3Schristos if (aclsetf(dest_dir, acl_type, acl) < 0) {
5479aa2a9c3Schristos warn("failed to set acl entries on %s", dest_dir);
5489aa2a9c3Schristos acl_free(acl);
5499aa2a9c3Schristos return (1);
5509aa2a9c3Schristos }
5519aa2a9c3Schristos acl_free(acl);
5529aa2a9c3Schristos return (0);
5539aa2a9c3Schristos }
554f6a91933Schristos #endif
5559aa2a9c3Schristos
556e40b0636Smycroft void
usage(void)557633b06feSwiz usage(void)
558e40b0636Smycroft {
559633b06feSwiz (void)fprintf(stderr,
560d8781269Sdarcy "usage: %s [-R [-H | -L | -P]] [-f | -i] [-alNpv] src target\n"
561d8781269Sdarcy " %s [-R [-H | -L | -P]] [-f | -i] [-alNpv] src1 ... srcN directory\n",
562633b06feSwiz getprogname(), getprogname());
563e40b0636Smycroft exit(1);
5649dc385beSmycroft /* NOTREACHED */
565e40b0636Smycroft }
566