root/usr/src/lib/libcmdutils/common/process_xattrs.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright 2012 Milan Jurik. All rights reserved.
 * Copyright (c) 2019, Joyent, Inc.
 */

#include "libcmdutils.h"


/*
 * Gets file descriptors of attribute directories for source and target
 * attribute files
 */
int
get_attrdirs(int indfd, int outdfd, char *attrfile, int *sfd, int *tfd)
{
        int     pwdfd;
        int     fd1;
        int     fd2;

        pwdfd = open(".", O_RDONLY);
        if ((pwdfd != -1) && (fchdir(indfd) == 0)) {
                if ((fd1 = attropen(attrfile, ".", O_RDONLY)) == -1) {
                        (void) fchdir(pwdfd);
                        (void) close(pwdfd);
                        return (1);
                }
                *sfd = fd1;
        } else {
                (void) fchdir(pwdfd);
                (void) close(pwdfd);
                return (1);
        }
        if (fchdir(outdfd) == 0) {
                if ((fd2 = attropen(attrfile, ".", O_RDONLY)) == -1) {
                        (void) fchdir(pwdfd);
                        (void) close(pwdfd);
                        return (1);
                }
                *tfd = fd2;
        } else {
                (void) fchdir(pwdfd);
                (void) close(pwdfd);
                return (1);
        }
        (void) fchdir(pwdfd);
        return (0);
}

/*
 * mv_xattrs - Copies the content of the extended attribute files. Then
 *      moves the extended system attributes from the input attribute files
 *      to the target attribute files. Moves the extended system attributes
 *      from source to the target file. This function returns 0 on success
 *      and nonzero on error.
 */
int
mv_xattrs(char *cmd, char *infile, char *outfile, int sattr, int silent)
{
        int srcfd = -1;
        int indfd = -1;
        int outdfd = -1;
        int tmpfd = -1;
        int sattrfd = -1;
        int tattrfd = -1;
        int asfd = -1;
        int atfd = -1;
        DIR *dirp = NULL;
        struct dirent *dp = NULL;
        char *etext = NULL;
        struct stat st1;
        struct stat st2;
        nvlist_t *response = NULL;
        nvlist_t *res = NULL;

        if ((srcfd = open(infile, O_RDONLY)) == -1) {
                etext = dgettext(TEXT_DOMAIN, "cannot open source");
                goto error;
        }
        if (sattr)
                response = sysattr_list(cmd, srcfd, infile);

        if ((indfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
                etext = dgettext(TEXT_DOMAIN, "cannot openat source");
                goto error;
        }
        if ((outdfd = attropen(outfile, ".", O_RDONLY)) == -1) {
                etext = dgettext(TEXT_DOMAIN, "cannot attropen target");
                goto error;
        }
        if ((tmpfd = dup(indfd)) == -1) {
                etext = dgettext(TEXT_DOMAIN, "cannot dup descriptor");
                goto error;

        }
        if ((dirp = fdopendir(tmpfd)) == NULL) {
                etext = dgettext(TEXT_DOMAIN, "cannot access source");
                goto error;
        }
        while ((dp = readdir(dirp)) != NULL) {
                if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
                    (dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
                    dp->d_name[2] == '\0') ||
                    (sysattr_type(dp->d_name) == _RO_SATTR) ||
                    (sysattr_type(dp->d_name) == _RW_SATTR))
                        continue;

                if ((sattrfd = openat(indfd, dp->d_name,
                    O_RDONLY)) == -1) {
                        etext = dgettext(TEXT_DOMAIN,
                            "cannot open src attribute file");
                        goto error;
                }
                if (fstat(sattrfd, &st1) < 0) {
                        etext = dgettext(TEXT_DOMAIN,
                            "could not stat attribute file");
                        goto error;
                }
                if ((tattrfd = openat(outdfd, dp->d_name,
                    O_RDWR|O_CREAT|O_TRUNC, st1.st_mode)) == -1) {
                        etext = dgettext(TEXT_DOMAIN,
                            "cannot open target attribute file");
                        goto error;
                }
                if (fstat(tattrfd, &st2) < 0) {
                        etext = dgettext(TEXT_DOMAIN,
                            "could not stat attribute file");
                        goto error;
                }
                if (writefile(sattrfd, tattrfd, infile, outfile, dp->d_name,
                    dp->d_name, &st1, &st2) != 0) {
                        etext = dgettext(TEXT_DOMAIN,
                            "failed to copy extended attribute "
                            "from source to target");
                        goto error;
                }

                errno = 0;
                if (sattr) {
                        /*
                         * Gets non default extended system attributes from
                         * source to copy to target.
                         */
                        res = sysattr_list(cmd, sattrfd, dp->d_name);

                        if (res != NULL &&
                            get_attrdirs(indfd, outdfd, dp->d_name, &asfd,
                            &atfd) != 0) {
                                etext = dgettext(TEXT_DOMAIN,
                                    "Failed to open attribute files");
                                goto error;
                        }
                        /*
                         * Copy extended system attribute from source
                         * attribute file to target attribute file
                         */
                        if (res != NULL &&
                            (renameat(asfd, VIEW_READWRITE, atfd,
                            VIEW_READWRITE) != 0)) {
                                if (errno == EPERM)
                                        etext = dgettext(TEXT_DOMAIN,
                                            "Permission denied -"
                                            "failed to move system attribute");
                                else
                                        etext = dgettext(TEXT_DOMAIN,
                                            "failed to move extended "
                                            "system attribute");
                                goto error;
                        }
                }
                if (sattrfd != -1)
                        (void) close(sattrfd);
                if (tattrfd != -1)
                        (void) close(tattrfd);
                if (asfd != -1)
                        (void) close(asfd);
                if (atfd != -1)
                        (void) close(atfd);
                if (res != NULL) {
                        nvlist_free(res);
                        res = NULL;
                }
        }
        errno = 0;
        /* Copy extended system attribute from source to target */

        if (response != NULL) {
                if (renameat(indfd, VIEW_READWRITE, outdfd,
                    VIEW_READWRITE) == 0)
                        goto done;

                if (errno == EPERM)
                        etext = dgettext(TEXT_DOMAIN, "Permission denied");
                else
                        etext = dgettext(TEXT_DOMAIN,
                            "failed to move system attribute");
        }
error:
        nvlist_free(res);
        if (silent == 0 && etext != NULL) {
                if (!sattr)
                        (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
                            "%s: %s: cannot move extended attributes, "),
                            cmd, infile);
                else
                        (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
                            "%s: %s: cannot move extended system "
                            "attributes, "), cmd, infile);
                perror(etext);
        }
