GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/cp/cp.c Lines: 97 179 54.2 %
Date: 2017-11-07 Branches: 77 158 48.7 %

Line Branch Exec Source
1
/*	$OpenBSD: cp.c,v 1.46 2017/06/27 21:49:47 tedu Exp $	*/
2
/*	$NetBSD: cp.c,v 1.14 1995/09/07 06:14:51 jtc Exp $	*/
3
4
/*
5
 * Copyright (c) 1988, 1993, 1994
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * This code is derived from software contributed to Berkeley by
9
 * David Hitz of Auspex Systems Inc.
10
 *
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following conditions
13
 * are met:
14
 * 1. Redistributions of source code must retain the above copyright
15
 *    notice, this list of conditions and the following disclaimer.
16
 * 2. Redistributions in binary form must reproduce the above copyright
17
 *    notice, this list of conditions and the following disclaimer in the
18
 *    documentation and/or other materials provided with the distribution.
19
 * 3. Neither the name of the University nor the names of its contributors
20
 *    may be used to endorse or promote products derived from this software
21
 *    without specific prior written permission.
22
 *
23
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33
 * SUCH DAMAGE.
34
 */
35
36
/*
37
 * Cp copies source files to target files.
38
 *
39
 * The global PATH_T structure "to" always contains the path to the
40
 * current target file.  Since fts(3) does not change directories,
41
 * this path can be either absolute or dot-relative.
42
 *
43
 * The basic algorithm is to initialize "to" and use fts(3) to traverse
44
 * the file hierarchy rooted in the argument list.  A trivial case is the
45
 * case of 'cp file1 file2'.  The more interesting case is the case of
46
 * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
47
 * path (relative to the root of the traversal) is appended to dir (stored
48
 * in "to") to form the final target path.
49
 */
