source: rtems/cpukit/libmisc/shell/main_cp.c @ a8ed055

4.115
Last change on this file since a8ed055 was a8ed055, checked in by Ralf Corsepius <ralf.corsepius@…>, on 12/04/11 at 18:27:40

2011-12-04 Ralf Corsépius <ralf.corsepius@…>

  • libmisc/shell/main_cp.c: Include "internal.h". Make rtems_shell_main_cp non-static.
  • Property mode set to 100644
File size: 14.9 KB
Line 
1/* $NetBSD: cp.c,v 1.39 2005/10/24 12:59:07 kleink Exp $ */
2
3/*
4 * Copyright (c) 1988, 1993, 1994
5 *  The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * David Hitz of Auspex Systems Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifdef HAVE_CONFIG_H
36#include "config.h"
37#endif
38
39#if 0
40#ifndef lint
41__COPYRIGHT(
42"@(#) Copyright (c) 1988, 1993, 1994\n\
43  The Regents of the University of California.  All rights reserved.\n");
44#endif /* not lint */
45
46#ifndef lint
47#if 0
48static char sccsid[] = "@(#)cp.c  8.5 (Berkeley) 4/29/95";
49#else
50__RCSID("$NetBSD: cp.c,v 1.39 2005/10/24 12:59:07 kleink Exp $");
51#endif
52#endif /* not lint */
53#endif
54/*
55 * Cp copies source files to target files.
56 *
57 * The global PATH_T structure "to" always contains the path to the
58 * current target file.  Since fts(3) does not change directories,
59 * this path can be either absolute or dot-relative.
60 *
61 * The basic algorithm is to initialize "to" and use fts(3) to traverse
62 * the file hierarchy rooted in the argument list.  A trivial case is the
63 * case of 'cp file1 file2'.  The more interesting case is the case of
64 * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
65 * path (relative to the root of the traversal) is appended to dir (stored
66 * in "to") to form the final target path.
67 */
68
69#include <rtems.h>
70#include <rtems/shell.h>
71#include <rtems/shellconfig.h>
72#define __need_getopt_newlib
73#include <getopt.h>
74
75#include <sys/param.h>
76#include <sys/stat.h>
77
78#include <err.h>
79#include <errno.h>
80#include <fts.h>
81#include <limits.h>
82#include <signal.h>
83#include <stdio.h>
84#include <stdlib.h>
85#include <string.h>
86#include <unistd.h>
87
88#include "internal.h"
89
90#include "extern-cp.h"
91
92#define S_ISTXT 0
93
94#define STRIP_TRAILING_SLASH(p) {                                       \
95        while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')      \
96                *--(p).p_end = 0;                                       \
97}
98
99enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
100
101static int Rflag, rflag;
102
103static int main_cp(rtems_shell_cp_globals* cp_globals, int, char *[]);
104static int copy(rtems_shell_cp_globals* cp_globals, char *[], enum op, int);
105static int mastercmp(const FTSENT **, const FTSENT **);
106
107static void
108rtems_shell_cp_exit (rtems_shell_cp_globals* cp_global, int code __attribute__((unused)))
109{
110  longjmp (cp_global->exit_jmp, 1);
111}
112
113int
114rtems_shell_main_cp(int argc, char *argv[])
115{
116  rtems_shell_cp_globals cp_globals;
117  memset (&cp_globals, 0, sizeof (cp_globals));
118  Rflag = rflag = 0;
119  if (setjmp (cp_globals.exit_jmp) == 0)
120    return main_cp (&cp_globals, argc, argv);
121  return 1;
122}
123
124int
125main_cp(rtems_shell_cp_globals* cp_globals, int argc, char *argv[])
126{
127        struct stat to_stat, tmp_stat;
128        enum op type;
129        int Hflag, Lflag, Pflag, ch, fts_options, r, have_trailing_slash;
130        char *target;
131  struct getopt_data getopt_reent;
132
133        Hflag = Lflag = Pflag = 0;
134  memset(&getopt_reent, 0, sizeof(getopt_data));
135
136        while ((ch = getopt_r(argc, argv, "HLPRafilnprv", &getopt_reent)) != -1)
137                switch (ch) {
138                case 'H':
139                        Hflag = 1;
140                        Lflag = Pflag = 0;
141                        break;
142                case 'L':
143                        Lflag = 1;
144                        Hflag = Pflag = 0;
145                        break;
146                case 'P':
147                        Pflag = 1;
148                        Hflag = Lflag = 0;
149                        break;
150                case 'R':
151                        Rflag = 1;
152                        break;
153                case 'a':
154                        Pflag = 1;
155                        pflag = 1;
156                        Rflag = 1;
157                        Hflag = Lflag = 0;
158                        break;
159                case 'f':
160                        fflag = 1;
161                        iflag = nflag = 0;
162                        break;
163                case 'i':
164                        iflag = 1;
165                        fflag = nflag = 0;
166                        break;
167                case 'l':
168                        lflag = 1;
169                        break;
170                case 'n':
171                        nflag = 1;
172                        fflag = iflag = 0;
173                        break;
174                case 'p':
175                        pflag = 1;
176                        break;
177                case 'r':
178                        rflag = Lflag = 1;
179                        Hflag = Pflag = 0;
180                        break;
181                case 'v':
182                        vflag = 1;
183                        break;
184                default:
185                        usage(cp_globals);
186                        break;
187                }
188        argc -= getopt_reent.optind;
189        argv += getopt_reent.optind;
190
191        if (argc < 2)
192                usage(cp_globals);
193
194        fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
195        if (Rflag && rflag)
196                errx(exit_jump, 1, "the -R and -r options may not be specified together");
197        if (rflag)
198                Rflag = 1;
199        if (Rflag) {
200                if (Hflag)
201                        fts_options |= FTS_COMFOLLOW;
202                if (Lflag) {
203                        fts_options &= ~FTS_PHYSICAL;
204                        fts_options |= FTS_LOGICAL;
205                }
206        } else {
207                fts_options &= ~FTS_PHYSICAL;
208                fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
209        }
210#if 0
211        (void)signal(SIGINFO, siginfo);
212#endif
213
214        /* Save the target base in "to". */
215        target = argv[--argc];
216        if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path))
217                errx(exit_jump, 1, "%s: name too long", target);
218        to.p_end = to.p_path + strlen(to.p_path);
219        if (to.p_path == to.p_end) {
220                *to.p_end++ = '.';
221                *to.p_end = 0;
222        }
223        have_trailing_slash = (to.p_end[-1] == '/');
224        if (have_trailing_slash)
225                STRIP_TRAILING_SLASH(to);
226        to.target_end = to.p_end;
227
228        /* Set end of argument list for fts(3). */
229        argv[argc] = NULL;
230
231        /*
232         * Cp has two distinct cases:
233         *
234         * cp [-R] source target
235         * cp [-R] source1 ... sourceN directory
236         *
237         * In both cases, source can be either a file or a directory.
238         *
239         * In (1), the target becomes a copy of the source. That is, if the
240         * source is a file, the target will be a file, and likewise for
241         * directories.
242         *
243         * In (2), the real target is not directory, but "directory/source".
244         */
245        r = stat(to.p_path, &to_stat);
246        if (r == -1 && errno != ENOENT)
247                err(exit_jump, 1, "%s", to.p_path);
248        if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
249                /*
250                 * Case (1).  Target is not a directory.
251                 */
252                if (argc > 1)
253                        errx(exit_jump, 1, "%s is not a directory", to.p_path);
254
255                /*
256                 * Need to detect the case:
257                 *      cp -R dir foo
258                 * Where dir is a directory and foo does not exist, where
259                 * we want pathname concatenations turned on but not for
260                 * the initial mkdir().
261                 */
262                if (r == -1) {
263                        if (Rflag && (Lflag || Hflag))
264                                stat(*argv, &tmp_stat);
265                        else
266                                lstat(*argv, &tmp_stat);
267
268                        if (S_ISDIR(tmp_stat.st_mode) && Rflag)
269                                type = DIR_TO_DNE;
270                        else
271                                type = FILE_TO_FILE;
272                } else
273                        type = FILE_TO_FILE;
274
275                if (have_trailing_slash && type == FILE_TO_FILE) {
276                        if (r == -1)
277                                errx(exit_jump, 1, "directory %s does not exist",
278                                     to.p_path);
279                        else
280                                errx(exit_jump, 1, "%s is not a directory", to.p_path);
281                }
282        } else
283                /*
284                 * Case (2).  Target is a directory.
285                 */
286                type = FILE_TO_DIR;
287
288  return copy(cp_globals, argv, type, fts_options);
289}
290
291int
292copy(rtems_shell_cp_globals* cp_globals,
293     char *argv[], enum op type, int fts_options)
294{
295        struct stat to_stat;
296        FTS *ftsp;
297        FTSENT *curr;
298        int base = 0, dne, badcp, rval;
299        size_t nlen;
300        char *p, *target_mid;
301        mode_t mask, mode;
302
303        /*
304         * Keep an inverted copy of the umask, for use in correcting
305         * permissions on created directories when not using -p.
306         */
307        mask = ~umask(0777);
308        umask(~mask);
309
310        if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL)
311                err(exit_jump, 1, "fts_open");
312        for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) {
313                switch (curr->fts_info) {
314                case FTS_NS:
315                case FTS_DNR:
316                case FTS_ERR:
317                        warnx("%s: %s",
318                            curr->fts_path, strerror(curr->fts_errno));
319                        badcp = rval = 1;
320                        continue;
321                case FTS_DC:                    /* Warn, continue. */
322                        warnx("%s: directory causes a cycle", curr->fts_path);
323                        badcp = rval = 1;
324                        continue;
325                default:
326                        ;
327                }
328
329                /*
330                 * If we are in case (2) or (3) above, we need to append the
331                 * source name to the target name.
332                 */
333                if (type != FILE_TO_FILE) {
334                        /*
335                         * Need to remember the roots of traversals to create
336                         * correct pathnames.  If there's a directory being
337                         * copied to a non-existent directory, e.g.
338                         *      cp -R a/dir noexist
339                         * the resulting path name should be noexist/foo, not
340                         * noexist/dir/foo (where foo is a file in dir), which
341                         * is the case where the target exists.
342                         *
343                         * Also, check for "..".  This is for correct path
344                         * concatenation for paths ending in "..", e.g.
345                         *      cp -R .. /tmp
346                         * Paths ending in ".." are changed to ".".  This is
347                         * tricky, but seems the easiest way to fix the problem.
348                         *
349                         * XXX
350                         * Since the first level MUST be FTS_ROOTLEVEL, base
351                         * is always initialized.
352                         */
353                        if (curr->fts_level == FTS_ROOTLEVEL) {
354                                if (type != DIR_TO_DNE) {
355                                        p = strrchr(curr->fts_path, '/');
356                                        base = (p == NULL) ? 0 :
357                                            (int)(p - curr->fts_path + 1);
358
359                                        if (!strcmp(&curr->fts_path[base],
360                                            ".."))
361                                                base += 1;
362                                } else
363                                        base = curr->fts_pathlen;
364                        }
365
366                        p = &curr->fts_path[base];
367                        nlen = curr->fts_pathlen - base;
368                        target_mid = to.target_end;
369                        if (*p != '/' && target_mid[-1] != '/')
370                                *target_mid++ = '/';
371                        *target_mid = 0;
372                        if (target_mid - to.p_path + nlen >= PATH_MAX) {
373                                warnx("%s%s: name too long (not copied)",
374                                    to.p_path, p);
375                                badcp = rval = 1;
376                                continue;
377                        }
378                        (void)strncat(target_mid, p, nlen);
379                        to.p_end = target_mid + nlen;
380                        *to.p_end = 0;
381                        STRIP_TRAILING_SLASH(to);
382                }
383
384                if (curr->fts_info == FTS_DP) {
385                        /*
386                         * We are nearly finished with this directory.  If we
387                         * didn't actually copy it, or otherwise don't need to
388                         * change its attributes, then we are done.
389                         */
390                        if (!curr->fts_number)
391                                continue;
392                        /*
393                         * If -p is in effect, set all the attributes.
394                         * Otherwise, set the correct permissions, limited
395                         * by the umask.  Optimise by avoiding a chmod()
396                         * if possible (which is usually the case if we
397                         * made the directory).  Note that mkdir() does not
398                         * honour setuid, setgid and sticky bits, but we
399                         * normally want to preserve them on directories.
400                         */
401                        if (pflag) {
402                                if (setfile(cp_globals, curr->fts_statp, -1))
403                                        rval = 1;
404                                if (preserve_dir_acls(curr->fts_statp,
405                                    curr->fts_accpath, to.p_path) != 0)
406                                        rval = 1;
407                        } else {
408                                mode = curr->fts_statp->st_mode;
409                                if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) ||
410                                    ((mode | S_IRWXU) & mask) != (mode & mask))
411                                        if (chmod(to.p_path, mode & mask) != 0){
412                                                warn("chmod: %s", to.p_path);
413                                                rval = 1;
414                                        }
415                        }
416                        continue;
417                }
418
419                /* Not an error but need to remember it happened */
420                if (stat(to.p_path, &to_stat) == -1)
421                        dne = 1;
422                else {
423                        if (to_stat.st_dev == curr->fts_statp->st_dev &&
424                            to_stat.st_ino == curr->fts_statp->st_ino) {
425                                warnx("%s and %s are identical (not copied).",
426                                    to.p_path, curr->fts_path);
427                                badcp = rval = 1;
428                                if (S_ISDIR(curr->fts_statp->st_mode))
429                                        (void)fts_set(ftsp, curr, FTS_SKIP);
430                                continue;
431                        }
432                        if (!S_ISDIR(curr->fts_statp->st_mode) &&
433                            S_ISDIR(to_stat.st_mode)) {
434                                warnx("cannot overwrite directory %s with "
435                                    "non-directory %s",
436                                    to.p_path, curr->fts_path);
437                                badcp = rval = 1;
438                                continue;
439                        }
440                        dne = 0;
441                }
442
443                switch (curr->fts_statp->st_mode & S_IFMT) {
444                case S_IFLNK:
445                        /* Catch special case of a non-dangling symlink */
446                        if ((fts_options & FTS_LOGICAL) ||
447                            ((fts_options & FTS_COMFOLLOW) &&
448                            curr->fts_level == 0)) {
449                                if (copy_file(cp_globals, curr, dne))
450                                        badcp = rval = 1;
451                        } else {
452                                if (copy_link(cp_globals, curr, !dne))
453                                        badcp = rval = 1;
454                        }
455                        break;
456                case S_IFDIR:
457                        if (!Rflag) {
458                                warnx("%s is a directory (not copied).",
459                                    curr->fts_path);
460                                (void)fts_set(ftsp, curr, FTS_SKIP);
461                                badcp = rval = 1;
462                                break;
463                        }
464                        /*
465                         * If the directory doesn't exist, create the new
466                         * one with the from file mode plus owner RWX bits,
467                         * modified by the umask.  Trade-off between being
468                         * able to write the directory (if from directory is
469                         * 555) and not causing a permissions race.  If the
470                         * umask blocks owner writes, we fail..
471                         */
472                        if (dne) {
473                                if (mkdir(to.p_path,
474                                    curr->fts_statp->st_mode | S_IRWXU) < 0)
475                                        err(exit_jump, 1, "%s", to.p_path);
476                        } else if (!S_ISDIR(to_stat.st_mode)) {
477                                errno = ENOTDIR;
478                                err(exit_jump, 1, "%s", to.p_path);
479                        }
480                        /*
481                         * Arrange to correct directory attributes later
482                         * (in the post-order phase) if this is a new
483                         * directory, or if the -p flag is in effect.
484                         */
485                        curr->fts_number = pflag || dne;
486                        break;
487                case S_IFBLK:
488                case S_IFCHR:
489                        if (Rflag) {
490                                if (copy_special(cp_globals, curr->fts_statp, !dne))
491                                        badcp = rval = 1;
492                        } else {
493                                if (copy_file(cp_globals, curr, dne))
494                                        badcp = rval = 1;
495                        }
496                        break;
497                case S_IFSOCK:
498                        warnx("%s is a socket (not copied).",
499                                    curr->fts_path);
500                case S_IFIFO:
501                        if (Rflag) {
502                                if (copy_fifo(cp_globals, curr->fts_statp, !dne))
503                                        badcp = rval = 1;
504                        } else {
505                                if (copy_file(cp_globals, curr, dne))
506                                        badcp = rval = 1;
507                        }
508                        break;
509                default:
510                        if (copy_file(cp_globals, curr, dne))
511                                badcp = rval = 1;
512                        break;
513                }
514                if (vflag && !badcp)
515                        (void)printf("%s -> %s\n", curr->fts_path, to.p_path);
516        }
517        if (errno)
518                err(exit_jump, 1, "fts_read");
519        fts_close(ftsp);
520        return (rval);
521}
522
523/*
524 * mastercmp --
525 *      The comparison function for the copy order.  The order is to copy
526 *      non-directory files before directory files.  The reason for this
527 *      is because files tend to be in the same cylinder group as their
528 *      parent directory, whereas directories tend not to be.  Copying the
529 *      files first reduces seeking.
530 */
531int
532mastercmp(const FTSENT **a, const FTSENT **b)
533{
534        int a_info, b_info;
535
536        a_info = (*a)->fts_info;
537        if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR)
538                return (0);
539        b_info = (*b)->fts_info;
540        if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR)
541                return (0);
542        if (a_info == FTS_D)
543                return (-1);
544        if (b_info == FTS_D)
545                return (1);
546        return (0);
547}
548
549rtems_shell_cmd_t rtems_shell_CP_Command = {
550  "cp",                                                /* name */
551  "cp [-R [-H | -L | -P]] [-f | -i] [-pv] src target", /* usage */
552  "files",                                             /* topic */
553  rtems_shell_main_cp,                                 /* command */
554  NULL,                                                /* alias */
555  NULL                                                 /* next */
556};
557
Note: See TracBrowser for help on using the repository browser.