]>
git.saurik.com Git - apple/file_cmds.git/blob - cp/cp.c
   1 /*      $NetBSD: cp.c,v 1.24 1998/08/19 01:29:11 thorpej Exp $  */ 
   4  * Copyright (c) 1988, 1993, 1994 
   5  *      The Regents of the University of California.  All rights reserved. 
   7  * This code is derived from software contributed to Berkeley by 
   8  * David Hitz of Auspex Systems Inc. 
  10  * Redistribution and use in source and binary forms, with or without 
  11  * modification, are permitted provided that the following conditions 
  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. All advertising materials mentioning features or use of this software 
  19  *    must display the following acknowledgement: 
  20  *      This product includes software developed by the University of 
  21  *      California, Berkeley and its contributors. 
  22  * 4. Neither the name of the University nor the names of its contributors 
  23  *    may be used to endorse or promote products derived from this software 
  24  *    without specific prior written permission. 
  26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 
  27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
  28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
  29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 
  30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
  31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
  32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
  33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
  34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
  35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
  39 #include <sys/cdefs.h> 
  42 "@(#) Copyright (c) 1988, 1993, 1994\n\ 
  43         The Regents of the University of California.  All rights reserved.\n"); 
  48 static char sccsid
[] = "@(#)cp.c        8.5 (Berkeley) 4/29/95"; 
  50 __RCSID("$NetBSD: cp.c,v 1.24 1998/08/19 01:29:11 thorpej Exp $"); 
  55  * Cp copies source files to target files. 
  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. 
  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. 
  69 #include <sys/param.h> 
  86 #define STRIP_TRAILING_SLASH(p) {                                       \ 
  87         while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')      \ 
  91 PATH_T to 
= { to
.p_path
, "" }; 
  94 int Rflag
, iflag
, pflag
, rflag
, fflag
; 
  97 enum op 
{ FILE_TO_FILE
, FILE_TO_DIR
, DIR_TO_DNE 
}; 
  99 int main 
__P((int, char *[])); 
 100 int copy 
__P((char *[], enum op
, int)); 
 101 int mastercmp 
__P((const FTSENT 
**, const FTSENT 
**)); 
 108         struct stat to_stat
, tmp_stat
; 
 110         int Hflag
, Lflag
, Pflag
, ch
, fts_options
, r
; 
 113         Hflag 
= Lflag 
= Pflag 
= Rflag 
= 0; 
 114         while ((ch 
= getopt(argc
, argv
, "HLPRfipr")) != -1)  
 136                         iflag 
= isatty(fileno(stdin
)); 
 156         fts_options 
= FTS_NOCHDIR 
| FTS_PHYSICAL
; 
 160                     "the -R and -r options may not be specified together."); 
 161                 if (Hflag 
|| Lflag 
|| Pflag
) 
 163         "the -H, -L, and -P options may not be specified with the -r option."); 
 164                 fts_options 
&= ~FTS_PHYSICAL
; 
 165                 fts_options 
|= FTS_LOGICAL
; 
 169                         fts_options 
|= FTS_COMFOLLOW
; 
 171                         fts_options 
&= ~FTS_PHYSICAL
; 
 172                         fts_options 
|= FTS_LOGICAL
; 
 175                 fts_options 
&= ~FTS_PHYSICAL
; 
 176                 fts_options 
|= FTS_LOGICAL
; 
 181         /* Copy the umask for explicit mode setting. */ 
 183         (void)umask(myumask
); 
 185         /* Save the target base in "to". */ 
 186         target 
= argv
[--argc
]; 
 187         if (strlen(target
) > MAXPATHLEN
) 
 188                 errx(1, "%s: name too long", target
); 
 189         (void)strcpy(to
.p_path
, target
); 
 190         to
