Bug in Samba's implementation of FSCTL_QUERY_ALLOCATED_RANGES?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

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.

Samba: samba-4.19.6-1.fc39.x86_64
Linux: 6.10 pre -rc1.

David






[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux