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