xref: /original-bsd/bin/cp/cp.c (revision 612877e1)
1 /*
2  * Copyright (c) 1988, 1993, 1994
3  *	The Regents of the University of California.  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 static char copyright[] =
13 "@(#) Copyright (c) 1988, 1993, 1994\n\
14 	The Regents of the University of California.  All rights reserved.\n";
15 #endif /* not lint */
16 
17 #ifndef lint
18 static char sccsid[] = "@(#)cp.c	8.2 (Berkeley) 04/01/94";
19 #endif /* not lint */
20 
21 /*
22  * Cp copies source files to target files.
23  *
24  * The global PATH_T structure "to" always contains the path to the
25  * current target file.  Since fts(3) does not change directories,
26  * this path can be either absolute or dot-realative.
27  *
28  * The basic algorithm is to initialize "to" and use fts(3) to traverse
29  * the file hierarchy rooted in the argument list.  A trivial case is the
30  * case of 'cp file1 file2'.  The more interesting case is the case of
31  * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
32  * path (relative to the root of the traversal) is appended to dir (stored
33  * in "to") to form the final target path.
34  */
35 
36 #include <sys/param.h>
37 #include <sys/stat.h>
38 #include <sys/mman.h>
39 #include <sys/time.h>
40 
41 #include <dirent.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <fts.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 
51 #include "extern.h"
52 
53 #define	STRIP_TRAILING_SLASH(p) {					\
54         while ((p).p_end > (p).p_path && (p).p_end[-1] == '/')		\
55                 *--(p).p_end = 0;					\
56 }
57 
58 PATH_T to = { to.p_path, "" };
59 
60 uid_t myuid;
61 int Rflag, iflag, pflag, rflag;
62 int myumask;
63 
64 enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
65 
66 int copy __P((char *[], enum op, int));
67 int mastercmp __P((const FTSENT **, const FTSENT **));
68 
69 int
70 main(argc, argv)
71 	int argc;
72 	char *argv[];
73 {
74 	struct stat to_stat, tmp_stat;
75 	enum op type;
76 	int Hflag, Lflag, Pflag, ch, fts_options, r;
77 	char *target;
78 
79 	Hflag = Lflag = Pflag = Rflag = 0;
80 	while ((ch = getopt(argc, argv, "HLPRfipr")) != EOF)
81 		switch (ch) {
82 		case 'H':
83 			Hflag = 1;
84 			Lflag = Pflag = 0;
85 			break;
86 		case 'L':
87 			Lflag = 1;
88 			Hflag = Pflag = 0;
89 			break;
90 		case 'P':
91 			Pflag = 1;
92 			Hflag = Lflag = 0;
93 			break;
94 		case 'R':
95 			Rflag = 1;
96 			break;
97 		case 'f':
98 			iflag = 0;
99 			break;
100 		case 'i':
101 			iflag = isatty(fileno(stdin));
102 			break;
103 		case 'p':
104 			pflag = 1;
105 			break;
106 		case 'r':
107 			rflag = 1;
108 			break;
109 		case '?':
110 		default:
111 			usage();
112 			break;
113 		}
114 	argc -= optind;
115 	argv += optind;
116 
117 	if (argc < 2)
118 		usage();
119 
120 	fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
121 	if (rflag) {
122 		if (Rflag)
123 			errx(1,
124 		    "the -R and -r options may not be specified together.");
125 		if (Hflag || Lflag || Pflag)
126 			errx(1,
127 	"the -H, -L, and -P options may not be specified with the -r option.");
128 		fts_options &= ~FTS_PHYSICAL;
129 		fts_options |= FTS_LOGICAL;
130 	}
131 	if (Rflag) {
132 		if (Hflag)
133 			fts_options |= FTS_COMFOLLOW;
134 		if (Lflag) {
135 			fts_options &= ~FTS_PHYSICAL;
136 			fts_options |= FTS_LOGICAL;
137 		}
138 	} else {
139 		fts_options &= ~FTS_PHYSICAL;
140 		fts_options |= FTS_LOGICAL;
141 	}
142 
143 	myuid = getuid();
144 
145 	/* Copy the umask for explicit mode setting. */
146 	myumask = umask(0);
147 	(void)umask(myumask);
148 
149 	/* Save the target base in "to". */
150 	target = argv[--argc];
151 	if (strlen(target) > MAXPATHLEN)
152 		errx(1, "%s: name too long", target);
153 	(void)strcpy(to.p_path, target);
154 	to.p_end = to.p_path + strlen(to.p_path);
155         if (to.p_path == to.p_end) {
156 		*to.p_end++ = '.';
157 		*to.p_end = 0;
158 	}
159         STRIP_TRAILING_SLASH(to);
160 	to.target_end = to.p_end;
161 
162 	/* Set end of argument list for fts(3). */
163 	argv[argc] = NULL;
164 
165 	/*
166 	 * Cp has two distinct cases:
167 	 *
168 	 * cp [-R] source target
169 	 * cp [-R] source1 ... sourceN directory
170 	 *
171 	 * In both cases, source can be either a file or a directory.
172 	 *
173 	 * In (1), the target becomes a copy of the source. That is, if the
174 	 * source is a file, the target will be a file, and likewise for
175 	 * directories.
176 	 *
177 	 * In (2), the real target is not directory, but "directory/source".
178 	 */
179 	r = stat(to.p_path, &to_stat);
180 	if (r == -1 && errno != ENOENT)
181 		err(1, "%s", to.p_path);
182 	if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
183 		/*
184 		 * Case (1).  Target is not a directory.
185 		 */
186 		if (argc > 1) {
187 			usage();
188 			exit(1);
189 		}
190 		/*
191 		 * Need to detect the case:
192 		 *	cp -R dir foo
193 		 * Where dir is a directory and foo does not exist, where
194 		 * we want pathname concatenations turned on but not for
195 		 * the initial mkdir().
196 		 */
197 		if (r == -1) {
198 			if (rflag || (Rflag && (Lflag || Hflag)))
199 				stat(*argv, &tmp_stat);
200 			else
201 				lstat(*argv, &tmp_stat);
202 
203 			if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag))
204 				type = DIR_TO_DNE;
205 			else
206 				type = FILE_TO_FILE;
207 		} else
208 			type = FILE_TO_FILE;
209 	} else
210 		/*
211 		 * Case (2).  Target is a directory.
212 		 */
213 		type = FILE_TO_DIR;
214 
215 	exit (copy(argv, type, fts_options));
216 }
217 
218 int
219 copy(argv, type, fts_options)
220 	char *argv[];
221 	enum op type;
222 	int fts_options;
223 {
224 	struct stat to_stat;
225 	FTS *ftsp;
226 	FTSENT *curr;
227 	int base, dne, nlen, rval;
228 	char *p;
229 
230 	if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL)
231 		err(1, NULL);
232 	for (rval = 0; (curr = fts_read(ftsp)) != NULL;) {
233 		switch (curr->fts_info) {
234 		case FTS_NS:
235 		case FTS_ERR:
236 			warnx("%s: %s",
237 			    curr->fts_path, strerror(curr->fts_errno));
238 			rval = 1;
239 			continue;
240 		case FTS_DC:			/* Warn, continue. */
241 			warnx("%s: directory causes a cycle", curr->fts_path);
242 			rval = 1;
243 			continue;
244 		case FTS_DP:			/* Ignore, continue. */
245 			continue;
246 		}
247 
248 		/*
249 		 * If we are in case (2) or (3) above, we need to append the
250                  * source name to the target name.
251                  */
252 		if (type != FILE_TO_FILE) {
253 			if ((curr->fts_namelen +
254 			    to.target_end - to.p_path + 1) > MAXPATHLEN) {
255 				warnx("%s/%s: name too long (not copied)",
256 				    to.p_path, curr->fts_name);
257 				rval = 1;
258 				continue;
259 			}
260 
261 			/*
262 			 * Need to remember the roots of traversals to create
263 			 * correct pathnames.  If there's a directory being
264 			 * copied to a non-existent directory, e.g.
265 			 *	cp -R a/dir noexist
266 			 * the resulting path name should be noexist/foo, not
267 			 * noexist/dir/foo (where foo is a file in dir), which
268 			 * is the case where the target exists.
269 			 *
270 			 * Also, check for "..".  This is for correct path
271 			 * concatentation for paths ending in "..", e.g.
272 			 *	cp -R .. /tmp
273 			 * Paths ending in ".." are changed to ".".  This is
274 			 * tricky, but seems the easiest way to fix the problem.
275 			 *
276 			 * XXX
277 			 * Since the first level MUST be FTS_ROOTLEVEL, base
278 			 * is always initialized.
279 			 */
280 			if (curr->fts_level == FTS_ROOTLEVEL)
281 				if (type != DIR_TO_DNE) {
282 					p = strrchr(curr->fts_path, '/');
283 					base = (p == NULL) ? 0 :
284 					    (int)(p - curr->fts_path + 1);
285 
286 					if (!strcmp(&curr->fts_path[base],
287 					    ".."))
288 						base += 1;
289 				} else
290 					base = curr->fts_pathlen;
291 
292 			if (to.target_end[-1] != '/') {
293 				*to.target_end = '/';
294 				*(to.target_end + 1) = 0;
295 			}
296 			p = &curr->fts_path[base];
297 			nlen = curr->fts_pathlen - base;
298 
299 			(void)strncat(to.target_end + 1, p, nlen);
300 			to.p_end = to.target_end + nlen + 1;
301 			*to.p_end = 0;
302 			STRIP_TRAILING_SLASH(to);
303 		}
304 
305 		/* Not an error but need to remember it happened */
306 		if (stat(to.p_path, &to_stat) == -1)
307 			dne = 1;
308 		else {
309 			if (to_stat.st_dev == curr->fts_statp->st_dev &&
310 			    to_stat.st_ino == curr->fts_statp->st_ino) {
311 				warnx("%s and %s are identical (not copied).",
312 				    to.p_path, curr->fts_path);
313 				rval = 1;
314 				if (S_ISDIR(curr->fts_statp->st_mode))
315 					(void)fts_set(ftsp, curr, FTS_SKIP);
316 				continue;
317 			}
318 			dne = 0;
319 		}
320 
321 		switch (curr->fts_statp->st_mode & S_IFMT) {
322 		case S_IFLNK:
323 			if (copy_link(curr, !dne))
324 				rval = 1;
325 			break;
326 		case S_IFDIR:
327 			if (!Rflag && !rflag) {
328 				warnx("%s is a directory (not copied).",
329 				    curr->fts_path);
330 				(void)fts_set(ftsp, curr, FTS_SKIP);
331 				rval = 1;
332 				break;
333 			}
334 			/*
335 			 * If the directory doesn't exist, create the new
336 			 * one with the from file mode plus owner RWX bits,
337 			 * modified by the umask.  Trade-off between being
338 			 * able to write the directory (if from directory is
339 			 * 555) and not causing a permissions race.  If the
340 			 * umask blocks owner writes, we fail..
341 			 */
342 			if (dne) {
343 				if (mkdir(to.p_path,
344 				    curr->fts_statp->st_mode | S_IRWXU) < 0)
345 					err(1, "%s", to.p_path);
346 			} else if (!S_ISDIR(to_stat.st_mode)) {
347 				errno = ENOTDIR;
348 				err(1, "%s: %s", to.p_path);
349 			}
350 			/*
351 			 * If not -p and directory didn't exist, set it to be
352 			 * the same as the from directory, umodified by the
353                          * umask; arguably wrong, but it's been that way
354                          * forever.
355 			 */
356 			if (pflag && setfile(curr->fts_statp, 0))
357 				rval = 1;
358 			else if (dne)
359 				(void)chmod(to.p_path,
360 				    curr->fts_statp->st_mode);
361 			break;
362 		case S_IFBLK:
363 		case S_IFCHR:
364 			if (Rflag) {
365 				if (copy_special(curr->fts_statp, !dne))
366 					rval = 1;
367 			} else
368 				if (copy_file(curr, dne))
369 					rval = 1;
370 			break;
371 		case S_IFIFO:
372 			if (Rflag)
373 				if (copy_fifo(curr->fts_statp, !dne))
374 					rval = 1;
375 			else
376 				if (copy_file(curr, dne))
377 					rval = 1;
378 			break;
379 		default:
380 			if (copy_file(curr, dne))
381 				rval = 1;
382 			break;
383 		}
384 	}
385 	if (errno)
386 		err(1, "fts_read");
387 	return (rval);
388 }
389 
390 /*
391  * mastercmp --
392  *	The comparison function for the copy order.  The order is to copy
393  *	non-directory files before directory files.  The reason for this
394  *	is because files tend to be in the same cylinder group as their
395  *	parent directory, whereas directories tend not to be.  Copying the
396  *	files first reduces seeking.
397  */
398 int
399 mastercmp(a, b)
400 	const FTSENT **a, **b;
401 {
402 	int a_info, b_info;
403 
404 	a_info = (*a)->fts_info;
405 	if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR)
406 		return (0);
407 	b_info = (*b)->fts_info;
408 	if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR)
409 		return (0);
410 	if (a_info == FTS_D)
411 		return (-1);
412 	if (b_info == FTS_D)
413 		return (1);
414 	return (0);
415 }
416