Commit Graph

2237 Commits

Author SHA1 Message Date
Linus Torvalds
71fab6fa76 50 ksmbd server fixes
-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmo9nx0ACgkQiiy9cAdy
 T1GiNgwAuxlhhJghBrXqNqzW7FwJRsrC+ibEfCQDdI5ifREo+6Ov6I9FBEu5z/XX
 OAXFJWRHYHzZEc7XqpjXOqIXRq9GN3ADA2bwQ0fsh94TazmIeYXEDcMDnutQOECM
 rqm+5kO+mY+c76XPm1C1CcNi+Te3q7Ry4O6rY7JhgGn5agZPyEEVHQ2nhRbS4YCp
 LQESWWd169CIeHkBJCUnPbLPPaC56pkkHaEKgoPuZk0lbSTMOeQrmWInxX2lXBNQ
 udO79m7Fz2XrGy0vjBwBFfOaWdBd2Gk1b76Hc/3EwfVVm2NN8w6ge0L4m+1RglDz
 3VQSBsJ3nNVNRxSLpfjRwAE+pFvIy7+XmHA4lJ0REeklpyVF+27i/QvqPuCovm/g
 AfMbrExIcDt+96DEe+h5iJtbP7WiuCIsYWtvpUtnDUdz4aTrmRlJhzaDvz88vmDi
 5GRmLm13oGoPN3UTF3PQDgFG4Jigs+1YlKrbeIBOXkIbcYBslIDIIxN0b82i2GRT
 6osoVCRX
 =/FfE
 -----END PGP SIGNATURE-----

Merge tag 'v7.2-rc-part2-smb3-server-fixes' of git://git.samba.org/ksmbd

Pull smb server updates from Steve French:
 "This is mostly a correctness and compatibility update for ksmbd's
  SMB2/3 lease, oplock, durable handle, compound request, CREATE,
  rename, stream and share-mode handling.

  A large part of the series fixes cases found by smbtorture where ksmbd
  diverged from the SMB2/3 protocol requirements.

  The main changes are:

   - Rework SMB2 lease state handling so lease state is shared per
     ClientGuid/LeaseKey across opens, with better validation of lease
     create contexts, ACK handling, epochs, break-in-progress reporting,
     v2 lease notification routing, and chained lease breaks

   - Fix several oplock break corner cases, including ACK validation,
     timeout downgrade behavior, level-II break handling on unlink,
     share-conflict lease breaks, and read-control/stat-open behavior

   - Fix durable handle behavior around delete-on-close, stale
     reconnects, reconnect context parsing, oplock/lease break
     invalidation, and durable v2 AppInstanceId replacement

   - Fix compound request handling so related commands propagate failed
     statuses correctly, preserve response framing across chained
     errors, keep compound FIDs across READ/WRITE/FLUSH, and send
     interim STATUS_PENDING where clients expect cancellable compound
     I/O

   - Tighten CREATE and stream semantics, including create attribute
     validation, allocation size reporting, explicit create security
     descriptors, unnamed DATA stream handling, stream directory
     validation, and stream delete sharing against the base file

   - Fix rename and metadata behavior, including parent directory
     sharing checks, denying directory rename with open children, and
     preserving SMB ChangeTime across rename for open handles

   - Fix two important safety issues: a multichannel byte-range lock
     list owner race that could lead to use-after-free, and an NTLMv2
     session key update before authentication proof validation

   - Fix a concurrent SMB2 NEGOTIATE preauth use-after-free, a UBSAN
     warning in compression capability parsing, a false hung-task
     warning in the durable handle scavenger, endian debug logging,
     Smatch indentation warnings, and kernel-doc warnings

   - Increase the default SMB3 transaction size from 1MB to 4MB to
     better match modern read/write negotiation and improve sequential
     I/O behavior"

* tag 'v7.2-rc-part2-smb3-server-fixes' of git://git.samba.org/ksmbd: (50 commits)
  ksmbd: fix kernel-doc warnings in smb2_lease_break_noti()
  ksmbd: fix inconsistent indenting warnings
  ksmbd: validate NTLMv2 response before updating session key
  ksmbd: increase SMB3_DEFAULT_TRANS_SIZE from 1MB to 4MB
  ksmbd: fix UBSAN array-index-out-of-bounds in decode_compress_ctxt()
  ksmbd: sleep interruptibly in the durable handle scavenger
  ksmbd: start file id allocation at 1
  ksmbd: treat read-control opens as stat opens only for leases
  ksmbd: validate :: stream type against directory create
  ksmbd: break conflicting-open leases only as far as needed
  ksmbd: break handle caching for share conflicts
  ksmbd: normalize ungrantable lease states
  ksmbd: return oplock protocol error for level II ack
  ksmbd: avoid level II oplock break notification on unlink
  ksmbd: downgrade oplock after break timeout
  ksmbd: apply create security descriptor first
  ksmbd: return requested create allocation size
  ksmbd: tighten create file attribute validation
  ksmbd: reject empty-attribute synchronize-only create
  ksmbd: honor stream delete sharing for base file
  ...
2026-06-26 08:17:42 -07:00
Shyam Prasad N
f53208233b cifs: define variable sized buffer for querydir responses
QueryDirectory responses today are stored in one of two fixed
sized buffers: smallbuf (448 bytes) or bigbuf (16KB). These are
borrowed from server struct and are not sufficient for large-sized
query dir operations.