50
51
#include <sys/types.h>
52
#include <sys/stat.h>
53
#include <sys/mman.h>
54
#include <sys/time.h>
55
56
#include <dirent.h>
57
#include <err.h>
58
#include <errno.h>
59
#include <fcntl.h>
60
#include <fts.h>
61
#include <limits.h>
62
#include <stdio.h>
63
#include <stdlib.h>
64
#include <string.h>
65
#include <unistd.h>
66
67
#include "extern.h"
68
69
#define	fts_dne(_x)	(_x->fts_pointer != NULL)
70
71
PATH_T to = { to.p_path, "" };
72
73
uid_t myuid;
74
int Rflag, fflag, iflag, pflag, rflag, vflag;
75
mode_t myumask;
76
77
enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
78
79
int copy(char *[], enum op, int);
80
char *find_last_component(char *);
81
82
int
83
main(int argc, char *argv[])
84
{
85
2336
	struct stat to_stat, tmp_stat;
86
	enum op type;
87
	int Hflag, Lflag, Pflag, ch, fts_options, r;
88
	char *target;
89
90
1168
	Hflag = Lflag = Pflag = Rflag = 0;
91
2533
	while ((ch = getopt(argc, argv, "HLPRfiprv")) != -1)
92


197
		switch (ch) {
93
		case 'H':
94
			Hflag = 1;
95
			Lflag = Pflag = 0;
96
			break;
97
		case 'L':
98
			Lflag = 1;
99
			Hflag = Pflag = 0;
100
			break;
101
		case 'P':
102
			Pflag = 1;
103
			Hflag = Lflag = 0;
104
			break;
105
		case 'R':
106
50
			Rflag = 1;
107
50
			break;
108
		case 'f':
109
97
			fflag = 1;
110
97
			iflag = 0;
111
97
			break;
112
		case 'i':
113
			iflag = 1;
114
			fflag = 0;
115
			break;
116
		case 'p':
117
50
			pflag = 1;
118
50
			break;
119
		case 'r':
120
			rflag = 1;
121
			break;
122
		case 'v':
123
			vflag = 1;
124
			break;
125
		default:
126
			usage();
127
			break;
128
		}
129
1168
	argc -= optind;
130
1168
	argv += optind;
131
132
	/*
133
	 * Unfortunately, -R will use mkfifo & mknod;
134
	 * -p will use fchown, fchmod, lchown, fchflags..
135
	 */
136
1168
	if (Rflag == 0 && pflag == 0)
137
1118
		if (pledge("stdio rpath wpath cpath fattr flock", NULL) == -1)
138
			err(1, "pledge");
139
140
1168
	if (argc < 2)
141
		usage();
142
143
	fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
144
1168
	if (rflag) {
145
		if (Rflag)
146
			errx(1,
147
		    "the -R and -r options may not be specified together.");
148
		if (Hflag || Lflag || Pflag)
149
			errx(1,
150
	"the -H, -L, and -P options may not be specified with the -r option.");
151
		fts_options &= ~FTS_PHYSICAL;
152
		fts_options |= FTS_LOGICAL;
153
	}
154
1168
	if (Rflag) {
155
50
		if (Hflag)
156
			fts_options |= FTS_COMFOLLOW;
157
50
		if (Lflag) {
158
			fts_options &= ~FTS_PHYSICAL;
159
			fts_options |= FTS_LOGICAL;
160
		}
161
	} else {
162
1118
		fts_options &= ~FTS_PHYSICAL;
163
		fts_options |= FTS_LOGICAL;
164
	}
165
166
1168
	myuid = getuid();
167
168
	/* Copy the umask for explicit mode setting. */
169
1168
	myumask = umask(0);
170
1168
	(void)umask(myumask);
171
172
	/* Save the target base in "to". */
173
1168
	target = argv[--argc];
174
1168
	if (strlcpy(to.p_path, target, sizeof to.p_path) >= sizeof(to.p_path))
175
		errx(1, "%s: name too long", target);
176
1168
	to.p_end = to.p_path + strlen(to.p_path);
177
1168
	if (to.p_path == to.p_end) {
178
		*to.p_end++ = '.';
179
		*to.p_end = '\0';
180
	}
181
1168
	to.target_end = to.p_end;
182
183
	/* Set end of argument list for fts(3). */
184
1168
	argv[argc] = NULL;
185
186
	/*
187
	 * Cp has two distinct cases:
188
	 *
189
	 * cp [-R] source target
190
	 * cp [-R] source1 ... sourceN directory
191
	 *
192
	 * In both cases, source can be either a file or a directory.
193
	 *
194
	 * In (1), the target becomes a copy of the source. That is, if the
195
	 * source is a file, the target will be a file, and likewise for
196
	 * directories.
197
	 *
198
	 * In (2), the real target is not directory, but "directory/source".
199
	 */
200
1168
	r = stat(to.p_path, &to_stat);
201

1726
	if (r == -1 && errno != ENOENT)
202
		err(1, "%s", to.p_path);
203

1778
	if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
204
		/*
205
		 * Case (1).  Target is not a directory.
206
		 */
207
1029
		if (argc > 1)
208
			usage();
209
		/*
210
		 * Need to detect the case:
211
		 *	cp -R dir foo
212
		 * Where dir is a directory and foo does not exist, where
213
		 * we want pathname concatenations turned on but not for
214
		 * the initial mkdir().
215
		 */
216
1029
		if (r == -1) {
217

1166
			if (rflag || (Rflag && (Lflag || Hflag)))
218
				stat(*argv, &tmp_stat);
219
			else
220
558
				lstat(*argv, &tmp_stat);
221
222

608
			if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag))
223
50
				type = DIR_TO_DNE;
224
			else
225
				type = FILE_TO_FILE;
226
		} else
227
			type = FILE_TO_FILE;
228
	} else {
229
		/*
230
		 * Case (2).  Target is a directory.
231
		 */
232
		type = FILE_TO_DIR;
233
	}
234
235
2336
	return (copy(argv, type, fts_options));
