tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: extended attributes (need help)



The patch I posted yesterday has a race condition, if a user process 
quickly sets two attributes on two different filesystems, then the second
one will panic on VFS_ROOT() in namei() because the root vnode is already 
locked. 

I tried an alternative version that does not resolve a path from the root, 
but I get various hang, crashes and panic. I struggle to find what is wrong
here. Any hint anyone?


/*
 * Autocreate an attribute storage
 */
static struct ufs_extattr_list_entry *
ufs_extattr_autocreate_attr(struct ufsmount *ump, int attrnamespace,
    const char *attrname, struct lwp *l)
{
        struct vnode *root_vp;
        struct vnode *backing_vp;
        struct nameidata nd;
        struct vattr va;
        char path[PATH_MAX + 1];
        struct ufs_extattr_fileheader uef;
        struct ufs_extattr_list_entry *uele;
        int need_close;
        int error;

        root_vp = NULL;
        backing_vp = NULL;
        nd.ni_vp = NULL;
        nd.ni_dvp = NULL;
        uele = NULL;
        need_close = 0;

        /* 
         * We only support system and user namespace autocreation
         */ 
        switch (attrnamespace) {
        case EXTATTR_NAMESPACE_SYSTEM:
                (void)snprintf(path, PATH_MAX, "%s/%s/%s", 
                               UFS_EXTATTR_FSROOTSUBDIR,
                               UFS_EXTATTR_SUBDIR_SYSTEM,
                               attrname);
                break;
        case EXTATTR_NAMESPACE_USER:
                (void)snprintf(path, PATH_MAX, "%s/%s/%s", 
                               UFS_EXTATTR_FSROOTSUBDIR,
                               UFS_EXTATTR_SUBDIR_USER,
                               attrname);
                break;
        default:
                return NULL;
                break;
        }

        /*
         * Get filesystem root
         */
        error = vfs_busy(ump->um_mountp, NULL);
        if (error != 0) {
                printf("%s: vfs_busy() returned %d\n", __func__, error);
                return NULL;
        }

        if ((error = VFS_ROOT(ump->um_mountp, &root_vp)) != 0)
                printf("%s: VFS_ROOT() returned %d\n", __func__, error);

        vfs_unbusy(ump->um_mountp, false, NULL);

        if (error != 0)
                return NULL;

        VOP_UNLOCK(root_vp, 0);

        /*
         * Lookup backing store for creation
         */
        NDINIT(&nd, CREATE, LOCKPARENT|LOCKLEAF, UIO_SYSSPACE, path);
        nd.ni_startdir = root_vp;
        nd.ni_rootdir = root_vp;

        if ((error = lookup(&nd)) != 0)
                goto out;

        KASSERT(nd.ni_vp == NULL);
        KASSERT(nd.ni_dvp != NULL);
        KASSERT(VOP_ISLOCKED(nd.ni_dvp) == LK_EXCLUSIVE);
        KASSERT(VOP_ISLOCKED(root_vp) == 0);

        /*
         * Create backing store
         */
        VATTR_NULL(&va);
        va.va_type = VREG;
        va.va_mode = 0600;
        va.va_vaflags |= VA_EXCLUSIVE;

        error = VOP_CREATE(nd.ni_dvp, &backing_vp, &nd.ni_cnd, &va);
        if (error != 0) {
                printf("%s: cannot create %s, error = %d\n", 
                       __func__, path, error);
                goto out;
        }

        KASSERT(backing_vp != NULL);
        KASSERT(VOP_ISLOCKED(backing_vp) == LK_EXCLUSIVE);
        KASSERT(VOP_ISLOCKED(nd.ni_dvp) == LK_EXCLUSIVE);

        /*
         * Open backing store for writing
         */
        error = VOP_OPEN(backing_vp, FREAD|FWRITE, l->l_cred);
        if (error != 0) {
                printf("%s: cannot open %s, error = %d\n",
                       __func__, path, error);
                goto out;
        }

        /*
         * Remember we have to close the file on failure
         */
        need_close = 1;

        /*
         * This is decreased by vn_close
         */
        mutex_enter(&backing_vp->v_interlock);
        backing_vp->v_writecount++;
        mutex_exit(&backing_vp->v_interlock);

        /*
         * Now write extended attribute header in backing store fiie
         */     
        uef.uef_magic = UFS_EXTATTR_MAGIC;
        uef.uef_version = UFS_EXTATTR_VERSION;
        uef.uef_size = UFS_EXTATTR_AUTOCREATE;

        error = vn_rdwr(UIO_WRITE, backing_vp, &uef, sizeof(uef), 0,
                        UIO_SYSSPACE, IO_NODELOCKED|IO_APPEND, 
                        l->l_cred, NULL, l);

        KASSERT(VOP_ISLOCKED(backing_vp) == LK_EXCLUSIVE);
        VOP_UNLOCK(backing_vp, 0);

        if (error != 0) {
                printf("%s: write uef header failed for %s, error = %d\n", 
                       __func__, attrname, error);
                goto out;
        }

        /*
         * ufs_extattr_enable_with_open does it, we do it too
         * but I am not sure of the purpose
         */
        vref(backing_vp);

        /*
         * Enable the attribute. 
         */
        error = ufs_extattr_enable(ump, attrnamespace,
                                   attrname, backing_vp, l);
        if (error != 0) {
                printf("%s: enable %s failed, error %d\n", 
                       __func__, attrname, error);
                goto out;
        }

        uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
        if (uele == NULL) {
                printf("%s: attribute %s created but not found!\n",
                       __func__, attrname);
                goto out;
        }

out:
        if (backing_vp != NULL) {
                /*
                 * Success, we keep the vnode open and unlocked
                 */
                if (uele != NULL) {
                        KASSERT(VOP_ISLOCKED(backing_vp) == 0);
                        printf("%s: UFS autocreated EA backing store "
                               "for attribute %s\n", 
                               ump->um_mountp->mnt_stat.f_mntonname,
                               attrname);
                }
                /*
                 * Failure and it was left open, close it. 
                 */
                else if (need_close) {
                        KASSERT(VOP_ISLOCKED(backing_vp) == 0);
                        vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
                }
                /*
                 * Failure and it was unopen (but locked), just forget it
                 */
                else {
                        KASSERT(VOP_ISLOCKED(backing_vp) == LK_EXCLUSIVE);
                        vput(backing_vp);
                }
        }

        if (nd.ni_dvp != NULL) {
                KASSERT(VOP_ISLOCKED(nd.ni_dvp) == LK_EXCLUSIVE);
                vput(nd.ni_dvp);
        }
        
        if (root_vp != NULL) {
                KASSERT(VOP_ISLOCKED(root_vp) == 0);
                vrele(root_vp);
        }

        return uele;
}

-- 
Emmanuel Dreyfus
manu%netbsd.org@localhost


Home | Main Index | Thread Index | Old Index