With this change we will now define a new buffer type specifically
for cifs_search_info to hold variable sized responses. These will
be allocated by kmalloc and freed by kfree.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-24 16:49:02 -05:00
Huiwen He
99cd0a6eeb smb/client: do not account EOF extension as allocation
cifs_setsize() updates the local inode size after SetEOF succeeds. It also
used the new EOF as a local i_blocks estimate, but extending EOF does not
prove that the intervening range was allocated.

For example, after writing 1 MiB and then extending EOF to 10 MiB, the
client can report the file as fully allocated even though the server still
reports a much smaller AllocationSize:

        $ dd if=/dev/zero of=test bs=1M count=1
        $ truncate -s 10M test && stat -c 'size=%s blocks=%b' test
        $ stat --cached=never -c 'size=%s blocks=%b' test

        client stat:                    size=10485760 blocks=20480
        server stat:                    size=10485760 blocks=2056
        client stat after revalidation: size=10485760 blocks=2056

A later attribute revalidation may correct i_blocks, but callers such as
xfstests generic/495 invoke swapon immediately after truncate. The swapfile
hole check can therefore observe the inflated local i_blocks value and
accept a sparse file.

Do not grow i_blocks from cifs_setsize() on EOF extension.  Only clamp it
on shrink; allocation growth must come from write completion or from
server-reported AllocationSize.

With this change, EOF extension no longer makes a sparse file appear
fully allocated before the next attribute revalidation, and xfstests
generic/495 no longer accepts it through the inflated local i_blocks value.

Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-24 16:48:56 -05:00
Huiwen He
2a4b3d2db5 smb/client: preserve errors from smb2_set_sparse()
smb2_set_sparse() converts every FSCTL_SET_SPARSE failure to false and
marks sparse support as broken for the share. Callers therefore report
EOPNOTSUPP even for errors such as ENOSPC or EACCES, and later sparse
operations remain disabled until the share is unmounted.

Return the SMB2 ioctl error directly. Set broken_sparse_sup only for
EOPNOTSUPP, which indicates that the server does not support the request.

Update smb3_punch_hole() to propagate the error returned by
smb2_set_sparse().

Fixes: 3d1a3745d8 ("Add sparse file support to SMB2/SMB3 mounts")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-24 10:33:51 -05:00
Haoxiang Li
1c6267a1d5 smb: client: Fix next buffer leak in receive_encrypted_standard()
receive_encrypted_standard() allocates next_buffer before checking
whether the number of compound PDUs already reached MAX_COMPOUND. If
the limit check fails, the function returns immediately and the newly
allocated next_buffer is not assigned to server->smallbuf/server->bigbuf,
making it leaked.

Move the MAX_COMPOUND check before allocating next_buffer.

Fixes: b24df3e30c ("cifs: update receive_encrypted_standard to handle compounded responses")
Cc: stable@vger.kernel.org
Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-24 10:33:51 -05:00
Fredric Cover
2c253f230a smb/client: use %pe to print error pointer
Currently, __reconnect_target_locked() prints the error pointer
'hostname' using PTR_ERR() and %ld, printing only the error code.

Fix this by using by using %pe to print the error symbolic code instead,
and remove PTR_ERR().

Signed-off-by: Fredric Cover <fredric.cover.lkernel@gmail.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-24 10:33:51 -05:00
Huiwen He
898d280f4e smb/client: name the default fallocate mode
FALLOC_FL_ALLOCATE_RANGE identifies the default fallocate
allocation mode and is defined as zero.

Use the symbolic name instead of a literal zero in
smb3_fallocate() to make the mode dispatch clearer. This
does not change behavior.

Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-24 10:33:51 -05:00
Steve French
d281a757ff smb common: add missing AAPL defines
Add various defines for AAPL open context, e.g. for queries of
server capabilities.

Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-24 10:33:51 -05:00
Ralph Boehme
760ef2c579 smb/client: fix chown/chgrp with SMB3 POSIX Extensions
Ownership (chown) and group (chgrp) modifications were being ignored when
mounting with SMB3 POSIX Extensions unless CIFS_MOUNT_CIFS_ACL or
CIFS_MOUNT_MODE_FROM_SID were also explicitly set.

Fix this by checking for posix_extensions in cifs_setattr_nounix() when
updating UID and GID, ensuring that id_mode_to_cifs_acl() is called to map
and set the ownership/group information on the server.

Cc: stable@vger.kernel.org
Signed-off-by: Ralph Boehme <slow@samba.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-24 10:33:51 -05:00
Ralph Boehme
4939889c98 smb/client: fix security flag calculation when setting security descriptors
In id_mode_to_cifs_acl(), aclflag was initialized to CIFS_ACL_DACL by default.
This forced the client to request setting the DACL even when only an ownership
(chown) or group (chgrp) change was being performed.

Let build_sec_desc() do the proper flag calculation by initializing aclflag
to 0. build_sec_desc() sets the appropriate bits (CIFS_ACL_OWNER, CIFS_ACL_GROUP,
or CIFS_ACL_DACL) depending on what actually changed. During ownership transfer,
CIFS_ACL_DACL is only set if replace_sids_and_copy_aces() actually replaces the
SIDs inside any of the DACL's ACEs.

If build_sec_desc() results in aclflag being 0 (meaning no changes were mapped),
exit early to avoid sending an empty security descriptor update to the server.

Signed-off-by: Ralph Boehme <slow@samba.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-24 10:33:41 -05:00
Namjae Jeon
da793cf6d6 ksmbd: fix kernel-doc warnings in smb2_lease_break_noti()
kernel test robot report missing kernel-doc descriptions for the 'wait_ack'
and 'inc_epoch' parameters of smb2_lease_break_noti():

  Warning: fs/smb/server/oplock.c:937 function parameter 'wait_ack' not
   described in 'smb2_lease_break_noti'
  Warning: fs/smb/server/oplock.c:937 function parameter 'inc_epoch' not
   described in 'smb2_lease_break_noti'