.p_end 
= to
.p_path 
+ strlen(to
.p_path
); 
 191         if (to
.p_path 
== to
.p_end
) { 
 195         STRIP_TRAILING_SLASH(to
); 
 196         to
.target_end 
= to
.p_end
; 
 198         /* Set end of argument list for fts(3). */ 
 202          * Cp has two distinct cases: 
 204          * cp [-R] source target 
 205          * cp [-R] source1 ... sourceN directory 
 207          * In both cases, source can be either a file or a directory. 
 209          * In (1), the target becomes a copy of the source. That is, if the 
 210          * source is a file, the target will be a file, and likewise for 
 213          * In (2), the real target is not directory, but "directory/source". 
 215         r 
= stat(to
.p_path
, &to_stat
); 
 216         if (r 
== -1 && errno 
!= ENOENT
) 
 217                 err(1, "%s", to
.p_path
); 
 218         if (r 
== -1 || !S_ISDIR(to_stat
.st_mode
)) { 
 220                  * Case (1).  Target is not a directory. 
 227                  * Need to detect the case: 
 229                  * Where dir is a directory and foo does not exist, where 
 230                  * we want pathname concatenations turned on but not for 
 231                  * the initial mkdir(). 
 234                         if (rflag 
|| (Rflag 
&& (Lflag 
|| Hflag
))) 
 235                                 r 
= stat(*argv
, &tmp_stat
); 
 237                                 r 
= lstat(*argv
, &tmp_stat
); 
 241                         if (S_ISDIR(tmp_stat
.st_mode
) && (Rflag 
|| rflag
)) 
 249                  * Case (2).  Target is a directory. 
 254         exit (copy(argv
, type
, fts_options
)); 
 259 copy(argv
, type
, fts_options
) 
 267         int base
, dne
, nlen
, rval
; 
 270         base 
= 0;       /* XXX gcc -Wuninitialized (see comment below) */ 
 272         if ((ftsp 
= fts_open(argv
, fts_options
, mastercmp
)) == NULL
) 
 274         for (rval 
= 0; (curr 
= fts_read(ftsp
)) != NULL
;) { 
 275                 switch (curr
->fts_info
) { 
 279                             curr
->fts_path
, strerror(curr
->fts_errno
)); 
 282                 case FTS_DC
:                    /* Warn, continue. */ 
 283                         warnx("%s: directory causes a cycle", curr
->fts_path
); 
 289                  * If we are in case (2) or (3) above, we need to append the  
 290                  * source name to the target name.   
 292                 if (type 
!= FILE_TO_FILE
) { 
 293                         if ((curr
->fts_namelen 
+ 
 294                             to
.target_end 
- to
.p_path 
+ 1) > MAXPATHLEN
) { 
 295                                 warnx("%s/%s: name too long (not copied)",  
 296                                     to
.p_path
, curr
->fts_name
); 
 302                          * Need to remember the roots of traversals to create 
 303                          * correct pathnames.  If there's a directory being 
 304                          * copied to a non-existent directory, e.g. 
 305                          *      cp -R a/dir noexist 
 306                          * the resulting path name should be noexist/foo, not 
 307                          * noexist/dir/foo (where foo is a file in dir), which 
 308                          * is the case where the target exists. 
 310                          * Also, check for "..".  This is for correct path 
 311                          * concatentation for paths ending in "..", e.g. 
 313                          * Paths ending in ".." are changed to ".".  This is 
 314                          * tricky, but seems the easiest way to fix the problem. 
 317                          * Since the first level MUST be FTS_ROOTLEVEL, base 
 318                          * is always initialized. 
 320                         if (curr
->fts_level 
== FTS_ROOTLEVEL
) { 
 321                                 if (type 
!= DIR_TO_DNE
) { 
 322                                         p 
= strrchr(curr
->fts_path
, '/'); 
 323                                         base 
= (p 
== NULL
) ? 0 :  
 324                                             (int)(p 
- curr
->fts_path 
+ 1); 
 326                                         if (!strcmp(&curr
->fts_path
[base
],  
 330                                         base 
= curr
->fts_pathlen
; 
 333                         p 
= &curr
->fts_path
[base
]; 
 334                         nlen 
= curr
->fts_pathlen 
- base
; 
 337                         if (*p 
!= '/' && *(tmp 
- 1) != '/') 
 341                         (void)strncat(tmp
, p
, nlen
); 
 342                         to
.p_end 
= tmp 
+ nlen
; 
 344                         STRIP_TRAILING_SLASH(to
); 
 347                 /* Not an error but need to remember it happened */ 
 348                 if (stat(to
.p_path
, &to_stat
) == -1) 
 351                         if (to_stat
.st_dev 
== curr
->fts_statp
->st_dev 
&& 
 352                             to_stat
.st_ino 
== curr
->fts_statp
->st_ino
) { 
 353                                 warnx("%s and %s are identical (not copied).", 
 354                                     to
.p_path
, curr
->fts_path
); 
 356                                 if (S_ISDIR(curr
->fts_statp
->st_mode
)) 
 357                                         (void)fts_set(ftsp
, curr
, FTS_SKIP
); 
 360                         if (!S_ISDIR(curr
->fts_statp
->st_mode
) && 
 361                             S_ISDIR(to_stat
.st_mode
)) { 
 362                 warnx("cannot overwrite directory %s with non-directory %s", 
 363                                     to
.p_path
, curr
->fts_path
); 
 370                 switch (curr
->fts_statp
->st_mode 
& S_IFMT
) { 
 372                         if (copy_link(curr
, !dne
)) 
 376                         if (!Rflag 
&& !rflag
) { 
 377                                 if (curr
->fts_info 
== FTS_DP
) 
 378                                         warnx("%s is a directory (not copied).", 
 380                                 (void)fts_set(ftsp
, curr
, FTS_SKIP
); 
 386                          * Directories get noticed twice: 
 387                          *  In the first pass, create it if needed. 
 388                          *  In the second pass, after the children have been copied, set the permissions. 
 390                         if (curr
->fts_info 
== FTS_D
) /* First pass */ 
 393                                  * If the directory doesn't exist, create the new 
 394                                  * one with the from file mode plus owner RWX bits, 
 395                                  * modified by the umask.  Trade-off between being 
 396                                  * able to write the directory (if from directory is 
 397                                  * 555) and not causing a permissions race.  If the 
 398                                  * umask blocks owner writes, we fail.. 
 402                                             curr
->fts_statp
->st_mode 
| S_IRWXU
) < 0) 
 403                                                 err(1, "%s", to
.p_path
); 
 404                                 } else if (!S_ISDIR(to_stat
.st_mode
)) { 
 406                                         err(1, "%s", to
.p_path
); 
 409                         else if (curr
->fts_info 
== FTS_DP
) /* Second pass */ 
 412                                  * If not -p and directory didn't exist, set it to be 
 413                                  * the same as the from directory, umodified by the  
 414                                  * umask; arguably wrong, but it's been that way  
 417                                 if (pflag 
&& setfile(curr
->fts_statp
, 0)) 
 420                                         (void)chmod(to
.p_path
,  
 421                                             curr
->fts_statp
->st_mode
); 
 425                                 warnx("directory %s encountered when not expected.", curr
->fts_path
); 
 434                                 if (copy_special(curr
->fts_statp
, !dne
)) 
 437                                 if (copy_file(curr
, dne
)) 
 442                                 if (copy_fifo(curr
->fts_statp
, !dne
)) 
 445                                 if (copy_file(curr
, dne
)) 
 449                         if (copy_file(curr
, dne
)) 
 461  *      The comparison function for the copy order.  The order is to copy 
 462  *      non-directory files before directory files.  The reason for this 
 463  *      is because files tend to be in the same cylinder group as their 
 464  *      parent directory, whereas directories tend not to be.  Copying the 
 465  *      files first reduces seeking. 
 469         const FTSENT 
**a
, **b
; 
 473         a_info 
= (*a
)->fts_info
; 
 474         if (a_info 
== FTS_ERR 
|| a_info 
== FTS_NS 
|| a_info 
== FTS_DNR
) 
 476         b_info 
= (*b
)->fts_info
; 
 477         if (b_info 
== FTS_ERR 
|| b_info 
== FTS_NS 
|| b_info 
== FTS_DNR
)