]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ksmbd: treat read-control opens as stat opens only for leases
authorNamjae Jeon <linkinjeon@kernel.org>
Sun, 21 Jun 2026 10:57:55 +0000 (19:57 +0900)
committerSteve French <stfrench@microsoft.com>
Tue, 23 Jun 2026 01:15:06 +0000 (20:15 -0500)
A second open that requests only metadata-level access must not break
the existing caching state. ksmbd already skips the break for such opens
via fp->attrib_only (FILE_READ_ATTRIBUTES,
FILE_WRITE_ATTRIBUTES and FILE_SYNCHRONIZE).

An open requesting only READ_CONTROL (reading the security descriptor)
must be treated differently depending on the existing caching state.
smbtorture smb2.lease.statopen4 expects a read-control open NOT to break
a caching lease, while smb2.oplock.statopen1 expects the same open to
break a batch oplock. So READ_CONTROL is a stat open for leases but not
for oplocks.

Extend the stat-open break-skip in smb_grant_oplock() to also cover a
read-control-only open, but only when the existing holder is a lease.
The global fp->attrib_only flag (used for share-mode, rename and truncate
decisions) is left unchanged so oplock behaviour is preserved.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/server/oplock.c

index afd492be88fb2fe98c5c663e6e829db0a24856b0..99cbd7aa03fc5c51bbefd51ffbba1307634710d5 100644 (file)
@@ -312,6 +312,18 @@ void opinfo_put(struct oplock_info *opinfo)
        free_opinfo(opinfo);
 }
 
+static bool ksmbd_inode_has_lease(struct ksmbd_inode *ci)
+{
+       struct oplock_info *opinfo = opinfo_get_list(ci);
+       bool is_lease;
+
+       if (!opinfo)
+               return false;
+       is_lease = opinfo->is_lease;
+       opinfo_put(opinfo);
+       return is_lease;
+}
+
 static void opinfo_add(struct oplock_info *opinfo, struct ksmbd_file *fp)
 {
        struct ksmbd_inode *ci = fp->f_ci;
@@ -1402,10 +1414,22 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
        if (!opinfo_count(fp))
                goto set_lev;
 
-       /* grant none-oplock if second open is trunc */
-       if (fp->attrib_only && fp->cdoption != FILE_OVERWRITE_IF_LE &&
+       /*
+        * A stat open that only requests metadata access must not break the
+        * existing caching state. READ_CONTROL (reading the security
+        * descriptor) does not conflict with a lease, but it does conflict
+        * with an oplock, so only treat a read-control-only open as a stat
+        * open when the existing holder is a lease.
+        */
+       if (fp->cdoption != FILE_OVERWRITE_IF_LE &&
            fp->cdoption != FILE_OVERWRITE_LE &&
-           fp->cdoption != FILE_SUPERSEDE_LE) {
+           fp->cdoption != FILE_SUPERSEDE_LE &&
+           (fp->attrib_only ||
+            (!(fp->daccess & ~(FILE_READ_ATTRIBUTES_LE |
+                               FILE_WRITE_ATTRIBUTES_LE |
+                               FILE_SYNCHRONIZE_LE |
+                               FILE_READ_CONTROL_LE)) &&
+             ksmbd_inode_has_lease(ci)))) {
                req_op_level = SMB2_OPLOCK_LEVEL_NONE;
                goto set_lev;
        }