Document both parameters to silence the warnings.

Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-23 17:52:42 -05:00
Namjae Jeon
f455ea21f2 ksmbd: fix inconsistent indenting warnings
Detected by Smatch.

   fs/smb/server/oplock.c:1446 smb_grant_oplock()
   warn: inconsistent indenting

Reported-by: Dan Carpenter <error27@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-23 17:52:42 -05:00
Haofeng Li
954d196beb ksmbd: validate NTLMv2 response before updating session key
ksmbd_auth_ntlmv2() derives the NTLMv2 session key into
sess->sess_key before it verifies the NTLMv2 response.
ksmbd_decode_ntlmssp_auth_blob() then continues into KEY_XCH even
when ksmbd_auth_ntlmv2() failed.

With SMB3 multichannel binding, the failed authentication operates on
an existing session and the session setup error path does not expire
binding sessions. A client can send a binding session setup with a
bad NT proof and KEY_XCH and still modify sess->sess_key before
STATUS_LOGON_FAILURE is returned.

Relevant path:

  smb2_sess_setup()
    -> conn->binding = true
    -> ntlm_authenticate()
       -> session_user()
       -> ksmbd_decode_ntlmssp_auth_blob()
          -> ksmbd_auth_ntlmv2()
             -> calc_ntlmv2_hash()
             -> hmac_md5_usingrawkey(..., sess->sess_key)
             -> crypto_memneq() returns mismatch
          -> KEY_XCH arc4_crypt(..., sess->sess_key, ...)
    -> out_err without expiring the binding session

Derive the base session key into a local buffer and copy it to
sess->sess_key only after the proof matches. Return immediately on
authentication failure so KEY_XCH is only processed after successful
authentication.

Fixes: e2f34481b2 ("cifsd: add server-side procedures for SMB3")
Fixes: f9929ef6a2 ("ksmbd: add support for key exchange")
Cc: stable@vger.kernel.org
Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-23 17:52:42 -05:00
Namjae Jeon
4429b56506 ksmbd: increase SMB3_DEFAULT_TRANS_SIZE from 1MB to 4MB
This patch raises `SMB3_DEFAULT_TRANS_SIZE` to 4MB to align it with
`smb2 max read/write`. This allows better I/O negotiation with modern
clients and improves sequential read/write performance on high-speed
networks.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:06 -05:00
Namjae Jeon
474fd91f38 ksmbd: fix UBSAN array-index-out-of-bounds in decode_compress_ctxt()
decode_compress_ctxt() walks CompressionAlgorithms[] using the client
supplied CompressionAlgorithmCount. That field is declared in
struct smb2_compression_capabilities_context as a fixed 4-element array,
but the number of algorithms is actually variable and clients such as
Windows advertise more than four (e.g. LZ77, LZ77+Huffman, LZNT1,
Pattern_V1 and LZ4).

The on-wire context length is already validated, so the access is within
the received buffer, but indexing the statically sized [4] array makes
UBSAN report an out-of-bounds access:

  UBSAN: array-index-out-of-bounds in smb2pdu.c:1122:48
  index 4 is out of range for type '__le16 [4]'
  Call Trace:
   smb2_handle_negotiate+0xda7/0xde0 [ksmbd]
   ksmbd_smb_negotiate_common+0x27b/0x3e0 [ksmbd]
   smb2_negotiate_request+0x14/0x20 [ksmbd]
   handle_ksmbd_work+0x181/0x500 [ksmbd]

Walk the algorithms through a pointer so the fixed-array bounds check is
not applied, while keeping the existing length validation that bounds the
loop to the data actually received.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:06 -05:00
Namjae Jeon
5a7f4d6d8e ksmbd: sleep interruptibly in the durable handle scavenger
The durable handle scavenger kthread waits up to DURABLE_HANDLE_MAX_TIMEOUT
(300 seconds) between scans using wait_event_timeout(), which sleeps in
TASK_UNINTERRUPTIBLE. When there are no durable handles pending expiry the
task stays in D state far longer than 120 seconds, so the hung task
detector prints a bogus "task ksmbd-durable-s blocked for more than 120
seconds" warning with a backtrace, even though the thread is only idle.

Use wait_event_interruptible_timeout() so the thread sleeps in
TASK_INTERRUPTIBLE, which the hung task detector ignores. This also suits
the already-freezable kthread. Treat a negative return (e.g. -ERESTARTSYS)
like a timeout when recomputing the next wake interval.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:06 -05:00
Namjae Jeon
6b375be0b4 ksmbd: start file id allocation at 1
ksmbd allocates both the volatile id (per-session file table) and the
persistent id (global file table) with idr_alloc_cyclic() starting at 0.
The first open after the module loads therefore gets volatile id 0 and
persistent id 0, and ksmbd returns an SMB2 FileId of {0, 0} in the create
response.

Clients treat an all-zero FileId as a null handle. smbtorture's
smb2_util_handle_empty() considers {0, 0} empty, so tests that guard the
close with it (e.g. smb2.oplock.statopen1, smb2.lease.statopen*) never
close that first handle. The leaked open keeps the inode's oplock count
non-zero, so a later batch oplock request on the same file is downgraded
to level II and the test fails.

