1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  28 /*        All Rights Reserved   */
  29 
  30 /*
  31  * University Copyright- Copyright (c) 1982, 1986, 1988
  32  * The Regents of the University of California
  33  * All Rights Reserved
  34  *
  35  * University Acknowledgment- Portions of this document are derived from
  36  * software developed by the University of California, Berkeley, and its
  37  * contributors.
  38  */
  39 
  40 /*
  41  * Combined mv/cp/ln command:
  42  *      mv file1 file2
  43  *      mv dir1 dir2
  44  *      mv file1 ... filen dir1
  45  */
  46 #include <sys/time.h>
  47 #include <signal.h>
  48 #include <locale.h>
  49 #include <stdarg.h>
  50 #include <sys/acl.h>
  51 #include <libcmdutils.h>
  52 #include <aclutils.h>
  53 #include "getresponse.h"
  54 
  55 #define FTYPE(A)        (A.st_mode)
  56 #define FMODE(A)        (A.st_mode)
  57 #define UID(A)          (A.st_uid)
  58 #define GID(A)          (A.st_gid)
  59 #define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino)
  60 #define ISDIR(A)        ((A.st_mode & S_IFMT) == S_IFDIR)
  61 #define ISDOOR(A)       ((A.st_mode & S_IFMT) == S_IFDOOR)
  62 #define ISLNK(A)        ((A.st_mode & S_IFMT) == S_IFLNK)
  63 #define ISREG(A)        (((A).st_mode & S_IFMT) == S_IFREG)
  64 #define ISDEV(A)        ((A.st_mode & S_IFMT) == S_IFCHR || \
  65                         (A.st_mode & S_IFMT) == S_IFBLK || \
  66                         (A.st_mode & S_IFMT) == S_IFIFO)
  67 #define ISSOCK(A)       ((A.st_mode & S_IFMT) == S_IFSOCK)
  68 
  69 #define DELIM   '/'
  70 #define EQ(x, y)        (strcmp(x, y) == 0)
  71 #define FALSE   0
  72 #define MODEBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
  73 #define TRUE 1
  74 
  75 static char             *dname(char *);
  76 static int              lnkfil(char *, char *);
  77 static int              cpymve(char *, char *);
  78 static int              chkfiles(char *, char **);
  79 static int              rcopy(char *, char *);
  80 static int              chk_different(char *, char *);
  81 static int              chg_time(char *, struct stat);
  82 static int              chg_mode(char *, uid_t, gid_t, mode_t);
  83 static int              copydir(char *, char *);
  84 static int              copyspecial(char *);
  85 static int              getrealpath(char *, char *);
  86 static void             usage(void);
  87 static void             Perror(char *);
  88 static void             Perror2(char *, char *);
  89 static int              use_stdin(void);
  90 static int              copyattributes(char *, char *);
  91 static int              copy_sysattr(char *, char *);
  92 static tree_node_t      *create_tnode(dev_t, ino_t);
  93 
  94 static struct stat      s1, s2, s3, s4;
  95 static int              cpy = FALSE;
  96 static int              mve = FALSE;
  97 static int              lnk = FALSE;
  98 static char             *cmd;
  99 static int              silent = 0;
 100 static int              fflg = 0;
 101 static int              iflg = 0;
 102 static int              pflg = 0;
 103 static int              Rflg = 0;       /* recursive copy */
 104 static int              rflg = 0;       /* recursive copy */
 105 static int              sflg = 0;
 106 static int              Hflg = 0;       /* follow cmd line arg symlink to dir */
 107 static int              Lflg = 0;       /* follow symlinks */
 108 static int              Pflg = 0;       /* do not follow symlinks */
 109 static int              atflg = 0;
 110 static int              attrsilent = 0;
 111 static int              targetexists = 0;
 112 static int              cmdarg;         /* command line argument */
 113 static avl_tree_t       *stree = NULL;  /* source file inode search tree */
 114 static acl_t            *s1acl;
 115 static int              saflg = 0;      /* 'cp' extended system attr. */
 116 static int              srcfd = -1;
 117 static int              targfd = -1;
 118 static int              sourcedirfd = -1;
 119 static int              targetdirfd = -1;
 120 static DIR              *srcdirp = NULL;
 121 static int              srcattrfd = -1;
 122 static int              targattrfd = -1;
 123 static struct stat      attrdir;
 124 
 125 /* Extended system attributes support */
 126 
 127 static int open_source(char  *);
 128 static int open_target_srctarg_attrdirs(char  *, char *);
 129 static int open_attrdirp(char *);
 130 static int traverse_attrfile(struct dirent *, char *, char *, int);
 131 static void rewind_attrdir(DIR *);
 132 static void close_all();
 133 
 134 
 135 int
 136 main(int argc, char *argv[])
 137 {
 138         int c, i, r, errflg = 0;
 139         char target[PATH_MAX];
 140         int (*move)(char *, char *);
 141 
 142         /*
 143          * Determine command invoked (mv, cp, or ln)
 144          */
 145 
 146         if (cmd = strrchr(argv[0], '/'))
 147                 ++cmd;
 148         else
 149                 cmd = argv[0];
 150 
 151         /*
 152          * Set flags based on command.
 153          */
 154 
 155         (void) setlocale(LC_ALL, "");
 156 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
 157 #define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it weren't */
 158 #endif
 159         (void) textdomain(TEXT_DOMAIN);
 160         if (init_yes() < 0) {
 161                 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
 162                     strerror(errno));
 163                 exit(3);
 164         }
 165 
 166         if (EQ(cmd, "mv"))
 167                 mve = TRUE;
 168         else if (EQ(cmd, "ln"))
 169                 lnk = TRUE;
 170         else if (EQ(cmd, "cp"))
 171                 cpy = TRUE;
 172         else {
 173                 (void) fprintf(stderr,
 174                     gettext("Invalid command name (%s); expecting "
 175                     "mv, cp, or ln.\n"), cmd);
 176                 exit(1);
 177         }
 178 
 179         /*
 180          * Check for options:
 181          *      cp  -r|-R [-H|-L|-P] [-fip@/] file1 [file2 ...] target
 182          *      cp [-fiprR@/] file1 [file2 ...] target
 183          *      ln [-f] [-n] [-s] file1 [file2 ...] target
 184          *      ln [-f] [-n] [-s] file1 [file2 ...]
 185          *      mv [-f|i] file1 [file2 ...] target
 186          *      mv [-f|i] dir1 target
 187          */
 188 
 189         if (cpy) {
 190                 while ((c = getopt(argc, argv, "fHiLpPrR@/")) != EOF)
 191                         switch (c) {
 192                         case 'f':
 193                                 fflg++;
 194                                 break;
 195                         case 'i':
 196                                 iflg++;
 197                                 break;
 198                         case 'p':
 199                                 pflg++;
 200 #ifdef XPG4
 201                                 attrsilent = 1;
 202                                 atflg = 0;
 203                                 saflg = 0;
 204 #else
 205                                 if (atflg == 0)
 206                                         attrsilent = 1;
 207 #endif
 208                                 break;
 209                         case 'H':
 210                                 /*
 211                                  * If more than one of -H, -L, or -P are
 212                                  * specified, only the last option specified
 213                                  * determines the behavior.
 214                                  */
 215                                 Lflg = Pflg = 0;
 216                                 Hflg++;
 217                                 break;
 218                         case 'L':
 219                                 Hflg = Pflg = 0;
 220                                 Lflg++;
 221                                 break;
 222                         case 'P':
 223                                 Lflg = Hflg = 0;
 224                                 Pflg++;
 225                                 break;
 226                         case 'R':
 227                                 /*
 228                                  * The default behavior of cp -R|-r
 229                                  * when specified without -H|-L|-P
 230                                  * is -L.
 231                                  */
 232                                 Rflg++;
 233                                 /*FALLTHROUGH*/
 234                         case 'r':
 235                                 rflg++;
 236                                 break;
 237                         case '@':
 238                                 atflg++;
 239                                 attrsilent = 0;
 240 #ifdef XPG4
 241                                 pflg = 0;
 242 #endif
 243                                 break;
 244                         case '/':
 245                                 saflg++;
 246                                 attrsilent = 0;
 247 #ifdef XPG4
 248                                 pflg = 0;
 249 #endif
 250                                 break;
 251                         default:
 252                                 errflg++;
 253                         }
 254 
 255                 /* -R or -r must be specified with -H, -L, or -P */
 256                 if ((Hflg || Lflg || Pflg) && !(Rflg || rflg)) {
 257                         errflg++;
 258                 }
 259 
 260         } else if (mve) {
 261                 while ((c = getopt(argc, argv, "fis")) != EOF)
 262                         switch (c) {
 263                         case 'f':
 264                                 silent++;
 265 #ifdef XPG4
 266                                 iflg = 0;
 267 #endif
 268                                 break;
 269                         case 'i':
 270                                 iflg++;
 271 #ifdef XPG4
 272                                 silent = 0;
 273 #endif
 274                                 break;
 275                         default:
 276                                 errflg++;
 277                         }
 278         } else { /* ln */
 279                 while ((c = getopt(argc, argv, "fns")) != EOF)
 280                         switch (c) {
 281                         case 'f':
 282                                 silent++;
 283                                 break;
 284                         case 'n':
 285                                 /* silently ignored; this is the default */
 286                                 break;
 287                         case 's':
 288                                 sflg++;
 289                                 break;
 290                         default:
 291                                 errflg++;
 292                         }
 293         }
 294 
 295         /*
 296          * For BSD compatibility allow - to delimit the end of
 297          * options for mv.
 298          */
 299         if (mve && optind < argc && (strcmp(argv[optind], "-") == 0))
 300                 optind++;
 301 
 302         /*
 303          * Check for sufficient arguments
 304          * or a usage error.
 305          */
 306 
 307         argc -= optind;
 308         argv  = &argv[optind];
 309 
 310         if ((argc < 2 && lnk != TRUE) || (argc < 1 && lnk == TRUE)) {
 311                 (void) fprintf(stderr,
 312                     gettext("%s: Insufficient arguments (%d)\n"),
 313                     cmd, argc);
 314                 usage();
 315         }
 316 
 317         if (errflg != 0)
 318                 usage();
 319 
 320         /*
 321          * If there is more than a source and target,
 322          * the last argument (the target) must be a directory
 323          * which really exists.
 324          */
 325 
 326         if (argc > 2) {
 327                 if (stat(argv[argc-1], &s2) < 0) {
 328                         (void) fprintf(stderr,
 329                             gettext("%s: %s not found\n"),
 330                             cmd, argv[argc-1]);
 331                         exit(2);
 332                 }
 333 
 334                 if (!ISDIR(s2)) {
 335                         (void) fprintf(stderr,
 336                             gettext("%s: Target %s must be a directory\n"),
 337                             cmd, argv[argc-1]);
 338                         usage();
 339                 }
 340         }
 341 
 342         if (strlen(argv[argc-1]) >= PATH_MAX) {
 343                 (void) fprintf(stderr,
 344                     gettext("%s: Target %s file name length exceeds PATH_MAX"
 345                     " %d\n"), cmd, argv[argc-1], PATH_MAX);
 346                 exit(78);
 347         }
 348 
 349         if (argc == 1) {
 350                 if (!lnk)
 351                         usage();
 352                 (void) strcpy(target, ".");
 353         } else {
 354                 (void) strcpy(target, argv[--argc]);
 355         }
 356 
 357         /*
 358          * Perform a multiple argument mv|cp|ln by
 359          * multiple invocations of cpymve() or lnkfil().
 360          */
 361         if (lnk)
 362                 move = lnkfil;
 363         else
 364                 move = cpymve;
 365 
 366         r = 0;
 367         for (i = 0; i < argc; i++) {
 368                 stree = NULL;
 369                 cmdarg = 1;
 370                 r += move(argv[i], target);
 371         }
 372 
 373         /*
 374          * Show errors by nonzero exit code.
 375          */
 376 
 377         return (r?2:0);
 378 }
 379 
 380 static int
 381 lnkfil(char *source, char *target)
 382 {
 383         char    *buf = NULL;
 384 
 385         if (sflg) {
 386 
 387                 /*
 388                  * If target is a directory make complete
 389                  * name of the new symbolic link within that
 390                  * directory.
 391                  */
 392 
 393                 if ((stat(target, &s2) >= 0) && ISDIR(s2)) {
 394                         size_t len;
 395 
 396                         len = strlen(target) + strlen(dname(source)) + 4;
 397                         if ((buf = (char *)malloc(len)) == NULL) {
 398                                 (void) fprintf(stderr,
 399                                     gettext("%s: Insufficient memory "
 400                                     "to %s %s\n"), cmd, cmd, source);
 401                                 exit(3);
 402                         }
 403                         (void) snprintf(buf, len, "%s/%s",
 404                             target, dname(source));
 405                         target = buf;
 406                 }
 407 
 408                 /*
 409                  * Check to see if the file exists already.
 410                  * In this case we use lstat() instead of stat():
 411                  * unlink(2) and symlink(2) will operate on the file
 412                  * itself, not its reference, if the file is a symlink.
 413                  */
 414 
 415                 if ((lstat(target, &s2) == 0)) {
 416                         /*
 417                          * Check if the silent flag is set ie. the -f option
 418                          * is used.  If so, use unlink to remove the current
 419                          * target to replace with the new target, specified
 420                          * on the command line.  Proceed with symlink.
 421                          */
 422                         if (silent) {
 423                         /*
 424                          * Don't allow silent (-f) removal of an existing
 425                          * directory; could leave unreferenced directory
 426                          * entries.
 427                          */
 428                                 if (ISDIR(s2)) {
 429                                         (void) fprintf(stderr,
 430                                             gettext("%s: cannot create link "
 431                                             "over directory %s\n"), cmd,
 432                                             target);
 433                                         return (1);
 434                                 }
 435                                 if (unlink(target) < 0) {
 436                                         (void) fprintf(stderr,
 437                                             gettext("%s: cannot unlink %s: "),
 438                                             cmd, target);
 439                                         perror("");
 440                                         return (1);
 441                                 }
 442                         }
 443                 }
 444 
 445 
 446                 /*
 447                  * Create a symbolic link to the source.
 448                  */
 449 
 450                 if (symlink(source, target) < 0) {
 451                         (void) fprintf(stderr,
 452                             gettext("%s: cannot create %s: "),
 453                             cmd, target);
 454                         perror("");
 455                         if (buf != NULL)
 456                                 free(buf);
 457                         return (1);
 458                 }
 459                 if (buf != NULL)
 460                         free(buf);
 461                 return (0);
 462         }
 463 
 464         switch (chkfiles(source, &target)) {
 465                 case 1: return (1);
 466                 case 2: return (0);
 467                         /* default - fall through */
 468         }
 469 
 470         /*
 471          * Make sure source file is not a directory,
 472          * we cannot link directories...
 473          */
 474 
 475         if (ISDIR(s1)) {
 476                 (void) fprintf(stderr,
 477                     gettext("%s: %s is a directory\n"), cmd, source);
 478                 return (1);
 479         }
 480 
 481         /*
 482          * hard link, call link() and return.
 483          */
 484 
 485         if (link(source, target) < 0) {
 486                 if (errno == EXDEV)
 487                         (void) fprintf(stderr,
 488                             gettext("%s: %s is on a different file system\n"),
 489                             cmd, target);
 490                 else {
 491                         (void) fprintf(stderr,
 492                             gettext("%s: cannot create link %s: "),
 493                             cmd, target);
 494                         perror("");
 495                 }
 496                 if (buf != NULL)
 497                         free(buf);
 498                 return (1);
 499         } else {
 500                 if (buf != NULL)
 501                         free(buf);
 502                 return (0);
 503         }
 504 }
 505 
 506 static int
 507 cpymve(char *source, char *target)
 508 {
 509         int     n;
 510         int fi, fo;
 511         int ret = 0;
 512         int attret = 0;
 513         int sattret = 0;
 514         int errno_save;
 515         int error = 0;
 516 
 517         switch (chkfiles(source, &target)) {
 518                 case 1: return (1);
 519                 case 2: return (0);
 520                         /* default - fall through */
 521         }
 522 
 523         /*
 524          * If it's a recursive copy and source
 525          * is a directory, then call rcopy (from copydir).
 526          */
 527         if (cpy) {
 528                 if (ISDIR(s1)) {
 529                         int             rc;
 530                         avl_index_t     where = 0;
 531                         tree_node_t     *tnode;
 532                         tree_node_t     *tptr;
 533                         dev_t           save_dev = s1.st_dev;
 534                         ino_t           save_ino = s1.st_ino;
 535 
 536                         /*
 537                          * We will be recursing into the directory so
 538                          * save the inode information to a search tree
 539                          * to avoid getting into an endless loop.
 540                          */
 541                         if ((rc = add_tnode(&stree, save_dev, save_ino)) != 1) {
 542                                 if (rc == 0) {
 543                                         /*
 544                                          * We've already visited this directory.
 545                                          * Don't remove the search tree entry
 546                                          * to make sure we don't get into an
 547                                          * endless loop if revisited from a
 548                                          * different part of the hierarchy.
 549                                          */
 550                                         (void) fprintf(stderr, gettext(
 551                                             "%s: cycle detected: %s\n"),
 552                                             cmd, source);
 553                                 } else {
 554                                         Perror(source);
 555                                 }
 556                                 return (1);
 557                         }
 558 
 559                         cmdarg = 0;
 560                         rc = copydir(source, target);
 561 
 562                         /*
 563                          * Create a tnode to get an index to the matching
 564                          * node (same dev and inode) in the search tree,
 565                          * then use the index to remove the matching node
 566                          * so it we do not wrongly detect a cycle when
 567                          * revisiting this directory from another part of
 568                          * the hierarchy.
 569                          */
 570                         if ((tnode = create_tnode(save_dev,
 571                             save_ino)) == NULL) {
 572                                 Perror(source);
 573                                 return (1);
 574                         }
 575                         if ((tptr = avl_find(stree, tnode, &where)) != NULL) {
 576                                 avl_remove(stree, tptr);
 577                         }
 578                         free(tptr);
 579                         free(tnode);
 580                         return (rc);
 581 
 582                 } else if (ISDEV(s1) && Rflg) {
 583                         return (copyspecial(target));
 584                 } else {
 585                         goto copy;
 586                 }
 587         }
 588 
 589         if (mve) {
 590                 if (rename(source, target) >= 0)
 591                         return (0);
 592                 if (errno != EXDEV) {
 593                         if (errno == ENOTDIR && ISDIR(s1)) {
 594                                 (void) fprintf(stderr,
 595                                     gettext("%s: %s is a directory\n"),
 596                                     cmd, source);
 597                                 return (1);
 598                         }
 599                         (void) fprintf(stderr,
 600                             gettext("%s: cannot rename %s to %s: "),
 601                             cmd, source, target);
 602                         perror("");
 603                         return (1);
 604                 }
 605 
 606                 /*
 607                  * cannot move a non-directory (source) onto an existing
 608                  * directory (target)
 609                  *
 610                  */
 611                 if (targetexists && ISDIR(s2) && (!ISDIR(s1))) {
 612                         (void) fprintf(stderr,
 613                             gettext("%s: cannot mv a non directory %s "
 614                             "over existing directory"
 615                             " %s \n"), cmd, source, target);
 616                         return (1);
 617                 }
 618                 if (ISDIR(s1)) {
 619 #ifdef XPG4
 620                         if (targetexists && ISDIR(s2)) {
 621                                 /* existing target dir must be empty */
 622                                 if (rmdir(target) < 0) {
 623                                         errno_save = errno;
 624                                         (void) fprintf(stderr,
 625                                             gettext("%s: cannot rmdir %s: "),
 626                                             cmd, target);
 627                                         errno = errno_save;
 628                                         perror("");
 629                                         return (1);
 630                                 }
 631                         }
 632 #endif
 633                         if ((n =  copydir(source, target)) == 0)
 634                                 (void) rmdir(source);
 635                         return (n);
 636                 }
 637 
 638                 /* doors cannot be moved across filesystems */
 639                 if (ISDOOR(s1)) {
 640                         (void) fprintf(stderr,
 641                             gettext("%s: %s: cannot move door "
 642                             "across file systems\n"), cmd, source);
 643                         return (1);
 644                 }
 645 
 646                 /* sockets cannot be moved across filesystems */
 647                 if (ISSOCK(s1)) {
 648                         (void) fprintf(stderr,
 649                             gettext("%s: %s: cannot move socket "
 650                             "across file systems\n"), cmd, source);
 651                         return (1);
 652                 }
 653 
 654                 /*
 655                  * File cannot be renamed, try to recreate the symbolic
 656                  * link or special device, or copy the file wholesale
 657                  * between file systems.
 658                  */
 659                 if (ISLNK(s1)) {
 660                         register int    m;
 661                         register mode_t md;
 662                         char symln[PATH_MAX + 1];
 663 
 664                         if (targetexists && unlink(target) < 0) {
 665                                 (void) fprintf(stderr,
 666                                     gettext("%s: cannot unlink %s: "),
 667                                     cmd, target);
 668                                 perror("");
 669                                 return (1);
 670                         }
 671 
 672                         if ((m = readlink(source, symln,
 673                             sizeof (symln) - 1)) < 0) {
 674                                 Perror(source);
 675                                 return (1);
 676                         }
 677                         symln[m] = '\0';
 678 
 679                         md = umask(~(s1.st_mode & MODEBITS));
 680                         if (symlink(symln, target) < 0) {
 681                                 Perror(target);
 682                                 return (1);
 683                         }
 684                         (void) umask(md);
 685                         m = lchown(target, UID(s1), GID(s1));
 686 #ifdef XPG4
 687                         if (m < 0) {
 688                                 (void) fprintf(stderr, gettext("%s: cannot"
 689                                     " change owner and group of"
 690                                     " %s: "), cmd, target);
 691                                 perror("");
 692                         }
 693 #endif
 694                         goto cleanup;
 695                 }
 696                 if (ISDEV(s1)) {
 697 
 698                         if (targetexists && unlink(target) < 0) {
 699                                 (void) fprintf(stderr,
 700                                     gettext("%s: cannot unlink %s: "),
 701                                     cmd, target);
 702                                 perror("");
 703                                 return (1);
 704                         }
 705 
 706                         if (mknod(target, s1.st_mode, s1.st_rdev) < 0) {
 707                                 Perror(target);
 708                                 return (1);
 709                         }
 710 
 711                         (void) chg_mode(target, UID(s1), GID(s1), FMODE(s1));
 712                         (void) chg_time(target, s1);
 713                         goto cleanup;
 714                 }
 715 
 716                 if (ISREG(s1)) {
 717                         if (ISDIR(s2)) {
 718                                 if (targetexists && rmdir(target) < 0) {
 719                                         (void) fprintf(stderr,
 720                                             gettext("%s: cannot rmdir %s: "),
 721                                             cmd, target);
 722                                         perror("");
 723                                         return (1);
 724                                 }
 725                         } else {
 726                                 if (targetexists && unlink(target) < 0) {
 727                                         (void) fprintf(stderr,
 728                                             gettext("%s: cannot unlink %s: "),
 729                                             cmd, target);
 730                                         perror("");
 731                                         return (1);
 732                                 }
 733                         }
 734 
 735 
 736 copy:
 737                         /*
 738                          * If the source file is a symlink, and either
 739                          * -P or -H flag (only if -H is specified and the
 740                          * source file is not a command line argument)
 741                          * were specified, then action is taken on the symlink
 742                          * itself, not the file referenced by the symlink.
 743                          * Note: this is executed for 'cp' only.
 744                          */
 745                         if (cpy && (Pflg || (Hflg && !cmdarg)) && (ISLNK(s1))) {
 746                                 int     m;
 747                                 mode_t  md;
 748                                 char symln[PATH_MAX + 1];
 749 
 750                                 m = readlink(source, symln, sizeof (symln) - 1);
 751 
 752                                 if (m < 0) {
 753                                         Perror(source);
 754                                         return (1);
 755                                 }
 756                                 symln[m] = '\0';
 757 
 758                                 /*
 759                                  * Copy the sym link to the target.
 760                                  * Note: If the target exists, write a
 761                                  * diagnostic message, do nothing more
 762                                  * with the source file, and return to
 763                                  * process any remaining files.
 764                                  */
 765                                 md = umask(~(s1.st_mode & MODEBITS));
 766                                 if (symlink(symln, target) < 0) {
 767                                         Perror(target);
 768                                         return (1);
 769                                 }
 770                                 (void) umask(md);
 771                                 m = lchown(target, UID(s1), GID(s1));
 772 
 773                                 if (m < 0) {
 774                                         (void) fprintf(stderr, gettext(
 775                                             "cp: cannot change owner and "
 776                                             "group of %s:"), target);
 777                                         perror("");
 778                                 }
 779                         } else {
 780                                 /*
 781                                  * Copy the file.  If it happens to be a
 782                                  * symlink, copy the file referenced
 783                                  * by the symlink.
 784                                  */
 785                                 fi = open(source, O_RDONLY);
 786                                 if (fi < 0) {
 787                                         (void) fprintf(stderr,
 788                                             gettext("%s: cannot open %s: "),
 789                                             cmd, source);
 790                                         perror("");
 791                                         return (1);
 792                                 }
 793 
 794                                 fo = creat(target, s1.st_mode & MODEBITS);
 795                                 if (fo < 0) {
 796                                         /*
 797                                          * If -f and creat() failed, unlink
 798                                          * and try again.
 799                                          */
 800                                         if (fflg) {
 801                                                 (void) unlink(target);
 802                                                 fo = creat(target,
 803                                                     s1.st_mode & MODEBITS);
 804                                         }
 805                                 }
 806                                 if (fo < 0) {
 807                                         (void) fprintf(stderr,
 808                                             gettext("%s: cannot create %s: "),
 809                                             cmd, target);
 810                                         perror("");
 811                                         (void) close(fi);
 812                                         return (1);
 813                                 } else {
 814                                         /* stat the new file, its used below */
 815                                         (void) stat(target, &s2);
 816                                 }
 817 
 818                                 /*
 819                                  * Set target's permissions to the source
 820                                  * before any copying so that any partially
 821                                  * copied file will have the source's
 822                                  * permissions (at most) or umask permissions
 823                                  * whichever is the most restrictive.
 824                                  *
 825                                  * ACL for regular files
 826                                  */
 827 
 828                                 if (pflg || mve) {
 829                                         (void) chmod(target, FMODE(s1));
 830                                         if (s1acl != NULL) {
 831                                                 if ((acl_set(target,
 832                                                     s1acl)) < 0) {
 833                                                         error++;
 834                                                         (void) fprintf(stderr,
 835                                                             gettext("%s: "
 836                                                             "Failed to set "
 837                                                             "acl entries "
 838                                                             "on %s\n"), cmd,
 839                                                             target);
 840                                                         acl_free(s1acl);
 841                                                         s1acl = NULL;
 842                                                         /*
 843                                                          * else: silent and
 844                                                          * continue
 845                                                          */
 846                                                 }
 847                                         }
 848                                 }
 849 
 850                                 if (fstat(fi, &s1) < 0) {
 851                                         (void) fprintf(stderr,
 852                                             gettext("%s: cannot access %s\n"),
 853                                             cmd, source);
 854                                         return (1);
 855                                 }
 856                                 if (IDENTICAL(s1, s2)) {
 857                                         (void) fprintf(stderr,
 858                                             gettext(
 859                                             "%s: %s and %s are identical\n"),
 860                                             cmd, source, target);
 861                                         return (1);
 862                                 }
 863 
 864                                 if (writefile(fi, fo, source, target, NULL,
 865                                     NULL, &s1, &s2) != 0) {
 866                                         return (1);
 867                                 }
 868 
 869                                 (void) close(fi);
 870                                 if (close(fo) < 0) {
 871                                         Perror2(target, "write");
 872                                         return (1);
 873                                 }
 874                         }
 875                         /* Copy regular extended attributes */
 876                         if (pflg || atflg || mve || saflg) {
 877                                 attret = copyattributes(source, target);
 878                                 if (attret != 0 && !attrsilent) {
 879                                         (void) fprintf(stderr, gettext(
 880                                             "%s: Failed to preserve"
 881                                             " extended attributes of file"
 882                                             " %s\n"), cmd, source);
 883                                 }
 884                                 /* Copy extended system attributes */
 885                                 if (pflg || mve || saflg)
 886                                         sattret = copy_sysattr(source, target);
 887                                 if (mve && attret != 0) {
 888                                         (void) unlink(target);
 889                                         return (1);
 890                                 }
 891                                 if (attrsilent) {
 892                                         attret = 0;
 893                                 }
 894                         }
 895 
 896                         /*
 897                          * XPG4: the write system call will clear setgid
 898                          * and setuid bits, so set them again.
 899                          */
 900                         if (pflg || mve) {
 901                                 if ((ret = chg_mode(target, UID(s1), GID(s1),
 902                                     FMODE(s1))) > 0)
 903                                         return (1);
 904                                 /*
 905                                  * Reapply ACL, since chmod may have
 906                                  * altered ACL
 907                                  */
 908                                 if (s1acl != NULL) {
 909                                         if ((acl_set(target, s1acl)) < 0) {
 910                                                 error++;
 911                                                 (void) fprintf(stderr,
 912                                                     gettext("%s: Failed to "
 913                                                     "set acl entries "
 914                                                     "on %s\n"), cmd, target);
 915                                                 /*
 916                                                  * else: silent and
 917                                                  * continue
 918                                                  */
 919                                         }
 920                                 }
 921                                 if ((ret = chg_time(target, s1)) > 0)
 922                                         return (1);
 923                         }
 924                         if (cpy) {
 925                                 if (error != 0 || attret != 0 || sattret != 0)
 926                                         return (1);
 927                                 return (0);
 928                         }
 929                         goto cleanup;
 930                 }
 931                 (void) fprintf(stderr,
 932                     gettext("%s: %s: unknown file type 0x%x\n"), cmd,
 933                     source, (s1.st_mode & S_IFMT));
 934                 return (1);
 935 
 936 cleanup:
 937                 if (unlink(source) < 0) {
 938                         (void) unlink(target);
 939                         (void) fprintf(stderr,
 940                             gettext("%s: cannot unlink %s: "),
 941                             cmd, source);
 942                         perror("");
 943                         return (1);
 944                 }
 945                 if (error != 0 || attret != 0 || sattret != 0)
 946                         return (1);
 947                 return (ret);
 948         }
 949         /*NOTREACHED*/
 950         return (ret);
 951 }
 952 
 953 /*
 954  * create_tnode()
 955  *
 956  * Create a node for use with the search tree which contains the
 957  * inode information (device id and inode number).
 958  *
 959  * Input
 960  *      dev     - device id
 961  *      ino     - inode number
 962  *
 963  * Output
 964  *      tnode   - NULL on error, otherwise returns a tnode structure
 965  *                which contains the input device id and inode number.
 966  */
 967 static tree_node_t *
 968 create_tnode(dev_t dev, ino_t ino)
 969 {
 970         tree_node_t     *tnode;
 971 
 972         if ((tnode = (tree_node_t *)malloc(sizeof (tree_node_t))) != NULL) {
 973                 tnode->node_dev = dev;
 974                 tnode->node_ino = ino;
 975         }
 976 
 977         return (tnode);
 978 }
 979 
 980 static int
 981 chkfiles(char *source, char **to)
 982 {
 983         char    *buf = (char *)NULL;
 984         int     (*statf)() = (cpy &&
 985             !(Pflg || (Hflg && !cmdarg))) ? stat : lstat;
 986         char    *target = *to;
 987         int     error;
 988 
 989         /*
 990          * Make sure source file exists.
 991          */
 992         if ((*statf)(source, &s1) < 0) {
 993                 /*
 994                  * Keep the old error message except when someone tries to
 995                  * mv/cp/ln a symbolic link that has a trailing slash and
 996                  * points to a file.
 997                  */
 998                 if (errno == ENOTDIR)
 999                         (void) fprintf(stderr, "%s: %s: %s\n", cmd, source,
1000                             strerror(errno));
1001                 else
1002                         (void) fprintf(stderr,
1003                             gettext("%s: cannot access %s\n"), cmd, source);
1004                 return (1);
1005         }
1006 
1007         /*
1008          * Get ACL info: don't bother with ln or cp/mv'ing symlinks
1009          */
1010         if (!lnk && !ISLNK(s1)) {
1011                 if (s1acl != NULL) {
1012                         acl_free(s1acl);
1013                         s1acl = NULL;
1014                 }
1015                 if ((error = acl_get(source, ACL_NO_TRIVIAL, &s1acl)) != 0) {
1016                         (void) fprintf(stderr,
1017                             "%s: failed to get acl entries: %s\n", source,
1018                             acl_strerror(error));
1019                         return (1);
1020                 }
1021                 /* else: just permission bits */
1022         }
1023 
1024         /*
1025          * If stat fails, then the target doesn't exist,
1026          * we will create a new target with default file type of regular.
1027          */
1028 
1029         FTYPE(s2) = S_IFREG;
1030         targetexists = 0;
1031         if ((*statf)(target, &s2) >= 0) {
1032                 if (ISLNK(s2))
1033                         (void) stat(target, &s2);
1034                 /*
1035                  * If target is a directory,
1036                  * make complete name of new file
1037                  * within that directory.
1038                  */
1039                 if (ISDIR(s2)) {
1040                         size_t len;
1041 
1042                         len = strlen(target) + strlen(dname(source)) + 4;
1043                         if ((buf = (char *)malloc(len)) == NULL) {
1044                                 (void) fprintf(stderr,
1045                                     gettext("%s: Insufficient memory to "
1046                                     "%s %s\n "), cmd, cmd, source);
1047                                 exit(3);
1048                         }
1049                         (void) snprintf(buf, len, "%s/%s",
1050                             target, dname(source));
1051                         *to = target = buf;
1052                 }
1053 
1054                 if ((*statf)(target, &s2) >= 0) {
1055                         int overwrite   = FALSE;
1056                         int override    = FALSE;
1057 
1058                         targetexists++;
1059                         if (cpy || mve) {
1060                                 /*
1061                                  * For cp and mv, it is an error if the
1062                                  * source and target are the same file.
1063                                  * Check for the same inode and file
1064                                  * system, but don't check for the same
1065                                  * absolute pathname because it is an
1066                                  * error when the source and target are
1067                                  * hard links to the same file.
1068                                  */
1069                                 if (IDENTICAL(s1, s2)) {
1070                                         (void) fprintf(stderr,
1071                                             gettext(
1072                                             "%s: %s and %s are identical\n"),
1073                                             cmd, source, target);
1074                                         if (buf != NULL)
1075                                                 free(buf);
1076                                         return (1);
1077                                 }
1078                         }
1079                         if (lnk) {
1080                                 /*
1081                                  * For ln, it is an error if the source and
1082                                  * target are identical files (same inode,
1083                                  * same file system, and filenames resolve
1084                                  * to same absolute pathname).
1085                                  */
1086                                 if (!chk_different(source, target)) {
1087                                         if (buf != NULL)
1088                                                 free(buf);
1089                                         return (1);
1090                                 }
1091                         }
1092                         if (lnk && !silent) {
1093                                 (void) fprintf(stderr,
1094                                     gettext("%s: %s: File exists\n"),
1095                                     cmd, target);
1096                                 if (buf != NULL)
1097                                         free(buf);
1098                                 return (1);
1099                         }
1100 
1101                         /*
1102                          * overwrite:
1103                          * If the user does not have access to
1104                          * the target, ask ----if it is not
1105                          * silent and user invoked command
1106                          * interactively.
1107                          *
1108                          * override:
1109                          * If not silent, and stdin is a terminal, and
1110                          * there's no write access, and the file isn't a
1111                          * symbolic link, ask for permission.
1112                          *
1113                          * XPG4: both overwrite and override:
1114                          * ask only one question.
1115                          *
1116                          * TRANSLATION_NOTE - The following messages will
1117                          * contain the first character of the strings for
1118                          * "yes" and "no" defined in the file
1119                          * "nl_langinfo.po".  After substitution, the
1120                          * message will appear as follows:
1121                          *      <cmd>: overwrite <filename> (y/n)?
1122                          * where <cmd> is the name of the command
1123                          * (cp, mv) and <filename> is the destination file
1124                          */
1125 
1126 
1127                         overwrite = iflg && !silent && use_stdin();
1128                         override = !cpy && (access(target, 2) < 0) &&
1129                             !silent && use_stdin() && !ISLNK(s2);
1130 
1131                         if (overwrite && override) {
1132                                 (void) fprintf(stderr,
1133                                     gettext("%s: overwrite %s and override "
1134                                     "protection %o (%s/%s)? "), cmd, target,
1135                                     FMODE(s2) & MODEBITS, yesstr, nostr);
1136                                 if (yes() == 0) {
1137                                         if (buf != NULL)
1138                                                 free(buf);
1139                                         return (2);
1140                                 }
1141                         } else if (overwrite && ISREG(s2)) {
1142                                 (void) fprintf(stderr,
1143                                     gettext("%s: overwrite %s (%s/%s)? "),
1144                                     cmd, target, yesstr, nostr);
1145                                 if (yes() == 0) {
1146                                         if (buf != NULL)
1147                                                 free(buf);
1148                                         return (2);
1149                                 }
1150                         } else if (override) {
1151                                 (void) fprintf(stderr,
1152                                     gettext("%s: %s: override protection "
1153                                     /*CSTYLED*/
1154                                     "%o (%s/%s)? "),
1155                                     /*CSTYLED*/
1156                                     cmd, target, FMODE(s2) & MODEBITS,
1157                                     yesstr, nostr);
1158                                 if (yes() == 0) {
1159                                         if (buf != NULL)
1160                                                 free(buf);
1161                                         return (2);
1162                                 }
1163                         }
1164 
1165                         if (lnk && unlink(target) < 0) {
1166                                 (void) fprintf(stderr,
1167                                     gettext("%s: cannot unlink %s: "),
1168                                     cmd, target);
1169                                 perror("");
1170                                 return (1);
1171                         }
1172                 }
1173         }
1174         return (0);
1175 }
1176 
1177 /*
1178  * check whether source and target are different
1179  * return 1 when they are different
1180  * return 0 when they are identical, or when unable to resolve a pathname
1181  */
1182 static int
1183 chk_different(char *source, char *target)
1184 {
1185         char    rtarget[PATH_MAX], rsource[PATH_MAX];
1186 
1187         if (IDENTICAL(s1, s2)) {
1188                 /*
1189                  * IDENTICAL will be true for hard links, therefore
1190                  * check whether the filenames are different
1191                  */
1192                 if ((getrealpath(source, rsource) == 0) ||
1193                     (getrealpath(target, rtarget) == 0)) {
1194                         return (0);
1195                 }
1196                 if (strncmp(rsource, rtarget, PATH_MAX) == 0) {
1197                         (void) fprintf(stderr, gettext(
1198                             "%s: %s and %s are identical\n"),
1199                             cmd, source, target);
1200                         return (0);
1201                 }
1202         }
1203         return (1);
1204 }
1205 
1206 /*
1207  * get real path (resolved absolute pathname)
1208  * return 1 on success, 0 on failure
1209  */
1210 static int
1211 getrealpath(char *path, char *rpath)
1212 {
1213         if (realpath(path, rpath) == NULL) {
1214                 int     errno_save = errno;
1215                 (void) fprintf(stderr, gettext(
1216                     "%s: cannot resolve path %s: "), cmd, path);
1217                 errno = errno_save;
1218                 perror("");
1219                 return (0);
1220         }
1221         return (1);
1222 }
1223 
1224 static int
1225 rcopy(char *from, char *to)
1226 {
1227         DIR *fold = opendir(from);
1228         struct dirent *dp;
1229         struct stat statb, s1save;
1230         int errs = 0;
1231         char fromname[PATH_MAX];
1232 
1233         if (fold == 0 || ((pflg || mve) && fstat(fold->dd_fd, &statb) < 0)) {
1234                 Perror(from);
1235                 return (1);
1236         }
1237         if (pflg || mve) {
1238                 /*
1239                  * Save s1 (stat information for source dir) so that
1240                  * mod and access times can be reserved during "cp -p"
1241                  * or mv, since s1 gets overwritten.
1242                  */
1243                 s1save = s1;
1244         }
1245         for (;;) {
1246                 dp = readdir(fold);
1247                 if (dp == 0) {
1248                         (void) closedir(fold);
1249                         if (pflg || mve)
1250                                 return (chg_time(to, s1save) + errs);
1251                         return (errs);
1252                 }
1253                 if (dp->d_ino == 0)
1254                         continue;
1255                 if ((strcmp(dp->d_name, ".") == 0) ||
1256                     (strcmp(dp->d_name, "..") == 0))
1257                         continue;
1258                 if (strlen(from)+1+strlen(dp->d_name) >=
1259                     sizeof (fromname) - 1) {
1260                         (void) fprintf(stderr,
1261                             gettext("%s : %s/%s: Name too long\n"),
1262                             cmd, from, dp->d_name);
1263                         errs++;
1264                         continue;
1265                 }
1266                 (void) snprintf(fromname, sizeof (fromname),
1267                     "%s/%s", from, dp->d_name);
1268                 errs += cpymve(fromname, to);
1269         }
1270 }
1271 
1272 static char *
1273 dname(char *name)
1274 {
1275         register char *p;
1276 
1277         /*
1278          * Return just the file name given the complete path.
1279          * Like basename(1).
1280          */
1281 
1282         p = name;
1283 
1284         /*
1285          * While there are characters left,
1286          * set name to start after last
1287          * delimiter.
1288          */
1289 
1290         while (*p)
1291                 if (*p++ == DELIM && *p)
1292                         name = p;
1293         return (name);
1294 }
1295 
1296 static void
1297 usage(void)
1298 {
1299         /*
1300          * Display usage message.
1301          */
1302 
1303         if (mve) {
1304                 (void) fprintf(stderr, gettext(
1305                     "Usage: mv [-f] [-i] f1 f2\n"
1306                     "       mv [-f] [-i] f1 ... fn d1\n"
1307                     "       mv [-f] [-i] d1 d2\n"));
1308         } else if (lnk) {
1309 #ifdef XPG4
1310                 (void) fprintf(stderr, gettext(
1311                     "Usage: ln [-f] [-s] f1 [f2]\n"
1312                     "       ln [-f] [-s] f1 ... fn d1\n"
1313                     "       ln [-f] -s d1 d2\n"));
1314 #else
1315                 (void) fprintf(stderr, gettext(
1316                     "Usage: ln [-f] [-n] [-s] f1 [f2]\n"
1317                     "       ln [-f] [-n] [-s] f1 ... fn d1\n"
1318                     "       ln [-f] [-n] -s d1 d2\n"));
1319 #endif
1320         } else if (cpy) {
1321                 (void) fprintf(stderr, gettext(
1322                     "Usage: cp [-f] [-i] [-p] [-@] [-/] f1 f2\n"
1323                     "       cp [-f] [-i] [-p] [-@] [-/] f1 ... fn d1\n"
1324                     "       cp -r|-R [-H|-L|-P] [-f] [-i] [-p] [-@] [-/] "
1325                     "d1 ... dn-1 dn\n"));
1326         }
1327         exit(2);
1328 }
1329 
1330 /*
1331  * chg_time()
1332  *
1333  * Try to preserve modification and access time.
1334  * If 1) pflg is not set, or 2) pflg is set and this is the Solaris version,
1335  * don't report a utimensat() failure.
1336  * If this is the XPG4 version and utimensat fails, if 1) pflg is set (cp -p)
1337  * or 2) we are doing a mv, print a diagnostic message; arrange for a non-zero
1338  * exit status only if pflg is set.
1339  * utimensat(2) is being used to achieve granularity in nanoseconds
1340  * (if supported by the underlying file system) while setting file times.
1341  */
1342 static int
1343 chg_time(char *to, struct stat ss)
1344 {
1345         struct timespec times[2];
1346         int rc;
1347 
1348         times[0] = ss.st_atim;
1349         times[1] = ss.st_mtim;
1350 
1351         rc = utimensat(AT_FDCWD, to, times, 0);
1352 #ifdef XPG4
1353         if ((pflg || mve) && rc != 0) {
1354                 (void) fprintf(stderr,
1355                     gettext("%s: cannot set times for %s: "), cmd, to);
1356                 perror("");
1357                 if (pflg)
1358                         return (1);
1359         }
1360 #endif
1361 
1362         return (0);
1363 
1364 }
1365 
1366 /*
1367  * chg_mode()
1368  *
1369  * This function is called upon "cp -p" or mv across filesystems.
1370  *
1371  * Try to preserve the owner and group id.  If chown() fails,
1372  * only print a diagnostic message if doing a mv in the XPG4 version;
1373  * try to clear S_ISUID and S_ISGID bits in the target.  If unable to clear
1374  * S_ISUID and S_ISGID bits, print a diagnostic message and arrange for a
1375  * non-zero exit status because this is a security violation.
1376  * Try to preserve permissions.
1377  * If this is the XPG4 version and chmod() fails, print a diagnostic message
1378  * and arrange for a non-zero exit status.
1379  * If this is the Solaris version and chmod() fails, do not print a
1380  * diagnostic message or exit with a non-zero value.
1381  */
1382 static int
1383 chg_mode(char *target, uid_t uid, gid_t gid, mode_t mode)
1384 {
1385         int clearflg = 0; /* controls message printed upon chown() error */
1386 
1387         if (chown(target, uid, gid) != 0) {
1388 #ifdef XPG4
1389                 if (mve) {
1390                         (void) fprintf(stderr, gettext("%s: cannot change"
1391                             " owner and group of %s: "), cmd, target);
1392                         perror("");
1393                 }
1394 #endif
1395                 if (mode & (S_ISUID | S_ISGID)) {
1396                         /* try to clear S_ISUID and S_ISGID */
1397                         mode &= ~S_ISUID & ~S_ISGID;
1398                         ++clearflg;
1399                 }
1400         }
1401         if (chmod(target, mode) != 0) {
1402                 if (clearflg) {
1403                         (void) fprintf(stderr, gettext(
1404                             "%s: cannot clear S_ISUID and S_ISGID bits in"
1405                             " %s: "), cmd, target);
1406                         perror("");
1407                         /* cp -p should get non-zero exit; mv should not */
1408                         if (pflg)
1409                                 return (1);
1410                 }
1411 #ifdef XPG4
1412                 else {
1413                         (void) fprintf(stderr, gettext(
1414                         "%s: cannot set permissions for %s: "), cmd, target);
1415                         perror("");
1416                         /* cp -p should get non-zero exit; mv should not */
1417                         if (pflg)
1418                                 return (1);
1419                 }
1420 #endif
1421         }
1422         return (0);
1423 
1424 }
1425 
1426 static void
1427 Perror(char *s)
1428 {
1429         char buf[PATH_MAX + 10];
1430 
1431         (void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s);
1432         perror(buf);
1433 }
1434 
1435 static void
1436 Perror2(char *s1, char *s2)
1437 {
1438         char buf[PATH_MAX + 20];
1439 
1440         (void) snprintf(buf, sizeof (buf), "%s: %s: %s",
1441             cmd, gettext(s1), gettext(s2));
1442         perror(buf);
1443 }
1444 
1445 /*
1446  * used for cp -R and for mv across file systems
1447  */
1448 static int
1449 copydir(char *source, char *target)
1450 {
1451         int ret, attret = 0;
1452         int sattret = 0;
1453         int pret = 0;           /* need separate flag if -p is specified */
1454         mode_t  fixmode = (mode_t)0;    /* cleanup mode after copy */
1455         struct stat s1save;
1456         acl_t  *s1acl_save;
1457         int error = 0;
1458 
1459         s1acl_save = NULL;
1460 
1461         if (cpy && !rflg) {
1462                 (void) fprintf(stderr,
1463                     gettext("%s: %s: is a directory\n"), cmd, source);
1464                 return (1);
1465         }
1466 
1467         if (stat(target, &s2) < 0) {
1468                 if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) {
1469                         (void) fprintf(stderr, "%s: ", cmd);
1470                         perror(target);
1471                         return (1);
1472                 }
1473                 if (stat(target, &s2) == 0) {
1474                         fixmode = s2.st_mode;
1475                 } else {
1476                         fixmode = s1.st_mode;
1477                 }
1478                 (void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU));
1479         } else if (!(ISDIR(s2))) {
1480                 (void) fprintf(stderr,
1481                     gettext("%s: %s: not a directory.\n"), cmd, target);
1482                 return (1);
1483         }
1484         if (pflg || mve) {
1485                 /*
1486                  * Save s1 (stat information for source dir) and acl info,
1487                  * if any, so that ownership, modes, times, and acl's can
1488                  * be reserved during "cp -p" or mv.
1489                  * s1 gets overwritten when doing the recursive copy.
1490                  */
1491                 s1save = s1;
1492                 if (s1acl != NULL) {
1493                         s1acl_save = acl_dup(s1acl);
1494                         if (s1acl_save == NULL) {
1495                                 (void) fprintf(stderr, gettext("%s: "
1496                                     "Insufficient memory to save acl"
1497                                     " entry\n"), cmd);
1498                                 if (pflg)
1499                                         return (1);
1500 
1501                         }
1502 #ifdef XPG4
1503                         else {
1504                                 (void) fprintf(stderr, gettext("%s: "
1505                                     "Insufficient memory to save acl"
1506                                     " entry\n"), cmd);
1507                                 if (pflg)
1508                                         return (1);
1509                         }
1510 #endif
1511                 }
1512         }
1513 
1514         ret = rcopy(source, target);
1515 
1516         /*
1517          * Once we created a directory, go ahead and set
1518          * its attributes, e.g. acls and time. The info
1519          * may get overwritten if we continue traversing
1520          * down the tree.
1521          *
1522          * ACL for directory
1523          */
1524         if (pflg || mve) {
1525                 if ((pret = chg_mode(target, UID(s1save), GID(s1save),
1526                     FMODE(s1save))) == 0)
1527                         pret = chg_time(target, s1save);
1528                 ret += pret;
1529                 if (s1acl_save != NULL) {
1530                         if (acl_set(target, s1acl_save) < 0) {
1531                                 error++;
1532 #ifdef XPG4
1533                                 if (pflg || mve) {
1534 #else
1535                                 if (pflg) {
1536 #endif
1537                                         (void) fprintf(stderr, gettext(
1538                                             "%s: failed to set acl entries "
1539                                             "on %s\n"), cmd, target);
1540                                         if (pflg) {
1541                                                 acl_free(s1acl_save);
1542                                                 s1acl_save = NULL;
1543                                                 ret++;
1544                                         }
1545                                 }
1546                                 /* else: silent and continue */
1547                         }
1548                         acl_free(s1acl_save);
1549                         s1acl_save = NULL;
1550                 }
1551         } else if (fixmode != (mode_t)0)
1552                 (void) chmod(target, fixmode & MODEBITS);
1553 
1554         if (pflg || atflg || mve || saflg) {
1555                 attret = copyattributes(source, target);
1556                 if (!attrsilent && attret != 0) {
1557                         (void) fprintf(stderr, gettext("%s: Failed to preserve"
1558                             " extended attributes of directory"
1559                             " %s\n"), cmd, source);
1560                 } else {
1561                         /*
1562                          * Otherwise ignore failure.
1563                          */
1564                         attret = 0;
1565                 }
1566                 /* Copy extended system attributes */
1567                 if (pflg || mve || saflg) {
1568                         sattret = copy_sysattr(source, target);
1569                         if (sattret != 0) {
1570                                 (void) fprintf(stderr, gettext(
1571                                     "%s: Failed to preserve "
1572                                     "extended system attributes "
1573                                     "of directory %s\n"), cmd, source);
1574                         }
1575                 }
1576         }
1577         if (attret != 0 || sattret != 0 || error != 0)
1578                 return (1);
1579         return (ret);
1580 }
1581 
1582 static int
1583 copyspecial(char *target)
1584 {
1585         int ret = 0;
1586 
1587         if (mknod(target, s1.st_mode, s1.st_rdev) != 0) {
1588                 (void) fprintf(stderr, gettext(
1589                     "cp: cannot create special file %s: "), target);
1590                 perror("");
1591                 return (1);
1592         }
1593 
1594         if (pflg) {
1595                 if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0)
1596                         ret = chg_time(target, s1);
1597         }
1598 
1599         return (ret);
1600 }
1601 
1602 static int
1603 use_stdin(void)
1604 {
1605 #ifdef XPG4
1606         return (1);
1607 #else
1608         return (isatty(fileno(stdin)));
1609 #endif
1610 }
1611 
1612 /* Copy non-system extended attributes */
1613 
1614 static int
1615 copyattributes(char *source, char *target)
1616 {
1617         struct dirent *dp;
1618         int error = 0;
1619         int aclerror;
1620         mode_t mode;
1621         int clearflg = 0;
1622         acl_t *xacl = NULL;
1623         acl_t *attrdiracl = NULL;
1624         struct timespec times[2];
1625 
1626 
1627         if (pathconf(source,  _PC_XATTR_EXISTS) != 1)
1628                 return (0);
1629 
1630         if (pathconf(target, _PC_XATTR_ENABLED) != 1) {
1631                 if (!attrsilent) {
1632                         (void) fprintf(stderr,
1633                             gettext(
1634                             "%s: cannot preserve extended attributes, "
1635                             "operation not supported on file"
1636                             " %s\n"), cmd, target);
1637                 }
1638                 return (1);
1639         }
1640         if (open_source(source) != 0)
1641                 return (1);
1642         if (open_target_srctarg_attrdirs(source, target) !=  0)
1643                 return (1);
1644         if (open_attrdirp(source) != 0)
1645                 return (1);
1646 
1647         if (pflg || mve) {
1648                 if (fchmod(targetdirfd, attrdir.st_mode) == -1) {
1649                         if (!attrsilent) {
1650                                 (void) fprintf(stderr,
1651                                     gettext("%s: failed to set file mode"
1652                                     " correctly on attribute directory of"
1653                                     " file %s: "), cmd, target);
1654                                 perror("");
1655                                 ++error;
1656                         }
1657                 }
1658 
1659                 if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) {
1660                         if (!attrsilent) {
1661                                 (void) fprintf(stderr,
1662                                     gettext("%s: failed to set file"
1663                                     " ownership correctly on attribute"
1664                                     " directory of file %s: "), cmd, target);
1665                                 perror("");
1666                                 ++error;
1667                         }
1668                 }
1669                 /*
1670                  * Now that we are the owner we can update st_ctime by calling
1671                  * utimensat.
1672                  */
1673                 times[0] = attrdir.st_atim;
1674                 times[1] = attrdir.st_mtim;
1675                 if (utimensat(targetdirfd, ".", times, 0) < 0) {
1676                         if (!attrsilent) {
1677                                 (void) fprintf(stderr,
1678                                     gettext("%s: cannot set attribute times"
1679                                     " for %s: "), cmd, target);
1680                                 perror("");
1681                                 ++error;
1682                         }
1683                 }
1684 
1685                 /*
1686                  * Now set owner and group of attribute directory, implies
1687                  * changing the ACL of the hidden attribute directory first.
1688                  */
1689                 if ((aclerror = facl_get(sourcedirfd,
1690                     ACL_NO_TRIVIAL, &attrdiracl)) != 0) {
1691                         if (!attrsilent) {
1692                                 (void) fprintf(stderr, gettext(
1693                                     "%s: failed to get acl entries of"
1694                                     " attribute directory for"
1695                                     " %s : %s\n"), cmd,
1696                                     source, acl_strerror(aclerror));
1697                                 ++error;
1698                         }
1699                 }
1700 
1701                 if (attrdiracl) {
1702                         if (facl_set(targetdirfd, attrdiracl) != 0) {
1703                                 if (!attrsilent) {
1704                                         (void) fprintf(stderr, gettext(
1705                                         "%s: failed to set acl entries"
1706                                         " on attribute directory "
1707                                         "for %s\n"), cmd, target);
1708                                         ++error;
1709                                 }
1710                                 acl_free(attrdiracl);
1711                                 attrdiracl = NULL;
1712                         }
1713                 }
1714         }
1715 
1716         while ((dp = readdir(srcdirp)) != NULL) {
1717                 int ret;
1718 
1719                 if ((ret = traverse_attrfile(dp, source, target, 1)) == -1)
1720                         continue;
1721                 else if (ret > 0) {
1722                         ++error;
1723                         goto out;
1724                 }
1725 
1726                 if (pflg || mve) {
1727                         if ((aclerror = facl_get(srcattrfd,
1728                             ACL_NO_TRIVIAL, &xacl)) != 0) {
1729                                 if (!attrsilent) {
1730                                         (void) fprintf(stderr, gettext(
1731                                             "%s: failed to get acl entries of"
1732                                             " attribute %s for"
1733                                             " %s: %s"), cmd, dp->d_name,
1734                                             source, acl_strerror(aclerror));
1735                                         ++error;
1736                                 }
1737                         }
1738                 }
1739 
1740                 /*
1741                  * preserve ACL
1742                  */
1743                 if ((pflg || mve) && xacl != NULL) {
1744                         if ((facl_set(targattrfd, xacl)) < 0) {
1745                                 if (!attrsilent) {
1746                                         (void) fprintf(stderr, gettext(
1747                                             "%s: failed to set acl entries on"
1748                                             " attribute %s for"
1749                                             "%s\n"), cmd, dp->d_name, target);
1750                                         ++error;
1751                                 }
1752                                 acl_free(xacl);
1753                                 xacl = NULL;
1754                         }
1755                 }
1756 
1757                 if (writefile(srcattrfd, targattrfd, source, target,
1758                     dp->d_name, dp->d_name, &s3, &s4) != 0) {
1759                         if (!attrsilent) {
1760                                 ++error;
1761                         }
1762                         goto next;
1763                 }
1764 
1765                 if (pflg || mve) {
1766                         mode = FMODE(s3);
1767 
1768                         if (fchown(targattrfd, UID(s3), GID(s3)) != 0) {
1769                                 if (!attrsilent) {
1770                                         (void) fprintf(stderr,
1771                                             gettext("%s: cannot change"
1772                                             " owner and group of"
1773                                             " attribute %s for" " file"
1774                                             " %s: "), cmd, dp->d_name, target);
1775                                         perror("");
1776                                         ++error;
1777                                 }
1778                                 if (mode & (S_ISUID | S_ISGID)) {
1779                                         /* try to clear S_ISUID and S_ISGID */
1780                                         mode &= ~S_ISUID & ~S_ISGID;
1781                                         ++clearflg;
1782                                 }
1783                         }
1784                         times[0] = s3.st_atim;
1785                         times[1] = s3.st_mtim;
1786                         if (utimensat(targetdirfd, dp->d_name, times, 0) < 0) {
1787                                 if (!attrsilent) {
1788                                         (void) fprintf(stderr,
1789                                             gettext("%s: cannot set attribute"
1790                                             " times for %s: "), cmd, target);
1791                                         perror("");
1792                                         ++error;
1793                                 }
1794                         }
1795                         if (fchmod(targattrfd, mode) != 0) {
1796                                 if (clearflg) {
1797                                         (void) fprintf(stderr, gettext(
1798                                             "%s: cannot clear S_ISUID and "
1799                                             "S_ISGID bits in attribute %s"
1800                                             " for file"
1801                                             " %s: "), cmd, dp->d_name, target);
1802                                 } else {
1803                                         if (!attrsilent) {
1804                                                 (void) fprintf(stderr,
1805                                                     gettext(
1806                                 "%s: cannot set permissions of attribute"
1807                                 " %s for %s: "), cmd, dp->d_name, target);
1808                                                 perror("");
1809                                                 ++error;
1810                                         }
1811                                 }
1812                         }
1813                         if (xacl && ((facl_set(targattrfd, xacl)) < 0)) {
1814                                 if (!attrsilent) {
1815                                         (void) fprintf(stderr, gettext(
1816                                             "%s: failed to set acl entries on"
1817                                             " attribute %s for"
1818                                             "%s\n"), cmd, dp->d_name, target);
1819                                         ++error;
1820                                 }
1821                                 acl_free(xacl);
1822                                 xacl = NULL;
1823                         }
1824                 }
1825 next:
1826                 if (xacl != NULL) {
1827                         acl_free(xacl);
1828                         xacl = NULL;
1829                 }
1830                 if (srcattrfd != -1)
1831                         (void) close(srcattrfd);
1832                 if (targattrfd != -1)
1833                         (void) close(targattrfd);
1834                 srcattrfd = targattrfd = -1;
1835         }
1836 out:
1837         if (xacl != NULL) {
1838                 acl_free(xacl);
1839                 xacl = NULL;
1840         }
1841         if (attrdiracl != NULL) {
1842                 acl_free(attrdiracl);
1843                 attrdiracl = NULL;
1844         }
1845 
1846         if (!saflg && !pflg && !mve)
1847                 close_all();
1848         return (error == 0 ? 0 : 1);
1849 }
1850 
1851 /* Copy extended system attributes from source to target */
1852 
1853 static int
1854 copy_sysattr(char *source, char *target)
1855 {
1856         struct dirent   *dp;
1857         nvlist_t        *response;
1858         int             error = 0;
1859         int             target_sa_support = 0;
1860 
1861         if (sysattr_support(source, _PC_SATTR_EXISTS) != 1)
1862                 return (0);
1863 
1864         if (open_source(source) != 0)
1865                 return (1);
1866 
1867         /*
1868          * Gets non default extended system attributes from the
1869          * source file to copy to the target. The target has
1870          * the defaults set when its created and thus  no need
1871          * to copy the defaults.
1872          */
1873         response = sysattr_list(cmd, srcfd, source);
1874 
1875         if (sysattr_support(target, _PC_SATTR_ENABLED) != 1) {
1876                 if (response != NULL) {
1877                         (void) fprintf(stderr,
1878                             gettext(
1879                             "%s: cannot preserve extended system "
1880                             "attribute, operation not supported on file"
1881                             " %s\n"), cmd, target);
1882                         error++;
1883                         goto out;
1884                 }
1885         } else {
1886                 target_sa_support = 1;
1887         }
1888 
1889         if (target_sa_support) {
1890                 if (srcdirp == NULL) {
1891                         if (open_target_srctarg_attrdirs(source,
1892                             target) !=  0) {
1893                                 error++;
1894                                 goto out;
1895                         }
1896                         if (open_attrdirp(source) != 0) {
1897                                 error++;
1898                                 goto out;
1899                         }
1900                 } else {
1901                         rewind_attrdir(srcdirp);
1902                 }
1903                 while ((dp = readdir(srcdirp)) != NULL) {
1904                         nvlist_t        *res;
1905                         int             ret;
1906 
1907                         if ((ret = traverse_attrfile(dp, source, target,
1908                             0)) == -1)
1909                                 continue;
1910                         else if (ret > 0) {
1911                                 ++error;
1912                                 goto out;
1913                         }
1914                         /*
1915                          * Gets non default extended system attributes from the
1916                          * attribute file to copy to the target. The target has
1917                          * the defaults set when its created and thus  no need
1918                          * to copy the defaults.
1919                          */
1920                         if (dp->d_name != NULL) {
1921                                 res = sysattr_list(cmd, srcattrfd, dp->d_name);
1922                                 if (res == NULL)
1923                                         goto next;
1924 
1925                         /*
1926                          * Copy non default extended system attributes of named
1927                          * attribute file.
1928                          */
1929                                 if (fsetattr(targattrfd,
1930                                     XATTR_VIEW_READWRITE, res) != 0) {
1931                                         ++error;
1932                                         (void) fprintf(stderr, gettext("%s: "
1933                                             "Failed to copy extended system "
1934                                             "attributes from attribute file "
1935                                             "%s of %s to %s\n"), cmd,
1936                                             dp->d_name, source, target);
1937                                 }
1938                         }
1939 next:
1940                         if (srcattrfd != -1)
1941                                 (void) close(srcattrfd);
1942                         if (targattrfd != -1)
1943                                 (void) close(targattrfd);
1944                         srcattrfd = targattrfd = -1;
1945                         if (res != NULL)
1946                                 nvlist_free(res);
1947                 }
1948         }
1949         /* Copy source file non default extended system attributes to target */
1950         if (target_sa_support && (response != NULL) &&
1951             (fsetattr(targfd, XATTR_VIEW_READWRITE, response)) != 0) {
1952                 ++error;
1953                 (void) fprintf(stderr, gettext("%s: Failed to "
1954                     "copy extended system attributes from "
1955                     "%s to %s\n"), cmd, source, target);
1956         }
1957 out:
1958         if (response != NULL)
1959                 nvlist_free(response);
1960         close_all();
1961         return (error == 0 ? 0 : 1);
1962 }
1963 
1964 /* Open the source file */
1965 
1966 int
1967 open_source(char  *src)
1968 {
1969         int     error = 0;
1970 
1971         srcfd = -1;
1972         if ((srcfd = open(src, O_RDONLY)) == -1) {
1973                 if (pflg && attrsilent) {
1974                         error++;
1975                         goto out;
1976                 }
1977                 if (!attrsilent) {
1978                         (void) fprintf(stderr,
1979                             gettext("%s: cannot open file"
1980                             " %s: "), cmd, src);
1981                         perror("");
1982                 }
1983                 ++error;
1984         }
1985 out:
1986         if (error)
1987                 close_all();
1988         return (error == 0 ? 0 : 1);
1989 }
1990 
1991 /* Open source attribute dir, target and target attribute dir. */
1992 
1993 int
1994 open_target_srctarg_attrdirs(char  *src, char *targ)
1995 {
1996         int             error = 0;
1997 
1998         targfd = sourcedirfd = targetdirfd = -1;
1999 
2000         if ((targfd = open(targ, O_RDONLY)) == -1) {
2001                 if (pflg && attrsilent) {
2002                         error++;
2003                         goto out;
2004                 }
2005                 if (!attrsilent) {
2006                         (void) fprintf(stderr,
2007                             gettext("%s: cannot open file"
2008                             " %s: "), cmd, targ);
2009                         perror("");
2010                 }
2011                 ++error;
2012                 goto out;
2013         }
2014 
2015         if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
2016                 if (pflg && attrsilent) {
2017                         error++;
2018                         goto out;
2019                 }
2020                 if (!attrsilent) {
2021                         (void) fprintf(stderr,
2022                             gettext("%s: cannot open attribute"
2023                             " directory for %s: "), cmd, src);
2024                         perror("");
2025                 }
2026                 ++error;
2027                 goto out;
2028         }
2029 
2030         if (fstat(sourcedirfd, &attrdir) == -1) {
2031                 if (pflg && attrsilent) {
2032                         error++;
2033                         goto out;
2034                 }
2035 
2036                 if (!attrsilent) {
2037                         (void) fprintf(stderr,
2038                             gettext("%s: could not retrieve stat"
2039                             " information for attribute directory"
2040                             "of file %s: "), cmd, src);
2041                         perror("");
2042                 }
2043                 ++error;
2044                 goto out;
2045         }
2046         if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) {
2047                 if (pflg && attrsilent) {
2048                         error++;
2049                         goto out;
2050                 }
2051                 if (!attrsilent) {
2052                         (void) fprintf(stderr,
2053                             gettext("%s: cannot open attribute"
2054                             " directory for %s: "), cmd, targ);
2055                         perror("");
2056                 }
2057                 ++error;
2058         }
2059 out:
2060         if (error)
2061                 close_all();
2062         return (error == 0 ? 0 : 1);
2063 }
2064 
2065 int
2066 open_attrdirp(char *source)
2067 {
2068         int tmpfd = -1;
2069         int error = 0;
2070 
2071         /*
2072          * dup sourcedirfd for use by fdopendir().
2073          * fdopendir will take ownership of given fd and will close
2074          * it when closedir() is called.
2075          */
2076 
2077         if ((tmpfd = dup(sourcedirfd)) == -1) {
2078                 if (pflg && attrsilent) {
2079                         error++;
2080                         goto out;
2081                 }
2082                 if (!attrsilent) {
2083                         (void) fprintf(stderr,
2084                             gettext(
2085                             "%s: unable to dup attribute directory"
2086                             " file descriptor for %s: "), cmd, source);
2087                         perror("");
2088                         ++error;
2089                 }
2090                 goto out;
2091         }
2092         if ((srcdirp = fdopendir(tmpfd)) == NULL) {
2093                 if (pflg && attrsilent) {
2094                         error++;
2095                         goto out;
2096                 }
2097                 if (!attrsilent) {
2098                         (void) fprintf(stderr,
2099                             gettext("%s: failed to open attribute"
2100                             " directory for %s: "), cmd, source);
2101                         perror("");
2102                         ++error;
2103                 }
2104         }
2105 out:
2106         if (error)
2107                 close_all();
2108         return (error == 0 ? 0 : 1);
2109 }
2110 
2111 /* Skips through ., .., and system attribute 'view' files */
2112 int
2113 traverse_attrfile(struct dirent *dp, char *source, char *target, int  first)
2114 {
2115         int             error = 0;
2116 
2117         srcattrfd = targattrfd = -1;
2118 
2119         if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
2120             (dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
2121             dp->d_name[2] == '\0') ||
2122             (sysattr_type(dp->d_name) == _RO_SATTR) ||
2123             (sysattr_type(dp->d_name) == _RW_SATTR))
2124                 return (-1);
2125 
2126         if ((srcattrfd = openat(sourcedirfd, dp->d_name,
2127             O_RDONLY)) == -1) {
2128                 if (!attrsilent) {
2129                         (void) fprintf(stderr,
2130                             gettext("%s: cannot open attribute %s on"
2131                             " file %s: "), cmd, dp->d_name, source);
2132                         perror("");
2133                         ++error;
2134                         goto out;
2135                 }
2136         }
2137 
2138         if (fstat(srcattrfd, &s3) < 0) {
2139                 if (!attrsilent) {
2140                         (void) fprintf(stderr,
2141                             gettext("%s: could not stat attribute"
2142                             " %s on file"
2143                             " %s: "), cmd, dp->d_name, source);
2144                         perror("");
2145                         ++error;
2146                 }
2147                 goto out;
2148         }
2149 
2150         if (first) {
2151                 (void) unlinkat(targetdirfd, dp->d_name, 0);
2152                 if ((targattrfd = openat(targetdirfd, dp->d_name,
2153                     O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) {
2154                         if (!attrsilent) {
2155                                 (void) fprintf(stderr,
2156                                     gettext("%s: could not create attribute"
2157                                     " %s on file %s: "), cmd, dp->d_name,
2158                                     target);
2159                                 perror("");
2160                                 ++error;
2161                         }
2162                         goto out;
2163                 }
2164         } else {
2165                 if ((targattrfd = openat(targetdirfd, dp->d_name,
2166                     O_RDONLY)) == -1) {
2167                         if (!attrsilent) {
2168                                 (void) fprintf(stderr,
2169                                     gettext("%s: could not open attribute"
2170                                     " %s on file %s: "), cmd, dp->d_name,
2171                                     target);
2172                                 perror("");
2173                                 ++error;
2174                         }
2175                         goto out;
2176                 }
2177         }
2178 
2179 
2180         if (fstat(targattrfd, &s4) < 0) {
2181                 if (!attrsilent) {
2182                         (void) fprintf(stderr,
2183                             gettext("%s: could not stat attribute"
2184                             " %s on file"
2185                             " %s: "), cmd, dp->d_name, target);
2186                         perror("");
2187                         ++error;
2188                 }
2189         }
2190 
2191 out:
2192         if (error) {
2193                 if (srcattrfd != -1)
2194                         (void) close(srcattrfd);
2195                 if (targattrfd != -1)
2196                         (void) close(targattrfd);
2197                 srcattrfd = targattrfd = -1;
2198         }
2199         return (error == 0 ? 0 :1);
2200 }
2201 
2202 void
2203 rewind_attrdir(DIR * sdp)
2204 {
2205         int pwdfd;
2206 
2207         pwdfd = open(".", O_RDONLY);
2208         if ((pwdfd != -1) && (fchdir(sourcedirfd) == 0)) {
2209                 rewinddir(sdp);
2210                 (void) fchdir(pwdfd);
2211                 (void) close(pwdfd);
2212         } else {
2213                 if (!attrsilent) {
2214                         (void) fprintf(stderr, gettext("%s: "
2215                             "failed to rewind attribute dir\n"),
2216                             cmd);
2217                 }
2218         }
2219 }
2220 
2221 void
2222 close_all()
2223 {
2224         if (srcattrfd != -1)
2225                 (void) close(srcattrfd);
2226         if (targattrfd != -1)
2227                 (void) close(targattrfd);
2228         if (sourcedirfd != -1)
2229                 (void) close(sourcedirfd);
2230         if (targetdirfd != -1)
2231                 (void) close(targetdirfd);
2232         if (srcdirp != NULL) {
2233                 (void) closedir(srcdirp);
2234                 srcdirp = NULL;
2235         }
2236         if (srcfd != -1)
2237                 (void) close(srcfd);
2238         if (targfd != -1)
2239                 (void) close(targfd);
2240 }