Skip to content

Commit 8df4bcd

Browse files
namjaejeongregkh
authored andcommitted
ksmbd: add support for durable handles v1/v2
[ Upstream commit c8efcc7 ] 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 <[email protected]> Signed-off-by: Steve French <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 665e858 commit 8df4bcd

File tree

9 files changed

+506
-21
lines changed

9 files changed

+506
-21
lines changed

fs/smb/server/ksmbd_netlink.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ struct ksmbd_heartbeat {
7575
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1)
7676
#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2)
7777
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3)
78+
#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE BIT(4)
7879

7980
/*
8081
* IPC request for ksmbd server startup

fs/smb/server/mgmt/user_session.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ void destroy_previous_session(struct ksmbd_conn *conn,
324324
memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
325325
goto out;
326326

327+
ksmbd_destroy_file_table(&prev_sess->file_table);
327328
prev_sess->state = SMB2_SESSION_EXPIRED;
328329
out:
329330
up_write(&conn->session_lock);

fs/smb/server/oplock.c

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
159159
opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info,
160160
op_entry);
161161
if (opinfo) {
162-
if (!atomic_inc_not_zero(&opinfo->refcount))
162+
if (opinfo->conn == NULL ||
163+
!atomic_inc_not_zero(&opinfo->refcount))
163164
opinfo = NULL;
164165
else {
165166
atomic_inc(&opinfo->conn->r_count);
@@ -527,7 +528,7 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
527528
*/
528529
read_lock(&ci->m_lock);
529530
list_for_each_entry(opinfo, &ci->m_op_list, op_entry) {
530-
if (!opinfo->is_lease)
531+
if (!opinfo->is_lease || !opinfo->conn)
531532
continue;
532533
read_unlock(&ci->m_lock);
533534
lease = opinfo->o_lease;
@@ -651,7 +652,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
651652
struct smb2_hdr *rsp_hdr;
652653
struct ksmbd_file *fp;
653654

654-
fp = ksmbd_lookup_durable_fd(br_info->fid);
655+
fp = ksmbd_lookup_global_fd(br_info->fid);
655656
if (!fp)
656657
goto out;
657658

@@ -1115,7 +1116,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
11151116

11161117
read_lock(&p_ci->m_lock);
11171118
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
1118-
if (!opinfo->is_lease)
1119+
if (opinfo->conn == NULL || !opinfo->is_lease)
11191120
continue;
11201121

11211122
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE &&
@@ -1160,7 +1161,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
11601161

11611162
read_lock(&p_ci->m_lock);
11621163
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
1163-
if (!opinfo->is_lease)
1164+
if (opinfo->conn == NULL || !opinfo->is_lease)
11641165
continue;
11651166

11661167
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) {
@@ -1372,6 +1373,9 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
13721373

13731374
rcu_read_lock();
13741375
list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) {
1376+
if (brk_op->conn == NULL)
1377+
continue;
1378+
13751379
if (!atomic_inc_not_zero(&brk_op->refcount))
13761380
continue;
13771381

@@ -1508,11 +1512,10 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
15081512
/**
15091513
* parse_lease_state() - parse lease context containted in file open request
15101514
* @open_req: buffer containing smb2 file open(create) request
1511-
* @is_dir: whether leasing file is directory
15121515
*
15131516
* Return: oplock state, -ENOENT if create lease context not found
15141517
*/
1515-
struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
1518+
struct lease_ctx_info *parse_lease_state(void *open_req)
15161519
{
15171520
struct create_context *cc;
15181521
struct smb2_create_req *req = (struct smb2_create_req *)open_req;
@@ -1530,12 +1533,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
15301533
struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;
15311534

15321535
memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
1533-
if (is_dir) {
1534-
lreq->req_state = lc->lcontext.LeaseState &
1535-
~SMB2_LEASE_WRITE_CACHING_LE;
1536-
lreq->is_dir = true;
1537-
} else
1538-
lreq->req_state = lc->lcontext.LeaseState;
1536+
lreq->req_state = lc->lcontext.LeaseState;
15391537
lreq->flags = lc->lcontext.LeaseFlags;
15401538
lreq->epoch = lc->lcontext.Epoch;
15411539
lreq->duration = lc->lcontext.LeaseDuration;
@@ -1659,6 +1657,8 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp)
16591657
buf->Name[3] = 'Q';
16601658

16611659
buf->Timeout = cpu_to_le32(fp->durable_timeout);
1660+
if (fp->is_persistent)
1661+
buf->Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
16621662
}
16631663