Start the id allocation at 1 (KSMBD_START_FID) so no handle is ever
assigned a {0, 0} FileId, matching the behaviour of other SMB servers.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:06 -05:00
Namjae Jeon
be939e11c4 ksmbd: treat read-control opens as stat opens only for leases
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>
2026-06-22 20:15:06 -05:00
Namjae Jeon
256257279c ksmbd: validate :: stream type against directory create
smb2.streams.dir opens <dir>::$DATA with FILE_DIRECTORY_FILE and expects
STATUS_NOT_A_DIRECTORY, then opens <dir>::$DATA without it and expects
STATUS_FILE_IS_A_DIRECTORY.

Commit "treat unnamed DATA stream as base file" canonicalizes the ::$DATA
suffix to a NULL stream name so the open continues through the base-file
path. That skipped the stream/directory type validation, which was
guarded by "if (stream_name)", so opening a directory's ::$DATA stream
with FILE_DIRECTORY_FILE incorrectly returned STATUS_OK and a plain open
of it no longer reported STATUS_FILE_IS_A_DIRECTORY.

parse_stream_name() still records the explicit $DATA type in s_type even
when it clears stream_name.  Run the data-stream vs directory validation
whenever s_type is DATA_STREAM, not only when stream_name is set, so the
canonicalized ::$DATA open is rejected with the correct status.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:06 -05:00
Namjae Jeon
889d2e3894 ksmbd: break conflicting-open leases only as far as needed
smb2.lease.oplock and smb2.lease.breaking1 hold a lease and then issue a
single conflicting open on the same file.  The held lease must break one
step to drop write caching (RWH->RH, RW->R) and then stop, so
lease_break_info.count is 1 and the lease keeps its read/handle caching.

ksmbd instead cascaded the break all the way down to none
(e.g. RWH->RH->R->none), so the break count was 2 or 3 and the reported
lease state ended at 0.  Commit "chain pending lease breaks before waking
waiters" forces break_level to SMB2_OPLOCK_LEVEL_NONE for any non-lease
open against a handle-caching lease, which drives oplock_break()'s retry
loop down to none even when only one open is contending.

Drop that break_level override so a conflicting open breaks a lease only
to its own compatible level (level II, i.e. RH/R).

A deeper break is still required when a truncating open is also waiting
behind the same lease break.  smb2.lease.breaking3 keeps a normal open
pending through RWH->RH and an overwrite open pending behind it, and
expects the lease to continue RH->R->none before either open completes.
The overwrite waiter sets open_trunc on the lease while it blocks on the
pending break, so extend the retry loop to chain another break while that
truncating waiter still needs the lease at none.  The per-break open_trunc
snapshot stays cleared, so the cascade steps down (RH->R->none) instead of
collapsing straight to none, and the normal open stays pending until the
lease is fully broken.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:06 -05:00
Namjae Jeon
b35f8f898c ksmbd: break handle caching for share conflicts
smb2.lease.break_twice first opens a file with an RHW lease and then tries
a second open with restrictive sharing.  That open must fail with a sharing
violation, but the existing lease should be broken from RHW to RW because
only handle caching conflicts with the requested sharing.

ksmbd used the normal write-cache break calculation for this path, so RHW
was broken to RH.  The following successful open then did not generate the
expected second break from RW to R.

Pass share-conflict context into the lease break helper and, for lease
breaks caused by sharing, drop only SMB2_LEASE_HANDLE_CACHING from the
current lease state.  Other break paths keep the existing write/truncate
break behavior.

A share-conflict break must also remain a single break.  The triggering
open fails with a sharing violation and is never granted, so there is no
target oplock level to converge on.  The lease break retry loop, however,
keeps breaking while the lease level is still above req_op_level, which
broke RHW all the way down to R in one open (lease_break_info.count became
2 instead of 1).  Skip the again loop for share-conflict breaks so the
sharing open produces exactly one RHW->RW break and the later successful
open produces the separate RW->R break the test expects.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:06 -05:00
Namjae Jeon
43f2134942 ksmbd: normalize ungrantable lease states
smb2.lease.request verifies which SMB2 lease state combinations are granted
by the server.  Requests for H-only, W-only, and HW leases are valid lease
state bitmasks, but they are not grantable combinations and should be
returned as lease state none.

ksmbd only checked that the requested bits were inside the SMB2 lease state
mask.  As a result it could grant H-only, W-only, or HW requests and return
non-zero lease states where the client expects no lease.

Keep the bitmask validation, but normalize ungrantable combinations to zero
before allocating or looking up the lease.  The grantable combinations
remain unchanged: R, RH, RW, and RHW.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:06 -05:00
Namjae Jeon
854b4f8f80 ksmbd: return oplock protocol error for level II ack
SMB2 level II to none oplock breaks do not require an acknowledgment from
the client.  smb2.oplock.levelii500 intentionally acknowledges such a break
and expects the server to reject it with STATUS_INVALID_OPLOCK_PROTOCOL.

ksmbd drops the local level II oplock to none immediately after sending the
break notification because it does not wait for an ACK.  When the client
then sends the invalid ACK, smb20_oplock_break_ack() sees that the oplock
is not in OPLOCK_ACK_WAIT state and returns STATUS_INVALID_DEVICE_STATE
before checking the current oplock level.

If the oplock is already none when an unexpected SMB2 oplock break ACK
arrives, report STATUS_INVALID_OPLOCK_PROTOCOL.  Keep the existing
STATUS_INVALID_DEVICE_STATE response for other unexpected non-wait states.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:06 -05:00
Namjae Jeon
ec476c2580 ksmbd: avoid level II oplock break notification on unlink
smb2_util_unlink() opens the target with FILE_DELETE_ON_CLOSE and then
closes that handle.  Other clients can also mark a file for delete with
SMB2 SET_INFO FileDispositionInformation.

