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.23 (Berkeley) 04/03/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 (void)close(to_fd); 338 } 339 340 copy_dir() 341 { 342 struct stat from_stat; 343 struct dirent *dp, **dir_list; 344 register int dir_cnt, i; 345 char *old_from, *old_to; 346 347 dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL); 348 if (dir_cnt == -1) { 349 (void)fprintf(stderr, "%s: can't read directory %s.\n", 350 progname, from.p_path); 351 exit_val = 1; 352 } 353 354 /* 355 * Instead of handling directory entries in the order they appear 356 * on disk, do non-directory files before directory files. 357 * There are two reasons to do directories last. The first is 358 * efficiency. Files tend to be in the same cylinder group as 359 * their parent, whereas directories tend not to be. Copying files 360 * all at once reduces seeking. Second, deeply nested tree's 361 * could use up all the file descriptors if we didn't close one 362 * directory before recursivly starting on the next. 363 */ 364 /* copy files */ 365 for (i = 0; i < dir_cnt; ++i) { 366 dp = dir_list[i]; 367 if (dp->d_namlen <= 2 && dp->d_name[0] == '.' 368 && (dp->d_name[1] == NULL || dp->d_name[1] == '.')) 369 goto done; 370 old_from = path_append(&from, dp->d_name, (int)dp->d_namlen); 371 if (!old_from) { 372 exit_val = 1; 373 goto done; 374 } 375 376 if (statfcn(from.p_path, &from_stat) < 0) { 377 error(dp->d_name); 378 path_restore(&from, old_from); 379 goto done; 380 } 381 if (S_ISDIR(from_stat.st_mode)) { 382 path_restore(&from, old_from); 383 continue; 384 } 385 old_to = path_append(&to, dp->d_name, (int)dp->d_namlen); 386 if (old_to) { 387 copy(); 388 path_restore(&to, old_to); 389 } else 390 exit_val = 1; 391 path_restore(&from, old_from); 392 done: dir_list[i] = NULL; 393 (void)free((void *)dp); 394 } 395 396 /* copy directories */ 397 for (i = 0; i < dir_cnt; ++i) { 398 dp = dir_list[i]; 399 if (!dp) 400 continue; 401 old_from = path_append(&from, dp->d_name, (int) dp->d_namlen); 402 if (!old_from) { 403 exit_val = 1; 404 (void)free((void *)dp); 405 continue; 406 } 407 old_to = path_append(&to, dp->d_name, (int) dp->d_namlen); 408 if (!old_to) { 409 exit_val = 1; 410 (void)free((void *)dp); 411 path_restore(&from, old_from); 412 continue; 413 } 414 copy(); 415 free((void *)dp); 416 path_restore(&from, old_from); 417 path_restore(&to, old_to); 418 } 419 free((void *)dir_list); 420 } 421 422 copy_link(exists) 423 int exists; 424 { 425 int len; 426 char link[MAXPATHLEN]; 427 428 if ((len = readlink(from.p_path, link, sizeof(link))) == -1) { 429 error(from.p_path); 430 return; 431 } 432 link[len] = '\0'; 433 if (exists && unlink(to.p_path)) { 434 error(to.p_path); 435 return; 436 } 437 if (symlink(link, to.p_path)) { 438 error(link); 439 return; 440 } 441 } 442 443 copy_fifo(from_stat, exists) 444 struct stat *from_stat; 445 int exists; 446 { 447 if (exists && unlink(to.p_path)) { 448 error(to.p_path); 449 return; 450 } 451 if (mkfifo(to.p_path, from_stat->st_mode)) { 452 error(to.p_path); 453 return; 454 } 455 if (pflag) 456 setfile(from_stat, 0); 457 } 458 459 copy_special(from_stat, exists) 460 struct stat *from_stat; 461 int exists; 462 { 463 if (exists && unlink(to.p_path)) { 464 error(to.p_path); 465 return; 466 } 467 if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 468 error(to.p_path); 469 return; 470 } 471 if (pflag) 472 setfile(from_stat, 0); 473 } 474 475 setfile(fs, fd) 476 register struct stat *fs; 477 int fd; 478 { 479 static struct timeval tv[2]; 480 char path[100]; 481 482 fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 483 484 tv[0].tv_sec = fs->st_atime; 485 tv[1].tv_sec = fs->st_mtime; 486 if (utimes(to.p_path, tv)) { 487 (void)snprintf(path, sizeof(path), "utimes: %s", to.p_path); 488 error(path); 489 } 490 /* 491 * Changing the ownership probably won't succeed, unless we're root 492 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 493 * the mode; current BSD behavior is to remove all setuid bits on 494 * chown. If chown fails, lose setuid/setgid bits. 495 */ 496 if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : 497 chown(to.p_path, fs->st_uid, fs->st_gid)) { 498 if (errno != EPERM) { 499 (void)snprintf(path, sizeof(path), 500 "chown: %s", to.p_path); 501 error(path); 502 } 503 fs->st_mode &= ~(S_ISUID|S_ISGID); 504 } 505 if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { 506 (void)snprintf(path, sizeof(path), "chown: %s", to.p_path); 507 error(path); 508 } 509 } 510 511 error(s) 512 char *s; 513 { 514 exit_val = 1; 515 (void)fprintf(stderr, "%s: %s: %s\n", progname, s, strerror(errno)); 516 } 517 518 usage() 519 { 520 (void)fprintf(stderr, 521 "usage: cp [-Rfhip] src target;\n or: cp [-Rfhip] src1 ... srcN directory\n"); 522 exit(1); 523 } 524