Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/bin/cp/cp.c
39475 views
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
/*
36
* Cp copies source files to target files.
37
*
38
* The global PATH_T structure "to" always contains the path to the
39
* current target file. Since fts(3) does not change directories,
40
* this path can be either absolute or dot-relative.
41
*
42
* The basic algorithm is to initialize "to" and use fts(3) to traverse
43
* the file hierarchy rooted in the argument list. A trivial case is the
44
* case of 'cp file1 file2'. The more interesting case is the case of
45
* 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
46
* path (relative to the root of the traversal) is appended to dir (stored
47
* in "to") to form the final target path.
48
*/
49
50
#include <sys/types.h>
51
#include <sys/stat.h>
52
53
#include <assert.h>
54
#include <err.h>
55
#include <errno.h>
56
#include <fcntl.h>
57
#include <fts.h>
58
#include <getopt.h>
59
#include <limits.h>
60
#include <signal.h>
61
#include <stdbool.h>
62
#include <stdio.h>
63
#include <stdlib.h>
64
#include <string.h>
65
#include <unistd.h>
66
67
#include "extern.h"
68
69
static char dot[] = ".";
70
71
#define END(buf) (buf + sizeof(buf))
72
PATH_T to = { .dir = -1, .end = to.path };
73
bool Nflag, fflag, iflag, lflag, nflag, pflag, sflag, vflag;
74
static bool Hflag, Lflag, Pflag, Rflag, rflag, Sflag;
75
volatile sig_atomic_t info;
76
77
enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
78
79
static int copy(char *[], enum op, int, struct stat *);
80
static void siginfo(int __unused);
81
82
enum {
83
SORT_OPT = CHAR_MAX,
84
};
85
86
static const struct option long_opts[] =
87
{
88
{ "archive", no_argument, NULL, 'a' },
89
{ "force", no_argument, NULL, 'f' },
90
{ "interactive", no_argument, NULL, 'i' },
91
{ "dereference", no_argument, NULL, 'L' },
92
{ "link", no_argument, NULL, 'l' },
93
{ "no-clobber", no_argument, NULL, 'n' },
94
{ "no-dereference", no_argument, NULL, 'P' },
95
{ "recursive", no_argument, NULL, 'R' },
96
{ "symbolic-link", no_argument, NULL, 's' },
97
{ "verbose", no_argument, NULL, 'v' },
98
{ "one-file-system", no_argument, NULL, 'x' },
99
{ "sort", no_argument, NULL, SORT_OPT },
100
{ 0 }
101
};
102
103
int
104
main(int argc, char *argv[])
105
{
106
struct stat to_stat, tmp_stat;
107
enum op type;
108
int ch, fts_options, r;
109
char *sep, *target;
110
bool have_trailing_slash = false;
111
112
fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
113
while ((ch = getopt_long(argc, argv, "+HLPRafilNnprsvx", long_opts,
114
NULL)) != -1)
115
switch (ch) {
116
case 'H':
117
Hflag = true;
118
Lflag = Pflag = false;
119
break;
120
case 'L':
121
Lflag = true;
122
Hflag = Pflag = false;
123
break;
124
case 'P':
125
Pflag = true;
126
Hflag = Lflag = false;
127
break;
128
case 'R':
129
Rflag = true;
130
break;
131
case 'a':
132
pflag = true;
133
Rflag = true;
134
Pflag = true;
135
Hflag = Lflag = false;
136
break;
137
case 'f':
138
fflag = true;
139
iflag = nflag = false;
140
break;
141
case 'i':
142
iflag = true;
143
fflag = nflag = false;
144
break;
145
case 'l':
146
lflag = true;
147
break;
148
case 'N':
149
Nflag = true;
150
break;
151
case 'n':
152
nflag = true;
153
fflag = iflag = false;
154
break;
155
case 'p':
156
pflag = true;
157
break;
158
case 'r':
159
rflag = Lflag = true;
160
Hflag = Pflag = false;
161
break;
162
case 's':
163
sflag = true;
164
break;
165
case 'v':
166
vflag = true;
167
break;
168
case 'x':
169
fts_options |= FTS_XDEV;
170
break;
171
case SORT_OPT:
172
Sflag = true;
173
break;
174
default:
175
usage();
176
}
177
argc -= optind;
178
argv += optind;
179
180
if (argc < 2)
181
usage();
182
183
if (Rflag && rflag)
184
errx(1, "the -R and -r options may not be specified together");
185
if (lflag && sflag)
186
errx(1, "the -l and -s options may not be specified together");
187
if (rflag)
188
Rflag = true;
189
if (Rflag) {
190
if (Hflag)
191
fts_options |= FTS_COMFOLLOW;
192
if (Lflag) {
193
fts_options &= ~FTS_PHYSICAL;
194
fts_options |= FTS_LOGICAL;
195
}
196
} else if (!Pflag) {
197
fts_options &= ~FTS_PHYSICAL;
198
fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
199
}
200
(void)signal(SIGINFO, siginfo);
201
202
/* Save the target base in "to". */
203
target = argv[--argc];
204
if (*target == '\0') {
205
target = dot;
206
} else if ((sep = strrchr(target, '/')) != NULL && sep[1] == '\0') {
207
have_trailing_slash = true;
208
while (sep > target && *sep == '/')
209
sep--;
210
sep[1] = '\0';
211
}
212
/*
213
* Copy target into to.base, leaving room for a possible separator
214
* which will be appended later in the non-FILE_TO_FILE cases.
215
*/
216
if (strlcpy(to.base, target, sizeof(to.base) - 1) >=
217
sizeof(to.base) - 1)
218
errc(1, ENAMETOOLONG, "%s", target);
219
220
/* Set end of argument list for fts(3). */
221
argv[argc] = NULL;
222
223
/*
224
* Cp has two distinct cases:
225
*
226
* cp [-R] source target
227
* cp [-R] source1 ... sourceN directory
228
*
229
* In both cases, source can be either a file or a directory.
230
*
231
* In (1), the target becomes a copy of the source. That is, if the
232
* source is a file, the target will be a file, and likewise for
233
* directories.
234
*
235
* In (2), the real target is not directory, but "directory/source".
236
*/
237
r = stat(to.base, &to_stat);
238
if (r == -1 && errno != ENOENT)
239
err(1, "%s", target);
240
if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
241
/*
242
* Case (1). Target is not a directory.
243
*/
244
if (argc > 1)
245
errc(1, ENOTDIR, "%s", target);
246
247
/*
248
* Need to detect the case:
249
* cp -R dir foo
250
* Where dir is a directory and foo does not exist, where
251
* we want pathname concatenations turned on but not for
252
* the initial mkdir().
253
*/
254
if (r == -1) {
255
if (Rflag && (Lflag || Hflag))
256
stat(*argv, &tmp_stat);
257
else
258
lstat(*argv, &tmp_stat);
259
260
if (S_ISDIR(tmp_stat.st_mode) && Rflag)
261
type = DIR_TO_DNE;
262
else
263
type = FILE_TO_FILE;
264
} else
265
type = FILE_TO_FILE;
266
267
if (have_trailing_slash && type == FILE_TO_FILE) {
268
if (r == -1)
269
errc(1, ENOENT, "%s", target);
270
else
271
errc(1, ENOTDIR, "%s", target);
272
}
273
} else {
274
/*
275
* Case (2). Target is a directory.
276
*/
277
type = FILE_TO_DIR;
278
}
279
280
/*
281
* For DIR_TO_DNE, we could provide copy() with the to_stat we've
282
* already allocated on the stack here that isn't being used for
283
* anything. Not doing so, though, simplifies later logic a little bit
284
* as we need to skip checking root_stat on the first iteration and
285
* ensure that we set it with the first mkdir().
286
*/
287
exit (copy(argv, type, fts_options, (type == DIR_TO_DNE ? NULL :
288
&to_stat)));
289
}
290
291
static int
292
ftscmp(const FTSENT * const *a, const FTSENT * const *b)
293
{
294
return (strcmp((*a)->fts_name, (*b)->fts_name));
295
}
296
297
static int
298
copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
299
{
300
char rootname[NAME_MAX];
301
struct stat created_root_stat, to_stat, *curr_stat;
302
FTS *ftsp;
303
FTSENT *curr;
304
char *recpath = NULL, *sep;
305
int atflags, dne, badcp, len, level, rval;
306
mode_t mask, mode;
307
bool beneath = Rflag && type != FILE_TO_FILE;
308
309
/*
310
* Keep an inverted copy of the umask, for use in correcting
311
* permissions on created directories when not using -p.
312
*/
313
mask = ~umask(0777);
314
umask(~mask);
315
316
if (type == FILE_TO_FILE) {
317
to.dir = AT_FDCWD;
318
to.end = to.path + strlcpy(to.path, to.base, sizeof(to.path));
319
to.base[0] = '\0';
320
} else if (type == FILE_TO_DIR) {
321
to.dir = open(to.base, O_DIRECTORY | O_SEARCH);
322
if (to.dir < 0)
323
err(1, "%s", to.base);
324
/*
325
* We have previously made sure there is room for this.
326
*/
327
if (strcmp(to.base, "/") != 0) {
328
sep = strchr(to.base, '\0');
329
sep[0] = '/';
330
sep[1] = '\0';
331
}
332
} else {
333
/*
334
* We will create the destination directory imminently.
335
*/
336
to.dir = -1;
337
}
338
339
level = FTS_ROOTLEVEL;
340
if ((ftsp = fts_open(argv, fts_options, Sflag ? ftscmp : NULL)) == NULL)
341
err(1, "fts_open");
342
for (badcp = rval = 0;
343
(curr = fts_read(ftsp)) != NULL;
344
badcp = 0, *to.end = '\0') {
345
curr_stat = curr->fts_statp;
346
switch (curr->fts_info) {
347
case FTS_NS:
348
case FTS_DNR:
349
case FTS_ERR:
350
if (level > curr->fts_level) {
351
/* leaving a directory; remove its name from to.path */
352
if (type == DIR_TO_DNE &&
353
curr->fts_level == FTS_ROOTLEVEL) {
354
/* this is actually our created root */
355
} else {
356
while (to.end > to.path && *to.end != '/')
357
to.end--;
358
assert(strcmp(to.end + (*to.end == '/'),
359
curr->fts_name) == 0);
360
*to.end = '\0';
361
}
362
level--;
363
}
364
warnc(curr->fts_errno, "%s", curr->fts_path);
365
badcp = rval = 1;
366
continue;
367
case FTS_DC: /* Warn, continue. */
368
warnx("%s: directory causes a cycle", curr->fts_path);
369
badcp = rval = 1;
370
continue;
371
case FTS_D:
372
/*
373
* Stash the root basename off for detecting
374
* recursion later.
375
*
376
* This will be essential if the root is a symlink
377
* and we're rolling with -L or -H. The later
378
* bits will need this bit in particular.
379
*/
380
if (curr->fts_level == FTS_ROOTLEVEL) {
381
strlcpy(rootname, curr->fts_name,
382
sizeof(rootname));
383
}
384
/* we must have a destination! */
385
if (type == DIR_TO_DNE &&
386
curr->fts_level == FTS_ROOTLEVEL) {
387
assert(to.dir < 0);
388
assert(root_stat == NULL);
389
mode = curr_stat->st_mode | S_IRWXU;
390
/*
391
* Will our umask prevent us from entering
392
* the directory after we create it?
393
*/
394
if (~mask & S_IRWXU)
395
umask(~mask & ~S_IRWXU);
396
if (mkdir(to.base, mode) != 0) {
397
warn("%s", to.base);
398
fts_set(ftsp, curr, FTS_SKIP);
399
badcp = rval = 1;
400
if (~mask & S_IRWXU)
401
umask(~mask);
402
continue;
403
}
404
to.dir = open(to.base, O_DIRECTORY | O_SEARCH);
405
if (to.dir < 0) {
406
warn("%s", to.base);
407
(void)rmdir(to.base);
408
fts_set(ftsp, curr, FTS_SKIP);
409
badcp = rval = 1;
410
if (~mask & S_IRWXU)
411
umask(~mask);
412
continue;
413
}
414
if (fstat(to.dir, &created_root_stat) != 0) {
415
warn("%s", to.base);
416
(void)close(to.dir);
417
(void)rmdir(to.base);
418
fts_set(ftsp, curr, FTS_SKIP);
419
to.dir = -1;
420
badcp = rval = 1;
421
if (~mask & S_IRWXU)
422
umask(~mask);
423
continue;
424
}
425
if (~mask & S_IRWXU)
426
umask(~mask);
427
root_stat = &created_root_stat;
428
curr->fts_number = 1;
429
/*
430
* We have previously made sure there is
431
* room for this.
432
*/
433
sep = strchr(to.base, '\0');
434
sep[0] = '/';
435
sep[1] = '\0';
436
} else {
437
/* entering a directory; append its name to to.path */
438
len = snprintf(to.end, END(to.path) - to.end, "%s%s",
439
to.end > to.path ? "/" : "", curr->fts_name);
440
if (to.end + len >= END(to.path)) {
441
*to.end = '\0';
442
warnc(ENAMETOOLONG, "%s%s%s%s", to.base,
443
to.path, to.end > to.path ? "/" : "",
444
curr->fts_name);
445
fts_set(ftsp, curr, FTS_SKIP);
446
badcp = rval = 1;
447
continue;
448
}
449
to.end += len;
450
}
451
level++;
452
/*
453
* We're on the verge of recursing on ourselves.
454
* Either we need to stop right here (we knowingly
455
* just created it), or we will in an immediate
456
* descendant. Record the path of the immediate
457
* descendant to make our lives a little less
458
* complicated looking.
459
*/
460
if (type != FILE_TO_FILE &&
461
root_stat->st_dev == curr_stat->st_dev &&
462
root_stat->st_ino == curr_stat->st_ino) {
463
assert(recpath == NULL);
464
if (root_stat == &created_root_stat) {
465
/*
466
* This directory didn't exist
467
* when we started, we created it
468
* as part of traversal. Stop
469
* right here before we do
470
* something silly.
471
*/
472
fts_set(ftsp, curr, FTS_SKIP);
473
continue;
474
}
475
if (asprintf(&recpath, "%s/%s", to.path,
476
rootname) < 0) {
477
warnc(ENOMEM, NULL);
478
fts_set(ftsp, curr, FTS_SKIP);
479
badcp = rval = 1;
480
continue;
481
}
482
}
483
if (recpath != NULL &&
484
strcmp(recpath, to.path) == 0) {
485
fts_set(ftsp, curr, FTS_SKIP);
486
continue;
487
}
488
break;
489
case FTS_DP:
490
/*
491
* We are nearly finished with this directory. If we
492
* didn't actually copy it, or otherwise don't need to
493
* change its attributes, then we are done.
494
*
495
* If -p is in effect, set all the attributes.
496
* Otherwise, set the correct permissions, limited
497
* by the umask. Optimise by avoiding a chmod()
498
* if possible (which is usually the case if we
499
* made the directory). Note that mkdir() does not
500
* honour setuid, setgid and sticky bits, but we
501
* normally want to preserve them on directories.
502
*/
503
if (curr->fts_number && pflag) {
504
int fd = *to.path ? -1 : to.dir;
505
if (setfile(curr_stat, fd, true))
506
rval = 1;
507
if (preserve_dir_acls(curr->fts_accpath,
508
to.path) != 0)
509
rval = 1;
510
} else if (curr->fts_number) {
511
const char *path = *to.path ? to.path : dot;
512
mode = curr_stat->st_mode;
513
if (fchmodat(to.dir, path, mode & mask, 0) != 0) {
514
warn("chmod: %s%s", to.base, to.path);
515
rval = 1;
516
}
517
}
518
if (level > curr->fts_level) {
519
/* leaving a directory; remove its name from to.path */
520
if (type == DIR_TO_DNE &&
521
curr->fts_level == FTS_ROOTLEVEL) {
522
/* this is actually our created root */
523
} else {
524
while (to.end > to.path && *to.end != '/')
525
to.end--;
526
assert(strcmp(to.end + (*to.end == '/'),
527
curr->fts_name) == 0);
528
*to.end = '\0';
529
}
530
level--;
531
}
532
continue;
533
default:
534
/* something else: append its name to to.path */
535
if (type == FILE_TO_FILE)
536
break;
537
len = snprintf(to.end, END(to.path) - to.end, "%s%s",
538
to.end > to.path ? "/" : "", curr->fts_name);
539
if (to.end + len >= END(to.path)) {
540
*to.end = '\0';
541
warnc(ENAMETOOLONG, "%s%s%s%s", to.base,
542
to.path, to.end > to.path ? "/" : "",
543
curr->fts_name);
544
badcp = rval = 1;
545
continue;
546
}
547
/* intentionally do not update to.end */
548
break;
549
}
550
551
/* Not an error but need to remember it happened. */
552
if (to.path[0] == '\0') {
553
/*
554
* This can happen in two cases:
555
* - DIR_TO_DNE; we created the directory and
556
* populated root_stat earlier.
557
* - FILE_TO_DIR if a source has a trailing slash;
558
* the caller populated root_stat.
559
*/
560
dne = false;
561
to_stat = *root_stat;
562
} else {
563
atflags = beneath ? AT_RESOLVE_BENEATH : 0;
564
if (curr->fts_info == FTS_D || curr->fts_info == FTS_SL)
565
atflags |= AT_SYMLINK_NOFOLLOW;
566
dne = fstatat(to.dir, to.path, &to_stat, atflags) != 0;
567
}
568
569
/* Check if source and destination are identical. */
570
if (!dne &&
571
to_stat.st_dev == curr_stat->st_dev &&
572
to_stat.st_ino == curr_stat->st_ino) {
573
warnx("%s%s and %s are identical (not copied).",
574
to.base, to.path, curr->fts_path);
575
badcp = rval = 1;
576
if (S_ISDIR(curr_stat->st_mode))
577
fts_set(ftsp, curr, FTS_SKIP);
578
continue;
579
}
580
581
switch (curr_stat->st_mode & S_IFMT) {
582
case S_IFLNK:
583
if ((fts_options & FTS_LOGICAL) ||
584
((fts_options & FTS_COMFOLLOW) &&
585
curr->fts_level == 0)) {
586
/*
587
* We asked FTS to follow links but got
588
* here anyway, which means the target is
589
* nonexistent or inaccessible. Let
590
* copy_file() deal with the error.
591
*/
592
if (copy_file(curr, dne, beneath))
593
badcp = rval = 1;
594
} else {
595
/* Copy the link. */
596
if (copy_link(curr, dne, beneath))
597
badcp = rval = 1;
598
}
599
break;
600
case S_IFDIR:
601
if (!Rflag) {
602
warnx("%s is a directory (not copied).",
603
curr->fts_path);
604
fts_set(ftsp, curr, FTS_SKIP);
605
badcp = rval = 1;
606
break;
607
}
608
/*
609
* If the directory doesn't exist, create the new
610
* one with the from file mode plus owner RWX bits,
611
* modified by the umask. Trade-off between being
612
* able to write the directory (if from directory is
613
* 555) and not causing a permissions race. If the
614
* umask blocks owner writes, we fail.
615
*/
616
if (dne) {
617
mode = curr_stat->st_mode | S_IRWXU;
618
/*
619
* Will our umask prevent us from entering
620
* the directory after we create it?
621
*/
622
if (~mask & S_IRWXU)
623
umask(~mask & ~S_IRWXU);
624
if (mkdirat(to.dir, to.path, mode) != 0) {
625
warn("%s%s", to.base, to.path);
626
fts_set(ftsp, curr, FTS_SKIP);
627
badcp = rval = 1;
628
if (~mask & S_IRWXU)
629
umask(~mask);
630
break;
631
}
632
if (~mask & S_IRWXU)
633
umask(~mask);
634
} else if (!S_ISDIR(to_stat.st_mode)) {
635
warnc(ENOTDIR, "%s%s", to.base, to.path);
636
fts_set(ftsp, curr, FTS_SKIP);
637
badcp = rval = 1;
638
break;
639
}
640
/*
641
* Arrange to correct directory attributes later
642
* (in the post-order phase) if this is a new
643
* directory, or if the -p flag is in effect.
644
* Note that fts_number may already be set if this
645
* is the newly created destination directory.
646
*/
647
curr->fts_number |= pflag || dne;
648
break;
649
case S_IFBLK:
650
case S_IFCHR:
651
if (Rflag && !sflag) {
652
if (copy_special(curr_stat, dne, beneath))
653
badcp = rval = 1;
654
} else {
655
if (copy_file(curr, dne, beneath))
656
badcp = rval = 1;
657
}
658
break;
659
case S_IFSOCK:
660
warnx("%s is a socket (not copied).",
661
curr->fts_path);
662
break;
663
case S_IFIFO:
664
if (Rflag && !sflag) {
665
if (copy_fifo(curr_stat, dne, beneath))
666
badcp = rval = 1;
667
} else {
668
if (copy_file(curr, dne, beneath))
669
badcp = rval = 1;
670
}
671
break;
672
default:
673
if (copy_file(curr, dne, beneath))
674
badcp = rval = 1;
675
break;
676
}
677
if (vflag && !badcp)
678
(void)printf("%s -> %s%s\n", curr->fts_path, to.base, to.path);
679
}
680
assert(level == FTS_ROOTLEVEL);
681
if (errno)
682
err(1, "fts_read");
683
(void)fts_close(ftsp);
684
if (to.dir != AT_FDCWD && to.dir >= 0)
685
(void)close(to.dir);
686
free(recpath);
687
return (rval);
688
}
689
690
static void
691
siginfo(int sig __unused)
692
{
693
694
info = 1;
695
}
696
697