xref: /original-bsd/bin/cp/cp.c (revision c73c5200)
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  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by the University of California, Berkeley.  The name of the
14  * University may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  */
20 
21 #ifndef lint
22 char copyright[] =
23 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
24  All rights reserved.\n";
25 #endif /* not lint */
26 
27 #ifndef lint
28 static char sccsid[] = "@(#)cp.c	5.5 (Berkeley) 05/19/89";
29 #endif /* not lint */
30 
31 /*
32  * cp copies source files to target files.
33  *
34  * The global path_t structures "to" and "from" always contain paths to the
35  * current source and target files, respectively.  Since cp does not change
36  * directories, these paths can be either absolute or dot-realative.
37  *
38  * The basic algorithm is to initialize "to" and "from", and then call the
39  * recursive copy() function to do the actual work.  If "from" is a file,
40  * copy copies the data.  If "from" is a directory, copy creates the
41  * corresponding "to" directory, and calls itself recursively on all of
42  * the entries in the "from" directory.
43  *
44  * Instead of handling directory entries in the order they appear on disk,
45  * copy() does non-directory files before directory files.
46  *
47  * There are two reasons to do directories last.  The first is efficiency.
48  * Files tend to be in the same cylinder group as their parent, whereas
49  * directories tend not to be. Copying files all at once reduces seeking.
50  *
51  * Second, deeply nested tree's could use up all the file descriptors if we
52  * didn't close one directory before recursivly starting on the next.
53  */
54 
55 #include <sys/param.h>
56 #include <sys/stat.h>
57 #include <sys/file.h>
58 #include <sys/dir.h>
59 #include <sys/time.h>
60 
61 #include <stdio.h>
62 #include <errno.h>
63 #include <strings.h>
64 
65 typedef struct {
66 	char	*p_path;	/* pointer to the start of a path. */
67 	char	*p_end;		/* pointer to NULL at end of path. */
68 } path_t;
69 
70 #define	type(st)	((st).st_mode&S_IFMT)
71 
72 char *path_append(), *path_basename();
73 void path_restore();
74 
75 int exit_val, symfollow, my_umask;
76 int interactive_flag, preserve_flag, recursive_flag;
77 char *buf;				/* I/O; malloc for best alignment. */
78 char from_buf[MAXPATHLEN + 1],		/* Source path buffer. */
79      to_buf[MAXPATHLEN + 1];		/* Target path buffer. */
80 path_t from = {from_buf, from_buf};
81 path_t to = {to_buf, to_buf};
82 
83 main(argc, argv)
84 	int argc;
85 	char **argv;
86 {
87 	extern int optind, errno;
88 	struct stat to_stat;
89 	register int c, r;
90 	int force_flag;
91 	char *old_to, *malloc();
92 
93 	force_flag = 0;
94 	while ((c = getopt(argc, argv, "Rfhipr")) != EOF) {
95 	switch ((char)c) {
96 		case 'f':
97 			force_flag = 1;
98 			break;
99 		case 'h':
100 			symfollow = 1;
101 			break;
102 		case 'i':
103 			interactive_flag = isatty(fileno(stdin));
104 			break;
105 		case 'p':
106 			preserve_flag = 1;
107 			(void)umask(0);
108 			break;
109 		case 'r':
110 		case 'R':
111 			recursive_flag = 1;
112 			break;
113 		case '?':
114 		default:
115 			usage();
116 			break;
117 		}
118 	}
119 	argc -= optind;
120 	argv += optind;
121 
122 	if (argc < 2)
123 		usage();
124 
125 	if (force_flag)
126 		interactive_flag = 0;
127 
128 	my_umask = umask(0);
129 	(void)umask(my_umask);
130 
131 	buf = (char *)malloc(MAXBSIZE);
132 	if (!buf) {
133 		(void)fprintf(stderr, "cp: out of space.\n");
134 		exit(1);
135 	}
136 
137 	/* Consume last argument first. */
138 	if (!path_set(&to, argv[--argc]))
139 		exit(exit_val);
140 
141 	/*
142 	 * Cp has two distinct cases:
143 	 *
144 	 * Case (1)	  $ cp [-rip] source target
145 	 *
146 	 * Case (2)	  $ cp [-rip] source1 ... directory
147 	 *
148 	 * In both cases, source can be either a file or a directory.
149 	 *
150 	 * In (1), the target becomes a copy of the source. That is, if the
151 	 * source is a file, the target will be a file, and likewise for
152 	 * directories.
153 	 *
154 	 * In (2), the real target is not directory, but "directory/source".
155 	 */
156 
157 	r = stat(to.p_path, &to_stat);
158 	if (r == -1 && errno != ENOENT) {
159 		error(to.p_path, "stat");
160 		exit(1);
161 	}
162 	if (r == -1 || type(to_stat) != S_IFDIR) {
163 		/*
164 		 * Case (1).  Target is not a directory.
165 		 */
166 		if (argc > 1) {
167 			usage();
168 			exit(1);
169 		}
170 		if (!path_set(&from, *argv))
171 			exit(exit_val);
172 		copy();
173 	}
174 	else {
175 		/*
176 		 * Case (2).  Target is a directory.
177 		 */
178 		for (; argc; --argc, ++argv) {
179 			if (!path_set(&from, *argv))
180 				continue;
181 			old_to = path_append(&to, path_basename(&from), -1);
182 			if (!old_to)
183 				continue;
184 			copy();
185 			path_restore(&to, old_to);
186 		}
187 	}
188 	exit(exit_val);
189 }
190 
191 /* copy file or directory at "from" to "to". */
192 copy()
193 {
194 	struct stat from_stat, to_stat;
195 	int statval;
196 
197 	statval = symfollow || !recursive_flag ?
198 	    stat(from.p_path, &from_stat) : lstat(from.p_path, &from_stat);
199 	if (statval == -1) {
200 		error(from.p_path, "stat");
201 		return;
202 	}
203 
204 	/* not an error, but need to remember it happened */
205 	if (stat(to.p_path, &to_stat) == -1)
206 		to_stat.st_ino = -1;
207 	else if (to_stat.st_dev == from_stat.st_dev &&
208 	    to_stat.st_ino == from_stat.st_ino) {
209 		(void)fprintf(stderr,
210 		    "cp: %s and %s are identical (not copied).\n",
211 		    to.p_path, from.p_path);
212 		exit_val = 1;
213 		return;
214 	}
215 
216 	switch(type(from_stat)) {
217 	case S_IFLNK:
218 		copy_link(to_stat.st_ino != -1);
219 		return;
220 	case S_IFDIR:
221 		if (!recursive_flag) {
222 			(void)fprintf(stderr,
223 			    "cp: %s is a directory (not copied).\n",
224 			    from.p_path);
225 			exit_val = 1;
226 			return;
227 		}
228 		if (to_stat.st_ino == -1) {
229 			if (mkdir(to.p_path, 0777) < 0) {
230 				error(to.p_path, "mkdir");
231 				return;
232 			}
233 			from_stat.st_mode &= ~my_umask;
234 		}
235 		else if (type(to_stat) != S_IFDIR) {
236 			(void)fprintf(stderr, "cp: %s: not a directory.\n",
237 			    to.p_path);
238 			return;
239 		}
240 		copy_dir();
241 		break;
242 	case S_IFCHR:
243 	case S_IFBLK:
244 		if (recursive_flag) {
245 			copy_special(&from_stat, &to_stat);
246 			if (preserve_flag)
247 				setfile(&from_stat);
248 			return;
249 		}
250 		/* FALLTHROUGH */
251 	default:
252 		if (!copy_file(from_stat.st_mode))
253 			return;
254 	}
255 	if (preserve_flag)
256 		setfile(&from_stat);
257 }
258 
259 copy_file(mode)
260 	u_short mode;			/* permissions for new file. */
261 {
262 	register int from_fd, to_fd, rcount, wcount;
263 
264 	from_fd = open(from.p_path, O_RDONLY, 0);
265 	if (from_fd == -1) {
266 		error(from.p_path, "open");
267 		(void)close(from_fd);
268 		return(0);
269 	}
270 
271 	/*
272 	 * In the interactive case, use O_EXCL to notice existing files. If
273 	 * the file exists, verify with the user.
274 	 */
275 	to_fd = open(to.p_path,
276 	    (interactive_flag ? O_EXCL : 0) | O_WRONLY | O_CREAT | O_TRUNC,
277 	    mode);
278 
279 	if (to_fd == -1 && errno == EEXIST && interactive_flag) {
280 		int checkch, ch;
281 
282 		(void)fprintf(stderr, "overwrite %s? ", to.p_path);
283 		checkch = ch = getchar();
284 		while (ch != '\n' && ch != EOF)
285 			ch = getchar();
286 		if (checkch != 'y')
287 			return(0);
288 		/* try again. */
289 		to_fd = open(to.p_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
290 	}
291 
292 	if (to_fd == -1) {
293 		error(to.p_path, "open");
294 		(void)close(from_fd);
295 		return(0);
296 	}
297 
298 	while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
299 		wcount = write(to_fd, buf, rcount);
300 		if (rcount != wcount || wcount == -1) {
301 			error(from.p_path, "write");
302 			break;
303 		}
304 	}
305 	(void)close(from_fd);
306 	(void)close(to_fd);
307 	return(1);
308 }
309 
310 copy_dir()
311 {
312 	struct stat from_stat;
313 	char *old_from, *old_to;
314 	struct direct *dp, **dir_list;
315 	int dir_cnt, i;
316 
317 	dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL);
318 	if (dir_cnt == -1) {
319 		(void)fprintf(stderr, "cp: can't read directory %s.\n",
320 		    from.p_path);
321 		exit_val = 1;
322 	}
323 
324 	/* Copy files first. */
325 	for (i = 0; i < dir_cnt; ++i) {
326 		dp = dir_list[i];
327 		if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
328 		    && (dp->d_name[1] == NULL || dp->d_name[1] == '.')) {
329 			(void)free((char *)dp);
330 			dir_list[i] = NULL;
331 			continue;
332 		}
333 		old_from = path_append(&from, dp->d_name, (int)dp->d_namlen);
334 		if (!old_from) {
335 			dir_list[i] = NULL;
336 			(void)free((char *)dp);
337 			continue;
338 		}
339 
340 		if (stat(from.p_path, &from_stat) < 0) {
341 			error(dp->d_name, "stat");
342 			path_restore(&from, old_from);
343 			continue;
344 		}
345 
346 		if (type(from_stat) != S_IFDIR) {
347 			old_to = path_append(&to, dp->d_name,
348 			    (int)dp->d_namlen);
349 			if (!old_to) {
350 				dir_list[i] = NULL;
351 				(void)free((char *)dp);
352 				continue;
353 			}
354 			copy();
355 			path_restore(&to, old_to);
356 			dir_list[i] = NULL;
357 			(void)free((char *)dp);
358 		}
359 		path_restore(&from, old_from);
360 	}
361 
362 	/* then copy directories. */
363 	for (i = 0; i < dir_cnt; ++i) {
364 		dp = dir_list[i];
365 		if (!dp)
366 			continue;
367 		old_from = path_append(&from, dp->d_name, (int) dp->d_namlen);
368 		if (!old_from) {
369 			(void)free((char *)dp);
370 			continue;
371 		}
372 		old_to = path_append(&to, dp->d_name, (int) dp->d_namlen);
373 		if (!old_to) {
374 			(void)free((char *)dp);
375 			path_restore(&from, old_from);
376 			continue;
377 		}
378 		copy();
379 		free((char *)dp);
380 		path_restore(&from, old_from);
381 		path_restore(&to, old_to);
382 	}
383 	free((char *)dir_list);
384 }
385 
386 copy_link(exists)
387 	int exists;
388 {
389 	char link[MAXPATHLEN];
390 
391 	if (readlink(from.p_path, link, sizeof(link)) == -1) {
392 		error(from.p_path, "readlink");
393 		return;
394 	}
395 	if (exists && unlink(to.p_path)) {
396 		error(to.p_path, "unlink");
397 		return;
398 	}
399 	if (symlink(link, to.p_path)) {
400 		error(link, "symlink");
401 		return;
402 	}
403 }
404 
405 copy_special(from_stat, to_stat)
406 	struct stat *from_stat, *to_stat;
407 {
408 	if (to_stat->st_ino != -1 && unlink(to.p_path)) {
409 		error(to.p_path, "unlink");
410 		return;
411 	}
412 	if (mknod(to.p_path, from_stat->st_mode,  from_stat->st_rdev)) {
413 		error(to.p_path, "mknod");
414 		return;
415 	}
416 }
417 
418 setfile(fs)
419 	struct stat *fs;
420 {
421 	static struct timeval tv[2];
422 
423 	if (chown(to.p_path, fs->st_uid, fs->st_gid))
424 		error(to.p_path, "chown");
425 	if (chmod(to.p_path, fs->st_mode))
426 		error(to.p_path, "chmod");
427 	tv[0].tv_sec = fs->st_atime;
428 	tv[1].tv_sec = fs->st_mtime;
429 	if (utimes(to.p_path, tv))
430 		error(to.p_path, "utimes");
431 }
432 
433 error(s, call)
434 	char *s, *call;
435 {
436 	extern int errno;
437 
438 	exit_val = 1;
439 	(void)fprintf(stderr, "cp: %s: %s: %s\n", call, s, strerror(errno));
440 }
441 
442 /********************************************************************
443  * Path Manipulation Routines.
444  ********************************************************************/
445 
446 /*
447  * These functions manipulate paths in "path_t" structures.
448  *
449  * They eliminate multiple slashes in paths when they notice them, and keep
450  * the path non-slash terminated.
451  *
452  * Both path_set() and path_append() return 0 if the requested name
453  * would be too long.
454  */
455 
456 #define	STRIP_TRAILING_SLASH(p) { \
457 	while ((p)->p_end > (p)->p_path && (p)->p_end[-1] == '/') \
458 		*--(p)->p_end = 0; \
459 	}
460 
461 /*
462  * Move specified string into path.  Convert "" to "." to handle BSD
463  * semantics for a null path.  Strip trailing slashes.
464  */
465 path_set(p, string)
466 	register path_t *p;
467 	char *string;
468 {
469 	if (strlen(string) > MAXPATHLEN) {
470 		fprintf(stderr, "cp: %s: name too long.\n", string);
471 		exit_val = 1;
472 		return(0);
473 	}
474 
475 	(void)strcpy(p->p_path, string);
476 	p->p_end = p->p_path + strlen(p->p_path);
477 
478 	if (p->p_path == p->p_end) {
479 		*p->p_end++ = '.';
480 		*p->p_end = 0;
481 	}
482 
483 	STRIP_TRAILING_SLASH(p);
484 	return(1);
485 }
486 
487 /*
488  * Append specified string to path, inserting '/' if necessary.  Return a
489  * pointer to the old end of path for restoration.
490  */
491 char *
492 path_append(p, name, len)
493 	register path_t *p;
494 	char *name;
495 	int len;
496 {
497 	char *old;
498 
499 	old = p->p_end;
500 	if (len == -1)
501 		len = strlen(name);
502 
503 	/*
504 	 * The final "+ 1" accounts for the '/' between old path and name.
505 	 */
506 	if ((len + p->p_end - p->p_path + 1) > MAXPATHLEN) {
507 		fprintf(stderr,
508 		    "cp: %s/%s: name too long.\n", p->p_path, name);
509 		exit_val = 1;
510 		return(0);
511 	}
512 
513 	/*
514 	 * This code should always be executed, since paths shouldn't
515 	 * end in '/'.
516 	 */
517 	if (p->p_end[-1] != '/') {
518 		*p->p_end++ = '/';
519 		*p->p_end = 0;
520 	}
521 
522 	(void)strncat(p->p_end, name, len);
523 	p->p_end += len;
524 	*p->p_end = 0;
525 
526 	STRIP_TRAILING_SLASH(p);
527 	return(old);
528 }
529 
530 /*
531  * Restore path to previous value.  (As returned by path_append.)
532  */
533 void
534 path_restore(p, old)
535 	path_t *p;
536 	char *old;
537 {
538 	p->p_end = old;
539 	*p->p_end = 0;
540 }
541 
542 /*
543  * Return basename of path.  (Like basename(1).)
544  */
545 char *
546 path_basename(p)
547 	path_t *p;
548 {
549 	char *basename;
550 
551 	basename = rindex(p->p_path, '/');
552 	if (!basename)
553 		basename = p->p_path;
554 	return(basename);
555 }
556 
557 usage()
558 {
559 	(void)fprintf(stderr,
560 	   "usage: cp [-ip] f1 f2; or: cp [-irp] f1 ... fn directory\n");
561 	exit(1);
562 }
563