GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/cp/cp.c Lines: 72 172 41.9 %
Date: 2016-12-06 Branches: 47 150 31.3 %

Line Branch Exec Source
1
/*	$OpenBSD: cp.c,v 1.43 2016/03/07 18:56:33 tb 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 <locale.h>
62
#include <stdio.h>
63
#include <stdlib.h>
64
#include <string.h>
65
#include <unistd.h>
66
#include <limits.h>
67
68
#include "extern.h"
69
70
#define	fts_dne(_x)	(_x->fts_pointer != NULL)
71
72
PATH_T to = { to.p_path, "" };
73
74
uid_t myuid;
75
int Rflag, fflag, iflag, pflag, rflag;
76
mode_t myumask;
77
78
enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
79
80
int copy(char *[], enum op, int);
81
char *find_last_component(char *);
82
83
int
84
main(int argc, char *argv[])
85
83
{
86
	struct stat to_stat, tmp_stat;
87
	enum op type;
88
	int Hflag, Lflag, Pflag, ch, fts_options, r;
89
	char *target;
90
91
83
	(void)setlocale(LC_ALL, "");
92
93
83
	Hflag = Lflag = Pflag = Rflag = 0;
94
166
	while ((ch = getopt(argc, argv, "HLPRfipr")) != -1)
95
		switch (ch) {
96
		case 'H':
97
			Hflag = 1;
98
			Lflag = Pflag = 0;
99
			break;
100
		case 'L':
101
			Lflag = 1;
102
			Hflag = Pflag = 0;
103
			break;
104
		case 'P':
105
			Pflag = 1;
106
			Hflag = Lflag = 0;
107
			break;
108
		case 'R':
109
			Rflag = 1;
110
			break;
111
		case 'f':
112
			fflag = 1;
113
			iflag = 0;
114
			break;
115
		case 'i':
116
			iflag = 1;
117
			fflag = 0;
118
			break;
119
		case 'p':
120
			pflag = 1;
121
			break;
122
		case 'r':
123
			rflag = 1;
124
			break;
125
		default:
126
			usage();
127
			break;
128
		}
129
83
	argc -= optind;
130
83
	argv += optind;
131
132
	/*
133
	 * Unfortunately, -R will use mkfifo & mknod;
134
	 * -p will use fchown, fchmod, lchown, fchflags..
135
	 */
136

83
	if (Rflag == 0 && pflag == 0)
137
83
		if (pledge("stdio rpath wpath cpath fattr", NULL) == -1)
138
			err(1, "pledge");
139
140
83
	if (argc < 2)
141
		usage();
142
143
83
	fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
144
83
	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
83
	if (Rflag) {
155
		if (Hflag)
156
			fts_options |= FTS_COMFOLLOW;
157
		if (Lflag) {
158
			fts_options &= ~FTS_PHYSICAL;
159
			fts_options |= FTS_LOGICAL;
160
		}
161
	} else {
162
83
		fts_options &= ~FTS_PHYSICAL;
163
83
		fts_options |= FTS_LOGICAL;
164
	}
165
166
83
	myuid = getuid();
167
168
	/* Copy the umask for explicit mode setting. */
169
83
	myumask = umask(0);
170
83
	(void)umask(myumask);
171
172
	/* Save the target base in "to". */
173
83
	target = argv[--argc];
174
83
	if (strlcpy(to.p_path, target, sizeof to.p_path) >= sizeof(to.p_path))
175
		errx(1, "%s: name too long", target);
176
83
	to.p_end = to.p_path + strlen(to.p_path);
177
83
	if (to.p_path == to.p_end) {
178
		*to.p_end++ = '.';
179
		*to.p_end = '\0';
180
	}
181
83
	to.target_end = to.p_end;
182
183
	/* Set end of argument list for fts(3). */
184
83
	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
83
	r = stat(to.p_path, &to_stat);
201

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

83
	if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
204
		/*
205
		 * Case (1).  Target is not a directory.
206
		 */
207
82
		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
82
		if (r == -1) {
217

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

62
			if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag))
223
				type = DIR_TO_DNE;
224
			else
225
62
				type = FILE_TO_FILE;
226
		} else
227
20
			type = FILE_TO_FILE;
228
	} else {
229
		/*
230
		 * Case (2).  Target is a directory.
231
		 */
232
1
		type = FILE_TO_DIR;
233
	}
234
235
83
	exit(copy(argv, type, fts_options));
236
}
237
238
char *
239
find_last_component(char *path)
240
1
{
241
	char *p;
242
243
1
	if ((p = strrchr(path, '/')) == NULL)
244
		p = path;
245
	else {
246
		/* Special case foo/ */
247
1
		if (!*(p+1)) {
248
			while ((p >= path) && *p == '/')
249
				p--;
250
251
			while ((p >= path) && *p != '/')
252
				p--;
253
		}
254
255
1
		p++;
256
	}
257
258
1
	return (p);
259
}
260
261
int
262
copy(char *argv[], enum op type, int fts_options)
263
83
{
264
	struct stat to_stat;
265
	FTS *ftsp;
266
	FTSENT *curr;
267
	int base, nlen, rval;
268
	char *p, *target_mid;
269
83
	base = 0;
270
271
83
	if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
272
		err(1, NULL);
273
249
	for (rval = 0; (curr = fts_read(ftsp)) != NULL;) {
274
83
		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
83
		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
1
			if (curr->fts_level == FTS_ROOTLEVEL) {
313
1
				if (type != DIR_TO_DNE) {
314
1
					p = find_last_component(curr->fts_path);
315
1
					base = p - curr->fts_path;
316
317
1
					if (!strcmp(&curr->fts_path[base],
318
					    ".."))
319
						base += 1;
320
				} else
321
					base = curr->fts_pathlen;
322
			}
323
324
1
			p = &curr->fts_path[base];
325
1
			nlen = curr->fts_pathlen - base;
326
1
			target_mid = to.target_end;
327

1
			if (*p != '/' && target_mid[-1] != '/')
328
1
				*target_mid++ = '/';
329
1
			*target_mid = '\0';
330
1
			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
1
			(void)strncat(target_mid, p, nlen);
337
1
			to.p_end = target_mid + nlen;
338
1
			*to.p_end = '\0';
339
		}
340
341
		/* Not an error but need to remember it happened */
342
83
		if (stat(to.p_path, &to_stat) == -1) {
343
63
			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
63
			curr->fts_pointer = (void *)1;
353
		} 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
20
			if (curr->fts_info == FTS_DP) {
363
				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
				if (pflag && setfile(curr->fts_statp, -1))
372
					rval = 1;
373
				else if (fts_dne(curr))
374
					(void)chmod(to.p_path,
375
					    curr->fts_statp->st_mode);
376
				continue;
377
			}
378

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

20
			if (!S_ISDIR(curr->fts_statp->st_mode) &&
388
			    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

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