| Age | Commit message (Collapse) | Author | Files | Lines |
|
Deferred CHANGE_NOTIFY work keeps an async message ID after the original
request work is released. A durable handle can outlive its connection, so
the connection teardown can destroy its async IDA before the handle close
releases the pending notify work.
Give the synthetic deferred work a connection reference. Release it after
the async ID in ksmbd_free_work_struct(). This keeps the async IDA alive
until the deferred work is released, even when the original connection has
already left the connection list.
During server shutdown there is no client to receive a cleanup response.
Skip the write and only release the pending work.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
An existing file can be opened with a truncating create request that
supplies FileAttributes. Do not reset its cached DOS attributes while
opening it. After a successful truncation, apply the requested attributes
and store them in the DOS attribute xattr.
This preserves READONLY when a truncating open requests that attribute.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_CREATE_OR_GET_OBJECT_ID requires a fixed-size output buffer, but an
invalid file handle must take precedence over output buffer validation.
Look up the handle before checking the available response buffer size. This
returns STATUS_FILE_CLOSED for a closed handle while preserving the buffer
size validation for valid handles.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
A base file that has been marked for deletion remains present while stream
handles are open. Name-based opens of either the base file or one of its
streams must return STATUS_DELETE_PENDING during that interval.
ksmbd_inode_pending_delete() returned only the per-handle stream state for
stream handles. It therefore skipped the inode-wide S_DEL_PENDING state set
by the base file delete-on-close path. As a result, a new stream open
incorrectly succeeded.
Check the inode-wide pending-delete state first for every handle. Only when
the base file is not pending, check the per-handle stream state.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Break read and handle caching leases before entering the VFS rename path.
This keeps the destination name hidden until the lease holder acknowledges
the break.
Send the break synchronously before returning STATUS_PENDING for a rename.
This avoids a race between the interim response and notification handling.
Keep the existing asynchronous notification flow for all other lease break
paths so chained breaks retain their ordering.
Use the connection which owns the open for the notification. A lease table
is shared by connections using the same client GUID. Its saved connection
may belong to another active channel. Use it only when the owning channel
is being released.
Check directory sharing before issuing a break to avoid unnecessary lease
breaks for a rename that must fail with a sharing violation.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
RH leases map to SMB2_OPLOCK_LEVEL_II because they do not include write
caching. smb_grant_oplock() only sent break notifications for previous
BATCH or EXCLUSIVE levels, so a conflicting open could skip the lease
break when the existing lease was RH.
That leaves the opener to fail or complete without the expected pending
lease break sequence, instead of first asking the holder to drop handle
caching.
Treat share-mode conflicts against leases with HANDLE_CACHING as needing
a break even when the mapped oplock level is LEVEL_II. This lets the
server send the RH -> R lease break and wait for the normal break
handling before continuing the conflicting open.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_QUERY_NETWORK_INTERFACE_INFO is not tied to an open file handle.
Clients send SMB2_NO_FID for both file id fields when issuing this
request.
Reject requests that provide any other file id before checking the
output buffer size. This returns STATUS_INVALID_PARAMETER for invalid
file ids instead of treating the request as valid or reporting
STATUS_BUFFER_TOO_SMALL for a small output buffer.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_DUPLICATE_EXTENTS_TO_FILE currently returns STATUS_NOT_SUPPORTED
when vfs_clone_file_range() cannot clone the requested range. That can
happen on filesystems without reflink support even though the server can
still satisfy the request by copying the bytes.
Validate the requested source range before attempting the operation. If
the destination range extends past EOF, leave the destination size
unchanged and complete the request without copying, matching observed
client expectations for this ioctl.
Reject sparse source to non-sparse destination requests as unsupported.
Keep sparse destination and sparse-to-sparse cases on the normal clone
or copy path.
Reject overlapping same-file ranges as unsupported before attempting
the clone or copy operation.
Return the expected handle status for invalid handles. A closed target
handle fails with STATUS_FILE_CLOSED, while a bad source handle embedded
in the request buffer fails with STATUS_INVALID_HANDLE.
Fall back to vfs_copy_file_range() whenever the clone operation does not
copy the full requested length, and keep reporting an error only if the
fallback also fails or copies a partial range.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Advertise trim support through FS_SECTOR_SIZE_INFORMATION and handle
FSCTL_FILE_LEVEL_TRIM requests.
Process each trim range by punching a hole while keeping the file size
unchanged, and report the number of ranges completed in the ioctl
response. The trim operation uses the same byte-range lock handling as
zero data for the affected part of the file.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_SET_ZERO_DATA changes file allocation state and must respect
byte-range locks over the affected part of the file. Check the requested
range, clipped to EOF, before issuing the fallocate operation.
Return STATUS_FILE_LOCK_CONFLICT when the range conflicts with an
existing lock. Ranges starting past EOF are left untouched by the lock
check so they continue to succeed.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_SET_SPARSE should not require FILE_WRITE_ATTRIBUTES only. A handle
with FILE_WRITE_DATA or FILE_APPEND_DATA is also allowed to set the file
allocation state, while FILE_WRITE_EA alone must still be rejected.
FSCTL_QUERY_ALLOCATED_RANGES needs FILE_READ_DATA access. Reject handles
that only have metadata access such as FILE_READ_ATTRIBUTES.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_QUERY_ALLOCATED_RANGES currently relies on SEEK_DATA and SEEK_HOLE
for every file. That works for files with holes, but it is not a good
match for dense files.
A zeroed range in a dense file may be represented as an unwritten extent
and skipped by SEEK_DATA. The server can then return no allocated ranges
even though the file should still be treated as allocated from the
protocol point of view.
For dense files, report the requested range clipped to EOF as allocated
instead of probing holes. Keep using SEEK_DATA and SEEK_HOLE for files
marked with FILE_ATTRIBUTE_SPARSE_FILE, and wait for writeback before
probing so punch-hole updates are visible to the filesystem seek
implementation.
This fixes the case where a query after zeroing data could return no
ranges for a dense file.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
silenced __rpc_method()'s own "Unsupported RPC: mdssvc" log line
but missed that ksmbd_session_rpc_open() failing for that same,
now-still-rejected pipe also trips a second, separate pr_err() here in
its caller. macOS's routine mdssvc (Spotlight) probes still spam the
kernel log via this second site on every single probe, defeating the
original commit's stated purpose.
Suppress this specific case the same way the other site does; behavior
is unchanged for every other RPC failure.
__rpc_method() (mgmt/user_session.c) returns -ENOENT for mdssvc, not
-EINVAL.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
get_file_stream_info() (FileStreamInformation QUERY_INFO) reported
each enumerated stream's StreamSize/StreamAllocationSize as
stream_name_len -- the byte length of the stream's *name*, not its
data. This is the same bug class already fixed for EndOfFile/
AllocationSize on an open stream handle (ksmbd_stream_eof()), just
missed at this second site: a client enumerating streams sees a size
derived from the name string length instead of the stream's actual
content length, inconsistent with what querying the same stream by
handle reports.
Compute the real value length the same way ksmbd_stream_eof() does,
via ksmbd_vfs_casexattr_len() on the already-known xattr key.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Extends the existing V1 inline-FinderInfo mechanism
(SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) with the V2 variant:
byte-identical layout otherwise, except the ShortNameLength+Reserved
bytes (ignored outright by V1 clients) become a single flags field that
V2 clients actually interpret.
Negotiation: when a client's own client_caps requests V2
(SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR_V2), advertise V2 instead of
V1 in the server's own server_caps reply -- they're mutually exclusive
on the wire, not both set together.
Wire format: the only currently-defined V2 flag,
AAPL_READDIR_ATTR_V2_NO_XATTR, signals that an item has no
xattrs/streams so the client can skip a separate query. Compute this
per-entry in ksmbd_vfs_fill_dentry_attrs() by checking for any xattr
under the XATTR_NAME_STREAM ("user.DosStream.") prefix -- a reliable,
distinct marker for genuine ADS/stream xattrs, unlike DOSATTRIB or ACL
xattrs which live under different prefixes, so this can't
false-positive into telling Finder a file has no extra data when it
actually does. Only computed when a V2 connection is active, to avoid
the extra listxattr() call otherwise.
V1's fixed ShortNameLength=24 convention (real macOS clients ignore
the value outright per the same client source, so it's cosmetic
parity with other real servers, not a functional requirement) is kept
V1-only rather than reused as a V2 base value -- V2 clients do
interpret this field, so it needs a clean 0-or-flag value, not a
leftover V1 constant that happens not to collide with the one defined
flag bit today.
Confirmed via live diagnostics that macOS actually negotiates and
uses V2 (client_caps bit 0x10 set) rather than falling back to V1 or
ignoring the capability.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
fsctl_copychunk() treats FSCTL_SRV_COPYCHUNK with ChunkCount=0 as the
standard SMB2 "query my copy limits, don't copy anything" request and
returns success without ever looking up the file handles. That's
correct for compliant SMB2 clients, but macOS Finder's Cmd+D duplicate
sends ChunkCount=0 expecting the server to copy the whole file/stream
-- so duplicated files are left at their just-created 0 bytes while
the client reports success.
Scope the full-copy fallback to AAPL-negotiated connections on a
Time Machine share (conn->is_aapl && KSMBD_SHARE_FLAG_TIME_MACHINE)
only, so standard non-AAPL SMB2 clients, and AAPL-negotiated clients
on ordinary shares, keep the spec-correct query-limits behavior
unchanged.
both streams and regular files now share a single chunk_count == 0
fast path added right after src_file_size is computed, reusing the
same buffered-copy helper and vfs_copy_file_range()/COPY_FILE_SPLICE
fallback the existing per-chunk loop already uses, rather than the
separate xattr-specific get/setxattr path this used before that
rework.
ChunksWritten/ChunkBytesWritten are 0 in the response: this is a
synthesized whole-file copy, not a response to any chunk descriptor
the client actually sent (it sent none), so there's no real chunk to
report the count/size of. Only TotalBytesWritten is meaningful here.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
smb2_notify() currently returns STATUS_NOT_IMPLEMENTED synchronously
for every CHANGE_NOTIFY request. Genuine SMB2 servers never complete a
CHANGE_NOTIFY spontaneously -- it's satisfied only by a real directory
change or with STATUS_NOTIFY_CLEANUP when the watched handle is
closed. macOS smbfs.kext depends on this deferred-completion contract:
receiving STATUS_NOT_IMPLEMENTED instead makes it hard-freeze on
unmount, since it never sees the cleanup it's waiting for.
Add a notify_pendings list on struct ksmbd_file (protected by the
existing f_lock) and a notify_entry list_head on struct ksmbd_work to
link onto it. smb2_notify() now replies STATUS_PENDING immediately and
queues a deferred STATUS_NOTIFY_CLEANUP response on the watched
handle; __ksmbd_close_fd() drains and sends any pending notifications
when the handle is actually closed. The drain splices the list out
under fp->f_lock first, then processes the detached copy without the
lock -- smb2_notify() on another connection can be adding to the same
list at the same time a close happens on this one, and
ksmbd_conn_write() can sleep (it takes the connection's write mutex),
so it must not be called while the spinlock is held.
Also handle the FileId=FFFF...FFFF share-root sentinel that macOS
backupd sends to watch for changes without holding an open handle --
without an immediate STATUS_PENDING/STATUS_NOTIFY_CLEANUP reply here,
backupd aborts Time Machine setup with STATUS_FILE_CLOSED.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Without READDIR_ATTR, macOS Finder resolves type/creator/icon for
every file in a directory listing by opening its AFP_AfpInfo stream
individually -- one extra CREATE+QUERY_INFO+CLOSE round trip per file,
which is the dominant cost of browsing a large directory over SMB from
a Mac.
When the client negotiates READDIR_ATTR (conn->aapl_readdir_attr, set
during CREATE's AAPL context exchange), inline the same information
directly into each FILEID_BOTH_DIRECTORY_INFORMATION FIND entry:
EaSize = max_access, expanded specific rights
(GENERIC_ALL_FLAGS), not the raw FILE_GENERIC_ALL_LE
"generic" meta-bit -- that bit has none of the
specific FILE_* rights macOS's smbfs.kext checks
bit-by-bit, so reporting it directly would fail
every access check and show Finder's "no entry"
badge on every file/folder.
ShortNameLength = 24 (fixed; the spec says 0 when there's no short
name; kept for wire parity with reference server,
see below)
ShortName[0..7] = resource fork size (0 -- no resource forks)
ShortName[8..23] = compressed FinderInfo (all zero: type/creator
unset, client falls back to extension-based
icon/type detection, consistent with the
AFP_AfpInfo synthesis this mirrors)
Reserved2 = Unix mode bits
Reparse-point status is still carried via ExtFileAttributes rather
than EaSize once READDIR_ATTR is active, since EaSize is repurposed
for max_access.
Reverse-engineered from macOS smbfs.kext network behavior and cross-checked
against reference implementation marshalling (reference implementation
behavior). Also confirmed against AAPL's published public client behavior
(public client behavior reference) -- every field here matches exactly,
except ShortNameLength=24: real V1 clients read but never examine that
field, so it's kept for wire parity with reference server, not because
macOS requires it.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Once a server advertises the AAPL COPYFILE capability, macOS requires
an AFP_AfpInfo stream on every file it looks at for Finder type/
creator/icon resolution. smb2_set_stream_name_xattr() currently
returns -EBADF (STATUS_OBJECT_NAME_NOT_FOUND) when a client opens
AFP_AfpInfo with FILE_OPEN disposition and the xattr doesn't exist
yet, which macOS treats as fatal for that file: Finder falls back to
showing a generic icon, and file operations that depend on succeeding
against this stream (e.g. Cmd+D duplication) fail.
Synthesize a 60-byte zeroed AFP_AfpInfo xattr (magic 0x00051607,
version 0x00020000, both big-endian per the AFP_AfpInfo wire format)
on first FILE_OPEN probe instead. type=0/creator=0 tells macOS to fall
back to extension-based type detection, which is correct for files with
no explicit Finder metadata. The synthesized xattr persists on disk, so
this only pays the extra write once per file; a later genuine write from
macOS (e.g. after the user assigns a custom icon) overwrites it normally.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
macOS clients (Finder, and specifically Time Machine's backupd) send
an "AAPL" SMB2 create context on CREATE to negotiate AAPL-specific
server capabilities (server_caps/vol_caps/model string). Without a
response to this context, macOS Time Machine over SMB does not work
at all.
Add the AAPL create context structs (create_aapl_rsp,
aapl_server_query_req) and create_aapl_rsp_buf(), which builds the
kAAPL_SERVER_QUERY response mirroring the layout observed from macOS's
own smbd, including the model string workaround: omitting the model
string when the client requested it causes smbfs.kext to enter a
broken disconnect path requiring a full macOS reboot to recover from.
Command codes and bitmap values reuse the existing SMB2_CRTCTX_AAPL_*
constants in fs/smb/common/smb2pdu.h.
Wire format confirmed against AAPL's published public client kernel
source (public client behavior reference) -- every field
here and every SMB2_CRTCTX_AAPL_* constant matches exactly.
Hook the request parsing and response into smb2_open()'s existing
create-context handling, following the same DataOffset+DataLength
bounds-checking convention already used by every other context parser
in this file. The AAPL model string is configurable via the existing
netlink startup path (server_conf.aapl_model, default "Xserve").
This is scoped to shares with the new KSMBD_SHARE_FLAG_TIME_MACHINE
flag only, not enabled globally -- AAPL's AAPL extension is
undocumented, so containing its blast radius to shares that explicitly
opt in limits risk to ordinary SMB shares.
conn->aapl_readdir_attr is set here when the client also advertises
READDIR_ATTR support, but the actual inline-FinderInfo wire format
(the feature that flag gates) is not implemented yet -- follow-up
commit.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Free it unconditionally after ksmbd_alloc_user() calls.
kmemleak splat:
unreferenced object 0xffff888103b83540 (size 192):
comm "pool-0", pid 16970, jiffies 4377290937
hex dump (first 32 bytes):
00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
backtrace (crc 408ccc66):
__kvmalloc_node_noprof+0x730/0x920
handle_generic_event+0xec/0x1a0 [ksmbd]
genl_family_rcv_msg_doit+0xe0/0x130
genl_rcv_msg+0x181/0x290
netlink_rcv_skb+0x4f/0x100
genl_rcv+0x28/0x40
netlink_unicast+0x1e6/0x2c0
netlink_sendmsg+0x20a/0x450
____sys_sendmsg+0x2e8/0x310
___sys_sendmsg+0x78/0xc0
__sys_sendmsg+0x63/0xc0
do_syscall_64+0xa1/0x670
entry_SYSCALL_64_after_hwframe+0x76/0x7e
Fixes: a77e0e02af1c ("ksmbd: add support for supplementary groups")
Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
smb2_open() calls vfs_fallocate(fp->filp, ...) unconditionally when a
client's CREATE request includes an AllocationSize create context.
For a stream handle, fp->filp refers to the base file's data fork
(streams are xattr-backed on the same underlying file, not separate
files), so this pre-allocates storage on the base file's actual data
instead of doing anything meaningful for the stream -- fallocate has
no applicability to an xattr-backed stream at all.
Skip the fallocate call for stream handles.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
set_file_disposition_info() checks S_ISDIR(inode->i_mode) &&
ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY to refuse deleting a non-empty
directory. A stream handle's fp->filp refers to the same underlying
inode as its base file or directory (streams are xattr-backed on that
same inode), so this check also fires when the target is actually a
stream attached to a directory, not the directory itself -- deleting
the stream then incorrectly fails with -EBUSY whenever the directory
happens to be non-empty, even though removing an xattr has nothing to
do with the directory's contents.
Skip the directory-emptiness check for stream handles, matching how
ksmbd_stream_fd() is already used elsewhere in this function.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
ndr_decode_dos_attr() only populates da->itime for version-4 DOS
attribute xattrs; for version 3 it's skipped entirely (only
da->create_time is set). smb2_update_xattrs() declared da without
initializing it, so fp->itime = da.itime unconditionally copies
whatever was on the kernel stack for any file carrying a version-3
xattr (e.g. written by an older client or server) -- uninitialized
stack memory that can later be exposed to a client via QUERY_INFO.
Zero-initialize da at declaration, matching the pattern
fsctl_set_sparse() already uses in this same file.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_GET_REPARSE_POINT writes
Both cases write a fixed-size response structure into rsp->Buffer
without first checking that out_buf_len (the space smb2_ioctl()
actually has available, computed by smb2_calc_max_out_buf_len() from
the client's OutputBufferLength minus space already consumed earlier
in a compound request) is large enough. Every comparable case in this
same switch (FSCTL_SRV_ENUMERATE_SNAPSHOTS, FSCTL_GET_COMPRESSION,
FSCTL_VALIDATE_NEGOTIATE_INFO, FSCTL_SRV_REQUEST_RESUME_KEY,
FSCTL_SRV_COPYCHUNK) validates this first; these two don't.
A client can send a compound SMB2 request where an earlier command in
the same compound chain consumes most of work->response_buf, leaving
smb2_calc_max_out_buf_len() only a few bytes of out_buf_len for a
trailing FSCTL_CREATE_OR_GET_OBJECT_ID or FSCTL_GET_REPARSE_POINT.
Both then unconditionally write their full fixed-size structure
(64 bytes and 8 bytes respectively) at rsp->Buffer[0] regardless,
overflowing past the actual remaining space in the response buffer.
Add the same out_buf_len check used by every other fixed-size-response
case in this function, before the write.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
When a client's Durable Handle Request V2 sets Timeout=0 ("let the
server choose"), fp->durable_timeout was set to 60. Every other use
of this field is in milliseconds: DURABLE_HANDLE_MAX_TIMEOUT (300000)
in smb2pdu.h, the nonzero branch immediately above
(min_t(unsigned int, dh_info.timeout, DURABLE_HANDLE_MAX_TIMEOUT),
where dh_info.timeout is the wire value and already milliseconds per
spec), and the scavenger in vfs_cache.c, which adds it directly to
jiffies_to_msecs(jiffies).
60 is off by 1000x: the handle becomes scavenger-eligible 60
milliseconds after close instead of 60 seconds. A client requesting
Timeout=0 is relying entirely on the server's default to cover the
gap between a dropped connection and its reconnect -- 60ms is not
enough time for even a fast network blip to be detected and
reconnected, so any real disruption loses the race and a subsequent
DH2C reconnect fails with a durable-handle lookup miss instead of
succeeding.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
ksmbd_fd_set_delete_pending()/ksmbd_fd_clear_delete_pending()
to keep a stream's FileDispositionInformation from marking the whole
file for deletion, but used the inode-wide S_DEL_ON_CLS_STREAM flag to
do it -- the exact same problem class the commit was fixing, one level
up.
S_DEL_ON_CLS_STREAM lives on the shared ksmbd_inode, not on any
specific stream handle. If a file has multiple stream handles open and
one gets marked delete-pending via FileDispositionInformation, the
flag can't record *which* stream should be deleted: whichever stream
handle happens to close first (not necessarily the one that was
actually marked) sees S_DEL_ON_CLS_STREAM set and has its xattr
removed. Two clients (or two handles from the same client) touching
different streams on the same file can end up deleting the wrong one.
ksmbd_inode_pending_delete() has the same issue: it only checks
S_DEL_PENDING, which is never set for a stream handle, so a client
querying FileStandardInformation.DeletePending on a stream marked via
this path would incorrectly see 0.
Track this per-handle instead (stream_del_pending on struct
ksmbd_file), matching the file itself rather than the shared inode.
ksmbd_fd_set_delete_on_close() (the CREATE-time FILE_DELETE_ON_CLOSE
option, a separate call path from FileDispositionInformation) still
uses the inode-wide flag; __ksmbd_inode_close() now checks both, since
either one should trigger removing the stream's xattr on close.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_QUERY_ALLOCATED_RANGES can be issued with a valid input buffer but
without room for an output range. Do not reject the request before
looking at the file layout. If the query would produce a range, return
STATUS_BUFFER_TOO_SMALL. If it produces no ranges, return success with an
empty output.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_SET_SPARSE without an input buffer sets a file sparse. Treat a
zero-length input buffer as SetSparse=true and keep rejecting truncated
non-empty buffers.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_SET_SPARSE applies to files. Return STATUS_INVALID_PARAMETER when a
client sends it for a directory handle instead of setting the sparse file
attribute on the directory.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Advertise FILE_SUPPORTS_SPARSE_FILES so clients can use FSCTL_SET_SPARSE.
Do not mark regular files sparse just because sparse support is advertised;
FILE_ATTRIBUTE_SPARSE_FILE should reflect the state set by
FSCTL_SET_SPARSE.
Persist the sparse attribute in the DOS attribute xattr regardless of
the store dos attributes setting. Restore the sparse and compressed bits
from that xattr when only those emulated attributes need to be preserved.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
The compression state can be emulated with the DOS attribute xattr when the
backing filesystem cannot store it directly. Do not limit that state to
shares with store dos attributes enabled, otherwise a file reopened through
another handle can lose FILE_ATTRIBUTE_COMPRESSED in file information
responses.
Load only the compressed bit from the DOS attribute xattr when store dos
attributes is disabled. Keep the normal DOS attribute behavior unchanged
when it is enabled. Also avoid clearing an already restored compressed
bit just because the backing filesystem does not report FS_COMPR_FL.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FILE_ATTRIBUTE_COMPRESSED is controlled by FSCTL_SET_COMPRESSION and should
not be set directly through FileBasicInformation. Keep the existing
compression state when updating basic attributes and ignore the compressed
bit supplied by the client in the basic information request.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Advertise file compression support and keep the compression state in the
per-file DOS attributes when the backing filesystem cannot apply the
compression flag directly. FSCTL_SET_COMPRESSION should still update the
state returned by FSCTL_GET_COMPRESSION and file compression information in
that case.
When a new object is created under a compressed directory, inherit the
compression attribute from the parent. If FILE_NO_COMPRESSION is
specified, clear the compression state after creation and let it override
inheritance for both files and directories.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Two kernel_sendmsg() calls can still use the same TCP skb if the first skb
can take more data. This can happen when ksmbd sends two SMB2 responses
very close to each other.
Without MSG_EOR, TCP can append the next sendmsg data to the previous skb.
Then STATUS_PENDING and the later response can be put into the same TCP
skb. MSG_EOR marks the skb as end of record, so TCP will not collapse the
next sendmsg data into it.
Example:
smbtorture //${server_ip}/export -U${username}%${password} smb2.compound_async.write_write
Client request:
Write Request Len:64 Off:0, File: compound_async_write_write; Write Request Len:64 Off:64
Before this patch, server responses:
Write Response, File: compound_async_write_write
Write Response
SMB2, STATUS_PENDING, Write Response, MessageId 7
SMB2, Write Response, MessageId 7
After this patch:
Write Response, File: compound_async_write_write
Write Response, Error: STATUS_PENDING
Write Response
Signed-off-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Put the arguments of ksmbd_transport_ops ->writev() into a struct.
This makes the function call shorter and easier to read.
Add __ksmbd_conn_write() for the common write code. A later patch will use
it for another write helper.
No functional change.
Signed-off-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
When the last request in a compound request becomes async, ksmbd sends a
STATUS_PENDING response for it. But the responses for previous requests in
the same compound request are still kept in the same response buffer.
Send these previous responses first. Clear NextCommand for the last
response in this part, sign it again if needed, and reset the iov state.
After that, the async request sends STATUS_PENDING first, and sends the
real response later. Both are separate responses.
Example:
smbtorture //${server_ip}/export -U${username}%${password} smb2.compound_async.write_write
Client request:
Write Request Len:64 Off:0, File: compound_async_write_write; Write Request Len:64 Off:64
Before this patch, STATUS_PENDING Write Response is the first of
several responses:
Write Response, Error: STATUS_PENDING
Write Response, File: compound_async_write_write; Write Response
But STATUS_PENDING Write Response should be in the middle of several
responses, after this patch:
Write Response, File: compound_async_write_write
Write Response
SMB2, STATUS_PENDING, Write Response, MessageId 7
SMB2, Write Response, MessageId 7
Signed-off-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Unknown RPC pipe names and malformed CREATE parameters both use
-EINVAL. Mapping that errno to STATUS_OBJECT_NAME_NOT_FOUND therefore
also hides invalid request parameters as a missing pipe.
Return -ENOENT when RPC method lookup cannot find a supported pipe and
map only that error to STATUS_OBJECT_NAME_NOT_FOUND. Preserve
STATUS_INVALID_PARAMETER for -EINVAL returned by request validation.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
smb2_update_xattrs() copies the DOS SPARSE attribute bit verbatim from
the stored xattr into the in-memory file attributes, without checking
whether the share is currently advertising FILE_SUPPORTS_SPARSE_FILES.
A file whose xattr has a stale SPARSE bit (set by a previous client,
or from before the share was reconfigured) would keep reporting as
sparse even after sparse-file support is turned off for the share.
This matters for Time Machine: sparsebundle band files rely on
accurate sparse-file status being reported, since macOS decides
whether to issue FSCTL_SET_SPARSE based on it. Mask the bit out when
the share doesn't currently advertise sparse-file support.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
macOS routinely probes the mdssvc RPC pipe to check for Spotlight
search support. __rpc_method() already falls through to returning 0
(unsupported) for it via the default case, but that path also logs
"Unsupported RPC: mdssvc" via pr_err on every single probe -- which
happens often enough during normal macOS browsing/backup activity to
spam the kernel log. Add an explicit case that returns the same value
without the log line; behavior is unchanged, this only removes noise
for an expected, routine client behavior.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
create_smb2_pipe() maps ksmbd_session_rpc_open() failing with -EINVAL
(pipe name not recognized/supported) to STATUS_INVALID_PARAMETER.
macOS Time Machine's backupd treats STATUS_INVALID_PARAMETER on a
pipe open as a fatal error and aborts the backup immediately, whereas
STATUS_OBJECT_NAME_NOT_FOUND is handled gracefully -- the client just
treats that particular pipe as unavailable and continues.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
fp->stream.size holds the byte length of the mangled xattr *name*
string (it's used as the attr_name_len argument when looking up the
xattr), not the size of the stream's actual data. CREATE and every
QUERY_INFO handler that reports EndOfFile/AllocationSize for a stream
handle used fp->stream.size directly, so clients received a bogus
size derived from the internal xattr key name length instead of the
stream's real content length.
Add ksmbd_stream_eof() to query the xattr's actual value length via
ksmbd_vfs_casexattr_len(), and use it at every site that reports a
stream handle's size: the CREATE response, get_file_standard_info(),
get_file_all_info(), get_file_network_open_info(), and
find_file_posix_info().
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
set_file_disposition_info() calls ksmbd_set_inode_pending_delete() /
ksmbd_clear_inode_pending_delete() unconditionally, which always sets
S_DEL_PENDING on the whole inode (ci->m_flags), regardless of whether
the handle being closed is a regular file or an alternate data stream.
Requesting delete-pending on a single stream handle (e.g. deleting
just an alternate data stream some clients keep alongside a file)
would therefore incorrectly schedule deletion of the entire file's
data, not just the stream.
Add ksmbd_fd_set_delete_pending()/ksmbd_fd_clear_delete_pending(),
following the same stream-vs-whole-file routing pattern already used
by ksmbd_fd_set_delete_on_close() for the CREATE-time DeleteOnClose
option, and switch set_file_disposition_info() to use them.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
The FSCTL_COPYCHUNK/FSCTL_COPYCHUNK_WRITE input length check uses
in_buf_len <= sizeof(struct copychunk_ioctl_req), which rejects a
buffer that is exactly sizeof(struct copychunk_ioctl_req) bytes -- the
minimal, valid request containing only the fixed header with
ChunkCount=0 and no chunk entries, used by clients to query the
server's copy limits before issuing a real copychunk.
Since copychunk_ioctl_req ends in a flexible array member, the correct
minimum is that the buffer covers the fixed header, so use
offsetof(..., Chunks) with '<' instead of '<=' against sizeof(): same
value, but the boundary case is now correctly accepted.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
macOS can reuse the main file's chunk list when issuing a copychunk
request for alternate data streams. The requested source range can
therefore exceed the length of the xattr-backed stream and currently
fails with STATUS_INVALID_VIEW_SIZE.
For AAPL connections copying between two streams, limit the actual copy
to the available source data while reporting the requested chunk length
as written. Keep the source range validation unchanged for non-AAPL
connections and requests involving a regular file.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Copychunk rejects requests when either handle refers to an alternate
data stream. These streams are stored in extended attributes and cannot
be passed directly to vfs_copy_file_range().
Use the bounded buffered copy path when a source or destination is a
stream. Obtain the source length from the stream extended attribute and
perform I/O through the existing stream-aware read and write helpers.
Keep vfs_copy_file_range() and its fallback for regular files only.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
The copychunk error mapping handles -EACCES in an independent if
statement. The following error chain therefore reaches its final else
clause and overwrites STATUS_ACCESS_DENIED with
STATUS_UNEXPECTED_IO_ERROR.
Join the -EACCES check to the remaining error chain so an access failure
is returned as STATUS_ACCESS_DENIED.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Copying an overlapping range within the same file through
do_splice_direct() can overwrite source data that has not yet been
read. This corrupts the destination when the target range starts
inside and after the source range.
Handle overlapping ranges with a bounded temporary buffer. Copy from
the end when the destination follows the source and from the beginning
otherwise, providing memmove semantics without allocating the entire
copy length.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
The FSCTL_SRV_REQUEST_RESUME_KEY response contains a mandatory
four-byte context field after ContextLength.
Defining the context as a flexible array excludes it from
sizeof(struct resume_key_ioctl_rsp), so ksmbd sends only 28 bytes
instead of the required 32 bytes. The truncated response cannot be
decoded and results in an NDR buffer size error.
Define the reserved context as a fixed four-byte field. This makes the
response size match the wire format and ensures the field is zeroed and
included in OutputCount.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_SRV_ENUM_SNAPS is currently unimplemented, causing clients to
treat shadow-copy enumeration as unsupported even when the share simply
has no snapshots.
Handle the count-only SRV_SNAPSHOT_ARRAY request and return a valid
empty snapshot list after validating the file handle and minimum output
buffer size. Report a two-byte empty UTF-16 MULTI_SZ array and zero
snapshot counts.
This allows smb2.ioctl.shadow_copy to run without a snapshot backend.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|