]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - bin/cp/cp.c
Update to RC6
[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 Hflag, Lflag, 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 Pflag, ch, fts_options, r, have_trailing_slash;
103         char *target;
104
105         fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
106         Pflag = 0;
107         while ((ch = getopt(argc, argv, "HLPRafilnprsvx")) != -1)
108                 switch (ch) {
109                 case 'H':
110                         Hflag = 1;
111                         Lflag = Pflag = 0;
112                         break;
113                 case 'L':
114                         Lflag = 1;
115                         Hflag = Pflag = 0;
116                         break;
117                 case 'P':
118                         Pflag = 1;
119                         Hflag = Lflag = 0;
120                         break;
121                 case 'R':
122                         Rflag = 1;
123                         break;
124                 case 'a':
125                         pflag = 1;
126                         Rflag = 1;
127                         Pflag = 1;
128                         Hflag = Lflag = 0;
129                         break;
130                 case 'f':
131                         fflag = 1;
132                         iflag = nflag = 0;
133                         break;
134                 case 'i':
135                         iflag = 1;
136                         fflag = nflag = 0;
137                         break;
138                 case 'l':
139                         lflag = 1;
140                         break;
141                 case 'n':
142                         nflag = 1;
143                         fflag = iflag = 0;
144                         break;
145                 case 'p':
146                         pflag = 1;
147                         break;
148                 case 'r':
149                         rflag = Lflag = 1;
150                         Hflag = Pflag = 0;
151                         break;
152                 case 's':
153                         sflag = 1;
154                         break;
155                 case 'v':
156                         vflag = 1;
157                         break;
158                 case 'x':
159                         fts_options |= FTS_XDEV;
160                         break;
161                 default:
162                         usage();
163                         break;
164                 }
165         argc -= optind;
166         argv += optind;
167
168         if (argc < 2)
169                 usage();
170
171         if (Rflag && rflag)
172                 errx(1, "the -R and -r options may not be specified together");
173         if (lflag && sflag)
174                 errx(1, "the -l and -s options may not be specified together");
175         if (rflag)
176                 Rflag = 1;
177         if (Rflag) {
178                 if (Hflag)
179                         fts_options |= FTS_COMFOLLOW;
180                 if (Lflag) {
181                         fts_options &= ~FTS_PHYSICAL;
182                         fts_options |= FTS_LOGICAL;
183                 }
184         } else if (!Pflag) {
185                 fts_options &= ~FTS_PHYSICAL;
186                 fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
187         }
188         (void)signal(SIGINFO, siginfo);
189
190         /* Save the target base in "to". */
191         target = argv[--argc];
192         if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path))
193                 errx(1, "%s: name too long", target);
194         to.p_end = to.p_path + strlen(to.p_path);
195         if (to.p_path == to.p_end) {
196                 *to.p_end++ = '.';
197                 *to.p_end = 0;
198         }
199         have_trailing_slash = (to.p_end[-1] == '/');
200         if (have_trailing_slash)
201                 STRIP_TRAILING_SLASH(to);
202         to.target_end = to.p_end;
203
204         /* Set end of argument list for fts(3). */
205         argv[argc] = NULL;
206
207         /*
208          * Cp has two distinct cases:
209          *
210          * cp [-R] source target
211          * cp [-R] source1 ... sourceN directory
212          *
213          * In both cases, source can be either a file or a directory.
214          *
215          * In (1), the target becomes a copy of the source. That is, if the
216          * source is a file, the target will be a file, and likewise for
217          * directories.
218          *
219          * In (2), the real target is not directory, but "directory/source".
220          */
221         r = stat(to.p_path, &to_stat);
222         if (r == -1 && errno != ENOENT)
223                 err(1, "%s", to.p_path);
224         if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
225                 /*
226                  * Case (1).  Target is not a directory.
227                  */
228                 if (argc > 1)
229                         errx(1, "%s is not a directory", to.p_path);
230
231                 /*
232                  * Need to detect the case:
233                  *      cp -R dir foo
234                  * Where dir is a directory and foo does not exist, where
235                  * we want pathname concatenations turned on but not for
236                  * the initial mkdir().
237                  */
238                 if (r == -1) {
239                         if (Rflag && (Lflag || Hflag))
240                                 stat(*argv, &tmp_stat);
241                         else
242                                 lstat(*argv, &tmp_stat);
243
244                         if (S_ISDIR(tmp_stat.st_mode) && Rflag)
245                                 type = DIR_TO_DNE;
246                         else
247                                 type = FILE_TO_FILE;
248                 } else
249                         type = FILE_TO_FILE;
250
251                 if (have_trailing_slash && type == FILE_TO_FILE) {
252                         if (r == -1) {
253                                 errx(1, "directory %s does not exist",
254                                     to.p_path);
255                         } else
256                                 errx(1, "%s is not a directory", to.p_path);
257                 }
258         } else
259                 /*
260                  * Case (2).  Target is a directory.
261                  */
262                 type = FILE_TO_DIR;
263
264         exit (copy(argv, type, fts_options));
265 }
266
267 /* Does the right thing based on -R + -H/-L/-P */
268 static int
269 copy_stat(const char *path, struct stat *sb)
270 {
271
272         /*
273          * For -R -H/-P, we need to lstat() instead; copy() cares about the link
274          * itself rather than the target if we're not following links during the
275          * traversal.
276          */
277         if (!Rflag || Lflag)
278                 return (stat(path, sb));
279         return (lstat(path, sb));
280 }
281
282
283 static int
284 copy(char *argv[], enum op type, int fts_options)
285 {
286         struct stat to_stat;
287         FTS *ftsp;
288         FTSENT *curr;
289         int base = 0, dne, badcp, rval;
290         size_t nlen;
291         char *p, *target_mid;
292         mode_t mask, mode;
293
294         /*
295          * Keep an inverted copy of the umask, for use in correcting
296          * permissions on created directories when not using -p.
297          */
298         mask = ~umask(0777);
299         umask(~mask);
300
301         if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
302                 err(1, "fts_open");
303         for (badcp = rval = 0; errno = 0, (curr = fts_read(ftsp)) != NULL;
304             badcp = 0) {
305                 switch (curr->fts_info) {
306                 case FTS_NS:
307                 case FTS_DNR:
308                 case FTS_ERR:
309                         warnx("%s: %s",
310                             curr->fts_path, strerror(curr->fts_errno));
311                         badcp = rval = 1;
312                         continue;
313                 case FTS_DC:                    /* Warn, continue. */
314                         warnx("%s: directory causes a cycle", curr->fts_path);
315                         badcp = rval = 1;
316                         continue;
317                 default:
318                         ;
319                 }
320
321                 /*
322                  * If we are in case (2) or (3) above, we need to append the
323                  * source name to the target name.
324                  */
325                 if (type != FILE_TO_FILE) {
326                         /*
327                          * Need to remember the roots of traversals to create
328                          * correct pathnames.  If there's a directory being
329                          * copied to a non-existent directory, e.g.
330                          *      cp -R a/dir noexist
331                          * the resulting path name should be noexist/foo, not
332                          * noexist/dir/foo (where foo is a file in dir), which
333                          * is the case where the target exists.
334                          *
335                          * Also, check for "..".  This is for correct path
336                          * concatenation for paths ending in "..", e.g.
337                          *      cp -R .. /tmp
338                          * Paths ending in ".." are changed to ".".  This is
339                          * tricky, but seems the easiest way to fix the problem.
340                          *
341                          * XXX
342                          * Since the first level MUST be FTS_ROOTLEVEL, base
343                          * is always initialized.
344                          */
345                         if (curr->fts_level == FTS_ROOTLEVEL) {
346                                 if (type != DIR_TO_DNE) {
347                                         p = strrchr(curr->fts_path, '/');
348                                         base = (p == NULL) ? 0 :
349                                             (int)(p - curr->fts_path + 1);
350
351                                         if (!strcmp(&curr->fts_path[base],
352                                             ".."))
353                                                 base += 1;
354                                 } else
355                                         base = curr->fts_pathlen;
356                         }
357
358                         p = &curr->fts_path[base];
359                         nlen = curr->fts_pathlen - base;
360                         target_mid = to.target_end;
361                         if (*p != '/' && target_mid[-1] != '/')
362                                 *target_mid++ = '/';
363                         *target_mid = 0;
364                         if (target_mid - to.p_path + nlen >= PATH_MAX) {
365                                 warnx("%s%s: name too long (not copied)",
366                                     to.p_path, p);
367                                 badcp = rval = 1;
368                                 continue;
369                         }
370                         (void)strncat(target_mid, p, nlen);
371                         to.p_end = target_mid + nlen;
372                         *to.p_end = 0;
373                         STRIP_TRAILING_SLASH(to);
374                 }
375
376                 if (curr->fts_info == FTS_DP) {
377                         /*
378                          * We are nearly finished with this directory.  If we
379                          * didn't actually copy it, or otherwise don't need to
380                          * change its attributes, then we are done.
381                          */
382                         if (!curr->fts_number)
383                                 continue;
384                         /*
385                          * If -p is in effect, set all the attributes.
386                          * Otherwise, set the correct permissions, limited
387                          * by the umask.  Optimise by avoiding a chmod()
388                          * if possible (which is usually the case if we
389                          * made the directory).  Note that mkdir() does not
390                          * honour setuid, setgid and sticky bits, but we
391                          * normally want to preserve them on directories.
392                          */
393                         if (pflag) {
394                                 if (setfile(curr->fts_statp, -1))
395                                         rval = 1;
396                                 if (preserve_dir_acls(curr->fts_statp,
397                                     curr->fts_accpath, to.p_path) != 0)
398                                         rval = 1;
399                         } else {
400                                 mode = curr->fts_statp->st_mode;
401                                 if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) ||
402                                     ((mode | S_IRWXU) & mask) != (mode & mask))
403                                         if (chmod(to.p_path, mode & mask) !=
404                                             0) {
405                                                 warn("chmod: %s", to.p_path);
406                                                 rval = 1;
407                                         }
408                         }
409                         continue;
410                 }
411
412                 /* Not an error but need to remember it happened. */
413                 if (copy_stat(to.p_path, &to_stat) == -1)
414                         dne = 1;
415                 else {
416                         if (to_stat.st_dev == curr->fts_statp->st_dev &&
417                             to_stat.st_ino == curr->fts_statp->st_ino) {
418                                 warnx("%s and %s are identical (not copied).",
419                                     to.p_path, curr->fts_path);
420                                 badcp = rval = 1;
421                                 if (S_ISDIR(curr->fts_statp->st_mode))
422                                         (void)fts_set(ftsp, curr, FTS_SKIP);
423                                 continue;
424                         }
425                         if (!S_ISDIR(curr->fts_statp->st_mode) &&
426                             S_ISDIR(to_stat.st_mode)) {
427                                 warnx("cannot overwrite directory %s with "
428                                     "non-directory %s",
429                                     to.p_path, curr->fts_path);
430                                 badcp = rval = 1;
431                                 continue;
432                         }
433                         dne = 0;
434                 }
435
436                 switch (curr->fts_statp->st_mode & S_IFMT) {
437                 case S_IFLNK:
438                         /* Catch special case of a non-dangling symlink. */
439                         if ((fts_options & FTS_LOGICAL) ||
440                             ((fts_options & FTS_COMFOLLOW) &&
441                             curr->fts_level == 0)) {
442                                 if (copy_file(curr, dne))
443                                         badcp = rval = 1;
444                         } else {        
445                                 if (copy_link(curr, !dne))
446                                         badcp = rval = 1;
447                         }
448                         break;
449                 case S_IFDIR:
450                         if (!Rflag) {
451                                 warnx("%s is a directory (not copied).",
452                                     curr->fts_path);
453                                 (void)fts_set(ftsp, curr, FTS_SKIP);
454                                 badcp = rval = 1;
455                                 break;
456                         }
457                         /*
458                          * If the directory doesn't exist, create the new
459                          * one with the from file mode plus owner RWX bits,
460                          * modified by the umask.  Trade-off between being
461                          * able to write the directory (if from directory is
462                          * 555) and not causing a permissions race.  If the
463                          * umask blocks owner writes, we fail.
464                          */
465                         if (dne) {
466                                 if (mkdir(to.p_path,
467                                     curr->fts_statp->st_mode | S_IRWXU) < 0)
468                                         err(1, "%s", to.p_path);
469                         } else if (!S_ISDIR(to_stat.st_mode)) {
470                                 errno = ENOTDIR;
471                                 err(1, "%s", to.p_path);
472                         }
473                         /*
474                          * Arrange to correct directory attributes later
475                          * (in the post-order phase) if this is a new
476                          * directory, or if the -p flag is in effect.
477                          */
478                         curr->fts_number = pflag || dne;
479                         break;
480                 case S_IFBLK:
481                 case S_IFCHR:
482                         if (Rflag && !sflag) {
483                                 if (copy_special(curr->fts_statp, !dne))
484                                         badcp = rval = 1;
485                         } else {
486                                 if (copy_file(curr, dne))
487                                         badcp = rval = 1;
488                         }
489                         break;
490                 case S_IFSOCK:
491                         warnx("%s is a socket (not copied).",
492                             curr->fts_path);
493                         break;
494                 case S_IFIFO:
495                         if (Rflag && !sflag) {
496                                 if (copy_fifo(curr->fts_statp, !dne))
497                                         badcp = rval = 1;
498                         } else {
499                                 if (copy_file(curr, dne))
500                                         badcp = rval = 1;
501                         }
502                         break;
503                 default:
504                         if (copy_file(curr, dne))
505                                 badcp = rval = 1;
506                         break;
507                 }
508                 if (vflag && !badcp)
509                         (void)printf("%s -> %s\n", curr->fts_path, to.p_path);
510         }
511         if (errno)
512                 err(1, "fts_read");
513         fts_close(ftsp);
514         return (rval);
515 }
516
517 static void
518 siginfo(int sig __unused)
519 {
520
521         info = 1;
522 }