diff options
Diffstat (limited to 'fs/smb/server/oplock.c')
-rw-r--r-- | fs/smb/server/oplock.c | 126 |
1 files changed, 93 insertions, 33 deletions
diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 2292ca6ff0..a8f52c4ebb 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -159,7 +159,8 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info, op_entry); if (opinfo) { - if (!atomic_inc_not_zero(&opinfo->refcount)) + if (opinfo->conn == NULL || + !atomic_inc_not_zero(&opinfo->refcount)) opinfo = NULL; else { atomic_inc(&opinfo->conn->r_count); @@ -206,9 +207,9 @@ static void opinfo_add(struct oplock_info *opinfo) { struct ksmbd_inode *ci = opinfo->o_fp->f_ci; - write_lock(&ci->m_lock); + down_write(&ci->m_lock); list_add_rcu(&opinfo->op_entry, &ci->m_op_list); - write_unlock(&ci->m_lock); + up_write(&ci->m_lock); } static void opinfo_del(struct oplock_info *opinfo) @@ -220,9 +221,9 @@ static void opinfo_del(struct oplock_info *opinfo) lease_del_list(opinfo); write_unlock(&lease_list_lock); } - write_lock(&ci->m_lock); + down_write(&ci->m_lock); list_del_rcu(&opinfo->op_entry); - write_unlock(&ci->m_lock); + up_write(&ci->m_lock); } static unsigned long opinfo_count(struct ksmbd_file *fp) @@ -525,21 +526,18 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, * Compare lease key and client_guid to know request from same owner * of same client */ - read_lock(&ci->m_lock); + down_read(&ci->m_lock); list_for_each_entry(opinfo, &ci->m_op_list, op_entry) { - if (!opinfo->is_lease) + if (!opinfo->is_lease || !opinfo->conn) continue; - read_unlock(&ci->m_lock); lease = opinfo->o_lease; ret = compare_guid_key(opinfo, client_guid, lctx->lease_key); if (ret) { m_opinfo = opinfo; /* skip upgrading lease about breaking lease */ - if (atomic_read(&opinfo->breaking_cnt)) { - read_lock(&ci->m_lock); + if (atomic_read(&opinfo->breaking_cnt)) continue; - } /* upgrading lease */ if ((atomic_read(&ci->op_count) + @@ -569,9 +567,8 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, lease_none_upgrade(opinfo, lctx->req_state); } } - read_lock(&ci->m_lock); } - read_unlock(&ci->m_lock); + up_read(&ci->m_lock); return m_opinfo; } @@ -656,7 +653,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) struct smb2_hdr *rsp_hdr; struct ksmbd_file *fp; - fp = ksmbd_lookup_durable_fd(br_info->fid); + fp = ksmbd_lookup_global_fd(br_info->fid); if (!fp) goto out; @@ -1118,9 +1115,9 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, if (!p_ci) return; - read_lock(&p_ci->m_lock); + down_read(&p_ci->m_lock); list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) { - if (!opinfo->is_lease) + if (opinfo->conn == NULL || !opinfo->is_lease) continue; if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE && @@ -1136,13 +1133,11 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, continue; } - read_unlock(&p_ci->m_lock); oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE); opinfo_conn_put(opinfo); - read_lock(&p_ci->m_lock); } } - read_unlock(&p_ci->m_lock); + up_read(&p_ci->m_lock); ksmbd_inode_put(p_ci); } @@ -1156,16 +1151,16 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) opinfo = rcu_dereference(fp->f_opinfo); rcu_read_unlock(); - if (!opinfo->is_lease || opinfo->o_lease->version != 2) + if (!opinfo || !opinfo->is_lease || opinfo->o_lease->version != 2) return; p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent); if (!p_ci) return; - read_lock(&p_ci->m_lock); + down_read(&p_ci->m_lock); list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) { - if (!opinfo->is_lease) + if (opinfo->conn == NULL || !opinfo->is_lease) continue; if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) { @@ -1177,13 +1172,11 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) atomic_dec(&opinfo->conn->r_count); continue; } - read_unlock(&p_ci->m_lock); oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE); opinfo_conn_put(opinfo); - read_lock(&p_ci->m_lock); } } - read_unlock(&p_ci->m_lock); + up_read(&p_ci->m_lock); ksmbd_inode_put(p_ci); } @@ -1377,6 +1370,9 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, rcu_read_lock(); list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) { + if (brk_op->conn == NULL) + continue; + if (!atomic_inc_not_zero(&brk_op->refcount)) continue; @@ -1513,11 +1509,10 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) /** * parse_lease_state() - parse lease context containted in file open request * @open_req: buffer containing smb2 file open(create) request - * @is_dir: whether leasing file is directory * * Return: oplock state, -ENOENT if create lease context not found */ -struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir) +struct lease_ctx_info *parse_lease_state(void *open_req) { struct create_context *cc; struct smb2_create_req *req = (struct smb2_create_req *)open_req; @@ -1535,12 +1530,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir) struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); - if (is_dir) { - lreq->req_state = lc->lcontext.LeaseState & - ~SMB2_LEASE_WRITE_CACHING_LE; - lreq->is_dir = true; - } else - lreq->req_state = lc->lcontext.LeaseState; + lreq->req_state = lc->lcontext.LeaseState; lreq->flags = lc->lcontext.LeaseFlags; lreq->epoch = lc->lcontext.Epoch; lreq->duration = lc->lcontext.LeaseDuration; @@ -1664,6 +1654,8 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) buf->Name[3] = 'Q'; buf->Timeout = cpu_to_le32(fp->durable_timeout); + if (fp->is_persistent) + buf->Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); } /** @@ -1831,3 +1823,71 @@ out: read_unlock(&lease_list_lock); return ret_op; } + +int smb2_check_durable_oplock(struct ksmbd_conn *conn, + struct ksmbd_share_config *share, + struct ksmbd_file *fp, + struct lease_ctx_info *lctx, + char *name) +{ + struct oplock_info *opinfo = opinfo_get(fp); + int ret = 0; + + if (!opinfo) + return 0; + + if (opinfo->is_lease == false) { + if (lctx) { + pr_err("create context include lease\n"); + ret = -EBADF; + goto out; + } + + if (opinfo->level != SMB2_OPLOCK_LEVEL_BATCH) { + pr_err("oplock level is not equal to SMB2_OPLOCK_LEVEL_BATCH\n"); + ret = -EBADF; + } + + goto out; + } + + if (memcmp(conn->ClientGUID, fp->client_guid, + SMB2_CLIENT_GUID_SIZE)) { + ksmbd_debug(SMB, "Client guid of fp is not equal to the one of connection\n"); + ret = -EBADF; + goto out; + } + + if (!lctx) { + ksmbd_debug(SMB, "create context does not include lease\n"); + ret = -EBADF; + goto out; + } + + if (memcmp(opinfo->o_lease->lease_key, lctx->lease_key, + SMB2_LEASE_KEY_SIZE)) { + ksmbd_debug(SMB, + "lease key of fp does not match lease key in create context\n"); + ret = -EBADF; + goto out; + } + + if (!(opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE)) { + ksmbd_debug(SMB, "lease state does not contain SMB2_LEASE_HANDLE_CACHING\n"); + ret = -EBADF; + goto out; + } + + if (opinfo->o_lease->version != lctx->version) { + ksmbd_debug(SMB, + "lease version of fp does not match the one in create context\n"); + ret = -EBADF; + goto out; + } + + if (!ksmbd_inode_pending_delete(fp)) + ret = ksmbd_validate_name_reconnect(share, fp, name); +out: + opinfo_put(opinfo); + return ret; +} |