]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - bin/cp/cp.c
sys/{x86,amd64}: remove one of doubled ;s
[FreeBSD/FreeBSD.git] / bin / cp / cp.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1988, 1993, 1994
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * David Hitz of Auspex Systems Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #if 0
36 #ifndef lint
37 static char const copyright[] =
38 "@(#) Copyright (c) 1988, 1993, 1994\n\
39         The Regents of the University of California.  All rights reserved.\n";
40 #endif /* not lint */
41
42 #ifndef lint
43 static char sccsid[] = "@(#)cp.c        8.2 (Berkeley) 4/1/94";
44 #endif /* not lint */
45 #endif
46 #include <sys/cdefs.h>
47 __FBSDID("$FreeBSD$");
48
49 /*
50  * Cp copies source files to target files.
51  *
52  * The global PATH_T structure "to" always contains the path to the
53  * current target file.  Since fts(3) does not change directories,
54  * this path can be either absolute or dot-relative.
55  *
56  * The basic algorithm is to initialize "to" and use fts(3) to traverse
57  * the file hierarchy rooted in the argument list.  A trivial case is the
58  * case of 'cp file1 file2'.  The more interesting case is the case of
59  * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
60  * path (relative to the root of the traversal) is appended to dir (stored
61  * in "to") to form the final target path.
62  */
63
64 #include <sys/types.h>
65 #include <sys/stat.h>
66
67 #include <err.h>
68 #include <errno.h>
69 #include <fts.h>
70 #include <limits.h>
71 #include <signal.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include <unistd.h>
76
77 #include "extern.h"
78
79 #define STRIP_TRAILING_SLASH(p) {                                       \
80         while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')      \
81         *--(p).p_end = 0;                                               \
82 }
83
84 static char emptystring[] = "";
85
86 PATH_T to = { to.p_path, emptystring, "" };
87
88 int fflag, iflag, lflag, nflag, pflag, sflag, vflag;
89 static int Rflag, rflag;
90 volatile sig_atomic_t info;
91
92 enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
93
94 static int copy(char *[], enum op, int);
95 static void siginfo(int __unused);
96
97 int
98 main(int argc, char *argv[])
99 {
100         struct stat to_stat, tmp_stat;
101         enum op type;
102         int Hflag, Lflag, ch, fts_options, r, have_trailing_slash;
103         char *target;
104
105         fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
106         Hflag = Lflag = 0;
107         while ((ch = getopt(argc, argv, "HLPRafilnprsvx")) != -1)
108                 switch (ch) {
109                 case 'H':
110                         Hflag = 1;
111                         Lflag = 0;
112                         break;
113                 case 'L':
114                         Lflag = 1;
115                         Hflag = 0;
116                         break;
117                 case 'P':
118                         Hflag = Lflag = 0;
119                         break;
120                 case 'R':
121                         Rflag = 1;
122                         break;
123                 case 'a':
124                         pflag = 1;
125                         Rflag = 1;
126                         Hflag = Lflag = 0;
127                         break;
128                 case 'f':
129                         fflag = 1;
130                         iflag = nflag = 0;
131                         break;
132                 case 'i':
133                         iflag = 1;
134                         fflag = nflag = 0;
135                         break;
136                 case 'l':
137                         lflag = 1;
138                         break;
139                 case 'n':
140                         nflag = 1;
141                         fflag = iflag = 0;
142                         break;
143                 case 'p':
144                         pflag = 1;
145                         break;
146                 case 'r':
147                         rflag = Lflag = 1;
148                         Hflag = 0;
149                         break;
150                 case 's':
151                         sflag = 1;
152                         break;
153                 case 'v':
154                         vflag = 1;
155                         break;
156                 case 'x':
157                         fts_options |= FTS_XDEV;
158                         break;
159                 default:
160                         usage();
161                         break;
162                 }
163         argc -= optind;
164         argv += optind;
165
166         if (argc < 2)
167                 usage();
168
169         if (Rflag && rflag)
170                 errx(1, "the -R and -r options may not be specified together");
171         if (lflag && sflag)
172                 errx(1, "the -l and -s options may not be specified together");
173         if (rflag)
174                 Rflag = 1;
175         if (Rflag) {
176                 if (Hflag)
177                         fts_options |= FTS_COMFOLLOW;
178                 if (Lflag) {
179                         fts_options &= ~FTS_PHYSICAL;
180                         fts_options |= FTS_LOGICAL;
181                 }
182         } else {
183                 fts_options &= ~FTS_PHYSICAL;
184                 fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
185         }
186         (void)signal(SIGINFO, siginfo);
187
188         /* Save the target base in "to". */
189         target = argv[--argc];
190         if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path))
191                 errx(1, "%s: name too long", target);
192         to.p_end = to.p_path + strlen(to.p_path);
193         if (to.p_path == to.p_end) {
194                 *to.p_end++ = '.';
195                 *to.p_end = 0;
196         }
197         have_trailing_slash = (to.p_end[-1] == '/');
198         if (have_trailing_slash)
199                 STRIP_TRAILING_SLASH(to);
200         to.target_end = to.p_end;
201
202         /* Set end of argument list for fts(3). */
203         argv[argc] = NULL;
204
205         /*
206          * Cp has two distinct cases:
207          *
208          * cp [-R] source target
209          * cp [-R] source1 ... sourceN directory
210          *
211          * In both cases, source can be either a file or a directory.
212          *
213          * In (1), the target becomes a copy of the source. That is, if the
214          * source is a file, the target will be a file, and likewise for
215          * directories.
216          *
217          * In (2), the real target is not directory, but "directory/source".
218          */
219         r = stat(to.p_path, &to_stat);
220         if (r == -1 && errno != ENOENT)
221                 err(1, "%s", to.p_path);
222         if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
223                 /*
224                  * Case (1).  Target is not a directory.
225                  */
226                 if (argc > 1)
227                         errx(1, "%s is not a directory", to.p_path);
228
229                 /*
230                  * Need to detect the case:
231                  *      cp -R dir foo
232                  * Where dir is a directory and foo does not exist, where
233                  * we want pathname concatenations turned on but not for
234                  * the initial mkdir().
235                  */
236                 if (r == -1) {
237                         if (Rflag && (Lflag || Hflag))
238                                 stat(*argv, &tmp_stat);
239                         else
240                                 lstat(*argv, &tmp_stat);
241
242                         if (S_ISDIR(tmp_stat.st_mode) && Rflag)
243                                 type = DIR_TO_DNE;
244                         else
245                                 type = FILE_TO_FILE;
246                 } else
247                         type = FILE_TO_FILE;
248
249                 if (have_trailing_slash && type == FILE_TO_FILE) {
250                         if (r == -1) {
251                                 errx(1, "directory %s does not exist",
252                                     to.p_path);
253                         } else
254                                 errx(1, "%s is not a directory", to.p_path);
255                 }
256         } else
257                 /*
258                  * Case (2).  Target is a directory.
259                  */
260                 type = FILE_TO_DIR;
261
262         exit (copy(argv, type, fts_options));
263 }
264
265 static int
266 copy(char *argv[], enum op type, int fts_options)
267 {
268         struct stat to_stat;
269         FTS *ftsp;
270         FTSENT *curr;
271         int base = 0, dne, badcp, rval;
272         size_t nlen;
273         char *p, *target_mid;
274         mode_t mask, mode;
275
276         /*
277          * Keep an inverted copy of the umask, for use in correcting
278          * permissions on created directories when not using -p.
279          */
280         mask = ~umask(0777);
281         umask(~mask);
282
283         if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
284                 err(1, "fts_open");
285         for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) {
286                 switch (curr->fts_info) {
287                 case FTS_NS:
288                 case FTS_DNR:
289                 case FTS_ERR:
290                         warnx("%s: %s",
291                             curr->fts_path, strerror(curr->fts_errno));
292                         badcp = rval = 1;
293                         continue;
294                 case FTS_DC:                    /* Warn, continue. */
295                         warnx("%s: directory causes a cycle", curr->fts_path);
296                         badcp = rval = 1;
297                         continue;
298                 default:
299                         ;
300                 }
301
302                 /*
303                  * If we are in case (2) or (3) above, we need to append the
304                  * source name to the target name.
305                  */
306                 if (type != FILE_TO_FILE) {
307                         /*
308                          * Need to remember the roots of traversals to create
309                          * correct pathnames.  If there's a directory being
310                          * copied to a non-existent directory, e.g.
311                          *      cp -R a/dir noexist
312                          * the resulting path name should be noexist/foo, not
313                          * noexist/dir/foo (where foo is a file in dir), which
314                          * is the case where the target exists.
315                          *
316                          * Also, check for "..".  This is for correct path
317                          * concatenation for paths ending in "..", e.g.
318                          *      cp -R .. /tmp
319                          * Paths ending in ".." are changed to ".".  This is
320                          * tricky, but seems the easiest way to fix the problem.
321                          *
322                          * XXX
323                          * Since the first level MUST be FTS_ROOTLEVEL, base
324                          * is always initialized.
325                          */
326                         if (curr->fts_level == FTS_ROOTLEVEL) {
327                                 if (type != DIR_TO_DNE) {
328                                         p = strrchr(curr->fts_path, '/');
329                                         base = (p == NULL) ? 0 :
330                                             (int)(p - curr->fts_path + 1);
331
332                                         if (!strcmp(&curr->fts_path[base],
333                                             ".."))
334                                                 base += 1;
335                                 } else
336                                         base = curr->fts_pathlen;
337                         }
338
339                         p = &curr->fts_path[base];
340                         nlen = curr->fts_pathlen - base;
341                         target_mid = to.target_end;
342                         if (*p != '/' && target_mid[-1] != '/')
343                                 *target_mid++ = '/';
344                         *target_mid = 0;
345                         if (target_mid - to.p_path + nlen >= PATH_MAX) {
346                                 warnx("%s%s: name too long (not copied)",
347                                     to.p_path, p);
348                                 badcp = rval = 1;
349                                 continue;
350                         }
351                         (void)strncat(target_mid, p, nlen);
352                         to.p_end = target_mid + nlen;
353                         *to.p_end = 0;
354                         STRIP_TRAILING_SLASH(to);
355                 }
356
357                 if (curr->fts_info == FTS_DP) {
358                         /*
359                          * We are nearly finished with this directory.  If we
360                          * didn't actually copy it, or otherwise don't need to
361                          * change its attributes, then we are done.
362                          */
363                         if (!curr->fts_number)
364                                 continue;
365                         /*
366                          * If -p is in effect, set all the attributes.
367                          * Otherwise, set the correct permissions, limited
368                          * by the umask.  Optimise by avoiding a chmod()
369                          * if possible (which is usually the case if we
370                          * made the directory).  Note that mkdir() does not
371                          * honour setuid, setgid and sticky bits, but we
372                          * normally want to preserve them on directories.
373                          */
374                         if (pflag) {
375                                 if (setfile(curr->fts_statp, -1))
376                                         rval = 1;
377                                 if (preserve_dir_acls(curr->fts_statp,
378                                     curr->fts_accpath, to.p_path) != 0)
379                                         rval = 1;
380                         } else {
381                                 mode = curr->fts_statp->st_mode;
382                                 if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) ||
383                                     ((mode | S_IRWXU) & mask) != (mode & mask))
384                                         if (chmod(to.p_path, mode & mask) !=
385                                             0) {
386                                                 warn("chmod: %s", to.p_path);
387                                                 rval = 1;
388                                         }
389                         }
390                         continue;
391                 }
392
393                 /* Not an error but need to remember it happened. */
394                 if (stat(to.p_path, &to_stat) == -1)
395                         dne = 1;
396                 else {
397                         if (to_stat.st_dev == curr->fts_statp->st_dev &&
398                             to_stat.st_ino == curr->fts_statp->st_ino) {
399                                 warnx("%s and %s are identical (not copied).",
400                                     to.p_path, curr->fts_path);
401                                 badcp = rval = 1;
402                                 if (S_ISDIR(curr->fts_statp->st_mode))
403                                         (void)fts_set(ftsp, curr, FTS_SKIP);
404                                 continue;
405                         }
406                         if (!S_ISDIR(curr->fts_statp->st_mode) &&
407                             S_ISDIR(to_stat.st_mode)) {
408                                 warnx("cannot overwrite directory %s with "
409                                     "non-directory %s",
410                                     to.p_path, curr->fts_path);
411                                 badcp = rval = 1;
412                                 continue;
413                         }
414                         dne = 0;
415                 }
416
417                 switch (curr->fts_statp->st_mode & S_IFMT) {
418                 case S_IFLNK:
419                         /* Catch special case of a non-dangling symlink. */
420                         if ((fts_options & FTS_LOGICAL) ||
421                             ((fts_options & FTS_COMFOLLOW) &&
422                             curr->fts_level == 0)) {
423                                 if (copy_file(curr, dne))
424                                         badcp = rval = 1;
425                         } else {        
426                                 if (copy_link(curr, !dne))
427                                         badcp = rval = 1;
428                         }
429                         break;
430                 case S_IFDIR:
431                         if (!Rflag) {
432                                 warnx("%s is a directory (not copied).",
433                                     curr->fts_path);
434                                 (void)fts_set(ftsp, curr, FTS_SKIP);
435                                 badcp = rval = 1;
436                                 break;
437                         }
438                         /*
439                          * If the directory doesn't exist, create the new
440                          * one with the from file mode plus owner RWX bits,
441                          * modified by the umask.  Trade-off between being
442                          * able to write the directory (if from directory is
443                          * 555) and not causing a permissions race.  If the
444                          * umask blocks owner writes, we fail.
445                          */
446                         if (dne) {
447                                 if (mkdir(to.p_path,
448                                     curr->fts_statp->st_mode | S_IRWXU) < 0)
449                                         err(1, "%s", to.p_path);
450                         } else if (!S_ISDIR(to_stat.st_mode)) {
451                                 errno = ENOTDIR;
452                                 err(1, "%s", to.p_path);
453                         }
454                         /*
455                          * Arrange to correct directory attributes later
456                          * (in the post-order phase) if this is a new
457                          * directory, or if the -p flag is in effect.
458                          */
459                         curr->fts_number = pflag || dne;
460                         break;
461                 case S_IFBLK:
462                 case S_IFCHR:
463                         if (Rflag && !sflag) {
464                                 if (copy_special(curr->fts_statp, !dne))
465                                         badcp = rval = 1;
466                         } else {
467                                 if (copy_file(curr, dne))
468                                         badcp = rval = 1;
469                         }
470                         break;
471                 case S_IFSOCK:
472                         warnx("%s is a socket (not copied).",
473                             curr->fts_path);
474                         break;
475                 case S_IFIFO:
476                         if (Rflag && !sflag) {
477                                 if (copy_fifo(curr->fts_statp, !dne))
478                                         badcp = rval = 1;
479                         } else {
480                                 if (copy_file(curr, dne))
481                                         badcp = rval = 1;
482                         }
483                         break;
484                 default:
485                         if (copy_file(curr, dne))
486                                 badcp = rval = 1;
487                         break;
488                 }
489                 if (vflag && !badcp)
490                         (void)printf("%s -> %s\n", curr->fts_path, to.p_path);
491         }
492         if (errno)
493                 err(1, "fts_read");
494         fts_close(ftsp);
495         return (rval);
496 }
497
498 static void
499 siginfo(int sig __unused)
500 {
501
502         info = 1;
503 }