打开一个有delegation的文件(权限没有冲突)

    这篇文章中我们讨论这样一种情况:假设用户user1以只读权限打开了文件file1,并且服务器为这个文件分配了delegation。现在用户user2用样以只读权限打开文件file1,那么user2打开文件的过程是什么样的呢?是否还需要向服务器发送OPEN请求?

    和前一篇文章相似,客户端仍然需要创建一个RPC任务,在发送OPEN请求报文前执行函数nfs4_open_prepare(),我们看看这种情况下程序怎么执行的。

static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
{
        struct nfs4_opendata *data = calldata;          // 取出nfs4_opendata结构
        struct nfs4_state_owner *sp = data->owner;      // nfs4_state_owner结构

        ....
        if (data->state != NULL) {      // 这是一个nfs4_state结构.
                struct nfs_delegation *delegation;
                ....

                delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
                if (data->o_arg.claim != NFS4_OPEN_CLAIM_DELEGATE_CUR &&
                    can_open_delegated(delegation, data->o_arg.fmode))
                        goto unlock_no_action;
                rcu_read_unlock();
        }
        ....

unlock_no_action:
        rcu_read_unlock();
out_no_action:
        task->tk_action = NULL; 
}

    我们略去了无关的代码,可以看出nfs4_open_prepare()调用了函数can_open_delegated()。如果这个函数返回非0值就不需要发起OPEN请求了,如果这个函数返回0就仍然需要发起OPEN请求。我们马上看看这个函数的代码

static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
{
        if (delegation == NULL)
                return 0;
        if ((delegation->type & fmode) != fmode)        // 访问权限不同,不能打开.
                return 0;
        if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
                return 0;
        nfs_mark_delegation_referenced(delegation);     // 可以使用
        return 1;
}

    这个函数很简单,就是比较了了文件中delegation的类型是OPEN操作中的类型是否一致。如果delegation的类型是只读,而fmode中请求的访问权限也是只读,则二者匹配,就不需要再次发起OPEN请求了。但是,这个函数还检查了标志位NFS_DELEGATION_NEED_RECLAIM,前面的文章中讲过,这个标志位标志delegation中的信息无效,需要更新了。因此如果设置了这个标志位函数也返回0。如果确定了可以使用这个delegation,那么就需要调用函数nfs_mark_delegation_referenced(),这个函数的作用是设置标志位NFS_DELEGATION_REFERENCED。前面的文章中讲过,NFS_DELEGATION_REFERENCED表示这是一个有效的delegation。客户端的delegation只在一段时间内有效,如果delegation在一段时间内没有使用,客户端就认为这个delegation已经没用了,就会清除这个标志位。再过一段时间就会删除这个delegation。这里设置标志位NFS_DELEGATION_REFERENCED的作用是告诉客户端,这是一个有用的delegation,你不要删除了。

    同样,由于没有发起RPC请求,在设置nfs4_state时nfs4_opendata_to_nfs4_state()也会调用到nfs4_try_open_cached(),我们看看这种情况下nfs4_try_open_cached()的流程。

static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
{
        struct nfs4_state *state = opendata->state;     // 这是使用的nfs4_state结构.
        struct nfs_inode *nfsi = NFS_I(state->inode);   // 这是文件索引节点
        struct nfs_delegation *delegation;              // 查找delegation.
        int open_mode = opendata->o_arg.open_flags & (O_EXCL|O_TRUNC);
        fmode_t fmode = opendata->o_arg.fmode;          // 这是文件访问模式
        nfs4_stateid stateid;
        int ret = -EAGAIN;

        for (;;) {
                rcu_read_lock();
                delegation = rcu_dereference(nfsi->delegation); // 取出delegation.
                // 检查是否可以引用这个delegation.
                if (!can_open_delegated(delegation, fmode)) {
                        rcu_read_unlock();
                        break;          // 如果不可以引用这个delegation,就直接退出.
                }
                /* Save the delegation */       // OK,现在可以引用这个delegation了.
                nfs4_stateid_copy(&stateid, &delegation->stateid);      // 先取出了delegation的stateid.
                rcu_read_unlock();
		// 检查用户是否有访问权限.
                ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode);
                if (ret != 0)
                        goto out;
                ret = -EAGAIN;

                /* Try to update the stateid using the delegation */
		// 更新stateid
                if (update_open_stateid(state, NULL, &stateid, fmode))
                        goto out_return_state;
        }
