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