source: rtems/cpukit/libmisc/shell/main_cp.c @ 8916bdc7

4.104.115
Last change on this file since 8916bdc7 was 031deada, checked in by Ralf Corsepius <ralf.corsepius@…>, on 01/02/09 at 13:04:13

Add attribute((unused)) to unused function args.

  • 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 "extern-cp.h"
89
90#define S_ISTXT 0
91
92#define STRIP_TRAILING_SLASH(p) {                                       \
93        while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')      \
94                *--(p).p_end = 0;                                       \
95}
96
97enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
98
99static int Rflag, rflag;
100
101static int main_cp(rtems_shell_cp_globals* cp_globals, int, char *[]);
102static int copy(rtems_shell_cp_globals* cp_globals, char *[], enum op, int);
103static int mastercmp(const FTSENT **, const FTSENT **);
104
105void
106rtems_shell_cp_exit (rtems_shell_cp_globals* cp_global, int code __attribute__((unused)))
107{
108  longjmp (cp_global->exit_jmp, 1);
109}
110
111int
112rtems_shell_main_cp(int argc, char *argv[])
113{
114  rtems_shell_cp_globals cp_globals;
115  memset (&cp_globals, 0, sizeof (cp_globals));
116  Rflag = rflag = 0;
117  if (setjmp (cp_globals.exit_jmp) == 0)
118    return main_cp (&cp_globals, argc, argv);
119  return 1;
120}
121
122int
123main_cp(rtems_shell_cp_globals* cp_globals, int argc, char *argv[])
124{
125        struct stat to_stat, tmp_stat;
126        enum op type;
127        int Hflag, Lflag, Pflag, ch, fts_options, r, have_trailing_slash;
128        char *target;
129  struct getopt_data getopt_reent;
130
131        Hflag = Lflag = Pflag = 0;
132  memset(&getopt_reent, 0, sizeof(getopt_data));
133
134        while ((ch = getopt_r(argc, argv, "HLPRafilnprv", &getopt_reent)) != -1)
135                switch (ch) {
136                case 'H':
137                        Hflag = 1;
138                        Lflag = Pflag = 0;
139                        break;
140                case 'L':
141                        Lflag = 1;
142                        Hflag = Pflag = 0;
143                        break;
144                case 'P':
145                        Pflag = 1;
146                        Hflag = Lflag = 0;
147                        break;
148                case 'R':
149                        Rflag = 1;
150                        break;
151                case 'a':
152                        Pflag = 1;
153                        pflag = 1;
154                        Rflag = 1;
155                        Hflag = Lflag = 0;
156                        break;
157                case 'f':
158                        fflag = 1;
159                        iflag = nflag = 0;
160                        break;
161                case 'i':
162                        iflag = 1;
163                        fflag = nflag = 0;
164                        break;
165                case 'l':
166                        lflag = 1;
167                        break;
168                case 'n':
169                        nflag = 1;
170                        fflag = iflag = 0;
171                        break;
172                case 'p':
173                        pflag = 1;
174                        break;
175                case 'r':
176                        rflag = Lflag = 1;
177                        Hflag = Pflag = 0;
178                        break;
179                case 'v':
180                        vflag = 1;
181                        break;
182                default:
183                        usage(cp_globals);
184                        break;
185                }
186        argc -= getopt_reent.optind;
187        argv += getopt_reent.optind;
188
189        if (argc < 2)
190                usage(cp_globals);
191
192        fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
193        if (Rflag && rflag)
194                errx(exit_jump, 1, "the -R and -r options may not be specified together");
195        if (rflag)
196                Rflag = 1;
197        if (Rflag) {
198                if (Hflag)
199                        fts_options |= FTS_COMFOLLOW;
200                if (Lflag) {
201                        fts_options &= ~FTS_PHYSICAL;
202                        fts_options |= FTS_LOGICAL;
203                }
204        } else {
205                fts_options &= ~FTS_PHYSICAL;
206                fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
207        }
208#if 0
209        (void)signal(SIGINFO, siginfo);
210#endif
211 
212        /* Save the target base in "to". */
213        target = argv[--argc];
214        if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path))
215                errx(exit_jump, 1, "%s: name too long", target);
216        to.p_end = to.p_path + strlen(to.p_path);
217        if (to.p_path == to.p_end) {
218                *to.p_end++ = '.';
219                *to.p_end = 0;
220        }
221        have_trailing_slash = (to.p_end[-1] == '/');
222        if (have_trailing_slash)
223                STRIP_TRAILING_SLASH(to);
224        to.target_end = to.p_end;
225
226        /* Set end of argument list for fts(3). */
227        argv[argc] = NULL;
228
229        /*
230         * Cp has two distinct cases:
231         *
232         * cp [-R] source target
233         * cp [-R] source1 ... sourceN directory
234         *
235         * In both cases, source can be either a file or a directory.
236         *
237         * In (1), the target becomes a copy of the source. That is, if the
238         * source is a file, the target will be a file, and likewise for
239         * directories.
240         *
241         * In (2), the real target is not directory, but "directory/source".
242         */
243        r = stat(to.p_path, &to_stat);
244        if (r == -1 && errno != ENOENT)
245                err(exit_jump, 1, "%s", to.p_path);
246        if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
247                /*
248                 * Case (1).  Target is not a directory.
249                 */
250                if (argc > 1)
251                        errx(exit_jump, 1, "%s is not a directory", to.p_path);
252
253                /*
254                 * Need to detect the case:
255                 *      cp -R dir foo
256                 * Where dir is a directory and foo does not exist, where
257                 * we want pathname concatenations turned on but not for
258                 * the initial mkdir().
259                 */
260                if (r == -1) {
261                        if (Rflag && (Lflag || Hflag))
262                                stat(*argv, &tmp_stat);
263                        else
264                                lstat(*argv, &tmp_stat);
265
266                        if (S_ISDIR(tmp_stat.st_mode) && Rflag)
267                                type = DIR_TO_DNE;
268                        else
269                                type = FILE_TO_FILE;
270                } else
271                        type = FILE_TO_FILE;
272
273                if (have_trailing_slash && type == FILE_TO_FILE) {
274                        if (r == -1)
275                                errx(exit_jump, 1, "directory %s does not exist",
276                                     to.p_path);
277                        else
278                                errx(exit_jump, 1, "%s is not a directory", to.p_path);
279                }
280        } else
281                /*
282                 * Case (2).  Target is a directory.
283                 */
284                type = FILE_TO_DIR;
285
286  return copy(cp_globals, argv, type, fts_options);
287}
288
289int
290copy(rtems_shell_cp_globals* cp_globals,
291     char *argv[], enum op type, int fts_options)
292{
293        struct stat to_stat;
294        FTS *ftsp;
295        FTSENT *curr;
296        int base = 0, dne, badcp, rval;
297        size_t nlen;
298        char *p, *target_mid;
299        mode_t mask, mode;
300
301        /*
302         * Keep an inverted copy of the umask, for use in correcting
303         * permissions on created directories when not using -p.
304         */
305        mask = ~umask(0777);
306        umask(~mask);
307
308        if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL)
309                err(exit_jump, 1, "fts_open");
310        for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) {
311                switch (curr->fts_info) {
312                case FTS_NS:
313                case FTS_DNR:
314                case FTS_ERR:
315                        warnx("%s: %s",
316                            curr->fts_path, strerror(curr->fts_errno));
317                        badcp = rval = 1;
318                        continue;
319                case FTS_DC:                    /* Warn, continue. */
320                        warnx("%s: directory causes a cycle", curr->fts_path);
321                        badcp = rval = 1;
322                        continue;
323                default:
324                        ;
325                }
326
327                /*
328                 * If we are in case (2) or (3) above, we need to append the
329                 * source name to the target name.
330                 */
331                if (type != FILE_TO_FILE) {
332                        /*
333                         * Need to remember the roots of traversals to create
334                         * correct pathnames.  If there's a directory being
335                         * copied to a non-existent directory, e.g.
336                         *      cp -R a/dir noexist
337                         * the resulting path name should be noexist/foo, not
338                         * noexist/dir/foo (where foo is a file in dir), which
339                         * is the case where the target exists.
340                         *
341                         * Also, check for "..".  This is for correct path
342                         * concatenation for paths ending in "..", e.g.
343                         *      cp -R .. /tmp
344                         * Paths ending in ".." are changed to ".".  This is
345                         * tricky, but seems the easiest way to fix the problem.
346                         *
347                         * XXX
348                         * Since the first level MUST be FTS_ROOTLEVEL, base
349                         * is always initialized.
350                         */
351                        if (curr->fts_level == FTS_ROOTLEVEL) {
352                                if (type != DIR_TO_DNE) {
353                                        p = strrchr(curr->fts_path, '/');
354                                        base = (p == NULL) ? 0 :
355                                            (int)(p - curr->fts_path + 1);
356
357                                        if (!strcmp(&curr->fts_path[base],
358                                            ".."))
359                                                base += 1;
360                                } else
361                                        base = curr->fts_pathlen;
362                        }
363
364                        p = &curr->fts_path[base];
365                        nlen = curr->fts_pathlen - base;
366                        target_mid = to.target_end;
367                        if (*p != '/' && target_mid[-1] != '/')
368                                *target_mid++ = '/';
369                        *target_mid = 0;
370                        if (target_mid - to.p_path + nlen >= PATH_MAX) {
371                                warnx("%s%s: name too long (not copied)",
372                                    to.p_path, p);
373                                badcp = rval = 1;
374                                continue;
375                        }
376                        (void)strncat(target_mid, p, nlen);
377                        to.p_end = target_mid + nlen;
378                        *to.p_end = 0;
379                        STRIP_TRAILING_SLASH(to);
380                }
381
382                if (curr->fts_info == FTS_DP) {
383                        /*
384                         * We are nearly finished with this directory.  If we
385                         * didn't actually copy it, or otherwise don't need to
386                         * change its attributes, then we are done.
387                         */
388                        if (!curr->fts_number)
389                                continue;
390                        /*
391                         * If -p is in effect, set all the attributes.
392                         * Otherwise, set the correct permissions, limited
393                         * by the umask.  Optimise by avoiding a chmod()
394                         * if possible (which is usually the case if we
395                         * made the directory).  Note that mkdir() does not
396                         * honour setuid, setgid and sticky bits, but we
397                         * normally want to preserve them on directories.
398                         */
399                        if (pflag) {
400                                if (setfile(cp_globals, curr->fts_statp, -1))
401                                        rval = 1;
402                                if (preserve_dir_acls(curr->fts_statp,
403                                    curr->fts_accpath, to.p_path) != 0)
404                                        rval = 1;
405                        } else {
406                                mode = curr->fts_statp->st_mode;
407                                if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) ||
408                                    ((mode | S_IRWXU) & mask) != (mode & mask))
409                                        if (chmod(to.p_path, mode & mask) != 0){
410                                                warn("chmod: %s", to.p_path);
411                                                rval = 1;
412                                        }
413                        }
414                        continue;
415                }
416
417                /* Not an error but need to remember it happened */
418                if (stat(to.p_path, &to_stat) == -1)
419                        dne = 1;
420                else {
421                        if (to_stat.st_dev == curr->fts_statp->st_dev &&
422                            to_stat.st_ino == curr->fts_statp->st_ino) {
423                                warnx("%s and %s are identical (not copied).",
424                                    to.p_path, curr->fts_path);
425                                badcp = rval = 1;
426                                if (S_ISDIR(curr->fts_statp->st_mode))
427                                        (void)fts_set(ftsp, curr, FTS_SKIP);
428                                continue;
429                        }
430                        if (!S_ISDIR(curr->fts_statp->st_mode) &&
431                            S_ISDIR(to_stat.st_mode)) {
432                                warnx("cannot overwrite directory %s with "
433                                    "non-directory %s",
434                                    to.p_path, curr->fts_path);
435                                badcp = rval = 1;
436                                continue;
437                        }
438                        dne = 0;
439                }
440
441                switch (curr->fts_statp->st_mode & S_IFMT) {
442                case S_IFLNK:
443                        /* Catch special case of a non-dangling symlink */
444                        if ((fts_options & FTS_LOGICAL) ||
445                            ((fts_options & FTS_COMFOLLOW) &&
446                            curr->fts_level == 0)) {
447                                if (copy_file(cp_globals, curr, dne))
448                                        badcp = rval = 1;
449                        } else {       
450                                if (copy_link(cp_globals, curr, !dne))
451                                        badcp = rval = 1;
452                        }
453                        break;
454                case S_IFDIR:
455                        if (!Rflag) {
456                                warnx("%s is a directory (not copied).",
457                                    curr->fts_path);
458                                (void)fts_set(ftsp, curr, FTS_SKIP);
459                                badcp = rval = 1;
460                                break;
461                        }
462                        /*
463                         * If the directory doesn't exist, create the new
464                         * one with the from file mode plus owner RWX bits,
465                         * modified by the umask.  Trade-off between being
466                         * able to write the directory (if from directory is
467                         * 555) and not causing a permissions race.  If the
468                         * umask blocks owner writes, we fail..
469                         */
470                        if (dne) {
471                                if (mkdir(to.p_path,
472                                    curr->fts_statp->st_mode | S_IRWXU) < 0)
473                                        err(exit_jump, 1, "%s", to.p_path);
474                        } else if (!S_ISDIR(to_stat.st_mode)) {
475                                errno = ENOTDIR;
476                                err(exit_jump, 1, "%s", to.p_path);
477                        }
478                        /*
479                         * Arrange to correct directory attributes later
480                         * (in the post-order phase) if this is a new
481                         * directory, or if the -p flag is in effect.
482                         */
483                        curr->fts_number = pflag || dne;
484                        break;
485                case S_IFBLK:
486                case S_IFCHR:
487                        if (Rflag) {
488                                if (copy_special(cp_globals, curr->fts_statp, !dne))
489                                        badcp = rval = 1;
490                        } else {
491                                if (copy_file(cp_globals, curr, dne))
492                                        badcp = rval = 1;
493                        }
494                        break;
495                case S_IFSOCK:
496                        warnx("%s is a socket (not copied).",
497                                    curr->fts_path);
498                case S_IFIFO:
499                        if (Rflag) {
500                                if (copy_fifo(cp_globals, curr->fts_statp, !dne))
501                                        badcp = rval = 1;
502                        } else {
503                                if (copy_file(cp_globals, curr, dne))
504                                        badcp = rval = 1;
505                        }
506                        break;
507                default:
508                        if (copy_file(cp_globals, curr, dne))
509                                badcp = rval = 1;
510                        break;
511                }
512                if (vflag && !badcp)
513                        (void)printf("%s -> %s\n", curr->fts_path, to.p_path);
514        }
515        if (errno)
516                err(exit_jump, 1, "fts_read");
517        fts_close(ftsp);
518        return (rval);
519}
520
521/*
522 * mastercmp --
523 *      The comparison function for the copy order.  The order is to copy
524 *      non-directory files before directory files.  The reason for this
525 *      is because files tend to be in the same cylinder group as their
526 *      parent directory, whereas directories tend not to be.  Copying the
527 *      files first reduces seeking.
528 */
529int
530mastercmp(const FTSENT **a, const FTSENT **b)
531{
532        int a_info, b_info;
533
534        a_info = (*a)->fts_info;
535        if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR)
536                return (0);
537        b_info = (*b)->fts_info;
538        if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR)
539                return (0);
540        if (a_info == FTS_D)
541                return (-1);
542        if (b_info == FTS_D)
543                return (1);
544        return (0);
545}
546
547rtems_shell_cmd_t rtems_shell_CP_Command = {
548  "cp",                                                /* name */
549  "cp [-R [-H | -L | -P]] [-f | -i] [-pv] src target", /* usage */
550  "files",                                             /* topic */
551  rtems_shell_main_cp,                                 /* command */
552  NULL,                                                /* alias */
553  NULL                                                 /* next */
554};
555 
Note: See TracBrowser for help on using the repository browser.