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