When these unlink paths break existing SMB2 level II oplocks, ksmbd sends
an unsolicited SMB2_OPLOCK_BREAK notification to none.  This races with the
synchronous CREATE or SET_INFO response expected by the client, and
smbtorture reports NT_STATUS_INVALID_NETWORK_RESPONSE while running
smb2.oplock.exclusive2.

SMB2 level II oplock breaks do not require an acknowledgment in the delete
path.  Keep lease handling unchanged, but drop plain SMB2 level II oplocks
locally for unlink requests without sending a break notification.  Normal
write/truncate paths still send the level II to none notification,
preserving the behavior covered by smb2.oplock.levelII500.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
7db0da9915 ksmbd: downgrade oplock after break timeout
smb2.oplock.batch22a opens a file with a batch oplock and then issues a
second open that waits for the oplock break timeout.  After the timeout the
second open should succeed, but the granted oplock level must be level II.

When the break times out, oplock_break() returns -ENOENT after invalidating
the previous opener.  smb_grant_oplock() went straight to set_lev with the
original requested oplock level, so the second open could be granted a new
batch oplock.  Downgrade the requested oplock to level II on the -ENOENT
break-timeout path before granting the oplock to the new open.

A break that completes because the previous owner closed its handle from
the oplock break handler must be distinguished from a real timeout.
smb2.oplock.batch7 closes the first handle during the break wait, and the
second open is then expected to be granted the originally requested batch
oplock.  Return -EAGAIN from the non-lease break path when the previous
opener closed during the break wait, recheck sharing in smb_grant_oplock(),
and grant the requested oplock if the close removed the conflict. Real
break timeouts still return -ENOENT and keep the downgrade to level II.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
ba3cf6ee4f ksmbd: apply create security descriptor first
smb2.create.aclfile creates files with an SMB2_CREATE_SD_BUFFER create
context and expects the resulting security descriptor to match
the descriptor supplied by the client.

ksmbd currently tries to inherit the parent DACL first and only parses
the SMB2_CREATE_SD_BUFFER context when DACL inheritance fails.
If inheritance succeeds, the explicit security descriptor supplied on
create is ignored. This breaks create requests that include owner/group
information in the security descriptor.

Apply the create security descriptor first when the context is present.
Fall back to the existing inherited/default ACL path only when no create
security descriptor was supplied.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
3b41d8b05d ksmbd: return requested create allocation size
smb2.create.blob sends an SMB2_CREATE_ALLOCATION_SIZE create context with
a 1MiB allocation size and expects the create response AllocationSize field
to match the requested size.  smb2.create.open additionally compares the
AllocationSize returned in the CREATE response with the AllocationSize
returned by FILE_ALL_INFORMATION on the same handle.

ksmbd applies the allocation with fallocate(), but then fills both the
create response and handle-based information from stat.blocks << 9.  On
filesystems such as ext4 this can include filesystem allocation rounding
and metadata effects, causing a response larger than the SMB2 allocation
size context and a disagreement between the two queries.

Remember the requested allocation size while processing the create context,
store the reported allocation size in struct ksmbd_file, and use it for
both the create response and handle-based allocation size responses. Update
the stored value when FILE_ALLOCATION_INFORMATION changes it, and fall back
to stat.blocks << 9 when no allocation size context was provided.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
0984b1f058 ksmbd: tighten create file attribute validation
smb2.create.gentest checks each create FileAttributes bit independently and
expects FILE_ATTRIBUTE_INTEGRITY_STREAM and FILE_ATTRIBUTE_NO_SCRUB_DATA to
be rejected with STATUS_INVALID_PARAMETER.

ksmbd validates create FileAttributes against FILE_ATTRIBUTE_MASK, which
includes those bits. It also rejects only requests that have no known
attribute bit at all, so a request containing both known and unknown bits
can pass validation.

Use a create-specific attribute mask that excludes INTEGRITY_STREAM and
NO_SCRUB_DATA, and reject any bit outside that mask.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
4687da7b28 ksmbd: reject empty-attribute synchronize-only create
smb2.create.gentest checks each desired access bit independently and
expects an open that requests only SYNCHRONIZE with CreateDisposition
OPEN_IF and FileAttributes 0 to fail with STATUS_ACCESS_DENIED.

Rejecting all SYNCHRONIZE-only opens is too broad: SYNCHRONIZE does not
imply read, write, or delete data access, and
smb2.sharemode.sharemode-access expects a SYNCHRONIZE-only open to succeed
when it does not conflict with the existing share mode.

Limit the rejection to the gentest create shape: SYNCHRONIZE-only access,
OPEN_IF disposition, and no file attributes. Other synchronize-only opens
are handled by the normal permission and share-mode checks.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
19043971c9 ksmbd: honor stream delete sharing for base file
smb2.streams.delete opens an alternate data stream without
FILE_SHARE_DELETE and then tries to delete the base file.  Windows rejects
the base-file delete with STATUS_SHARING_VIOLATION while the stream handle
is open. ksmbd tracks stream opens on the same ksmbd_inode as the base
file, but the delete-on-close path only checked delete access on the base
handle before marking the inode delete-pending.  As a result, deleting
the base file succeeded even though an open stream handle denied delete
sharing. Add a helper to detect open stream handles on the same inode that
do not allow FILE_SHARE_DELETE, and reject base-file delete pending and
DELETE opens with a sharing violation in that case.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
affcd98fdd ksmbd: send pending interim for last compound I/O
smb2.compound_async.write_write and smb2.compound_async.read_read expect
the last I/O request in a compound request to become cancellable before
its final response is received. smb clients mark a request cancellable
after receiving an interim STATUS_PENDING response.

