mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	ksmbd: add support for durable handles v1/v2
Durable file handles allow reopening a file preserved on a short network outage and transparent client reconnection within a timeout. i.e. Durable handles aren't necessarily cleaned up when the opening process terminates. This patch add support for durable handle version 1 and 2. To prove durable handles work on ksmbd, I have tested this patch with the following smbtorture tests: smb2.durable-open.open-oplock smb2.durable-open.open-lease smb2.durable-open.reopen1 smb2.durable-open.reopen1a smb2.durable-open.reopen1a-lease smb2.durable-open.reopen2 smb2.durable-open.reopen2a smb2.durable-open.reopen2-lease smb2.durable-open.reopen2-lease-v2 smb2.durable-open.reopen3 smb2.durable-open.reopen4 smb2.durable-open.delete_on_close2 smb2.durable-open.file-position smb2.durable-open.lease smb2.durable-open.alloc-size smb2.durable-open.read-only smb2.durable-v2-open.create-blob smb2.durable-v2-open.open-oplock smb2.durable-v2-open.open-lease smb2.durable-v2-open.reopen1 smb2.durable-v2-open.reopen1a smb2.durable-v2-open.reopen1a-lease smb2.durable-v2-open.reopen2 smb2.durable-v2-open.reopen2b Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
		
							parent
							
								
									fa9415d402
								
							
						
					
					
						commit
						c8efcc7861
					
				
					 9 changed files with 506 additions and 21 deletions
				
			
		| 
						 | 
					@ -75,6 +75,7 @@ struct ksmbd_heartbeat {
 | 
				
			||||||
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION	BIT(1)
 | 
					#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION	BIT(1)
 | 
				
			||||||
#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL	BIT(2)
 | 
					#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL	BIT(2)
 | 
				
			||||||
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF	BIT(3)
 | 
					#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF	BIT(3)
 | 
				
			||||||
 | 
					#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE	BIT(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * IPC request for ksmbd server startup
 | 
					 * IPC request for ksmbd server startup
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -324,6 +324,7 @@ void destroy_previous_session(struct ksmbd_conn *conn,
 | 
				
			||||||
	    memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
 | 
						    memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ksmbd_destroy_file_table(&prev_sess->file_table);
 | 
				
			||||||
	prev_sess->state = SMB2_SESSION_EXPIRED;
 | 
						prev_sess->state = SMB2_SESSION_EXPIRED;
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	up_write(&conn->session_lock);
 | 
						up_write(&conn->session_lock);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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,
 | 
						opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info,
 | 
				
			||||||
					op_entry);
 | 
										op_entry);
 | 
				
			||||||
	if (opinfo) {
 | 
						if (opinfo) {
 | 
				
			||||||
		if (!atomic_inc_not_zero(&opinfo->refcount))
 | 
							if (opinfo->conn == NULL ||
 | 
				
			||||||
 | 
							    !atomic_inc_not_zero(&opinfo->refcount))
 | 
				
			||||||
			opinfo = NULL;
 | 
								opinfo = NULL;
 | 
				
			||||||
		else {
 | 
							else {
 | 
				
			||||||
			atomic_inc(&opinfo->conn->r_count);
 | 
								atomic_inc(&opinfo->conn->r_count);
 | 
				
			||||||
| 
						 | 
					@ -527,7 +528,7 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	read_lock(&ci->m_lock);
 | 
						read_lock(&ci->m_lock);
 | 
				
			||||||
	list_for_each_entry(opinfo, &ci->m_op_list, op_entry) {
 | 
						list_for_each_entry(opinfo, &ci->m_op_list, op_entry) {
 | 
				
			||||||
		if (!opinfo->is_lease)
 | 
							if (!opinfo->is_lease || !opinfo->conn)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		read_unlock(&ci->m_lock);
 | 
							read_unlock(&ci->m_lock);
 | 
				
			||||||
		lease = opinfo->o_lease;
 | 
							lease = opinfo->o_lease;
 | 
				
			||||||
| 
						 | 
					@ -641,7 +642,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
 | 
				
			||||||
	struct smb2_hdr *rsp_hdr;
 | 
						struct smb2_hdr *rsp_hdr;
 | 
				
			||||||
	struct ksmbd_file *fp;
 | 
						struct ksmbd_file *fp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fp = ksmbd_lookup_durable_fd(br_info->fid);
 | 
						fp = ksmbd_lookup_global_fd(br_info->fid);
 | 
				
			||||||
	if (!fp)
 | 
						if (!fp)
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1106,7 +1107,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	read_lock(&p_ci->m_lock);
 | 
						read_lock(&p_ci->m_lock);
 | 
				
			||||||
	list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
 | 
						list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
 | 
				
			||||||
		if (!opinfo->is_lease)
 | 
							if (opinfo->conn == NULL || !opinfo->is_lease)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE &&
 | 
							if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE &&
 | 
				
			||||||
| 
						 | 
					@ -1151,7 +1152,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	read_lock(&p_ci->m_lock);
 | 
						read_lock(&p_ci->m_lock);
 | 
				
			||||||
	list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
 | 
						list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
 | 
				
			||||||
		if (!opinfo->is_lease)
 | 
							if (opinfo->conn == NULL || !opinfo->is_lease)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) {
 | 
							if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) {
 | 
				
			||||||
| 
						 | 
					@ -1361,6 +1362,9 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rcu_read_lock();
 | 
						rcu_read_lock();
 | 
				
			||||||
	list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) {
 | 
						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))
 | 
							if (!atomic_inc_not_zero(&brk_op->refcount))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1496,11 +1500,10 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * parse_lease_state() - parse lease context containted in file open request
 | 
					 * parse_lease_state() - parse lease context containted in file open request
 | 
				
			||||||
 * @open_req:	buffer containing smb2 file open(create) 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
 | 
					 * 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 create_context *cc;
 | 
				
			||||||
	struct smb2_create_req *req = (struct smb2_create_req *)open_req;
 | 
						struct smb2_create_req *req = (struct smb2_create_req *)open_req;
 | 
				
			||||||
| 
						 | 
					@ -1518,12 +1521,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
 | 
				
			||||||
		struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;
 | 
							struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
 | 
							memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
 | 
				
			||||||
		if (is_dir) {
 | 
							lreq->req_state = lc->lcontext.LeaseState;
 | 
				
			||||||
			lreq->req_state = lc->lcontext.LeaseState &
 | 
					 | 
				
			||||||
				~SMB2_LEASE_WRITE_CACHING_LE;
 | 
					 | 
				
			||||||
			lreq->is_dir = true;
 | 
					 | 
				
			||||||
		} else
 | 
					 | 
				
			||||||
			lreq->req_state = lc->lcontext.LeaseState;
 | 
					 | 
				
			||||||
		lreq->flags = lc->lcontext.LeaseFlags;
 | 
							lreq->flags = lc->lcontext.LeaseFlags;
 | 
				
			||||||
		lreq->epoch = lc->lcontext.Epoch;
 | 
							lreq->epoch = lc->lcontext.Epoch;
 | 
				
			||||||
		lreq->duration = lc->lcontext.LeaseDuration;
 | 
							lreq->duration = lc->lcontext.LeaseDuration;
 | 
				
			||||||
| 
						 | 
					@ -1646,6 +1644,8 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp)
 | 
				
			||||||
	buf->Name[3] = 'Q';
 | 
						buf->Name[3] = 'Q';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	buf->Timeout = cpu_to_le32(fp->durable_timeout);
 | 
						buf->Timeout = cpu_to_le32(fp->durable_timeout);
 | 
				
			||||||
 | 
						if (fp->is_persistent)
 | 
				
			||||||
 | 
							buf->Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -1813,3 +1813,71 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
 | 
				
			||||||
	read_unlock(&lease_list_lock);
 | 
						read_unlock(&lease_list_lock);
 | 
				
			||||||
	return ret_op;
 | 
						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 connction\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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -111,7 +111,7 @@ void opinfo_put(struct oplock_info *opinfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Lease related functions */
 | 
					/* Lease related functions */
 | 
				
			||||||
void create_lease_buf(u8 *rbuf, struct lease *lease);
 | 
					void create_lease_buf(u8 *rbuf, struct lease *lease);
 | 
				
			||||||
struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir);
 | 
					struct lease_ctx_info *parse_lease_state(void *open_req);
 | 
				
			||||||
__u8 smb2_map_lease_to_oplock(__le32 lease_state);
 | 
					__u8 smb2_map_lease_to_oplock(__le32 lease_state);
 | 
				
			||||||
int lease_read_to_write(struct oplock_info *opinfo);
 | 
					int lease_read_to_write(struct oplock_info *opinfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -130,4 +130,9 @@ void destroy_lease_table(struct ksmbd_conn *conn);
 | 
				
			||||||
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
 | 
					void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
 | 
				
			||||||
				      struct lease_ctx_info *lctx);
 | 
									      struct lease_ctx_info *lctx);
 | 
				
			||||||
void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp);
 | 
					void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp);
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
#endif /* __KSMBD_OPLOCK_H */
 | 
					#endif /* __KSMBD_OPLOCK_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -256,6 +256,9 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
 | 
						if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
 | 
				
			||||||
		conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
 | 
							conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
 | 
				
			||||||
 | 
							conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -283,6 +286,9 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
 | 
				
			||||||
	if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
 | 
						if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
 | 
				
			||||||
		conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
 | 
							conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
 | 
				
			||||||
 | 
							conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	INIT_LIST_HEAD(&conn->preauth_sess_table);
 | 
						INIT_LIST_HEAD(&conn->preauth_sess_table);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2618,6 +2618,165 @@ static void ksmbd_acls_fattr(struct smb_fattr *fattr,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum {
 | 
				
			||||||
 | 
						DURABLE_RECONN_V2 = 1,
 | 
				
			||||||
 | 
						DURABLE_RECONN,
 | 
				
			||||||
 | 
						DURABLE_REQ_V2,
 | 
				
			||||||
 | 
						DURABLE_REQ,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct durable_info {
 | 
				
			||||||
 | 
						struct ksmbd_file *fp;
 | 
				
			||||||
 | 
						unsigned short int type;
 | 
				
			||||||
 | 
						bool persistent;
 | 
				
			||||||
 | 
						bool reconnected;
 | 
				
			||||||
 | 
						unsigned int timeout;
 | 
				
			||||||
 | 
						char *CreateGuid;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int parse_durable_handle_context(struct ksmbd_work *work,
 | 
				
			||||||
 | 
										struct smb2_create_req *req,
 | 
				
			||||||
 | 
										struct lease_ctx_info *lc,
 | 
				
			||||||
 | 
										struct durable_info *dh_info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ksmbd_conn *conn = work->conn;
 | 
				
			||||||
 | 
						struct create_context *context;
 | 
				
			||||||
 | 
						int dh_idx, err = 0;
 | 
				
			||||||
 | 
						u64 persistent_id = 0;
 | 
				
			||||||
 | 
						int req_op_level;
 | 
				
			||||||
 | 
						static const char * const durable_arr[] = {"DH2C", "DHnC", "DH2Q", "DHnQ"};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req_op_level = req->RequestedOplockLevel;
 | 
				
			||||||
 | 
						for (dh_idx = DURABLE_RECONN_V2; dh_idx <= ARRAY_SIZE(durable_arr);
 | 
				
			||||||
 | 
						     dh_idx++) {
 | 
				
			||||||
 | 
							context = smb2_find_context_vals(req, durable_arr[dh_idx - 1], 4);
 | 
				
			||||||
 | 
							if (IS_ERR(context)) {
 | 
				
			||||||
 | 
								err = PTR_ERR(context);
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (!context)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (dh_idx) {
 | 
				
			||||||
 | 
							case DURABLE_RECONN_V2:
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								struct create_durable_reconn_v2_req *recon_v2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (dh_info->type == DURABLE_RECONN ||
 | 
				
			||||||
 | 
								    dh_info->type == DURABLE_REQ_V2) {
 | 
				
			||||||
 | 
									err = -EINVAL;
 | 
				
			||||||
 | 
									goto out;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								recon_v2 = (struct create_durable_reconn_v2_req *)context;
 | 
				
			||||||
 | 
								persistent_id = recon_v2->Fid.PersistentFileId;
 | 
				
			||||||
 | 
								dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
 | 
				
			||||||
 | 
								if (!dh_info->fp) {
 | 
				
			||||||
 | 
									ksmbd_debug(SMB, "Failed to get durable handle state\n");
 | 
				
			||||||
 | 
									err = -EBADF;
 | 
				
			||||||
 | 
									goto out;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (memcmp(dh_info->fp->create_guid, recon_v2->CreateGuid,
 | 
				
			||||||
 | 
									   SMB2_CREATE_GUID_SIZE)) {
 | 
				
			||||||
 | 
									err = -EBADF;
 | 
				
			||||||
 | 
									ksmbd_put_durable_fd(dh_info->fp);
 | 
				
			||||||
 | 
									goto out;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								dh_info->type = dh_idx;
 | 
				
			||||||
 | 
								dh_info->reconnected = true;
 | 
				
			||||||
 | 
								ksmbd_debug(SMB,
 | 
				
			||||||
 | 
									"reconnect v2 Persistent-id from reconnect = %llu\n",
 | 
				
			||||||
 | 
										persistent_id);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							case DURABLE_RECONN:
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								struct create_durable_reconn_req *recon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (dh_info->type == DURABLE_RECONN_V2 ||
 | 
				
			||||||
 | 
								    dh_info->type == DURABLE_REQ_V2) {
 | 
				
			||||||
 | 
									err = -EINVAL;
 | 
				
			||||||
 | 
									goto out;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								recon = (struct create_durable_reconn_req *)context;
 | 
				
			||||||
 | 
								persistent_id = recon->Data.Fid.PersistentFileId;
 | 
				
			||||||
 | 
								dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
 | 
				
			||||||
 | 
								if (!dh_info->fp) {
 | 
				
			||||||
 | 
									ksmbd_debug(SMB, "Failed to get durable handle state\n");
 | 
				
			||||||
 | 
									err = -EBADF;
 | 
				
			||||||
 | 
									goto out;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								dh_info->type = dh_idx;
 | 
				
			||||||
 | 
								dh_info->reconnected = true;
 | 
				
			||||||
 | 
								ksmbd_debug(SMB, "reconnect Persistent-id from reconnect = %llu\n",
 | 
				
			||||||
 | 
									    persistent_id);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							case DURABLE_REQ_V2:
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								struct create_durable_req_v2 *durable_v2_blob;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (dh_info->type == DURABLE_RECONN ||
 | 
				
			||||||
 | 
								    dh_info->type == DURABLE_RECONN_V2) {
 | 
				
			||||||
 | 
									err = -EINVAL;
 | 
				
			||||||
 | 
									goto out;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								durable_v2_blob =
 | 
				
			||||||
 | 
									(struct create_durable_req_v2 *)context;
 | 
				
			||||||
 | 
								ksmbd_debug(SMB, "Request for durable v2 open\n");
 | 
				
			||||||
 | 
								dh_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->CreateGuid);
 | 
				
			||||||
 | 
								if (dh_info->fp) {
 | 
				
			||||||
 | 
									if (!memcmp(conn->ClientGUID, dh_info->fp->client_guid,
 | 
				
			||||||
 | 
										    SMB2_CLIENT_GUID_SIZE)) {
 | 
				
			||||||
 | 
										if (!(req->hdr.Flags & SMB2_FLAGS_REPLAY_OPERATION)) {
 | 
				
			||||||
 | 
											err = -ENOEXEC;
 | 
				
			||||||
 | 
											goto out;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										dh_info->fp->conn = conn;
 | 
				
			||||||
 | 
										dh_info->reconnected = true;
 | 
				
			||||||
 | 
										goto out;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) ||
 | 
				
			||||||
 | 
								     req_op_level == SMB2_OPLOCK_LEVEL_BATCH)) {
 | 
				
			||||||
 | 
									dh_info->CreateGuid =
 | 
				
			||||||
 | 
										durable_v2_blob->CreateGuid;
 | 
				
			||||||
 | 
									dh_info->persistent =
 | 
				
			||||||
 | 
										le32_to_cpu(durable_v2_blob->Flags);
 | 
				
			||||||
 | 
									dh_info->timeout =
 | 
				
			||||||
 | 
										le32_to_cpu(durable_v2_blob->Timeout);
 | 
				
			||||||
 | 
									dh_info->type = dh_idx;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							case DURABLE_REQ:
 | 
				
			||||||
 | 
								if (dh_info->type == DURABLE_RECONN)
 | 
				
			||||||
 | 
									goto out;
 | 
				
			||||||
 | 
								if (dh_info->type == DURABLE_RECONN_V2 ||
 | 
				
			||||||
 | 
								    dh_info->type == DURABLE_REQ_V2) {
 | 
				
			||||||
 | 
									err = -EINVAL;
 | 
				
			||||||
 | 
									goto out;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) ||
 | 
				
			||||||
 | 
								     req_op_level == SMB2_OPLOCK_LEVEL_BATCH)) {
 | 
				
			||||||
 | 
									ksmbd_debug(SMB, "Request for durable open\n");
 | 
				
			||||||
 | 
									dh_info->type = dh_idx;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * smb2_open() - handler for smb file open request
 | 
					 * smb2_open() - handler for smb file open request
 | 
				
			||||||
 * @work:	smb work containing request buffer
 | 
					 * @work:	smb work containing request buffer
 | 
				
			||||||
| 
						 | 
					@ -2641,6 +2800,7 @@ int smb2_open(struct ksmbd_work *work)
 | 
				
			||||||
	struct lease_ctx_info *lc = NULL;
 | 
						struct lease_ctx_info *lc = NULL;
 | 
				
			||||||
	struct create_ea_buf_req *ea_buf = NULL;
 | 
						struct create_ea_buf_req *ea_buf = NULL;
 | 
				
			||||||
	struct oplock_info *opinfo;
 | 
						struct oplock_info *opinfo;
 | 
				
			||||||
 | 
						struct durable_info dh_info = {0};
 | 
				
			||||||
	__le32 *next_ptr = NULL;
 | 
						__le32 *next_ptr = NULL;
 | 
				
			||||||
	int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0;
 | 
						int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0;
 | 
				
			||||||
	int rc = 0;
 | 
						int rc = 0;
 | 
				
			||||||
| 
						 | 
					@ -2721,6 +2881,49 @@ int smb2_open(struct ksmbd_work *work)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req_op_level = req->RequestedOplockLevel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE &&
 | 
				
			||||||
 | 
						    req->CreateContextsOffset) {
 | 
				
			||||||
 | 
							lc = parse_lease_state(req);
 | 
				
			||||||
 | 
							rc = parse_durable_handle_context(work, req, lc, &dh_info);
 | 
				
			||||||
 | 
							if (rc) {
 | 
				
			||||||
 | 
								ksmbd_debug(SMB, "error parsing durable handle context\n");
 | 
				
			||||||
 | 
								goto err_out2;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (dh_info.reconnected == true) {
 | 
				
			||||||
 | 
								rc = smb2_check_durable_oplock(conn, share, dh_info.fp, lc, name);
 | 
				
			||||||
 | 
								if (rc) {
 | 
				
			||||||
 | 
									ksmbd_put_durable_fd(dh_info.fp);
 | 
				
			||||||
 | 
									goto err_out2;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								rc = ksmbd_reopen_durable_fd(work, dh_info.fp);
 | 
				
			||||||
 | 
								if (rc) {
 | 
				
			||||||
 | 
									ksmbd_put_durable_fd(dh_info.fp);
 | 
				
			||||||
 | 
									goto err_out2;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (ksmbd_override_fsids(work)) {
 | 
				
			||||||
 | 
									rc = -ENOMEM;
 | 
				
			||||||
 | 
									ksmbd_put_durable_fd(dh_info.fp);
 | 
				
			||||||
 | 
									goto err_out2;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fp = dh_info.fp;
 | 
				
			||||||
 | 
								file_info = FILE_OPENED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								rc = ksmbd_vfs_getattr(&fp->filp->f_path, &stat);
 | 
				
			||||||
 | 
								if (rc)
 | 
				
			||||||
 | 
									goto err_out2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ksmbd_put_durable_fd(fp);
 | 
				
			||||||
 | 
								goto reconnected_fp;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE)
 | 
				
			||||||
 | 
							lc = parse_lease_state(req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) {
 | 
						if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) {
 | 
				
			||||||
		pr_err("Invalid impersonationlevel : 0x%x\n",
 | 
							pr_err("Invalid impersonationlevel : 0x%x\n",
 | 
				
			||||||
		       le32_to_cpu(req->ImpersonationLevel));
 | 
							       le32_to_cpu(req->ImpersonationLevel));
 | 
				
			||||||
| 
						 | 
					@ -3183,10 +3386,6 @@ int smb2_open(struct ksmbd_work *work)
 | 
				
			||||||
		need_truncate = 1;
 | 
							need_truncate = 1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req_op_level = req->RequestedOplockLevel;
 | 
					 | 
				
			||||||
	if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE)
 | 
					 | 
				
			||||||
		lc = parse_lease_state(req, S_ISDIR(file_inode(filp)->i_mode));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp);
 | 
						share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp);
 | 
				
			||||||
	if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) ||
 | 
						if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) ||
 | 
				
			||||||
	    (req_op_level == SMB2_OPLOCK_LEVEL_LEASE &&
 | 
						    (req_op_level == SMB2_OPLOCK_LEVEL_LEASE &&
 | 
				
			||||||
| 
						 | 
					@ -3197,6 +3396,11 @@ int smb2_open(struct ksmbd_work *work)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) {
 | 
							if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) {
 | 
				
			||||||
 | 
								if (S_ISDIR(file_inode(filp)->i_mode)) {
 | 
				
			||||||
 | 
									lc->req_state &= ~SMB2_LEASE_WRITE_CACHING_LE;
 | 
				
			||||||
 | 
									lc->is_dir = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/*
 | 
								/*
 | 
				
			||||||
			 * Compare parent lease using parent key. If there is no
 | 
								 * Compare parent lease using parent key. If there is no
 | 
				
			||||||
			 * a lease that has same parent key, Send lease break
 | 
								 * a lease that has same parent key, Send lease break
 | 
				
			||||||
| 
						 | 
					@ -3293,6 +3497,24 @@ int smb2_open(struct ksmbd_work *work)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
 | 
						memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dh_info.type == DURABLE_REQ_V2 || dh_info.type == DURABLE_REQ) {
 | 
				
			||||||
 | 
							if (dh_info.type == DURABLE_REQ_V2 && dh_info.persistent)
 | 
				
			||||||
 | 
								fp->is_persistent = true;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								fp->is_durable = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (dh_info.type == DURABLE_REQ_V2) {
 | 
				
			||||||
 | 
								memcpy(fp->create_guid, dh_info.CreateGuid,
 | 
				
			||||||
 | 
										SMB2_CREATE_GUID_SIZE);
 | 
				
			||||||
 | 
								if (dh_info.timeout)
 | 
				
			||||||
 | 
									fp->durable_timeout = min(dh_info.timeout,
 | 
				
			||||||
 | 
											300000);
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									fp->durable_timeout = 60;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					reconnected_fp:
 | 
				
			||||||
	rsp->StructureSize = cpu_to_le16(89);
 | 
						rsp->StructureSize = cpu_to_le16(89);
 | 
				
			||||||
	rcu_read_lock();
 | 
						rcu_read_lock();
 | 
				
			||||||
	opinfo = rcu_dereference(fp->f_opinfo);
 | 
						opinfo = rcu_dereference(fp->f_opinfo);
 | 
				
			||||||
| 
						 | 
					@ -3379,6 +3601,33 @@ int smb2_open(struct ksmbd_work *work)
 | 
				
			||||||
		next_off = conn->vals->create_disk_id_size;
 | 
							next_off = conn->vals->create_disk_id_size;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dh_info.type == DURABLE_REQ || dh_info.type == DURABLE_REQ_V2) {
 | 
				
			||||||
 | 
							struct create_context *durable_ccontext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							durable_ccontext = (struct create_context *)(rsp->Buffer +
 | 
				
			||||||
 | 
									le32_to_cpu(rsp->CreateContextsLength));
 | 
				
			||||||
 | 
							contxt_cnt++;
 | 
				
			||||||
 | 
							if (dh_info.type == DURABLE_REQ) {
 | 
				
			||||||
 | 
								create_durable_rsp_buf(rsp->Buffer +
 | 
				
			||||||
 | 
										le32_to_cpu(rsp->CreateContextsLength));
 | 
				
			||||||
 | 
								le32_add_cpu(&rsp->CreateContextsLength,
 | 
				
			||||||
 | 
										conn->vals->create_durable_size);
 | 
				
			||||||
 | 
								iov_len += conn->vals->create_durable_size;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								create_durable_v2_rsp_buf(rsp->Buffer +
 | 
				
			||||||
 | 
										le32_to_cpu(rsp->CreateContextsLength),
 | 
				
			||||||
 | 
										fp);
 | 
				
			||||||
 | 
								le32_add_cpu(&rsp->CreateContextsLength,
 | 
				
			||||||
 | 
										conn->vals->create_durable_v2_size);
 | 
				
			||||||
 | 
								iov_len += conn->vals->create_durable_v2_size;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (next_ptr)
 | 
				
			||||||
 | 
								*next_ptr = cpu_to_le32(next_off);
 | 
				
			||||||
 | 
							next_ptr = &durable_ccontext->Next;
 | 
				
			||||||
 | 
							next_off = conn->vals->create_durable_size;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (posix_ctxt) {
 | 
						if (posix_ctxt) {
 | 
				
			||||||
		contxt_cnt++;
 | 
							contxt_cnt++;
 | 
				
			||||||
		create_posix_rsp_buf(rsp->Buffer +
 | 
							create_posix_rsp_buf(rsp->Buffer +
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,6 +72,18 @@ struct create_durable_req_v2 {
 | 
				
			||||||
	__u8 CreateGuid[16];
 | 
						__u8 CreateGuid[16];
 | 
				
			||||||
} __packed;
 | 
					} __packed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct create_durable_reconn_req {
 | 
				
			||||||
 | 
						struct create_context ccontext;
 | 
				
			||||||
 | 
						__u8   Name[8];
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							__u8  Reserved[16];
 | 
				
			||||||
 | 
							struct {
 | 
				
			||||||
 | 
								__u64 PersistentFileId;
 | 
				
			||||||
 | 
								__u64 VolatileFileId;
 | 
				
			||||||
 | 
							} Fid;
 | 
				
			||||||
 | 
						} Data;
 | 
				
			||||||
 | 
					} __packed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct create_durable_reconn_v2_req {
 | 
					struct create_durable_reconn_v2_req {
 | 
				
			||||||
	struct create_context ccontext;
 | 
						struct create_context ccontext;
 | 
				
			||||||
	__u8   Name[8];
 | 
						__u8   Name[8];
 | 
				
			||||||
| 
						 | 
					@ -98,6 +110,9 @@ struct create_durable_rsp {
 | 
				
			||||||
	} Data;
 | 
						} Data;
 | 
				
			||||||
} __packed;
 | 
					} __packed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* See MS-SMB2 2.2.13.2.11 */
 | 
				
			||||||
 | 
					/* Flags */
 | 
				
			||||||
 | 
					#define SMB2_DHANDLE_FLAG_PERSISTENT	0x00000002
 | 
				
			||||||
struct create_durable_v2_rsp {
 | 
					struct create_durable_v2_rsp {
 | 
				
			||||||
	struct create_context ccontext;
 | 
						struct create_context ccontext;
 | 
				
			||||||
	__u8   Name[8];
 | 
						__u8   Name[8];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -305,7 +305,8 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fd_limit_close();
 | 
						fd_limit_close();
 | 
				
			||||||
	__ksmbd_remove_durable_fd(fp);
 | 
						__ksmbd_remove_durable_fd(fp);
 | 
				
			||||||
	__ksmbd_remove_fd(ft, fp);
 | 
						if (ft)
 | 
				
			||||||
 | 
							__ksmbd_remove_fd(ft, fp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	close_id_del_oplock(fp);
 | 
						close_id_del_oplock(fp);
 | 
				
			||||||
	filp = fp->filp;
 | 
						filp = fp->filp;
 | 
				
			||||||
| 
						 | 
					@ -465,11 +466,32 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
 | 
				
			||||||
	return fp;
 | 
						return fp;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id)
 | 
					struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return __ksmbd_lookup_fd(&global_ft, id);
 | 
						return __ksmbd_lookup_fd(&global_ft, id);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ksmbd_file *fp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fp = __ksmbd_lookup_fd(&global_ft, id);
 | 
				
			||||||
 | 
						if (fp && fp->conn) {
 | 
				
			||||||
 | 
							ksmbd_put_durable_fd(fp);
 | 
				
			||||||
 | 
							fp = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return fp;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ksmbd_put_durable_fd(struct ksmbd_file *fp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!atomic_dec_and_test(&fp->refcount))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						__ksmbd_close_fd(NULL, fp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid)
 | 
					struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ksmbd_file	*fp = NULL;
 | 
						struct ksmbd_file	*fp = NULL;
 | 
				
			||||||
| 
						 | 
					@ -639,6 +661,32 @@ __close_file_table_ids(struct ksmbd_file_table *ft,
 | 
				
			||||||
	return num;
 | 
						return num;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool is_reconnectable(struct ksmbd_file *fp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct oplock_info *opinfo = opinfo_get(fp);
 | 
				
			||||||
 | 
						bool reconn = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!opinfo)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (opinfo->op_state != OPLOCK_STATE_NONE) {
 | 
				
			||||||
 | 
							opinfo_put(opinfo);
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fp->is_resilient || fp->is_persistent)
 | 
				
			||||||
 | 
							reconn = true;
 | 
				
			||||||
 | 
						else if (fp->is_durable && opinfo->is_lease &&
 | 
				
			||||||
 | 
							 opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE)
 | 
				
			||||||
 | 
							reconn = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						else if (fp->is_durable && opinfo->level == SMB2_OPLOCK_LEVEL_BATCH)
 | 
				
			||||||
 | 
							reconn = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opinfo_put(opinfo);
 | 
				
			||||||
 | 
						return reconn;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon,
 | 
					static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon,
 | 
				
			||||||
			       struct ksmbd_file *fp)
 | 
								       struct ksmbd_file *fp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -648,7 +696,28 @@ static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon,
 | 
				
			||||||
static bool session_fd_check(struct ksmbd_tree_connect *tcon,
 | 
					static bool session_fd_check(struct ksmbd_tree_connect *tcon,
 | 
				
			||||||
			     struct ksmbd_file *fp)
 | 
								     struct ksmbd_file *fp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return false;
 | 
						struct ksmbd_inode *ci;
 | 
				
			||||||
 | 
						struct oplock_info *op;
 | 
				
			||||||
 | 
						struct ksmbd_conn *conn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!is_reconnectable(fp))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						conn = fp->conn;
 | 
				
			||||||
 | 
						ci = fp->f_ci;
 | 
				
			||||||
 | 
						write_lock(&ci->m_lock);
 | 
				
			||||||
 | 
						list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
 | 
				
			||||||
 | 
							if (op->conn != conn)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							op->conn = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						write_unlock(&ci->m_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fp->conn = NULL;
 | 
				
			||||||
 | 
						fp->tcon = NULL;
 | 
				
			||||||
 | 
						fp->volatile_id = KSMBD_NO_FID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ksmbd_close_tree_conn_fds(struct ksmbd_work *work)
 | 
					void ksmbd_close_tree_conn_fds(struct ksmbd_work *work)
 | 
				
			||||||
| 
						 | 
					@ -687,6 +756,68 @@ void ksmbd_free_global_file_table(void)
 | 
				
			||||||
	ksmbd_destroy_file_table(&global_ft);
 | 
						ksmbd_destroy_file_table(&global_ft);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share,
 | 
				
			||||||
 | 
									  struct ksmbd_file *fp, char *name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char *pathname, *ab_pathname;
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pathname = kmalloc(PATH_MAX, GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!pathname)
 | 
				
			||||||
 | 
							return -EACCES;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ab_pathname = d_path(&fp->filp->f_path, pathname, PATH_MAX);
 | 
				
			||||||
 | 
						if (IS_ERR(ab_pathname)) {
 | 
				
			||||||
 | 
							kfree(pathname);
 | 
				
			||||||
 | 
							return -EACCES;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (name && strcmp(&ab_pathname[share->path_sz + 1], name)) {
 | 
				
			||||||
 | 
							ksmbd_debug(SMB, "invalid name reconnect %s\n", name);
 | 
				
			||||||
 | 
							ret = -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kfree(pathname);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ksmbd_inode *ci;
 | 
				
			||||||
 | 
						struct oplock_info *op;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!fp->is_durable || fp->conn || fp->tcon) {
 | 
				
			||||||
 | 
							pr_err("Invalid durable fd [%p:%p]\n", fp->conn, fp->tcon);
 | 
				
			||||||
 | 
							return -EBADF;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (has_file_id(fp->volatile_id)) {
 | 
				
			||||||
 | 
							pr_err("Still in use durable fd: %llu\n", fp->volatile_id);
 | 
				
			||||||
 | 
							return -EBADF;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fp->conn = work->conn;
 | 
				
			||||||
 | 
						fp->tcon = work->tcon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ci = fp->f_ci;
 | 
				
			||||||
 | 
						write_lock(&ci->m_lock);
 | 
				
			||||||
 | 
						list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
 | 
				
			||||||
 | 
							if (op->conn)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							op->conn = fp->conn;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						write_unlock(&ci->m_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						__open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID);
 | 
				
			||||||
 | 
						if (!has_file_id(fp->volatile_id)) {
 | 
				
			||||||
 | 
							fp->conn = NULL;
 | 
				
			||||||
 | 
							fp->tcon = NULL;
 | 
				
			||||||
 | 
							return -EBADF;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int ksmbd_init_file_table(struct ksmbd_file_table *ft)
 | 
					int ksmbd_init_file_table(struct ksmbd_file_table *ft)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL);
 | 
						ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@
 | 
				
			||||||
#include <linux/workqueue.h>
 | 
					#include <linux/workqueue.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "vfs.h"
 | 
					#include "vfs.h"
 | 
				
			||||||
 | 
					#include "mgmt/share_config.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Windows style file permissions for extended response */
 | 
					/* Windows style file permissions for extended response */
 | 
				
			||||||
#define	FILE_GENERIC_ALL	0x1F01FF
 | 
					#define	FILE_GENERIC_ALL	0x1F01FF
 | 
				
			||||||
| 
						 | 
					@ -106,6 +107,9 @@ struct ksmbd_file {
 | 
				
			||||||
	int				dot_dotdot[2];
 | 
						int				dot_dotdot[2];
 | 
				
			||||||
	unsigned int			f_state;
 | 
						unsigned int			f_state;
 | 
				
			||||||
	bool				reserve_lease_break;
 | 
						bool				reserve_lease_break;
 | 
				
			||||||
 | 
						bool				is_durable;
 | 
				
			||||||
 | 
						bool				is_persistent;
 | 
				
			||||||
 | 
						bool				is_resilient;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void set_ctx_actor(struct dir_context *ctx,
 | 
					static inline void set_ctx_actor(struct dir_context *ctx,
 | 
				
			||||||
| 
						 | 
					@ -141,7 +145,9 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
 | 
				
			||||||
void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp);
 | 
					void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp);
 | 
				
			||||||
struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d);
 | 
					struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d);
 | 
				
			||||||
void ksmbd_inode_put(struct ksmbd_inode *ci);
 | 
					void ksmbd_inode_put(struct ksmbd_inode *ci);
 | 
				
			||||||
 | 
					struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id);
 | 
				
			||||||
struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id);
 | 
					struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id);
 | 
				
			||||||
 | 
					void ksmbd_put_durable_fd(struct ksmbd_file *fp);
 | 
				
			||||||
struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid);
 | 
					struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid);
 | 
				
			||||||
struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry);
 | 
					struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry);
 | 
				
			||||||
unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp);
 | 
					unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp);
 | 
				
			||||||
| 
						 | 
					@ -173,6 +179,9 @@ void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp);
 | 
				
			||||||
void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp);
 | 
					void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp);
 | 
				
			||||||
void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
 | 
					void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
 | 
				
			||||||
				  int file_info);
 | 
									  int file_info);
 | 
				
			||||||
 | 
					int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp);
 | 
				
			||||||
 | 
					int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share,
 | 
				
			||||||
 | 
									  struct ksmbd_file *fp, char *name);
 | 
				
			||||||
int ksmbd_init_file_cache(void);
 | 
					int ksmbd_init_file_cache(void);
 | 
				
			||||||
void ksmbd_exit_file_cache(void);
 | 
					void ksmbd_exit_file_cache(void);
 | 
				
			||||||
#endif /* __VFS_CACHE_H__ */
 | 
					#endif /* __VFS_CACHE_H__ */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue