Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:19

0001 /* $NetBSD: cp.c,v 1.39 2005/10/24 12:59:07 kleink Exp $ */
0002 
0003 /*
0004  * Copyright (c) 1988, 1993, 1994
0005  *  The Regents of the University of California.  All rights reserved.
0006  *
0007  * This code is derived from software contributed to Berkeley by
0008  * David Hitz of Auspex Systems Inc.
0009  *
0010  * Redistribution and use in source and binary forms, with or without
0011  * modification, are permitted provided that the following conditions
0012  * are met:
0013  * 1. Redistributions of source code must retain the above copyright
0014  *    notice, this list of conditions and the following disclaimer.
0015  * 2. Redistributions in binary form must reproduce the above copyright
0016  *    notice, this list of conditions and the following disclaimer in the
0017  *    documentation and/or other materials provided with the distribution.
0018  * 3. Neither the name of the University nor the names of its contributors
0019  *    may be used to endorse or promote products derived from this software
0020  *    without specific prior written permission.
0021  *
0022  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
0023  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0024  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0025  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
0026  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0027  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0028  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0029  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0030  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0031  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0032  * SUCH DAMAGE.
0033  */
0034 
0035 #ifdef HAVE_CONFIG_H
0036 #include "config.h"
0037 #endif
0038 
0039 #if 0
0040 #ifndef lint
0041 __COPYRIGHT(
0042 "@(#) Copyright (c) 1988, 1993, 1994\n\
0043   The Regents of the University of California.  All rights reserved.\n");
0044 #endif /* not lint */
0045 
0046 #ifndef lint
0047 #if 0
0048 static char sccsid[] = "@(#)cp.c  8.5 (Berkeley) 4/29/95";
0049 #else
0050 __RCSID("$NetBSD: cp.c,v 1.39 2005/10/24 12:59:07 kleink Exp $");
0051 #endif
0052 #endif /* not lint */
0053 #endif
0054 /*
0055  * Cp copies source files to target files.
0056  *
0057  * The global PATH_T structure "to" always contains the path to the
0058  * current target file.  Since fts(3) does not change directories,
0059  * this path can be either absolute or dot-relative.
0060  *
0061  * The basic algorithm is to initialize "to" and use fts(3) to traverse
0062  * the file hierarchy rooted in the argument list.  A trivial case is the
0063  * case of 'cp file1 file2'.  The more interesting case is the case of
0064  * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
0065  * path (relative to the root of the traversal) is appended to dir (stored
0066  * in "to") to form the final target path.
0067  */
0068 
0069 #include <rtems.h>
0070 #include <rtems/shell.h>
0071 #include <rtems/shellconfig.h>
0072 #define __need_getopt_newlib
0073 #include <getopt.h>
0074 
0075 #include <sys/param.h>
0076 #include <sys/stat.h>
0077 
0078 #include "err.h"
0079 #include <errno.h>
0080 #include "fts.h"
0081 #include <limits.h>
0082 #include <signal.h>
0083 #include <stdio.h>
0084 #include <stdlib.h>
0085 #include <string.h>
0086 #include <unistd.h>
0087 
0088 #include "internal.h"
0089 
0090 #include "extern-cp.h"
0091 
0092 #define S_ISTXT 0
0093 
0094 #define STRIP_TRAILING_SLASH(p) {                   \
0095         while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')  \
0096                 *--(p).p_end = 0;                   \
0097 }
0098 
0099 enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
0100 
0101 static int Rflag, rflag;
0102 
0103 static int main_cp(rtems_shell_cp_globals* cp_globals, int, char *[]);
0104 static int copy(rtems_shell_cp_globals* cp_globals, char *[], enum op, int);
0105 static int mastercmp(const FTSENT **, const FTSENT **);
0106 
0107 int
0108 rtems_shell_main_cp(int argc, char *argv[])
0109 {
0110   rtems_shell_cp_globals cp_globals;
0111   memset (&cp_globals, 0, sizeof (cp_globals));
0112   Rflag = rflag = 0;
0113   if (setjmp (cp_globals.exit_jmp) == 0)
0114     return main_cp (&cp_globals, argc, argv);
0115   return 1;
0116 }
0117 
0118 int
0119 main_cp(rtems_shell_cp_globals* cp_globals, int argc, char *argv[])
0120 {
0121     struct stat to_stat, tmp_stat;
0122     enum op type;
0123     int Hflag, Lflag, Pflag, ch, fts_options, r, have_trailing_slash;
0124     char *target;
0125   struct getopt_data getopt_reent;
0126 
0127     Hflag = Lflag = Pflag = 0;
0128   memset(&getopt_reent, 0, sizeof(getopt_data));
0129 
0130     while ((ch = getopt_r(argc, argv, "HLPRafilnprv", &getopt_reent)) != -1)
0131         switch (ch) {
0132         case 'H':
0133             Hflag = 1;
0134             Lflag = Pflag = 0;
0135             break;
0136         case 'L':
0137             Lflag = 1;
0138             Hflag = Pflag = 0;
0139             break;
0140         case 'P':
0141             Pflag = 1;
0142             Hflag = Lflag = 0;
0143             break;
0144         case 'R':
0145             Rflag = 1;
0146             break;
0147         case 'a':
0148             Pflag = 1;
0149             pflag = 1;
0150             Rflag = 1;
0151             Hflag = Lflag = 0;
0152             break;
0153         case 'f':
0154             fflag = 1;
0155             iflag = nflag = 0;
0156             break;
0157         case 'i':
0158             iflag = 1;
0159             fflag = nflag = 0;
0160             break;
0161         case 'l':
0162             lflag = 1;
0163             break;
0164         case 'n':
0165             nflag = 1;
0166             fflag = iflag = 0;
0167             break;
0168         case 'p':
0169             pflag = 1;
0170             break;
0171         case 'r':
0172             rflag = Lflag = 1;
0173             Hflag = Pflag = 0;
0174             break;
0175         case 'v':
0176             vflag = 1;
0177             break;
0178         default:
0179             usage(cp_globals);
0180             break;
0181         }
0182     argc -= getopt_reent.optind;
0183     argv += getopt_reent.optind;
0184 
0185     if (argc < 2)
0186         usage(cp_globals);
0187 
0188     fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
0189     if (Rflag && rflag)
0190         errx(exit_jump, 1, "the -R and -r options may not be specified together");
0191     if (rflag)
0192         Rflag = 1;
0193     if (Rflag) {
0194         if (Hflag)
0195             fts_options |= FTS_COMFOLLOW;
0196         if (Lflag) {
0197             fts_options &= ~FTS_PHYSICAL;
0198             fts_options |= FTS_LOGICAL;
0199         }
0200     } else {
0201         fts_options &= ~FTS_PHYSICAL;
0202         fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
0203     }
0204 #if 0
0205     (void)signal(SIGINFO, siginfo);
0206 #endif
0207 
0208     /* Save the target base in "to". */
0209     target = argv[--argc];
0210     if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path))
0211         errx(exit_jump, 1, "%s: name too long", target);
0212     to.p_end = to.p_path + strlen(to.p_path);
0213         if (to.p_path == to.p_end) {
0214         *to.p_end++ = '.';
0215         *to.p_end = 0;
0216     }
0217     have_trailing_slash = (to.p_end[-1] == '/');
0218     if (have_trailing_slash)
0219         STRIP_TRAILING_SLASH(to);
0220     to.target_end = to.p_end;
0221 
0222     /* Set end of argument list for fts(3). */
0223     argv[argc] = NULL;
0224 
0225     /*
0226      * Cp has two distinct cases:
0227      *
0228      * cp [-R] source target
0229      * cp [-R] source1 ... sourceN directory
0230      *
0231      * In both cases, source can be either a file or a directory.
0232      *
0233      * In (1), the target becomes a copy of the source. That is, if the
0234      * source is a file, the target will be a file, and likewise for
0235      * directories.
0236      *
0237      * In (2), the real target is not directory, but "directory/source".
0238      */
0239     r = stat(to.p_path, &to_stat);
0240     if (r == -1 && errno != ENOENT)
0241         err(exit_jump, 1, "%s", to.p_path);
0242     if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
0243         /*
0244          * Case (1).  Target is not a directory.
0245          */
0246         if (argc > 1)
0247             errx(exit_jump, 1, "%s is not a directory", to.p_path);
0248 
0249         /*
0250          * Need to detect the case:
0251          *  cp -R dir foo
0252          * Where dir is a directory and foo does not exist, where
0253          * we want pathname concatenations turned on but not for
0254          * the initial mkdir().
0255          */
0256         if (r == -1) {
0257             if (Rflag && (Lflag || Hflag))
0258             #ifdef __rtems__
0259                 (void)
0260             #endif
0261                 stat(*argv, &tmp_stat);
0262             else
0263             #ifdef __rtems__
0264                 (void)
0265             #endif
0266                 lstat(*argv, &tmp_stat);
0267 
0268             if (S_ISDIR(tmp_stat.st_mode) && Rflag)
0269                 type = DIR_TO_DNE;
0270             else
0271                 type = FILE_TO_FILE;
0272         } else
0273             type = FILE_TO_FILE;
0274 
0275         if (have_trailing_slash && type == FILE_TO_FILE) {
0276             if (r == -1)
0277                 errx(exit_jump, 1, "directory %s does not exist",
0278                      to.p_path);
0279             else
0280                 errx(exit_jump, 1, "%s is not a directory", to.p_path);
0281         }
0282     } else
0283         /*
0284          * Case (2).  Target is a directory.
0285          */
0286         type = FILE_TO_DIR;
0287 
0288   return copy(cp_globals, argv, type, fts_options);
0289 }
0290 
0291 int
0292 copy(rtems_shell_cp_globals* cp_globals,
0293      char *argv[], enum op type, int fts_options)
0294 {
0295     struct stat to_stat;
0296     FTS *ftsp;
0297     FTSENT *curr;
0298     int base = 0, dne, badcp, rval;
0299     size_t nlen;
0300     char *p, *target_mid;
0301     mode_t mask, mode;
0302 
0303     /*
0304      * Keep an inverted copy of the umask, for use in correcting
0305      * permissions on created directories when not using -p.
0306      */
0307     mask = ~umask(0777);
0308     umask(~mask);
0309 
0310     if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL)
0311         err(exit_jump, 1, "fts_open");
0312     for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) {
0313         switch (curr->fts_info) {
0314         case FTS_NS:
0315         case FTS_DNR:
0316         case FTS_ERR:
0317             warnx("%s: %s",
0318                 curr->fts_path, strerror(curr->fts_errno));
0319         #ifdef __rtems__
0320         /*
0321          * Coverity spotted that badcp is set by each loop
0322          * iteration so setting it right before continue
0323          * results in the value being unused. See CID 1255344
0324          *
0325          * The current NetBSD source (v1.62) was checked and
0326          * the same issue appears to apply although the
0327          * variable names have changed since this was imported
0328          * to RTEMS.
0329          *
0330          * This pattern exists in multiple places in this file.
0331          */
0332             rval = 1;
0333         #else
0334             badcp = rval = 1;
0335         #endif
0336             continue;
0337         case FTS_DC:            /* Warn, continue. */
0338             warnx("%s: directory causes a cycle", curr->fts_path);
0339         #ifdef __rtems__
0340             rval = 1;
0341         #else
0342             badcp = rval = 1;
0343         #endif
0344             continue;
0345         default:
0346             ;
0347         }
0348 
0349         /*
0350          * If we are in case (2) or (3) above, we need to append the
0351                  * source name to the target name.
0352                  */
0353         if (type != FILE_TO_FILE) {
0354             /*
0355              * Need to remember the roots of traversals to create
0356              * correct pathnames.  If there's a directory being
0357              * copied to a non-existent directory, e.g.
0358              *  cp -R a/dir noexist
0359              * the resulting path name should be noexist/foo, not
0360              * noexist/dir/foo (where foo is a file in dir), which
0361              * is the case where the target exists.
0362              *
0363              * Also, check for "..".  This is for correct path
0364              * concatenation for paths ending in "..", e.g.
0365              *  cp -R .. /tmp
0366              * Paths ending in ".." are changed to ".".  This is
0367              * tricky, but seems the easiest way to fix the problem.
0368              *
0369              * XXX
0370              * Since the first level MUST be FTS_ROOTLEVEL, base
0371              * is always initialized.
0372              */
0373             if (curr->fts_level == FTS_ROOTLEVEL) {
0374                 if (type != DIR_TO_DNE) {
0375                     p = strrchr(curr->fts_path, '/');
0376                     base = (p == NULL) ? 0 :
0377                         (int)(p - curr->fts_path + 1);
0378 
0379                     if (!strcmp(&curr->fts_path[base],
0380                         ".."))
0381                         base += 1;
0382                 } else
0383                     base = curr->fts_pathlen;
0384             }
0385 
0386             p = &curr->fts_path[base];
0387             nlen = curr->fts_pathlen - base;
0388             target_mid = to.target_end;
0389             if (*p != '/' && target_mid[-1] != '/')
0390                 *target_mid++ = '/';
0391             *target_mid = 0;
0392             if (target_mid - to.p_path + nlen >= PATH_MAX) {
0393                 warnx("%s%s: name too long (not copied)",
0394                     to.p_path, p);
0395             #ifdef __rtems__
0396                 rval = 1;
0397             #else
0398                 badcp = rval = 1;
0399             #endif
0400                 continue;
0401             }
0402             (void)strncat(target_mid, p, nlen);
0403             to.p_end = target_mid + nlen;
0404             *to.p_end = 0;
0405             STRIP_TRAILING_SLASH(to);
0406         }
0407 
0408         if (curr->fts_info == FTS_DP) {
0409             /*
0410              * We are nearly finished with this directory.  If we
0411              * didn't actually copy it, or otherwise don't need to
0412              * change its attributes, then we are done.
0413              */
0414             if (!curr->fts_number)
0415                 continue;
0416             /*
0417              * If -p is in effect, set all the attributes.
0418              * Otherwise, set the correct permissions, limited
0419              * by the umask.  Optimise by avoiding a chmod()
0420              * if possible (which is usually the case if we
0421              * made the directory).  Note that mkdir() does not
0422              * honour setuid, setgid and sticky bits, but we
0423              * normally want to preserve them on directories.
0424              */
0425             if (pflag) {
0426                 if (setfile(cp_globals, curr->fts_statp, -1))
0427                     rval = 1;
0428                 if (preserve_dir_acls(curr->fts_statp,
0429                     curr->fts_accpath, to.p_path) != 0)
0430                     rval = 1;
0431             } else {
0432                 mode = curr->fts_statp->st_mode;
0433                 if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) ||
0434                     ((mode | S_IRWXU) & mask) != (mode & mask))
0435                     if (chmod(to.p_path, mode & mask) != 0){
0436                         warn("chmod: %s", to.p_path);
0437                         rval = 1;
0438                     }
0439             }
0440             continue;
0441         }
0442 
0443         /* Not an error but need to remember it happened */
0444         if (stat(to.p_path, &to_stat) == -1)
0445             dne = 1;
0446         else {
0447             if (to_stat.st_dev == curr->fts_statp->st_dev &&
0448                 to_stat.st_ino == curr->fts_statp->st_ino) {
0449                 warnx("%s and %s are identical (not copied).",
0450                     to.p_path, curr->fts_path);
0451             #ifdef __rtems__
0452                 rval = 1;
0453             #else
0454                 badcp = rval = 1;
0455             #endif
0456                 if (S_ISDIR(curr->fts_statp->st_mode))
0457                     (void)fts_set(ftsp, curr, FTS_SKIP);
0458                 continue;
0459             }
0460             if (!S_ISDIR(curr->fts_statp->st_mode) &&
0461                 S_ISDIR(to_stat.st_mode)) {
0462                 warnx("cannot overwrite directory %s with "
0463                     "non-directory %s",
0464                     to.p_path, curr->fts_path);
0465             #ifdef __rtems__
0466                 rval = 1;
0467             #else
0468                 badcp = rval = 1;
0469             #endif
0470                 continue;
0471             }
0472             dne = 0;
0473         }
0474 
0475         switch (curr->fts_statp->st_mode & S_IFMT) {
0476         case S_IFLNK:
0477             /* Catch special case of a non-dangling symlink */
0478             if ((fts_options & FTS_LOGICAL) ||
0479                 ((fts_options & FTS_COMFOLLOW) &&
0480                 curr->fts_level == 0)) {
0481                 if (copy_file(cp_globals, curr, dne))
0482                     badcp = rval = 1;
0483             } else {
0484                 if (copy_link(cp_globals, curr, !dne))
0485                     badcp = rval = 1;
0486             }
0487             break;
0488         case S_IFDIR:
0489             if (!Rflag) {
0490                 warnx("%s is a directory (not copied).",
0491                     curr->fts_path);
0492                 (void)fts_set(ftsp, curr, FTS_SKIP);
0493                 badcp = rval = 1;
0494                 break;
0495             }
0496             /*
0497              * If the directory doesn't exist, create the new
0498              * one with the from file mode plus owner RWX bits,
0499              * modified by the umask.  Trade-off between being
0500              * able to write the directory (if from directory is
0501              * 555) and not causing a permissions race.  If the
0502              * umask blocks owner writes, we fail..
0503              */
0504             if (dne) {
0505                 if (mkdir(to.p_path,
0506                     curr->fts_statp->st_mode | S_IRWXU) < 0)
0507                     err(exit_jump, 1, "%s", to.p_path);
0508             } else if (!S_ISDIR(to_stat.st_mode)) {
0509                 errno = ENOTDIR;
0510                 err(exit_jump, 1, "%s", to.p_path);
0511             }
0512             /*
0513              * Arrange to correct directory attributes later
0514              * (in the post-order phase) if this is a new
0515              * directory, or if the -p flag is in effect.
0516              */
0517             curr->fts_number = pflag || dne;
0518             break;
0519         case S_IFBLK:
0520         case S_IFCHR:
0521             if (Rflag) {
0522                 if (copy_special(cp_globals, curr->fts_statp, !dne))
0523                     badcp = rval = 1;
0524             } else {
0525                 if (copy_file(cp_globals, curr, dne))
0526                     badcp = rval = 1;
0527             }
0528             break;
0529         case S_IFSOCK:
0530             warnx("%s is a socket (not copied).",
0531                     curr->fts_path);
0532         case S_IFIFO:
0533             if (Rflag) {
0534                 if (copy_fifo(cp_globals, curr->fts_statp, !dne))
0535                     badcp = rval = 1;
0536             } else {
0537                 if (copy_file(cp_globals, curr, dne))
0538                     badcp = rval = 1;
0539             }
0540             break;
0541         default:
0542             if (copy_file(cp_globals, curr, dne))
0543                 badcp = rval = 1;
0544             break;
0545         }
0546         if (vflag && !badcp)
0547             (void)printf("%s -> %s\n", curr->fts_path, to.p_path);
0548     }
0549     if (errno)
0550         err(exit_jump, 1, "fts_read");
0551     fts_close(ftsp);
0552     return (rval);
0553 }
0554 
0555 /*
0556  * mastercmp --
0557  *  The comparison function for the copy order.  The order is to copy
0558  *  non-directory files before directory files.  The reason for this
0559  *  is because files tend to be in the same cylinder group as their
0560  *  parent directory, whereas directories tend not to be.  Copying the
0561  *  files first reduces seeking.
0562  */
0563 int
0564 mastercmp(const FTSENT **a, const FTSENT **b)
0565 {
0566     int a_info, b_info;
0567 
0568     a_info = (*a)->fts_info;
0569     if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR)
0570         return (0);
0571     b_info = (*b)->fts_info;
0572     if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR)
0573         return (0);
0574     if (a_info == FTS_D)
0575         return (-1);
0576     if (b_info == FTS_D)
0577         return (1);
0578     return (0);
0579 }
0580 
0581 rtems_shell_cmd_t rtems_shell_CP_Command = {
0582   "cp",                                                /* name */
0583   "cp [-R [-H | -L | -P]] [-f | -i] [-pv] src target", /* usage */
0584   "files",                                             /* topic */
0585   rtems_shell_main_cp,                                 /* command */
0586   NULL,                                                /* alias */
0587   NULL                                                 /* next */
0588 };
0589