Commit 8b8cd4be authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '6.7-rc5-ksmbd-server-fixes' of git://git.samba.org/ksmbd

Pull smb server fixes from Steve French:

 - Memory leak fix (in lock error path)

 - Two fixes for create with allocation size

 - FIx for potential UAF in lease break error path

 - Five directory lease (caching) fixes found during additional recent
   testing

* tag '6.7-rc5-ksmbd-server-fixes' of git://git.samba.org/ksmbd:
  ksmbd: fix wrong name of SMB2_CREATE_ALLOCATION_SIZE
  ksmbd: fix wrong allocation size update in smb2_open()
  ksmbd: avoid duplicate opinfo_put() call on error of smb21_lease_break_ack()
  ksmbd: lazy v2 lease break on smb2_write()
  ksmbd: send v2 lease break notification for directory
  ksmbd: downgrade RWH lease caching state to RH for directory
  ksmbd: set v2 lease capability
  ksmbd: set epoch in create context v2 lease
  ksmbd: fix memory leak in smb2_lock()
parents 26aff849 13736654
...@@ -1145,7 +1145,7 @@ struct smb2_server_client_notification { ...@@ -1145,7 +1145,7 @@ struct smb2_server_client_notification {
#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */ #define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */
#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ" #define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ"
#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC" #define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC"
#define SMB2_CREATE_ALLOCATION_SIZE "AISi" #define SMB2_CREATE_ALLOCATION_SIZE "AlSi"
#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc" #define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc"
#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp" #define SMB2_CREATE_TIMEWARP_REQUEST "TWrp"
#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid" #define SMB2_CREATE_QUERY_ON_DISK_ID "QFid"
...@@ -1253,6 +1253,7 @@ struct create_mxac_rsp { ...@@ -1253,6 +1253,7 @@ struct create_mxac_rsp {
#define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04) #define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04)
#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02) #define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02)
#define SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE cpu_to_le32(0x04)
#define SMB2_LEASE_KEY_SIZE 16 #define SMB2_LEASE_KEY_SIZE 16
......
...@@ -102,9 +102,10 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) ...@@ -102,9 +102,10 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx)
lease->new_state = 0; lease->new_state = 0;
lease->flags = lctx->flags; lease->flags = lctx->flags;
lease->duration = lctx->duration; lease->duration = lctx->duration;
lease->is_dir = lctx->is_dir;
memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE);
lease->version = lctx->version; lease->version = lctx->version;
lease->epoch = 0; lease->epoch = le16_to_cpu(lctx->epoch);
INIT_LIST_HEAD(&opinfo->lease_entry); INIT_LIST_HEAD(&opinfo->lease_entry);
opinfo->o_lease = lease; opinfo->o_lease = lease;
...@@ -395,8 +396,8 @@ void close_id_del_oplock(struct ksmbd_file *fp) ...@@ -395,8 +396,8 @@ void close_id_del_oplock(struct ksmbd_file *fp)
{ {
struct oplock_info *opinfo; struct oplock_info *opinfo;
if (S_ISDIR(file_inode(fp->filp)->i_mode)) if (fp->reserve_lease_break)
return; smb_lazy_parent_lease_break_close(fp);
opinfo = opinfo_get(fp); opinfo = opinfo_get(fp);
if (!opinfo) if (!opinfo)
...@@ -543,12 +544,13 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, ...@@ -543,12 +544,13 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
/* upgrading lease */ /* upgrading lease */
if ((atomic_read(&ci->op_count) + if ((atomic_read(&ci->op_count) +
atomic_read(&ci->sop_count)) == 1) { atomic_read(&ci->sop_count)) == 1) {
if (lease->state == if (lease->state != SMB2_LEASE_NONE_LE &&
(lctx->req_state & lease->state)) { lease->state == (lctx->req_state & lease->state)) {
lease->state |= lctx->req_state; lease->state |= lctx->req_state;
if (lctx->req_state & if (lctx->req_state &
SMB2_LEASE_WRITE_CACHING_LE) SMB2_LEASE_WRITE_CACHING_LE)
lease_read_to_write(opinfo); lease_read_to_write(opinfo);
} }
} else if ((atomic_read(&ci->op_count) + } else if ((atomic_read(&ci->op_count) +
atomic_read(&ci->sop_count)) > 1) { atomic_read(&ci->sop_count)) > 1) {
...@@ -900,7 +902,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) ...@@ -900,7 +902,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
lease->new_state = lease->new_state =
SMB2_LEASE_READ_CACHING_LE; SMB2_LEASE_READ_CACHING_LE;
} else { } else {
if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE &&
!lease->is_dir)
lease->new_state = lease->new_state =
SMB2_LEASE_READ_CACHING_LE; SMB2_LEASE_READ_CACHING_LE;
else else
...@@ -1032,6 +1035,7 @@ static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) ...@@ -1032,6 +1035,7 @@ static void copy_lease(struct oplock_info *op1, struct oplock_info *op2)
SMB2_LEASE_KEY_SIZE); SMB2_LEASE_KEY_SIZE);
lease2->duration = lease1->duration; lease2->duration = lease1->duration;
lease2->flags = lease1->flags; lease2->flags = lease1->flags;
lease2->epoch = lease1->epoch++;
} }
static int add_lease_global_list(struct oplock_info *opinfo) static int add_lease_global_list(struct oplock_info *opinfo)
...@@ -1081,6 +1085,89 @@ static void set_oplock_level(struct oplock_info *opinfo, int level, ...@@ -1081,6 +1085,89 @@ static void set_oplock_level(struct oplock_info *opinfo, int level,
} }
} }
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
struct lease_ctx_info *lctx)
{
struct oplock_info *opinfo;
struct ksmbd_inode *p_ci = NULL;
if (lctx->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);
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
if (!opinfo->is_lease)
continue;
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE &&
(!(lctx->flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) ||
!compare_guid_key(opinfo, fp->conn->ClientGUID,
lctx->parent_lease_key))) {
if (!atomic_inc_not_zero(&opinfo->refcount))
continue;
atomic_inc(&opinfo->conn->r_count);
if (ksmbd_conn_releasing(opinfo->conn)) {
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);
ksmbd_inode_put(p_ci);
}
void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
{
struct oplock_info *opinfo;
struct ksmbd_inode *p_ci = NULL;
rcu_read_lock();
opinfo = rcu_dereference(fp->f_opinfo);
rcu_read_unlock();
if (!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);
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
if (!opinfo->is_lease)
continue;
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) {
if (!atomic_inc_not_zero(&opinfo->refcount))
continue;
atomic_inc(&opinfo->conn->r_count);
if (ksmbd_conn_releasing(opinfo->conn)) {
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);
ksmbd_inode_put(p_ci);
}
/** /**
* smb_grant_oplock() - handle oplock/lease request on file open * smb_grant_oplock() - handle oplock/lease request on file open
* @work: smb work * @work: smb work
...@@ -1104,10 +1191,6 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, ...@@ -1104,10 +1191,6 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
bool prev_op_has_lease; bool prev_op_has_lease;
__le32 prev_op_state = 0; __le32 prev_op_state = 0;
/* not support directory lease */
if (S_ISDIR(file_inode(fp->filp)->i_mode))
return 0;
opinfo = alloc_opinfo(work, pid, tid); opinfo = alloc_opinfo(work, pid, tid);
if (!opinfo) if (!opinfo)
return -ENOMEM; return -ENOMEM;
...@@ -1364,6 +1447,7 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) ...@@ -1364,6 +1447,7 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
memcpy(buf->lcontext.LeaseKey, lease->lease_key, memcpy(buf->lcontext.LeaseKey, lease->lease_key,
SMB2_LEASE_KEY_SIZE); SMB2_LEASE_KEY_SIZE);
buf->lcontext.LeaseFlags = lease->flags; buf->lcontext.LeaseFlags = lease->flags;
buf->lcontext.Epoch = cpu_to_le16(++lease->epoch);
buf->lcontext.LeaseState = lease->state; buf->lcontext.LeaseState = lease->state;
memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key,
SMB2_LEASE_KEY_SIZE); SMB2_LEASE_KEY_SIZE);
...@@ -1400,10 +1484,11 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) ...@@ -1400,10 +1484,11 @@ 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) struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
{ {
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;
...@@ -1421,8 +1506,14 @@ struct lease_ctx_info *parse_lease_state(void *open_req) ...@@ -1421,8 +1506,14 @@ struct lease_ctx_info *parse_lease_state(void *open_req)
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);
lreq->req_state = lc->lcontext.LeaseState; 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->flags = lc->lcontext.LeaseFlags; lreq->flags = lc->lcontext.LeaseFlags;
lreq->epoch = lc->lcontext.Epoch;
lreq->duration = lc->lcontext.LeaseDuration; lreq->duration = lc->lcontext.LeaseDuration;
memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey,
SMB2_LEASE_KEY_SIZE); SMB2_LEASE_KEY_SIZE);
......
...@@ -34,7 +34,9 @@ struct lease_ctx_info { ...@@ -34,7 +34,9 @@ struct lease_ctx_info {
__le32 flags; __le32 flags;
__le64 duration; __le64 duration;
__u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE];
__le16 epoch;
int version; int version;
bool is_dir;
}; };
struct lease_table { struct lease_table {
...@@ -53,6 +55,7 @@ struct lease { ...@@ -53,6 +55,7 @@ struct lease {
__u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE];
int version; int version;
unsigned short epoch; unsigned short epoch;
bool is_dir;
struct lease_table *l_lb; struct lease_table *l_lb;
}; };
...@@ -108,7 +111,7 @@ void opinfo_put(struct oplock_info *opinfo); ...@@ -108,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); struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir);
__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);
...@@ -124,4 +127,7 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, ...@@ -124,4 +127,7 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci,
struct lease_ctx_info *lctx); struct lease_ctx_info *lctx);
void destroy_lease_table(struct ksmbd_conn *conn); void destroy_lease_table(struct ksmbd_conn *conn);
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
struct lease_ctx_info *lctx);
void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp);
#endif /* __KSMBD_OPLOCK_H */ #endif /* __KSMBD_OPLOCK_H */
...@@ -221,7 +221,8 @@ void init_smb3_0_server(struct ksmbd_conn *conn) ...@@ -221,7 +221,8 @@ void init_smb3_0_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
...@@ -245,7 +246,8 @@ void init_smb3_02_server(struct ksmbd_conn *conn) ...@@ -245,7 +246,8 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
(!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) && (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
...@@ -270,7 +272,8 @@ int init_smb3_11_server(struct ksmbd_conn *conn) ...@@ -270,7 +272,8 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
(!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) && (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
......
...@@ -2516,7 +2516,7 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path * ...@@ -2516,7 +2516,7 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path *
da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME |
XATTR_DOSINFO_ITIME; XATTR_DOSINFO_ITIME;
rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_idmap(path->mnt), path, &da, false); rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_idmap(path->mnt), path, &da, true);
if (rc) if (rc)
ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); ksmbd_debug(SMB, "failed to store file attribute into xattr\n");
} }
...@@ -2732,10 +2732,6 @@ int smb2_open(struct ksmbd_work *work) ...@@ -2732,10 +2732,6 @@ int smb2_open(struct ksmbd_work *work)
} }
} }
req_op_level = req->RequestedOplockLevel;
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));
...@@ -3189,23 +3185,6 @@ int smb2_open(struct ksmbd_work *work) ...@@ -3189,23 +3185,6 @@ int smb2_open(struct ksmbd_work *work)
goto err_out; goto err_out;
} }
rc = ksmbd_vfs_getattr(&path, &stat);
if (rc)
goto err_out;
if (stat.result_mask & STATX_BTIME)
fp->create_time = ksmbd_UnixTimeToNT(stat.btime);
else
fp->create_time = ksmbd_UnixTimeToNT(stat.ctime);
if (req->FileAttributes || fp->f_ci->m_fattr == 0)
fp->f_ci->m_fattr =
cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes)));
if (!created)
smb2_update_xattrs(tcon, &path, fp);
else
smb2_new_xattrs(tcon, &path, fp);
if (file_present || created) if (file_present || created)
ksmbd_vfs_kern_path_unlock(&parent_path, &path); ksmbd_vfs_kern_path_unlock(&parent_path, &path);
...@@ -3215,6 +3194,10 @@ int smb2_open(struct ksmbd_work *work) ...@@ -3215,6 +3194,10 @@ 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 &&
...@@ -3225,6 +3208,13 @@ int smb2_open(struct ksmbd_work *work) ...@@ -3225,6 +3208,13 @@ 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) {
/*
* Compare parent lease using parent key. If there is no
* a lease that has same parent key, Send lease break
* notification.
*/
smb_send_parent_lease_break_noti(fp, lc);
req_op_level = smb2_map_lease_to_oplock(lc->req_state); req_op_level = smb2_map_lease_to_oplock(lc->req_state);
ksmbd_debug(SMB, ksmbd_debug(SMB,
"lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n",
...@@ -3295,6 +3285,23 @@ int smb2_open(struct ksmbd_work *work) ...@@ -3295,6 +3285,23 @@ int smb2_open(struct ksmbd_work *work)
} }
} }
rc = ksmbd_vfs_getattr(&path, &stat);
if (rc)
goto err_out1;
if (stat.result_mask & STATX_BTIME)
fp->create_time = ksmbd_UnixTimeToNT(stat.btime);
else
fp->create_time = ksmbd_UnixTimeToNT(stat.ctime);
if (req->FileAttributes || fp->f_ci->m_fattr == 0)
fp->f_ci->m_fattr =
cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes)));
if (!created)
smb2_update_xattrs(tcon, &path, fp);
else
smb2_new_xattrs(tcon, &path, fp);
memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
rsp->StructureSize = cpu_to_le16(89); rsp->StructureSize = cpu_to_le16(89);
...@@ -7080,6 +7087,7 @@ int smb2_lock(struct ksmbd_work *work) ...@@ -7080,6 +7087,7 @@ int smb2_lock(struct ksmbd_work *work)
smb2_remove_blocked_lock, smb2_remove_blocked_lock,
argv); argv);
if (rc) { if (rc) {
kfree(argv);
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
} }
...@@ -8211,6 +8219,11 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) ...@@ -8211,6 +8219,11 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)
le32_to_cpu(req->LeaseState)); le32_to_cpu(req->LeaseState));
} }
if (ret < 0) {
rsp->hdr.Status = err;
goto err_out;
}
lease_state = lease->state; lease_state = lease->state;
opinfo->op_state = OPLOCK_STATE_NONE; opinfo->op_state = OPLOCK_STATE_NONE;
wake_up_interruptible_all(&opinfo->oplock_q); wake_up_interruptible_all(&opinfo->oplock_q);
...@@ -8218,11 +8231,6 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) ...@@ -8218,11 +8231,6 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)
wake_up_interruptible_all(&opinfo->oplock_brk); wake_up_interruptible_all(&opinfo->oplock_brk);
opinfo_put(opinfo); opinfo_put(opinfo);
if (ret < 0) {
rsp->hdr.Status = err;
goto err_out;
}
rsp->StructureSize = cpu_to_le16(36); rsp->StructureSize = cpu_to_le16(36);
rsp->Reserved = 0; rsp->Reserved = 0;
rsp->Flags = 0; rsp->Flags = 0;
......
...@@ -517,6 +517,9 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -517,6 +517,9 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
} }
} }
/* Reserve lease break for parent dir at closing time */
fp->reserve_lease_break = true;
/* Do we need to break any of a levelII oplock? */ /* Do we need to break any of a levelII oplock? */
smb_break_all_levII_oplock(work, fp, 1); smb_break_all_levII_oplock(work, fp, 1);
......
...@@ -87,6 +87,17 @@ static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) ...@@ -87,6 +87,17 @@ static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp)
return __ksmbd_inode_lookup(fp->filp->f_path.dentry); return __ksmbd_inode_lookup(fp->filp->f_path.dentry);
} }
struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d)
{
struct ksmbd_inode *ci;
read_lock(&inode_hash_lock);
ci = __ksmbd_inode_lookup(d);
read_unlock(&inode_hash_lock);
return ci;
}
int ksmbd_query_inode_status(struct dentry *dentry) int ksmbd_query_inode_status(struct dentry *dentry)
{ {
struct ksmbd_inode *ci; struct ksmbd_inode *ci;
...@@ -199,7 +210,7 @@ static void ksmbd_inode_free(struct ksmbd_inode *ci) ...@@ -199,7 +210,7 @@ static void ksmbd_inode_free(struct ksmbd_inode *ci)
kfree(ci); kfree(ci);
} }
static void ksmbd_inode_put(struct ksmbd_inode *ci) void ksmbd_inode_put(struct ksmbd_inode *ci)
{ {
if (atomic_dec_and_test(&ci->m_count)) if (atomic_dec_and_test(&ci->m_count))
ksmbd_inode_free(ci); ksmbd_inode_free(ci);
......
...@@ -105,6 +105,7 @@ struct ksmbd_file { ...@@ -105,6 +105,7 @@ struct ksmbd_file {
struct ksmbd_readdir_data readdir_data; struct ksmbd_readdir_data readdir_data;
int dot_dotdot[2]; int dot_dotdot[2];
unsigned int f_state; unsigned int f_state;
bool reserve_lease_break;
}; };
static inline void set_ctx_actor(struct dir_context *ctx, static inline void set_ctx_actor(struct dir_context *ctx,
...@@ -138,6 +139,8 @@ struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id); ...@@ -138,6 +139,8 @@ struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id);
struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
u64 pid); u64 pid);
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);
void ksmbd_inode_put(struct ksmbd_inode *ci);
struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id);
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment