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