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