cp.c

Go to the documentation of this file.
00001 /*      cp 1.12 - copy files                            Author: Kees J. Bot
00002  *      mv      - move files                                    20 Jul 1993
00003  *      rm      - remove files
00004  *      ln      - make a link
00005  *      cpdir   - copy a directory tree (cp -psmr)
00006  *      clone   - make a link farm (ln -fmr)
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 /* Copy files in this size chunks: */
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       /* Precisely POSIX conforming. */
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;                /* Call name of this program. */
00046 int ex_code= 0;                 /* Final exit code. */
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;            /* How did the user call me? */
00052 action_t action;                /* Copying, moving, or linking. */
00053 int pflag= 0;                   /* -p/-s: Make orginal and copy the same. */
00054 int iflag= 0;                   /* -i: Interactive overwriting/deleting. */
00055 int fflag= 0;                   /* -f: Force. */
00056 int sflag= 0;                   /* -s: Make a symbolic link (ln/clone). */
00057 int Sflag= 0;                   /* -S: Make a symlink if across devices. */
00058 int mflag= 0;                   /* -m: Merge trees, no target dir trickery. */
00059 int rflag= 0;                   /* -r/-R: Recursively copy a tree. */
00060 int vflag= 0;                   /* -v: Verbose. */
00061 int xflag= 0;                   /* -x: Don't traverse past mount points. */
00062 int xdev= 0;                    /* Set when moving or linking cross-device. */
00063 int expand= 0;                  /* Expand symlinks, ignore links. */
00064 int conforming= CONFORMING;     /* Sometimes standards are a pain. */
00065 
00066 int fc_mask;                    /* File creation mask. */
00067 int uid, gid;                   /* Effective uid & gid. */
00068 int istty;                      /* Can have terminal input. */
00069 
00070 #ifndef S_ISLNK
00071 /* There were no symlinks in medieval times. */
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;      /* Number of allocated cells. */
00099 #endif
00100 
00101 void *allocate(void *mem, size_t size)
00102 /* Like realloc, but with checking of the return value. */
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 /* Release a chunk of memory. */
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;  /* The actual pathname. */
00125     size_t              idx;    /* Index for the terminating null byte. */
00126     size_t              lim;    /* Actual length of the path array. */
00127 } pathname_t;
00128 
00129 void path_init(pathname_t *pp)
00130 /* Initialize a pathname to the null string. */
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 /* Add a component to a pathname. */
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;  /* add an extra 50% growing space. */
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 /* Delete part of a pathname to a remembered length. */
00163 {
00164     pp->path[pp->idx= didx]= 0;
00165 }
00166 
00167 #if DEBUG
00168 const char *path_name(const pathname_t *pp)
00169 /* Return the actual name as a C string. */
00170 {
00171     return pp->path;
00172 }
00173 
00174 size_t path_length(const pathname_t *pp)
00175 /* The length of the pathname. */
00176 {
00177     return pp->idx;
00178 }
00179 
00180 void path_drop(pathname_t *pp)
00181 /* Release the storage occupied by the pathname. */
00182 {
00183     deallocate(pp->path);
00184 }
00185 
00186 #else /* !DEBUG */
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 /* !DEBUG */
00191 
00192 char *basename(const char *path)
00193 /* Return the last component of a pathname.  (Note: declassifies a const
00194  * char * just like strchr.
00195  */
00196 {
00197     const char *p= path;
00198 
00199     for (;;) {
00200         while (*p == '/') p++;                  /* Trailing slashes? */
00201 
00202         if (*p == 0) break;
00203 
00204         path= p;
00205         while (*p != 0 && *p != '/') p++;       /* Skip component. */
00206     }
00207     return (char *) path;
00208 }
00209 
00210 int affirmative(void)
00211 /* Get a yes/no answer from the suspecting user. */
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 /* True iff the file with the given attributes allows writing.  (And we have
00228  * a terminal to ask if ok to overwrite.)
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     /* Tell if a file, which stat(2) information in '*stp', has been seen
00244      * earlier by this function under a different name.  If not return a
00245      * null pointer with errno set to ENOENT, otherwise return the name of
00246      * the link.  Return a null pointer with an error code in errno for any
00247      * error, using E2BIG for a too long file name.
00248      *
00249      * Use link_islink(nil, nil) to reset all bookkeeping.
00250      *
00251      * Call for a file twice to delete it from the store.
00252      */
00253 
00254     typedef struct link {       /* In-memory link store. */
00255         struct link     *next;          /* Hash chain on inode number. */
00256         ino_t           ino;            /* File's inode number. */
00257         off_t           off;            /* Offset to more info in temp file. */
00258     } link_t;
00259     typedef struct dlink {      /* On-disk link store. */
00260         dev_t           dev;            /* Device number. */
00261         char            file[PATH_MAX]; /* Name of earlier seen link. */
00262     } dlink_t;
00263     static link_t *links[256];          /* Hash list of known links. */
00264     static int tfd= -1;                 /* Temp file for file name storage. */
00265     static dlink_t dlink;
00266     link_t *lp, **plp;
00267     size_t len;
00268     off_t off;
00269 
00270     if (file == nil) {
00271         /* Reset everything. */
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     /* The file must be a non-directory with more than one link. */
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             /* May have seen this link before.  Get it and check. */
00294             if (lseek(tfd, lp->off, SEEK_SET) == -1) return nil;
00295             if (read(tfd, &dlink, sizeof(dlink)) < 0) return nil;
00296 
00297             /* Only need to check the device number. */
00298             if (dlink.dev == stp->st_dev) {
00299                 if (strcmp(file, dlink.file) == 0) {
00300                     /* Called twice.  Forget about this link. */
00301                     *plp= lp->next;
00302                     free(lp);
00303                     errno= ENOENT;
00304                     return nil;
00305                 }
00306 
00307                 /* Return the name of the earlier link. */
00308                 return dlink.file;
00309             }
00310         }
00311         plp= &lp->next;
00312     }
00313 
00314     /* First time I see this link.  Add it to the store. */
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 /* Keep the link structure intact if src has been seen before. */
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         /* if (errno != ENOENT) ... */
00360         return 0;
00361     }
00362 
00363     /* Try to link the file copied earlier to the new file. */
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 /* Copy one file to another and copy (some of) the attributes. */
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         /* The file doesn't exist yet. */
00384 
00385         if (!S_ISREG(srcst->st_mode)) {
00386             /* Making a new mode 666 regular file. */
00387             srcst->st_mode= (S_IFREG | 0666) & fc_mask;
00388         } else
00389         if (!pflag && conforming) {
00390             /* Making a new file copying mode with umask applied. */
00391             srcst->st_mode &= fc_mask;
00392         }
00393     } else {
00394         /* File exists, ask if ok to overwrite if '-i'. */
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             /* Don't overwrite, remove first. */
00404             if (unlink(dst) < 0 && errno != ENOENT) {
00405                 report(dst);
00406                 return 0;
00407             }
00408         } else {
00409             /* Overwrite. */
00410             if (!pflag) {
00411                 /* Keep the existing mode and ownership. */
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     /* Keep the link structure if possible. */
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         /* Retry adding a "w" bit. */
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         /* Retry after trying to delete. */
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     /* Get current parameters. */
00445     if (fstat(dstfd, dstst) < 0) {
00446         report(dst);
00447         close(srcfd);
00448         close(dstfd);
00449         return 0;
00450     }
00451 
00452     /* Copy the little bytes themselves. */
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     /* Copy the ownership. */
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         /* Suid bits must be cleared in the holy name of
00505          * security (and the assumed user stupidity).
00506          */
00507         srcst->st_mode&= ~06000;
00508     }
00509 
00510     /* Copy the mode. */
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     /* Copy the file modification time. */
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 /* Inspect the source file and then copy it.  Treatment of symlinks and
00549  * special files is a bit complicated.  The filetype and link-structure are
00550  * ignored if (expand && !rflag), symlinks and link-structure are ignored
00551  * if (expand && rflag), everything is copied precisely if !expand.
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     /* You can forget it if the destination is a directory. */
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     /* Apply the file creation mask if so required. */
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);     /* Don't want it twice. */
00645         return;
00646     }
00647 
00648     if (linked) return;
00649 
00650     if (S_ISLNK(srcst->st_mode)) return;
00651 
00652     /* Copy the ownership. */
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     /* Copy the file modification time. */
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         /* A normal link. */
00723         if (link(src, dst) < 0) {
00724             if (!Sflag || errno != EXDEV) {
00725                 report2(src, dst);
00726                 return;
00727             }
00728             /* Can't do a cross-device link, we have to symlink. */
00729             xdev= 1;
00730         } else {
00731             if (vflag) printf("ln %s..\n", src);
00732             return;
00733         }
00734     }
00735 
00736     /* Do a symlink. */
00737     if (!rflag && !Sflag) {
00738         /* We can get away with a "don't care if it works" symlink. */
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     /* If the source is a symlink then it is simply copied. */
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     /* Make a symlink that has to work, i.e. we must be able to access the
00766      * source now, and the link must work.
00767      */
00768     if (dst[0] == '/' && src[0] != '/') {
00769         /* ln -[rsS] relative/path /full/path. */
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     /* Count the number of subdirectories in the destination file and
00777      * add one '..' for each.
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                     /* A "." component; skip. */
00786                     do p++; while (*p == '/');
00787                     continue;
00788                 } else
00789                 if (p[1] == '.' && (p[2] == '/' || p[2] == 0)) {
00790                     /* A ".." component; oops. */
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 /* Make a linked list of all the names in a directory. */
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 /* Chop an entry of a name list. */
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 /* Get rid of a whole list. */
00863 {
00864     while (dlist != nil) chop_dlist(&dlist);
00865 }
00866 
00867 void do1(pathname_t *src, pathname_t *dst, int depth)
00868 /* Perform the appropriate action on a source and destination file. */
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     /* st_ino == 0 if not stat()'ed yet, or nonexistent. */
00899     srcst.st_ino= 0;
00900     dstst.st_ino= 0;
00901 
00902     if (action != LINK || !sflag || rflag) {
00903         /* Source must exist unless symlinking. */
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         /* First call: Not cross-device yet, first dst not seen yet,
00912          * remember top device number.
00913          */
00914         xdev= 0;
00915         topdst_ino= 0;
00916         topsrc_dev= srcst.st_dev;
00917     }
00918 
00919     /* Inspect the intended destination unless removing. */
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             /* It's a cross-device rename, i.e. copy and remove. */
00932             xdev= 1;
00933         } else
00934         if (!mflag || dstst.st_ino == 0 || !S_ISDIR(dstst.st_mode)) {
00935             /* Try to simply rename the file (not merging trees). */
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                 /* Success. */
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         /* Copy/move/remove/link a single file. */
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     /* Recursively copy/move/remove/link a directory if -r or -R. */
00992     if (!rflag) {
00993         errno= EISDIR;
00994         report(path_name(src));
00995         return;
00996     }
00997 
00998     /* Ok to remove contents of dir? */
00999     if (action == REMOVE) {
01000         if (xflag && topsrc_dev != srcst.st_dev) {
01001             /* Don't recurse past a mount point. */
01002             return;
01003         }
01004         if (iflag) {
01005             fprintf(stderr, "Remove contents of %s? ", path_name(src));
01006             if (!affirmative()) return;
01007         }
01008     }
01009 
01010     /* Gather the names in the source directory. */
01011     if (!eat_dir(path_name(src), &dlist)) {
01012         report(path_name(src));
01013         return;
01014     }
01015 
01016     /* Check/create the target directory. */
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             /* Create a new target directory. */
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             /* Target directory already exists. */
01052             if (action == MOVE && !mflag) {
01053                 errno= EEXIST;
01054                 report(path_name(dst));
01055                 drop_dlist(dlist);
01056                 return;
01057             }
01058             if (!pflag) {
01059                 /* Keep the existing attributes. */
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             /* Remember the top destination. */
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             /* E.g. cp -r /shallow /shallow/deep. */
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             /* Don't recurse past a mount point. */
01085             drop_dlist(dlist);
01086             return;
01087         }
01088     }
01089 
01090     /* Go down. */
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         /* The contents of the source directory should have
01107          * been (re)moved above.  Get rid of the empty dir.
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         /* Set the attributes of a new directory. */
01123         struct utimbuf ut;
01124 
01125         /* Copy the ownership. */
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         /* Copy the mode. */
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         /* Copy the file modification time. */
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     /* The first argument is the call name while debugging. */
01209     if (argc < 2) exit(-1);
01210     argv++;
01211     argc--;
01212 #endif
01213 #if DEBUG
01214     vflag= isatty(1);
01215 #endif
01216 
01217     /* Call name of this program. */
01218     prog_name= basename(argv[0]);
01219 
01220     /* Required action. */
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     /* Who am I?, where am I?, how protective am I? */
01263     uid= geteuid();
01264     gid= getegid();
01265     istty= isatty(0);
01266     fc_mask= ~umask(0);
01267 
01268     /* Gather flags. */
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             /* Flag supported? */
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                     /* Forget about POSIX, do it right. */
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                 /*FALL THROUGH*/
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         /* 'ln dir/file' is to be read as 'ln dir/file .'. */
01329         if ((argc - i) == 1 && action == LINK) argv[argc++]= ".";
01330         /*FALL THROUGH*/
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         /* The last argument is a directory, this means we have to
01342          * throw the whole lot into this directory.  This is the
01343          * Right Thing unless you use -r.
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         /* Just two files (or many files for rm). */
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 }

Generated on Fri Apr 14 22:57:04 2006 for minix by  doxygen 1.4.6