1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  * Copyright 2012 Milan Jurik. All rights reserved.
  26  * Copyright (c) 2018, Joyent, Inc.
  27  */
  28 
  29 #include "libcmdutils.h"
  30 
  31 
  32 /*
  33  * Gets file descriptors of attribute directories for source and target
  34  * attribute files
  35  */
  36 int
  37 get_attrdirs(int indfd, int outdfd, char *attrfile, int *sfd, int *tfd)
  38 {
  39         int     pwdfd;
  40         int     fd1;
  41         int     fd2;
  42 
  43         pwdfd = open(".", O_RDONLY);
  44         if ((pwdfd != -1) && (fchdir(indfd) == 0)) {
  45                 if ((fd1 = attropen(attrfile, ".", O_RDONLY)) == -1) {
  46                         (void) fchdir(pwdfd);
  47                         (void) close(pwdfd);
  48                         return (1);
  49                 }
  50                 *sfd = fd1;
  51         } else {
  52                 (void) fchdir(pwdfd);
  53                 (void) close(pwdfd);
  54                 return (1);
  55         }
  56         if (fchdir(outdfd) == 0) {
  57                 if ((fd2 = attropen(attrfile, ".", O_RDONLY)) == -1) {
  58                         (void) fchdir(pwdfd);
  59                         (void) close(pwdfd);
  60                         return (1);
  61                 }
  62                 *tfd = fd2;
  63         } else {
  64                 (void) fchdir(pwdfd);
  65                 (void) close(pwdfd);
  66                 return (1);
  67         }
  68         (void) fchdir(pwdfd);
  69         return (0);
  70 }
  71 
  72 /*
  73  * mv_xattrs - Copies the content of the extended attribute files. Then
  74  *      moves the extended system attributes from the input attribute files
  75  *      to the target attribute files. Moves the extended system attributes
  76  *      from source to the target file. This function returns 0 on success
  77  *      and nonzero on error.
  78  */
  79 int
  80 mv_xattrs(char *cmd, char *infile, char *outfile, int sattr, int silent)
  81 {
  82         int srcfd = -1;
  83         int indfd = -1;
  84         int outdfd = -1;
  85         int tmpfd = -1;
  86         int sattrfd = -1;
  87         int tattrfd = -1;
  88         int asfd = -1;
  89         int atfd = -1;
  90         DIR *dirp = NULL;
  91         struct dirent *dp = NULL;
  92         char *etext = NULL;
  93         struct stat st1;
  94         struct stat st2;
  95         nvlist_t *response = NULL;
  96         nvlist_t *res = NULL;
  97 
  98         if ((srcfd = open(infile, O_RDONLY)) == -1) {
  99                 etext = dgettext(TEXT_DOMAIN, "cannot open source");
 100                 goto error;
 101         }
 102         if (sattr)
 103                 response = sysattr_list(cmd, srcfd, infile);
 104 
 105         if ((indfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
 106                 etext = dgettext(TEXT_DOMAIN, "cannot openat source");
 107                 goto error;
 108         }
 109         if ((outdfd = attropen(outfile, ".", O_RDONLY)) == -1) {
 110                 etext = dgettext(TEXT_DOMAIN, "cannot attropen target");
 111                 goto error;
 112         }
 113         if ((tmpfd = dup(indfd)) == -1) {
 114                 etext = dgettext(TEXT_DOMAIN, "cannot dup descriptor");
 115                 goto error;
 116 
 117         }
 118         if ((dirp = fdopendir(tmpfd)) == NULL) {
 119                 etext = dgettext(TEXT_DOMAIN, "cannot access source");
 120                 goto error;
 121         }
 122         while ((dp = readdir(dirp)) != NULL) {
 123                 if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
 124                     (dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
 125                     dp->d_name[2] == '\0') ||
 126                     (sysattr_type(dp->d_name) == _RO_SATTR) ||
 127                     (sysattr_type(dp->d_name) == _RW_SATTR))
 128                         continue;
 129 
 130                 if ((sattrfd = openat(indfd, dp->d_name,
 131                     O_RDONLY)) == -1) {
 132                         etext = dgettext(TEXT_DOMAIN,
 133                             "cannot open src attribute file");
 134                         goto error;
 135                 }
 136                 if (fstat(sattrfd, &st1) < 0) {
 137                         etext = dgettext(TEXT_DOMAIN,
 138                             "could not stat attribute file");
 139                         goto error;
 140                 }
 141                 if ((tattrfd = openat(outdfd, dp->d_name,
 142                     O_RDWR|O_CREAT|O_TRUNC, st1.st_mode)) == -1) {
 143                         etext = dgettext(TEXT_DOMAIN,
 144                             "cannot open target attribute file");
 145                         goto error;
 146                 }
 147                 if (fstat(tattrfd, &st2) < 0) {
 148                         etext = dgettext(TEXT_DOMAIN,
 149                             "could not stat attribute file");
 150                         goto error;
 151                 }
 152                 if (writefile(sattrfd, tattrfd, infile, outfile, dp->d_name,
 153                     dp->d_name, &st1, &st2) != 0) {
 154                         etext = dgettext(TEXT_DOMAIN,
 155                             "failed to copy extended attribute "
 156                             "from source to target");
 157                         goto error;
 158                 }
 159 
 160                 errno = 0;
 161                 if (sattr) {
 162                         /*
 163                          * Gets non default extended system attributes from
 164                          * source to copy to target.
 165                          */
 166                         res = sysattr_list(cmd, sattrfd, dp->d_name);
 167 
 168                         if (res != NULL &&
 169                             get_attrdirs(indfd, outdfd, dp->d_name, &asfd,
 170                             &atfd) != 0) {
 171                                 etext = dgettext(TEXT_DOMAIN,
 172                                     "Failed to open attribute files");
 173                                 goto error;
 174                         }
 175                         /*
 176                          * Copy extended system attribute from source
 177                          * attribute file to target attribute file
 178                          */
 179                         if (res != NULL &&
 180                             (renameat(asfd, VIEW_READWRITE, atfd,
 181                             VIEW_READWRITE) != 0)) {
 182                                 if (errno == EPERM)
 183                                         etext = dgettext(TEXT_DOMAIN,
 184                                             "Permission denied -"
 185                                             "failed to move system attribute");
 186                                 else
 187                                         etext = dgettext(TEXT_DOMAIN,
 188                                             "failed to move extended "
 189                                             "system attribute");
 190                                 goto error;
 191                         }
 192                 }
 193                 if (sattrfd != -1)
 194                         (void) close(sattrfd);
 195                 if (tattrfd != -1)
 196                         (void) close(tattrfd);
 197                 if (asfd != -1)
 198                         (void) close(asfd);
 199                 if (atfd != -1)
 200                         (void) close(atfd);
 201                 if (res != NULL) {
 202                         nvlist_free(res);
 203                         res = NULL;
 204                 }
 205         }
 206         errno = 0;
 207         /* Copy extended system attribute from source to target */
 208 
 209         if (response != NULL) {
 210                 if (renameat(indfd, VIEW_READWRITE, outdfd,
 211                     VIEW_READWRITE) == 0)
 212                         goto done;
 213 
 214                 if (errno == EPERM)
 215                         etext = dgettext(TEXT_DOMAIN, "Permission denied");
 216                 else
 217                         etext = dgettext(TEXT_DOMAIN,
 218                             "failed to move system attribute");
 219         }
 220 error:
 221         nvlist_free(res);
 222         if (silent == 0 && etext != NULL) {
 223                 if (!sattr)
 224                         (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
 225                             "%s: %s: cannot move extended attributes, "),
 226                             cmd, infile);
 227                 else
 228                         (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
 229                             "%s: %s: cannot move extended system "
 230                             "attributes, "), cmd, infile);
 231                 perror(etext);
 232         }
 233 done:
 234         if (dirp)
 235                 (void) closedir(dirp);
 236         if (sattrfd != -1)
 237                 (void) close(sattrfd);
 238         if (tattrfd != -1)
 239                 (void) close(tattrfd);
 240         if (asfd != -1)
 241                 (void) close(asfd);
 242         if (atfd != -1)
 243                 (void) close(atfd);
 244         if (indfd != -1)
 245                 (void) close(indfd);
 246         if (outdfd != -1)
 247                 (void) close(outdfd);
 248         nvlist_free(response);
 249         if (etext != NULL)
 250                 return (1);
 251         else
 252                 return (0);
 253 }
 254 
 255 /*
 256  * The function returns non default extended system attribute list
 257  * associated with 'fname' and returns NULL when an error has occured
 258  * or when only extended system attributes other than archive,
 259  * av_modified or crtime are set.
 260  *
 261  * The function returns system attribute list for the following cases:
 262  *
 263  *      - any extended system attribute other than the default attributes
 264  *        ('archive', 'av_modified' and 'crtime') is set
 265  *      - nvlist has NULL name string
 266  *      - nvpair has data type of 'nvlist'
 267  *      - default data type.
 268  */
 269 
 270 nvlist_t *
 271 sysattr_list(char *cmd, int fd, char *fname)
 272 {
 273         boolean_t       value;
 274         data_type_t     type;
 275         nvlist_t        *response;
 276         nvpair_t        *pair;
 277         f_attr_t        fattr;
 278         char            *name;
 279 
 280         if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
 281                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
 282                     "%s: %s: fgetattr failed\n"),
 283                     cmd, fname);
 284                 return (NULL);
 285         }
 286         pair = NULL;
 287         while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
 288 
 289                 name = nvpair_name(pair);
 290 
 291                 if (name != NULL)
 292                         fattr = name_to_attr(name);
 293                 else
 294                         return (response);
 295 
 296                 type = nvpair_type(pair);
 297                 switch (type) {
 298                         case DATA_TYPE_BOOLEAN_VALUE:
 299                                 if (nvpair_value_boolean_value(pair,
 300                                     &value) != 0) {
 301                                         (void) fprintf(stderr,
 302                                             dgettext(TEXT_DOMAIN, "%s "
 303                                             "nvpair_value_boolean_value "
 304                                             "failed\n"), cmd);
 305                                         continue;
 306                                 }
 307                                 if (value && fattr != F_ARCHIVE &&
 308                                     fattr != F_AV_MODIFIED)
 309                                         return (response);
 310                                 break;
 311                         case DATA_TYPE_UINT64_ARRAY:
 312                                 if (fattr != F_CRTIME)
 313                                         return (response);
 314                                 break;
 315                         case DATA_TYPE_NVLIST:
 316                         default:
 317                                 return (response);
 318                 }
 319         }
 320         nvlist_free(response);
 321         return (NULL);
 322 }