]> git.cameronkatri.com Git - apple_cmds.git/blob - file_cmds/cp/utils.c
file_cmds: Fix compilation for lower targets
[apple_cmds.git] / file_cmds / cp / utils.c
1 /*-
2 * Copyright (c) 1991, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #ifndef lint
31 #if 0
32 static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94";
33 #endif
34 #endif /* not lint */
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD: src/bin/cp/utils.c,v 1.46 2005/09/05 04:36:08 csjp Exp $");
37
38 #include <sys/types.h>
39 #include <sys/acl.h>
40 #include <sys/param.h>
41 #include <sys/stat.h>
42 #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
43 #include <sys/mman.h>
44 #endif
45
46 #include <err.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <fts.h>
50 #include <limits.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <sysexits.h>
54 #include <unistd.h>
55 #include <locale.h>
56
57 #ifdef __APPLE__
58 #include <sys/time.h>
59 #include <copyfile.h>
60 #include <string.h>
61 #include <sys/mount.h>
62 #include <get_compat.h>
63 #include <sys/attr.h>
64 #include <sys/clonefile.h>
65 #else
66 #define COMPAT_MODE(a,b) (1)
67 #endif /* __APPLE__ */
68
69 #if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 140000
70 int rpmatch(const char *);
71 #endif
72
73 #include "extern.h"
74 #define cp_pct(x,y) (int)(100.0 * (double)(x) / (double)(y))
75
76 /* Memory strategy threshold, in pages: if physmem is larger then this, use a
77 * large buffer */
78 #define PHYSPAGES_THRESHOLD (32*1024)
79
80 /* Maximum buffer size in bytes - do not allow it to grow larger than this */
81 #define BUFSIZE_MAX (2*1024*1024)
82
83 /* Small (default) buffer size in bytes. It's inefficient for this to be
84 * smaller than MAXPHYS */
85 #define BUFSIZE_SMALL (MAXPHYS)
86
87 int
88 copy_file(const FTSENT *entp, int dne)
89 {
90 static char *buf = NULL;
91 static size_t bufsize;
92 struct stat *fs;
93 int ch, checkch, from_fd, rval, to_fd;
94 ssize_t rcount;
95 ssize_t wcount;
96 size_t wresid;
97 off_t wtotal;
98 char *bufp;
99 char resp[] = {'\0', '\0'};
100 #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
101 char *p;
102 #endif
103 mode_t mode = 0;
104 struct stat to_stat;
105
106 if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
107 warn("%s", entp->fts_path);
108 return (1);
109 }
110
111 fs = entp->fts_statp;
112
113 /*
114 * If the file exists and we're interactive, verify with the user.
115 * If the file DNE, set the mode to be the from file, minus setuid
116 * bits, modified by the umask; arguably wrong, but it makes copying
117 * executables work right and it's been that way forever. (The
118 * other choice is 666 or'ed with the execute bits on the from file
119 * modified by the umask.)
120 */
121 if (!dne) {
122 #define YESNO "(y/n [n]) "
123 if (nflag) {
124 if (vflag)
125 printf("%s not overwritten\n", to.p_path);
126 (void)close(from_fd);
127 return (1);
128 } else if (iflag) {
129 (void)fprintf(stderr, "overwrite %s? %s",
130 to.p_path, YESNO);
131
132 /* Load user specified locale */
133 setlocale(LC_MESSAGES, "");
134
135 checkch = ch = getchar();
136 while (ch != '\n' && ch != EOF)
137 ch = getchar();
138
139 /* only care about the first character */
140 resp[0] = checkch;
141
142 if (rpmatch(resp) != 1) {
143 (void)close(from_fd);
144 (void)fprintf(stderr, "not overwritten\n");
145 return (1);
146 }
147 }
148
149 if (cflag) {
150 (void)unlink(to.p_path);
151 int error = clonefile(entp->fts_path, to.p_path, 0);
152 if (error)
153 warn("%s: clonefile failed", to.p_path);
154 (void)close(from_fd);
155 return error == 0 ? 0 : 1;
156 }
157
158 if (COMPAT_MODE("bin/cp", "unix2003")) {
159 /* first try to overwrite existing destination file name */
160 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
161 if (to_fd == -1) {
162 if (fflag) {
163 /* Only if it fails remove file and create a new one */
164 (void)unlink(to.p_path);
165 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
166 fs->st_mode & ~(S_ISUID | S_ISGID));
167 }
168 }
169 } else {
170 if (fflag) {
171 /* remove existing destination file name,
172 * create a new file */
173 (void)unlink(to.p_path);
174 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
175 fs->st_mode & ~(S_ISUID | S_ISGID));
176 } else
177 /* overwrite existing destination file name */
178 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
179 }
180 } else {
181
182 if (cflag) {
183 int error = clonefile(entp->fts_path, to.p_path, 0);
184 if (error)
185 warn("%s: clonefile failed", to.p_path);
186 (void)close(from_fd);
187 return error == 0 ? 0 : 1;
188 }
189
190 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
191 fs->st_mode & ~(S_ISUID | S_ISGID));
192 }
193
194 if (to_fd == -1) {
195 warn("%s", to.p_path);
196 (void)close(from_fd);
197 return (1);
198 }
199
200 rval = 0;
201
202 #ifdef __APPLE__
203 if (S_ISREG(fs->st_mode)) {
204 struct statfs sfs;
205
206 /*
207 * Pre-allocate blocks for the destination file if it
208 * resides on Xsan.
209 */
210 if (fstatfs(to_fd, &sfs) == 0 &&
211 strcmp(sfs.f_fstypename, "acfs") == 0) {
212 fstore_t fst;
213
214 fst.fst_flags = 0;
215 fst.fst_posmode = F_PEOFPOSMODE;
216 fst.fst_offset = 0;
217 fst.fst_length = fs->st_size;
218
219 (void) fcntl(to_fd, F_PREALLOCATE, &fst);
220 }
221 }
222 #endif /* __APPLE__ */
223
224 if (fstat(to_fd, &to_stat) != -1) {
225 mode = to_stat.st_mode;
226 if ((mode & (S_IRWXG|S_IRWXO))
227 && fchmod(to_fd, mode & ~(S_IRWXG|S_IRWXO))) {
228 if (errno != EPERM) /* we have write access but do not own the file */
229 warn("%s: fchmod failed", to.p_path);
230 mode = 0;
231 }
232 } else {
233 warn("%s", to.p_path);
234 }
235 /*
236 * Mmap and write if less than 8M (the limit is so we don't totally
237 * trash memory on big files. This is really a minor hack, but it
238 * wins some CPU back.
239 */
240 #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
241 if (S_ISREG(fs->st_mode) && fs->st_size > 0 &&
242 fs->st_size <= 8 * 1048576) {
243 if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
244 MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
245 warn("%s", entp->fts_path);
246 rval = 1;
247 } else {
248 wtotal = 0;
249 for (bufp = p, wresid = fs->st_size; ;
250 bufp += wcount, wresid -= (size_t)wcount) {
251 wcount = write(to_fd, bufp, wresid);
252 wtotal += wcount;
253 if (info) {
254 info = 0;
255 (void)fprintf(stderr,
256 "%s -> %s %3d%%\n",
257 entp->fts_path, to.p_path,
258 cp_pct(wtotal, fs->st_size));
259
260 }
261 if (wcount >= (ssize_t)wresid || wcount <= 0)
262 break;
263 }
264 if (wcount != (ssize_t)wresid) {
265 warn("%s", to.p_path);
266 rval = 1;
267 }
268 /* Some systems don't unmap on close(2). */
269 if (munmap(p, fs->st_size) < 0) {
270 warn("%s", entp->fts_path);
271 rval = 1;
272 }
273 }
274 } else
275 #endif
276 {
277 if (buf == NULL) {
278 /*
279 * Note that buf and bufsize are static. If
280 * malloc() fails, it will fail at the start
281 * and not copy only some files.
282 */
283 if (sysconf(_SC_PHYS_PAGES) >
284 PHYSPAGES_THRESHOLD)
285 bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
286 else
287 bufsize = BUFSIZE_SMALL;
288 buf = malloc(bufsize);
289 if (buf == NULL)
290 err(1, "Not enough memory");
291 }
292 wtotal = 0;
293 while ((rcount = read(from_fd, buf, bufsize)) > 0) {
294 for (bufp = buf, wresid = rcount; ;
295 bufp += wcount, wresid -= wcount) {
296 wcount = write(to_fd, bufp, wresid);
297 wtotal += wcount;
298 if (info) {
299 info = 0;
300 (void)fprintf(stderr,
301 "%s -> %s %3d%%\n",
302 entp->fts_path, to.p_path,
303 cp_pct(wtotal, fs->st_size));
304
305 }
306 if (wcount >= (ssize_t)wresid || wcount <= 0)
307 break;
308 }
309 if (wcount != (ssize_t)wresid) {
310 warn("%s", to.p_path);
311 rval = 1;
312 break;
313 }
314 }
315 if (rcount < 0) {
316 warn("%s", entp->fts_path);
317 rval = 1;
318 }
319 }
320
321 /*
322 * Don't remove the target even after an error. The target might
323 * not be a regular file, or its attributes might be important,
324 * or its contents might be irreplaceable. It would only be safe
325 * to remove it if we created it and its length is 0.
326 */
327 if (mode != 0)
328 if (fchmod(to_fd, mode))
329 warn("%s: fchmod failed", to.p_path);
330 #ifdef __APPLE__
331 /* do these before setfile in case copyfile changes mtime */
332 if (!Xflag && S_ISREG(fs->st_mode)) { /* skip devices, etc */
333 if (fcopyfile(from_fd, to_fd, NULL, COPYFILE_XATTR) < 0)
334 warn("%s: could not copy extended attributes to %s", entp->fts_path, to.p_path);
335 }
336 if (pflag && setfile(fs, to_fd))
337 rval = 1;
338 if (pflag) {
339 /* If this ACL denies writeattr then setfile will fail... */
340 if (fcopyfile(from_fd, to_fd, NULL, COPYFILE_ACL) < 0)
341 warn("%s: could not copy ACL to %s", entp->fts_path, to.p_path);
342 }
343 #else /* !__APPLE__ */
344 if (pflag && setfile(fs, to_fd))
345 rval = 1;
346 if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
347 rval = 1;
348 #endif /* __APPLE__ */
349 (void)close(from_fd);
350 if (close(to_fd)) {
351 warn("%s", to.p_path);
352 rval = 1;
353 }
354 return (rval);
355 }
356
357 int
358 copy_link(const FTSENT *p, int exists)
359 {
360 ssize_t len;
361 char llink[PATH_MAX];
362
363 if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) {
364 warn("readlink: %s", p->fts_path);
365 return (1);
366 }
367 llink[len] = '\0';
368 if (exists && unlink(to.p_path)) {
369 warn("unlink: %s", to.p_path);
370 return (1);
371 }
372 if (symlink(llink, to.p_path)) {
373 warn("symlink: %s", llink);
374 return (1);
375 }
376 #ifdef __APPLE__
377 if (!Xflag)
378 if (copyfile(p->fts_path, to.p_path, NULL, COPYFILE_XATTR | COPYFILE_NOFOLLOW_SRC) <0)
379 warn("%s: could not copy extended attributes to %s",
380 p->fts_path, to.p_path);
381 #endif
382 return (pflag ? setfile(p->fts_statp, -1) : 0);
383 }
384
385 int
386 copy_fifo(struct stat *from_stat, int exists)
387 {
388 if (exists && unlink(to.p_path)) {
389 warn("unlink: %s", to.p_path);
390 return (1);
391 }
392 if (mkfifo(to.p_path, from_stat->st_mode)) {
393 warn("mkfifo: %s", to.p_path);
394 return (1);
395 }
396 return (pflag ? setfile(from_stat, -1) : 0);
397 }
398
399 int
400 copy_special(struct stat *from_stat, int exists)
401 {
402 if (exists && unlink(to.p_path)) {
403 warn("unlink: %s", to.p_path);
404 return (1);
405 }
406 if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
407 warn("mknod: %s", to.p_path);
408 return (1);
409 }
410 return (pflag ? setfile(from_stat, -1) : 0);
411 }
412
413 int
414 setfile(struct stat *fs, int fd)
415 {
416 struct attrlist ts_req = {};
417 struct stat ts;
418 int rval, gotstat, islink, fdval;
419 struct {
420 struct timespec mtime;
421 struct timespec atime;
422 } set_ts;
423
424 rval = 0;
425 fdval = fd != -1;
426 islink = !fdval && S_ISLNK(fs->st_mode);
427 fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO;
428 unsigned int options = islink ? FSOPT_NOFOLLOW : 0;
429
430 ts_req.bitmapcount = ATTR_BIT_MAP_COUNT;
431 ts_req.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME;
432 set_ts.mtime = fs->st_mtimespec;
433 set_ts.atime = fs->st_atimespec;
434
435 if (fdval ? fsetattrlist(fd, &ts_req, &set_ts, sizeof(set_ts), options) :
436 setattrlist(to.p_path, &ts_req, &set_ts, sizeof(set_ts), options)) {
437 warn("%ssetattrlist: %s", fdval ? "f" : "", to.p_path);
438 rval = 1;
439 }
440 if (fdval ? fstat(fd, &ts) : (islink ? lstat(to.p_path, &ts) :
441 stat(to.p_path, &ts))) {
442 gotstat = 0;
443 } else {
444 gotstat = 1;
445 ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO;
446 }
447 /*
448 * Changing the ownership probably won't succeed, unless we're root
449 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
450 * the mode; current BSD behavior is to remove all setuid bits on
451 * chown. If chown fails, lose setuid/setgid bits.
452 */
453 if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid) {
454 if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) : (islink ?
455 lchown(to.p_path, fs->st_uid, fs->st_gid) :
456 chown(to.p_path, fs->st_uid, fs->st_gid))) {
457 if (errno != EPERM) {
458 warn("%schown: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path);
459 rval = 1;
460 }
461 fs->st_mode &= ~(S_ISUID | S_ISGID);
462 }
463 }
464
465 if (!gotstat || fs->st_mode != ts.st_mode) {
466 if (fdval ? fchmod(fd, fs->st_mode) : (islink ?
467 lchmod(to.p_path, fs->st_mode) :
468 chmod(to.p_path, fs->st_mode))) {
469 warn("%schmod: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path);
470 rval = 1;
471 }
472 }
473
474 if (!gotstat || fs->st_flags != ts.st_flags) {
475 if (fdval ? fchflags(fd, fs->st_flags) : (islink ?
476 lchflags(to.p_path, fs->st_flags) :
477 chflags(to.p_path, fs->st_flags))) {
478 if (errno != EPERM) {
479 warn("%schflags: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path);
480 rval = 1;
481 }
482 }
483 }
484 return (rval);
485 }
486
487 #ifndef __APPLE__
488 int
489 preserve_fd_acls(int source_fd, int dest_fd)
490 {
491 struct acl *aclp;
492 acl_t acl;
493
494 if (fpathconf(source_fd, _PC_ACL_EXTENDED) != 1 ||
495 fpathconf(dest_fd, _PC_ACL_EXTENDED) != 1)
496 return (0);
497 acl = acl_get_fd(source_fd);
498 if (acl == NULL) {
499 warn("failed to get acl entries while setting %s", to.p_path);
500 return (1);
501 }
502 aclp = &acl->ats_acl;
503 if (aclp->acl_cnt == 3)
504 return (0);
505 if (acl_set_fd(dest_fd, acl) < 0) {
506 warn("failed to set acl entries for %s", to.p_path);
507 return (1);
508 }
509 return (0);
510 }
511
512 int
513 preserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir)
514 {
515 acl_t (*aclgetf)(const char *, acl_type_t);
516 int (*aclsetf)(const char *, acl_type_t, acl_t);
517 struct acl *aclp;
518 acl_t acl;
519
520 if (pathconf(source_dir, _PC_ACL_EXTENDED) != 1 ||
521 pathconf(dest_dir, _PC_ACL_EXTENDED) != 1)
522 return (0);
523 /*
524 * If the file is a link we will not follow it
525 */
526 if (S_ISLNK(fs->st_mode)) {
527 aclgetf = acl_get_link_np;
528 aclsetf = acl_set_link_np;
529 } else {
530 aclgetf = acl_get_file;
531 aclsetf = acl_set_file;
532 }
533 /*
534 * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
535 * size ACL will be returned. So it is not safe to simply
536 * check the pointer to see if the default ACL is present.
537 */
538 acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
539 if (acl == NULL) {
540 warn("failed to get default acl entries on %s",
541 source_dir);
542 return (1);
543 }
544 aclp = &acl->ats_acl;
545 if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
546 ACL_TYPE_DEFAULT, acl) < 0) {
547 warn("failed to set default acl entries on %s",
548 dest_dir);
549 return (1);
550 }
551 acl = aclgetf(source_dir, ACL_TYPE_ACCESS);
552 if (acl == NULL) {
553 warn("failed to get acl entries on %s", source_dir);
554 return (1);
555 }
556 aclp = &acl->ats_acl;
557 if (aclsetf(dest_dir, ACL_TYPE_ACCESS, acl) < 0) {
558 warn("failed to set acl entries on %s", dest_dir);
559 return (1);
560 }
561 return (0);
562 }
563 #endif /* !__APPLE__ */
564
565 void
566 usage(void)
567 {
568
569 if (COMPAT_MODE("bin/cp", "unix2003")) {
570 (void)fprintf(stderr, "%s\n%s\n",
571 "usage: cp [-R [-H | -L | -P]] [-fi | -n] [-apvXc] source_file target_file",
572 " cp [-R [-H | -L | -P]] [-fi | -n] [-apvXc] source_file ... "
573 "target_directory");
574 } else {
575 (void)fprintf(stderr, "%s\n%s\n",
576 "usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-apvXc] source_file target_file",
577 " cp [-R [-H | -L | -P]] [-f | -i | -n] [-apvXc] source_file ... "
578 "target_directory");
579 }
580 exit(EX_USAGE);
581 }