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  */
  27 
  28 #include "libcmdutils.h"
  29 
  30 
  31 /*
  32  * Gets file descriptors of attribute directories for source and target
  33  * attribute files
  34  */
  35 int
  36 get_attrdirs(int indfd, int outdfd, char *attrfile, int *sfd, int *tfd)
  37 {
  38         int     pwdfd;
  39         int     fd1;
  40         int     fd2;
  41 
  42         pwdfd = open(".", O_RDONLY);
  43         if ((pwdfd != -1) && (fchdir(indfd) == 0)) {
  44                 if ((fd1 = attropen(attrfile, ".", O_RDONLY)) == -1) {
  45                         (void) fchdir(pwdfd);
  46                         (void) close(pwdfd);
  47                         return (1);
  48                 }
  49                 *sfd = fd1;
  50         } else {
  51                 (void) fchdir(pwdfd);
  52                 (void) close(pwdfd);
  53                 return (1);
  54         }
  55         if (fchdir(outdfd) == 0) {
  56                 if ((fd2 = attropen(attrfile, ".", O_RDONLY)) == -1) {
  57                         (void) fchdir(pwdfd);
  58                         (void) close(pwdfd);
  59                         return (1);
  60                 }
  61                 *tfd = fd2;
  62         } else {
  63                 (void) fchdir(pwdfd);
  64                 (void) close(pwdfd);
  65                 return (1);
  66         }
  67         (void) fchdir(pwdfd);
  68         return (0);
  69 }
  70 
  71 /*
  72  * mv_xattrs - Copies the content of the extended attribute files. Then
  73  *      moves the extended system attributes from the input attribute files
  74  *      to the target attribute files. Moves the extended system attributes
  75  *      from source to the target file. This function returns 0 on success
  76  *      and nonzero on error.
  77  */
  78 int
  79 mv_xattrs(char *cmd, char *infile, char *outfile, int sattr, int silent)
  80 {
  81         int srcfd = -1;
  82         int indfd = -1;
  83         int outdfd = -1;
  84         int tmpfd = -1;
  85         int sattrfd = -1;
  86         int tattrfd = -1;
  87         int asfd = -1;
  88         int atfd = -1;
  89         DIR *dirp = NULL;
  90         struct dirent *dp = NULL;
  91         char *etext = NULL;
  92         struct stat st1;
  93         struct stat st2;
  94         nvlist_t *response = NULL;
  95         nvlist_t *res = NULL;
  96 
  97         if ((srcfd = open(infile, O_RDONLY)) == -1) {
  98                 etext = dgettext(TEXT_DOMAIN, "cannot open source");
  99                 goto error;
 100         }
 101         if (sattr)
 102                 response = sysattr_list(cmd, srcfd, infile);
 103 
 104         if ((indfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
 105                 etext = dgettext(TEXT_DOMAIN, "cannot openat source");
 106                 goto error;
 107         }
 108         if ((outdfd = attropen(outfile, ".", O_RDONLY)) == -1) {
 109                 etext = dgettext(TEXT_DOMAIN, "cannot attropen target");
 110                 goto error;
 111         }
 112         if ((tmpfd = dup(indfd)) == -1) {
 113                 etext = dgettext(TEXT_DOMAIN, "cannot dup descriptor");
 114                 goto error;
 115 
 116         }
 117         if ((dirp = fdopendir(tmpfd)) == NULL) {
 118                 etext = dgettext(TEXT_DOMAIN, "cannot access source");
 119                 goto error;
 120         }
 121         while ((dp = readdir(dirp)) != NULL) {
 122                 if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
 123                     (dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
 124                     dp->d_name[2] == '\0') ||
 125                     (sysattr_type(dp->d_name) == _RO_SATTR) ||
 126                     (sysattr_type(dp->d_name) == _RW_SATTR))
 127                         continue;
 128 
 129                 if ((sattrfd = openat(indfd, dp->d_name,
 130                     O_RDONLY)) == -1) {
 131                         etext = dgettext(TEXT_DOMAIN,
 132                             "cannot open src attribute file");
 133                         goto error;
 134                 }
 135                 if (fstat(sattrfd, &st1) < 0) {
 136                         etext = dgettext(TEXT_DOMAIN,
 137                             "could not stat attribute file");
 138                         goto error;
 139                 }
 140                 if ((tattrfd = openat(outdfd, dp->d_name,
 141                     O_RDWR|O_CREAT|O_TRUNC, st1.st_mode)) == -1) {
 142                         etext = dgettext(TEXT_DOMAIN,
 143                             "cannot open target attribute file");
 144                         goto error;
 145                 }
 146                 if (fstat(tattrfd, &st2) < 0) {
 147                         etext = dgettext(TEXT_DOMAIN,
 148                             "could not stat attribute file");
 149                         goto error;
 150                 }
 151                 if (writefile(sattrfd, tattrfd, infile, outfile, dp->d_name,
 152                     dp->d_name, &st1, &st2) != 0) {
 153                         etext = dgettext(TEXT_DOMAIN,
 154                             "failed to copy extended attribute "
 155                             "from source to target");
 156                         goto error;
 157                 }
 158 
 159                 errno = 0;
 160                 if (sattr) {
 161                         /*
 162                          * Gets non default extended system attributes from
 163                          * source to copy to target.
 164                          */
 165                         if (dp->d_name != NULL)
 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 }