home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / bin / cp / cp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-27  |  14.4 KB  |  581 lines

  1. /*
  2.  * Copyright (c) 1988 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * This code is derived from software contributed to Berkeley by
  6.  * David Hitz of Auspex Systems Inc.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  * 1. Redistributions of source code must retain the above copyright
  12.  *    notice, this list of conditions and the following disclaimer.
  13.  * 2. Redistributions in binary form must reproduce the above copyright
  14.  *    notice, this list of conditions and the following disclaimer in the
  15.  *    documentation and/or other materials provided with the distribution.
  16.  * 3. All advertising materials mentioning features or use of this software
  17.  *    must display the following acknowledgement:
  18.  *    This product includes software developed by the University of
  19.  *    California, Berkeley and its contributors.
  20.  * 4. Neither the name of the University nor the names of its contributors
  21.  *    may be used to endorse or promote products derived from this software
  22.  *    without specific prior written permission.
  23.  *
  24.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  25.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  28.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34.  * SUCH DAMAGE.
  35.  */
  36.  
  37. #ifndef lint
  38. char copyright[] =
  39. "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
  40.  All rights reserved.\n";
  41. #endif /* not lint */
  42.  
  43. #ifndef lint
  44. static char sccsid[] = "@(#)cp.c    5.26 (Berkeley) 10/27/91";
  45. #endif /* not lint */
  46.  
  47. /*
  48.  * cp copies source files to target files.
  49.  * 
  50.  * The global PATH_T structures "to" and "from" always contain paths to the
  51.  * current source and target files, respectively.  Since cp does not change
  52.  * directories, these paths can be either absolute or dot-realative.
  53.  * 
  54.  * The basic algorithm is to initialize "to" and "from", and then call the
  55.  * recursive copy() function to do the actual work.  If "from" is a file,
  56.  * copy copies the data.  If "from" is a directory, copy creates the
  57.  * corresponding "to" directory, and calls itself recursively on all of
  58.  * the entries in the "from" directory.
  59.  */
  60.  
  61. #include <sys/param.h>
  62. #include <sys/stat.h>
  63. #include <sys/mman.h>
  64. #include <sys/time.h>
  65. #include <dirent.h>
  66. #include <fcntl.h>
  67. #include <errno.h>
  68. #include <unistd.h>
  69. #include <stdio.h>
  70. #include <stdlib.h>
  71. #include <string.h>
  72. #include "extern.h"
  73.  
  74. static void copy __P((void));
  75. static void copy_dir __P((void));
  76. static void copy_fifo __P((struct stat *, int));
  77. static void copy_file __P((struct stat *, int));
  78. static void copy_link __P((int));
  79. static void copy_special __P((struct stat *, int));
  80. static void setfile __P((struct stat *, int));
  81. static void usage __P((void));
  82.  
  83. PATH_T from = { from.p_path, "" };
  84. PATH_T to = { to.p_path, "" };
  85.  
  86. uid_t myuid;
  87. int exit_val, myumask;
  88. int iflag, pflag, orflag, rflag;
  89. int (*statfcn)();
  90. char *progname;
  91.  
  92. main(argc, argv)
  93.     int argc;
  94.     char **argv;
  95. {
  96.     extern int optind;
  97.     struct stat to_stat;
  98.     register int c, r;
  99.     int symfollow, lstat(), stat();
  100.     char *old_to, *p;
  101.  
  102.     /*
  103.      * The utility cp(1) is used by mv(1) -- except for usage statements,
  104.      * print the "called as" program name.
  105.      */
  106.     progname = (p = rindex(*argv,'/')) ? ++p : *argv;
  107.  
  108.     symfollow = 0;
  109.     while ((c = getopt(argc, argv, "Rfhipr")) != EOF) {
  110.     switch ((char)c) {
  111.         case 'f':
  112.             iflag = 0;
  113.             break;
  114.         case 'h':
  115.             symfollow = 1;
  116.             break;
  117.         case 'i':
  118.             iflag = isatty(fileno(stdin));
  119.             break;
  120.         case 'p':
  121.             pflag = 1;
  122.             break;
  123.         case 'R':
  124.             rflag = 1;
  125.             break;
  126.         case 'r':
  127.             orflag = 1;
  128.             break;
  129.         case '?':
  130.         default:
  131.             usage();
  132.             break;
  133.         }
  134.     }
  135.     argc -= optind;
  136.     argv += optind;
  137.  
  138.     if (argc < 2)
  139.         usage();
  140.  
  141.     if (rflag && orflag) {
  142.         (void)fprintf(stderr,
  143.             "cp: the -R and -r options are mutually exclusive.\n");
  144.         exit(1);
  145.     }
  146.  
  147.     myuid = getuid();
  148.  
  149.     /* copy the umask for explicit mode setting */
  150.     myumask = umask(0);
  151.     (void)umask(myumask);
  152.  
  153.     /* consume last argument first. */
  154.     if (!path_set(&to, argv[--argc]))
  155.         exit(1);
  156.  
  157.     statfcn = symfollow || !rflag ? stat : lstat;
  158.  
  159.     /*
  160.      * Cp has two distinct cases:
  161.      *
  162.      * % cp [-rip] source target
  163.      * % cp [-rip] source1 ... directory
  164.      *
  165.      * In both cases, source can be either a file or a directory.
  166.      *
  167.      * In (1), the target becomes a copy of the source. That is, if the
  168.      * source is a file, the target will be a file, and likewise for
  169.      * directories.
  170.      *
  171.      * In (2), the real target is not directory, but "directory/source".
  172.      */
  173.  
  174.     r = stat(to.p_path, &to_stat);
  175.     if (r == -1 && errno != ENOENT) {
  176.         err("%s: %s", to.p_path, strerror(errno));
  177.         exit(1);
  178.     }
  179.     if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
  180.         /*
  181.          * Case (1).  Target is not a directory.
  182.          */
  183.         if (argc > 1) {
  184.             usage();
  185.             exit(1);
  186.         }
  187.         if (!path_set(&from, *argv))
  188.             exit(1);
  189.         copy();
  190.     }
  191.     else {
  192.         /*
  193.          * Case (2).  Target is a directory.
  194.          */
  195.         for (;; ++argv) {
  196.             if (!path_set(&from, *argv))
  197.                 continue;
  198.             if (!(old_to =
  199.                 path_append(&to, path_basename(&from), -1)))
  200.                 continue;
  201.             copy();
  202.             if (!--argc)
  203.                 break;
  204.             path_restore(&to, old_to);
  205.         }
  206.     }
  207.     exit(exit_val);
  208. }
  209.  
  210. /* copy file or directory at "from" to "to". */
  211. static void
  212. copy()
  213. {
  214.     struct stat from_stat, to_stat;
  215.     int dne, statval;
  216.  
  217.     statval = statfcn(from.p_path, &from_stat);
  218.     if (statval == -1) {
  219.         err("%s: %s", from.p_path, strerror(errno));
  220.         return;
  221.     }
  222.  
  223.     /* not an error, but need to remember it happened */
  224.     if (stat(to.p_path, &to_stat) == -1)
  225.         dne = 1;
  226.     else {
  227.         if (to_stat.st_dev == from_stat.st_dev &&
  228.             to_stat.st_ino == from_stat.st_ino) {
  229.             (void)fprintf(stderr,
  230.                 "%s: %s and %s are identical (not copied).\n",
  231.                 progname, to.p_path, from.p_path);
  232.             exit_val = 1;
  233.             return;
  234.         }
  235.         dne = 0;
  236.     }
  237.  
  238.     switch(from_stat.st_mode & S_IFMT) {
  239.     case S_IFLNK:
  240.         copy_link(!dne);
  241.         return;
  242.     case S_IFDIR:
  243.         if (!rflag && !orflag) {
  244.             (void)fprintf(stderr,
  245.                 "%s: %s is a directory (not copied).\n",
  246.                 progname, from.p_path);
  247.             exit_val = 1;
  248.             return;
  249.         }
  250.         if (dne) {
  251.             /*
  252.              * If the directory doesn't exist, create the new
  253.              * one with the from file mode plus owner RWX bits,
  254.              * modified by the umask.  Trade-off between being
  255.              * able to write the directory (if from directory is
  256.              * 555) and not causing a permissions race.  If the
  257.              * umask blocks owner writes cp fails.
  258.              */
  259.             if (mkdir(to.p_path, from_stat.st_mode|S_IRWXU) < 0) {
  260.                 err("%s: %s", to.p_path, strerror(errno));
  261.                 return;
  262.             }
  263.         }
  264.         else if (!S_ISDIR(to_stat.st_mode)) {
  265.             (void)fprintf(stderr, "%s: %s: not a directory.\n",
  266.                 progname, to.p_path);
  267.             return;
  268.         }
  269.         copy_dir();
  270.         /*
  271.          * If not -p and directory didn't exist, set it to be the
  272.          * same as the from directory, umodified by the umask;
  273.          * arguably wrong, but it's been that way forever.
  274.          */
  275.         if (pflag)
  276.             setfile(&from_stat, 0);
  277.         else if (dne)
  278.             (void)chmod(to.p_path, from_stat.st_mode);
  279.         return;
  280.     case S_IFCHR:
  281.     case S_IFBLK:
  282.         if (rflag) {
  283.             copy_special(&from_stat, !dne);
  284.             return;
  285.         }
  286.         break;
  287.     case S_IFIFO:
  288.         if (rflag) {
  289.             copy_fifo(&from_stat, !dne);
  290.             return;
  291.         }
  292.         break;
  293.     }
  294.     copy_file(&from_stat, dne);
  295. }
  296.  
  297. static void
  298. copy_file(fs, dne)
  299.     struct stat *fs;
  300.     int dne;
  301. {
  302.     static char buf[MAXBSIZE];
  303.     register int from_fd, to_fd, rcount, wcount;
  304.     struct stat to_stat;
  305.     char *p;
  306.  
  307.     if ((from_fd = open(from.p_path, O_RDONLY, 0)) == -1) {
  308.         err("%s: %s", from.p_path, strerror(errno));
  309.         return;
  310.     }
  311.  
  312.     /*
  313.      * If the file exists and we're interactive, verify with the user.
  314.      * If the file DNE, set the mode to be the from file, minus setuid
  315.      * bits, modified by the umask; arguably wrong, but it makes copying
  316.      * executables work right and it's been that way forever.  (The
  317.      * other choice is 666 or'ed with the execute bits on the from file
  318.      * modified by the umask.)
  319.      */
  320.     if (!dne) {
  321.         if (iflag) {
  322.             int checkch, ch;
  323.  
  324.             (void)fprintf(stderr, "overwrite %s? ", to.p_path);
  325.             checkch = ch = getchar();
  326.             while (ch != '\n' && ch != EOF)
  327.                 ch = getchar();
  328.             if (checkch != 'y') {
  329.                 (void)close(from_fd);
  330.                 return;
  331.             }
  332.         }
  333.         to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0);
  334.     } else
  335.         to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC,
  336.             fs->st_mode & ~(S_ISUID|S_ISGID));
  337.  
  338.     if (to_fd == -1) {
  339.         err("%s: %s", to.p_path, strerror(errno));
  340.         (void)close(from_fd);
  341.         return;
  342.     }
  343.  
  344.     /*
  345.      * Mmap and write if less than 8M (the limit is so we don't totally
  346.      * trash memory on big files.  This is really a minor hack, but it
  347.      * wins some CPU back.
  348.      */
  349.     if (fs->st_size <= 8 * 1048576) {
  350.         if ((p = mmap(NULL, fs->st_size, PROT_READ,
  351.             MAP_FILE, from_fd, (off_t)0)) == (char *)-1)
  352.             err("%s: %s", from.p_path, strerror(errno));
  353.         if (write(to_fd, p, fs->st_size) != fs->st_size)
  354.             err("%s: %s", to.p_path, strerror(errno));
  355.     } else {
  356.         while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
  357.             wcount = write(to_fd, buf, rcount);
  358.             if (rcount != wcount || wcount == -1) {
  359.                 err("%s: %s", to.p_path, strerror(errno));
  360.                 break;
  361.             }
  362.         }
  363.         if (rcount < 0)
  364.             err("%s: %s", from.p_path, strerror(errno));
  365.     }
  366.     if (pflag)
  367.         setfile(fs, to_fd);
  368.     /*
  369.      * If the source was setuid or setgid, lose the bits unless the
  370.      * copy is owned by the same user and group.
  371.      */
  372.     else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid)
  373.         if (fstat(to_fd, &to_stat))
  374.             err("%s: %s", to.p_path, strerror(errno));
  375. #define    RETAINBITS    (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
  376.         else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd,
  377.             fs->st_mode & RETAINBITS & ~myumask))
  378.             err("%s: %s", to.p_path, strerror(errno));
  379.     (void)close(from_fd);
  380.     if (close(to_fd))
  381.         err("%s: %s", to.p_path, strerror(errno));
  382. }
  383.  
  384. static void
  385. copy_dir()
  386. {
  387.     struct stat from_stat;
  388.     struct dirent *dp, **dir_list;
  389.     register int dir_cnt, i;
  390.     char *old_from, *old_to;
  391.  
  392.     dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL);
  393.     if (dir_cnt == -1) {
  394.         (void)fprintf(stderr, "%s: can't read directory %s.\n",
  395.             progname, from.p_path);
  396.         exit_val = 1;
  397.     }
  398.  
  399.     /*
  400.      * Instead of handling directory entries in the order they appear
  401.      * on disk, do non-directory files before directory files.
  402.      * There are two reasons to do directories last.  The first is
  403.      * efficiency.  Files tend to be in the same cylinder group as
  404.      * their parent, whereas directories tend not to be.  Copying files
  405.      * all at once reduces seeking.  Second, deeply nested tree's
  406.      * could use up all the file descriptors if we didn't close one
  407.      * directory before recursivly starting on the next.
  408.      */
  409.     /* copy files */
  410.     for (i = 0; i < dir_cnt; ++i) {
  411.         dp = dir_list[i];
  412.         if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
  413.             && (dp->d_name[1] == NULL || dp->d_name[1] == '.'))
  414.             goto done;
  415.         if (!(old_from =
  416.             path_append(&from, dp->d_name, (int)dp->d_namlen)))
  417.             goto done;
  418.  
  419.         if (statfcn(from.p_path, &from_stat) < 0) {
  420.             err("%s: %s", dp->d_name, strerror(errno));
  421.             path_restore(&from, old_from);
  422.             goto done;
  423.         }
  424.         if (S_ISDIR(from_stat.st_mode)) {
  425.             path_restore(&from, old_from);
  426.             continue;
  427.         }
  428.         if (old_to = path_append(&to, dp->d_name, (int)dp->d_namlen)) {
  429.             copy();
  430.             path_restore(&to, old_to);
  431.         }
  432.         path_restore(&from, old_from);
  433. done:        dir_list[i] = NULL;
  434.         free(dp);
  435.     }
  436.  
  437.     /* copy directories */
  438.     for (i = 0; i < dir_cnt; ++i) {
  439.         dp = dir_list[i];
  440.         if (!dp)
  441.             continue;
  442.         if (!(old_from =
  443.             path_append(&from, dp->d_name, (int)dp->d_namlen))) {
  444.             free(dp);
  445.             continue;
  446.         }
  447.         if (!(old_to =
  448.             path_append(&to, dp->d_name, (int)dp->d_namlen))) {
  449.             free(dp);
  450.             path_restore(&from, old_from);
  451.             continue;
  452.         }
  453.         copy();
  454.         free(dp);
  455.         path_restore(&from, old_from);
  456.         path_restore(&to, old_to);
  457.     }
  458.     free(dir_list);
  459. }
  460.  
  461. static void
  462. copy_link(exists)
  463.     int exists;
  464. {
  465.     int len;
  466.     char link[MAXPATHLEN];
  467.  
  468.     if ((len = readlink(from.p_path, link, sizeof(link))) == -1) {
  469.         err("readlink: %s: %s", from.p_path, strerror(errno));
  470.         return;
  471.     }
  472.     link[len] = '\0';
  473.     if (exists && unlink(to.p_path)) {
  474.         err("unlink: %s: %s", to.p_path, strerror(errno));
  475.         return;
  476.     }
  477.     if (symlink(link, to.p_path)) {
  478.         err("symlink: %s: %s", link, strerror(errno));
  479.         return;
  480.     }
  481. }
  482.  
  483. static void
  484. copy_fifo(from_stat, exists)
  485.     struct stat *from_stat;
  486.     int exists;
  487. {
  488.     if (exists && unlink(to.p_path)) {
  489.         err("unlink: %s: %s", to.p_path, strerror(errno));
  490.         return;
  491.     }
  492.     if (mkfifo(to.p_path, from_stat->st_mode)) {
  493.         err("mkfifo: %s: %s", to.p_path, strerror(errno));
  494.         return;
  495.     }
  496.     if (pflag)
  497.         setfile(from_stat, 0);
  498. }
  499.  
  500. static void
  501. copy_special(from_stat, exists)
  502.     struct stat *from_stat;
  503.     int exists;
  504. {
  505.     if (exists && unlink(to.p_path)) {
  506.         err("unlink: %s: %s", to.p_path, strerror(errno));
  507.         return;
  508.     }
  509.     if (mknod(to.p_path, from_stat->st_mode,  from_stat->st_rdev)) {
  510.         err("mknod: %s: %s", to.p_path, strerror(errno));
  511.         return;
  512.     }
  513.     if (pflag)
  514.         setfile(from_stat, 0);
  515. }
  516.  
  517. static void
  518. setfile(fs, fd)
  519.     register struct stat *fs;
  520.     int fd;
  521. {
  522.     static struct timeval tv[2];
  523.  
  524.     fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
  525.  
  526.     tv[0].tv_sec = fs->st_atime;
  527.     tv[1].tv_sec = fs->st_mtime;
  528.     if (utimes(to.p_path, tv))
  529.         err("utimes: %s: %s", to.p_path, strerror(errno));
  530.     /*
  531.      * Changing the ownership probably won't succeed, unless we're root
  532.      * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
  533.      * the mode; current BSD behavior is to remove all setuid bits on
  534.      * chown.  If chown fails, lose setuid/setgid bits.
  535.      */
  536.     if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
  537.         chown(to.p_path, fs->st_uid, fs->st_gid)) {
  538.         if (errno != EPERM)
  539.             err("chown: %s: %s", to.p_path, strerror(errno));
  540.         fs->st_mode &= ~(S_ISUID|S_ISGID);
  541.     }
  542.     if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode))
  543.         err("chown: %s: %s", to.p_path, strerror(errno));
  544. }
  545.  
  546. static void
  547. usage()
  548. {
  549.     (void)fprintf(stderr,
  550. "usage: cp [-Rfhip] src target;\n       cp [-Rfhip] src1 ... srcN directory\n");
  551.     exit(1);
  552. }
  553.  
  554. #if __STDC__
  555. #include <stdarg.h>
  556. #else
  557. #include <varargs.h>
  558. #endif
  559.  
  560. void
  561. #if __STDC__
  562. err(const char *fmt, ...)
  563. #else
  564. err(fmt, va_alist)
  565.     char *fmt;
  566.         va_dcl
  567. #endif
  568. {
  569.     va_list ap;
  570. #if __STDC__
  571.     va_start(ap, fmt);
  572. #else
  573.     va_start(ap);
  574. #endif
  575.     (void)fprintf(stderr, "%s: ", progname);
  576.     (void)vfprintf(stderr, fmt, ap);
  577.     va_end(ap);
  578.     (void)fprintf(stderr, "\n");
  579.     exit_val = 1;
  580. }
  581.