xref: /original-bsd/bin/cp/cp.c (revision d8754ee5)
1 /*
2  * Copyright (c) 1988 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * David Hitz of Auspex Systems Inc.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 char copyright[] =
13 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
14  All rights reserved.\n";
15 #endif /* not lint */
16 
17 #ifndef lint
18 static char sccsid[] = "@(#)cp.c	5.24 (Berkeley) 05/06/91";
19 #endif /* not lint */
20 
21 /*
22  * cp copies source files to target files.
23  *
24  * The global PATH_T structures "to" and "from" always contain paths to the
25  * current source and target files, respectively.  Since cp does not change
26  * directories, these paths can be either absolute or dot-realative.
27  *
28  * The basic algorithm is to initialize "to" and "from", and then call the
29  * recursive copy() function to do the actual work.  If "from" is a file,
30  * copy copies the data.  If "from" is a directory, copy creates the
31  * corresponding "to" directory, and calls itself recursively on all of
32  * the entries in the "from" directory.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <dirent.h>
39 #include <fcntl.h>
40 #include <errno.h>
41 #include <unistd.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include "cp.h"
46 
47 PATH_T from = { from.p_path, "" };
48 PATH_T to = { to.p_path, "" };
49 
50 uid_t myuid;
51 int exit_val, myumask;
52 int iflag, pflag, orflag, rflag;
53 int (*statfcn)();
54 char *buf, *progname;
55 
56 main(argc, argv)
57 	int argc;
58 	char **argv;
59 {
60 	extern int optind;
61 	struct stat to_stat;
62 	register int c, r;
63 	int symfollow, lstat(), stat();
64 	char *old_to, *p;
65 
66 	/*
67 	 * The utility cp(1) is used by mv(1) -- except for usage statements,
68 	 * print the "called as" program name.
69 	 */
70 	progname = (p = rindex(*argv,'/')) ? ++p : *argv;
71 
72 	symfollow = 0;
73 	while ((c = getopt(argc, argv, "Rfhipr")) != EOF) {
74 	switch ((char)c) {
75 		case 'f':
76 			iflag = 0;
77 			break;
78 		case 'h':
79 			symfollow = 1;
80 			break;
81 		case 'i':
82 			iflag = isatty(fileno(stdin));
83 			break;
84 		case 'p':
85 			pflag = 1;
86 			break;
87 		case 'R':
88 			rflag = 1;
89 			break;
90 		case 'r':
91 			orflag = 1;
92 			break;
93 		case '?':
94 		default:
95 			usage();
96 			break;
97 		}
98 	}
99 	argc -= optind;
100 	argv += optind;
101 
102 	if (argc < 2)
103 		usage();
104 
105 	if (rflag && orflag) {
106 		(void)fprintf(stderr,
107 		    "cp: the -R and -r options are mutually exclusive.\n");
108 		exit(1);
109 	}
110 
111 	buf = (char *)malloc(MAXBSIZE);
112 	if (!buf) {
113 		(void)fprintf(stderr, "%s: out of space.\n", progname);
114 		exit(1);
115 	}
116 
117 	myuid = getuid();
118 
119 	/* copy the umask for explicit mode setting */
120 	myumask = umask(0);
121 	(void)umask(myumask);
122 
123 	/* consume last argument first. */
124 	if (!path_set(&to, argv[--argc]))
125 		exit(1);
126 
127 	statfcn = symfollow || !rflag ? stat : lstat;
128 
129 	/*
130 	 * Cp has two distinct cases:
131 	 *
132 	 * % cp [-rip] source target
133 	 * % cp [-rip] source1 ... directory
134 	 *
135 	 * In both cases, source can be either a file or a directory.
136 	 *
137 	 * In (1), the target becomes a copy of the source. That is, if the
138 	 * source is a file, the target will be a file, and likewise for
139 	 * directories.
140 	 *
141 	 * In (2), the real target is not directory, but "directory/source".
142 	 */
143 
144 	r = stat(to.p_path, &to_stat);
145 	if (r == -1 && errno != ENOENT) {
146 		error(to.p_path);
147 		exit(1);
148 	}
149 	if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
150 		/*
151 		 * Case (1).  Target is not a directory.
152 		 */
153 		if (argc > 1) {
154 			usage();
155 			exit(1);
156 		}
157 		if (!path_set(&from, *argv))
158 			exit(1);
159 		copy();
160 	}
161 	else {
162 		/*
163 		 * Case (2).  Target is a directory.
164 		 */
165 		for (;; ++argv) {
166 			if (!path_set(&from, *argv)) {
167 				exit_val = 1;
168 				continue;
169 			}
170 			old_to = path_append(&to, path_basename(&from), -1);
171 			if (!old_to) {
172 				exit_val = 1;
173 				continue;
174 			}
175 			copy();
176 			if (!--argc)
177 				break;
178 			path_restore(&to, old_to);
179 		}
180 	}
181 	exit(exit_val);
182 }
183 
184 /* copy file or directory at "from" to "to". */
185 copy()
186 {
187 	struct stat from_stat, to_stat;
188 	int dne, statval;
189 
190 	statval = statfcn(from.p_path, &from_stat);
191 	if (statval == -1) {
192 		error(from.p_path);
193 		return;
194 	}
195 
196 	/* not an error, but need to remember it happened */
197 	if (stat(to.p_path, &to_stat) == -1)
198 		dne = 1;
199 	else {
200 		if (to_stat.st_dev == from_stat.st_dev &&
201 		    to_stat.st_ino == from_stat.st_ino) {
202 			(void)fprintf(stderr,
203 			    "%s: %s and %s are identical (not copied).\n",
204 			    progname, to.p_path, from.p_path);
205 			exit_val = 1;
206 			return;
207 		}
208 		dne = 0;
209 	}
210 
211 	switch(from_stat.st_mode & S_IFMT) {
212 	case S_IFLNK:
213 		copy_link(!dne);
214 		return;
215 	case S_IFDIR:
216 		if (!rflag && !orflag) {
217 			(void)fprintf(stderr,
218 			    "%s: %s is a directory (not copied).\n",
219 			    progname, from.p_path);
220 			exit_val = 1;
221 			return;
222 		}
223 		if (dne) {
224 			/*
225 			 * If the directory doesn't exist, create the new
226 			 * one with the from file mode plus owner RWX bits,
227 			 * modified by the umask.  Trade-off between being
228 			 * able to write the directory (if from directory is
229 			 * 555) and not causing a permissions race.  If the
230 			 * umask blocks owner writes cp fails.
231 			 */
232 			if (mkdir(to.p_path, from_stat.st_mode|S_IRWXU) < 0) {
233 				error(to.p_path);
234 				return;
235 			}
236 		}
237 		else if (!S_ISDIR(to_stat.st_mode) != S_IFDIR) {
238 			(void)fprintf(stderr, "%s: %s: not a directory.\n",
239 			    progname, to.p_path);
240 			return;
241 		}
242 		copy_dir();
243 		/*
244 		 * If not -p and directory didn't exist, set it to be the
245 		 * same as the from directory, umodified by the umask;
246 		 * arguably wrong, but it's been that way forever.
247 		 */
248 		if (pflag)
249 			setfile(&from_stat, 0);
250 		else if (dne)
251 			(void)chmod(to.p_path, from_stat.st_mode);
252 		return;
253 	case S_IFCHR:
254 	case S_IFBLK:
255 		if (rflag) {
256 			copy_special(&from_stat, !dne);
257 			return;
258 		}
259 		break;
260 	case S_IFIFO:
261 		if (rflag) {
262 			copy_fifo(&from_stat, !dne);
263 			return;
264 		}
265 		break;
266 	}
267 	copy_file(&from_stat, dne);
268 }
269 
270 copy_file(fs, dne)
271 	struct stat *fs;
272 	int dne;
273 {
274 	register int from_fd, to_fd, rcount, wcount;
275 	struct stat to_stat;
276 
277 	if ((from_fd = open(from.p_path, O_RDONLY, 0)) == -1) {
278 		error(from.p_path);
279 		return;
280 	}
281 
282 	/*
283 	 * If the file exists and we're interactive, verify with the user.
284 	 * If the file DNE, set the mode to be the from file, minus setuid
285 	 * bits, modified by the umask; arguably wrong, but it makes copying
286 	 * executables work right and it's been that way forever.  (The
287 	 * other choice is 666 or'ed with the execute bits on the from file
288 	 * modified by the umask.)
289 	 */
290 	if (!dne) {
291 		if (iflag) {
292 			int checkch, ch;
293 
294 			(void)fprintf(stderr, "overwrite %s? ", to.p_path);
295 			checkch = ch = getchar();
296 			while (ch != '\n' && ch != EOF)
297 				ch = getchar();
298 			if (checkch != 'y') {
299 				(void)close(from_fd);
300 				return;
301 			}
302 		}
303 		to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0);
304 	} else
305 		to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC,
306 		    fs->st_mode & ~(S_ISUID|S_ISGID));
307 
308 	if (to_fd == -1) {
309 		error(to.p_path);
310 		(void)close(from_fd);
311 		return;
312 	}
313 
314 	while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
315 		wcount = write(to_fd, buf, rcount);
316 		if (rcount != wcount || wcount == -1) {
317 			error(to.p_path);
318 			break;
319 		}
320 	}
321 	if (rcount < 0)
322 		error(from.p_path);
323 	if (pflag)
324 		setfile(fs, to_fd);
325 	/*
326 	 * If the source was setuid or setgid, lose the bits unless the
327 	 * copy is owned by the same user and group.
328 	 */
329 	else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid)
330 		if (fstat(to_fd, &to_stat))
331 			error(to.p_path);
332 #define	RETAINBITS	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
333 		else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd,
334 		    fs->st_mode & RETAINBITS & ~myumask))
335 			error(to.p_path);
336 	(void)close(from_fd);
337 	if (close(to_fd))
338 		error(to.p_path);
339 }
340 
341 copy_dir()
342 {
343 	struct stat from_stat;
344 	struct dirent *dp, **dir_list;
345 	register int dir_cnt, i;
346 	char *old_from, *old_to;
347 
348 	dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL);
349 	if (dir_cnt == -1) {
350 		(void)fprintf(stderr, "%s: can't read directory %s.\n",
351 		    progname, from.p_path);
352 		exit_val = 1;
353 	}
354 
355 	/*
356 	 * Instead of handling directory entries in the order they appear
357 	 * on disk, do non-directory files before directory files.
358 	 * There are two reasons to do directories last.  The first is
359 	 * efficiency.  Files tend to be in the same cylinder group as
360 	 * their parent, whereas directories tend not to be.  Copying files
361 	 * all at once reduces seeking.  Second, deeply nested tree's
362 	 * could use up all the file descriptors if we didn't close one
363 	 * directory before recursivly starting on the next.
364 	 */
365 	/* copy files */
366 	for (i = 0; i < dir_cnt; ++i) {
367 		dp = dir_list[i];
368 		if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
369 		    && (dp->d_name[1] == NULL || dp->d_name[1] == '.'))
370 			goto done;
371 		old_from = path_append(&from, dp->d_name, (int)dp->d_namlen);
372 		if (!old_from) {
373 			exit_val = 1;
374 			goto done;
375 		}
376 
377 		if (statfcn(from.p_path, &from_stat) < 0) {
378 			error(dp->d_name);
379 			path_restore(&from, old_from);
380 			goto done;
381 		}
382 		if (S_ISDIR(from_stat.st_mode)) {
383 			path_restore(&from, old_from);
384 			continue;
385 		}
386 		old_to = path_append(&to, dp->d_name, (int)dp->d_namlen);
387 		if (old_to) {
388 			copy();
389 			path_restore(&to, old_to);
390 		} else
391 			exit_val = 1;
392 		path_restore(&from, old_from);
393 done:		dir_list[i] = NULL;
394 		(void)free((void *)dp);
395 	}
396 
397 	/* copy directories */
398 	for (i = 0; i < dir_cnt; ++i) {
399 		dp = dir_list[i];
400 		if (!dp)
401 			continue;
402 		old_from = path_append(&from, dp->d_name, (int) dp->d_namlen);
403 		if (!old_from) {
404 			exit_val = 1;
405 			(void)free((void *)dp);
406 			continue;
407 		}
408 		old_to = path_append(&to, dp->d_name, (int) dp->d_namlen);
409 		if (!old_to) {
410 			exit_val = 1;
411 			(void)free((void *)dp);
412 			path_restore(&from, old_from);
413 			continue;
414 		}
415 		copy();
416 		free((void *)dp);
417 		path_restore(&from, old_from);
418 		path_restore(&to, old_to);
419 	}
420 	free((void *)dir_list);
421 }
422 
423 copy_link(exists)
424 	int exists;
425 {
426 	int len;
427 	char link[MAXPATHLEN];
428 
429 	if ((len = readlink(from.p_path, link, sizeof(link))) == -1) {
430 		error(from.p_path);
431 		return;
432 	}
433 	link[len] = '\0';
434 	if (exists && unlink(to.p_path)) {
435 		error(to.p_path);
436 		return;
437 	}
438 	if (symlink(link, to.p_path)) {
439 		error(link);
440 		return;
441 	}
442 }
443 
444 copy_fifo(from_stat, exists)
445 	struct stat *from_stat;
446 	int exists;
447 {
448 	if (exists && unlink(to.p_path)) {
449 		error(to.p_path);
450 		return;
451 	}
452 	if (mkfifo(to.p_path, from_stat->st_mode)) {
453 		error(to.p_path);
454 		return;
455 	}
456 	if (pflag)
457 		setfile(from_stat, 0);
458 }
459 
460 copy_special(from_stat, exists)
461 	struct stat *from_stat;
462 	int exists;
463 {
464 	if (exists && unlink(to.p_path)) {
465 		error(to.p_path);
466 		return;
467 	}
468 	if (mknod(to.p_path, from_stat->st_mode,  from_stat->st_rdev)) {
469 		error(to.p_path);
470 		return;
471 	}
472 	if (pflag)
473 		setfile(from_stat, 0);
474 }
475 
476 setfile(fs, fd)
477 	register struct stat *fs;
478 	int fd;
479 {
480 	static struct timeval tv[2];
481 	char path[100];
482 
483 	fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
484 
485 	tv[0].tv_sec = fs->st_atime;
486 	tv[1].tv_sec = fs->st_mtime;
487 	if (utimes(to.p_path, tv)) {
488 		(void)snprintf(path, sizeof(path), "utimes: %s", to.p_path);
489 		error(path);
490 	}
491 	/*
492 	 * Changing the ownership probably won't succeed, unless we're root
493 	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
494 	 * the mode; current BSD behavior is to remove all setuid bits on
495 	 * chown.  If chown fails, lose setuid/setgid bits.
496 	 */
497 	if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
498 	    chown(to.p_path, fs->st_uid, fs->st_gid)) {
499 		if (errno != EPERM) {
500 			(void)snprintf(path, sizeof(path),
501 			    "chown: %s", to.p_path);
502 			error(path);
503 		}
504 		fs->st_mode &= ~(S_ISUID|S_ISGID);
505 	}
506 	if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
507 		(void)snprintf(path, sizeof(path), "chown: %s", to.p_path);
508 		error(path);
509 	}
510 }
511 
512 error(s)
513 	char *s;
514 {
515 	exit_val = 1;
516 	(void)fprintf(stderr, "%s: %s: %s\n", progname, s, strerror(errno));
517 }
518 
519 usage()
520 {
521 	(void)fprintf(stderr,
522 "usage: cp [-Rfhip] src target;\n   or: cp [-Rfhip] src1 ... srcN directory\n");
523 	exit(1);
524 }
525