00001
00002
00003
00004
00005
00006
00007
00008 #define nil 0
00009 #include <stdio.h>
00010 #include <sys/types.h>
00011 #include <stdlib.h>
00012 #include <string.h>
00013 #include <stddef.h>
00014 #include <unistd.h>
00015 #include <fcntl.h>
00016 #include <time.h>
00017 #include <sys/stat.h>
00018 #include <utime.h>
00019 #include <dirent.h>
00020 #include <errno.h>
00021 #ifndef DEBUG
00022 #define DEBUG 0
00023 #define NDEBUG 1
00024 #endif
00025 #include <assert.h>
00026
00027 #include <sys/dir.h>
00028
00029
00030 #if __minix && !__minix_vmd
00031 #define CHUNK (8192 * sizeof(char *))
00032 #else
00033 #define CHUNK (1024 << (sizeof(int) + sizeof(char *)))
00034 #endif
00035
00036
00037 #ifndef CONFORMING
00038 #define CONFORMING 1
00039 #endif
00040
00041
00042 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
00043 #define arraylimit(a) ((a) + arraysize(a))
00044
00045 char *prog_name;
00046 int ex_code= 0;
00047
00048 typedef enum identity { CP, MV, RM, LN, CPDIR, CLONE } identity_t;
00049 typedef enum action { COPY, MOVE, REMOVE, LINK } action_t;
00050
00051 identity_t identity;
00052 action_t action;
00053 int pflag= 0;
00054 int iflag= 0;
00055 int fflag= 0;
00056 int sflag= 0;
00057 int Sflag= 0;
00058 int mflag= 0;
00059 int rflag= 0;
00060 int vflag= 0;
00061 int xflag= 0;
00062 int xdev= 0;
00063 int expand= 0;
00064 int conforming= CONFORMING;
00065
00066 int fc_mask;
00067 int uid, gid;
00068 int istty;
00069
00070 #ifndef S_ISLNK
00071
00072 #define S_ISLNK(mode) (0)
00073 #define lstat stat
00074 #define symlink(path1, path2) (errno= ENOSYS, -1)
00075 #define readlink(path, buf, len) (errno= ENOSYS, -1)
00076 #endif
00077
00078 void report(const char *label)
00079 {
00080 if (action == REMOVE && fflag) return;
00081 fprintf(stderr, "%s: %s: %s\n", prog_name, label, strerror(errno));
00082 ex_code= 1;
00083 }
00084
00085 void fatal(const char *label)
00086 {
00087 report(label);
00088 exit(1);
00089 }
00090
00091 void report2(const char *src, const char *dst)
00092 {
00093 fprintf(stderr, "%s %s %s: %s\n", prog_name, src, dst, strerror(errno));
00094 ex_code= 1;
00095 }
00096
00097 #if DEBUG
00098 size_t nchunks= 0;
00099 #endif
00100
00101 void *allocate(void *mem, size_t size)
00102
00103 {
00104 #if DEBUG
00105 if (mem == nil) nchunks++;
00106 #endif
00107 if ((mem= mem == nil ? malloc(size) : realloc(mem, size)) == nil)
00108 fatal("malloc()");
00109 return mem;
00110 }
00111
00112 void deallocate(void *mem)
00113
00114 {
00115 if (mem != nil) {
00116 #if DEBUG
00117 nchunks--;
00118 #endif
00119 free(mem);
00120 }
00121 }
00122
00123 typedef struct pathname {
00124 char *path;
00125 size_t idx;
00126 size_t lim;
00127 } pathname_t;
00128
00129 void path_init(pathname_t *pp)
00130
00131 {
00132 pp->path= allocate(nil, pp->lim= DIRSIZ + 2);
00133 pp->path[pp->idx= 0]= 0;
00134 }
00135
00136 void path_add(pathname_t *pp, const char *name)
00137
00138 {
00139 size_t lim;
00140 char *p;
00141
00142 lim= pp->idx + strlen(name) + 2;
00143
00144 if (lim > pp->lim) {
00145 pp->lim= lim += lim/2;
00146
00147 pp->path= allocate(pp->path, lim);
00148 }
00149
00150 p= pp->path + pp->idx;
00151 if (p > pp->path && p[-1] != '/') *p++ = '/';
00152
00153 while (*name != 0) {
00154 if (*name != '/' || p == pp->path || p[-1] != '/') *p++ = *name;
00155 name++;
00156 }
00157 *p = 0;
00158 pp->idx= p - pp->path;
00159 }
00160
00161 void path_trunc(pathname_t *pp, size_t didx)
00162
00163 {
00164 pp->path[pp->idx= didx]= 0;
00165 }
00166
00167 #if DEBUG
00168 const char *path_name(const pathname_t *pp)
00169
00170 {
00171 return pp->path;
00172 }
00173
00174 size_t path_length(const pathname_t *pp)
00175
00176 {
00177 return pp->idx;
00178 }
00179
00180 void path_drop(pathname_t *pp)
00181
00182 {
00183 deallocate(pp->path);
00184 }
00185
00186 #else
00187 #define path_name(pp) ((const char *) (pp)->path)
00188 #define path_length(pp) ((pp)->idx)
00189 #define path_drop(pp) deallocate((void *) (pp)->path)
00190 #endif
00191
00192 char *basename(const char *path)
00193
00194
00195
00196 {
00197 const char *p= path;
00198
00199 for (;;) {
00200 while (*p == '/') p++;
00201
00202 if (*p == 0) break;
00203
00204 path= p;
00205 while (*p != 0 && *p != '/') p++;
00206 }
00207 return (char *) path;
00208 }
00209
00210 int affirmative(void)
00211
00212 {
00213 int c;
00214 int ok;
00215
00216 fflush(stdout);
00217 fflush(stderr);
00218
00219 while ((c= getchar()) == ' ') {}
00220 ok= (c == 'y' || c == 'Y');
00221 while (c != EOF && c != '\n') c= getchar();
00222
00223 return ok;
00224 }
00225
00226 int writable(const struct stat *stp)
00227
00228
00229
00230 {
00231 if (!istty || uid == 0) return 1;
00232 if (stp->st_uid == uid) return stp->st_mode & S_IWUSR;
00233 if (stp->st_gid == gid) return stp->st_mode & S_IWGRP;
00234 return stp->st_mode & S_IWOTH;
00235 }
00236
00237 #ifndef PATH_MAX
00238 #define PATH_MAX 1024
00239 #endif
00240
00241 static char *link_islink(struct stat *stp, const char *file)
00242 {
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254 typedef struct link {
00255 struct link *next;
00256 ino_t ino;
00257 off_t off;
00258 } link_t;
00259 typedef struct dlink {
00260 dev_t dev;
00261 char file[PATH_MAX];
00262 } dlink_t;
00263 static link_t *links[256];
00264 static int tfd= -1;
00265 static dlink_t dlink;
00266 link_t *lp, **plp;
00267 size_t len;
00268 off_t off;
00269
00270 if (file == nil) {
00271
00272 for (plp= links; plp < arraylimit(links); plp++) {
00273 while ((lp= *plp) != nil) {
00274 *plp= lp->next;
00275 free(lp);
00276 }
00277 }
00278 if (tfd != -1) close(tfd);
00279 tfd= -1;
00280 return nil;
00281 }
00282
00283
00284 if (S_ISDIR(stp->st_mode) || stp->st_nlink <= 1) {
00285 errno= ENOENT;
00286 return nil;
00287 }
00288
00289 plp= &links[stp->st_ino % arraysize(links)];
00290
00291 while ((lp= *plp) != nil) {
00292 if (lp->ino == stp->st_ino) {
00293
00294 if (lseek(tfd, lp->off, SEEK_SET) == -1) return nil;
00295 if (read(tfd, &dlink, sizeof(dlink)) < 0) return nil;
00296
00297
00298 if (dlink.dev == stp->st_dev) {
00299 if (strcmp(file, dlink.file) == 0) {
00300
00301 *plp= lp->next;
00302 free(lp);
00303 errno= ENOENT;
00304 return nil;
00305 }
00306
00307
00308 return dlink.file;
00309 }
00310 }
00311 plp= &lp->next;
00312 }
00313
00314
00315 if (tfd == -1) {
00316 for (;;) {
00317 char *tmp;
00318
00319 tmp= tmpnam(nil);
00320 tfd= open(tmp, O_RDWR|O_CREAT|O_EXCL, 0600);
00321 if (tfd < 0) {
00322 if (errno != EEXIST) return nil;
00323 } else {
00324 (void) unlink(tmp);
00325 break;
00326 }
00327 }
00328 }
00329 if ((len= strlen(file)) >= PATH_MAX) {
00330 errno= E2BIG;
00331 return nil;
00332 }
00333
00334 dlink.dev= stp->st_dev;
00335 strcpy(dlink.file, file);
00336 len += offsetof(dlink_t, file) + 1;
00337 if ((off= lseek(tfd, 0, SEEK_END)) == -1) return nil;
00338 if (write(tfd, &dlink, len) != len) return nil;
00339
00340 if ((lp= malloc(sizeof(*lp))) == nil) return nil;
00341 lp->next= nil;
00342 lp->ino= stp->st_ino;
00343 lp->off= off;
00344 *plp= lp;
00345 errno= ENOENT;
00346 return nil;
00347 }
00348
00349 int trylink(const char *src, const char *dst, struct stat *srcst,
00350 struct stat *dstst)
00351
00352 {
00353 char *olddst;
00354 int linked;
00355
00356 if (action == COPY && expand) return 0;
00357
00358 if ((olddst= link_islink(srcst, dst)) == nil) {
00359
00360 return 0;
00361 }
00362
00363
00364 if (dstst->st_ino != 0) (void) unlink(dst);
00365
00366 if ((linked= (link(olddst, dst) == 0)) && vflag)
00367 printf("ln %s ..\n", olddst);
00368
00369 return linked;
00370 }
00371
00372 int copy(const char *src, const char *dst, struct stat *srcst,
00373 struct stat *dstst)
00374
00375 {
00376 char buf[CHUNK];
00377 int srcfd, dstfd;
00378 ssize_t n;
00379
00380 assert(srcst->st_ino != 0);
00381
00382 if (dstst->st_ino == 0) {
00383
00384
00385 if (!S_ISREG(srcst->st_mode)) {
00386
00387 srcst->st_mode= (S_IFREG | 0666) & fc_mask;
00388 } else
00389 if (!pflag && conforming) {
00390
00391 srcst->st_mode &= fc_mask;
00392 }
00393 } else {
00394
00395
00396 if (iflag || (action == MOVE && !fflag && !writable(dstst))) {
00397 fprintf(stderr, "Overwrite %s? (mode = %03o) ",
00398 dst, dstst->st_mode & 07777);
00399 if (!affirmative()) return 0;
00400 }
00401
00402 if (action == MOVE) {
00403
00404 if (unlink(dst) < 0 && errno != ENOENT) {
00405 report(dst);
00406 return 0;
00407 }
00408 } else {
00409
00410 if (!pflag) {
00411
00412 srcst->st_mode= dstst->st_mode;
00413 srcst->st_uid= dstst->st_uid;
00414 srcst->st_gid= dstst->st_gid;
00415 }
00416 }
00417 }
00418
00419
00420 if (trylink(src, dst, srcst, dstst)) return 1;
00421
00422 if ((srcfd= open(src, O_RDONLY)) < 0) {
00423 report(src);
00424 return 0;
00425 }
00426
00427 dstfd= open(dst, O_WRONLY|O_CREAT|O_TRUNC, srcst->st_mode & 0777);
00428 if (dstfd < 0 && fflag && errno == EACCES) {
00429
00430 (void) chmod(dst, dstst->st_mode | S_IWUSR);
00431 dstfd= open(dst, O_WRONLY|O_CREAT|O_TRUNC, 0);
00432 }
00433 if (dstfd < 0 && fflag && errno == EACCES) {
00434
00435 (void) unlink(dst);
00436 dstfd= open(dst, O_WRONLY|O_CREAT|O_TRUNC, 0);
00437 }
00438 if (dstfd < 0) {
00439 report(dst);
00440 close(srcfd);
00441 return 0;
00442 }
00443
00444
00445 if (fstat(dstfd, dstst) < 0) {
00446 report(dst);
00447 close(srcfd);
00448 close(dstfd);
00449 return 0;
00450 }
00451
00452
00453 while ((n= read(srcfd, buf, sizeof(buf))) > 0) {
00454 char *bp = buf;
00455 ssize_t r;
00456
00457 while (n > 0 && (r= write(dstfd, bp, n)) > 0) {
00458 bp += r;
00459 n -= r;
00460 }
00461 if (r <= 0) {
00462 if (r == 0) {
00463 fprintf(stderr,
00464 "%s: Warning: EOF writing to %s\n",
00465 prog_name, dst);
00466 break;
00467 }
00468 fatal(dst);
00469 }
00470 }
00471
00472 if (n < 0) {
00473 report(src);
00474 close(srcfd);
00475 close(dstfd);
00476 return 0;
00477 }
00478
00479 close(srcfd);
00480 close(dstfd);
00481
00482
00483 if ((pflag || !conforming)
00484 && S_ISREG(dstst->st_mode)
00485 && (dstst->st_uid != srcst->st_uid
00486 || dstst->st_gid != srcst->st_gid)
00487 ) {
00488 if (chmod(dst, 0) == 0) dstst->st_mode&= ~07777;
00489 if (chown(dst, srcst->st_uid, srcst->st_gid) < 0) {
00490 if (errno != EPERM) {
00491 report(dst);
00492 return 0;
00493 }
00494 } else {
00495 dstst->st_uid= srcst->st_uid;
00496 dstst->st_gid= srcst->st_gid;
00497 }
00498 }
00499
00500 if (conforming && S_ISREG(dstst->st_mode)
00501 && (dstst->st_uid != srcst->st_uid
00502 || dstst->st_gid != srcst->st_gid)
00503 ) {
00504
00505
00506
00507 srcst->st_mode&= ~06000;
00508 }
00509
00510
00511 if (S_ISREG(dstst->st_mode) && dstst->st_mode != srcst->st_mode) {
00512 if (chmod(dst, srcst->st_mode) < 0) {
00513 if (errno != EPERM) {
00514 report(dst);
00515 return 0;
00516 }
00517 fprintf(stderr, "%s: Can't change the mode of %s\n",
00518 prog_name, dst);
00519 }
00520 }
00521
00522
00523 if ((pflag || !conforming) && S_ISREG(dstst->st_mode)) {
00524 struct utimbuf ut;
00525
00526 ut.actime= action == MOVE ? srcst->st_atime : time(nil);
00527 ut.modtime= srcst->st_mtime;
00528 if (utime(dst, &ut) < 0) {
00529 if (errno != EPERM) {
00530 report(dst);
00531 return 0;
00532 }
00533 if (pflag) {
00534 fprintf(stderr,
00535 "%s: Can't set the time of %s\n",
00536 prog_name, dst);
00537 }
00538 }
00539 }
00540 if (vflag) {
00541 printf(action == COPY ? "cp %s ..\n" : "mv %s ..\n", src);
00542 }
00543 return 1;
00544 }
00545
00546 void copy1(const char *src, const char *dst, struct stat *srcst,
00547 struct stat *dstst)
00548
00549
00550
00551
00552
00553 {
00554 int r, linked;
00555
00556 assert(srcst->st_ino != 0);
00557
00558 if (srcst->st_ino == dstst->st_ino && srcst->st_dev == dstst->st_dev) {
00559 fprintf(stderr, "%s: can't copy %s onto itself\n",
00560 prog_name, src);
00561 ex_code= 1;
00562 return;
00563 }
00564
00565
00566 if (dstst->st_ino != 0 && S_ISDIR(dstst->st_mode)) {
00567 errno= EISDIR;
00568 report(dst);
00569 return;
00570 }
00571
00572 if (S_ISREG(srcst->st_mode) || (expand && !rflag)) {
00573 if (!copy(src, dst, srcst, dstst)) return;
00574
00575 if (action == MOVE && unlink(src) < 0) {
00576 report(src);
00577 return;
00578 }
00579 return;
00580 }
00581
00582 if (dstst->st_ino != 0) {
00583 if (iflag || (action == MOVE && !fflag && !writable(dstst))) {
00584 fprintf(stderr, "Replace %s? (mode = %03o) ",
00585 dst, dstst->st_mode & 07777);
00586 if (!affirmative()) return;
00587 }
00588 if (unlink(dst) < 0) {
00589 report(dst);
00590 return;
00591 }
00592 dstst->st_ino= 0;
00593 }
00594
00595
00596 if (!pflag && conforming) srcst->st_mode &= fc_mask;
00597
00598 linked= 0;
00599
00600 if (S_ISLNK(srcst->st_mode)) {
00601 char buf[1024+1];
00602
00603 if ((r= readlink(src, buf, sizeof(buf)-1)) < 0) {
00604 report(src);
00605 return;
00606 }
00607 buf[r]= 0;
00608 r= symlink(buf, dst);
00609 if (vflag && r == 0)
00610 printf("ln -s %s %s\n", buf, dst);
00611 } else
00612 if (trylink(src, dst, srcst, dstst)) {
00613 linked= 1;
00614 r= 0;
00615 } else
00616 if (S_ISFIFO(srcst->st_mode)) {
00617 r= mkfifo(dst, srcst->st_mode);
00618 if (vflag && r == 0)
00619 printf("mkfifo %s\n", dst);
00620 } else
00621 if (S_ISBLK(srcst->st_mode) || S_ISCHR(srcst->st_mode)) {
00622 r= mknod(dst, srcst->st_mode, srcst->st_rdev);
00623 if (vflag && r == 0) {
00624 printf("mknod %s %c %d %d\n",
00625 dst,
00626 S_ISBLK(srcst->st_mode) ? 'b' : 'c',
00627 (srcst->st_rdev >> 8) & 0xFF,
00628 (srcst->st_rdev >> 0) & 0xFF);
00629 }
00630 } else {
00631 fprintf(stderr, "%s: %s: odd filetype %5o (not copied)\n",
00632 prog_name, src, srcst->st_mode);
00633 ex_code= 1;
00634 return;
00635 }
00636
00637 if (r < 0 || lstat(dst, dstst) < 0) {
00638 report(dst);
00639 return;
00640 }
00641
00642 if (action == MOVE && unlink(src) < 0) {
00643 report(src);
00644 (void) unlink(dst);
00645 return;
00646 }
00647
00648 if (linked) return;
00649
00650 if (S_ISLNK(srcst->st_mode)) return;
00651
00652
00653 if ((pflag || !conforming)
00654 && (dstst->st_uid != srcst->st_uid
00655 || dstst->st_gid != srcst->st_gid)
00656 ) {
00657 if (chown(dst, srcst->st_uid, srcst->st_gid) < 0) {
00658 if (errno != EPERM) {
00659 report(dst);
00660 return;
00661 }
00662 }
00663 }
00664
00665
00666 if (pflag || !conforming) {
00667 struct utimbuf ut;
00668
00669 ut.actime= action == MOVE ? srcst->st_atime : time(nil);
00670 ut.modtime= srcst->st_mtime;
00671 if (utime(dst, &ut) < 0) {
00672 if (errno != EPERM) {
00673 report(dst);
00674 return;
00675 }
00676 fprintf(stderr, "%s: Can't set the time of %s\n",
00677 prog_name, dst);
00678 }
00679 }
00680 }
00681
00682 void remove1(const char *src, struct stat *srcst)
00683 {
00684 if (iflag || (!fflag && !writable(srcst))) {
00685 fprintf(stderr, "Remove %s? (mode = %03o) ", src,
00686 srcst->st_mode & 07777);
00687 if (!affirmative()) return;
00688 }
00689 if (unlink(src) < 0) {
00690 report(src);
00691 } else {
00692 if (vflag) printf("rm %s\n", src);
00693 }
00694 }
00695
00696 void link1(const char *src, const char *dst, struct stat *srcst,
00697 struct stat *dstst)
00698 {
00699 pathname_t sym;
00700 const char *p;
00701
00702 if (dstst->st_ino != 0 && (iflag || fflag)) {
00703 if (srcst->st_ino == dstst->st_ino) {
00704 if (fflag) return;
00705 fprintf(stderr, "%s: Can't link %s onto itself\n",
00706 prog_name, src);
00707 ex_code= 1;
00708 return;
00709 }
00710 if (iflag) {
00711 fprintf(stderr, "Remove %s? ", dst);
00712 if (!affirmative()) return;
00713 }
00714 errno= EISDIR;
00715 if (S_ISDIR(dstst->st_mode) || unlink(dst) < 0) {
00716 report(dst);
00717 return;
00718 }
00719 }
00720
00721 if (!sflag && !(rflag && S_ISLNK(srcst->st_mode)) && !(Sflag && xdev)) {
00722
00723 if (link(src, dst) < 0) {
00724 if (!Sflag || errno != EXDEV) {
00725 report2(src, dst);
00726 return;
00727 }
00728
00729 xdev= 1;
00730 } else {
00731 if (vflag) printf("ln %s..\n", src);
00732 return;
00733 }
00734 }
00735
00736
00737 if (!rflag && !Sflag) {
00738
00739 if (symlink(src, dst) < 0) {
00740 report(dst);
00741 return;
00742 }
00743 if (vflag) printf("ln -s %s %s\n", src, dst);
00744 return;
00745 }
00746
00747
00748 if (S_ISLNK(srcst->st_mode)) {
00749 int r;
00750 char buf[1024+1];
00751
00752 if ((r= readlink(src, buf, sizeof(buf)-1)) < 0) {
00753 report(src);
00754 return;
00755 }
00756 buf[r]= 0;
00757 if (symlink(buf, dst) < 0) {
00758 report(dst);
00759 return;
00760 }
00761 if (vflag) printf("ln -s %s %s\n", buf, dst);
00762 return;
00763 }
00764
00765
00766
00767
00768 if (dst[0] == '/' && src[0] != '/') {
00769
00770 fprintf(stderr,
00771 "%s: Symlinking %s to %s is too difficult for me to figure out\n",
00772 prog_name, src, dst);
00773 exit(1);
00774 }
00775
00776
00777
00778
00779 path_init(&sym);
00780 if (src[0] != '/') {
00781 p= dst;
00782 while (*p != 0) {
00783 if (p[0] == '.') {
00784 if (p[1] == '/' || p[1] == 0) {
00785
00786 do p++; while (*p == '/');
00787 continue;
00788 } else
00789 if (p[1] == '.' && (p[2] == '/' || p[2] == 0)) {
00790
00791 switch (path_length(&sym)) {
00792 case 0:
00793 fprintf(stderr,
00794 "%s: Symlinking %s to %s is too difficult for me to figure out\n",
00795 prog_name, src, dst);
00796 exit(1);
00797 case 2:
00798 path_trunc(&sym, 0);
00799 break;
00800 default:
00801 path_trunc(&sym, path_length(&sym) - 3);
00802 }
00803 p++;
00804 do p++; while (*p == '/');
00805 continue;
00806 }
00807 }
00808 while (*p != 0 && *p != '/') p++;
00809 while (*p == '/') p++;
00810 if (*p == 0) break;
00811 path_add(&sym, "..");
00812 }
00813 }
00814 path_add(&sym, src);
00815
00816 if (symlink(path_name(&sym), dst) < 0) {
00817 report(dst);
00818 } else {
00819 if (vflag) printf("ln -s %s %s\n", path_name(&sym), dst);
00820 }
00821 path_drop(&sym);
00822 }
00823
00824 typedef struct entrylist {
00825 struct entrylist *next;
00826 char *name;
00827 } entrylist_t;
00828
00829 int eat_dir(const char *dir, entrylist_t **dlist)
00830
00831 {
00832 DIR *dp;
00833 struct dirent *entry;
00834
00835 if ((dp= opendir(dir)) == nil) return 0;
00836
00837 while ((entry= readdir(dp)) != nil) {
00838 if (strcmp(entry->d_name, ".") == 0) continue;
00839 if (strcmp(entry->d_name, "..") == 0) continue;
00840
00841 *dlist= allocate(nil, sizeof(**dlist));
00842 (*dlist)->name= allocate(nil, strlen(entry->d_name)+1);
00843 strcpy((*dlist)->name, entry->d_name);
00844 dlist= &(*dlist)->next;
00845 }
00846 closedir(dp);
00847 *dlist= nil;
00848 return 1;
00849 }
00850
00851 void chop_dlist(entrylist_t **dlist)
00852
00853 {
00854 entrylist_t *junk= *dlist;
00855
00856 *dlist= junk->next;
00857 deallocate(junk->name);
00858 deallocate(junk);
00859 }
00860
00861 void drop_dlist(entrylist_t *dlist)
00862
00863 {
00864 while (dlist != nil) chop_dlist(&dlist);
00865 }
00866
00867 void do1(pathname_t *src, pathname_t *dst, int depth)
00868
00869 {
00870 size_t slashsrc, slashdst;
00871 struct stat srcst, dstst;
00872 entrylist_t *dlist;
00873 static ino_t topdst_ino;
00874 static dev_t topdst_dev;
00875 static dev_t topsrc_dev;
00876
00877 #if DEBUG
00878 if (vflag && depth == 0) {
00879 char flags[100], *pf= flags;
00880
00881 if (pflag) *pf++= 'p';
00882 if (iflag) *pf++= 'i';
00883 if (fflag) *pf++= 'f';
00884 if (sflag) *pf++= 's';
00885 if (Sflag) *pf++= 'S';
00886 if (mflag) *pf++= 'm';
00887 if (rflag) *pf++= 'r';
00888 if (vflag) *pf++= 'v';
00889 if (xflag) *pf++= 'x';
00890 if (expand) *pf++= 'L';
00891 if (conforming) *pf++= 'C';
00892 *pf= 0;
00893 printf(": %s -%s %s %s\n", prog_name, flags,
00894 path_name(src), path_name(dst));
00895 }
00896 #endif
00897
00898
00899 srcst.st_ino= 0;
00900 dstst.st_ino= 0;
00901
00902 if (action != LINK || !sflag || rflag) {
00903
00904 if ((expand ? stat : lstat)(path_name(src), &srcst) < 0) {
00905 report(path_name(src));
00906 return;
00907 }
00908 }
00909
00910 if (depth == 0) {
00911
00912
00913
00914 xdev= 0;
00915 topdst_ino= 0;
00916 topsrc_dev= srcst.st_dev;
00917 }
00918
00919
00920 if (action != REMOVE) {
00921 if ((expand ? stat : lstat)(path_name(dst), &dstst) < 0) {
00922 if (errno != ENOENT) {
00923 report(path_name(dst));
00924 return;
00925 }
00926 }
00927 }
00928
00929 if (action == MOVE && !xdev) {
00930 if (dstst.st_ino != 0 && srcst.st_dev != dstst.st_dev) {
00931
00932 xdev= 1;
00933 } else
00934 if (!mflag || dstst.st_ino == 0 || !S_ISDIR(dstst.st_mode)) {
00935
00936
00937 if (srcst.st_ino == dstst.st_ino) {
00938 fprintf(stderr,
00939 "%s: Can't move %s onto itself\n",
00940 prog_name, path_name(src));
00941 ex_code= 1;
00942 return;
00943 }
00944
00945 if (dstst.st_ino != 0) {
00946 if (iflag || (!fflag && !writable(&dstst))) {
00947 fprintf(stderr,
00948 "Replace %s? (mode = %03o) ",
00949 path_name(dst),
00950 dstst.st_mode & 07777);
00951 if (!affirmative()) return;
00952 }
00953 if (!S_ISDIR(dstst.st_mode))
00954 (void) unlink(path_name(dst));
00955 }
00956
00957 if (rename(path_name(src), path_name(dst)) == 0) {
00958
00959 if (vflag) {
00960 printf("mv %s %s\n", path_name(src),
00961 path_name(dst));
00962 }
00963 return;
00964 }
00965 if (errno == EXDEV) {
00966 xdev= 1;
00967 } else {
00968 report2(path_name(src), path_name(dst));
00969 return;
00970 }
00971 }
00972 }
00973
00974 if (srcst.st_ino == 0 || !S_ISDIR(srcst.st_mode)) {
00975
00976 switch (action) {
00977 case COPY:
00978 case MOVE:
00979 copy1(path_name(src), path_name(dst), &srcst, &dstst);
00980 break;
00981 case REMOVE:
00982 remove1(path_name(src), &srcst);
00983 break;
00984 case LINK:
00985 link1(path_name(src), path_name(dst), &srcst, &dstst);
00986 break;
00987 }
00988 return;
00989 }
00990
00991
00992 if (!rflag) {
00993 errno= EISDIR;
00994 report(path_name(src));
00995 return;
00996 }
00997
00998
00999 if (action == REMOVE) {
01000 if (xflag && topsrc_dev != srcst.st_dev) {
01001
01002 return;
01003 }
01004 if (iflag) {
01005 fprintf(stderr, "Remove contents of %s? ", path_name(src));
01006 if (!affirmative()) return;
01007 }
01008 }
01009
01010
01011 if (!eat_dir(path_name(src), &dlist)) {
01012 report(path_name(src));
01013 return;
01014 }
01015
01016
01017 if (action != REMOVE && dstst.st_ino != 0 && !S_ISDIR(dstst.st_mode)) {
01018 if (action != MOVE && !fflag) {
01019 errno= ENOTDIR;
01020 report(path_name(dst));
01021 return;
01022 }
01023 if (iflag) {
01024 fprintf(stderr, "Replace %s? ", path_name(dst));
01025 if (!affirmative()) {
01026 drop_dlist(dlist);
01027 return;
01028 }
01029 }
01030 if (unlink(path_name(dst)) < 0) {
01031 report(path_name(dst));
01032 drop_dlist(dlist);
01033 return;
01034 }
01035 dstst.st_ino= 0;
01036 }
01037
01038 if (action != REMOVE) {
01039 if (dstst.st_ino == 0) {
01040
01041 if (!pflag && conforming) srcst.st_mode&= fc_mask;
01042
01043 if (mkdir(path_name(dst), srcst.st_mode | S_IRWXU) < 0
01044 || stat(path_name(dst), &dstst) < 0) {
01045 report(path_name(dst));
01046 drop_dlist(dlist);
01047 return;
01048 }
01049 if (vflag) printf("mkdir %s\n", path_name(dst));
01050 } else {
01051
01052 if (action == MOVE && !mflag) {
01053 errno= EEXIST;
01054 report(path_name(dst));
01055 drop_dlist(dlist);
01056 return;
01057 }
01058 if (!pflag) {
01059
01060 srcst.st_mode= dstst.st_mode;
01061 srcst.st_uid= dstst.st_uid;
01062 srcst.st_gid= dstst.st_gid;
01063 srcst.st_mtime= dstst.st_mtime;
01064 }
01065 }
01066
01067 if (topdst_ino == 0) {
01068
01069 topdst_dev= dstst.st_dev;
01070 topdst_ino= dstst.st_ino;
01071 }
01072
01073 if (srcst.st_ino == topdst_ino && srcst.st_dev == topdst_dev) {
01074
01075 fprintf(stderr,
01076 "%s%s %s/ %s/: infinite recursion avoided\n",
01077 prog_name, action != MOVE ? " -r" : "",
01078 path_name(src), path_name(dst));
01079 drop_dlist(dlist);
01080 return;
01081 }
01082
01083 if (xflag && topsrc_dev != srcst.st_dev) {
01084
01085 drop_dlist(dlist);
01086 return;
01087 }
01088 }
01089
01090
01091 slashsrc= path_length(src);
01092 slashdst= path_length(dst);
01093
01094 while (dlist != nil) {
01095 path_add(src, dlist->name);
01096 if (action != REMOVE) path_add(dst, dlist->name);
01097
01098 do1(src, dst, depth+1);
01099
01100 path_trunc(src, slashsrc);
01101 path_trunc(dst, slashdst);
01102 chop_dlist(&dlist);
01103 }
01104
01105 if (action == MOVE || action == REMOVE) {
01106
01107
01108
01109 if (action == REMOVE && iflag) {
01110 fprintf(stderr, "Remove directory %s? ",
01111 path_name(src));
01112 if (!affirmative()) return;
01113 }
01114 if (rmdir(path_name(src)) < 0) {
01115 if (errno != ENOTEMPTY) report(path_name(src));
01116 return;
01117 }
01118 if (vflag) printf("rmdir %s\n", path_name(src));
01119 }
01120
01121 if (action != REMOVE) {
01122
01123 struct utimbuf ut;
01124
01125
01126 if ((pflag || !conforming)
01127 && (dstst.st_uid != srcst.st_uid
01128 || dstst.st_gid != srcst.st_gid)
01129 ) {
01130 if (chown(path_name(dst), srcst.st_uid,
01131 srcst.st_gid) < 0) {
01132 if (errno != EPERM) {
01133 report(path_name(dst));
01134 return;
01135 }
01136 }
01137 }
01138
01139
01140 if (dstst.st_mode != srcst.st_mode) {
01141 if (chmod(path_name(dst), srcst.st_mode) < 0) {
01142 report(path_name(dst));
01143 return;
01144 }
01145 }
01146
01147
01148 if (dstst.st_mtime != srcst.st_mtime) {
01149 ut.actime= action == MOVE ? srcst.st_atime : time(nil);
01150 ut.modtime= srcst.st_mtime;
01151 if (utime(path_name(dst), &ut) < 0) {
01152 if (errno != EPERM) {
01153 report(path_name(dst));
01154 return;
01155 }
01156 fprintf(stderr,
01157 "%s: Can't set the time of %s\n",
01158 prog_name, path_name(dst));
01159 }
01160 }
01161 }
01162 }
01163
01164 void usage(void)
01165 {
01166 char *flags1, *flags2;
01167
01168 switch (identity) {
01169 case CP:
01170 flags1= "pifsmrRvx";
01171 flags2= "pifsrRvx";
01172 break;
01173 case MV:
01174 flags1= "ifsmvx";
01175 flags2= "ifsvx";
01176 break;
01177 case RM:
01178 fprintf(stderr, "Usage: rm [-ifrRvx] file ...\n");
01179 exit(1);
01180 case LN:
01181 flags1= "ifsSmrRvx";
01182 flags2= "ifsSrRvx";
01183 break;
01184 case CPDIR:
01185 flags1= "ifvx";
01186 flags2= nil;
01187 break;
01188 case CLONE:
01189 flags1= "ifsSvx";
01190 flags2= nil;
01191 break;
01192 }
01193 fprintf(stderr, "Usage: %s [-%s] file1 file2\n", prog_name, flags1);
01194 if (flags2 != nil)
01195 fprintf(stderr, " %s [-%s] file ... dir\n", prog_name, flags2);
01196 exit(1);
01197 }
01198
01199 void main(int argc, char **argv)
01200 {
01201 int i;
01202 char *flags;
01203 struct stat st;
01204 pathname_t src, dst;
01205 size_t slash;
01206
01207 #if DEBUG >= 3
01208
01209 if (argc < 2) exit(-1);
01210 argv++;
01211 argc--;
01212 #endif
01213 #if DEBUG
01214 vflag= isatty(1);
01215 #endif
01216
01217
01218 prog_name= basename(argv[0]);
01219
01220
01221 if (strcmp(prog_name, "cp") == 0) {
01222 identity= CP;
01223 action= COPY;
01224 flags= "pifsmrRvx";
01225 expand= 1;
01226 } else
01227 if (strcmp(prog_name, "mv") == 0) {
01228 identity= MV;
01229 action= MOVE;
01230 flags= "ifsmvx";
01231 rflag= pflag= 1;
01232 } else
01233 if (strcmp(prog_name, "rm") == 0) {
01234 identity= RM;
01235 action= REMOVE;
01236 flags= "ifrRvx";
01237 } else
01238 if (strcmp(prog_name, "ln") == 0) {
01239 identity= LN;
01240 action= LINK;
01241 flags= "ifsSmrRvx";
01242 } else
01243 if (strcmp(prog_name, "cpdir") == 0) {
01244 identity= CPDIR;
01245 action= COPY;
01246 flags= "pifsmrRvx";
01247 rflag= mflag= pflag= 1;
01248 conforming= 0;
01249 } else
01250 if (strcmp(prog_name, "clone") == 0) {
01251 identity= CLONE;
01252 action= LINK;
01253 flags= "ifsSmrRvx";
01254 rflag= mflag= fflag= 1;
01255 } else {
01256 fprintf(stderr,
01257 "%s: Identity crisis, not called cp, mv, rm, ln, cpdir, or clone\n",
01258 prog_name);
01259 exit(1);
01260 }
01261
01262
01263 uid= geteuid();
01264 gid= getegid();
01265 istty= isatty(0);
01266 fc_mask= ~umask(0);
01267
01268
01269 i= 1;
01270 while (i < argc && argv[i][0] == '-') {
01271 char *opt= argv[i++] + 1;
01272
01273 if (opt[0] == '-' && opt[1] == 0) break;
01274
01275 while (*opt != 0) {
01276
01277 if (strchr(flags, *opt) == nil) usage();
01278
01279 switch (*opt++) {
01280 case 'p':
01281 pflag= 1;
01282 break;
01283 case 'i':
01284 iflag= 1;
01285 if (action == MOVE) fflag= 0;
01286 break;
01287 case 'f':
01288 fflag= 1;
01289 if (action == MOVE) iflag= 0;
01290 break;
01291 case 's':
01292 if (action == LINK) {
01293 sflag= 1;
01294 } else {
01295
01296 conforming= 0;
01297 }
01298 break;
01299 case 'S':
01300 Sflag= 1;
01301 break;
01302 case 'm':
01303 mflag= 1;
01304 break;
01305 case 'r':
01306 expand= 0;
01307
01308 case 'R':
01309 rflag= 1;
01310 break;
01311 case 'v':
01312 vflag= 1;
01313 break;
01314 case 'x':
01315 xflag= 1;
01316 break;
01317 default:
01318 assert(0);
01319 }
01320 }
01321 }
01322
01323 switch (action) {
01324 case REMOVE:
01325 if (i == argc) usage();
01326 break;
01327 case LINK:
01328
01329 if ((argc - i) == 1 && action == LINK) argv[argc++]= ".";
01330
01331 default:
01332 if ((argc - i) < 2) usage();
01333 }
01334
01335 path_init(&src);
01336 path_init(&dst);
01337
01338 if (action != REMOVE && !mflag
01339 && stat(argv[argc-1], &st) >= 0 && S_ISDIR(st.st_mode)
01340 ) {
01341
01342
01343
01344
01345 path_add(&dst, argv[argc-1]);
01346 slash= path_length(&dst);
01347
01348 do {
01349 path_add(&src, argv[i]);
01350 path_add(&dst, basename(argv[i]));
01351
01352 do1(&src, &dst, 0);
01353
01354 path_trunc(&src, 0);
01355 path_trunc(&dst, slash);
01356 } while (++i < argc-1);
01357 } else
01358 if (action == REMOVE || (argc - i) == 2) {
01359
01360 do {
01361 path_add(&src, argv[i]);
01362 if (action != REMOVE) path_add(&dst, argv[i+1]);
01363
01364 do1(&src, &dst, 0);
01365 path_trunc(&src, 0);
01366 } while (action == REMOVE && ++i < argc);
01367 } else {
01368 usage();
01369 }
01370 path_drop(&src);
01371 path_drop(&dst);
01372
01373 #if DEBUG
01374 if (nchunks != 0) {
01375 fprintf(stderr, "(%ld chunks of memory not freed)\n",
01376 (long) nchunks);
01377 }
01378 #endif
01379 exit(ex_code);
01380 }