BSD 4_3_Net_2 release
[unix-history] / usr / src / bin / cp / cp.c
... / ...
CommitLineData
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, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38char copyright[] =
39"@(#) Copyright (c) 1988 The Regents of the University of California.\n\
40 All rights reserved.\n";
41#endif /* not lint */
42
43#ifndef lint
44static char sccsid[] = "@(#)cp.c 5.24 (Berkeley) 5/6/91";
45#endif /* not lint */
46
47/*
48 * cp copies source files to target files.
49 *
50 * The global PATH_T structures "to" and "from" always contain paths to the
51 * current source and target files, respectively. Since cp does not change
52 * directories, these paths can be either absolute or dot-realative.
53 *
54 * The basic algorithm is to initialize "to" and "from", and then call the
55 * recursive copy() function to do the actual work. If "from" is a file,
56 * copy copies the data. If "from" is a directory, copy creates the
57 * corresponding "to" directory, and calls itself recursively on all of
58 * the entries in the "from" directory.
59 */
60
61#include <sys/param.h>
62#include <sys/stat.h>
63#include <sys/time.h>
64#include <dirent.h>
65#include <fcntl.h>
66#include <errno.h>
67#include <unistd.h>
68#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include "cp.h"
72
73PATH_T from = { from.p_path, "" };
74PATH_T to = { to.p_path, "" };
75
76uid_t myuid;
77int exit_val, myumask;
78int iflag, pflag, orflag, rflag;
79int (*statfcn)();
80char *buf, *progname;
81
82main(argc, argv)
83 int argc;
84 char **argv;
85{
86 extern int optind;
87 struct stat to_stat;
88 register int c, r;
89 int symfollow, lstat(), stat();
90 char *old_to, *p;
91
92 /*
93 * The utility cp(1) is used by mv(1) -- except for usage statements,
94 * print the "called as" program name.
95 */
96 progname = (p = rindex(*argv,'/')) ? ++p : *argv;
97
98 symfollow = 0;
99 while ((c = getopt(argc, argv, "Rfhipr")) != EOF) {
100 switch ((char)c) {
101 case 'f':
102 iflag = 0;
103 break;
104 case 'h':
105 symfollow = 1;
106 break;
107 case 'i':
108 iflag = isatty(fileno(stdin));
109 break;
110 case 'p':
111 pflag = 1;
112 break;
113 case 'R':
114 rflag = 1;
115 break;
116 case 'r':
117 orflag = 1;
118 break;
119 case '?':
120 default:
121 usage();
122 break;
123 }
124 }
125 argc -= optind;
126 argv += optind;
127
128 if (argc < 2)
129 usage();
130
131 if (rflag && orflag) {
132 (void)fprintf(stderr,
133 "cp: the -R and -r options are mutually exclusive.\n");
134 exit(1);
135 }
136
137 buf = (char *)malloc(MAXBSIZE);
138 if (!buf) {
139 (void)fprintf(stderr, "%s: out of space.\n", progname);
140 exit(1);
141 }
142
143 myuid = getuid();
144
145 /* copy the umask for explicit mode setting */
146 myumask = umask(0);
147 (void)umask(myumask);
148
149 /* consume last argument first. */
150 if (!path_set(&to, argv[--argc]))
151 exit(1);
152
153 statfcn = symfollow || !rflag ? stat : lstat;
154
155 /*
156 * Cp has two distinct cases:
157 *
158 * % cp [-rip] source target
159 * % cp [-rip] source1 ... directory
160 *
161 * In both cases, source can be either a file or a directory.
162 *
163 * In (1), the target becomes a copy of the source. That is, if the
164 * source is a file, the target will be a file, and likewise for
165 * directories.
166 *
167 * In (2), the real target is not directory, but "directory/source".
168 */
169
170 r = stat(to.p_path, &to_stat);
171 if (r == -1 && errno != ENOENT) {
172 error(to.p_path);
173 exit(1);
174 }
175 if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
176 /*
177 * Case (1). Target is not a directory.
178 */
179 if (argc > 1) {
180 usage();
181 exit(1);
182 }
183 if (!path_set(&from, *argv))
184 exit(1);
185 copy();
186 }
187 else {
188 /*
189 * Case (2). Target is a directory.
190 */
191 for (;; ++argv) {
192 if (!path_set(&from, *argv)) {
193 exit_val = 1;
194 continue;
195 }
196 old_to = path_append(&to, path_basename(&from), -1);
197 if (!old_to) {
198 exit_val = 1;
199 continue;
200 }
201 copy();
202 if (!--argc)
203 break;
204 path_restore(&to, old_to);
205 }
206 }
207 exit(exit_val);
208}
209
210/* copy file or directory at "from" to "to". */
211copy()
212{
213 struct stat from_stat, to_stat;
214 int dne, statval;
215
216 statval = statfcn(from.p_path, &from_stat);
217 if (statval == -1) {
218 error(from.p_path);
219 return;
220 }
221
222 /* not an error, but need to remember it happened */
223 if (stat(to.p_path, &to_stat) == -1)
224 dne = 1;
225 else {
226 if (to_stat.st_dev == from_stat.st_dev &&
227 to_stat.st_ino == from_stat.st_ino) {
228 (void)fprintf(stderr,
229 "%s: %s and %s are identical (not copied).\n",
230 progname, to.p_path, from.p_path);
231 exit_val = 1;
232 return;
233 }
234 dne = 0;
235 }
236
237 switch(from_stat.st_mode & S_IFMT) {
238 case S_IFLNK:
239 copy_link(!dne);
240 return;
241 case S_IFDIR:
242 if (!rflag && !orflag) {
243 (void)fprintf(stderr,
244 "%s: %s is a directory (not copied).\n",
245 progname, from.p_path);
246 exit_val = 1;
247 return;
248 }
249 if (dne) {
250 /*
251 * If the directory doesn't exist, create the new
252 * one with the from file mode plus owner RWX bits,
253 * modified by the umask. Trade-off between being
254 * able to write the directory (if from directory is
255 * 555) and not causing a permissions race. If the
256 * umask blocks owner writes cp fails.
257 */
258 if (mkdir(to.p_path, from_stat.st_mode|S_IRWXU) < 0) {
259 error(to.p_path);
260 return;
261 }
262 }
263 else if (!S_ISDIR(to_stat.st_mode) != S_IFDIR) {
264 (void)fprintf(stderr, "%s: %s: not a directory.\n",
265 progname, to.p_path);
266 return;
267 }
268 copy_dir();
269 /*
270 * If not -p and directory didn't exist, set it to be the
271 * same as the from directory, umodified by the umask;
272 * arguably wrong, but it's been that way forever.
273 */
274 if (pflag)
275 setfile(&from_stat, 0);
276 else if (dne)
277 (void)chmod(to.p_path, from_stat.st_mode);
278 return;
279 case S_IFCHR:
280 case S_IFBLK:
281 if (rflag) {
282 copy_special(&from_stat, !dne);
283 return;
284 }
285 break;
286 case S_IFIFO:
287 if (rflag) {
288 copy_fifo(&from_stat, !dne);
289 return;
290 }
291 break;
292 }
293 copy_file(&from_stat, dne);
294}
295
296copy_file(fs, dne)
297 struct stat *fs;
298 int dne;
299{
300 register int from_fd, to_fd, rcount, wcount;
301 struct stat to_stat;
302
303 if ((from_fd = open(from.p_path, O_RDONLY, 0)) == -1) {
304 error(from.p_path);
305 return;
306 }
307
308 /*
309 * If the file exists and we're interactive, verify with the user.
310 * If the file DNE, set the mode to be the from file, minus setuid
311 * bits, modified by the umask; arguably wrong, but it makes copying
312 * executables work right and it's been that way forever. (The
313 * other choice is 666 or'ed with the execute bits on the from file
314 * modified by the umask.)
315 */
316 if (!dne) {
317 if (iflag) {
318 int checkch, ch;
319
320 (void)fprintf(stderr, "overwrite %s? ", to.p_path);
321 checkch = ch = getchar();
322 while (ch != '\n' && ch != EOF)
323 ch = getchar();
324 if (checkch != 'y') {
325 (void)close(from_fd);
326 return;
327 }
328 }
329 to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0);
330 } else
331 to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC,
332 fs->st_mode & ~(S_ISUID|S_ISGID));
333
334 if (to_fd == -1) {
335 error(to.p_path);
336 (void)close(from_fd);
337 return;
338 }
339
340 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
341 wcount = write(to_fd, buf, rcount);
342 if (rcount != wcount || wcount == -1) {
343 error(to.p_path);
344 break;
345 }
346 }
347 if (rcount < 0)
348 error(from.p_path);
349 if (pflag)
350 setfile(fs, to_fd);
351 /*
352 * If the source was setuid or setgid, lose the bits unless the
353 * copy is owned by the same user and group.
354 */
355 else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid)
356 if (fstat(to_fd, &to_stat))
357 error(to.p_path);
358#define RETAINBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
359 else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd,
360 fs->st_mode & RETAINBITS & ~myumask))
361 error(to.p_path);
362 (void)close(from_fd);
363 if (close(to_fd))
364 error(to.p_path);
365}
366
367copy_dir()
368{
369 struct stat from_stat;
370 struct dirent *dp, **dir_list;
371 register int dir_cnt, i;
372 char *old_from, *old_to;
373
374 dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL);
375 if (dir_cnt == -1) {
376 (void)fprintf(stderr, "%s: can't read directory %s.\n",
377 progname, from.p_path);
378 exit_val = 1;
379 }
380
381 /*
382 * Instead of handling directory entries in the order they appear
383 * on disk, do non-directory files before directory files.
384 * There are two reasons to do directories last. The first is
385 * efficiency. Files tend to be in the same cylinder group as
386 * their parent, whereas directories tend not to be. Copying files
387 * all at once reduces seeking. Second, deeply nested tree's
388 * could use up all the file descriptors if we didn't close one
389 * directory before recursivly starting on the next.
390 */
391 /* copy files */
392 for (i = 0; i < dir_cnt; ++i) {
393 dp = dir_list[i];
394 if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
395 && (dp->d_name[1] == NULL || dp->d_name[1] == '.'))
396 goto done;
397 old_from = path_append(&from, dp->d_name, (int)dp->d_namlen);
398 if (!old_from) {
399 exit_val = 1;
400 goto done;
401 }
402
403 if (statfcn(from.p_path, &from_stat) < 0) {
404 error(dp->d_name);
405 path_restore(&from, old_from);
406 goto done;
407 }
408 if (S_ISDIR(from_stat.st_mode)) {
409 path_restore(&from, old_from);
410 continue;
411 }
412 old_to = path_append(&to, dp->d_name, (int)dp->d_namlen);
413 if (old_to) {
414 copy();
415 path_restore(&to, old_to);
416 } else
417 exit_val = 1;
418 path_restore(&from, old_from);
419done: dir_list[i] = NULL;
420 (void)free((void *)dp);
421 }
422
423 /* copy directories */
424 for (i = 0; i < dir_cnt; ++i) {
425 dp = dir_list[i];
426 if (!dp)
427 continue;
428 old_from = path_append(&from, dp->d_name, (int) dp->d_namlen);
429 if (!old_from) {
430 exit_val = 1;
431 (void)free((void *)dp);
432 continue;
433 }
434 old_to = path_append(&to, dp->d_name, (int) dp->d_namlen);
435 if (!old_to) {
436 exit_val = 1;
437 (void)free((void *)dp);
438 path_restore(&from, old_from);
439 continue;
440 }
441 copy();
442 free((void *)dp);
443 path_restore(&from, old_from);
444 path_restore(&to, old_to);
445 }
446 free((void *)dir_list);
447}
448
449copy_link(exists)
450 int exists;
451{
452 int len;
453 char link[MAXPATHLEN];
454
455 if ((len = readlink(from.p_path, link, sizeof(link))) == -1) {
456 error(from.p_path);
457 return;
458 }
459 link[len] = '\0';
460 if (exists && unlink(to.p_path)) {
461 error(to.p_path);
462 return;
463 }
464 if (symlink(link, to.p_path)) {
465 error(link);
466 return;
467 }
468}
469
470copy_fifo(from_stat, exists)
471 struct stat *from_stat;
472 int exists;
473{
474 if (exists && unlink(to.p_path)) {
475 error(to.p_path);
476 return;
477 }
478 if (mkfifo(to.p_path, from_stat->st_mode)) {
479 error(to.p_path);
480 return;
481 }
482 if (pflag)
483 setfile(from_stat, 0);
484}
485
486copy_special(from_stat, exists)
487 struct stat *from_stat;
488 int exists;
489{
490 if (exists && unlink(to.p_path)) {
491 error(to.p_path);
492 return;
493 }
494 if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
495 error(to.p_path);
496 return;
497 }
498 if (pflag)
499 setfile(from_stat, 0);
500}
501
502setfile(fs, fd)
503 register struct stat *fs;
504 int fd;
505{
506 static struct timeval tv[2];
507 char path[100];
508
509 fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
510
511 tv[0].tv_sec = fs->st_atime;
512 tv[1].tv_sec = fs->st_mtime;
513 if (utimes(to.p_path, tv)) {
514 (void)snprintf(path, sizeof(path), "utimes: %s", to.p_path);
515 error(path);
516 }
517 /*
518 * Changing the ownership probably won't succeed, unless we're root
519 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
520 * the mode; current BSD behavior is to remove all setuid bits on
521 * chown. If chown fails, lose setuid/setgid bits.
522 */
523 if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
524 chown(to.p_path, fs->st_uid, fs->st_gid)) {
525 if (errno != EPERM) {
526 (void)snprintf(path, sizeof(path),
527 "chown: %s", to.p_path);
528 error(path);
529 }
530 fs->st_mode &= ~(S_ISUID|S_ISGID);
531 }
532 if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
533 (void)snprintf(path, sizeof(path), "chown: %s", to.p_path);
534 error(path);
535 }
536}
537
538error(s)
539 char *s;
540{
541 exit_val = 1;
542 (void)fprintf(stderr, "%s: %s: %s\n", progname, s, strerror(errno));
543}
544
545usage()
546{
547 (void)fprintf(stderr,
548"usage: cp [-Rfhip] src target;\n or: cp [-Rfhip] src1 ... srcN directory\n");
549 exit(1);
550}