xref: /original-bsd/bin/cp/cp.c (revision f7b3bed6)
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.17 (Berkeley) 05/31/90";
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/file.h>
38 #include <sys/dir.h>
39 #include <sys/time.h>
40 #include <stdio.h>
41 #include <errno.h>
42 #include <string.h>
43 
44 #define	type(st)	((st).st_mode & S_IFMT)
45 
46 typedef struct {
47 	char	p_path[MAXPATHLEN + 1];	/* pointer to the start of a path. */
48 	char	*p_end;			/* pointer to NULL at end of path. */
49 } PATH_T;
50 
51 PATH_T from = { "", from.p_path };
52 PATH_T to = { "", to.p_path };
53 
54 extern int errno;
55 uid_t myuid;
56 int exit_val, myumask;
57 int interactive_flag, preserve_flag, recursive_flag;
58 int (*statfcn)();
59 char *buf, *pname;
60 char *path_append(), *path_basename();
61 
62 main(argc, argv)
63 	int argc;
64 	char **argv;
65 {
66 	extern int optind;
67 	struct stat to_stat;
68 	register int c, r;
69 	int force_flag, symfollow, lstat(), stat();
70 	char *old_to, *p, *malloc();
71 
72 	/*
73 	 * cp is used by mv(1) -- except for usage statements, print
74 	 * the "called as" program name.
75 	 */
76 	pname = (p = rindex(*argv,'/')) ? ++p : *argv;
77 
78 	force_flag = symfollow = 0;
79 	while ((c = getopt(argc, argv, "Rfhipr")) != EOF) {
80 	switch ((char)c) {
81 		case 'f':
82 			force_flag = 1;
83 			break;
84 		case 'h':
85 			symfollow = 1;
86 			break;
87 		case 'i':
88 			interactive_flag = isatty(fileno(stdin));
89 			break;
90 		case 'p':
91 			preserve_flag = 1;
92 			break;
93 		case 'r':
94 		case 'R':
95 			recursive_flag = 1;
96 			break;
97 		case '?':
98 		default:
99 			usage();
100 			break;
101 		}
102 	}
103 	argc -= optind;
104 	argv += optind;
105 
106 	if (argc < 2)
107 		usage();
108 
109 	if (force_flag)
110 		interactive_flag = 0;
111 
112 	buf = (char *)malloc(MAXBSIZE);
113 	if (!buf) {
114 		(void)fprintf(stderr, "%s: out of space.\n", pname);
115 		exit(1);
116 	}
117 
118 	myuid = getuid();
119 
120 	/* copy the umask for explicit mode setting */
121 	myumask = umask(0);
122 	(void)umask(myumask);
123 
124 	/* consume last argument first. */
125 	if (!path_set(&to, argv[--argc]))
126 		exit(exit_val);
127 
128 	statfcn = symfollow || !recursive_flag ? stat : lstat;
129 
130 	/*
131 	 * Cp has two distinct cases:
132 	 *
133 	 * Case (1)	  $ cp [-rip] source target
134 	 *
135 	 * Case (2)	  $ cp [-rip] source1 ... directory
136 	 *
137 	 * In both cases, source can be either a file or a directory.
138 	 *
139 	 * In (1), the target becomes a copy of the source. That is, if the
140 	 * source is a file, the target will be a file, and likewise for
141 	 * directories.
142 	 *
143 	 * In (2), the real target is not directory, but "directory/source".
144 	 */
145 
146 	r = stat(to.p_path, &to_stat);
147 	if (r == -1 && errno != ENOENT) {
148 		error(to.p_path);
149 		exit(1);
150 	}
151 	if (r == -1 || type(to_stat) != S_IFDIR) {
152 		/*
153 		 * Case (1).  Target is not a directory.
154 		 */
155 		if (argc > 1) {
156 			usage();
157 			exit(1);
158 		}
159 		if (!path_set(&from, *argv))
160 			exit(exit_val);
161 		copy();
162 	}
163 	else {
164 		/*
165 		 * Case (2).  Target is a directory.
166 		 */
167 		for (;; ++argv) {
168 			if (!path_set(&from, *argv))
169 				continue;
170 			old_to = path_append(&to, path_basename(&from), -1);
171 			if (!old_to)
172 				continue;
173 			copy();
174 			if (!--argc)
175 				break;
176 			path_restore(&to, old_to);
177 		}
178 	}
179 	exit(exit_val);
180 }
181 
182 /* copy file or directory at "from" to "to". */
183 copy()
184 {
185 	struct stat from_stat, to_stat;
186 	int dne, statval;
187 
188 	statval = statfcn(from.p_path, &from_stat);
189 	if (statval == -1) {
190 		error(from.p_path);
191 		return;
192 	}
193 
194 	/* not an error, but need to remember it happened */
195 	if (stat(to.p_path, &to_stat) == -1)
196 		dne = 1;
197 	else {
198 		if (to_stat.st_dev == from_stat.st_dev &&
199 		    to_stat.st_ino == from_stat.st_ino) {
200 			(void)fprintf(stderr,
201 			    "%s: %s and %s are identical (not copied).\n",
202 			    pname, to.p_path, from.p_path);
203 			exit_val = 1;
204 			return;
205 		}
206 		dne = 0;
207 	}
208 
209 	switch(type(from_stat)) {
210 	case S_IFLNK:
211 		copy_link(!dne);
212 		return;
213 	case S_IFDIR:
214 		if (!recursive_flag) {
215 			(void)fprintf(stderr,
216 			    "%s: %s is a directory (not copied).\n",
217 			    pname, from.p_path);
218 			exit_val = 1;
219 			return;
220 		}
221 		if (dne) {
222 			/*
223 			 * If the directory doesn't exist, create the new
224 			 * one with the from file mode plus owner RWX bits,
225 			 * modified by the umask.  Trade-off between being
226 			 * able to write the directory (if from directory is
227 			 * 555) and not causing a permissions race.  If the
228 			 * umask blocks owner writes cp fails.
229 			 */
230 			if (mkdir(to.p_path, from_stat.st_mode|S_IRWXU) < 0) {
231 				error(to.p_path);
232 				return;
233 			}
234 		}
235 		else if (type(to_stat) != S_IFDIR) {
236 			(void)fprintf(stderr, "%s: %s: not a directory.\n",
237 			    pname, to.p_path);
238 			return;
239 		}
240 		copy_dir();
241 		/*
242 		 * If not -p and directory didn't exist, set it to be the
243 		 * same as the from directory, umodified by the umask;
244 		 * arguably wrong, but it's been that way forever.
245 		 */
246 		if (preserve_flag)
247 			setfile(&from_stat, 0);
248 		else if (dne)
249 			(void)chmod(to.p_path, from_stat.st_mode);
250 		break;
251 	case S_IFCHR:
252 	case S_IFBLK:
253 		/*
254 		 * if recursive flag on, try and create the special device
255 		 * otherwise copy the contents.
256 		 */
257 		if (recursive_flag) {
258 			copy_special(&from_stat, !dne);
259 			if (preserve_flag)
260 				setfile(&from_stat, 0);
261 			return;
262 		}
263 		/* FALLTHROUGH */
264 	default:
265 		copy_file(&from_stat, dne);
266 	}
267 }
268 
269 copy_file(fs, dne)
270 	struct stat *fs;
271 	int dne;
272 {
273 	register int from_fd, to_fd, rcount, wcount;
274 	struct stat to_stat;
275 
276 	if ((from_fd = open(from.p_path, O_RDONLY, 0)) == -1) {
277 		error(from.p_path);
278 		return;
279 	}
280 
281 	/*
282 	 * If the file exists and we're interactive, verify with the user.
283 	 * If the file DNE, set the mode to be the from file, minus setuid
284 	 * bits, modified by the umask; arguably wrong, but it makes copying
285 	 * executables work right and it's been that way forever.  (The
286 	 * other choice is 666 or'ed with the execute bits on the from file
287 	 * modified by the umask.)
288 	 */
289 	if (!dne) {
290 		if (interactive_flag) {
291 			int checkch, ch;
292 
293 			(void)fprintf(stderr, "overwrite %s? ", to.p_path);
294 			checkch = ch = getchar();
295 			while (ch != '\n' && ch != EOF)
296 				ch = getchar();
297 			if (checkch != 'y') {
298 				(void)close(from_fd);
299 				return;
300 			}
301 		}
302 		to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0);
303 	} else
304 		to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC,
305 		    fs->st_mode & ~(S_ISUID|S_ISGID));
306 
307 	if (to_fd == -1) {
308 		error(to.p_path);
309 		(void)close(from_fd);
310 		return;
311 	}
312 
313 	while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
314 		wcount = write(to_fd, buf, rcount);
315 		if (rcount != wcount || wcount == -1) {
316 			error(to.p_path);
317 			break;
318 		}
319 	}
320 	if (rcount < 0)
321 		error(from.p_path);
322 	if (preserve_flag)
323 		setfile(fs, to_fd);
324 	/*
325 	 * If the source was setuid, set the bits on the copy if the copy
326 	 * was created and is owned by the same uid.  If the source was
327 	 * setgid, set the bits on the copy if the copy was created and is
328 	 * owned by the same gid and the user is a member of that group.
329 	 * If both setuid and setgid, lose both bits unless all the above
330 	 * conditions are met.
331 	 */
332 	else if (fs->st_mode & (S_ISUID|S_ISGID)) {
333 		if (fs->st_mode & S_ISUID && myuid != fs->st_uid)
334 			fs->st_mode &= ~(S_ISUID|S_ISGID);
335 		if (fs->st_mode & S_ISGID) {
336 			if (fstat(to_fd, &to_stat)) {
337 				error(to.p_path);
338 				fs->st_mode &= ~(S_ISUID|S_ISGID);
339 			}
340 			else if (fs->st_gid != to_stat.st_gid ||
341 			    !ismember(fs->st_gid))
342 				fs->st_mode &= ~(S_ISUID|S_ISGID);
343 		}
344 		if (fs->st_mode & (S_ISUID|S_ISGID) && fchmod(to_fd,
345 		    fs->st_mode & (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) &
346 		    ~myumask))
347 			error(to.p_path);
348 	}
349 	(void)close(from_fd);
350 	(void)close(to_fd);
351 }
352 
353 copy_dir()
354 {
355 	struct stat from_stat;
356 	struct direct *dp, **dir_list;
357 	register int dir_cnt, i;
358 	char *old_from, *old_to;
359 
360 	dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL);
361 	if (dir_cnt == -1) {
362 		(void)fprintf(stderr, "%s: can't read directory %s.\n",
363 		    pname, from.p_path);
364 		exit_val = 1;
365 	}
366 
367 	/*
368 	 * Instead of handling directory entries in the order they appear
369 	 * on disk, do non-directory files before directory files.
370 	 * There are two reasons to do directories last.  The first is
371 	 * efficiency.  Files tend to be in the same cylinder group as
372 	 * their parent, whereas directories tend not to be.  Copying files
373 	 * all at once reduces seeking.  Second, deeply nested tree's
374 	 * could use up all the file descriptors if we didn't close one
375 	 * directory before recursivly starting on the next.
376 	 */
377 	/* copy files */
378 	for (i = 0; i < dir_cnt; ++i) {
379 		dp = dir_list[i];
380 		if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
381 		    && (dp->d_name[1] == NULL || dp->d_name[1] == '.'))
382 			goto done;
383 		old_from = path_append(&from, dp->d_name, (int)dp->d_namlen);
384 		if (!old_from)
385 			goto done;
386 
387 		if (statfcn(from.p_path, &from_stat) < 0) {
388 			error(dp->d_name);
389 			path_restore(&from, old_from);
390 			goto done;
391 		}
392 		if (type(from_stat) == S_IFDIR) {
393 			path_restore(&from, old_from);
394 			continue;
395 		}
396 		old_to = path_append(&to, dp->d_name, (int)dp->d_namlen);
397 		if (old_to) {
398 			copy();
399 			path_restore(&to, old_to);
400 		}
401 		path_restore(&from, old_from);
402 done:		dir_list[i] = NULL;
403 		(void)free((char *)dp);
404 	}
405 
406 	/* copy directories */
407 	for (i = 0; i < dir_cnt; ++i) {
408 		dp = dir_list[i];
409 		if (!dp)
410 			continue;
411 		old_from = path_append(&from, dp->d_name, (int) dp->d_namlen);
412 		if (!old_from) {
413 			(void)free((char *)dp);
414 			continue;
415 		}
416 		old_to = path_append(&to, dp->d_name, (int) dp->d_namlen);
417 		if (!old_to) {
418 			(void)free((char *)dp);
419 			path_restore(&from, old_from);
420 			continue;
421 		}
422 		copy();
423 		free((char *)dp);
424 		path_restore(&from, old_from);
425 		path_restore(&to, old_to);
426 	}
427 	free((char *)dir_list);
428 }
429 
430 copy_link(exists)
431 	int exists;
432 {
433 	int len;
434 	char link[MAXPATHLEN];
435 
436 	if ((len = readlink(from.p_path, link, sizeof(link))) == -1) {
437 		error(from.p_path);
438 		return;
439 	}
440 	link[len] = '\0';
441 	if (exists && unlink(to.p_path)) {
442 		error(to.p_path);
443 		return;
444 	}
445 	if (symlink(link, to.p_path)) {
446 		error(link);
447 		return;
448 	}
449 }
450 
451 copy_special(from_stat, exists)
452 	struct stat *from_stat;
453 	int exists;
454 {
455 	if (exists && unlink(to.p_path)) {
456 		error(to.p_path);
457 		return;
458 	}
459 	if (mknod(to.p_path, from_stat->st_mode,  from_stat->st_rdev)) {
460 		error(to.p_path);
461 		return;
462 	}
463 }
464 
465 setfile(fs, fd)
466 	register struct stat *fs;
467 	int fd;
468 {
469 	static struct timeval tv[2];
470 
471 	tv[0].tv_sec = fs->st_atime;
472 	tv[1].tv_sec = fs->st_mtime;
473 	if (utimes(to.p_path, tv))
474 		error(to.p_path);
475 	/*
476 	 * Changing the ownership probably won't succeed, unless we're root
477 	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid before setting the
478 	 * mode; current BSD behavior is to remove all setuid bits on chown.
479 	 * If setuid, lose the bits if chown fails.
480 	 * If setgid, lose the bits if chgrp fails.
481 	 * If both, lose the bits if either fails.
482 	 */
483 	if ((fd ?
484 	    fchown(fd, fs->st_uid, -1) : chown(to.p_path, fs->st_uid, -1))) {
485 		if (errno != EPERM)
486 			error(to.p_path);
487 		if (fs->st_mode & S_ISUID)
488 			fs->st_mode &= ~(S_ISUID|S_ISGID);
489 	}
490 	if ((fd ?
491 	    fchown(fd, -1, fs->st_gid) : chown(to.p_path, -1, fs->st_gid))) {
492 		if (errno != EPERM)
493 			error(to.p_path);
494 		if (fs->st_mode & S_ISGID)
495 			fs->st_mode &= ~(S_ISUID|S_ISGID);
496 	}
497 	fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
498 	if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode))
499 		error(to.p_path);
500 }
501 
502 ismember(gid)
503 	gid_t gid;
504 {
505 	register int cnt;
506 	static int ngroups, groups[NGROUPS];
507 
508 	if (!ngroups) {
509 		ngroups = getgroups(NGROUPS, groups);
510 		if (ngroups == -1) {
511 			ngroups = 0;
512 			exit_val = 1;
513 			(void)fprintf(stderr, "%s: %s\n",
514 			    pname, strerror(errno));
515 			return(0);
516 		}
517 	}
518 	for (cnt = ngroups; cnt--;)
519 		if (gid == groups[cnt])
520 			return(1);
521 	return(0);
522 }
523 
524 error(s)
525 	char *s;
526 {
527 	exit_val = 1;
528 	(void)fprintf(stderr, "%s: %s: %s\n", pname, s, strerror(errno));
529 }
530 
531 /********************************************************************
532  * Path Manipulation Routines.
533  ********************************************************************/
534 
535 /*
536  * These functions manipulate paths in PATH_T structures.
537  *
538  * They eliminate multiple slashes in paths when they notice them, and keep
539  * the path non-slash terminated.
540  *
541  * Both path_set() and path_append() return 0 if the requested name
542  * would be too long.
543  */
544 
545 #define	STRIP_TRAILING_SLASH(p) { \
546 	while ((p)->p_end > (p)->p_path && (p)->p_end[-1] == '/') \
547 		*--(p)->p_end = 0; \
548 	}
549 
550 /*
551  * Move specified string into path.  Convert "" to "." to handle BSD
552  * semantics for a null path.  Strip trailing slashes.
553  */
554 path_set(p, string)
555 	register PATH_T *p;
556 	char *string;
557 {
558 	if (strlen(string) > MAXPATHLEN) {
559 		(void)fprintf(stderr, "%s: %s: name too long.\n",
560 		    pname, string);
561 		exit_val = 1;
562 		return(0);
563 	}
564 
565 	(void)strcpy(p->p_path, string);
566 	p->p_end = p->p_path + strlen(p->p_path);
567 
568 	if (p->p_path == p->p_end) {
569 		*p->p_end++ = '.';
570 		*p->p_end = 0;
571 	}
572 
573 	STRIP_TRAILING_SLASH(p);
574 	return(1);
575 }
576 
577 /*
578  * Append specified string to path, inserting '/' if necessary.  Return a
579  * pointer to the old end of path for restoration.
580  */
581 char *
582 path_append(p, name, len)
583 	register PATH_T *p;
584 	char *name;
585 	int len;
586 {
587 	char *old;
588 
589 	old = p->p_end;
590 	if (len == -1)
591 		len = strlen(name);
592 
593 	/*
594 	 * The final "+ 1" accounts for the '/' between old path and name.
595 	 */
596 	if ((len + p->p_end - p->p_path + 1) > MAXPATHLEN) {
597 		(void)fprintf(stderr,
598 		    "%s: %s/%s: name too long.\n", pname, p->p_path, name);
599 		exit_val = 1;
600 		return(0);
601 	}
602 
603 	/*
604 	 * This code should always be executed, since paths shouldn't
605 	 * end in '/'.
606 	 */
607 	if (p->p_end[-1] != '/') {
608 		*p->p_end++ = '/';
609 		*p->p_end = 0;
610 	}
611 
612 	(void)strncat(p->p_end, name, len);
613 	p->p_end += len;
614 	*p->p_end = 0;
615 
616 	STRIP_TRAILING_SLASH(p);
617 	return(old);
618 }
619 
620 /*
621  * Restore path to previous value.  (As returned by path_append.)
622  */
623 path_restore(p, old)
624 	PATH_T *p;
625 	char *old;
626 {
627 	p->p_end = old;
628 	*p->p_end = 0;
629 }
630 
631 /*
632  * Return basename of path.  (Like basename(1).)
633  */
634 char *
635 path_basename(p)
636 	PATH_T *p;
637 {
638 	char *basename;
639 
640 	basename = rindex(p->p_path, '/');
641 	return(basename ? ++basename : p->p_path);
642 }
643 
644 usage()
645 {
646 	(void)fprintf(stderr,
647 "usage: cp [-fhipr] src target;\n   or: cp [-fhipr] src1 ... srcN directory\n");
648 	exit(1);
649 }
650