out:
        return ERR_PTR(ret);
out_return_state:
        atomic_inc(&state->count);
        return state;
}

    这种情况下nfs4_try_open_cached()依次调用了四个函数:can_open_delegated()、nfs4_stateid_copy()、nfs_may_open()、update_open_stateid()。can_open_delegated()再次检查了user2是否可以使用这个delegation,由于前面已经检查过了,一般情况下这里不会出错。接下来我们看看另外三个函数的作用。

static inline void nfs4_stateid_copy(nfs4_stateid *dst, const nfs4_stateid *src)
{
        memcpy(dst, src, sizeof(*dst));
}
这个函数很简单,就是取出了delegation中的stateid。

int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags)
{
        return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags));
}

nfs_may_open()的作用是检查user2对文件file1的访问权限,是否可以以只读权限打开文件。因为客户端不会向服务器发起OPEN请求了。如果user2没有file1的访问权限,那么接下来的读操作就会出错。为了避免这种情况因此需要先检查user2对file1的访问权限。这里,nfs_do_access()会向服务器发起ACCESS请求,我们在前面的文章中讲解过这个函数了。

opendata->state是一个新创建的nfs4_state结构,update_open_stateid()的最用是更新这个结构中的信息。

static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, fmode_t fmode)
{
        struct nfs_inode *nfsi = NFS_I(state->inode);           // 找到文件的索引节点
        struct nfs_delegation *deleg_cur;
        int ret = 0;

        fmode &= (FMODE_READ|FMODE_WRITE);      // 文件访问权限  读写
        rcu_read_lock();
        deleg_cur = rcu_dereference(nfsi->delegation);  // 取出文件中的delegation.
        if (deleg_cur == NULL)
                goto no_delegation;

        spin_lock(&deleg_cur->lock);
        if (nfsi->delegation != deleg_cur ||
            (deleg_cur->type & fmode) != fmode) // 比较用户的访问权限和delegation中的访问权限是否一致
                goto no_delegation_unlock;

        if (delegation == NULL)
                delegation = &deleg_cur->stateid;
        else if (!nfs4_stateid_match(&deleg_cur->stateid, delegation))
                goto no_delegation_unlock;

        nfs_mark_delegation_referenced(deleg_cur);      // 设置标志位NFS_DELEGATION_REFERENCED
        // 这个函数在更新nfs4_state结构中的open_stateid和stateid.
        __update_open_stateid(state, open_stateid, &deleg_cur->stateid, fmode);
        ret = 1;
no_delegation_unlock:
        spin_unlock(&deleg_cur->lock);
no_delegation:
        rcu_read_unlock();
        if (!ret && open_stateid != NULL) {
                __update_open_stateid(state, open_stateid, NULL, fmode);
                ret = 1;
        }
        return ret;
}
    目前这种情况下会执行到__update_open_stateid(state, open_stateid, NULL, fmode);

static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
{
        write_seqlock(&state->seqlock);
        if (deleg_stateid != NULL) {
                // 设置nfs4_state结构中的stateid.
                nfs4_stateid_copy(&state->stateid, deleg_stateid);
                set_bit(NFS_DELEGATED_STATE, &state->flags);
        }
        // 现在open_stateid==NULL,因此没有设置open_stateid.
        if (open_stateid != NULL)
                nfs_set_open_stateid_locked(state, open_stateid, fmode);
        write_sequnlock(&state->seqlock);
        spin_lock(&state->owner->so_lock);
        // 这个函数在设置nfs4_state结构中各种访问权限的计数.
        update_open_stateflags(state, fmode);
        spin_unlock(&state->owner->so_lock);
}
可以看到,这种情况下没有设置open_stateid,只设置了stateid,而这个stateid就是从delegation中获取的,并且设置了标志位NFS_DELEGATED_STATE,说明这是一个包含delegation的nfs4_state结构,以后user2就使用delegation的stateid从服务器中读取数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值