done:
        if (dirp)
                (void) closedir(dirp);
        if (sattrfd != -1)
                (void) close(sattrfd);
        if (tattrfd != -1)
                (void) close(tattrfd);
        if (asfd != -1)
                (void) close(asfd);
        if (atfd != -1)
                (void) close(atfd);
        if (indfd != -1)
                (void) close(indfd);
        if (outdfd != -1)
                (void) close(outdfd);
        nvlist_free(response);
        if (etext != NULL)
                return (1);
        else
                return (0);
}

/*
 * The function returns non default extended system attribute list
 * associated with 'fname' and returns NULL when an error has occured
 * or when only extended system attributes other than archive,
 * av_modified or crtime are set.
 *
 * The function returns system attribute list for the following cases:
 *
 *      - any extended system attribute other than the default attributes
 *        ('archive', 'av_modified' and 'crtime') is set
 *      - nvlist has NULL name string
 *      - nvpair has data type of 'nvlist'
 *      - default data type.
 */

nvlist_t *
sysattr_list(const char *cmd, int fd, const char *fname)
{
        boolean_t       value;
        data_type_t     type;
        nvlist_t        *response;
        nvpair_t        *pair;
        f_attr_t        fattr;
        char            *name;

        if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
                (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
                    "%s: %s: fgetattr failed\n"),
                    cmd, fname);
                return (NULL);
        }
        pair = NULL;
        while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {

                name = nvpair_name(pair);

                if (name != NULL)
                        fattr = name_to_attr(name);
                else
                        return (response);

                type = nvpair_type(pair);
                switch (type) {
                        case DATA_TYPE_BOOLEAN_VALUE:
                                if (nvpair_value_boolean_value(pair,
                                    &value) != 0) {
                                        (void) fprintf(stderr,
                                            dgettext(TEXT_DOMAIN, "%s "
                                            "nvpair_value_boolean_value "
                                            "failed\n"), cmd);
                                        continue;
                                }
                                if (value && fattr != F_ARCHIVE &&
                                    fattr != F_AV_MODIFIED)
                                        return (response);
                                break;
                        case DATA_TYPE_UINT64_ARRAY:
                                if (fattr != F_CRTIME)
                                        return (response);
                                break;
                        case DATA_TYPE_NVLIST:
                        default:
                                return (response);
                }
        }
        nvlist_free(response);
        return (NULL);
}