1 1.50 christos /* $NetBSD: utils.c,v 1.50 2024/01/15 17:41:06 christos Exp $ */ 2 1.3 cgd 3 1.1 mycroft /*- 4 1.1 mycroft * Copyright (c) 1991, 1993, 1994 5 1.1 mycroft * The Regents of the University of California. All rights reserved. 6 1.1 mycroft * 7 1.1 mycroft * Redistribution and use in source and binary forms, with or without 8 1.1 mycroft * modification, are permitted provided that the following conditions 9 1.1 mycroft * are met: 10 1.1 mycroft * 1. Redistributions of source code must retain the above copyright 11 1.1 mycroft * notice, this list of conditions and the following disclaimer. 12 1.1 mycroft * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 mycroft * notice, this list of conditions and the following disclaimer in the 14 1.1 mycroft * documentation and/or other materials provided with the distribution. 15 1.25 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 mycroft * may be used to endorse or promote products derived from this software 17 1.1 mycroft * without specific prior written permission. 18 1.1 mycroft * 19 1.1 mycroft * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 mycroft * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 mycroft * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 mycroft * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 mycroft * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 mycroft * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 mycroft * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 mycroft * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 mycroft * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 mycroft * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 mycroft * SUCH DAMAGE. 30 1.1 mycroft */ 31 1.1 mycroft 32 1.8 thorpej #include <sys/cdefs.h> 33 1.1 mycroft #ifndef lint 34 1.3 cgd #if 0 35 1.3 cgd static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; 36 1.3 cgd #else 37 1.50 christos __RCSID("$NetBSD: utils.c,v 1.50 2024/01/15 17:41:06 christos Exp $"); 38 1.3 cgd #endif 39 1.1 mycroft #endif /* not lint */ 40 1.1 mycroft 41 1.48 christos #define _ACL_PRIVATE 42 1.20 wiz #include <sys/mman.h> 43 1.1 mycroft #include <sys/param.h> 44 1.1 mycroft #include <sys/stat.h> 45 1.1 mycroft #include <sys/time.h> 46 1.49 christos #ifndef SMALL 47 1.48 christos #include <sys/acl.h> 48 1.49 christos #endif 49 1.40 manu #include <sys/extattr.h> 50 1.1 mycroft 51 1.1 mycroft #include <err.h> 52 1.1 mycroft #include <errno.h> 53 1.1 mycroft #include <fcntl.h> 54 1.1 mycroft #include <fts.h> 55 1.36 tron #include <stdbool.h> 56 1.1 mycroft #include <stdio.h> 57 1.1 mycroft #include <stdlib.h> 58 1.1 mycroft #include <string.h> 59 1.1 mycroft #include <unistd.h> 60 1.1 mycroft 61 1.1 mycroft #include "extern.h" 62 1.1 mycroft 63 1.36 tron #define MMAP_MAX_SIZE (8 * 1048576) 64 1.36 tron #define MMAP_MAX_WRITE (64 * 1024) 65 1.36 tron 66 1.1 mycroft int 67 1.20 wiz set_utimes(const char *file, struct stat *fs) 68 1.16 wsanchez { 69 1.43 enami struct timespec ts[2]; 70 1.16 wsanchez 71 1.43 enami ts[0] = fs->st_atimespec; 72 1.43 enami ts[1] = fs->st_mtimespec; 73 1.16 wsanchez 74 1.43 enami if (lutimens(file, ts)) { 75 1.44 enami warn("lutimens: %s", file); 76 1.16 wsanchez return (1); 77 1.16 wsanchez } 78 1.16 wsanchez return (0); 79 1.16 wsanchez } 80 1.16 wsanchez 81 1.41 christos struct finfo { 82 1.41 christos const char *from; 83 1.41 christos const char *to; 84 1.45 mrg off_t size; 85 1.41 christos }; 86 1.41 christos 87 1.41 christos static void 88 1.45 mrg progress(const struct finfo *fi, off_t written) 89 1.41 christos { 90 1.41 christos int pcent = (int)((100.0 * written) / fi->size); 91 1.41 christos 92 1.41 christos pinfo = 0; 93 1.45 mrg (void)fprintf(stderr, "%s => %s %llu/%llu bytes %d%% written\n", 94 1.45 mrg fi->from, fi->to, (unsigned long long)written, 95 1.45 mrg (unsigned long long)fi->size, pcent); 96 1.41 christos } 97 1.41 christos 98 1.16 wsanchez int 99 1.20 wiz copy_file(FTSENT *entp, int dne) 100 1.1 mycroft { 101 1.1 mycroft static char buf[MAXBSIZE]; 102 1.1 mycroft struct stat to_stat, *fs; 103 1.32 jschauma int ch, checkch, from_fd, rcount, rval, to_fd, tolnk, wcount; 104 1.36 tron char *p; 105 1.45 mrg off_t ptotal = 0; 106 1.46 darcy 107 1.46 darcy /* if hard linking then simply link and return */ 108 1.46 darcy if (lflag) { 109 1.46 darcy (void)unlink(to.p_path); 110 1.46 darcy if (link(entp->fts_path, to.p_path)) { 111 1.46 darcy warn("%s", to.p_path); 112 1.46 darcy return (1); 113 1.46 darcy } 114 1.46 darcy return (0); 115 1.46 darcy } 116 1.46 darcy 117 1.1 mycroft if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { 118 1.1 mycroft warn("%s", entp->fts_path); 119 1.1 mycroft return (1); 120 1.1 mycroft } 121 1.1 mycroft 122 1.32 jschauma to_fd = -1; 123 1.1 mycroft fs = entp->fts_statp; 124 1.32 jschauma tolnk = ((Rflag && !(Lflag || Hflag)) || Pflag); 125 1.1 mycroft 126 1.1 mycroft /* 127 1.1 mycroft * If the file exists and we're interactive, verify with the user. 128 1.1 mycroft * If the file DNE, set the mode to be the from file, minus setuid 129 1.1 mycroft * bits, modified by the umask; arguably wrong, but it makes copying 130 1.1 mycroft * executables work right and it's been that way forever. (The 131 1.1 mycroft * other choice is 666 or'ed with the execute bits on the from file 132 1.1 mycroft * modified by the umask.) 133 1.1 mycroft */ 134 1.1 mycroft if (!dne) { 135 1.32 jschauma struct stat sb; 136 1.32 jschauma int sval; 137 1.32 jschauma 138 1.1 mycroft if (iflag) { 139 1.1 mycroft (void)fprintf(stderr, "overwrite %s? ", to.p_path); 140 1.1 mycroft checkch = ch = getchar(); 141 1.1 mycroft while (ch != '\n' && ch != EOF) 142 1.1 mycroft ch = getchar(); 143 1.2 mycroft if (checkch != 'y' && checkch != 'Y') { 144 1.1 mycroft (void)close(from_fd); 145 1.1 mycroft return (0); 146 1.1 mycroft } 147 1.1 mycroft } 148 1.32 jschauma 149 1.32 jschauma sval = tolnk ? 150 1.32 jschauma lstat(to.p_path, &sb) : stat(to.p_path, &sb); 151 1.32 jschauma if (sval == -1) { 152 1.32 jschauma warn("stat: %s", to.p_path); 153 1.38 wiz (void)close(from_fd); 154 1.32 jschauma return (1); 155 1.32 jschauma } 156 1.32 jschauma 157 1.32 jschauma if (!(tolnk && S_ISLNK(sb.st_mode))) 158 1.32 jschauma to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); 159 1.1 mycroft } else 160 1.1 mycroft to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 161 1.1 mycroft fs->st_mode & ~(S_ISUID | S_ISGID)); 162 1.1 mycroft 163 1.32 jschauma if (to_fd == -1 && (fflag || tolnk)) { 164 1.16 wsanchez /* 165 1.16 wsanchez * attempt to remove existing destination file name and 166 1.16 wsanchez * create a new file 167 1.16 wsanchez */ 168 1.16 wsanchez (void)unlink(to.p_path); 169 1.16 wsanchez to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 170 1.16 wsanchez fs->st_mode & ~(S_ISUID | S_ISGID)); 171 1.16 wsanchez } 172 1.16 wsanchez 173 1.1 mycroft if (to_fd == -1) { 174 1.1 mycroft warn("%s", to.p_path); 175 1.1 mycroft (void)close(from_fd); 176 1.23 simonb return (1); 177 1.1 mycroft } 178 1.1 mycroft 179 1.1 mycroft rval = 0; 180 1.1 mycroft 181 1.47 christos /* 182 1.50 christos * We always copy regular files, even if they appear to be 0-sized 183 1.50 christos * because kernfs and procfs don't return file sizes. 184 1.1 mycroft */ 185 1.50 christos bool need_copy = S_ISREG(fs->st_mode) || fs->st_size > 0; 186 1.41 christos 187 1.47 christos struct finfo fi; 188 1.41 christos 189 1.47 christos fi.from = entp->fts_path; 190 1.47 christos fi.to = to.p_path; 191 1.47 christos fi.size = fs->st_size; 192 1.19 chs 193 1.47 christos /* 194 1.47 christos * Mmap and write if less than 8M (the limit is so 195 1.47 christos * we don't totally trash memory on big files). 196 1.47 christos * This is really a minor hack, but it wins some CPU back. 197 1.47 christos */ 198 1.47 christos if (S_ISREG(fs->st_mode) && fs->st_size && fs->st_size <= MMAP_MAX_SIZE) { 199 1.47 christos size_t fsize = (size_t)fs->st_size; 200 1.47 christos p = mmap(NULL, fsize, PROT_READ, MAP_FILE|MAP_SHARED, 201 1.47 christos from_fd, (off_t)0); 202 1.47 christos if (p != MAP_FAILED) { 203 1.47 christos size_t remainder; 204 1.47 christos 205 1.47 christos need_copy = false; 206 1.47 christos 207 1.47 christos (void) madvise(p, (size_t)fs->st_size, MADV_SEQUENTIAL); 208 1.47 christos 209 1.47 christos /* 210 1.47 christos * Write out the data in small chunks to 211 1.47 christos * avoid locking the output file for a 212 1.47 christos * long time if the reading the data from 213 1.47 christos * the source is slow. 214 1.47 christos */ 215 1.47 christos remainder = fsize; 216 1.47 christos do { 217 1.47 christos ssize_t chunk; 218 1.47 christos 219 1.47 christos chunk = (remainder > MMAP_MAX_WRITE) ? 220 1.47 christos MMAP_MAX_WRITE : remainder; 221 1.47 christos if (write(to_fd, &p[fsize - remainder], 222 1.47 christos chunk) != chunk) { 223 1.16 wsanchez warn("%s", to.p_path); 224 1.16 wsanchez rval = 1; 225 1.16 wsanchez break; 226 1.16 wsanchez } 227 1.47 christos remainder -= chunk; 228 1.47 christos ptotal += chunk; 229 1.41 christos if (pinfo) 230 1.41 christos progress(&fi, ptotal); 231 1.47 christos } while (remainder > 0); 232 1.47 christos 233 1.47 christos if (munmap(p, fsize) < 0) { 234 1.47 christos warn("%s", entp->fts_path); 235 1.47 christos rval = 1; 236 1.16 wsanchez } 237 1.47 christos } 238 1.47 christos } 239 1.47 christos 240 1.47 christos if (need_copy) { 241 1.47 christos while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 242 1.47 christos wcount = write(to_fd, buf, (size_t)rcount); 243 1.47 christos if (rcount != wcount || wcount == -1) { 244 1.47 christos warn("%s", to.p_path); 245 1.1 mycroft rval = 1; 246 1.47 christos break; 247 1.1 mycroft } 248 1.47 christos ptotal += wcount; 249 1.47 christos if (pinfo) 250 1.47 christos progress(&fi, ptotal); 251 1.47 christos } 252 1.47 christos if (rcount < 0) { 253 1.47 christos warn("%s", entp->fts_path); 254 1.47 christos rval = 1; 255 1.1 mycroft } 256 1.1 mycroft } 257 1.1 mycroft 258 1.40 manu if (pflag && (fcpxattr(from_fd, to_fd) != 0)) 259 1.40 manu warn("%s: error copying extended attributes", to.p_path); 260 1.40 manu 261 1.49 christos #ifndef SMALL 262 1.48 christos if (pflag && preserve_fd_acls(from_fd, to_fd) != 0) 263 1.48 christos rval = 1; 264 1.49 christos #endif 265 1.48 christos 266 1.39 darcy (void)close(from_fd); 267 1.39 darcy 268 1.1 mycroft if (rval == 1) { 269 1.1 mycroft (void)close(to_fd); 270 1.1 mycroft return (1); 271 1.1 mycroft } 272 1.1 mycroft 273 1.1 mycroft if (pflag && setfile(fs, to_fd)) 274 1.1 mycroft rval = 1; 275 1.1 mycroft /* 276 1.1 mycroft * If the source was setuid or setgid, lose the bits unless the 277 1.1 mycroft * copy is owned by the same user and group. 278 1.1 mycroft */ 279 1.1 mycroft #define RETAINBITS \ 280 1.1 mycroft (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) 281 1.33 jld if (!pflag && dne 282 1.33 jld && fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) { 283 1.1 mycroft if (fstat(to_fd, &to_stat)) { 284 1.1 mycroft warn("%s", to.p_path); 285 1.1 mycroft rval = 1; 286 1.1 mycroft } else if (fs->st_gid == to_stat.st_gid && 287 1.1 mycroft fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) { 288 1.1 mycroft warn("%s", to.p_path); 289 1.1 mycroft rval = 1; 290 1.1 mycroft } 291 1.15 thorpej } 292 1.1 mycroft if (close(to_fd)) { 293 1.1 mycroft warn("%s", to.p_path); 294 1.1 mycroft rval = 1; 295 1.1 mycroft } 296 1.16 wsanchez /* set the mod/access times now after close of the fd */ 297 1.16 wsanchez if (pflag && set_utimes(to.p_path, fs)) { 298 1.16 wsanchez rval = 1; 299 1.16 wsanchez } 300 1.1 mycroft return (rval); 301 1.1 mycroft } 302 1.1 mycroft 303 1.1 mycroft int 304 1.20 wiz copy_link(FTSENT *p, int exists) 305 1.1 mycroft { 306 1.1 mycroft int len; 307 1.13 mycroft char target[MAXPATHLEN]; 308 1.1 mycroft 309 1.21 provos if ((len = readlink(p->fts_path, target, sizeof(target)-1)) == -1) { 310 1.1 mycroft warn("readlink: %s", p->fts_path); 311 1.1 mycroft return (1); 312 1.1 mycroft } 313 1.13 mycroft target[len] = '\0'; 314 1.1 mycroft if (exists && unlink(to.p_path)) { 315 1.1 mycroft warn("unlink: %s", to.p_path); 316 1.1 mycroft return (1); 317 1.1 mycroft } 318 1.13 mycroft if (symlink(target, to.p_path)) { 319 1.13 mycroft warn("symlink: %s", target); 320 1.1 mycroft return (1); 321 1.1 mycroft } 322 1.9 enami return (pflag ? setfile(p->fts_statp, 0) : 0); 323 1.1 mycroft } 324 1.1 mycroft 325 1.1 mycroft int 326 1.20 wiz copy_fifo(struct stat *from_stat, int exists) 327 1.1 mycroft { 328 1.1 mycroft if (exists && unlink(to.p_path)) { 329 1.1 mycroft warn("unlink: %s", to.p_path); 330 1.1 mycroft return (1); 331 1.1 mycroft } 332 1.1 mycroft if (mkfifo(to.p_path, from_stat->st_mode)) { 333 1.1 mycroft warn("mkfifo: %s", to.p_path); 334 1.1 mycroft return (1); 335 1.1 mycroft } 336 1.1 mycroft return (pflag ? setfile(from_stat, 0) : 0); 337 1.1 mycroft } 338 1.1 mycroft 339 1.1 mycroft int 340 1.20 wiz copy_special(struct stat *from_stat, int exists) 341 1.1 mycroft { 342 1.1 mycroft if (exists && unlink(to.p_path)) { 343 1.1 mycroft warn("unlink: %s", to.p_path); 344 1.1 mycroft return (1); 345 1.1 mycroft } 346 1.1 mycroft if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 347 1.1 mycroft warn("mknod: %s", to.p_path); 348 1.1 mycroft return (1); 349 1.1 mycroft } 350 1.1 mycroft return (pflag ? setfile(from_stat, 0) : 0); 351 1.1 mycroft } 352 1.1 mycroft 353 1.1 mycroft 354 1.16 wsanchez /* 355 1.16 wsanchez * Function: setfile 356 1.16 wsanchez * 357 1.16 wsanchez * Purpose: 358 1.16 wsanchez * Set the owner/group/permissions for the "to" file to the information 359 1.16 wsanchez * in the stat structure. If fd is zero, also call set_utimes() to set 360 1.16 wsanchez * the mod/access times. If fd is non-zero, the caller must do a utimes 361 1.16 wsanchez * itself after close(fd). 362 1.16 wsanchez */ 363 1.1 mycroft int 364 1.20 wiz setfile(struct stat *fs, int fd) 365 1.1 mycroft { 366 1.9 enami int rval, islink; 367 1.1 mycroft 368 1.1 mycroft rval = 0; 369 1.9 enami islink = S_ISLNK(fs->st_mode); 370 1.1 mycroft fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; 371 1.1 mycroft 372 1.1 mycroft /* 373 1.1 mycroft * Changing the ownership probably won't succeed, unless we're root 374 1.1 mycroft * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 375 1.1 mycroft * the mode; current BSD behavior is to remove all setuid bits on 376 1.1 mycroft * chown. If chown fails, lose setuid/setgid bits. 377 1.1 mycroft */ 378 1.1 mycroft if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : 379 1.9 enami lchown(to.p_path, fs->st_uid, fs->st_gid)) { 380 1.1 mycroft if (errno != EPERM) { 381 1.1 mycroft warn("chown: %s", to.p_path); 382 1.1 mycroft rval = 1; 383 1.1 mycroft } 384 1.1 mycroft fs->st_mode &= ~(S_ISUID | S_ISGID); 385 1.1 mycroft } 386 1.9 enami if (fd ? fchmod(fd, fs->st_mode) : lchmod(to.p_path, fs->st_mode)) { 387 1.10 mycroft warn("chmod: %s", to.p_path); 388 1.1 mycroft rval = 1; 389 1.1 mycroft } 390 1.1 mycroft 391 1.28 elad if (!islink && !Nflag) { 392 1.29 christos unsigned long fflags = fs->st_flags; 393 1.9 enami /* 394 1.9 enami * XXX 395 1.9 enami * NFS doesn't support chflags; ignore errors unless 396 1.9 enami * there's reason to believe we're losing bits. 397 1.9 enami * (Note, this still won't be right if the server 398 1.9 enami * supports flags and we were trying to *remove* flags 399 1.9 enami * on a file that we copied, i.e., that we didn't create.) 400 1.9 enami */ 401 1.9 enami errno = 0; 402 1.29 christos if ((fd ? fchflags(fd, fflags) : 403 1.29 christos chflags(to.p_path, fflags)) == -1) 404 1.9 enami if (errno != EOPNOTSUPP || fs->st_flags != 0) { 405 1.9 enami warn("chflags: %s", to.p_path); 406 1.9 enami rval = 1; 407 1.9 enami } 408 1.9 enami } 409 1.16 wsanchez /* if fd is non-zero, caller must call set_utimes() after close() */ 410 1.16 wsanchez if (fd == 0 && set_utimes(to.p_path, fs)) 411 1.16 wsanchez rval = 1; 412 1.1 mycroft return (rval); 413 1.1 mycroft } 414 1.1 mycroft 415 1.49 christos #ifndef SMALL 416 1.48 christos int 417 1.48 christos preserve_fd_acls(int source_fd, int dest_fd) 418 1.48 christos { 419 1.48 christos acl_t acl; 420 1.48 christos acl_type_t acl_type; 421 1.48 christos int acl_supported = 0, ret, trivial; 422 1.48 christos 423 1.48 christos ret = fpathconf(source_fd, _PC_ACL_NFS4); 424 1.48 christos if (ret > 0 ) { 425 1.48 christos acl_supported = 1; 426 1.48 christos acl_type = ACL_TYPE_NFS4; 427 1.48 christos } else if (ret < 0 && errno != EINVAL) { 428 1.48 christos warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path); 429 1.48 christos return (1); 430 1.48 christos } 431 1.48 christos if (acl_supported == 0) { 432 1.48 christos ret = fpathconf(source_fd, _PC_ACL_EXTENDED); 433 1.48 christos if (ret > 0 ) { 434 1.48 christos acl_supported = 1; 435 1.48 christos acl_type = ACL_TYPE_ACCESS; 436 1.48 christos } else if (ret < 0 && errno != EINVAL) { 437 1.48 christos warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s", 438 1.48 christos to.p_path); 439 1.48 christos return (1); 440 1.48 christos } 441 1.48 christos } 442 1.48 christos if (acl_supported == 0) 443 1.48 christos return (0); 444 1.48 christos 445 1.48 christos acl = acl_get_fd_np(source_fd, acl_type); 446 1.48 christos if (acl == NULL) { 447 1.48 christos warn("failed to get acl entries while setting %s", to.p_path); 448 1.48 christos return (1); 449 1.48 christos } 450 1.48 christos if (acl_is_trivial_np(acl, &trivial)) { 451 1.48 christos warn("acl_is_trivial() failed for %s", to.p_path); 452 1.48 christos acl_free(acl); 453 1.48 christos return (1); 454 1.48 christos } 455 1.48 christos if (trivial) { 456 1.48 christos acl_free(acl); 457 1.48 christos return (0); 458 1.48 christos } 459 1.48 christos if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) { 460 1.48 christos warn("failed to set acl entries for %s", to.p_path); 461 1.48 christos acl_free(acl); 462 1.48 christos return (1); 463 1.48 christos } 464 1.48 christos acl_free(acl); 465 1.48 christos return (0); 466 1.48 christos } 467 1.48 christos 468 1.48 christos int 469 1.48 christos preserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir) 470 1.48 christos { 471 1.48 christos acl_t (*aclgetf)(const char *, acl_type_t); 472 1.48 christos int (*aclsetf)(const char *, acl_type_t, acl_t); 473 1.48 christos struct acl *aclp; 474 1.48 christos acl_t acl; 475 1.48 christos acl_type_t acl_type; 476 1.48 christos int acl_supported = 0, ret, trivial; 477 1.48 christos 478 1.48 christos ret = pathconf(source_dir, _PC_ACL_NFS4); 479 1.48 christos if (ret > 0) { 480 1.48 christos acl_supported = 1; 481 1.48 christos acl_type = ACL_TYPE_NFS4; 482 1.48 christos } else if (ret < 0 && errno != EINVAL) { 483 1.48 christos warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir); 484 1.48 christos return (1); 485 1.48 christos } 486 1.48 christos if (acl_supported == 0) { 487 1.48 christos ret = pathconf(source_dir, _PC_ACL_EXTENDED); 488 1.48 christos if (ret > 0) { 489 1.48 christos acl_supported = 1; 490 1.48 christos acl_type = ACL_TYPE_ACCESS; 491 1.48 christos } else if (ret < 0 && errno != EINVAL) { 492 1.48 christos warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s", 493 1.48 christos source_dir); 494 1.48 christos return (1); 495 1.48 christos } 496 1.48 christos } 497 1.48 christos if (acl_supported == 0) 498 1.48 christos return (0); 499 1.48 christos 500 1.48 christos /* 501 1.48 christos * If the file is a link we will not follow it. 502 1.48 christos */ 503 1.48 christos if (S_ISLNK(fs->st_mode)) { 504 1.48 christos aclgetf = acl_get_link_np; 505 1.48 christos aclsetf = acl_set_link_np; 506 1.48 christos } else { 507 1.48 christos aclgetf = acl_get_file; 508 1.48 christos aclsetf = acl_set_file; 509 1.48 christos } 510 1.48 christos if (acl_type == ACL_TYPE_ACCESS) { 511 1.48 christos /* 512 1.48 christos * Even if there is no ACL_TYPE_DEFAULT entry here, a zero 513 1.48 christos * size ACL will be returned. So it is not safe to simply 514 1.48 christos * check the pointer to see if the default ACL is present. 515 1.48 christos */ 516 1.48 christos acl = aclgetf(source_dir, ACL_TYPE_DEFAULT); 517 1.48 christos if (acl == NULL) { 518 1.48 christos warn("failed to get default acl entries on %s", 519 1.48 christos source_dir); 520 1.48 christos return (1); 521 1.48 christos } 522 1.48 christos aclp = &acl->ats_acl; 523 1.48 christos if (aclp->acl_cnt != 0 && aclsetf(dest_dir, 524 1.48 christos ACL_TYPE_DEFAULT, acl) < 0) { 525 1.48 christos warn("failed to set default acl entries on %s", 526 1.48 christos dest_dir); 527 1.48 christos acl_free(acl); 528 1.48 christos return (1); 529 1.48 christos } 530 1.48 christos acl_free(acl); 531 1.48 christos } 532 1.48 christos acl = aclgetf(source_dir, acl_type); 533 1.48 christos if (acl == NULL) { 534 1.48 christos warn("failed to get acl entries on %s", source_dir); 535 1.48 christos return (1); 536 1.48 christos } 537 1.48 christos if (acl_is_trivial_np(acl, &trivial)) { 538 1.48 christos warn("acl_is_trivial() failed on %s", source_dir); 539 1.48 christos acl_free(acl); 540 1.48 christos return (1); 541 1.48 christos } 542 1.48 christos if (trivial) { 543 1.48 christos acl_free(acl); 544 1.48 christos return (0); 545 1.48 christos } 546 1.48 christos if (aclsetf(dest_dir, acl_type, acl) < 0) { 547 1.48 christos warn("failed to set acl entries on %s", dest_dir); 548 1.48 christos acl_free(acl); 549 1.48 christos return (1); 550 1.48 christos } 551 1.48 christos acl_free(acl); 552 1.48 christos return (0); 553 1.48 christos } 554 1.49 christos #endif 555 1.48 christos 556 1.1 mycroft void 557 1.20 wiz usage(void) 558 1.1 mycroft { 559 1.20 wiz (void)fprintf(stderr, 560 1.39 darcy "usage: %s [-R [-H | -L | -P]] [-f | -i] [-alNpv] src target\n" 561 1.39 darcy " %s [-R [-H | -L | -P]] [-f | -i] [-alNpv] src1 ... srcN directory\n", 562 1.20 wiz getprogname(), getprogname()); 563 1.1 mycroft exit(1); 564 1.14 mycroft /* NOTREACHED */ 565 1.1 mycroft } 566