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