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         int rc;
1358 
1359         times[0] = ss.st_atim;
1360         times[1] = ss.st_mtim;
1361 
1362         rc = utimensat(AT_FDCWD, to, times,
1363             ISLNK(s1) ? AT_SYMLINK_NOFOLLOW : 0);
1364 #ifdef XPG4
1365         if ((pflg || mve) && rc != 0) {
1366                 (void) fprintf(stderr,
1367                     gettext("%s: cannot set times for %s: "), cmd, to);
1368                 perror("");
1369                 if (pflg)
1370                         return (1);
1371         }
1372 #endif
1373 
1374         return (0);
1375 
1376 }
1377 
1378 /*
1379  * chg_mode()
1380  *
1381  * This function is called upon "cp -p" or mv across filesystems.
1382  *
1383  * Try to preserve the owner and group id.  If chown() fails,
1384  * only print a diagnostic message if doing a mv in the XPG4 version;
1385  * try to clear S_ISUID and S_ISGID bits in the target.  If unable to clear
1386  * S_ISUID and S_ISGID bits, print a diagnostic message and arrange for a
1387  * non-zero exit status because this is a security violation.
1388  * Try to preserve permissions.
1389  * If this is the XPG4 version and chmod() fails, print a diagnostic message
1390  * and arrange for a non-zero exit status.
1391  * If this is the Solaris version and chmod() fails, do not print a
1392  * diagnostic message or exit with a non-zero value.
1393  */
1394 static int
1395 chg_mode(char *target, uid_t uid, gid_t gid, mode_t mode)
1396 {
1397         int clearflg = 0; /* controls message printed upon chown() error */
1398         struct stat st;
1399 
1400         /* Don't change mode if target is symlink */
1401         if (lstat(target, &st) == 0 && ISLNK(st))
1402                 return (0);
1403 
1404         if (chown(target, uid, gid) != 0) {
1405 #ifdef XPG4
1406                 if (mve) {
1407                         (void) fprintf(stderr, gettext("%s: cannot change"
1408                             " owner and group of %s: "), cmd, target);
1409                         perror("");
1410                 }
1411 #endif
1412                 if (mode & (S_ISUID | S_ISGID)) {
1413                         /* try to clear S_ISUID and S_ISGID */
1414                         mode &= ~S_ISUID & ~S_ISGID;
1415                         ++clearflg;
1416                 }
1417         }
1418         if (chmod(target, mode) != 0) {
1419                 if (clearflg) {
1420                         (void) fprintf(stderr, gettext(
1421                             "%s: cannot clear S_ISUID and S_ISGID bits in"
1422                             " %s: "), cmd, target);
1423                         perror("");
1424                         /* cp -p should get non-zero exit; mv should not */
1425                         if (pflg)
1426                                 return (1);
1427                 }
1428 #ifdef XPG4
1429                 else {
1430                         (void) fprintf(stderr, gettext(
1431                         "%s: cannot set permissions for %s: "), cmd, target);
1432                         perror("");
1433                         /* cp -p should get non-zero exit; mv should not */
1434                         if (pflg)
1435                                 return (1);
1436                 }
1437 #endif
1438         }
1439         return (0);
1440 
1441 }
1442 
1443 static void
1444 Perror(char *s)
1445 {
1446         char buf[PATH_MAX + 10];
1447 
1448         (void) snprintf(buf, sizeof (buf), "%s: %s", cmd, s);
1449         perror(buf);
1450 }
1451 
1452 static void
1453 Perror2(char *s1, char *s2)
1454 {
1455         char buf[PATH_MAX + 20];
1456 
1457         (void) snprintf(buf, sizeof (buf), "%s: %s: %s",
1458             cmd, gettext(s1), gettext(s2));
1459         perror(buf);
1460 }
1461 
1462 /*
1463  * used for cp -R and for mv across file systems
1464  */
1465 static int
1466 copydir(char *source, char *target)
1467 {
1468         int ret, attret = 0;
1469         int sattret = 0;
1470         int pret = 0;           /* need separate flag if -p is specified */
1471         mode_t  fixmode = (mode_t)0;    /* cleanup mode after copy */
1472         struct stat s1save;
1473         acl_t  *s1acl_save;
1474         int error = 0;
1475 
1476         s1acl_save = NULL;
1477 
1478         if (cpy && !rflg) {
1479                 (void) fprintf(stderr,
1480                     gettext("%s: %s: is a directory\n"), cmd, source);
1481                 return (1);
1482         }
1483 
1484         if (stat(target, &s2) < 0) {
1485                 if (mkdir(target, (s1.st_mode & MODEBITS)) < 0) {
1486                         (void) fprintf(stderr, "%s: ", cmd);
1487                         perror(target);
1488                         return (1);
1489                 }
1490                 if (stat(target, &s2) == 0) {
1491                         fixmode = s2.st_mode;
1492                 } else {
1493                         fixmode = s1.st_mode;
1494                 }
1495                 (void) chmod(target, ((fixmode & MODEBITS) | S_IRWXU));
1496         } else if (!(ISDIR(s2))) {
1497                 (void) fprintf(stderr,
1498                     gettext("%s: %s: not a directory.\n"), cmd, target);
1499                 return (1);
1500         }
1501         if (pflg || mve) {
1502                 /*
1503                  * Save s1 (stat information for source dir) and acl info,
1504                  * if any, so that ownership, modes, times, and acl's can
1505                  * be reserved during "cp -p" or mv.
1506                  * s1 gets overwritten when doing the recursive copy.
1507                  */
1508                 s1save = s1;
1509                 if (s1acl != NULL) {
1510                         s1acl_save = acl_dup(s1acl);
1511                         if (s1acl_save == NULL) {
1512                                 (void) fprintf(stderr, gettext("%s: "
1513                                     "Insufficient memory to save acl"
1514                                     " entry\n"), cmd);
1515                                 if (pflg)
1516                                         return (1);
1517 
1518                         }
1519 #ifdef XPG4
1520                         else {
1521                                 (void) fprintf(stderr, gettext("%s: "
1522                                     "Insufficient memory to save acl"
1523                                     " entry\n"), cmd);
1524                                 if (pflg)
1525                                         return (1);
1526                         }
1527 #endif
1528                 }
1529         }
1530 
1531         ret = rcopy(source, target);
1532 
1533         /*
1534          * Once we created a directory, go ahead and set
1535          * its attributes, e.g. acls and time. The info
1536          * may get overwritten if we continue traversing
1537          * down the tree.
1538          *
1539          * ACL for directory
1540          */
1541         if (pflg || mve) {
1542                 if ((pret = chg_mode(target, UID(s1save), GID(s1save),
1543                     FMODE(s1save))) == 0)
1544                         pret = chg_time(target, s1save);
1545                 ret += pret;
1546                 if (s1acl_save != NULL) {
1547                         if (acl_set(target, s1acl_save) < 0) {
1548                                 error++;
1549 #ifdef XPG4
1550                                 if (pflg || mve) {
1551 #else
1552                                 if (pflg) {
1553 #endif
1554                                         (void) fprintf(stderr, gettext(
1555                                             "%s: failed to set acl entries "
1556                                             "on %s\n"), cmd, target);
1557                                         if (pflg) {
1558                                                 acl_free(s1acl_save);
1559                                                 s1acl_save = NULL;
1560                                                 ret++;
1561                                         }
1562                                 }
1563                                 /* else: silent and continue */
1564                         }
1565                         acl_free(s1acl_save);
1566                         s1acl_save = NULL;
1567                 }
1568         } else if (fixmode != (mode_t)0)
1569                 (void) chmod(target, fixmode & MODEBITS);
1570 
1571         if (pflg || atflg || mve || saflg) {
1572                 attret = copyattributes(source, target);
1573                 if (!attrsilent && attret != 0) {
1574                         (void) fprintf(stderr, gettext("%s: Failed to preserve"
1575                             " extended attributes of directory"
1576                             " %s\n"), cmd, source);
1577                 } else {
1578                         /*
1579                          * Otherwise ignore failure.
1580                          */
1581                         attret = 0;
1582                 }
1583                 /* Copy extended system attributes */
1584                 if (pflg || mve || saflg) {
1585                         sattret = copy_sysattr(source, target);
1586                         if (sattret != 0) {
1587                                 (void) fprintf(stderr, gettext(
1588                                     "%s: Failed to preserve "
1589                                     "extended system attributes "
1590                                     "of directory %s\n"), cmd, source);
1591                         }
1592                 }
1593         }
1594         if (attret != 0 || sattret != 0 || error != 0)
1595                 return (1);
1596         return (ret);
1597 }
1598 
1599 static int
1600 copyspecial(char *target)
1601 {
1602         int ret = 0;
1603 
1604         if (mknod(target, s1.st_mode, s1.st_rdev) != 0) {
1605                 (void) fprintf(stderr, gettext(
1606                     "cp: cannot create special file %s: "), target);
1607                 perror("");
1608                 return (1);
1609         }
1610 
1611         if (pflg) {
1612                 if ((ret = chg_mode(target, UID(s1), GID(s1), FMODE(s1))) == 0)
1613                         ret = chg_time(target, s1);
1614         }
1615 
1616         return (ret);
1617 }
1618 
1619 static int
1620 use_stdin(void)
1621 {
1622 #ifdef XPG4
1623         return (1);
1624 #else
1625         return (isatty(fileno(stdin)));
1626 #endif
1627 }
1628 
1629 /* Copy non-system extended attributes */
1630 
1631 static int
1632 copyattributes(char *source, char *target)
1633 {
1634         struct dirent *dp;
1635         int error = 0;
1636         int aclerror;
1637         mode_t mode;
1638         int clearflg = 0;
1639         acl_t *xacl = NULL;
1640         acl_t *attrdiracl = NULL;
1641         struct timespec times[2];
1642 
1643 
1644         if (pathconf(source,  _PC_XATTR_EXISTS) != 1)
1645                 return (0);
1646 
1647         if (pathconf(target, _PC_XATTR_ENABLED) != 1) {
1648                 if (!attrsilent) {
1649                         (void) fprintf(stderr,
1650                             gettext(
1651                             "%s: cannot preserve extended attributes, "
1652                             "operation not supported on file"
1653                             " %s\n"), cmd, target);
1654                 }
1655                 return (1);
1656         }
1657         if (open_source(source) != 0)
1658                 return (1);
1659         if (open_target_srctarg_attrdirs(source, target) !=  0)
1660                 return (1);
1661         if (open_attrdirp(source) != 0)
1662                 return (1);
1663 
1664         if (pflg || mve) {
1665                 if (fchmod(targetdirfd, attrdir.st_mode) == -1) {
1666                         if (!attrsilent) {
1667                                 (void) fprintf(stderr,
1668                                     gettext("%s: failed to set file mode"
1669                                     " correctly on attribute directory of"
1670                                     " file %s: "), cmd, target);
1671                                 perror("");
1672                                 ++error;
1673                         }
1674                 }
1675 
1676                 if (fchown(targetdirfd, attrdir.st_uid, attrdir.st_gid) == -1) {
1677                         if (!attrsilent) {
1678                                 (void) fprintf(stderr,
1679                                     gettext("%s: failed to set file"
1680                                     " ownership correctly on attribute"
1681                                     " directory of file %s: "), cmd, target);
1682                                 perror("");
1683                                 ++error;
1684                         }
1685                 }
1686                 /*
1687                  * Now that we are the owner we can update st_ctime by calling
1688                  * utimensat.
1689                  */
1690                 times[0] = attrdir.st_atim;
1691                 times[1] = attrdir.st_mtim;
1692                 if (utimensat(targetdirfd, ".", times, 0) < 0) {
1693                         if (!attrsilent) {
1694                                 (void) fprintf(stderr,
1695                                     gettext("%s: cannot set attribute times"
1696                                     " for %s: "), cmd, target);
1697                                 perror("");
1698                                 ++error;
1699                         }
1700                 }
1701 
1702                 /*
1703                  * Now set owner and group of attribute directory, implies
1704                  * changing the ACL of the hidden attribute directory first.
1705                  */
1706                 if ((aclerror = facl_get(sourcedirfd,
1707                     ACL_NO_TRIVIAL, &attrdiracl)) != 0) {
1708                         if (!attrsilent) {
1709                                 (void) fprintf(stderr, gettext(
1710                                     "%s: failed to get acl entries of"
1711                                     " attribute directory for"
1712                                     " %s : %s\n"), cmd,
1713                                     source, acl_strerror(aclerror));
1714                                 ++error;
1715                         }
1716                 }
1717 
1718                 if (attrdiracl) {
1719                         if (facl_set(targetdirfd, attrdiracl) != 0) {
1720                                 if (!attrsilent) {
1721                                         (void) fprintf(stderr, gettext(
1722                                         "%s: failed to set acl entries"
1723                                         " on attribute directory "
1724                                         "for %s\n"), cmd, target);
1725                                         ++error;
1726                                 }
1727                                 acl_free(attrdiracl);
1728                                 attrdiracl = NULL;
1729                         }
1730                 }
1731         }
1732 
1733         while ((dp = readdir(srcdirp)) != NULL) {
1734                 int ret;
1735 
1736                 if ((ret = traverse_attrfile(dp, source, target, 1)) == -1)
1737                         continue;
1738                 else if (ret > 0) {
1739                         ++error;
1740                         goto out;
1741                 }
1742 
1743                 if (pflg || mve) {
1744                         if ((aclerror = facl_get(srcattrfd,
1745                             ACL_NO_TRIVIAL, &xacl)) != 0) {
1746                                 if (!attrsilent) {
1747                                         (void) fprintf(stderr, gettext(
1748                                             "%s: failed to get acl entries of"
1749                                             " attribute %s for"
1750                                             " %s: %s"), cmd, dp->d_name,
1751                                             source, acl_strerror(aclerror));
1752                                         ++error;
1753                                 }
1754                         }
1755                 }
1756 
1757                 /*
1758                  * preserve ACL
1759                  */
1760                 if ((pflg || mve) && xacl != NULL) {
1761                         if ((facl_set(targattrfd, xacl)) < 0) {
1762                                 if (!attrsilent) {
1763                                         (void) fprintf(stderr, gettext(
1764                                             "%s: failed to set acl entries on"
1765                                             " attribute %s for"
1766                                             "%s\n"), cmd, dp->d_name, target);
1767                                         ++error;
1768                                 }
1769                                 acl_free(xacl);
1770                                 xacl = NULL;
1771                         }
1772                 }
1773 
1774                 if (writefile(srcattrfd, targattrfd, source, target,
1775                     dp->d_name, dp->d_name, &s3, &s4) != 0) {
1776                         if (!attrsilent) {
1777                                 ++error;
1778                         }
1779                         goto next;
1780                 }
1781 
1782                 if (pflg || mve) {
1783                         mode = FMODE(s3);
1784 
1785                         if (fchown(targattrfd, UID(s3), GID(s3)) != 0) {
1786                                 if (!attrsilent) {
1787                                         (void) fprintf(stderr,
1788                                             gettext("%s: cannot change"
1789                                             " owner and group of"
1790                                             " attribute %s for" " file"
1791                                             " %s: "), cmd, dp->d_name, target);
1792                                         perror("");
1793                                         ++error;
1794                                 }
1795                                 if (mode & (S_ISUID | S_ISGID)) {
1796                                         /* try to clear S_ISUID and S_ISGID */
1797                                         mode &= ~S_ISUID & ~S_ISGID;
1798                                         ++clearflg;
1799                                 }
1800                         }
1801                         times[0] = s3.st_atim;
1802                         times[1] = s3.st_mtim;
1803                         if (utimensat(targetdirfd, dp->d_name, times, 0) < 0) {
1804                                 if (!attrsilent) {
1805                                         (void) fprintf(stderr,
1806                                             gettext("%s: cannot set attribute"
1807                                             " times for %s: "), cmd, target);
1808                                         perror("");
1809                                         ++error;
1810                                 }
1811                         }
1812                         if (fchmod(targattrfd, mode) != 0) {
1813                                 if (clearflg) {
1814                                         (void) fprintf(stderr, gettext(
1815                                             "%s: cannot clear S_ISUID and "
1816                                             "S_ISGID bits in attribute %s"
1817                                             " for file"
1818                                             " %s: "), cmd, dp->d_name, target);
1819                                 } else {
1820                                         if (!attrsilent) {
1821                                                 (void) fprintf(stderr,
1822                                                     gettext(
1823                                 "%s: cannot set permissions of attribute"
1824                                 " %s for %s: "), cmd, dp->d_name, target);
1825                                                 perror("");
1826                                                 ++error;
1827                                         }
1828                                 }
1829                         }
1830                         if (xacl && ((facl_set(targattrfd, xacl)) < 0)) {
1831                                 if (!attrsilent) {
1832                                         (void) fprintf(stderr, gettext(
1833                                             "%s: failed to set acl entries on"
1834                                             " attribute %s for"
1835                                             "%s\n"), cmd, dp->d_name, target);
1836                                         ++error;
1837                                 }
1838                                 acl_free(xacl);
1839                                 xacl = NULL;
1840                         }
1841                 }
1842 next:
1843                 if (xacl != NULL) {
1844                         acl_free(xacl);
1845                         xacl = NULL;
1846                 }
1847                 if (srcattrfd != -1)
1848                         (void) close(srcattrfd);
1849                 if (targattrfd != -1)
1850                         (void) close(targattrfd);
1851                 srcattrfd = targattrfd = -1;
1852         }
1853 out:
1854         if (xacl != NULL) {
1855                 acl_free(xacl);
1856                 xacl = NULL;
1857         }
1858         if (attrdiracl != NULL) {
1859                 acl_free(attrdiracl);
1860                 attrdiracl = NULL;
1861         }
1862 
1863         if (!saflg && !pflg && !mve)
1864                 close_all();
1865         return (error == 0 ? 0 : 1);
1866 }
1867 
1868 /* Copy extended system attributes from source to target */
1869 
1870 static int
1871 copy_sysattr(char *source, char *target)
1872 {
1873         struct dirent   *dp;
1874         nvlist_t        *response;
1875         int             error = 0;
1876         int             target_sa_support = 0;
1877 
1878         if (sysattr_support(source, _PC_SATTR_EXISTS) != 1)
1879                 return (0);
1880 
1881         if (open_source(source) != 0)
1882                 return (1);
1883 
1884         /*
1885          * Gets non default extended system attributes from the
1886          * source file to copy to the target. The target has
1887          * the defaults set when its created and thus  no need
1888          * to copy the defaults.
1889          */
1890         response = sysattr_list(cmd, srcfd, source);
1891 
1892         if (sysattr_support(target, _PC_SATTR_ENABLED) != 1) {
1893                 if (response != NULL) {
1894                         (void) fprintf(stderr,
1895                             gettext(
1896                             "%s: cannot preserve extended system "
1897                             "attribute, operation not supported on file"
1898                             " %s\n"), cmd, target);
1899                         error++;
1900                         goto out;
1901                 }
1902         } else {
1903                 target_sa_support = 1;
1904         }
1905 
1906         if (target_sa_support) {
1907                 if (srcdirp == NULL) {
1908                         if (open_target_srctarg_attrdirs(source,
1909                             target) !=  0) {
1910                                 error++;
1911                                 goto out;
1912                         }
1913                         if (open_attrdirp(source) != 0) {
1914                                 error++;
1915                                 goto out;
1916                         }
1917                 } else {
1918                         rewind_attrdir(srcdirp);
1919                 }
1920                 while ((dp = readdir(srcdirp)) != NULL) {
1921                         nvlist_t        *res;
1922                         int             ret;
1923 
1924                         if ((ret = traverse_attrfile(dp, source, target,
1925                             0)) == -1)
1926                                 continue;
1927                         else if (ret > 0) {
1928                                 ++error;
1929                                 goto out;
1930                         }
1931                         /*
1932                          * Gets non default extended system attributes from the
1933                          * attribute file to copy to the target. The target has
1934                          * the defaults set when its created and thus  no need
1935                          * to copy the defaults.
1936                          */
1937                         if (dp->d_name != NULL) {
1938                                 res = sysattr_list(cmd, srcattrfd, dp->d_name);
1939                                 if (res == NULL)
1940                                         goto next;
1941 
1942                         /*
1943                          * Copy non default extended system attributes of named
1944                          * attribute file.
1945                          */
1946                                 if (fsetattr(targattrfd,
1947                                     XATTR_VIEW_READWRITE, res) != 0) {
1948                                         ++error;
1949                                         (void) fprintf(stderr, gettext("%s: "
1950                                             "Failed to copy extended system "
1951                                             "attributes from attribute file "
1952                                             "%s of %s to %s\n"), cmd,
1953                                             dp->d_name, source, target);
1954                                 }
1955                         }
1956 next:
1957                         if (srcattrfd != -1)
1958                                 (void) close(srcattrfd);
1959                         if (targattrfd != -1)
1960                                 (void) close(targattrfd);
1961                         srcattrfd = targattrfd = -1;
1962                         nvlist_free(res);
1963                 }
1964         }
1965         /* Copy source file non default extended system attributes to target */
1966         if (target_sa_support && (response != NULL) &&
1967             (fsetattr(targfd, XATTR_VIEW_READWRITE, response)) != 0) {
1968                 ++error;
1969                 (void) fprintf(stderr, gettext("%s: Failed to "
1970                     "copy extended system attributes from "
1971                     "%s to %s\n"), cmd, source, target);
1972         }
1973 out:
1974         nvlist_free(response);
1975         close_all();
1976         return (error == 0 ? 0 : 1);
1977 }
1978 
1979 /* Open the source file */
1980 
1981 int
1982 open_source(char  *src)
1983 {
1984         int     error = 0;
1985 
1986         srcfd = -1;
1987         if ((srcfd = open(src, O_RDONLY)) == -1) {
1988                 if (pflg && attrsilent) {
1989                         error++;
1990                         goto out;
1991                 }
1992                 if (!attrsilent) {
1993                         (void) fprintf(stderr,
1994                             gettext("%s: cannot open file"
1995                             " %s: "), cmd, src);
1996                         perror("");
1997                 }
1998                 ++error;
1999         }
2000 out:
2001         if (error)
2002                 close_all();
2003         return (error == 0 ? 0 : 1);
2004 }
2005 
2006 /* Open source attribute dir, target and target attribute dir. */
2007 
2008 int
2009 open_target_srctarg_attrdirs(char  *src, char *targ)
2010 {
2011         int             error = 0;
2012 
2013         targfd = sourcedirfd = targetdirfd = -1;
2014 
2015         if ((targfd = open(targ, O_RDONLY)) == -1) {
2016                 if (pflg && attrsilent) {
2017                         error++;
2018                         goto out;
2019                 }
2020                 if (!attrsilent) {
2021                         (void) fprintf(stderr,
2022                             gettext("%s: cannot open file"
2023                             " %s: "), cmd, targ);
2024                         perror("");
2025                 }
2026                 ++error;
2027                 goto out;
2028         }
2029 
2030         if ((sourcedirfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
2031                 if (pflg && attrsilent) {
2032                         error++;
2033                         goto out;
2034                 }
2035                 if (!attrsilent) {
2036                         (void) fprintf(stderr,
2037                             gettext("%s: cannot open attribute"
2038                             " directory for %s: "), cmd, src);
2039                         perror("");
2040                 }
2041                 ++error;
2042                 goto out;
2043         }
2044 
2045         if (fstat(sourcedirfd, &attrdir) == -1) {
2046                 if (pflg && attrsilent) {
2047                         error++;
2048                         goto out;
2049                 }
2050 
2051                 if (!attrsilent) {
2052                         (void) fprintf(stderr,
2053                             gettext("%s: could not retrieve stat"
2054                             " information for attribute directory"
2055                             "of file %s: "), cmd, src);
2056                         perror("");
2057                 }
2058                 ++error;
2059                 goto out;
2060         }
2061         if ((targetdirfd = openat(targfd, ".", O_RDONLY|O_XATTR)) == -1) {
2062                 if (pflg && attrsilent) {
2063                         error++;
2064                         goto out;
2065                 }
2066                 if (!attrsilent) {
2067                         (void) fprintf(stderr,
2068                             gettext("%s: cannot open attribute"
2069                             " directory for %s: "), cmd, targ);
2070                         perror("");
2071                 }
2072                 ++error;
2073         }
2074 out:
2075         if (error)
2076                 close_all();
2077         return (error == 0 ? 0 : 1);
2078 }
2079 
2080 int
2081 open_attrdirp(char *source)
2082 {
2083         int tmpfd = -1;
2084         int error = 0;
2085 
2086         /*
2087          * dup sourcedirfd for use by fdopendir().
2088          * fdopendir will take ownership of given fd and will close
2089          * it when closedir() is called.
2090          */
2091 
2092         if ((tmpfd = dup(sourcedirfd)) == -1) {
2093                 if (pflg && attrsilent) {
2094                         error++;
2095                         goto out;
2096                 }
2097                 if (!attrsilent) {
2098                         (void) fprintf(stderr,
2099                             gettext(
2100                             "%s: unable to dup attribute directory"
2101                             " file descriptor for %s: "), cmd, source);
2102                         perror("");
2103                         ++error;
2104                 }
2105                 goto out;
2106         }
2107         if ((srcdirp = fdopendir(tmpfd)) == NULL) {
2108                 if (pflg && attrsilent) {
2109                         error++;
2110                         goto out;
2111                 }
2112                 if (!attrsilent) {
2113                         (void) fprintf(stderr,
2114                             gettext("%s: failed to open attribute"
2115                             " directory for %s: "), cmd, source);
2116                         perror("");
2117                         ++error;
2118                 }
2119         }
2120 out:
2121         if (error)
2122                 close_all();
2123         return (error == 0 ? 0 : 1);
2124 }
2125 
2126 /* Skips through ., .., and system attribute 'view' files */
2127 int
2128 traverse_attrfile(struct dirent *dp, char *source, char *target, int  first)
2129 {
2130         int             error = 0;
2131 
2132         srcattrfd = targattrfd = -1;
2133 
2134         if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
2135             (dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
2136             dp->d_name[2] == '\0') ||
2137             (sysattr_type(dp->d_name) == _RO_SATTR) ||
2138             (sysattr_type(dp->d_name) == _RW_SATTR))
2139                 return (-1);
2140 
2141         if ((srcattrfd = openat(sourcedirfd, dp->d_name,
2142             O_RDONLY)) == -1) {
2143                 if (!attrsilent) {
2144                         (void) fprintf(stderr,
2145                             gettext("%s: cannot open attribute %s on"
2146                             " file %s: "), cmd, dp->d_name, source);
2147                         perror("");
2148                         ++error;
2149                         goto out;
2150                 }
2151         }
2152 
2153         if (fstat(srcattrfd, &s3) < 0) {
2154                 if (!attrsilent) {
2155                         (void) fprintf(stderr,
2156                             gettext("%s: could not stat attribute"
2157                             " %s on file"
2158                             " %s: "), cmd, dp->d_name, source);
2159                         perror("");
2160                         ++error;
2161                 }
2162                 goto out;
2163         }
2164 
2165         if (first) {
2166                 (void) unlinkat(targetdirfd, dp->d_name, 0);
2167                 if ((targattrfd = openat(targetdirfd, dp->d_name,
2168                     O_RDWR|O_CREAT|O_TRUNC, s3.st_mode & MODEBITS)) == -1) {
2169                         if (!attrsilent) {
2170                                 (void) fprintf(stderr,
2171                                     gettext("%s: could not create attribute"
2172                                     " %s on file %s: "), cmd, dp->d_name,
2173                                     target);
2174                                 perror("");
2175                                 ++error;
2176                         }
2177                         goto out;
2178                 }
2179         } else {
2180                 if ((targattrfd = openat(targetdirfd, dp->d_name,
2181                     O_RDONLY)) == -1) {
2182                         if (!attrsilent) {
2183                                 (void) fprintf(stderr,
2184                                     gettext("%s: could not open attribute"
2185                                     " %s on file %s: "), cmd, dp->d_name,
2186                                     target);
2187                                 perror("");
2188                                 ++error;
2189                         }
2190                         goto out;
2191                 }
2192         }
2193 
2194 
2195         if (fstat(targattrfd, &s4) < 0) {
2196                 if (!attrsilent) {
2197                         (void) fprintf(stderr,
2198                             gettext("%s: could not stat attribute"
2199                             " %s on file"
2200                             " %s: "), cmd, dp->d_name, target);
2201                         perror("");
2202                         ++error;
2203                 }
2204         }
2205 
2206 out:
2207         if (error) {
2208                 if (srcattrfd != -1)
2209                         (void) close(srcattrfd);
2210                 if (targattrfd != -1)
2211                         (void) close(targattrfd);
2212                 srcattrfd = targattrfd = -1;
2213         }
2214         return (error == 0 ? 0 :1);
2215 }
2216 
2217 void
2218 rewind_attrdir(DIR * sdp)
2219 {
2220         int pwdfd;
2221 
2222         pwdfd = open(".", O_RDONLY);
2223         if ((pwdfd != -1) && (fchdir(sourcedirfd) == 0)) {
2224                 rewinddir(sdp);
2225                 (void) fchdir(pwdfd);
2226                 (void) close(pwdfd);
2227         } else {
2228                 if (!attrsilent) {
2229                         (void) fprintf(stderr, gettext("%s: "
2230                             "failed to rewind attribute dir\n"),
2231                             cmd);
2232                 }
2233         }
2234 }
2235 
2236 void
2237 close_all()
2238 {
2239         if (srcattrfd != -1)
2240                 (void) close(srcattrfd);
2241         if (targattrfd != -1)
2242                 (void) close(targattrfd);
2243         if (sourcedirfd != -1)
2244                 (void) close(sourcedirfd);
2245         if (targetdirfd != -1)
2246                 (void) close(targetdirfd);
2247         if (srcdirp != NULL) {
2248                 (void) closedir(srcdirp);
2249                 srcdirp = NULL;
2250         }
2251         if (srcfd != -1)
2252                 (void) close(srcfd);
2253         if (targfd != -1)
2254                 (void) close(targfd);
2255 }