ksmbd handled the last READ/WRITE synchronously and returned the final
response directly, so the client never observed STATUS_PENDING and
req->cancel.can_cancel remained false.

For the last READ or WRITE in a compound request, register the work briefly
as async and send a STATUS_PENDING interim response before continuing with
the normal synchronous completion. The final READ/WRITE response remains
unchanged.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
c5db4de898 ksmbd: return success for deferred final close
ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table
reference. If another in-flight request still holds a reference, the final
close is deferred until that request drops its reference.

The function currently returns -EINVAL in that deferred-final-close case
because fp is cleared when the reference count does not reach zero.  That
turns a valid close into STATUS_FILE_CLOSED.

smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then
closes the same directory handle before receiving the find response.
The query holds a reference while it builds the response, so close must
mark the handle closed and return success even though final teardown is
delayed. Track whether the handle was successfully transitioned to
FP_CLOSED and return success when only the final close is deferred.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
e50a07437a ksmbd: preserve compound responses for chained errors
set_smb2_rsp_status() resets the response iov and compound offsets before
building an error response. That is fine for a single request, but it
corrupts a compound response when an error is detected after an earlier
compound element has already been completed.

smb2.compound.invalid4 sends a READ as the first compound element and a
bogus command as the second one. The READ response must remain in
the compound response with STATUS_END_OF_FILE, followed by the bogus
command response with STATUS_INVALID_PARAMETER. Resetting the response
state for the second command breaks the compound framing and the client
reports NT_STATUS_INVALID_NETWORK_RESPONSE.

When setting an error for a chained command, update and pin only
the current compound response slot instead of resetting the whole response.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
7d258465ea ksmbd: validate handle for create or get object id
FSCTL_CREATE_OR_GET_OBJECT_ID returned a dummy successful response without
checking whether the request handle was valid. That let an invalid related
compound handle succeed in smb2.compound.related5, although the client
expected STATUS_FILE_CLOSED.

Look up the file handle before building the object id response and fail
with STATUS_FILE_CLOSED when the handle is invalid or already closed.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
3f67e624e5 ksmbd: propagate failed command status in related compounds
In a related compound request, later commands can refer to the file handle
from an earlier command using the related FID value. If the earlier
command fails without producing a valid compound FID, the later related
commands must fail with the same status instead of operating on an invalid
or stale handle.

smb2.compound.related4 sends CREATE followed by IOCTL, CLOSE and SET_INFO.
The CREATE is expected to fail with STATUS_ACCESS_DENIED, and the remaining
related commands are expected to return STATUS_ACCESS_DENIED as well. ksmbd
only stored the compound FID on successful CREATE and did not remember
failed compound statuses.

Store the failed status in the work item and make related handle-based
requests fail immediately with that status only when the compound FID is
invalid. Also preserve and consume the related FID across successful
FLUSH, READ and WRITE requests whose responses do not carry a file id. Keep
a valid compound FID across non-close failures so later related commands
can continue to use the handle.

When extracting the FID from a successful READ, WRITE or FLUSH request, use
the request structure matching the SMB2 command: READ and WRITE place
PersistentFileId and VolatileFileId at a different offset than FLUSH, so a
single smb2_flush_req cast can save the wrong value as compound_fid and
make the following related request fail with STATUS_FILE_CLOSED
(smb2.compound_async.write_write after smb2.compound_async.flush_flush).
Only update the saved compound FID when the request carries a valid
volatile FID. otherwise an all-ones related FID would overwrite the CREATE
FID and break smb2.compound.related6.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
c841bd3d8d ksmbd: deny renaming directory with open children
Windows denies renaming a directory while a file below that directory is
still open. smb2.rename.rename_dir_openfile checks this by keeping a file
handle open under the directory and then attempting to rename the directory
handle.  ksmbd did not check open children before calling vfs_rename(), so
the rename incorrectly succeeded.

For non-POSIX clients, scan the global open file table for active handles
whose dentries are below the directory being renamed.  If any child is
open, fail the rename with -EACCES so the client receives
STATUS_ACCESS_DENIED.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
9a5784f4d5 ksmbd: check parent directory sharing conflicts on rename
When renaming a file, some existing opens on the parent directory must
block the rename with STATUS_SHARING_VIOLATION. This includes parent
directory handles opened with DELETE access and handles opened without
FILE_SHARE_DELETE.

ksmbd checked only the parent's desired access for FILE_DELETE.  That
handled smb2.rename.share_delete_and_delete_access, but missed the case
where the parent directory was opened without delete access and without
delete sharing, so smb2.rename.no_share_delete_no_delete_access incorrectly
succeeded.

Attribute-only parent opens, however, must not block the rename.
smb2.rename.msword opens the parent directory with only SYNCHRONIZE and
FILE_READ_ATTRIBUTES, no share access, and then renames an already-open
child file.  Windows allows this pattern.

Reject parent directory handles that request DELETE access, and reject
non-attribute-only parent opens that deny FILE_SHARE_DELETE, while allowing
attribute-only parent opens to coexist with child rename.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
24dcee8305 ksmbd: preserve open change time across rename
inode ctime is updated when a file is renamed. ksmbd returned that
ctime directly as SMB2 ChangeTime for handle-based query information.
This makes ChangeTime change after a rename through an already-open
handle, while Windows keeps the handle's ChangeTime stable for this
case.

Store the SMB ChangeTime in struct ksmbd_file when the handle is opened
and use that value for create, close, and handle-based query information
responses.  If a client explicitly sets FILE_BASIC_INFORMATION
ChangeTime, update the stored value as well.

