diff options
| author | Enzo Matsumiya <ematsumiya@suse.de> | 2025-09-11 17:57:05 -0300 |
|---|---|---|
| committer | Enzo Matsumiya <ematsumiya@suse.de> | 2025-09-12 17:27:38 -0300 |
| commit | 2ab89424bab5f5a7ae0e0e5e795a3d5b86939028 (patch) | |
| tree | b7b268a2d3a1efd7fa99dacf32a18131b972af31 | |
| parent | 88ec1e734bbae9acaff4ac5b22d85dcb50df670b (diff) | |
| download | linux-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.c | 21 | ||||
| -rw-r--r-- | fs/smb/client/dir.c | 24 | ||||
| -rw-r--r-- | fs/smb/client/smb2pdu.c | 73 |
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); |
