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         if (res != NULL)
 222                 nvlist_free(res);
 223         if (silent == 0 && etext != NULL) {
 224                 if (!sattr)
 225                         (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
 226                             "%s: %s: cannot move extended attributes, "),
 227                             cmd, infile);
 228                 else
 229                         (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
 230                             "%s: %s: cannot move extended system "
 231                             "attributes, "), cmd, infile);
 232                 perror(etext);
 233         }
 234 done:
 235         if (dirp)
 236                 (void) closedir(dirp);
 237         if (sattrfd != -1)
 238                 (void) close(sattrfd);
 239         if (tattrfd != -1)
 240                 (void) close(tattrfd);
 241         if (asfd != -1)
 242                 (void) close(asfd);
 243         if (atfd != -1)
 244                 (void) close(atfd);
 245         if (indfd != -1)
 246                 (void) close(indfd);
 247         if (outdfd != -1)
 248                 (void) close(outdfd);
 249         if (response != NULL)
 250                 nvlist_free(response);
 251         if (etext != NULL)
 252                 return (1);
 253         else
 254                 return (0);
 255 }
 256 
 257 /*
 258  * The function returns non default extended system attribute list
 259  * associated with 'fname' and returns NULL when an error has occured
 260  * or when only extended system attributes other than archive,
 261  * av_modified or crtime are set.
 262  *
 263  * The function returns system attribute list for the following cases:
 264  *
 265  *      - any extended system attribute other than the default attributes
 266  *        ('archive', 'av_modified' and 'crtime') is set
 267  *      - nvlist has NULL name string
 268  *      - nvpair has data type of 'nvlist'
 269  *      - default data type.
 270  */
 271 
 272 nvlist_t *
 273 sysattr_list(char *cmd, int fd, char *fname)
 274 {
 275         boolean_t       value;
 276         data_type_t     type;
 277         nvlist_t        *response;
 278         nvpair_t        *pair;
 279         f_attr_t        fattr;
 280         char            *name;
 281 
 282         if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
 283                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
 284                     "%s: %s: fgetattr failed\n"),
 285                     cmd, fname);
 286                 return (NULL);
 287         }
 288         pair = NULL;
 289         while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
 290 
 291                 name = nvpair_name(pair);
 292 
 293                 if (name != NULL)
 294                         fattr = name_to_attr(name);
 295                 else
 296                         return (response);
 297 
 298                 type = nvpair_type(pair);
 299                 switch (type) {
 300                         case DATA_TYPE_BOOLEAN_VALUE:
 301                                 if (nvpair_value_boolean_value(pair,
 302                                     &value) != 0) {
 303                                         (void) fprintf(stderr,
 304                                             dgettext(TEXT_DOMAIN, "%s "
 305                                             "nvpair_value_boolean_value "
 306                                             "failed\n"), cmd);
 307                                         continue;
 308                                 }
 309                                 if (value && fattr != F_ARCHIVE &&
 310                                     fattr != F_AV_MODIFIED)
 311                                         return (response);
 312                                 break;
 313                         case DATA_TYPE_UINT64_ARRAY:
 314                                 if (fattr != F_CRTIME)
 315                                         return (response);
 316                                 break;
 317                         case DATA_TYPE_NVLIST:
 318                         default:
 319                                 return (response);
 320                 }
 321         }
 322         if (response != NULL)
 323                 nvlist_free(response);
 324         return (NULL);
 325 }