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