summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnzo Matsumiya <ematsumiya@suse.de>2025-09-11 17:57:05 -0300
committerEnzo Matsumiya <ematsumiya@suse.de>2025-09-12 17:27:38 -0300
commit2ab89424bab5f5a7ae0e0e5e795a3d5b86939028 (patch)
treeb7b268a2d3a1efd7fa99dacf32a18131b972af31
parent88ec1e734bbae9acaff4ac5b22d85dcb50df670b (diff)
downloadlinux-2ab89424bab5f5a7ae0e0e5e795a3d5b86939028.tar.gz
linux-2ab89424bab5f5a7ae0e0e5e795a3d5b86939028.tar.bz2
linux-2ab89424bab5f5a7ae0e0e5e795a3d5b86939028.zip
smb: client: prevent lease breaks of cached parents when opening children
In SMB2_open_init(), lookup for a cached parent of oparms->path (child/target) and set ParentLeaseKey in lease context if found. Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
-rw-r--r--fs/smb/client/cached_dir.c21
-rw-r--r--fs/smb/client/dir.c24
-rw-r--r--fs/smb/client/smb2pdu.c73
3 files changed, 58 insertions, 60 deletions
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
index ad9c95c20c4e..0acaed4a2a34 100644
--- a/fs/smb/client/cached_dir.c
+++ b/fs/smb/client/cached_dir.c
@@ -209,7 +209,6 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, const char *path,
struct cached_fids *cfids;
const char *npath;
int retries = 0, cur_sleep = 1;
- __le32 lease_flags = 0;
bool has_info;
if (cifs_sb->root == NULL)
@@ -293,25 +292,6 @@ replay_again:
rc = -ENOENT;
goto out_err;
}
- if (dentry->d_parent && server->dialect >= SMB30_PROT_ID) {
- struct cached_fid *parent_cfid;
-
- spin_lock(&cfids->cfid_list_lock);
- list_for_each_entry(parent_cfid, &cfids->entries, entry) {
- if (parent_cfid->dentry == dentry->d_parent) {
- cifs_dbg(FYI, "found a parent cached file handle\n");
- if (cfid_is_valid(parent_cfid)) {
- lease_flags
- |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE;
- memcpy(pfid->parent_lease_key,
- parent_cfid->fid.lease_key,
- SMB2_LEASE_KEY_SIZE);
- }
- break;
- }
- }
- spin_unlock(&cfids->cfid_list_lock);
- }
}
/*
@@ -343,7 +323,6 @@ replay_again:
FILE_READ_EA,
.disposition = FILE_OPEN,
.fid = pfid,
- .lease_flags = lease_flags,
.replay = !!(retries),
};
diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c
index 2c6450e1c97c..23412156d12a 100644
--- a/fs/smb/client/dir.c
+++ b/fs/smb/client/dir.c
@@ -190,9 +190,7 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
int disposition;
struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_open_parms oparms;
- struct cached_fid *parent_cfid = NULL;
int rdwr_for_fscache = 0;
- __le32 lease_flags = 0;
*oplock = 0;
if (tcon->ses->server->oplocks)
@@ -314,28 +312,7 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
create_options |= CREATE_OPTION_READONLY;
-
retry_open:
- if (tcon->cfids && direntry->d_parent && server->dialect >= SMB30_PROT_ID) {
- parent_cfid = NULL;
- spin_lock(&tcon->cfids->cfid_list_lock);
- list_for_each_entry(parent_cfid, &tcon->cfids->entries, entry) {
- if (parent_cfid->dentry == direntry->d_parent) {
- cifs_dbg(FYI, "found a parent cached file handle\n");
- if (cfid_is_valid(parent_cfid)) {
- lease_flags
- |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE;
- memcpy(fid->parent_lease_key,
- parent_cfid->fid.lease_key,
- SMB2_LEASE_KEY_SIZE);
- parent_cfid->dirents.is_valid = false;
- }
- break;
- }
- }
- spin_unlock(&tcon->cfids->cfid_list_lock);
- }
-
oparms = (struct cifs_open_parms) {
.tcon = tcon,
.cifs_sb = cifs_sb,
@@ -344,7 +321,6 @@ retry_open:
.disposition = disposition,
.path = full_path,
.fid = fid,
- .lease_flags = lease_flags,
.mode = mode,
};
rc = server->ops->open(xid, &oparms, oplock, buf);
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 07ba61583114..f54029ee2466 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -2419,7 +2419,8 @@ add_lease_context(struct TCP_Server_Info *server,
if (iov[num].iov_base == NULL)
return -ENOMEM;
iov[num].iov_len = server->vals->create_lease_size;
- req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE;
+ /* keep the requested oplock level in case of just setting ParentLeaseKey */
+ req->RequestedOplockLevel = *oplock;
*num_iovec = num + 1;
return 0;
}
@@ -3001,6 +3002,45 @@ err_free_path:
return rc;
}
+/*
+ * Set ParentLeaseKey in @oparms if path's parent is cached.
+ *
+ * MS-SMB2 "Product Behavior" says Windows only checks/sets ParentLeaseKey when a lease is
+ * requested for the child/target.
+ * However, practically, adding the lease context with ParentLeaseKey set, even with oplock none,
+ * works fine.
+ *
+ * Ref: MS-SMB2 3.3.5.9 and MS-FSA 2.1.5.1
+ *
+ * Return: 0 if ParentLeaseKey was set, -errno otherwise
+ */
+static int add_parent_leasekey(struct cached_fids *cfids, struct cifs_open_parms *oparms)
+{
+ struct cached_fid *cfid;
+
+ if (!cfids || !oparms || !*oparms->path)
+ return -EINVAL;
+
+ /*
+ * We only have RH caching for cached dirs, so we must let the lease break happen for any
+ * operation that creates/removes entries.
+ */
+ if (oparms->disposition != FILE_OPEN || oparms->desired_access == DELETE)
+ return -EOPNOTSUPP;
+
+ cfid = find_cached_dir(cfids, oparms->path, CFID_LOOKUP_PARENT);
+ if (!cfid)
+ return -ENOENT;
+
+ cifs_dbg(FYI, "%s: found cached parent for path: %s\n", __func__, oparms->path);
+
+ memcpy(oparms->fid->parent_lease_key, cfid->fid.lease_key, SMB2_LEASE_KEY_SIZE);
+ oparms->lease_flags |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE;
+ close_cached_dir(cfid);
+
+ return 0;
+}
+
int
SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
struct smb_rqst *rqst, __u8 *oplock,
@@ -3077,24 +3117,27 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
iov[1].iov_len = uni_path_len;
iov[1].iov_base = path;
- if ((!server->oplocks) || (tcon->no_lease))
+ if (!server->oplocks || tcon->no_lease)
*oplock = SMB2_OPLOCK_LEVEL_NONE;
- if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) ||
- *oplock == SMB2_OPLOCK_LEVEL_NONE)
- req->RequestedOplockLevel = *oplock;
- else if (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) &&
- (oparms->create_options & CREATE_NOT_FILE))
- req->RequestedOplockLevel = *oplock; /* no srv lease support */
- else {
- rc = add_lease_context(server, req, iov, &n_iov,
- oparms->fid->lease_key, oplock,
- oparms->fid->parent_lease_key,
- oparms->lease_flags);
- if (rc)
- return rc;
+ if (server->capabilities & SMB2_GLOBAL_CAP_LEASING) {
+ rc = -EOPNOTSUPP;
+
+ if (server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING)
+ rc = add_parent_leasekey(tcon->cfids, oparms);
+
+ if (!rc || *oplock != SMB2_OPLOCK_LEVEL_NONE) {
+ rc = add_lease_context(server, req, iov, &n_iov,
+ oparms->fid->lease_key, oplock,
+ oparms->fid->parent_lease_key,
+ oparms->lease_flags);
+ if (rc)
+ return rc;
+ }
}
+ req->RequestedOplockLevel = *oplock;
+
if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) {
rc = add_durable_context(iov, &n_iov, oparms,
tcon->use_persistent);