This fixes smbtorture smb2.rename.simple_modtime, which expects
ChangeTime and LastWriteTime to remain unchanged after renaming an
already-open file.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
16c3064970 ksmbd: handle durable v2 app instance id
The SMB2_CREATE_APP_INSTANCE_ID create context is used with durable v2
opens to identify another open from the same application instance. When
a new durable v2 open arrives with the same AppInstanceId as an existing
open, the server should close the previous open without sending an
oplock break notification.

ksmbd ignored this create context. A second durable v2 batch oplock open
with the same AppInstanceId therefore went through the normal competing
open path and sent an oplock break to the first opener. smbtorture
smb2.durable-v2-open.app-instance expects no oplock break and then
expects the old handle to be closed.

Parse and store AppInstanceId for durable v2 opens. Before creating the
new open, find an existing file with the same AppInstanceId and close it
through the normal close teardown path without issuing an oplock break.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
73cd6295d0 ksmbd: fix durable reconnect context parsing
SMB2 create context DataLength describes only the create context data
payload. It does not include the create context header, name field, or
any local padding that exists in ksmbd's helper structures.

ksmbd validated durable reconnect contexts by comparing
DataOffset + DataLength against sizeof the whole helper structure. This
rejects a valid durable v2 reconnect context because the wire DH2C data
is 36 bytes while struct create_durable_handle_reconnect_v2 contains an
extra four byte pad.

Validate the durable context payload length against the corresponding
payload member instead. Also keep the reconnect context authoritative
when a later durable request context is present, matching the existing
durable v1 reconnect behavior.

This fixes smbtorture smb2.durable-v2-open.durable-v2-setinfo, where
the durable v2 reconnect after SET_INFO was rejected with
STATUS_INVALID_PARAMETER.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
26fa88dc87 ksmbd: invalidate durable handles on oplock break
When a durable handle is preserved after a disconnect, its oplock state
can still block later opens. If another client opens the same file and
the preserved oplock or lease has to be broken, the old durable handle
must no longer be reconnectable after the break cannot be acknowledged.

ksmbd was treating a missing connection, or an oplock break timeout, as a
successful break only by downgrading the oplock state.  The old durable
handle remained reconnectable, so a later durable reconnect for that
stale handle could succeed.

The open path can also see a detached durable handle before the break
notification helpers fully dispose of it. Invalidate such a preserved
durable handle directly when a competing open has to break its batch or
exclusive oplock, while leaving ordinary durable reconnects without a
competing open untouched.

If the old handle still reaches the reconnect path, reject it when the
same inode already has another active open. This matches the
smb2.durable-open.open2-lease/open2-oplock sequence where a later open
replaces the disconnected durable owner and the stale first handle must
not be reclaimed.

Also, reconnect lookup used only the persistent id. A new durable open
can get a persistent id that matches the stale reconnect request after
the old durable state is invalidated. Preserve the disconnected
handle's old volatile id and require durable reconnect contexts to match
it, so a stale reconnect cannot attach to a different durable open.

Windows allows the later open to proceed and rejects the old reconnect
with STATUS_OBJECT_NAME_NOT_FOUND. The smbtorture
smb2.durable-open.oplock test covers this case.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
166e4c0702 ksmbd: supersede disconnected delete-on-close durable handle
A durable handle opened with FILE_DELETE_ON_CLOSE is preserved across a
disconnect so it can be reclaimed by a durable reconnect.
smb2.durable-open.delete_on_close2 disconnects such a handle and then
reconnects it, expecting the reconnect to succeed.

When the client does not reconnect but instead opens the same name with a
new delete-on-close create, the preserved handle keeps the file present
with delete-on-close set. ksmbd then rejects the new open with
STATUS_ACCESS_DENIED on the file_present + FILE_DELETE_ON_CLOSE +
OPEN_IF/OVERWRITE_IF path. smb2.durable-open.delete_on_close1 expects this
open to create a fresh, empty file instead, i.e. the disconnected handle's
delete-on-close must take effect first.

Add ksmbd_close_disconnected_durable_delete_on_close(), which closes
disconnected (conn == NULL) durable handles that keep a delete-on-close
file present. The final close promotes S_DEL_ON_CLS to S_DEL_PENDING and
unlinks the file, so a re-resolved path is absent and the new open creates
it fresh. Call it from smb2_open() before the delete-on-close conflict
check, only for the conflicting open shapes. A live (connected) handle
still keeps the file and blocks the open as before.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
a04159d96c ksmbd: handle missing create contexts for lease opens
smb2_find_context_vals() assumes that callers only search create
contexts when the SMB2 CREATE request contains a non-empty create context
area. That is not always true. a client can send RequestedOplockLevel set
to SMB2_OPLOCK_LEVEL_LEASE without a lease create context.

In that case parse_lease_state() searches for a lease context and
smb2_find_context_vals() starts parsing from offset 0 with length 0,
returning -EINVAL. This makes the open fail with STATUS_INVALID_PARAMETER.
The smbtorture smb2.lease.duplicate_open test hits this while creating
a second file without a lease request.

Return NULL when the request has no create context area so the missing
context is treated the same as any other absent create context.  The open
then continues without granting a lease.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Gil Portnoy
0c05422747 ksmbd: fix use-after-free of conn->preauth_info in concurrent SMB2 NEGOTIATE
conn->preauth_info is shared connection state (struct
preauth_integrity_info, kmalloc-96) that is allocated and freed by the
SMB2 NEGOTIATE handler and read by the response send path.

