2013-01-15 20:22:57 +04:00
/*
Unix SMB / CIFS implementation .
Core SMB2 server
Copyright ( C ) Stefan Metzmacher 2009
2024-08-23 15:55:58 +03:00
Copyright ( C ) David Disseldorp 2013 - 2024
2013-01-15 20:22:57 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
# include "smbd/smbd.h"
# include "smbd/globals.h"
# include "../libcli/smb/smb_common.h"
2013-11-18 17:54:31 +04:00
# include "../libcli/security/security.h"
2013-01-15 20:22:57 +04:00
# include "../lib/util/tevent_ntstatus.h"
# include "rpc_server/srv_pipe_hnd.h"
# include "include/ntioctl.h"
2013-11-18 17:54:31 +04:00
# include "../librpc/ndr/libndr.h"
# include "librpc/gen_ndr/ndr_ioctl.h"
2013-01-15 20:22:57 +04:00
# include "smb2_ioctl_private.h"
2021-06-28 16:50:32 +03:00
# include "lib/util/sys_rw.h"
2013-01-15 20:22:57 +04:00
2018-03-21 22:01:05 +03:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_SMB2
2016-10-02 00:40:13 +03:00
/*
* XXX this may reduce dup_extents - > byte_count so that it ' s less than the
* target file size .
*/
static NTSTATUS fsctl_dup_extents_check_lengths ( struct files_struct * src_fsp ,
struct files_struct * dst_fsp ,
struct fsctl_dup_extents_to_file * dup_extents )
{
NTSTATUS status ;
if ( ( dup_extents - > source_off + dup_extents - > byte_count
< dup_extents - > source_off )
| | ( dup_extents - > target_off + dup_extents - > byte_count
< dup_extents - > target_off ) ) {
return NT_STATUS_INVALID_PARAMETER ; /* wrap */
}
status = vfs_stat_fsp ( src_fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/*
* XXX vfs_btrfs and vfs_default have size checks in the copychunk
* handler , as this needs to be rechecked after the src has potentially
* been extended by a previous chunk in the compound copychunk req .
*/
if ( src_fsp - > fsp_name - > st . st_ex_size
< dup_extents - > source_off + dup_extents - > byte_count ) {
DEBUG ( 2 , ( " dup_extents req exceeds src size \n " ) ) ;
return NT_STATUS_NOT_SUPPORTED ;
}
status = vfs_stat_fsp ( dst_fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
if ( dst_fsp - > fsp_name - > st . st_ex_size
< dup_extents - > target_off + dup_extents - > byte_count ) {
if ( dst_fsp - > fsp_name - > st . st_ex_size - dup_extents - > target_off
> dst_fsp - > fsp_name - > st . st_ex_size ) {
return NT_STATUS_INVALID_PARAMETER ; /* wrap */
}
/*
* this server behaviour is pretty hairy , but we need to match
* Windows , so . . .
*/
DEBUG ( 2 , ( " dup_extents req exceeds target size, capping \n " ) ) ;
dup_extents - > byte_count = dst_fsp - > fsp_name - > st . st_ex_size
- dup_extents - > target_off ;
}
return NT_STATUS_OK ;
}
2016-09-20 19:48:37 +03:00
static NTSTATUS fsctl_dup_extents_check_overlap ( struct files_struct * src_fsp ,
struct files_struct * dst_fsp ,
struct fsctl_dup_extents_to_file * dup_extents )
{
if ( ! file_id_equal ( & src_fsp - > file_id , & dst_fsp - > file_id ) ) {
/* src and dest refer to different files */
return NT_STATUS_OK ;
}
2021-06-28 16:50:32 +03:00
if ( sys_io_ranges_overlap ( dup_extents - > byte_count ,
dup_extents - > source_off ,
dup_extents - > byte_count ,
dup_extents - > target_off ) )
{
2016-09-20 19:48:37 +03:00
return NT_STATUS_NOT_SUPPORTED ;
}
return NT_STATUS_OK ;
}
2016-09-30 21:53:07 +03:00
static NTSTATUS fsctl_dup_extents_check_sparse ( struct files_struct * src_fsp ,
struct files_struct * dst_fsp )
{
/*
* 2.3 .8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply . . .
* STATUS_NOT_SUPPORTED : Target file is sparse , while source
* is a non - sparse file .
*
* WS2016 has the following behaviour ( MS are in the process of fixing
* the spec ) :
* STATUS_NOT_SUPPORTED is returned if the source is sparse , while the
* target is non - sparse . However , if target is sparse while the source
* is non - sparse , then FSCTL_DUPLICATE_EXTENTS_TO_FILE completes
* successfully .
*/
2020-04-03 08:52:10 +03:00
if ( src_fsp - > fsp_flags . is_sparse & & ! dst_fsp - > fsp_flags . is_sparse ) {
2016-09-30 21:53:07 +03:00
return NT_STATUS_NOT_SUPPORTED ;
}
return NT_STATUS_OK ;
}
2015-07-30 17:16:15 +03:00
struct fsctl_dup_extents_state {
struct tevent_context * ev ;
struct connection_struct * conn ;
2017-06-06 13:23:27 +03:00
struct files_struct * dst_fsp ;
2015-07-30 17:16:15 +03:00
struct fsctl_dup_extents_to_file dup_extents ;
} ;
2017-06-06 13:23:27 +03:00
static void fsctl_dup_extents_offload_read_done ( struct tevent_req * subreq ) ;
2015-07-30 17:16:15 +03:00
static void fsctl_dup_extents_vfs_done ( struct tevent_req * subreq ) ;
static struct tevent_req * fsctl_dup_extents_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct files_struct * dst_fsp ,
DATA_BLOB * in_input ,
struct smbd_smb2_request * smb2req )
{
struct tevent_req * req = NULL ;
struct tevent_req * subreq = NULL ;
struct fsctl_dup_extents_state * state = NULL ;
uint64_t src_fid_persistent = 0 ;
uint64_t src_fid_volatile = 0 ;
struct files_struct * src_fsp = NULL ;
int ndr_ret ;
2016-09-20 19:48:37 +03:00
NTSTATUS status ;
2015-07-30 17:16:15 +03:00
req = tevent_req_create ( mem_ctx , & state ,
struct fsctl_dup_extents_state ) ;
if ( req = = NULL ) {
return NULL ;
}
if ( dst_fsp = = NULL ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return tevent_req_post ( req , ev ) ;
}
2017-05-16 15:03:32 +03:00
* state = ( struct fsctl_dup_extents_state ) {
. conn = dst_fsp - > conn ,
. ev = ev ,
2017-06-06 13:23:27 +03:00
. dst_fsp = dst_fsp ,
2017-05-16 15:03:32 +03:00
} ;
2015-07-30 17:16:15 +03:00
if ( ( dst_fsp - > conn - > fs_capabilities
& FILE_SUPPORTS_BLOCK_REFCOUNTING ) = = 0 ) {
DBG_INFO ( " FS does not advertise block refcounting support \n " ) ;
tevent_req_nterror ( req , NT_STATUS_INVALID_DEVICE_REQUEST ) ;
return tevent_req_post ( req , ev ) ;
}
ndr_ret = ndr_pull_struct_blob ( in_input , state , & state - > dup_extents ,
( ndr_pull_flags_fn_t ) ndr_pull_fsctl_dup_extents_to_file ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
DBG_ERR ( " failed to unmarshall dup extents to file req \n " ) ;
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return tevent_req_post ( req , ev ) ;
}
src_fid_persistent = BVAL ( state - > dup_extents . source_fid , 0 ) ;
src_fid_volatile = BVAL ( state - > dup_extents . source_fid , 8 ) ;
src_fsp = file_fsp_get ( smb2req , src_fid_persistent , src_fid_volatile ) ;
if ( ( src_fsp = = NULL )
| | ( src_fsp - > file_id . devid ! = dst_fsp - > file_id . devid ) ) {
/*
* [ MS - FSCC ] 2.3 .8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply
* STATUS_INVALID_PARAMETER :
* The FileHandle parameter is either invalid or does not
* represent a handle to an opened file on the same volume .
*
* Windows Server responds with NT_STATUS_INVALID_HANDLE instead
* of STATUS_INVALID_PARAMETER here , despite the above spec .
*/
DBG_ERR ( " invalid src_fsp for dup_extents \n " ) ;
tevent_req_nterror ( req , NT_STATUS_INVALID_HANDLE ) ;
return tevent_req_post ( req , ev ) ;
}
2016-10-02 00:40:13 +03:00
status = fsctl_dup_extents_check_lengths ( src_fsp , dst_fsp ,
& state - > dup_extents ) ;
2022-09-01 13:30:57 +03:00
if ( tevent_req_nterror ( req , status ) ) {
2016-10-02 00:40:13 +03:00
return tevent_req_post ( req , ev ) ;
}
2015-07-30 17:16:15 +03:00
if ( state - > dup_extents . byte_count = = 0 ) {
DBG_ERR ( " skipping zero length dup extents \n " ) ;
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
2016-09-20 19:48:37 +03:00
status = fsctl_dup_extents_check_overlap ( src_fsp , dst_fsp ,
& state - > dup_extents ) ;
2022-09-01 13:30:57 +03:00
if ( tevent_req_nterror ( req , status ) ) {
2016-09-20 19:48:37 +03:00
return tevent_req_post ( req , ev ) ;
}
2016-09-30 21:53:07 +03:00
status = fsctl_dup_extents_check_sparse ( src_fsp , dst_fsp ) ;
2022-09-01 13:30:57 +03:00
if ( tevent_req_nterror ( req , status ) ) {
2016-09-30 21:53:07 +03:00
return tevent_req_post ( req , ev ) ;
}
2017-06-06 13:23:27 +03:00
subreq = SMB_VFS_OFFLOAD_READ_SEND ( state , ev , src_fsp ,
FSCTL_DUP_EXTENTS_TO_FILE ,
0 , 0 , 0 ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , fsctl_dup_extents_offload_read_done ,
req ) ;
return req ;
}
static void fsctl_dup_extents_offload_read_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct fsctl_dup_extents_state * state = tevent_req_data (
req , struct fsctl_dup_extents_state ) ;
2021-06-22 21:13:02 +03:00
uint32_t flags ;
uint64_t xferlen ;
2017-06-09 14:02:49 +03:00
DATA_BLOB token ;
2017-06-06 13:23:27 +03:00
NTSTATUS status ;
2021-06-22 21:13:02 +03:00
/*
* Note that both flags and xferlen are not used with copy - chunk .
*/
2017-06-06 13:23:27 +03:00
status = SMB_VFS_OFFLOAD_READ_RECV ( subreq , state - > dst_fsp - > conn ,
2021-06-22 21:13:02 +03:00
state , & flags , & xferlen , & token ) ;
2017-06-06 13:23:27 +03:00
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2017-05-04 16:09:36 +03:00
/* tell the VFS to ignore locks across the clone, matching ReFS */
2017-06-04 14:50:33 +03:00
subreq = SMB_VFS_OFFLOAD_WRITE_SEND ( state - > dst_fsp - > conn ,
state ,
state - > ev ,
2017-06-09 14:02:49 +03:00
FSCTL_DUP_EXTENTS_TO_FILE ,
& token ,
2017-06-04 14:50:33 +03:00
state - > dup_extents . source_off ,
state - > dst_fsp ,
state - > dup_extents . target_off ,
2017-06-10 10:05:55 +03:00
state - > dup_extents . byte_count ) ;
2015-07-30 17:16:15 +03:00
if ( tevent_req_nomem ( subreq , req ) ) {
2017-06-06 13:23:27 +03:00
return ;
2015-07-30 17:16:15 +03:00
}
tevent_req_set_callback ( subreq , fsctl_dup_extents_vfs_done , req ) ;
2017-06-06 13:23:27 +03:00
return ;
2015-07-30 17:16:15 +03:00
}
static void fsctl_dup_extents_vfs_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct fsctl_dup_extents_state * state = tevent_req_data (
req , struct fsctl_dup_extents_state ) ;
off_t nb_chunk ;
NTSTATUS status ;
2017-06-04 14:50:33 +03:00
status = SMB_VFS_OFFLOAD_WRITE_RECV ( state - > conn , subreq , & nb_chunk ) ;
2015-07-30 17:16:15 +03:00
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
if ( nb_chunk ! = state - > dup_extents . byte_count ) {
tevent_req_nterror ( req , NT_STATUS_IO_DEVICE_ERROR ) ;
return ;
}
tevent_req_done ( req ) ;
}
static NTSTATUS fsctl_dup_extents_recv ( struct tevent_req * req )
{
return tevent_req_simple_recv_ntstatus ( req ) ;
}
2013-11-18 17:54:31 +04:00
static NTSTATUS fsctl_get_cmprn ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct files_struct * fsp ,
size_t in_max_output ,
DATA_BLOB * out_output )
{
struct compression_state cmpr_state ;
enum ndr_err_code ndr_ret ;
DATA_BLOB output ;
NTSTATUS status ;
if ( fsp = = NULL ) {
return NT_STATUS_FILE_CLOSED ;
}
/* Windows doesn't check for SEC_FILE_READ_ATTRIBUTE permission here */
ZERO_STRUCT ( cmpr_state ) ;
2016-10-04 02:09:37 +03:00
if ( fsp - > conn - > fs_capabilities & FILE_FILE_COMPRESSION ) {
2020-10-13 13:02:34 +03:00
status = SMB_VFS_FGET_COMPRESSION ( fsp - > conn ,
2016-10-04 02:09:37 +03:00
mem_ctx ,
fsp ,
& cmpr_state . format ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
} else {
/*
* bso # 12144 : The underlying filesystem doesn ' t support
* compression , so we should respond with " not-compressed "
* ( like WS2016 ReFS ) instead of STATUS_NOT_SUPPORTED or
* NT_STATUS_INVALID_DEVICE_REQUEST .
*/
cmpr_state . format = COMPRESSION_FORMAT_NONE ;
2013-11-18 17:54:31 +04:00
}
ndr_ret = ndr_push_struct_blob ( & output , mem_ctx ,
& cmpr_state ,
( ndr_push_flags_fn_t ) ndr_push_compression_state ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
return NT_STATUS_INTERNAL_ERROR ;
}
if ( in_max_output < output . length ) {
DEBUG ( 1 , ( " max output %u too small for compression state %ld \n " ,
( unsigned int ) in_max_output , ( long int ) output . length ) ) ;
return NT_STATUS_INVALID_USER_BUFFER ;
}
* out_output = output ;
return NT_STATUS_OK ;
}
static NTSTATUS fsctl_set_cmprn ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct files_struct * fsp ,
DATA_BLOB * in_input )
{
struct compression_state cmpr_state ;
enum ndr_err_code ndr_ret ;
NTSTATUS status ;
if ( fsp = = NULL ) {
return NT_STATUS_FILE_CLOSED ;
}
/* WRITE_DATA permission is required, WRITE_ATTRIBUTES is not */
2023-12-21 12:58:09 +03:00
status = check_any_access_fsp ( fsp , FILE_WRITE_DATA ) ;
2013-11-18 17:54:31 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
ndr_ret = ndr_pull_struct_blob ( in_input , mem_ctx , & cmpr_state ,
( ndr_pull_flags_fn_t ) ndr_pull_compression_state ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
DEBUG ( 0 , ( " failed to unmarshall set compression req \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
2017-01-05 19:36:02 +03:00
status = NT_STATUS_NOT_SUPPORTED ;
if ( fsp - > conn - > fs_capabilities & FILE_FILE_COMPRESSION ) {
status = SMB_VFS_SET_COMPRESSION ( fsp - > conn ,
mem_ctx ,
fsp ,
cmpr_state . format ) ;
} else if ( cmpr_state . format = = COMPRESSION_FORMAT_NONE ) {
/*
* bso # 12144 : The underlying filesystem doesn ' t support
* compression . We should still accept set ( FORMAT_NONE ) requests
* ( like WS2016 ReFS ) .
*/
status = NT_STATUS_OK ;
2013-11-18 17:54:31 +04:00
}
2017-01-05 19:36:02 +03:00
return status ;
2013-11-18 17:54:31 +04:00
}
2015-02-11 02:24:16 +03:00
static NTSTATUS fsctl_zero_data ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct files_struct * fsp ,
DATA_BLOB * in_input )
{
struct file_zero_data_info zdata_info ;
enum ndr_err_code ndr_ret ;
struct lock_struct lck ;
int mode ;
uint64_t len ;
int ret ;
NTSTATUS status ;
if ( fsp = = NULL ) {
return NT_STATUS_FILE_CLOSED ;
}
/* WRITE_DATA permission is required */
2023-12-21 12:58:09 +03:00
status = check_any_access_fsp ( fsp , FILE_WRITE_DATA ) ;
2015-02-11 02:24:16 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/* allow regardless of whether FS supports sparse or not */
ndr_ret = ndr_pull_struct_blob ( in_input , mem_ctx , & zdata_info ,
( ndr_pull_flags_fn_t ) ndr_pull_file_zero_data_info ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
DEBUG ( 0 , ( " failed to unmarshall zero data request \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
if ( zdata_info . beyond_final_zero < zdata_info . file_off ) {
DEBUG ( 0 , ( " invalid zero data params: off %lu, bfz, %lu \n " ,
( unsigned long ) zdata_info . file_off ,
( unsigned long ) zdata_info . beyond_final_zero ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
/* convert strange "beyond final zero" param into length */
len = zdata_info . beyond_final_zero - zdata_info . file_off ;
if ( len = = 0 ) {
DEBUG ( 2 , ( " zero data called with zero length range \n " ) ) ;
return NT_STATUS_OK ;
}
init_strict_lock_struct ( fsp ,
fsp - > op - > global - > open_persistent_id ,
zdata_info . file_off ,
len ,
WRITE_LOCK ,
2021-11-17 02:00:03 +03:00
lp_posix_cifsu_locktype ( fsp ) ,
2015-02-11 02:24:16 +03:00
& lck ) ;
2017-07-09 15:34:10 +03:00
if ( ! SMB_VFS_STRICT_LOCK_CHECK ( fsp - > conn , fsp , & lck ) ) {
2015-02-11 02:24:16 +03:00
DEBUG ( 2 , ( " failed to lock range for zero-data \n " ) ) ;
return NT_STATUS_FILE_LOCK_CONFLICT ;
}
/*
2016-10-13 17:07:22 +03:00
* MS - FSCC < 58 > Section 2.3 .67
2015-02-11 02:24:16 +03:00
* This FSCTL sets the range of bytes to zero ( 0 ) without extending the
* file size .
*
* The VFS_FALLOCATE_FL_KEEP_SIZE flag is used to satisfy this
* constraint .
*/
mode = VFS_FALLOCATE_FL_PUNCH_HOLE | VFS_FALLOCATE_FL_KEEP_SIZE ;
ret = SMB_VFS_FALLOCATE ( fsp , mode , zdata_info . file_off , len ) ;
if ( ret = = - 1 ) {
status = map_nt_error_from_unix_common ( errno ) ;
DEBUG ( 2 , ( " zero-data fallocate(0x%x) failed: %s \n " , mode ,
strerror ( errno ) ) ) ;
return status ;
}
2020-04-03 08:52:10 +03:00
if ( ! fsp - > fsp_flags . is_sparse & & lp_strict_allocate ( SNUM ( fsp - > conn ) ) ) {
2015-02-11 02:24:16 +03:00
/*
* File marked non - sparse and " strict allocate " is enabled -
* allocate the range that we just punched out .
* In future FALLOC_FL_ZERO_RANGE could be used exclusively for
* this , but it ' s currently only supported on XFS and ext4 .
*
* The newly allocated range still won ' t be found by SEEK_DATA
* for QAR , but stat . st_blocks will reflect it .
*/
ret = SMB_VFS_FALLOCATE ( fsp , VFS_FALLOCATE_FL_KEEP_SIZE ,
zdata_info . file_off , len ) ;
if ( ret = = - 1 ) {
status = map_nt_error_from_unix_common ( errno ) ;
DEBUG ( 0 , ( " fallocate failed: %s \n " , strerror ( errno ) ) ) ;
return status ;
}
}
return NT_STATUS_OK ;
}
2015-02-19 18:36:05 +03:00
static NTSTATUS fsctl_qar_buf_push ( TALLOC_CTX * mem_ctx ,
struct file_alloced_range_buf * qar_buf ,
DATA_BLOB * qar_array_blob )
{
DATA_BLOB new_slot ;
enum ndr_err_code ndr_ret ;
bool ok ;
ndr_ret = ndr_push_struct_blob ( & new_slot , mem_ctx , qar_buf ,
( ndr_push_flags_fn_t ) ndr_push_file_alloced_range_buf ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
DEBUG ( 0 , ( " failed to marshall QAR buf \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
/* TODO should be able to avoid copy by pushing into prealloced buf */
ok = data_blob_append ( mem_ctx , qar_array_blob , new_slot . data ,
new_slot . length ) ;
data_blob_free ( & new_slot ) ;
if ( ! ok ) {
return NT_STATUS_NO_MEMORY ;
}
return NT_STATUS_OK ;
}
static NTSTATUS fsctl_qar_seek_fill ( TALLOC_CTX * mem_ctx ,
struct files_struct * fsp ,
off_t curr_off ,
off_t max_off ,
2024-08-23 15:55:58 +03:00
size_t in_max_output ,
2015-02-19 18:36:05 +03:00
DATA_BLOB * qar_array_blob )
{
NTSTATUS status = NT_STATUS_NOT_SUPPORTED ;
# ifdef HAVE_LSEEK_HOLE_DATA
while ( curr_off < = max_off ) {
off_t data_off ;
off_t hole_off ;
struct file_alloced_range_buf qar_buf ;
/* seek next data */
data_off = SMB_VFS_LSEEK ( fsp , curr_off , SEEK_DATA ) ;
if ( ( data_off = = - 1 ) & & ( errno = = ENXIO ) ) {
/* no data from curr_off to EOF */
break ;
} else if ( data_off = = - 1 ) {
status = map_nt_error_from_unix_common ( errno ) ;
DEBUG ( 1 , ( " lseek data failed: %s \n " , strerror ( errno ) ) ) ;
return status ;
}
if ( data_off > max_off ) {
/* found something, but passed range of interest */
break ;
}
hole_off = SMB_VFS_LSEEK ( fsp , data_off , SEEK_HOLE ) ;
if ( hole_off = = - 1 ) {
status = map_nt_error_from_unix_common ( errno ) ;
DEBUG ( 1 , ( " lseek hole failed: %s \n " , strerror ( errno ) ) ) ;
return status ;
}
if ( hole_off < = data_off ) {
DEBUG ( 1 , ( " lseek inconsistent: hole %lu at or before "
" data %lu \n " , ( unsigned long ) hole_off ,
( unsigned long ) data_off ) ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
2024-08-23 15:55:58 +03:00
if ( qar_array_blob - > length + sizeof ( qar_buf ) > in_max_output ) {
/*
* Earlier check ensures space for one range or more .
* Subsequent overflow results in a truncated response .
*/
DBG_NOTICE ( " truncated QAR output: need > %zu, max %zu \n " ,
qar_array_blob - > length + sizeof ( qar_buf ) ,
in_max_output ) ;
return STATUS_BUFFER_OVERFLOW ;
}
2015-02-19 18:36:05 +03:00
qar_buf . file_off = data_off ;
/* + 1 to convert maximum offset to length */
qar_buf . len = MIN ( hole_off , max_off + 1 ) - data_off ;
status = fsctl_qar_buf_push ( mem_ctx , & qar_buf , qar_array_blob ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return NT_STATUS_NO_MEMORY ;
}
curr_off = hole_off ;
}
status = NT_STATUS_OK ;
# endif
return status ;
}
static NTSTATUS fsctl_qar ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct files_struct * fsp ,
DATA_BLOB * in_input ,
size_t in_max_output ,
DATA_BLOB * out_output )
{
struct fsctl_query_alloced_ranges_req qar_req ;
struct fsctl_query_alloced_ranges_rsp qar_rsp ;
DATA_BLOB qar_array_blob = data_blob_null ;
uint64_t max_off ;
enum ndr_err_code ndr_ret ;
int ret ;
NTSTATUS status ;
SMB_STRUCT_STAT sbuf ;
if ( fsp = = NULL ) {
return NT_STATUS_FILE_CLOSED ;
}
/* READ_DATA permission is required */
2023-12-21 12:58:09 +03:00
status = check_any_access_fsp ( fsp , FILE_READ_DATA ) ;
2015-02-19 18:36:05 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
ndr_ret = ndr_pull_struct_blob ( in_input , mem_ctx , & qar_req ,
( ndr_pull_flags_fn_t ) ndr_pull_fsctl_query_alloced_ranges_req ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
DEBUG ( 0 , ( " failed to unmarshall QAR req \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
/*
* XXX Windows Server 2008 & 2012 servers don ' t return lock - conflict
* for QAR requests over an exclusively locked range !
*/
ret = SMB_VFS_FSTAT ( fsp , & sbuf ) ;
if ( ret = = - 1 ) {
status = map_nt_error_from_unix_common ( errno ) ;
DEBUG ( 2 , ( " fstat failed: %s \n " , strerror ( errno ) ) ) ;
return status ;
}
if ( ( qar_req . buf . len = = 0 )
| | ( sbuf . st_ex_size = = 0 )
| | ( qar_req . buf . file_off > = sbuf . st_ex_size ) ) {
/* zero length range or after EOF, no ranges to return */
return NT_STATUS_OK ;
}
/* check for integer overflow */
if ( qar_req . buf . file_off + qar_req . buf . len < qar_req . buf . file_off ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2024-08-23 15:55:58 +03:00
/* must have enough space for at least one range */
if ( in_max_output < sizeof ( struct file_alloced_range_buf ) ) {
DEBUG ( 2 , ( " QAR max %lu insufficient for one range \n " ,
( unsigned long ) in_max_output ) ) ;
return NT_STATUS_BUFFER_TOO_SMALL ;
}
2015-02-19 18:36:05 +03:00
/*
* Maximum offset is either the last valid offset _before_ EOF , or the
* last byte offset within the requested range . - 1 converts length to
* offset , which is easier to work with for SEEK_DATA / SEEK_HOLE , E . g . :
*
* / off = 0 / off = 512 K / st_ex_size = 1 M
* | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
* | File data |
* | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
* QAR end \
* | = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = |
* | QAR off = 512 K , len = 1 M |
* | = = = = = = = = = = = = = = = = = ^ = = = = = = = = = = = = = = = = = = = |
* max_off = 1 M - 1
* QAR end \
* | = = = = = = = = = = = = = = = = = = |
* | QAR off = 0 len = 512 K |
* | = = = = = = = = = = = = = = = = = = |
* ^
* max_off = 512 K - 1
*/
max_off = MIN ( sbuf . st_ex_size ,
qar_req . buf . file_off + qar_req . buf . len ) - 1 ;
2020-04-03 08:52:10 +03:00
if ( ! fsp - > fsp_flags . is_sparse ) {
2015-02-19 18:36:05 +03:00
struct file_alloced_range_buf qar_buf ;
/* file is non-sparse, claim file_off->max_off is allocated */
qar_buf . file_off = qar_req . buf . file_off ;
/* + 1 to convert maximum offset back to length */
qar_buf . len = max_off - qar_req . buf . file_off + 1 ;
status = fsctl_qar_buf_push ( mem_ctx , & qar_buf , & qar_array_blob ) ;
} else {
status = fsctl_qar_seek_fill ( mem_ctx , fsp , qar_req . buf . file_off ,
2024-08-23 15:55:58 +03:00
max_off , in_max_output ,
& qar_array_blob ) ;
2015-02-19 18:36:05 +03:00
}
2024-08-23 15:55:58 +03:00
if ( NT_STATUS_IS_OK ( status )
| | NT_STATUS_EQUAL ( status , STATUS_BUFFER_OVERFLOW ) ) {
/* marshall response. STATUS_BUFFER_OVERFLOW=truncated */
qar_rsp . far_buf_array = qar_array_blob ;
2015-02-19 18:36:05 +03:00
2024-08-23 15:55:58 +03:00
ndr_ret = ndr_push_struct_blob ( out_output , mem_ctx , & qar_rsp ,
( ndr_push_flags_fn_t ) ndr_push_fsctl_query_alloced_ranges_rsp ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
DEBUG ( 0 , ( " failed to marshall QAR rsp \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
2015-02-19 18:36:05 +03:00
}
2024-08-23 15:55:58 +03:00
return status ;
2015-02-19 18:36:05 +03:00
}
2015-07-30 17:16:15 +03:00
static void smb2_ioctl_filesys_dup_extents_done ( struct tevent_req * subreq ) ;
2013-01-15 20:22:57 +04:00
struct tevent_req * smb2_ioctl_filesys ( uint32_t ctl_code ,
struct tevent_context * ev ,
struct tevent_req * req ,
struct smbd_smb2_ioctl_state * state )
{
2013-11-18 17:54:31 +04:00
NTSTATUS status ;
2013-01-15 20:22:57 +04:00
switch ( ctl_code ) {
2013-11-18 17:54:31 +04:00
case FSCTL_GET_COMPRESSION :
status = fsctl_get_cmprn ( state , ev , state - > fsp ,
state - > in_max_output ,
& state - > out_output ) ;
if ( ! tevent_req_nterror ( req , status ) ) {
tevent_req_done ( req ) ;
}
return tevent_req_post ( req , ev ) ;
break ;
case FSCTL_SET_COMPRESSION :
status = fsctl_set_cmprn ( state , ev , state - > fsp ,
& state - > in_input ) ;
if ( ! tevent_req_nterror ( req , status ) ) {
tevent_req_done ( req ) ;
2015-02-11 02:24:16 +03:00
}
return tevent_req_post ( req , ev ) ;
break ;
case FSCTL_SET_ZERO_DATA :
status = fsctl_zero_data ( state , ev , state - > fsp ,
& state - > in_input ) ;
if ( ! tevent_req_nterror ( req , status ) ) {
tevent_req_done ( req ) ;
2015-02-19 18:36:05 +03:00
}
return tevent_req_post ( req , ev ) ;
break ;
case FSCTL_QUERY_ALLOCATED_RANGES :
status = fsctl_qar ( state , ev , state - > fsp ,
& state - > in_input ,
state - > in_max_output ,
& state - > out_output ) ;
if ( ! tevent_req_nterror ( req , status ) ) {
tevent_req_done ( req ) ;
2013-11-18 17:54:31 +04:00
}
return tevent_req_post ( req , ev ) ;
break ;
2015-07-30 17:16:15 +03:00
case FSCTL_DUP_EXTENTS_TO_FILE : {
struct tevent_req * subreq = NULL ;
subreq = fsctl_dup_extents_send ( state , ev ,
state - > fsp ,
& state - > in_input ,
state - > smb2req ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq ,
smb2_ioctl_filesys_dup_extents_done ,
req ) ;
return req ;
break ;
}
2013-01-15 20:22:57 +04:00
default : {
uint8_t * out_data = NULL ;
uint32_t out_data_len = 0 ;
if ( state - > fsp = = NULL ) {
status = NT_STATUS_NOT_SUPPORTED ;
} else {
status = SMB_VFS_FSCTL ( state - > fsp ,
state ,
ctl_code ,
state - > smbreq - > flags2 ,
state - > in_input . data ,
state - > in_input . length ,
& out_data ,
state - > in_max_output ,
& out_data_len ) ;
state - > out_output = data_blob_const ( out_data , out_data_len ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NOT_SUPPORTED ) ) {
if ( IS_IPC ( state - > smbreq - > conn ) ) {
status = NT_STATUS_FS_DRIVER_REQUIRED ;
} else {
status = NT_STATUS_INVALID_DEVICE_REQUEST ;
}
}
tevent_req_nterror ( req , status ) ;
return tevent_req_post ( req , ev ) ;
break ;
}
}
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
return tevent_req_post ( req , ev ) ;
}
2015-07-30 17:16:15 +03:00
static void smb2_ioctl_filesys_dup_extents_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
NTSTATUS status ;
status = fsctl_dup_extents_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( ! tevent_req_nterror ( req , status ) ) {
tevent_req_done ( req ) ;
}
}