VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/cp.c@ 3387

Last change on this file since 3387 was 3221, checked in by bird, 6 years ago

kmk_cp: use getopt_r and stop using global and static variables.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.3 KB
Line 
1/*
2 * Copyright (c) 1988, 1993, 1994
3 * The Regents of the University of California. 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 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#if 0
34#ifndef lint
35static char const copyright[] =
36"@(#) Copyright (c) 1988, 1993, 1994\n\
37 The Regents of the University of California. All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)cp.c 8.2 (Berkeley) 4/1/94";
42#endif /* not lint */
43#include <sys/cdefs.h>
44__FBSDID("$FreeBSD: src/bin/cp/cp.c,v 1.50 2004/04/06 20:06:44 markm Exp $");
45#endif
46
47/*
48 * Cp copies source files to target files.
49 *
50 * The global PATH_T structure "to" always contains the path to the
51 * current target file. Since fts(3) does not change directories,
52 * this path can be either absolute or dot-relative.
53 *
54 * The basic algorithm is to initialize "to" and use fts(3) to traverse
55 * the file hierarchy rooted in the argument list. A trivial case is the
56 * case of 'cp file1 file2'. The more interesting case is the case of
57 * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
58 * path (relative to the root of the traversal) is appended to dir (stored
59 * in "to") to form the final target path.
60 */
61
62
63/*********************************************************************************************************************************
64* Header Files *
65*********************************************************************************************************************************/
66#define FAKES_NO_GETOPT_H /* bird */
67#include "config.h"
68#include <sys/types.h>
69#include <sys/stat.h>
70
71#include "err.h"
72#include <errno.h>
73#include "fts.h"
74#include <limits.h>
75#include <signal.h>
76#include <stdio.h>
77#include <stdlib.h>
78#include <string.h>
79#include <unistd.h>
80#include "getopt_r.h"
81#include "k/kDefs.h"
82#ifdef _MSC_VER
83# include "mscfakes.h"
84#endif
85#include "cp_extern.h"
86#include "kmkbuiltin.h"
87#include "kbuild_protection.h"
88
89#if defined(_MSC_VER) || defined(__gnu_linux__) || defined(__linux__)
90extern size_t strlcpy(char *, const char *, size_t);
91#endif
92
93
94/*********************************************************************************************************************************
95* Defined Constants And Macros *
96*********************************************************************************************************************************/
97#ifndef S_IFWHT
98#define S_IFWHT 0
99#define S_ISWHT(s) 0
100#define undelete(s) (-1)
101#endif
102
103#ifndef S_ISTXT
104#ifdef S_ISVTX
105#define S_ISTXT S_ISVTX
106#else
107#define S_ISTXT 0
108#endif
109#endif /* !S_ISTXT */
110
111#ifndef __unused
112# define __unused
113#endif
114
115#if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
116# define IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
117#else
118# define IS_SLASH(ch) ((ch) == '/')
119#endif
120
121#define STRIP_TRAILING_SLASH(p) { \
122 while ((p).p_end > (p).p_path + 1 && IS_SLASH((p).p_end[-1])) \
123 *--(p).p_end = 0; \
124}
125
126/*********************************************************************************************************************************
127* Structures and Typedefs *
128*********************************************************************************************************************************/
129typedef struct CPINSTANCE
130{
131 CPUTILSINSTANCE Utils;
132 int Rflag, rflag;
133 int cp_ignore_non_existing, cp_changed_only;
134 KBUILDPROTECTION g_ProtData;
135} CPINSTANCE;
136
137/* have wrappers for globals in cp_extern! */
138
139
140enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
141
142
143/*********************************************************************************************************************************
144* Global Variables *
145*********************************************************************************************************************************/
146enum cp_arg {
147 CP_OPT_HELP = 261,
148 CP_OPT_VERSION,
149 CP_OPT_IGNORE_NON_EXISTING,
150 CP_OPT_CHANGED,
151 CP_OPT_DISABLE_PROTECTION,
152 CP_OPT_ENABLE_PROTECTION,
153 CP_OPT_ENABLE_FULL_PROTECTION,
154 CP_OPT_DISABLE_FULL_PROTECTION,
155 CP_OPT_PROTECTION_DEPTH
156};
157
158static struct option long_options[] =
159{
160 { "help", no_argument, 0, CP_OPT_HELP },
161 { "version", no_argument, 0, CP_OPT_VERSION },
162 { "ignore-non-existing", no_argument, 0, CP_OPT_IGNORE_NON_EXISTING },
163 { "changed", no_argument, 0, CP_OPT_CHANGED },
164 { "disable-protection", no_argument, 0, CP_OPT_DISABLE_PROTECTION },
165 { "enable-protection", no_argument, 0, CP_OPT_ENABLE_PROTECTION },
166 { "enable-full-protection", no_argument, 0, CP_OPT_ENABLE_FULL_PROTECTION },
167 { "disable-full-protection", no_argument, 0, CP_OPT_DISABLE_FULL_PROTECTION },
168 { "protection-depth", required_argument, 0, CP_OPT_PROTECTION_DEPTH },
169 { 0, 0, 0, 0 },
170};
171
172static char emptystring[] = "";
173
174#if defined(SIGINFO) && defined(KMK_BUILTIN_STANDALONE)
175volatile sig_atomic_t g_cp_info;
176#endif
177
178
179/*********************************************************************************************************************************
180* Internal Functions *
181*********************************************************************************************************************************/
182static int copy(CPINSTANCE *pThis, char * const *, enum op, int);
183#ifdef FTSCALL
184static int FTSCALL mastercmp(const FTSENT * const *, const FTSENT * const *);
185#else
186static int mastercmp(const FTSENT **, const FTSENT **);
187#endif
188#if defined(SIGINFO) && defined(KMK_BUILTIN_STANDALONE)
189static void siginfo(int __unused);
190#endif
191static int usage(PKMKBUILTINCTX, int);
192
193int
194kmk_builtin_cp(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx)
195{
196 CPINSTANCE This;
197 struct getopt_state_r gos;
198 struct stat to_stat, tmp_stat;
199 enum op type;
200 int Hflag, Lflag, Pflag, ch, fts_options, r, have_trailing_slash, rc;
201 char *target;
202
203 /* init globals */
204 This.Utils.pCtx = pCtx;
205 This.Utils.to.p_end = This.Utils.to.p_path;
206 This.Utils.to.target_end = emptystring;
207 memset(This.Utils.to.p_path, 0, sizeof(This.Utils.to.p_path));
208 This.Utils.fflag = 0;
209 This.Utils.iflag = 0;
210 This.Utils.nflag = 0;
211 This.Utils.pflag = 0;
212 This.Utils.vflag = 0;
213 This.Rflag = 0;
214 This.rflag = 0;
215 This.cp_ignore_non_existing = This.cp_changed_only = 0;
216 kBuildProtectionInit(&This.g_ProtData, pCtx);
217
218 Hflag = Lflag = Pflag = 0;
219 getopt_initialize_r(&gos, argc, argv, "HLPRfinprv", long_options, envp, pCtx);
220 while ((ch = getopt_long_r(&gos, NULL)) != -1)
221 switch (ch) {
222 case 'H':
223 Hflag = 1;
224 Lflag = Pflag = 0;
225 break;
226 case 'L':
227 Lflag = 1;
228 Hflag = Pflag = 0;
229 break;
230 case 'P':
231 Pflag = 1;
232 Hflag = Lflag = 0;
233 break;
234 case 'R':
235 This.Rflag = 1;
236 break;
237 case 'f':
238 This.Utils.fflag = 1;
239 This.Utils.iflag = This.Utils.nflag = 0;
240 break;
241 case 'i':
242 This.Utils.iflag = 1;
243 This.Utils.fflag = This.Utils.nflag = 0;
244 break;
245 case 'n':
246 This.Utils.nflag = 1;
247 This.Utils.fflag = This.Utils.iflag = 0;
248 break;
249 case 'p':
250 This.Utils.pflag = 1;
251 break;
252 case 'r':
253 This.rflag = 1;
254 break;
255 case 'v':
256 This.Utils.vflag = 1;
257 break;
258 case CP_OPT_HELP:
259 usage(pCtx, 0);
260 kBuildProtectionTerm(&This.g_ProtData);
261 return 0;
262 case CP_OPT_VERSION:
263 kBuildProtectionTerm(&This.g_ProtData);
264 return kbuild_version(argv[0]);
265 case CP_OPT_IGNORE_NON_EXISTING:
266 This.cp_ignore_non_existing = 1;
267 break;
268 case CP_OPT_CHANGED:
269 This.cp_changed_only = 1;
270 break;
271 case CP_OPT_DISABLE_PROTECTION:
272 kBuildProtectionDisable(&This.g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE);
273 break;
274 case CP_OPT_ENABLE_PROTECTION:
275 kBuildProtectionEnable(&This.g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE);
276 break;
277 case CP_OPT_ENABLE_FULL_PROTECTION:
278 kBuildProtectionEnable(&This.g_ProtData, KBUILDPROTECTIONTYPE_FULL);
279 break;
280 case CP_OPT_DISABLE_FULL_PROTECTION:
281 kBuildProtectionDisable(&This.g_ProtData, KBUILDPROTECTIONTYPE_FULL);
282 break;
283 case CP_OPT_PROTECTION_DEPTH:
284 if (kBuildProtectionSetDepth(&This.g_ProtData, gos.optarg)) {
285 kBuildProtectionTerm(&This.g_ProtData);
286 return 1;
287 }
288 break;
289 default:
290 kBuildProtectionTerm(&This.g_ProtData);
291 return usage(pCtx, 1);
292 }
293 argc -= gos.optind;
294 argv += gos.optind;
295
296 if (argc < 2) {
297 kBuildProtectionTerm(&This.g_ProtData);
298 return usage(pCtx, 1);
299 }
300
301 fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
302 if (This.rflag) {
303 if (This.Rflag) {
304 kBuildProtectionTerm(&This.g_ProtData);
305 return errx(pCtx, 1,
306 "the -R and -r options may not be specified together.");
307 }
308 if (Hflag || Lflag || Pflag)
309 errx(pCtx, 1,
310 "the -H, -L, and -P options may not be specified with the -r option.");
311 fts_options &= ~FTS_PHYSICAL;
312 fts_options |= FTS_LOGICAL;
313 }
314 if (This.Rflag) {
315 if (Hflag)
316 fts_options |= FTS_COMFOLLOW;
317 if (Lflag) {
318 fts_options &= ~FTS_PHYSICAL;
319 fts_options |= FTS_LOGICAL;
320 }
321 } else {
322 fts_options &= ~FTS_PHYSICAL;
323 fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
324 }
325#if defined(SIGINFO) && defined(KMK_BUILTIN_STANDALONE)
326 (void)signal(SIGINFO, siginfo);
327#endif
328
329 /* Save the target base in "to". */
330 target = argv[--argc];
331 if (strlcpy(This.Utils.to.p_path, target, sizeof(This.Utils.to.p_path)) >= sizeof(This.Utils.to.p_path)) {
332 kBuildProtectionTerm(&This.g_ProtData);
333 return errx(pCtx, 1, "%s: name too long", target);
334 }
335 This.Utils.to.p_end = This.Utils.to.p_path + strlen(This.Utils.to.p_path);
336 if (This.Utils.to.p_path == This.Utils.to.p_end) {
337 *This.Utils.to.p_end++ = '.';
338 *This.Utils.to.p_end = 0;
339 }
340 have_trailing_slash = IS_SLASH(This.Utils.to.p_end[-1]);
341 if (have_trailing_slash)
342 STRIP_TRAILING_SLASH(This.Utils.to);
343 This.Utils.to.target_end = This.Utils.to.p_end;
344
345 /* Set end of argument list for fts(3). */
346 argv[argc] = NULL;
347
348 /*
349 * Cp has two distinct cases:
350 *
351 * cp [-R] source target
352 * cp [-R] source1 ... sourceN directory
353 *
354 * In both cases, source can be either a file or a directory.
355 *
356 * In (1), the target becomes a copy of the source. That is, if the
357 * source is a file, the target will be a file, and likewise for
358 * directories.
359 *
360 * In (2), the real target is not directory, but "directory/source".
361 */
362 r = stat(This.Utils.to.p_path, &to_stat);
363 if (r == -1 && errno != ENOENT) {
364 kBuildProtectionTerm(&This.g_ProtData);
365 return err(pCtx, 1, "stat: %s", This.Utils.to.p_path);
366 }
367 if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
368 /*
369 * Case (1). Target is not a directory.
370 */
371 if (argc > 1) {
372 kBuildProtectionTerm(&This.g_ProtData);
373 return usage(pCtx, 1);
374 }
375 /*
376 * Need to detect the case:
377 * cp -R dir foo
378 * Where dir is a directory and foo does not exist, where
379 * we want pathname concatenations turned on but not for
380 * the initial mkdir().
381 */
382 if (r == -1) {
383 if (This.rflag || (This.Rflag && (Lflag || Hflag)))
384 stat(*argv, &tmp_stat);
385 else
386 lstat(*argv, &tmp_stat);
387
388 if (S_ISDIR(tmp_stat.st_mode) && (This.Rflag || This.rflag))
389 type = DIR_TO_DNE;
390 else
391 type = FILE_TO_FILE;
392 } else
393 type = FILE_TO_FILE;
394
395 if (have_trailing_slash && type == FILE_TO_FILE) {
396 kBuildProtectionTerm(&This.g_ProtData);
397 if (r == -1)
398 return errx(pCtx, 1, "directory %s does not exist",
399 This.Utils.to.p_path);
400 else
401 return errx(pCtx, 1, "%s is not a directory", This.Utils.to.p_path);
402 }
403 } else
404 /*
405 * Case (2). Target is a directory.
406 */
407 type = FILE_TO_DIR;
408
409 /* Finally, check that the "to" directory isn't protected. */
410 rc = 1;
411 if (!kBuildProtectionScanEnv(&This.g_ProtData, envp, "KMK_CP_")
412 && !kBuildProtectionEnforce(&This.g_ProtData,
413 This.Rflag || This.rflag
414 ? KBUILDPROTECTIONTYPE_RECURSIVE
415 : KBUILDPROTECTIONTYPE_FULL,
416 This.Utils.to.p_path)) {
417 rc = copy(&This, argv, type, fts_options);
418 }
419
420 kBuildProtectionTerm(&This.g_ProtData);
421 return rc;
422}
423
424#ifdef KMK_BUILTIN_STANDALONE
425int main(int argc, char **argv, char **envp)
426{
427 KMKBUILTINCTX Ctx = { "kmk_cp", NULL };
428 return kmk_builtin_cp(argc, argv, envp, &Ctx);
429}
430#endif
431
432static int
433copy(CPINSTANCE *pThis, char * const *argv, enum op type, int fts_options)
434{
435 struct stat to_stat;
436 FTS *ftsp;
437 FTSENT *curr;
438 int base = 0, dne, badcp, rval;
439 size_t nlen;
440 char *p, *target_mid;
441 mode_t mask, mode;
442
443 /*
444 * Keep an inverted copy of the umask, for use in correcting
445 * permissions on created directories when not using -p.
446 */
447 mask = ~umask(0777);
448 umask(~mask);
449
450 if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL)
451 return err(pThis->Utils.pCtx, 1, "fts_open");
452 for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) {
453 int copied = 0;
454
455 switch (curr->fts_info) {
456 case FTS_NS:
457 if ( pThis->cp_ignore_non_existing
458 && curr->fts_errno == ENOENT) {
459 if (pThis->Utils.vflag) {
460 warnx(pThis->Utils.pCtx, "fts: %s: %s", curr->fts_path,
461 strerror(curr->fts_errno));
462 }
463 continue;
464 }
465 /* fall thru */
466 case FTS_DNR:
467 case FTS_ERR:
468 warnx(pThis->Utils.pCtx, "fts: %s: %s",
469 curr->fts_path, strerror(curr->fts_errno));
470 badcp = rval = 1;
471 continue;
472 case FTS_DC: /* Warn, continue. */
473 warnx(pThis->Utils.pCtx, "%s: directory causes a cycle", curr->fts_path);
474 badcp = rval = 1;
475 continue;
476 default:
477 ;
478 }
479
480 /*
481 * If we are in case (2) or (3) above, we need to append the
482 * source name to the target name.
483 */
484 if (type != FILE_TO_FILE) {
485 /*
486 * Need to remember the roots of traversals to create
487 * correct pathnames. If there's a directory being
488 * copied to a non-existent directory, e.g.
489 * cp -R a/dir noexist
490 * the resulting path name should be noexist/foo, not
491 * noexist/dir/foo (where foo is a file in dir), which
492 * is the case where the target exists.
493 *
494 * Also, check for "..". This is for correct path
495 * concatenation for paths ending in "..", e.g.
496 * cp -R .. /tmp
497 * Paths ending in ".." are changed to ".". This is
498 * tricky, but seems the easiest way to fix the problem.
499 *
500 * XXX
501 * Since the first level MUST be FTS_ROOTLEVEL, base
502 * is always initialized.
503 */
504 if (curr->fts_level == FTS_ROOTLEVEL) {
505 if (type != DIR_TO_DNE) {
506 p = strrchr(curr->fts_path, '/');
507#if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
508 if (strrchr(curr->fts_path, '\\') > p)
509 p = strrchr(curr->fts_path, '\\');
510#endif
511 base = (p == NULL) ? 0 :
512 (int)(p - curr->fts_path + 1);
513
514 if (!strcmp(&curr->fts_path[base],
515 ".."))
516 base += 1;
517 } else
518 base = curr->fts_pathlen;
519 }
520
521 p = &curr->fts_path[base];
522 nlen = curr->fts_pathlen - base;
523 target_mid = pThis->Utils.to.target_end;
524 if (!IS_SLASH(*p) && !IS_SLASH(target_mid[-1]))
525 *target_mid++ = '/';
526 *target_mid = 0;
527 if (target_mid - pThis->Utils.to.p_path + nlen >= PATH_MAX) {
528 warnx(pThis->Utils.pCtx, "%s%s: name too long (not copied)",
529 pThis->Utils.to.p_path, p);
530 badcp = rval = 1;
531 continue;
532 }
533 (void)strncat(target_mid, p, nlen);
534 pThis->Utils.to.p_end = target_mid + nlen;
535 *pThis->Utils.to.p_end = 0;
536 STRIP_TRAILING_SLASH(pThis->Utils.to);
537 }
538
539 if (curr->fts_info == FTS_DP) {
540 /*
541 * We are nearly finished with this directory. If we
542 * didn't actually copy it, or otherwise don't need to
543 * change its attributes, then we are done.
544 */
545 if (!curr->fts_number)
546 continue;
547 /*
548 * If -p is in effect, set all the attributes.
549 * Otherwise, set the correct permissions, limited
550 * by the umask. Optimise by avoiding a chmod()
551 * if possible (which is usually the case if we
552 * made the directory). Note that mkdir() does not
553 * honour setuid, setgid and sticky bits, but we
554 * normally want to preserve them on directories.
555 */
556 if (pThis->Utils.pflag) {
557 if (copy_file_attribs(&pThis->Utils, curr->fts_statp, -1))
558 rval = 1;
559 } else {
560 mode = curr->fts_statp->st_mode;
561 if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) ||
562 ((mode | S_IRWXU) & mask) != (mode & mask))
563 if (chmod(pThis->Utils.to.p_path, mode & mask) != 0){
564 warn(pThis->Utils.pCtx, "chmod: %s", pThis->Utils.to.p_path);
565 rval = 1;
566 }
567 }
568 continue;
569 }
570
571 /* Not an error but need to remember it happened */
572 if (stat(pThis->Utils.to.p_path, &to_stat) == -1)
573 dne = 1;
574 else {
575 if (to_stat.st_dev == curr->fts_statp->st_dev &&
576 to_stat.st_dev != 0 &&
577 to_stat.st_ino == curr->fts_statp->st_ino &&
578 to_stat.st_ino != 0) {
579 warnx(pThis->Utils.pCtx, "%s and %s are identical (not copied).",
580 pThis->Utils.to.p_path, curr->fts_path);
581 badcp = rval = 1;
582 if (S_ISDIR(curr->fts_statp->st_mode))
583 (void)fts_set(ftsp, curr, FTS_SKIP);
584 continue;
585 }
586 if (!S_ISDIR(curr->fts_statp->st_mode) &&
587 S_ISDIR(to_stat.st_mode)) {
588 warnx(pThis->Utils.pCtx, "cannot overwrite directory %s with "
589 "non-directory %s",
590 pThis->Utils.to.p_path, curr->fts_path);
591 badcp = rval = 1;
592 continue;
593 }
594 dne = 0;
595 }
596
597 switch (curr->fts_statp->st_mode & S_IFMT) {
598#ifdef S_IFLNK
599 case S_IFLNK:
600 /* Catch special case of a non-dangling symlink */
601 if ((fts_options & FTS_LOGICAL) ||
602 ((fts_options & FTS_COMFOLLOW) &&
603 curr->fts_level == 0)) {
604 if (copy_file(&pThis->Utils, curr, dne, pThis->cp_changed_only, &copied))
605 badcp = rval = 1;
606 } else {
607 if (copy_link(&pThis->Utils, curr, !dne))
608 badcp = rval = 1;
609 }
610 break;
611#endif
612 case S_IFDIR:
613 if (!pThis->Rflag && !pThis->rflag) {
614 warnx(pThis->Utils.pCtx, "%s is a directory (not copied).",
615 curr->fts_path);
616 (void)fts_set(ftsp, curr, FTS_SKIP);
617 badcp = rval = 1;
618 break;
619 }
620 /*
621 * If the directory doesn't exist, create the new
622 * one with the from file mode plus owner RWX bits,
623 * modified by the umask. Trade-off between being
624 * able to write the directory (if from directory is
625 * 555) and not causing a permissions race. If the
626 * umask blocks owner writes, we fail..
627 */
628 if (dne) {
629 if (mkdir(pThis->Utils.to.p_path,
630 curr->fts_statp->st_mode | S_IRWXU) < 0)
631 return err(pThis->Utils.pCtx, 1, "mkdir: %s", pThis->Utils.to.p_path);
632 } else if (!S_ISDIR(to_stat.st_mode)) {
633 errno = ENOTDIR;
634 return err(pThis->Utils.pCtx, 1, "to-mode: %s", pThis->Utils.to.p_path);
635 }
636 /*
637 * Arrange to correct directory attributes later
638 * (in the post-order phase) if this is a new
639 * directory, or if the -p flag is in effect.
640 */
641 curr->fts_number = pThis->Utils.pflag || dne;
642 break;
643#ifdef S_IFBLK
644 case S_IFBLK:
645#endif
646 case S_IFCHR:
647 if (pThis->Rflag) {
648 if (copy_special(&pThis->Utils, curr->fts_statp, !dne))
649 badcp = rval = 1;
650 } else {
651 if (copy_file(&pThis->Utils, curr, dne, pThis->cp_changed_only, &copied))
652 badcp = rval = 1;
653 }
654 break;
655#ifdef S_IFIFO
656 case S_IFIFO:
657#endif
658 if (pThis->Rflag) {
659 if (copy_fifo(&pThis->Utils, curr->fts_statp, !dne))
660 badcp = rval = 1;
661 } else {
662 if (copy_file(&pThis->Utils, curr, dne, pThis->cp_changed_only, &copied))
663 badcp = rval = 1;
664 }
665 break;
666 default:
667 if (copy_file(&pThis->Utils, curr, dne, pThis->cp_changed_only, &copied))
668 badcp = rval = 1;
669 break;
670 }
671 if (pThis->Utils.vflag && !badcp)
672 kmk_builtin_ctx_printf(pThis->Utils.pCtx, 0, copied ? "%s -> %s\n" : "%s matches %s - not copied\n",
673 curr->fts_path, pThis->Utils.to.p_path);
674 }
675 if (errno)
676 return err(pThis->Utils.pCtx, 1, "fts_read");
677 return (rval);
678}
679
680/*
681 * mastercmp --
682 * The comparison function for the copy order. The order is to copy
683 * non-directory files before directory files. The reason for this
684 * is because files tend to be in the same cylinder group as their
685 * parent directory, whereas directories tend not to be. Copying the
686 * files first reduces seeking.
687 */
688#ifdef FTSCALL
689static int FTSCALL mastercmp(const FTSENT * const *a, const FTSENT * const *b)
690#else
691static int mastercmp(const FTSENT **a, const FTSENT **b)
692#endif
693{
694 int a_info, b_info;
695
696 a_info = (*a)->fts_info;
697 if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR)
698 return (0);
699 b_info = (*b)->fts_info;
700 if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR)
701 return (0);
702 if (a_info == FTS_D)
703 return (-1);
704 if (b_info == FTS_D)
705 return (1);
706 return (0);
707}
708
709#if defined(SIGINFO) && defined(KMK_BUILTIN_STANDALONE)
710static void
711siginfo(int sig __unused)
712{
713
714 g_cp_info = 1;
715}
716#endif
717
718
719static int
720usage(PKMKBUILTINCTX pCtx, int fIsErr)
721{
722 kmk_builtin_ctx_printf(pCtx, fIsErr,
723"usage: %s [options] src target\n"
724" or: %s [options] src1 ... srcN directory\n"
725" or: %s --help\n"
726" or: %s --version\n"
727"\n"
728"Options:\n"
729" -R Recursive copy.\n"
730" -H Follow symbolic links on the commandline. Only valid with -R.\n"
731" -L Follow all symbolic links. Only valid with -R.\n"
732" -P Do not follow symbolic links. Default. Only valid with -R\n"
733" -f Force. Overrides -i and -n.\n"
734" -i Iteractive. Overrides -n and -f.\n"
735" -n Don't overwrite any files. Overrides -i and -f.\n"
736" -v Verbose.\n"
737" --ignore-non-existing\n"
738" Don't fail if the specified source file doesn't exist.\n"
739" --changed\n"
740" Only copy if changed (i.e. compare first).\n"
741" --disable-protection\n"
742" Will disable the protection file protection applied with -R.\n"
743" --enable-protection\n"
744" Will enable the protection file protection applied with -R.\n"
745" --enable-full-protection\n"
746" Will enable the protection file protection for all operations.\n"
747" --disable-full-protection\n"
748" Will disable the protection file protection for all operations.\n"
749" --protection-depth\n"
750" Number or path indicating the file protection depth. Default: %d\n"
751"\n"
752"Environment:\n"
753" KMK_CP_DISABLE_PROTECTION\n"
754" Same as --disable-protection. Overrides command line.\n"
755" KMK_CP_ENABLE_PROTECTION\n"
756" Same as --enable-protection. Overrides everyone else.\n"
757" KMK_CP_ENABLE_FULL_PROTECTION\n"
758" Same as --enable-full-protection. Overrides everyone else.\n"
759" KMK_CP_DISABLE_FULL_PROTECTION\n"
760" Same as --disable-full-protection. Overrides command line.\n"
761" KMK_CP_PROTECTION_DEPTH\n"
762" Same as --protection-depth. Overrides command line.\n"
763"\n"
764"The file protection of the top %d layers of the file hierarchy is there\n"
765"to try prevent makefiles from doing bad things to your system. This\n"
766"protection is not bulletproof, but should help prevent you from shooting\n"
767"yourself in the foot.\n"
768 ,
769 pCtx->pszProgName, pCtx->pszProgName,
770 pCtx->pszProgName, pCtx->pszProgName,
771 kBuildProtectionDefaultDepth(), kBuildProtectionDefaultDepth());
772 return 1;
773}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use