From: David Disseldorp <ddiss@samba.org>
To: David Howells via samba-technical <samba-technical@lists.samba.org>
Cc: David Howells <dhowells@redhat.com>,
Steve French <sfrench@samba.org>, Jeremy Allison <jra@samba.org>,
linux-cifs@vger.kernel.org, Paulo Alcantara <pc@manguebit.com>
Subject: Re: Bug in Samba's implementation of FSCTL_QUERY_ALLOCATED_RANGES?
Date: Wed, 22 May 2024 18:53:05 +1000 [thread overview]
Message-ID: <20240522185305.69e04dab@echidna> (raw)
In-Reply-To: <349671.1716335639@warthog.procyon.org.uk>
Hi David,
On Wed, 22 May 2024 00:53:59 +0100, David Howells via samba-technical wrote:
> I've been debugging a failure in xfstests generic/286 whereby llseek() return
> -EIO and am thinking that the bug is in Samba. The test can be distilled to:
>
> mount //my/share /mnt -ooptions
> truncate -s 100M /mnt/a
> for i in $(seq 0 5 100); do xfs_io -c "w ${i}M 1M" /mnt/a; done
> xfstests-dev/src/seek_copy_test /mnt/a /mnt/b
Thanks for providing the reproducer and detailed analysis...
> This creates a big sparse file, makes 21 1MiB extents at 5MiB intervals and
> then tries to copy them one at a time, using SEEK_DATA/SEEK_HOLE to enumerate
> each extent.
>
> I can see the error in the protocol being returned from the server:
>
> 16 2.353013398 192.168.6.2 → 192.168.6.1 SMB2 206 Ioctl Request FSCTL_QUERY_ALLOCATED_RANGES File: a
> 17 2.353220936 192.168.6.1 → 192.168.6.2 SMB2 143 Ioctl Response, Error: STATUS_BUFFER_TOO_SMALL
>
> Stracing Samba shows:
>
> lseek(30, 94371840, SEEK_HOLE) = 95420416
> lseek(30, 95420416, SEEK_DATA) = 99614720
> lseek(30, 99614720, SEEK_HOLE) = 100663296
> lseek(30, 100663296, SEEK_DATA) = 104857600
> lseek(30, 104857600, SEEK_HOLE) = 105906176
> sendmsg(35, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\0\0\0I", iov_len=4}, {iov_base=NULL, iov_len=0}, {iov_base="\376SMB@\0\1\0#\0\0\300\v\0\n\0\1\0\0\0\0\0\0\0\265\2\0\0\0\0\0\0"..., iov_len=64}, {iov_base="\t\0\0\0\0\0\0\0", iov_len=8}, {iov_base="\0", iov_len=1}], msg_iovlen=5, msg_controllen=0, msg_flags=0}, MSG_DONTWAIT|MSG_NOSIGNAL) = 77
>
> You can see the error code in the sendmsg() as "...#\0\0\300...".
>
> Turning debugging on on Samba shows:
>
> [2024/05/21 23:56:58.727547, 2] ../../source3/smbd/smb2_ioctl_filesys.c:707(fsctl_qar)
> QAR output len 336 exceeds max 16
> [2024/05/21 23:56:58.727652, 3] ../../source3/smbd/smb2_server.c:4031(smbd_smb2_request_error_ex)
> smbd_smb2_request_error_ex: smbd_smb2_request_error_ex: idx[1] status[NT_STATUS_BUFFER_TOO_SMALL] || at ../../source3/smbd/smb2_ioctl.c:353
>
> The "exceeds max 16" indicates the "Max Ioctl Out Size" setting passed in the
> Ioctl Request frame (which can be seen as 16 in the full packet trace). 336
> is 21 * 16.
>
> This stems from the smb2_llseek() function in the cifs filesystem in the Linux
> kernel calling:
>
> rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
> cfile->fid.volatile_fid,
> FSCTL_QUERY_ALLOCATED_RANGES,
> (char *)&in_data, sizeof(in_data),
> sizeof(struct file_allocated_range_buffer),
> (char **)&out_data, &out_data_len);
>
> where the max_out_data_len parameter is sizeof() you can see, allowing for
> just a single element to be returned. The file_allocated_range_buffer struct
> is just:
>
> struct file_allocated_range_buffer {
> __le64 file_offset;
> __le64 length;
> } __packed;
>
> which is 16 bytes - hence the maximum output data seen by Samba.
>
>
> Now, cifs only wants the next extent. It fills in the input data with the
> base file position and the EOF:
>
> in_data.file_offset = cpu_to_le64(offset);
> in_data.length = cpu_to_le64(i_size_read(inode));
>
> and asks the server to retrieve the first allocated extent within this range.
>
> In Samba, however, fsctl_qar() does not pass in_max_output to
> fsctl_qar_seek_fill() and therefore doesn't limit the amount returned. The
> fill loop seems only to be limited by the maximum file offset and not the max
> buffer size.
>
> The:
>
> if (out_output->length > in_max_output) {
> DEBUG(2, ("QAR output len %lu exceeds max %lu\n",
> (unsigned long)out_output->length,
> (unsigned long)in_max_output));
> data_blob_free(out_output);
> return NT_STATUS_BUFFER_TOO_SMALL;
> }
>
> check at the end of fsctl_qar() generates the complaint.
>
> I think that fsctl_qar() should probably just discard the excess records.
>
> Looking at:
>
> https://learn.microsoft.com/en-us/windows/win32/api/winioctl/ni-winioctl-fsctl_query_allocated_ranges
>
> it's not obvious what should be done if the available records won't fit.
MS-FSCC from https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-FSCC/%5bMS-FSCC%5d.pdf
is a more protocol-specific reference here, but it's still unclear
regarding partial / truncated responses.
We do have an existing test for the STATUS_BUFFER_TOO_SMALL response in
test_ioctl_sparse_qar_malformed(), which would have been run against a
Windows server to confirm matching protocol behaviour. But it doesn't
cover partial responses.
I think the best way to proceed here would be to capture traffic for the
same workload against a Windows SMB server. This could be don't by using
your cifs.ko workload or extending test_ioctl_sparse_qar_malformed().
Cheers, David
next prev parent reply other threads:[~2024-05-22 9:25 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-05-21 23:53 Bug in Samba's implementation of FSCTL_QUERY_ALLOCATED_RANGES? David Howells
2024-05-22 8:53 ` David Disseldorp [this message]
2024-05-22 10:36 ` David Howells
2024-05-23 4:54 ` David Disseldorp
2024-05-23 5:05 ` ronnie sahlberg
2024-05-23 6:21 ` David Howells
2024-05-23 6:28 ` ronnie sahlberg
2024-05-23 6:36 ` David Howells
2024-05-23 14:29 ` Tom Talpey
2024-05-23 15:28 ` Paulo Alcantara
2024-05-23 22:49 ` Jeremy Allison
2024-05-24 7:45 ` Stefan Metzmacher
2024-08-22 22:26 ` David Howells
2024-08-23 13:20 ` David Disseldorp
2024-08-28 10:25 ` David Howells
2024-08-28 10:55 ` David Disseldorp
2024-08-28 11:52 ` David Howells
2024-08-28 12:57 ` David Disseldorp
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240522185305.69e04dab@echidna \
--to=ddiss@samba.org \
--cc=dhowells@redhat.com \
--cc=jra@samba.org \
--cc=linux-cifs@vger.kernel.org \
--cc=pc@manguebit.com \
--cc=samba-technical@lists.samba.org \
--cc=sfrench@samba.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.