smb2_handle_negotiate() allocates conn->preauth_info, and on a
deassemble_neg_contexts() failure kfrees it and sets it to NULL. Both the
allocation and the free/NULL happen under ksmbd_conn_lock(conn) (the
connection srv_mutex), which is held across the whole handler body.

The response send path smb3_preauth_hash_rsp(), called from the send:
block of __handle_ksmbd_work(), reads conn->preauth_info and dereferences
conn->preauth_info->Preauth_HashValue (via
ksmbd_gen_preauth_integrity_hash()) without taking conn_lock. When a
client drives two SMB2 NEGOTIATE requests on the same connection, one
worker can free conn->preauth_info on the failing-negotiate path while a
concurrent send-path worker is reading it, producing a slab
use-after-free read (KASAN-confirmed).

The send-path read tested conn->preauth_info for NULL but raced with the
free that occurs between the NULL check and the dereference, so the NULL
guard alone does not close the window.

Serialize the NEGOTIATE-branch read in smb3_preauth_hash_rsp() under
ksmbd_conn_lock(conn) and re-check conn->preauth_info inside the lock.
Because the negotiate handler holds conn_lock across its kfree + NULL
assignment, a reader that also takes conn_lock either runs fully before
the allocation or fully after the NULL store, and can never observe the
freed-but-not-yet-NULLed pointer. ksmbd_gen_preauth_integrity_hash()
takes no locks itself (it only computes a SHA-512 over the buffer), so
no lock-ordering inversion is introduced, and conn_lock is a sleepable
mutex which is safe on this send path (it already performs network I/O).

Fixes: aa7253c239 ("ksmbd: fix memory leak in smb2_handle_negotiate")
Signed-off-by: Gil Portnoy <dddhkts1@gmail.com>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:05 -05:00
Namjae Jeon
411398c9a1 ksmbd: keep common response iovecs in the work item
Most SMB responses need no more than four kvec entries, but every work
item currently allocates a separate four-entry array and frees it after
the response is sent.

Embed the common array in struct ksmbd_work and allocate a larger array
only when a response exceeds the inline capacity. This removes one
allocation and one free from the common request path while preserving
support for larger compound and read responses.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:04 -05:00
Namjae Jeon
2145945feb ksmbd: route v2 lease breaks on the client lease channel
v2 leases are scoped by ClientGuid.  When the same client uses multiple
connections, smbtorture expects lease break notifications to be sent on
the connection associated with the client lease table, not necessarily
on the connection that owns the individual open being broken.

Keep a referenced connection in the lease table and use it for v2 lease
break notifications while it is still active.  Fall back to the open's
connection if the table connection is being released.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:04 -05:00
Namjae Jeon
1f1083c36f ksmbd: break RH leases before delete-on-close
The delete paths only marked the opened file delete pending or
delete-on-close.  When another client still held a read/handle lease, no
lease break was sent before the delete state changed.

smb2.lease.unlink uses a create request with FILE_DELETE_ON_CLOSE and
expects the second client's unlink to break the first client's RH lease to
R with ACK_REQUIRED set.  SetInfo(FileDispositionInformation) has the same
lease-breaking requirement.

Break level-II/read-handle leases before setting delete pending or
delete-on-close so clients are notified before the file is removed.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:04 -05:00
Namjae Jeon
dc2264a9ef ksmbd: honor SMB2 v2 lease epochs
v2 lease responses should continue from the client supplied epoch.
Initialize a new v2 lease from the requested epoch plus one so create
responses match the epoch returned by Windows and expected by smbtorture.

For a single chained break sequence, increment the epoch only for the first
break notification. Follow-up breaks such as RH->R and R->NONE in
smb2.lease.v2_breaking3 reuse the same epoch.

Record when a waiter slept behind pending_break and let the later
truncate/open overwrite break consume that marker to reuse the current
epoch instead of assigning a new one.

Do not increment the epoch when a same-client, same-key create asks for
the already granted RH state. The epoch changes only when the granted lease
state changes.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:04 -05:00
Namjae Jeon
752bbd323e ksmbd: do not wait for RH lease break ack on overwrite
smb2.lease.breaking4 expects an overwrite against an RH lease to send
RH->NONE lease break notification but complete the triggering create
without waiting for the break ack.

Keep the lease in break-in-progress state until the client eventually
acknowledges the downgrade, but do not hold the overwrite request behind
that ack.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:04 -05:00
Namjae Jeon
7efb3f2458 ksmbd: chain pending lease breaks before waking waiters
A pending open can require more than one lease break before the existing
lease becomes compatible with the operation that triggered the break.
smb2.lease.breaking3 expects the server to hold the pending normal open
through RWH->RH and RH->R, while a later overwrite waiter must not
collapse that second break directly to RH->NONE.

Keep pending_break held for lease breaks until the current triggering
operation is compatible with the lease state. Snapshot the truncate request
per oplock_break() call so another waiter cannot overwrite the state of
the active break.

Use the requested oplock level when deciding whether to chain another
break. A second lease open only needs RWH->RH, while a normal none-oplock
open can continue down to R and then NONE.

For non-truncating metadata operations, break leases only down to read
caching. Operations such as delete-on-close need to drop handle caching,
but should not send a second R->NONE break after the client acknowledges
RH->R.

Also send STATUS_PENDING for levelII/read-lease break waiters. An async
SMB2 create becomes cancelable only after the server sends
an NT_STATUS_PENDING interim response. A waiter that blocks behind an
already active lease break must receive the interim response before
sleeping on pending_break, otherwise the client can process a later lease
break while the create request is still not marked pending.

Avoid duplicate interim responses when an overwrite first breaks a write
oplock and then scans levelII/read leases.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
2026-06-22 20:15:04 -05:00