236
1168
}
237
238
char *
239
find_last_component(char *path)
240
{
241
	char *p;
242
243
326
	if ((p = strrchr(path, '/')) == NULL)
244
10
		p = path;
245
	else {
246
		/* Special case foo/ */
247
153
		if (!*(p+1)) {
248
			while ((p >= path) && *p == '/')
249
				p--;
250
251
			while ((p >= path) && *p != '/')
252
				p--;
253
		}
254
255
153
		p++;
256
	}
257
258
163
	return (p);
259
}
260
261
int
262
copy(char *argv[], enum op type, int fts_options)
263
{
264
2336
	struct stat to_stat;
265
	FTS *ftsp;
266
	FTSENT *curr;
267
	int base, nlen, rval;
268
	char *p, *target_mid;
269
	base = 0;
270
271
1168
	if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
272
		err(1, NULL);
273
3898
	for (rval = 0; (curr = fts_read(ftsp)) != NULL;) {
274

1562
		switch (curr->fts_info) {
275
		case FTS_NS:
276
		case FTS_DNR:
277
		case FTS_ERR:
278
			warnx("%s: %s",
279
			    curr->fts_path, strerror(curr->fts_errno));
280
			rval = 1;
281
			continue;
282
		case FTS_DC:
283
			warnx("%s: directory causes a cycle", curr->fts_path);
284
			rval = 1;
285
			continue;
286
		}
287
288
		/*
289
		 * If we are in case (2) or (3) above, we need to append the
290
		 * source name to the target name.
291
		 */
292
1562
		if (type != FILE_TO_FILE) {
293
			/*
294
			 * Need to remember the roots of traversals to create
295
			 * correct pathnames.  If there's a directory being
296
			 * copied to a non-existent directory, e.g.
297
			 *	cp -R a/dir noexist
298
			 * the resulting path name should be noexist/foo, not
299
			 * noexist/dir/foo (where foo is a file in dir), which
300
			 * is the case where the target exists.
301
			 *
302
			 * Also, check for "..".  This is for correct path
303
			 * concatenation for paths ending in "..", e.g.
304
			 *	cp -R .. /tmp
305
			 * Paths ending in ".." are changed to ".".  This is
306
			 * tricky, but seems the easiest way to fix the problem.
307
			 *
308
			 * XXX
309
			 * Since the first level MUST be FTS_ROOTLEVEL, base
310
			 * is always initialized.
311
			 */
312
583
			if (curr->fts_level == FTS_ROOTLEVEL) {
313
263
				if (type != DIR_TO_DNE) {
314
163
					p = find_last_component(curr->fts_path);
315
163
					base = p - curr->fts_path;
316
317
163
					if (!strcmp(&curr->fts_path[base],
318
					    ".."))
319
						base += 1;
320
				} else
321
100
					base = curr->fts_pathlen;
322
			}
323
324
583
			p = &curr->fts_path[base];
325
583
			nlen = curr->fts_pathlen - base;
326
583
			target_mid = to.target_end;
327

846
			if (*p != '/' && target_mid[-1] != '/')
328
123
				*target_mid++ = '/';
329
583
			*target_mid = '\0';
330
583
			if (target_mid - to.p_path + nlen >= PATH_MAX) {
331
				warnx("%s%s: name too long (not copied)",
332
				    to.p_path, p);
333
				rval = 1;
334
				continue;
335
			}
336
583
			(void)strncat(target_mid, p, nlen);
337
583
			to.p_end = target_mid + nlen;
338
583
			*to.p_end = '\0';
339
583
		}
340
341
		/* Not an error but need to remember it happened */
342
1562
		if (stat(to.p_path, &to_stat) == -1) {
343
946
			if (curr->fts_info == FTS_DP)
344
				continue;
345
			/*
346
			 * We use fts_pointer as a boolean to indicate that
347
			 * we created this directory ourselves.  We'll use
348
			 * this later on via the fts_dne macro to decide
349
			 * whether or not to set the directory mode during
350
			 * the post-order pass.
351
			 */
352
946
			curr->fts_pointer = (void *)1;
353
946
		} else {
354
			/*
355
			 * Set directory mode/user/times on the post-order
356
			 * pass.  We can't do this earlier because the mode
357
			 * may not allow us write permission.  Furthermore,
358
			 * if we set the times during the pre-order pass,
359
			 * they will get changed later when the directory
360
			 * is populated.
361
			 */
362
616
			if (curr->fts_info == FTS_DP) {
363
130
				if (!S_ISDIR(to_stat.st_mode))
364
					continue;
365
				/*
366
				 * If not -p and directory didn't exist, set
367
				 * it to be the same as the from directory,
368
				 * unmodified by the umask; arguably wrong,
369
				 * but it's been that way forever.
370
				 */
371

260
				if (pflag && setfile(curr->fts_statp, -1))
372
					rval = 1;
373
130
				else if (fts_dne(curr))
374
130
					(void)chmod(to.p_path,
375
130
					    curr->fts_statp->st_mode);
376
				continue;
377
			}
378

928
			if (to_stat.st_dev == curr->fts_statp->st_dev &&
379
442
			    to_stat.st_ino == curr->fts_statp->st_ino) {
380
4
				warnx("%s and %s are identical (not copied).",
381
4
				    to.p_path, curr->fts_path);
382
				rval = 1;
383
4
				if (S_ISDIR(curr->fts_statp->st_mode))
384
					(void)fts_set(ftsp, curr, FTS_SKIP);
385
				continue;
386
			}
387

964
			if (!S_ISDIR(curr->fts_statp->st_mode) &&
388
482
			    S_ISDIR(to_stat.st_mode)) {
389
		warnx("cannot overwrite directory %s with non-directory %s",
390
				    to.p_path, curr->fts_path);
391
				rval = 1;
392
				continue;
393
			}
394
		}
395
396

1428
		switch (curr->fts_statp->st_mode & S_IFMT) {
397
		case S_IFLNK:
398
			if (copy_link(curr, !fts_dne(curr)))
399
				rval = 1;
400
			else if (vflag)
401
				(void)fprintf(stdout, "%s -> %s\n",
402
				    curr->fts_path, to.p_path);
403
			break;
404
		case S_IFDIR:
405
130
			if (!Rflag && !rflag) {
406
				warnx("%s is a directory (not copied).",
407
				    curr->fts_path);
408
				(void)fts_set(ftsp, curr, FTS_SKIP);
409
				rval = 1;
410
				break;
411
			}
412
			/*
413
			 * If the directory doesn't exist, create the new
414
			 * one with the from file mode plus owner RWX bits,
415
			 * modified by the umask.  Trade-off between being
416
			 * able to write the directory (if from directory is
417
			 * 555) and not causing a permissions race.  If the
418
			 * umask blocks owner writes, we fail..
419
			 */
420
130
			if (fts_dne(curr)) {
421
260
				if (mkdir(to.p_path,
422
260
				    curr->fts_statp->st_mode | S_IRWXU) < 0)
423
					err(1, "%s", to.p_path);
424
130
				else if (vflag)
425
					(void)fprintf(stdout, "%s -> %s\n",
426
					    curr->fts_path, to.p_path);
427
			} else if (!S_ISDIR(to_stat.st_mode))
428
				errc(1, ENOTDIR, "%s", to.p_path);
429
			break;
430
		case S_IFBLK:
431
		case S_IFCHR:
432
			if (Rflag) {
433
				if (copy_special(curr->fts_statp,
434
				    !fts_dne(curr)))
435
					rval = 1;
436
			} else
437
				if (copy_file(curr, fts_dne(curr)))
438
					rval = 1;
439
			if (!rval && vflag)
440
				(void)fprintf(stdout, "%s -> %s\n",
441
				    curr->fts_path, to.p_path);
442
			break;
443
		case S_IFIFO:
444
			if (Rflag) {
445
				if (copy_fifo(curr->fts_statp, !fts_dne(curr)))
446
					rval = 1;
447
			} else
448
				if (copy_file(curr, fts_dne(curr)))
449
					rval = 1;
450
			if (!rval && vflag)
451
				(void)fprintf(stdout, "%s -> %s\n",
452
				    curr->fts_path, to.p_path);
453
			break;
454
		case S_IFSOCK:
455
			warnc(EOPNOTSUPP, "%s", curr->fts_path);
456
			break;
457
		default:
458
1298
			if (copy_file(curr, fts_dne(curr)))
459
				rval = 1;
460
1298
			else if (vflag)
461
				(void)fprintf(stdout, "%s -> %s\n",
462
				    curr->fts_path, to.p_path);
463
			break;
464
		}
465
	}
466
1168
	if (errno)
467
		err(1, "fts_read");
468
1168
	(void)fts_close(ftsp);
469
1168
	return (rval);
470
1168
}