16641664
/**
@@ -1826,3 +1826,71 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
18261826
read_unlock(&lease_list_lock);
18271827
return ret_op;
18281828
}
1829+
1830+
int smb2_check_durable_oplock(struct ksmbd_conn *conn,
1831+
struct ksmbd_share_config *share,
1832+
struct ksmbd_file *fp,
1833+
struct lease_ctx_info *lctx,
1834+
char *name)
1835+
{
1836+
struct oplock_info *opinfo = opinfo_get(fp);
1837+
int ret = 0;
1838+
1839+
if (!opinfo)
1840+
return 0;
1841+
1842+
if (opinfo->is_lease == false) {
1843+
if (lctx) {
1844+
pr_err("create context include lease\n");
1845+
ret = -EBADF;
1846+
goto out;
1847+
}
1848+
1849+
if (opinfo->level != SMB2_OPLOCK_LEVEL_BATCH) {
1850+
pr_err("oplock level is not equal to SMB2_OPLOCK_LEVEL_BATCH\n");
1851+
ret = -EBADF;
1852+
}
1853+
1854+
goto out;
1855+
}
1856+
1857+
if (memcmp(conn->ClientGUID, fp->client_guid,
1858+
SMB2_CLIENT_GUID_SIZE)) {
1859+
ksmbd_debug(SMB, "Client guid of fp is not equal to the one of connction\n");
1860+
ret = -EBADF;
1861+
goto out;
1862+
}
1863+
1864+
if (!lctx) {
1865+
ksmbd_debug(SMB, "create context does not include lease\n");
1866+
ret = -EBADF;
1867+
goto out;
1868+
}
1869+
1870+
if (memcmp(opinfo->o_lease->lease_key, lctx->lease_key,
1871+
SMB2_LEASE_KEY_SIZE)) {
1872+
ksmbd_debug(SMB,
1873+
"lease key of fp does not match lease key in create context\n");
1874+
ret = -EBADF;
1875+
goto out;
1876+
}
1877+
1878+
if (!(opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE)) {
1879+
ksmbd_debug(SMB, "lease state does not contain SMB2_LEASE_HANDLE_CACHING\n");
1880+
ret = -EBADF;
1881+
goto out;
1882+
}
1883+
1884+
if (opinfo->o_lease->version != lctx->version) {
1885+
ksmbd_debug(SMB,
1886+
"lease version of fp does not match the one in create context\n");
1887+
ret = -EBADF;
1888+
goto out;
1889+
}
1890+
1891+
if (!ksmbd_inode_pending_delete(fp))
1892+
ret = ksmbd_validate_name_reconnect(share, fp, name);
1893+
out:
1894+
opinfo_put(opinfo);
1895+
return ret;
1896+
}

fs/smb/server/oplock.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ void opinfo_put(struct oplock_info *opinfo);
111111

112112
/* Lease related functions */
113113
void create_lease_buf(u8 *rbuf, struct lease *lease);
114-
struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir);
114+
struct lease_ctx_info *parse_lease_state(void *open_req);
115115
__u8 smb2_map_lease_to_oplock(__le32 lease_state);
116116
int lease_read_to_write(struct oplock_info *opinfo);
117117

@@ -130,4 +130,9 @@ void destroy_lease_table(struct ksmbd_conn *conn);
130130
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
131131
struct lease_ctx_info *lctx);
132132
void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp);
133+
int smb2_check_durable_oplock(struct ksmbd_conn *conn,
134+
struct ksmbd_share_config *share,
135+
struct ksmbd_file *fp,
136+
struct lease_ctx_info *lctx,
137+
char *name);
133138
#endif /* __KSMBD_OPLOCK_H */

fs/smb/server/smb2ops.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,9 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
261261

262262
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
263263
conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
264+
265+
if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
266+
conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
264267
}
265268

266269
/**
@@ -283,6 +286,9 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
283286
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
284287
conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
285288

289+
if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
290+
conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
291+
286292
INIT_LIST_HEAD(&conn->preauth_sess_table);
287293
return 0;
288294
}

0 commit comments

Comments
 (0)