1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2012 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Eclipse Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.eclipse.org/org/documents/epl-v10.html * 11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * * 20 ***********************************************************************/ 21 #pragma prototyped 22 /* 23 * Glenn Fowler 24 * AT&T Research 25 * 26 * cp/ln/mv -- copy/link/move files 27 */ 28 29 static const char usage_head[] = 30 "[-?@(#)$Id: cp (AT&T Research) 2012-04-20 $\n]" 31 USAGE_LICENSE 32 ; 33 34 static const char usage_cp[] = 35 "[+NAME?cp - copy files]" 36 "[+DESCRIPTION?If the last argument names an existing directory, \bcp\b " 37 "copies each \afile\a into a file with the same name in that directory. " 38 "Otherwise, if only two files are given, \bcp\b copies the first onto " 39 "the second. It is an error if the last argument is not a directory and " 40 "more than two files are given. By default directories are not copied.]" 41 42 "[a:archive?Preserve as much as possible of the structure and attributes " 43 "of the original files in the copy. Equivalent to \b--physical\b " 44 "\b--preserve\b \b--recursive\b.]" 45 "[A:attributes?Preserve selected file attributes:]:[eipt]" 46 "{" 47 "[+e?Everything permissible.]" 48 "[+i?Owner uid and gid.]" 49 "[+p?Permissions.]" 50 "[+t?Access and modify times.]" 51 "}" 52 "[p:preserve?Preserve file owner, group, permissions and timestamps.]" 53 "[h:hierarchy|parents?Form the name of each destination file by " 54 "appending to the target directory a slash and the specified source file " 55 "name. The last argument must be an existing directory. Missing " 56 "destination directories are created.]" 57 "[H:metaphysical?Follow command argument symbolic links, otherwise don't " 58 "follow.]" 59 "[l:link?Make hard links to destination files instead of copies.]" 60 "[U:remove-destination?Remove existing destination files before copying.]" 61 "[L:logical|dereference?Follow symbolic links and copy the files they " 62 "point to.]" 63 "[P|d:physical|nodereference?Don't follow symbolic links; copy symbolic " 64 "rather than the files they point to.]" 65 ; 66 67 static const char usage_ln[] = 68 "[+NAME?ln - link files]" 69 "[+DESCRIPTION?If the last argument names an existing directory, \bln\b " 70 "links each \afile\a into a file with the same name in that directory. " 71 "Otherwise, if only two files are given, \bln\b links the first onto the " 72 "second. It is an error if the last argument is not a directory and more " 73 "than two files are given. By default directories are not linked.]" 74 ; 75 76 static const char usage_mv[] = 77 "[+NAME?mv - rename files]" 78 "[+DESCRIPTION?If the last argument names an existing directory, \bmv\b " 79 "renames each \afile\a into a file with the same name in that directory. " 80 "Otherwise, if only two files are given, \bmv\b renames the first onto " 81 "the second. It is an error if the last argument is not a directory and " 82 "more than two files are given. If a source and destination file reside " 83 "on different filesystems then \bmv\b copies the file contents to the " 84 "destination and then deletes the source file.]" 85 86 "[U:remove-destination?Remove existing destination files before moving.]" 87 ; 88 89 static const char usage_tail[] = 90 "[f:force?Replace existing destination files.]" 91 "[i:interactive|prompt?Prompt whether to replace existing destination " 92 "files. An affirmative response (\by\b or \bY\b) replaces the file, a " 93 "quit response (\bq\b or \bQ\b) exits immediately, and all other " 94 "responses skip the file.]" 95 "[r|R:recursive?Operate on the contents of directories recursively.]" 96 "[s:symlink|symbolic-link?Make symbolic links to destination files.]" 97 "[u:update?Replace a destination file only if its modification time is " 98 "older than the corresponding source file modification time.]" 99 "[v:verbose?Print the name of each file before operating on it.]" 100 "[F:fsync|sync?\bfsync\b(2) each file after it is copied.]" 101 "[B:backup?Make backups of files that are about to be replaced. " 102 "\b--suffix\b sets the backup suffix. The backup type is determined in " 103 "this order: this option, the \bVERSION_CONTROL\b environment variable, " 104 "or the default value \bexisting\b. \atype\a may be one of:]:?[type]" 105 "{" 106 "[+numbered|t?Always make numbered backups. The numbered backup " 107 "suffix is \b.\aSNS\a, where \aS\a is the \bbackup-suffix\b and " 108 "\aN\a is the version number, starting at 1, incremented with " 109 "each version.]" 110 "[+existing|nil?Make numbered backups of files that already have " 111 "them, otherwise simple backups.]" 112 "[+simple|never?Always make simple backups.]" 113 "[+none|off?Disable backups.]" 114 "}" 115 "[S:suffix?A backup file is made by renaming the file to the same name " 116 "with the backup suffix appended. The backup suffix is determined in " 117 "this order: this option, the \bSIMPLE_BACKUP_SUFFIX\b, environment " 118 "variable, or the default value \b~\b.]:[suffix]" 119 "[b?\b--backup\b using the type in the \bVERSION_CONTROL\b environment " 120 "variable.]" 121 "[x|X:xdev|local|mount|one-file-system?Do not descend into directories " 122 "in different filesystems than their parents.]" 123 124 "\n" 125 "\nsource destination\n" 126 "file ... directory\n" 127 "\n" 128 129 "[+SEE ALSO?\bpax\b(1), \bfsync\b(2), \brename\b(2), \bunlink\b(2)," 130 " \bremove\b(3)]" 131 ; 132 133 #include <cmd.h> 134 #include <ls.h> 135 #include <times.h> 136 #include <fts_fix.h> 137 #include <fs3d.h> 138 #include <hashkey.h> 139 #include <stk.h> 140 #include <tmx.h> 141 142 #define PATH_CHUNK 256 143 144 #define CP 1 145 #define LN 2 146 #define MV 3 147 148 #define PRESERVE_IDS 0x1 /* preserve uid gid */ 149 #define PRESERVE_PERM 0x2 /* preserve permissions */ 150 #define PRESERVE_TIME 0x4 /* preserve times */ 151 152 #define BAK_replace 0 /* no backup -- just replace */ 153 #define BAK_existing 1 /* number if already else simple*/ 154 #define BAK_number 2 /* append .suffix number suffix */ 155 #define BAK_simple 3 /* append suffix */ 156 157 typedef struct State_s /* program state */ 158 { 159 Shbltin_t* context; /* builtin context */ 160 int backup; /* BAK_* type */ 161 int directory; /* destination is directory */ 162 int flags; /* FTS_* flags */ 163 int force; /* force approval */ 164 int fs3d; /* 3d fs enabled */ 165 int hierarchy; /* preserve hierarchy */ 166 int interactive; /* prompt for approval */ 167 int missmode; /* default missing dir mode */ 168 int official; /* move to next view */ 169 int op; /* {CP,LN,MV} */ 170 int perm; /* permissions to preserve */ 171 int postsiz; /* state.path post index */ 172 int presiz; /* state.path pre index */ 173 int preserve; /* preserve { ids perms times } */ 174 int recursive; /* subtrees too */ 175 int remove; /* remove destination before op */ 176 int suflen; /* strlen(state.suffix) */ 177 int sync; /* fsync() each file after copy */ 178 int uid; /* caller uid */ 179 int update; /* replace only if newer */ 180 int verbose; /* list each file before op */ 181 int wflags; /* open() for write flags */ 182 183 int (*link)(const char*, const char*); /* link */ 184 int (*stat)(const char*, struct stat*); /* stat */ 185 186 #define INITSTATE pathsiz /* (re)init state before this */ 187 int pathsiz; /* state.path buffer size */ 188 189 190 char* path; /* to pathname buffer */ 191 char* opname; /* state.op message string */ 192 char* suffix; /* backup suffix */ 193 194 Sfio_t* tmp; /* tmp string stream */ 195 196 char text[PATH_MAX]; /* link text buffer */ 197 } State_t; 198 199 static const char dot[2] = { '.' }; 200 201 /* 202 * preserve support 203 */ 204 205 static void 206 preserve(State_t* state, const char* path, struct stat* ns, struct stat* os) 207 { 208 int n; 209 210 if ((state->preserve & PRESERVE_TIME) && tmxtouch(path, tmxgetatime(os), tmxgetmtime(os), TMX_NOTIME, 0)) 211 error(ERROR_SYSTEM|2, "%s: cannot reset access and modify times", path); 212 if (state->preserve & PRESERVE_IDS) 213 { 214 n = ((ns->st_uid != os->st_uid) << 1) | (ns->st_gid != os->st_gid); 215 if (n && chown(state->path, os->st_uid, os->st_gid)) 216 switch (n) 217 { 218 case 01: 219 error(ERROR_SYSTEM|2, "%s: cannot reset group to %s", path, fmtgid(os->st_gid)); 220 break; 221 case 02: 222 error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s", path, fmtuid(os->st_uid)); 223 break; 224 case 03: 225 error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s and group to %s", path, fmtuid(os->st_uid), fmtgid(os->st_gid)); 226 break; 227 } 228 } 229 } 230 231 /* 232 * visit a single file and state.op to the destination 233 */ 234 235 static int 236 visit(State_t* state, register FTSENT* ent) 237 { 238 register char* base; 239 register int n; 240 register int len; 241 int rm; 242 int rfd; 243 int wfd; 244 int m; 245 int v; 246 char* s; 247 char* e; 248 char* protection; 249 Sfio_t* ip; 250 Sfio_t* op; 251 FTS* fts; 252 FTSENT* sub; 253 struct stat st; 254 255 if (ent->fts_info == FTS_DC) 256 { 257 error(2, "%s: directory causes cycle", ent->fts_path); 258 fts_set(NiL, ent, FTS_SKIP); 259 return 0; 260 } 261 if (ent->fts_level == 0) 262 { 263 base = ent->fts_name; 264 len = ent->fts_namelen; 265 if (state->hierarchy) 266 state->presiz = -1; 267 else 268 { 269 state->presiz = ent->fts_pathlen; 270 while (*base == '.' && *(base + 1) == '/') 271 for (base += 2; *base == '/'; base++); 272 if (*base == '.' && !*(base + 1)) 273 state->presiz--; 274 else if (*base) 275 state->presiz -= base - ent->fts_name; 276 base = ent->fts_name + len; 277 while (base > ent->fts_name && *(base - 1) == '/') 278 base--; 279 while (base > ent->fts_name && *(base - 1) != '/') 280 base--; 281 len -= base - ent->fts_name; 282 if (state->directory) 283 state->presiz -= len + 1; 284 } 285 } 286 else 287 { 288 base = ent->fts_path + state->presiz + 1; 289 len = ent->fts_pathlen - state->presiz - 1; 290 } 291 len++; 292 if (state->directory) 293 { 294 if ((state->postsiz + len) > state->pathsiz && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + len, PATH_CHUNK), 0))) 295 error(ERROR_SYSTEM|3, "out of space"); 296 if (state->hierarchy && ent->fts_level == 0 && strchr(base, '/')) 297 { 298 s = state->path + state->postsiz; 299 memcpy(s, base, len); 300 while (e = strchr(s, '/')) 301 { 302 *e = 0; 303 if (access(state->path, F_OK)) 304 { 305 st.st_mode = state->missmode; 306 if (s = strrchr(s, '/')) 307 { 308 *s = 0; 309 stat(state->path, &st); 310 *s = '/'; 311 } 312 if (mkdir(state->path, st.st_mode & S_IPERM)) 313 { 314 error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path); 315 fts_set(NiL, ent, FTS_SKIP); 316 return 0; 317 } 318 } 319 *e++ = '/'; 320 s = e; 321 } 322 } 323 } 324 switch (ent->fts_info) 325 { 326 case FTS_DP: 327 if (state->preserve && state->op != LN || ent->fts_level > 0 && (ent->fts_statp->st_mode & S_IRWXU) != S_IRWXU) 328 { 329 if (len && ent->fts_level > 0) 330 memcpy(state->path + state->postsiz, base, len); 331 else 332 state->path[state->postsiz] = 0; 333 if (stat(state->path, &st)) 334 error(ERROR_SYSTEM|2, "%s: cannot stat", state->path); 335 else 336 { 337 if ((ent->fts_statp->st_mode & S_IPERM) != (st.st_mode & S_IPERM) && chmod(state->path, ent->fts_statp->st_mode & S_IPERM)) 338 error(ERROR_SYSTEM|2, "%s: cannot reset directory mode to %s", state->path, fmtmode(st.st_mode & S_IPERM, 0) + 1); 339 if (state->preserve & (PRESERVE_IDS|PRESERVE_TIME)) 340 preserve(state, state->path, &st, ent->fts_statp); 341 } 342 } 343 return 0; 344 case FTS_DNR: 345 case FTS_DNX: 346 case FTS_D: 347 if (!state->recursive) 348 { 349 fts_set(NiL, ent, FTS_SKIP); 350 if (state->op == CP) 351 error(1, "%s: directory -- copying as plain file", ent->fts_path); 352 else if (state->link == link && !state->force) 353 { 354 error(2, "%s: cannot link directory", ent->fts_path); 355 return 0; 356 } 357 } 358 else switch (ent->fts_info) 359 { 360 case FTS_DNR: 361 error(2, "%s: cannot read directory", ent->fts_path); 362 return 0; 363 case FTS_DNX: 364 error(2, "%s: cannot search directory", ent->fts_path); 365 fts_set(NiL, ent, FTS_SKIP); 366 367 /*FALLTHROUGH*/ 368 case FTS_D: 369 if (state->directory) 370 memcpy(state->path + state->postsiz, base, len); 371 if (!(*state->stat)(state->path, &st)) 372 { 373 if (!S_ISDIR(st.st_mode)) 374 { 375 error(2, "%s: not a directory -- %s ignored", state->path, ent->fts_path); 376 return 0; 377 } 378 } 379 else if (mkdir(state->path, (ent->fts_statp->st_mode & S_IPERM)|(ent->fts_info == FTS_D ? S_IRWXU : 0))) 380 { 381 error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path); 382 fts_set(NiL, ent, FTS_SKIP); 383 } 384 if (!state->directory) 385 { 386 state->directory = 1; 387 state->path[state->postsiz++] = '/'; 388 state->presiz--; 389 } 390 return 0; 391 } 392 break; 393 case FTS_ERR: 394 case FTS_NS: 395 case FTS_SLNONE: 396 if (state->link != pathsetlink) 397 { 398 error(2, "%s: not found", ent->fts_path); 399 return 0; 400 } 401 break; 402 #if 0 403 case FTS_SL: 404 if (state->op == CP) 405 { 406 error(2, "%s: cannot copy non-terminal symbolic link", ent->fts_path); 407 return 0; 408 } 409 break; 410 #endif 411 } 412 if (state->directory) 413 memcpy(state->path + state->postsiz, base, len); 414 if ((*state->stat)(state->path, &st)) 415 st.st_mode = 0; 416 else if (state->update && !S_ISDIR(st.st_mode) && (unsigned long)ent->fts_statp->st_mtime < (unsigned long)st.st_mtime) 417 { 418 fts_set(NiL, ent, FTS_SKIP); 419 return 0; 420 } 421 else if (!state->fs3d || !iview(&st)) 422 { 423 /* 424 * target is in top 3d view 425 */ 426 427 if (state->op != LN && st.st_dev == ent->fts_statp->st_dev && st.st_ino == ent->fts_statp->st_ino) 428 { 429 if (state->op == MV) 430 { 431 /* 432 * let rename() handle it 433 */ 434 435 if (state->verbose) 436 sfputr(sfstdout, state->path, '\n'); 437 goto operate; 438 } 439 if (!state->official) 440 error(2, "%s: identical to %s", state->path, ent->fts_path); 441 return 0; 442 } 443 if (S_ISDIR(st.st_mode)) 444 { 445 error(2, "%s: cannot %s existing directory", state->path, state->opname); 446 return 0; 447 } 448 if (state->verbose) 449 sfputr(sfstdout, state->path, '\n'); 450 rm = state->remove || ent->fts_info == FTS_SL; 451 if (!rm || !state->force) 452 { 453 if (S_ISLNK(st.st_mode) && (n = -1) || (n = open(state->path, O_RDWR|O_BINARY|O_cloexec)) >= 0) 454 { 455 if (n >= 0) 456 close(n); 457 if (state->force) 458 /* ok */; 459 else if (state->interactive) 460 { 461 if ((n = astquery(-1, "%s %s? ", state->opname, state->path)) < 0 || sh_checksig(state->context)) 462 return -1; 463 if (n) 464 return 0; 465 } 466 else if (state->op == LN) 467 { 468 error(2, "%s: cannot %s existing file", state->path, state->opname); 469 return 0; 470 } 471 } 472 else if (state->force) 473 rm = 1; 474 else 475 { 476 protection = 477 #ifdef ETXTBSY 478 errno == ETXTBSY ? "``running program''" : 479 #endif 480 st.st_uid != state->uid ? "``not owner''" : 481 fmtmode(st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO), 0) + 1; 482 if (state->interactive) 483 { 484 if ((n = astquery(-1, "override protection %s for %s? ", protection, state->path)) < 0 || sh_checksig(state->context)) 485 return -1; 486 if (n) 487 return 0; 488 rm = 1; 489 } 490 else if (!rm) 491 { 492 error(2, "%s: cannot %s %s protection", state->path, state->opname, protection); 493 return 0; 494 } 495 } 496 } 497 switch (state->backup) 498 { 499 case BAK_existing: 500 case BAK_number: 501 v = 0; 502 if (s = strrchr(state->path, '/')) 503 { 504 e = state->path; 505 *s++ = 0; 506 } 507 else 508 { 509 e = (char*)dot; 510 s = state->path; 511 } 512 n = strlen(s); 513 if (fts = fts_open((char**)e, FTS_NOCHDIR|FTS_ONEPATH|FTS_PHYSICAL|FTS_NOPOSTORDER|FTS_NOSTAT|FTS_NOSEEDOTDIR, NiL)) 514 { 515 while (sub = fts_read(fts)) 516 { 517 if (strneq(s, sub->fts_name, n) && sub->fts_name[n] == '.' && strneq(sub->fts_name + n + 1, state->suffix, state->suflen) && (m = strtol(sub->fts_name + n + state->suflen + 1, &e, 10)) && streq(e, state->suffix) && m > v) 518 v = m; 519 if (sub->fts_level) 520 fts_set(NiL, sub, FTS_SKIP); 521 } 522 fts_close(fts); 523 } 524 if (s != state->path) 525 *--s = '/'; 526 if (v || state->backup == BAK_number) 527 { 528 sfprintf(state->tmp, "%s.%s%d%s", state->path, state->suffix, v + 1, state->suffix); 529 goto backup; 530 } 531 /*FALLTHROUGH*/ 532 case BAK_simple: 533 sfprintf(state->tmp, "%s%s", state->path, state->suffix); 534 backup: 535 if (!(s = sfstruse(state->tmp))) 536 error(ERROR_SYSTEM|3, "%s: out of space", state->path); 537 if (rename(state->path, s)) 538 { 539 error(ERROR_SYSTEM|2, "%s: cannot backup to %s", state->path, s); 540 return 0; 541 } 542 break; 543 default: 544 if (rm && remove(state->path)) 545 { 546 error(ERROR_SYSTEM|2, "%s: cannot remove", state->path); 547 return 0; 548 } 549 break; 550 } 551 } 552 operate: 553 switch (state->op) 554 { 555 case MV: 556 for (;;) 557 { 558 if (!rename(ent->fts_path, state->path)) 559 return 0; 560 if (errno == ENOENT) 561 rm = 1; 562 else if (!rm && st.st_mode && !remove(state->path)) 563 { 564 rm = 1; 565 continue; 566 } 567 if (errno != EXDEV && (rm || S_ISDIR(ent->fts_statp->st_mode))) 568 { 569 error(ERROR_SYSTEM|2, "%s: cannot rename to %s", ent->fts_path, state->path); 570 return 0; 571 } 572 else 573 break; 574 } 575 /*FALLTHROUGH*/ 576 case CP: 577 if (S_ISLNK(ent->fts_statp->st_mode)) 578 { 579 if ((n = pathgetlink(ent->fts_path, state->text, sizeof(state->text) - 1)) < 0) 580 { 581 error(ERROR_SYSTEM|2, "%s: cannot read symbolic link text", ent->fts_path); 582 return 0; 583 } 584 state->text[n] = 0; 585 if (pathsetlink(state->text, state->path)) 586 { 587 error(ERROR_SYSTEM|2, "%s: cannot copy symbolic link to %s", ent->fts_path, state->path); 588 return 0; 589 } 590 } 591 else if (state->op == CP || S_ISREG(ent->fts_statp->st_mode) || S_ISDIR(ent->fts_statp->st_mode)) 592 { 593 if (ent->fts_statp->st_size > 0 && (rfd = open(ent->fts_path, O_RDONLY|O_BINARY|O_cloexec)) < 0) 594 { 595 error(ERROR_SYSTEM|2, "%s: cannot read", ent->fts_path); 596 return 0; 597 } 598 else if ((wfd = open(state->path, (st.st_mode ? (state->wflags & ~O_EXCL) : state->wflags)|O_cloexec, ent->fts_statp->st_mode & state->perm)) < 0) 599 { 600 error(ERROR_SYSTEM|2, "%s: cannot write", state->path); 601 if (ent->fts_statp->st_size > 0) 602 close(rfd); 603 return 0; 604 } 605 else if (ent->fts_statp->st_size > 0) 606 { 607 if (!(ip = sfnew(NiL, NiL, SF_UNBOUND, rfd, SF_READ))) 608 { 609 error(ERROR_SYSTEM|2, "%s: %s read stream error", ent->fts_path, state->path); 610 close(rfd); 611 close(wfd); 612 return 0; 613 } 614 if (!(op = sfnew(NiL, NiL, SF_UNBOUND, wfd, SF_WRITE))) 615 { 616 error(ERROR_SYSTEM|2, "%s: %s write stream error", ent->fts_path, state->path); 617 close(wfd); 618 sfclose(ip); 619 return 0; 620 } 621 n = 0; 622 if (sfmove(ip, op, (Sfoff_t)SF_UNBOUND, -1) < 0) 623 n |= 3; 624 if (!sfeof(ip)) 625 n |= 1; 626 if (sfsync(op) || state->sync && fsync(wfd) || sfclose(op)) 627 n |= 2; 628 if (sfclose(ip)) 629 n |= 1; 630 if (n) 631 { 632 error(ERROR_SYSTEM|2, "%s: %s %s error", ent->fts_path, state->path, n == 1 ? ERROR_translate(0, 0, 0, "read") : n == 2 ? ERROR_translate(0, 0, 0, "write") : ERROR_translate(0, 0, 0, "io")); 633 return 0; 634 } 635 } 636 else 637 close(wfd); 638 } 639 else if (S_ISBLK(ent->fts_statp->st_mode) || S_ISCHR(ent->fts_statp->st_mode) || S_ISFIFO(ent->fts_statp->st_mode)) 640 { 641 if (mknod(state->path, ent->fts_statp->st_mode, idevice(ent->fts_statp))) 642 { 643 error(ERROR_SYSTEM|2, "%s: cannot copy special file to %s", ent->fts_path, state->path); 644 return 0; 645 } 646 } 647 else 648 { 649 error(2, "%s: cannot copy -- unknown file type 0%o", ent->fts_path, S_ITYPE(ent->fts_statp->st_mode)); 650 return 0; 651 } 652 if (state->preserve) 653 { 654 if (ent->fts_info != FTS_SL) 655 { 656 if (stat(state->path, &st)) 657 error(ERROR_SYSTEM|2, "%s: cannot stat", state->path); 658 else 659 { 660 if ((state->preserve & PRESERVE_PERM) && (ent->fts_statp->st_mode & state->perm) != (st.st_mode & state->perm) && chmod(state->path, ent->fts_statp->st_mode & state->perm)) 661 error(ERROR_SYSTEM|2, "%s: cannot reset mode to %s", state->path, fmtmode(st.st_mode & state->perm, 0) + 1); 662 if (state->preserve & (PRESERVE_IDS|PRESERVE_TIME)) 663 preserve(state, state->path, &st, ent->fts_statp); 664 } 665 } 666 if (state->op == MV && remove(ent->fts_path)) 667 error(ERROR_SYSTEM|1, "%s: cannot remove", ent->fts_path); 668 } 669 break; 670 case LN: 671 if ((*state->link)(ent->fts_path, state->path)) 672 error(ERROR_SYSTEM|2, "%s: cannot link to %s", ent->fts_path, state->path); 673 break; 674 } 675 return 0; 676 } 677 678 int 679 b_cp(int argc, register char** argv, Shbltin_t* context) 680 { 681 register char* file; 682 register char* s; 683 char** v; 684 char* backup_type; 685 FTS* fts; 686 FTSENT* ent; 687 const char* usage; 688 int path_resolve; 689 int standard; 690 struct stat st; 691 State_t* state; 692 Shbltin_t* sh; 693 Shbltin_t* cleanup = context; 694 695 cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY); 696 if (!(sh = CMD_CONTEXT(context)) || !(state = (State_t*)sh->ptr)) 697 { 698 if (!(state = newof(0, State_t, 1, 0))) 699 error(ERROR_SYSTEM|3, "out of space"); 700 if (sh) 701 sh->ptr = state; 702 } 703 else 704 memset(state, 0, offsetof(State_t, INITSTATE)); 705 state->context = context; 706 state->presiz = -1; 707 backup_type = 0; 708 state->flags = FTS_NOCHDIR|FTS_NOSEEDOTDIR; 709 state->uid = geteuid(); 710 state->wflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY; 711 if (!state->tmp && !(state->tmp = sfstropen())) 712 error(ERROR_SYSTEM|3, "out of space [tmp string]"); 713 sfputr(state->tmp, usage_head, -1); 714 standard = !!conformance(0, 0); 715 switch (error_info.id[0]) 716 { 717 case 'c': 718 case 'C': 719 sfputr(state->tmp, usage_cp, -1); 720 state->op = CP; 721 state->stat = stat; 722 path_resolve = -1; 723 break; 724 case 'l': 725 case 'L': 726 sfputr(state->tmp, usage_ln, -1); 727 state->op = LN; 728 state->flags |= FTS_PHYSICAL; 729 state->link = link; 730 state->remove = 1; 731 state->stat = lstat; 732 path_resolve = 1; 733 break; 734 case 'm': 735 case 'M': 736 sfputr(state->tmp, usage_mv, -1); 737 state->op = MV; 738 state->flags |= FTS_PHYSICAL; 739 state->preserve = PRESERVE_IDS|PRESERVE_PERM|PRESERVE_TIME; 740 state->stat = lstat; 741 path_resolve = 1; 742 break; 743 default: 744 error(3, "not implemented"); 745 break; 746 } 747 sfputr(state->tmp, usage_tail, -1); 748 if (!(usage = sfstruse(state->tmp))) 749 error(ERROR_SYSTEM|3, "%s: out of space", state->path); 750 state->opname = state->op == CP ? ERROR_translate(0, 0, 0, "overwrite") : ERROR_translate(0, 0, 0, "replace"); 751 for (;;) 752 { 753 switch (optget(argv, usage)) 754 { 755 case 'a': 756 state->flags |= FTS_PHYSICAL; 757 state->preserve = PRESERVE_IDS|PRESERVE_PERM|PRESERVE_TIME; 758 state->recursive = 1; 759 path_resolve = 1; 760 continue; 761 case 'A': 762 s = opt_info.arg; 763 for (;;) 764 { 765 switch (*s++) 766 { 767 case 0: 768 break; 769 case 'e': 770 state->preserve |= PRESERVE_IDS|PRESERVE_PERM|PRESERVE_TIME; 771 continue; 772 case 'i': 773 state->preserve |= PRESERVE_IDS; 774 continue; 775 case 'p': 776 state->preserve |= PRESERVE_PERM; 777 continue; 778 case 't': 779 state->preserve |= PRESERVE_TIME; 780 continue; 781 default: 782 error(1, "%s=%c: unknown attribute flag", opt_info.option, *(s - 1)); 783 continue; 784 } 785 break; 786 } 787 continue; 788 case 'b': 789 state->backup = 1; 790 continue; 791 case 'f': 792 state->force = 1; 793 if (state->op != CP || !standard) 794 state->interactive = 0; 795 continue; 796 case 'h': 797 state->hierarchy = 1; 798 continue; 799 case 'i': 800 state->interactive = 1; 801 if (state->op != CP || !standard) 802 state->force = 0; 803 continue; 804 case 'l': 805 state->op = LN; 806 state->link = link; 807 state->stat = lstat; 808 continue; 809 case 'p': 810 state->preserve = PRESERVE_IDS|PRESERVE_PERM|PRESERVE_TIME; 811 continue; 812 case 'r': 813 state->recursive = 1; 814 if (path_resolve < 0) 815 path_resolve = 0; 816 continue; 817 case 's': 818 state->op = LN; 819 state->link = pathsetlink; 820 state->stat = lstat; 821 continue; 822 case 'u': 823 state->update = 1; 824 continue; 825 case 'v': 826 state->verbose = 1; 827 continue; 828 case 'x': 829 state->flags |= FTS_XDEV; 830 continue; 831 case 'B': 832 backup_type = opt_info.arg; 833 state->backup = 1; 834 continue; 835 case 'F': 836 #if _lib_fsync 837 state->sync = 1; 838 #else 839 error(1, "%s not implemented on this system", opt_info.name); 840 #endif 841 continue; 842 case 'H': 843 state->flags |= FTS_META|FTS_PHYSICAL; 844 path_resolve = 1; 845 continue; 846 case 'L': 847 state->flags &= ~FTS_PHYSICAL; 848 path_resolve = 1; 849 continue; 850 case 'P': 851 state->flags &= ~FTS_META; 852 state->flags |= FTS_PHYSICAL; 853 path_resolve = 1; 854 continue; 855 case 'R': 856 state->recursive = 1; 857 state->flags &= ~FTS_META; 858 state->flags |= FTS_PHYSICAL; 859 path_resolve = 1; 860 continue; 861 case 'S': 862 state->suffix = opt_info.arg; 863 continue; 864 case 'U': 865 state->remove = 1; 866 continue; 867 case '?': 868 error(ERROR_USAGE|4, "%s", opt_info.arg); 869 continue; 870 case ':': 871 error(2, "%s", opt_info.arg); 872 continue; 873 } 874 break; 875 } 876 argc -= opt_info.index + 1; 877 argv += opt_info.index; 878 if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--")) 879 { 880 argc--; 881 argv++; 882 } 883 if (!(v = (char**)stkalloc(stkstd, (argc + 2) * sizeof(char*)))) 884 error(ERROR_SYSTEM|3, "out of space"); 885 memcpy(v, argv, (argc + 1) * sizeof(char*)); 886 argv = v; 887 if (!standard) 888 { 889 state->wflags |= O_EXCL; 890 if (!argc) 891 { 892 argc++; 893 argv[1] = (char*)dot; 894 } 895 } 896 if (state->backup) 897 { 898 if (!(file = backup_type) && !(backup_type = getenv("VERSION_CONTROL"))) 899 state->backup = 0; 900 else 901 switch (strkey(backup_type)) 902 { 903 case HASHKEY6('e','x','i','s','t','i'): 904 case HASHKEY5('e','x','i','s','t'): 905 case HASHKEY4('e','x','i','s'): 906 case HASHKEY3('e','x','i'): 907 case HASHKEY2('e','x'): 908 case HASHKEY1('e'): 909 case HASHKEY3('n','i','l'): 910 case HASHKEY2('n','i'): 911 state->backup = BAK_existing; 912 break; 913 case HASHKEY5('n','e','v','e','r'): 914 case HASHKEY4('n','e','v','e'): 915 case HASHKEY3('n','e','v'): 916 case HASHKEY2('n','e'): 917 case HASHKEY6('s','i','m','p','l','e'): 918 case HASHKEY5('s','i','m','p','l'): 919 case HASHKEY4('s','i','m','p'): 920 case HASHKEY3('s','i','m'): 921 case HASHKEY2('s','i'): 922 case HASHKEY1('s'): 923 state->backup = BAK_simple; 924 break; 925 case HASHKEY4('n','o','n','e'): 926 case HASHKEY3('n','o','n'): 927 case HASHKEY2('n','o'): 928 case HASHKEY3('o','f','f'): 929 case HASHKEY2('o','f'): 930 case HASHKEY1('o'): 931 state->backup = 0; 932 break; 933 case HASHKEY6('n','u','m','b','e','r'): 934 case HASHKEY5('n','u','m','b','e'): 935 case HASHKEY4('n','u','m','b'): 936 case HASHKEY3('n','u','m'): 937 case HASHKEY2('n','u'): 938 case HASHKEY1('t'): 939 state->backup = BAK_number; 940 break; 941 default: 942 if (file) 943 error(2, "%s: unknown backup type", backup_type); 944 break; 945 } 946 if (!state->suffix && !(state->suffix = getenv("SIMPLE_BACKUP_SUFFIX"))) 947 state->suffix = "~"; 948 state->suflen = strlen(state->suffix); 949 } 950 if (argc <= 0 || error_info.errors) 951 error(ERROR_USAGE|4, "%s", optusage(NiL)); 952 if (!path_resolve) 953 state->flags |= fts_flags() | FTS_META; 954 file = argv[argc]; 955 argv[argc] = 0; 956 if (s = strrchr(file, '/')) 957 { 958 while (*s == '/') 959 s++; 960 if (!(!*s || *s == '.' && (!*++s || *s == '.' && !*++s))) 961 s = 0; 962 } 963 if (file != (char*)dot) 964 pathcanon(file, 0, 0); 965 if (!(state->directory = !stat(file, &st) && S_ISDIR(st.st_mode)) && argc > 1) 966 error(ERROR_USAGE|4, "%s", optusage(NiL)); 967 if (s && !state->directory) 968 error(3, "%s: not a directory", file); 969 if ((state->fs3d = fs3d(FS3D_TEST)) && strmatch(file, "...|*/...|.../*")) 970 state->official = 1; 971 state->postsiz = strlen(file); 972 if (state->pathsiz < roundof(state->postsiz + 2, PATH_CHUNK) && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + 2, PATH_CHUNK), 0))) 973 error(ERROR_SYSTEM|3, "out of space"); 974 memcpy(state->path, file, state->postsiz + 1); 975 if (state->directory && state->path[state->postsiz - 1] != '/') 976 state->path[state->postsiz++] = '/'; 977 if (state->hierarchy) 978 { 979 if (!state->directory) 980 error(3, "%s: last argument must be a directory", file); 981 state->missmode = st.st_mode; 982 } 983 state->perm = state->uid ? S_IPERM : (S_IPERM & ~S_ISVTX); 984 if (!state->recursive) 985 state->flags |= FTS_TOP; 986 if (fts = fts_open(argv, state->flags, NiL)) 987 { 988 while (!sh_checksig(context) && (ent = fts_read(fts)) && !visit(state, ent)); 989 fts_close(fts); 990 } 991 else if (state->link != pathsetlink) 992 switch (state->op) 993 { 994 case CP: 995 error(ERROR_SYSTEM|2, "%s: cannot copy", argv[0]); 996 break; 997 case LN: 998 error(ERROR_SYSTEM|2, "%s: cannot link", argv[0]); 999 break; 1000 case MV: 1001 error(ERROR_SYSTEM|2, "%s: cannot move", argv[0]); 1002 break; 1003 } 1004 else if ((*state->link)(*argv, state->path)) 1005 error(ERROR_SYSTEM|2, "%s: cannot link to %s", *argv, state->path); 1006 if (cleanup && !sh) 1007 { 1008 if (state->path) 1009 free(state->path); 1010 free(state); 1011 } 1012 return error_info.errors != 0; 1013 } 1014