2017-09-08 08:02:43 +00:00
/* Copyright (c) Mark Harmstone 2017
*
* This file is part of WinBtrfs .
*
* WinBtrfs is free software : you can redistribute it and / or modify
* it under the terms of the GNU Lesser General Public Licence as published by
* the Free Software Foundation , either version 3 of the Licence , or
* ( at your option ) any later version .
*
* WinBtrfs 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 Lesser General Public Licence for more details .
*
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs . If not , see < http : //www.gnu.org/licenses/>. */
# include "btrfs_drv.h"
typedef struct send_dir {
LIST_ENTRY list_entry ;
2019-09-01 12:53:20 +00:00
uint64_t inode ;
bool dummy ;
2017-09-08 08:02:43 +00:00
BTRFS_TIME atime ;
BTRFS_TIME mtime ;
BTRFS_TIME ctime ;
struct send_dir * parent ;
2019-09-01 12:53:20 +00:00
uint16_t namelen ;
2017-09-08 08:02:43 +00:00
char * name ;
LIST_ENTRY deleted_children ;
} send_dir ;
typedef struct {
LIST_ENTRY list_entry ;
2019-09-01 12:53:20 +00:00
uint64_t inode ;
bool dir ;
2017-09-08 08:02:43 +00:00
send_dir * sd ;
char tmpname [ 64 ] ;
} orphan ;
typedef struct {
LIST_ENTRY list_entry ;
ULONG namelen ;
char name [ 1 ] ;
} deleted_child ;
typedef struct {
LIST_ENTRY list_entry ;
send_dir * sd ;
2019-09-01 12:53:20 +00:00
uint16_t namelen ;
2017-09-08 08:02:43 +00:00
char name [ 1 ] ;
} ref ;
typedef struct {
send_dir * sd ;
2019-09-01 12:53:20 +00:00
uint64_t last_child_inode ;
2017-09-08 08:02:43 +00:00
LIST_ENTRY list_entry ;
} pending_rmdir ;
typedef struct {
2019-09-01 12:53:20 +00:00
uint64_t offset ;
2017-09-08 08:02:43 +00:00
LIST_ENTRY list_entry ;
ULONG datalen ;
EXTENT_DATA data ;
} send_ext ;
typedef struct {
device_extension * Vcb ;
root * root ;
root * parent ;
2019-09-01 12:53:20 +00:00
uint8_t * data ;
2017-09-08 08:02:43 +00:00
ULONG datalen ;
ULONG num_clones ;
root * * clones ;
LIST_ENTRY orphans ;
LIST_ENTRY dirs ;
LIST_ENTRY pending_rmdirs ;
KEVENT buffer_event ;
send_dir * root_dir ;
send_info * send ;
struct {
2019-09-01 12:53:20 +00:00
uint64_t inode ;
bool deleting ;
bool new ;
uint64_t gen ;
uint64_t uid ;
uint64_t olduid ;
uint64_t gid ;
uint64_t oldgid ;
uint64_t mode ;
uint64_t oldmode ;
uint64_t size ;
uint64_t flags ;
2017-09-08 08:02:43 +00:00
BTRFS_TIME atime ;
BTRFS_TIME mtime ;
BTRFS_TIME ctime ;
2019-09-01 12:53:20 +00:00
bool file ;
2017-09-08 08:02:43 +00:00
char * path ;
orphan * o ;
send_dir * sd ;
LIST_ENTRY refs ;
LIST_ENTRY oldrefs ;
LIST_ENTRY exts ;
LIST_ENTRY oldexts ;
} lastinode ;
} send_context ;
# define MAX_SEND_WRITE 0xc000 // 48 KB
# define SEND_BUFFER_LENGTH 0x100000 // 1 MB
2019-09-01 12:53:20 +00:00
static NTSTATUS find_send_dir ( send_context * context , uint64_t dir , uint64_t generation , send_dir * * psd , bool * added_dummy ) ;
2017-09-08 08:02:43 +00:00
static NTSTATUS wait_for_flush ( send_context * context , traverse_ptr * tp1 , traverse_ptr * tp2 ) ;
2019-09-01 12:53:20 +00:00
static void send_command ( send_context * context , uint16_t cmd ) {
2017-09-08 08:02:43 +00:00
btrfs_send_command * bsc = ( btrfs_send_command * ) & context - > data [ context - > datalen ] ;
bsc - > cmd = cmd ;
bsc - > csum = 0 ;
context - > datalen + = sizeof ( btrfs_send_command ) ;
}
static void send_command_finish ( send_context * context , ULONG pos ) {
btrfs_send_command * bsc = ( btrfs_send_command * ) & context - > data [ pos ] ;
bsc - > length = context - > datalen - pos - sizeof ( btrfs_send_command ) ;
2019-09-01 12:53:20 +00:00
bsc - > csum = calc_crc32c ( 0 , ( uint8_t * ) bsc , context - > datalen - pos ) ;
2017-09-08 08:02:43 +00:00
}
2019-09-01 12:53:20 +00:00
static void send_add_tlv ( send_context * context , uint16_t type , void * data , uint16_t length ) {
2017-09-08 08:02:43 +00:00
btrfs_send_tlv * tlv = ( btrfs_send_tlv * ) & context - > data [ context - > datalen ] ;
tlv - > type = type ;
tlv - > length = length ;
if ( length > 0 & & data )
RtlCopyMemory ( & tlv [ 1 ] , data , length ) ;
context - > datalen + = sizeof ( btrfs_send_tlv ) + length ;
}
2019-09-01 12:53:20 +00:00
static char * uint64_to_char ( uint64_t num , char * buf ) {
2017-09-08 08:02:43 +00:00
char * tmp , tmp2 [ 20 ] ;
if ( num = = 0 ) {
buf [ 0 ] = ' 0 ' ;
return buf + 1 ;
}
tmp = & tmp2 [ 20 ] ;
while ( num > 0 ) {
tmp - - ;
* tmp = ( num % 10 ) + ' 0 ' ;
num / = 10 ;
}
RtlCopyMemory ( buf , tmp , tmp2 + sizeof ( tmp2 ) - tmp ) ;
return & buf [ tmp2 + sizeof ( tmp2 ) - tmp ] ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS get_orphan_name ( send_context * context , uint64_t inode , uint64_t generation , char * name ) {
2017-09-08 08:02:43 +00:00
char * ptr , * ptr2 ;
2019-09-01 12:53:20 +00:00
uint64_t index = 0 ;
2017-09-08 08:02:43 +00:00
KEY searchkey ;
name [ 0 ] = ' o ' ;
ptr = uint64_to_char ( inode , & name [ 1 ] ) ;
* ptr = ' - ' ; ptr + + ;
ptr = uint64_to_char ( generation , ptr ) ;
* ptr = ' - ' ; ptr + + ;
ptr2 = ptr ;
searchkey . obj_id = SUBVOL_ROOT_INODE ;
searchkey . obj_type = TYPE_DIR_ITEM ;
do {
NTSTATUS Status ;
traverse_ptr tp ;
ptr = uint64_to_char ( index , ptr ) ;
* ptr = 0 ;
2019-09-01 12:53:20 +00:00
searchkey . offset = calc_crc32c ( 0xfffffffe , ( uint8_t * ) name , ( ULONG ) ( ptr - name ) ) ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > root , & tp , & searchkey , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( searchkey , tp . item - > key ) )
goto cont ;
if ( context - > parent ) {
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > parent , & tp , & searchkey , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( searchkey , tp . item - > key ) )
goto cont ;
}
return STATUS_SUCCESS ;
cont :
index + + ;
ptr = ptr2 ;
2019-09-01 12:53:20 +00:00
} while ( true ) ;
2017-09-08 08:02:43 +00:00
}
static void add_orphan ( send_context * context , orphan * o ) {
LIST_ENTRY * le ;
le = context - > orphans . Flink ;
while ( le ! = & context - > orphans ) {
orphan * o2 = CONTAINING_RECORD ( le , orphan , list_entry ) ;
if ( o2 - > inode > o - > inode ) {
InsertHeadList ( o2 - > list_entry . Blink , & o - > list_entry ) ;
return ;
}
le = le - > Flink ;
}
InsertTailList ( & context - > orphans , & o - > list_entry ) ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS send_read_symlink ( send_context * context , uint64_t inode , char * * link , uint16_t * linklen ) {
2017-09-08 08:02:43 +00:00
NTSTATUS Status ;
KEY searchkey ;
traverse_ptr tp ;
EXTENT_DATA * ed ;
searchkey . obj_id = inode ;
searchkey . obj_type = TYPE_EXTENT_DATA ;
searchkey . offset = 0 ;
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > root , & tp , & searchkey , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( keycmp ( tp . item - > key , searchkey ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " could not find (%I64x,%x,%I64x) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
2017-09-08 08:02:43 +00:00
return STATUS_INTERNAL_ERROR ;
}
if ( tp . item - > size < sizeof ( EXTENT_DATA ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected at least %u \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp . item - > size , sizeof ( EXTENT_DATA ) ) ;
return STATUS_INTERNAL_ERROR ;
}
ed = ( EXTENT_DATA * ) tp . item - > data ;
if ( ed - > type ! = EXTENT_TYPE_INLINE ) {
WARN ( " symlink data was not inline, returning blank string \n " ) ;
* link = NULL ;
* linklen = 0 ;
return STATUS_SUCCESS ;
}
if ( tp . item - > size < offsetof ( EXTENT_DATA , data [ 0 ] ) + ed - > decoded_size ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected %u \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp . item - > size , offsetof ( EXTENT_DATA , data [ 0 ] ) + ed - > decoded_size ) ;
return STATUS_INTERNAL_ERROR ;
}
* link = ( char * ) ed - > data ;
2019-09-01 12:53:20 +00:00
* linklen = ( uint16_t ) ed - > decoded_size ;
2017-09-08 08:02:43 +00:00
return STATUS_SUCCESS ;
}
static NTSTATUS send_inode ( send_context * context , traverse_ptr * tp , traverse_ptr * tp2 ) {
NTSTATUS Status ;
INODE_ITEM * ii ;
if ( tp2 & & ! tp ) {
INODE_ITEM * ii2 = ( INODE_ITEM * ) tp2 - > item - > data ;
if ( tp2 - > item - > size < sizeof ( INODE_ITEM ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected %u \n " , tp2 - > item - > key . obj_id , tp2 - > item - > key . obj_type , tp2 - > item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp2 - > item - > size , sizeof ( INODE_ITEM ) ) ;
return STATUS_INTERNAL_ERROR ;
}
context - > lastinode . inode = tp2 - > item - > key . obj_id ;
2019-09-01 12:53:20 +00:00
context - > lastinode . deleting = true ;
2017-09-08 08:02:43 +00:00
context - > lastinode . gen = ii2 - > generation ;
context - > lastinode . mode = ii2 - > st_mode ;
context - > lastinode . flags = ii2 - > flags ;
context - > lastinode . o = NULL ;
context - > lastinode . sd = NULL ;
return STATUS_SUCCESS ;
}
ii = ( INODE_ITEM * ) tp - > item - > data ;
if ( tp - > item - > size < sizeof ( INODE_ITEM ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected %u \n " , tp - > item - > key . obj_id , tp - > item - > key . obj_type , tp - > item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp - > item - > size , sizeof ( INODE_ITEM ) ) ;
return STATUS_INTERNAL_ERROR ;
}
context - > lastinode . inode = tp - > item - > key . obj_id ;
2019-09-01 12:53:20 +00:00
context - > lastinode . deleting = false ;
2017-09-08 08:02:43 +00:00
context - > lastinode . gen = ii - > generation ;
context - > lastinode . uid = ii - > st_uid ;
context - > lastinode . gid = ii - > st_gid ;
context - > lastinode . mode = ii - > st_mode ;
context - > lastinode . size = ii - > st_size ;
context - > lastinode . atime = ii - > st_atime ;
context - > lastinode . mtime = ii - > st_mtime ;
context - > lastinode . ctime = ii - > st_ctime ;
context - > lastinode . flags = ii - > flags ;
2019-09-01 12:53:20 +00:00
context - > lastinode . file = false ;
2017-09-08 08:02:43 +00:00
context - > lastinode . o = NULL ;
context - > lastinode . sd = NULL ;
if ( context - > lastinode . path ) {
ExFreePool ( context - > lastinode . path ) ;
context - > lastinode . path = NULL ;
}
if ( tp2 ) {
INODE_ITEM * ii2 = ( INODE_ITEM * ) tp2 - > item - > data ;
LIST_ENTRY * le ;
if ( tp2 - > item - > size < sizeof ( INODE_ITEM ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected %u \n " , tp2 - > item - > key . obj_id , tp2 - > item - > key . obj_type , tp2 - > item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp2 - > item - > size , sizeof ( INODE_ITEM ) ) ;
return STATUS_INTERNAL_ERROR ;
}
context - > lastinode . oldmode = ii2 - > st_mode ;
context - > lastinode . olduid = ii2 - > st_uid ;
context - > lastinode . oldgid = ii2 - > st_gid ;
if ( ( ii2 - > st_mode & __S_IFREG ) = = __S_IFREG & & ( ii2 - > st_mode & __S_IFLNK ) ! = __S_IFLNK & & ( ii2 - > st_mode & __S_IFSOCK ) ! = __S_IFSOCK )
2019-09-01 12:53:20 +00:00
context - > lastinode . file = true ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
context - > lastinode . new = false ;
2017-09-08 08:02:43 +00:00
le = context - > orphans . Flink ;
while ( le ! = & context - > orphans ) {
orphan * o2 = CONTAINING_RECORD ( le , orphan , list_entry ) ;
if ( o2 - > inode = = tp - > item - > key . obj_id ) {
context - > lastinode . o = o2 ;
break ;
} else if ( o2 - > inode > tp - > item - > key . obj_id )
break ;
le = le - > Flink ;
}
} else
2019-09-01 12:53:20 +00:00
context - > lastinode . new = true ;
2017-09-08 08:02:43 +00:00
if ( tp - > item - > key . obj_id = = SUBVOL_ROOT_INODE ) {
send_dir * sd ;
Status = find_send_dir ( context , tp - > item - > key . obj_id , ii - > generation , & sd , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_send_dir returned %08x \n " , Status ) ;
return Status ;
}
sd - > atime = ii - > st_atime ;
sd - > mtime = ii - > st_mtime ;
sd - > ctime = ii - > st_ctime ;
context - > root_dir = sd ;
} else if ( ! tp2 ) {
ULONG pos = context - > datalen ;
2019-09-01 12:53:20 +00:00
uint16_t cmd ;
2017-09-08 08:02:43 +00:00
send_dir * sd ;
char name [ 64 ] ;
orphan * o ;
// skip creating orphan directory if we've already done so
if ( ii - > st_mode & __S_IFDIR ) {
LIST_ENTRY * le ;
le = context - > orphans . Flink ;
while ( le ! = & context - > orphans ) {
orphan * o2 = CONTAINING_RECORD ( le , orphan , list_entry ) ;
if ( o2 - > inode = = tp - > item - > key . obj_id ) {
context - > lastinode . o = o2 ;
o2 - > sd - > atime = ii - > st_atime ;
o2 - > sd - > mtime = ii - > st_mtime ;
o2 - > sd - > ctime = ii - > st_ctime ;
2019-09-01 12:53:20 +00:00
o2 - > sd - > dummy = false ;
2017-09-08 08:02:43 +00:00
return STATUS_SUCCESS ;
} else if ( o2 - > inode > tp - > item - > key . obj_id )
break ;
le = le - > Flink ;
}
}
if ( ( ii - > st_mode & __S_IFSOCK ) = = __S_IFSOCK )
cmd = BTRFS_SEND_CMD_MKSOCK ;
else if ( ( ii - > st_mode & __S_IFLNK ) = = __S_IFLNK )
cmd = BTRFS_SEND_CMD_SYMLINK ;
else if ( ( ii - > st_mode & __S_IFCHR ) = = __S_IFCHR | | ( ii - > st_mode & __S_IFBLK ) = = __S_IFBLK )
cmd = BTRFS_SEND_CMD_MKNOD ;
else if ( ( ii - > st_mode & __S_IFDIR ) = = __S_IFDIR )
cmd = BTRFS_SEND_CMD_MKDIR ;
else if ( ( ii - > st_mode & __S_IFIFO ) = = __S_IFIFO )
cmd = BTRFS_SEND_CMD_MKFIFO ;
else {
cmd = BTRFS_SEND_CMD_MKFILE ;
2019-09-01 12:53:20 +00:00
context - > lastinode . file = true ;
2017-09-08 08:02:43 +00:00
}
send_command ( context , cmd ) ;
Status = get_orphan_name ( context , tp - > item - > key . obj_id , ii - > generation , name ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " get_orphan_name returned %08x \n " , Status ) ;
return Status ;
}
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , name , ( uint16_t ) strlen ( name ) ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_INODE , & tp - > item - > key . obj_id , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
if ( cmd = = BTRFS_SEND_CMD_MKNOD | | cmd = = BTRFS_SEND_CMD_MKFIFO | | cmd = = BTRFS_SEND_CMD_MKSOCK ) {
2019-09-01 12:53:20 +00:00
uint64_t rdev = makedev ( ( ii - > st_rdev & 0xFFFFFFFFFFF ) > > 20 , ii - > st_rdev & 0xFFFFF ) , mode = ii - > st_mode ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_RDEV , & rdev , sizeof ( uint64_t ) ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_MODE , & mode , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
} else if ( cmd = = BTRFS_SEND_CMD_SYMLINK & & ii - > st_size > 0 ) {
char * link ;
2019-09-01 12:53:20 +00:00
uint16_t linklen ;
2017-09-08 08:02:43 +00:00
Status = send_read_symlink ( context , tp - > item - > key . obj_id , & link , & linklen ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_read_symlink returned %08x \n " , Status ) ;
return Status ;
}
send_add_tlv ( context , BTRFS_SEND_TLV_PATH_LINK , link , linklen ) ;
}
send_command_finish ( context , pos ) ;
if ( ii - > st_mode & __S_IFDIR ) {
Status = find_send_dir ( context , tp - > item - > key . obj_id , ii - > generation , & sd , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_send_dir returned %08x \n " , Status ) ;
return Status ;
}
2019-09-01 12:53:20 +00:00
sd - > dummy = false ;
2017-09-08 08:02:43 +00:00
} else
sd = NULL ;
context - > lastinode . sd = sd ;
o = ExAllocatePoolWithTag ( PagedPool , sizeof ( orphan ) , ALLOC_TAG ) ;
if ( ! o ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
o - > inode = tp - > item - > key . obj_id ;
2019-09-01 12:53:20 +00:00
o - > dir = ( ii - > st_mode & __S_IFDIR & & ii - > st_size > 0 ) ? true : false ;
2017-09-08 08:02:43 +00:00
strcpy ( o - > tmpname , name ) ;
o - > sd = sd ;
add_orphan ( context , o ) ;
context - > lastinode . path = ExAllocatePoolWithTag ( PagedPool , strlen ( o - > tmpname ) + 1 , ALLOC_TAG ) ;
if ( ! context - > lastinode . path ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
strcpy ( context - > lastinode . path , o - > tmpname ) ;
context - > lastinode . o = o ;
}
return STATUS_SUCCESS ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS send_add_dir ( send_context * context , uint64_t inode , send_dir * parent , char * name , uint16_t namelen , bool dummy , LIST_ENTRY * lastentry , send_dir * * psd ) {
2017-09-08 08:02:43 +00:00
LIST_ENTRY * le ;
send_dir * sd = ExAllocatePoolWithTag ( PagedPool , sizeof ( send_dir ) , ALLOC_TAG ) ;
if ( ! sd ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
sd - > inode = inode ;
sd - > dummy = dummy ;
sd - > parent = parent ;
if ( ! dummy ) {
sd - > atime = context - > lastinode . atime ;
sd - > mtime = context - > lastinode . mtime ;
sd - > ctime = context - > lastinode . ctime ;
}
if ( namelen > 0 ) {
sd - > name = ExAllocatePoolWithTag ( PagedPool , namelen , ALLOC_TAG ) ;
if ( ! sd - > name ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( sd ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
memcpy ( sd - > name , name , namelen ) ;
} else
sd - > name = NULL ;
sd - > namelen = namelen ;
InitializeListHead ( & sd - > deleted_children ) ;
if ( lastentry )
InsertHeadList ( lastentry , & sd - > list_entry ) ;
else {
le = context - > dirs . Flink ;
while ( le ! = & context - > dirs ) {
send_dir * sd2 = CONTAINING_RECORD ( le , send_dir , list_entry ) ;
if ( sd2 - > inode > sd - > inode ) {
InsertHeadList ( sd2 - > list_entry . Blink , & sd - > list_entry ) ;
if ( psd )
* psd = sd ;
return STATUS_SUCCESS ;
}
le = le - > Flink ;
}
InsertTailList ( & context - > dirs , & sd - > list_entry ) ;
}
if ( psd )
* psd = sd ;
return STATUS_SUCCESS ;
}
2019-09-01 12:53:20 +00:00
static __inline uint16_t find_path_len ( send_dir * parent , uint16_t namelen ) {
uint16_t len = namelen ;
2017-09-08 08:02:43 +00:00
while ( parent & & parent - > namelen > 0 ) {
len + = parent - > namelen + 1 ;
parent = parent - > parent ;
}
return len ;
}
static void find_path ( char * path , send_dir * parent , char * name , ULONG namelen ) {
ULONG len = namelen ;
RtlCopyMemory ( path , name , namelen ) ;
while ( parent & & parent - > namelen > 0 ) {
RtlMoveMemory ( path + parent - > namelen + 1 , path , len ) ;
RtlCopyMemory ( path , parent - > name , parent - > namelen ) ;
path [ parent - > namelen ] = ' / ' ;
len + = parent - > namelen + 1 ;
parent = parent - > parent ;
}
}
2019-09-01 12:53:20 +00:00
static void send_add_tlv_path ( send_context * context , uint16_t type , send_dir * parent , char * name , uint16_t namelen ) {
uint16_t len = find_path_len ( parent , namelen ) ;
2017-09-08 08:02:43 +00:00
send_add_tlv ( context , type , NULL , len ) ;
if ( len > 0 )
find_path ( ( char * ) & context - > data [ context - > datalen - len ] , parent , name , namelen ) ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS found_path ( send_context * context , send_dir * parent , char * name , uint16_t namelen ) {
2017-09-08 08:02:43 +00:00
ULONG pos = context - > datalen ;
if ( context - > lastinode . o ) {
send_command ( context , BTRFS_SEND_CMD_RENAME ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv_path ( context , BTRFS_SEND_TLV_PATH , context - > root_dir , context - > lastinode . o - > tmpname , ( uint16_t ) strlen ( context - > lastinode . o - > tmpname ) ) ;
2017-09-08 08:02:43 +00:00
send_add_tlv_path ( context , BTRFS_SEND_TLV_PATH_TO , parent , name , namelen ) ;
send_command_finish ( context , pos ) ;
} else {
send_command ( context , BTRFS_SEND_CMD_LINK ) ;
send_add_tlv_path ( context , BTRFS_SEND_TLV_PATH , parent , name , namelen ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH_LINK , context - > lastinode . path , context - > lastinode . path ? ( uint16_t ) strlen ( context - > lastinode . path ) : 0 ) ;
2017-09-08 08:02:43 +00:00
send_command_finish ( context , pos ) ;
}
if ( context - > lastinode . o ) {
2019-09-01 12:53:20 +00:00
uint16_t pathlen ;
2017-09-08 08:02:43 +00:00
if ( context - > lastinode . o - > sd ) {
if ( context - > lastinode . o - > sd - > name )
ExFreePool ( context - > lastinode . o - > sd - > name ) ;
context - > lastinode . o - > sd - > name = ExAllocatePoolWithTag ( PagedPool , namelen , ALLOC_TAG ) ;
if ( ! context - > lastinode . o - > sd - > name ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( context - > lastinode . o - > sd - > name , name , namelen ) ;
context - > lastinode . o - > sd - > namelen = namelen ;
context - > lastinode . o - > sd - > parent = parent ;
}
if ( context - > lastinode . path )
ExFreePool ( context - > lastinode . path ) ;
pathlen = find_path_len ( parent , namelen ) ;
context - > lastinode . path = ExAllocatePoolWithTag ( PagedPool , pathlen + 1 , ALLOC_TAG ) ;
if ( ! context - > lastinode . path ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
find_path ( context - > lastinode . path , parent , name , namelen ) ;
context - > lastinode . path [ pathlen ] = 0 ;
RemoveEntryList ( & context - > lastinode . o - > list_entry ) ;
ExFreePool ( context - > lastinode . o ) ;
context - > lastinode . o = NULL ;
}
return STATUS_SUCCESS ;
}
static void send_utimes_command_dir ( send_context * context , send_dir * sd , BTRFS_TIME * atime , BTRFS_TIME * mtime , BTRFS_TIME * ctime ) {
ULONG pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_UTIMES ) ;
send_add_tlv_path ( context , BTRFS_SEND_TLV_PATH , sd - > parent , sd - > name , sd - > namelen ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_ATIME , atime , sizeof ( BTRFS_TIME ) ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_MTIME , mtime , sizeof ( BTRFS_TIME ) ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_CTIME , ctime , sizeof ( BTRFS_TIME ) ) ;
send_command_finish ( context , pos ) ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS find_send_dir ( send_context * context , uint64_t dir , uint64_t generation , send_dir * * psd , bool * added_dummy ) {
2017-09-08 08:02:43 +00:00
NTSTATUS Status ;
LIST_ENTRY * le ;
char name [ 64 ] ;
le = context - > dirs . Flink ;
while ( le ! = & context - > dirs ) {
send_dir * sd2 = CONTAINING_RECORD ( le , send_dir , list_entry ) ;
if ( sd2 - > inode > dir )
break ;
else if ( sd2 - > inode = = dir ) {
* psd = sd2 ;
if ( added_dummy )
2019-09-01 12:53:20 +00:00
* added_dummy = false ;
2017-09-08 08:02:43 +00:00
return STATUS_SUCCESS ;
}
le = le - > Flink ;
}
if ( dir = = SUBVOL_ROOT_INODE ) {
2019-09-01 12:53:20 +00:00
Status = send_add_dir ( context , dir , NULL , NULL , 0 , false , le , psd ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_add_dir returned %08x \n " , Status ) ;
return Status ;
}
if ( added_dummy )
2019-09-01 12:53:20 +00:00
* added_dummy = false ;
2017-09-08 08:02:43 +00:00
return STATUS_SUCCESS ;
}
if ( context - > parent ) {
KEY searchkey ;
traverse_ptr tp ;
searchkey . obj_id = dir ;
searchkey . obj_type = TYPE_INODE_REF ; // directories should never have an extiref
searchkey . offset = 0xffffffffffffffff ;
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > parent , & tp , & searchkey , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( tp . item - > key . obj_id = = searchkey . obj_id & & tp . item - > key . obj_type = = searchkey . obj_type ) {
INODE_REF * ir = ( INODE_REF * ) tp . item - > data ;
send_dir * parent ;
if ( tp . item - > size < sizeof ( INODE_REF ) | | tp . item - > size < offsetof ( INODE_REF , name [ 0 ] ) + ir - > n ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was truncated \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
2017-09-08 08:02:43 +00:00
return STATUS_INTERNAL_ERROR ;
}
if ( tp . item - > key . offset = = SUBVOL_ROOT_INODE )
parent = context - > root_dir ;
else {
Status = find_send_dir ( context , tp . item - > key . offset , generation , & parent , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_send_dir returned %08x \n " , Status ) ;
return Status ;
}
}
2019-09-01 12:53:20 +00:00
Status = send_add_dir ( context , dir , parent , ir - > name , ir - > n , true , NULL , psd ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_add_dir returned %08x \n " , Status ) ;
return Status ;
}
if ( added_dummy )
2019-09-01 12:53:20 +00:00
* added_dummy = false ;
2017-09-08 08:02:43 +00:00
return STATUS_SUCCESS ;
}
}
Status = get_orphan_name ( context , dir , generation , name ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " get_orphan_name returned %08x \n " , Status ) ;
return Status ;
}
2019-09-01 12:53:20 +00:00
Status = send_add_dir ( context , dir , NULL , name , ( uint16_t ) strlen ( name ) , true , le , psd ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_add_dir returned %08x \n " , Status ) ;
return Status ;
}
if ( added_dummy )
2019-09-01 12:53:20 +00:00
* added_dummy = true ;
2017-09-08 08:02:43 +00:00
return STATUS_SUCCESS ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS send_inode_ref ( send_context * context , traverse_ptr * tp , bool tree2 ) {
2017-09-08 08:02:43 +00:00
NTSTATUS Status ;
2019-09-01 12:53:20 +00:00
uint64_t inode = tp ? tp - > item - > key . obj_id : 0 , dir = tp ? tp - > item - > key . offset : 0 ;
2017-09-08 08:02:43 +00:00
LIST_ENTRY * le ;
INODE_REF * ir ;
2019-09-01 12:53:20 +00:00
uint16_t len ;
2017-09-08 08:02:43 +00:00
send_dir * sd = NULL ;
orphan * o2 = NULL ;
if ( inode = = dir ) // root
return STATUS_SUCCESS ;
if ( tp - > item - > size < sizeof ( INODE_REF ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected at least %u \n " , tp - > item - > key . obj_id , tp - > item - > key . obj_type , tp - > item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp - > item - > size , sizeof ( INODE_REF ) ) ;
return STATUS_INTERNAL_ERROR ;
}
if ( dir ! = SUBVOL_ROOT_INODE ) {
2019-09-01 12:53:20 +00:00
bool added_dummy ;
2017-09-08 08:02:43 +00:00
Status = find_send_dir ( context , dir , context - > root - > root_item . ctransid , & sd , & added_dummy ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_send_dir returned %08x \n " , Status ) ;
return Status ;
}
// directory has higher inode number than file, so might need to be created
if ( added_dummy ) {
2019-09-01 12:53:20 +00:00
bool found = false ;
2017-09-08 08:02:43 +00:00
le = context - > orphans . Flink ;
while ( le ! = & context - > orphans ) {
o2 = CONTAINING_RECORD ( le , orphan , list_entry ) ;
if ( o2 - > inode = = dir ) {
2019-09-01 12:53:20 +00:00
found = true ;
2017-09-08 08:02:43 +00:00
break ;
} else if ( o2 - > inode > dir )
break ;
le = le - > Flink ;
}
if ( ! found ) {
ULONG pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_MKDIR ) ;
send_add_tlv_path ( context , BTRFS_SEND_TLV_PATH , NULL , sd - > name , sd - > namelen ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_INODE , & dir , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
send_command_finish ( context , pos ) ;
o2 = ExAllocatePoolWithTag ( PagedPool , sizeof ( orphan ) , ALLOC_TAG ) ;
if ( ! o2 ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
o2 - > inode = dir ;
2019-09-01 12:53:20 +00:00
o2 - > dir = true ;
2017-09-08 08:02:43 +00:00
memcpy ( o2 - > tmpname , sd - > name , sd - > namelen ) ;
o2 - > tmpname [ sd - > namelen ] = 0 ;
o2 - > sd = sd ;
add_orphan ( context , o2 ) ;
}
}
} else
sd = context - > root_dir ;
len = tp - > item - > size ;
ir = ( INODE_REF * ) tp - > item - > data ;
while ( len > 0 ) {
ref * r ;
if ( len < sizeof ( INODE_REF ) | | len < offsetof ( INODE_REF , name [ 0 ] ) + ir - > n ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was truncated \n " , tp - > item - > key . obj_id , tp - > item - > key . obj_type , tp - > item - > key . offset ) ;
2017-09-08 08:02:43 +00:00
return STATUS_INTERNAL_ERROR ;
}
r = ExAllocatePoolWithTag ( PagedPool , offsetof ( ref , name [ 0 ] ) + ir - > n , ALLOC_TAG ) ;
if ( ! r ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
r - > sd = sd ;
r - > namelen = ir - > n ;
RtlCopyMemory ( r - > name , ir - > name , ir - > n ) ;
InsertTailList ( tree2 ? & context - > lastinode . oldrefs : & context - > lastinode . refs , & r - > list_entry ) ;
2019-09-01 12:53:20 +00:00
len - = ( uint16_t ) offsetof ( INODE_REF , name [ 0 ] ) + ir - > n ;
2017-09-08 08:02:43 +00:00
ir = ( INODE_REF * ) & ir - > name [ ir - > n ] ;
}
return STATUS_SUCCESS ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS send_inode_extref ( send_context * context , traverse_ptr * tp , bool tree2 ) {
2017-09-08 08:02:43 +00:00
INODE_EXTREF * ier ;
2019-09-01 12:53:20 +00:00
uint16_t len ;
2017-09-08 08:02:43 +00:00
if ( tp - > item - > size < sizeof ( INODE_EXTREF ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected at least %u \n " , tp - > item - > key . obj_id , tp - > item - > key . obj_type , tp - > item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp - > item - > size , sizeof ( INODE_EXTREF ) ) ;
return STATUS_INTERNAL_ERROR ;
}
len = tp - > item - > size ;
ier = ( INODE_EXTREF * ) tp - > item - > data ;
while ( len > 0 ) {
NTSTATUS Status ;
send_dir * sd = NULL ;
orphan * o2 = NULL ;
ref * r ;
if ( len < sizeof ( INODE_EXTREF ) | | len < offsetof ( INODE_EXTREF , name [ 0 ] ) + ier - > n ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was truncated \n " , tp - > item - > key . obj_id , tp - > item - > key . obj_type , tp - > item - > key . offset ) ;
2017-09-08 08:02:43 +00:00
return STATUS_INTERNAL_ERROR ;
}
if ( ier - > dir ! = SUBVOL_ROOT_INODE ) {
LIST_ENTRY * le ;
2019-09-01 12:53:20 +00:00
bool added_dummy ;
2017-09-08 08:02:43 +00:00
Status = find_send_dir ( context , ier - > dir , context - > root - > root_item . ctransid , & sd , & added_dummy ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_send_dir returned %08x \n " , Status ) ;
return Status ;
}
// directory has higher inode number than file, so might need to be created
if ( added_dummy ) {
2019-09-01 12:53:20 +00:00
bool found = false ;
2017-09-08 08:02:43 +00:00
le = context - > orphans . Flink ;
while ( le ! = & context - > orphans ) {
o2 = CONTAINING_RECORD ( le , orphan , list_entry ) ;
if ( o2 - > inode = = ier - > dir ) {
2019-09-01 12:53:20 +00:00
found = true ;
2017-09-08 08:02:43 +00:00
break ;
} else if ( o2 - > inode > ier - > dir )
break ;
le = le - > Flink ;
}
if ( ! found ) {
ULONG pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_MKDIR ) ;
send_add_tlv_path ( context , BTRFS_SEND_TLV_PATH , NULL , sd - > name , sd - > namelen ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_INODE , & ier - > dir , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
send_command_finish ( context , pos ) ;
o2 = ExAllocatePoolWithTag ( PagedPool , sizeof ( orphan ) , ALLOC_TAG ) ;
if ( ! o2 ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
o2 - > inode = ier - > dir ;
2019-09-01 12:53:20 +00:00
o2 - > dir = true ;
2017-09-08 08:02:43 +00:00
memcpy ( o2 - > tmpname , sd - > name , sd - > namelen ) ;
o2 - > tmpname [ sd - > namelen ] = 0 ;
o2 - > sd = sd ;
add_orphan ( context , o2 ) ;
}
}
} else
sd = context - > root_dir ;
r = ExAllocatePoolWithTag ( PagedPool , offsetof ( ref , name [ 0 ] ) + ier - > n , ALLOC_TAG ) ;
if ( ! r ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
r - > sd = sd ;
r - > namelen = ier - > n ;
RtlCopyMemory ( r - > name , ier - > name , ier - > n ) ;
InsertTailList ( tree2 ? & context - > lastinode . oldrefs : & context - > lastinode . refs , & r - > list_entry ) ;
2019-09-01 12:53:20 +00:00
len - = ( uint16_t ) offsetof ( INODE_EXTREF , name [ 0 ] ) + ier - > n ;
2017-09-08 08:02:43 +00:00
ier = ( INODE_EXTREF * ) & ier - > name [ ier - > n ] ;
}
return STATUS_SUCCESS ;
}
static void send_subvol_header ( send_context * context , root * r , file_ref * fr ) {
ULONG pos = context - > datalen ;
send_command ( context , context - > parent ? BTRFS_SEND_CMD_SNAPSHOT : BTRFS_SEND_CMD_SUBVOL ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , fr - > dc - > utf8 . Buffer , fr - > dc - > utf8 . Length ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_UUID , r - > root_item . rtransid = = 0 ? & r - > root_item . uuid : & r - > root_item . received_uuid , sizeof ( BTRFS_UUID ) ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_TRANSID , & r - > root_item . ctransid , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
if ( context - > parent ) {
send_add_tlv ( context , BTRFS_SEND_TLV_CLONE_UUID ,
context - > parent - > root_item . rtransid = = 0 ? & context - > parent - > root_item . uuid : & context - > parent - > root_item . received_uuid , sizeof ( BTRFS_UUID ) ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_CLONE_CTRANSID , & context - > parent - > root_item . ctransid , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
}
send_command_finish ( context , pos ) ;
}
2019-09-01 12:53:20 +00:00
static void send_chown_command ( send_context * context , char * path , uint64_t uid , uint64_t gid ) {
2017-09-08 08:02:43 +00:00
ULONG pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_CHOWN ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , path , path ? ( uint16_t ) strlen ( path ) : 0 ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_UID , & uid , sizeof ( uint64_t ) ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_GID , & gid , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
send_command_finish ( context , pos ) ;
}
2019-09-01 12:53:20 +00:00
static void send_chmod_command ( send_context * context , char * path , uint64_t mode ) {
2017-09-08 08:02:43 +00:00
ULONG pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_CHMOD ) ;
mode & = 07777 ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , path , path ? ( uint16_t ) strlen ( path ) : 0 ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_MODE , & mode , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
send_command_finish ( context , pos ) ;
}
static void send_utimes_command ( send_context * context , char * path , BTRFS_TIME * atime , BTRFS_TIME * mtime , BTRFS_TIME * ctime ) {
ULONG pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_UTIMES ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , path , path ? ( uint16_t ) strlen ( path ) : 0 ) ;
2017-09-08 08:02:43 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_ATIME , atime , sizeof ( BTRFS_TIME ) ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_MTIME , mtime , sizeof ( BTRFS_TIME ) ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_CTIME , ctime , sizeof ( BTRFS_TIME ) ) ;
send_command_finish ( context , pos ) ;
}
2019-09-01 12:53:20 +00:00
static void send_truncate_command ( send_context * context , char * path , uint64_t size ) {
2017-09-08 08:02:43 +00:00
ULONG pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_TRUNCATE ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , path , path ? ( uint16_t ) strlen ( path ) : 0 ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_SIZE , & size , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
send_command_finish ( context , pos ) ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS send_unlink_command ( send_context * context , send_dir * parent , uint16_t namelen , char * name ) {
2017-09-08 08:02:43 +00:00
ULONG pos = context - > datalen ;
2019-09-01 12:53:20 +00:00
uint16_t pathlen ;
2017-09-08 08:02:43 +00:00
send_command ( context , BTRFS_SEND_CMD_UNLINK ) ;
pathlen = find_path_len ( parent , namelen ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , NULL , pathlen ) ;
find_path ( ( char * ) & context - > data [ context - > datalen - pathlen ] , parent , name , namelen ) ;
send_command_finish ( context , pos ) ;
return STATUS_SUCCESS ;
}
2019-09-01 12:53:20 +00:00
static void send_rmdir_command ( send_context * context , uint16_t pathlen , char * path ) {
2017-09-08 08:02:43 +00:00
ULONG pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_RMDIR ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , path , pathlen ) ;
send_command_finish ( context , pos ) ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS get_dir_last_child ( send_context * context , uint64_t * last_inode ) {
2017-09-08 08:02:43 +00:00
NTSTATUS Status ;
KEY searchkey ;
traverse_ptr tp ;
* last_inode = 0 ;
searchkey . obj_id = context - > lastinode . inode ;
searchkey . obj_type = TYPE_DIR_INDEX ;
searchkey . offset = 2 ;
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > parent , & tp , & searchkey , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
return Status ;
}
do {
traverse_ptr next_tp ;
if ( tp . item - > key . obj_id = = searchkey . obj_id & & tp . item - > key . obj_type = = searchkey . obj_type ) {
DIR_ITEM * di = ( DIR_ITEM * ) tp . item - > data ;
if ( tp . item - > size < sizeof ( DIR_ITEM ) | | tp . item - > size < offsetof ( DIR_ITEM , name [ 0 ] ) + di - > m + di - > n ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was truncated \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
2017-09-08 08:02:43 +00:00
return STATUS_INTERNAL_ERROR ;
}
if ( di - > key . obj_type = = TYPE_INODE_ITEM )
* last_inode = max ( * last_inode , di - > key . obj_id ) ;
} else
break ;
2019-09-01 12:53:20 +00:00
if ( find_next_item ( context - > Vcb , & tp , & next_tp , false , NULL ) )
2017-09-08 08:02:43 +00:00
tp = next_tp ;
else
break ;
2019-09-01 12:53:20 +00:00
} while ( true ) ;
2017-09-08 08:02:43 +00:00
return STATUS_SUCCESS ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS add_pending_rmdir ( send_context * context , uint64_t last_inode ) {
2017-09-08 08:02:43 +00:00
pending_rmdir * pr ;
LIST_ENTRY * le ;
pr = ExAllocatePoolWithTag ( PagedPool , sizeof ( pending_rmdir ) , ALLOC_TAG ) ;
if ( ! pr ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
pr - > sd = context - > lastinode . sd ;
pr - > last_child_inode = last_inode ;
le = context - > pending_rmdirs . Flink ;
while ( le ! = & context - > pending_rmdirs ) {
pending_rmdir * pr2 = CONTAINING_RECORD ( le , pending_rmdir , list_entry ) ;
if ( pr2 - > last_child_inode > pr - > last_child_inode ) {
InsertHeadList ( pr2 - > list_entry . Blink , & pr - > list_entry ) ;
return STATUS_SUCCESS ;
}
le = le - > Flink ;
}
InsertTailList ( & context - > pending_rmdirs , & pr - > list_entry ) ;
return STATUS_SUCCESS ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS look_for_collision ( send_context * context , send_dir * sd , char * name , ULONG namelen , uint64_t * inode , bool * dir ) {
2017-09-08 08:02:43 +00:00
NTSTATUS Status ;
KEY searchkey ;
traverse_ptr tp ;
DIR_ITEM * di ;
2019-09-01 12:53:20 +00:00
uint16_t len ;
2017-09-08 08:02:43 +00:00
searchkey . obj_id = sd - > inode ;
searchkey . obj_type = TYPE_DIR_ITEM ;
2019-09-01 12:53:20 +00:00
searchkey . offset = calc_crc32c ( 0xfffffffe , ( uint8_t * ) name , namelen ) ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > parent , & tp , & searchkey , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( keycmp ( tp . item - > key , searchkey ) )
return STATUS_SUCCESS ;
di = ( DIR_ITEM * ) tp . item - > data ;
len = tp . item - > size ;
do {
if ( len < sizeof ( DIR_ITEM ) | | len < offsetof ( DIR_ITEM , name [ 0 ] ) + di - > m + di - > n ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was truncated \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
2017-09-08 08:02:43 +00:00
return STATUS_INTERNAL_ERROR ;
}
if ( di - > n = = namelen & & RtlCompareMemory ( di - > name , name , namelen ) = = namelen ) {
* inode = di - > key . obj_type = = TYPE_INODE_ITEM ? di - > key . obj_id : 0 ;
2019-09-01 12:53:20 +00:00
* dir = di - > type = = BTRFS_TYPE_DIRECTORY ? true : false ;
2017-09-08 08:02:43 +00:00
return STATUS_OBJECT_NAME_COLLISION ;
}
di = ( DIR_ITEM * ) & di - > name [ di - > m + di - > n ] ;
2019-09-01 12:53:20 +00:00
len - = ( uint16_t ) offsetof ( DIR_ITEM , name [ 0 ] ) + di - > m + di - > n ;
2017-09-08 08:02:43 +00:00
} while ( len > 0 ) ;
return STATUS_SUCCESS ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS make_file_orphan ( send_context * context , uint64_t inode , bool dir , uint64_t generation , ref * r ) {
2017-09-08 08:02:43 +00:00
NTSTATUS Status ;
ULONG pos = context - > datalen ;
send_dir * sd = NULL ;
orphan * o ;
LIST_ENTRY * le ;
char name [ 64 ] ;
if ( ! dir ) {
deleted_child * dc ;
dc = ExAllocatePoolWithTag ( PagedPool , offsetof ( deleted_child , name [ 0 ] ) + r - > namelen , ALLOC_TAG ) ;
if ( ! dc ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
dc - > namelen = r - > namelen ;
RtlCopyMemory ( dc - > name , r - > name , r - > namelen ) ;
InsertTailList ( & r - > sd - > deleted_children , & dc - > list_entry ) ;
}
le = context - > orphans . Flink ;
while ( le ! = & context - > orphans ) {
orphan * o2 = CONTAINING_RECORD ( le , orphan , list_entry ) ;
if ( o2 - > inode = = inode ) {
send_command ( context , BTRFS_SEND_CMD_UNLINK ) ;
send_add_tlv_path ( context , BTRFS_SEND_TLV_PATH , r - > sd , r - > name , r - > namelen ) ;
send_command_finish ( context , pos ) ;
return STATUS_SUCCESS ;
} else if ( o2 - > inode > inode )
break ;
le = le - > Flink ;
}
Status = get_orphan_name ( context , inode , generation , name ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " get_orphan_name returned %08x \n " , Status ) ;
return Status ;
}
if ( dir ) {
Status = find_send_dir ( context , inode , generation , & sd , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_send_dir returned %08x \n " , Status ) ;
return Status ;
}
2019-09-01 12:53:20 +00:00
sd - > dummy = true ;
2017-09-08 08:02:43 +00:00
send_command ( context , BTRFS_SEND_CMD_RENAME ) ;
send_add_tlv_path ( context , BTRFS_SEND_TLV_PATH , r - > sd , r - > name , r - > namelen ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv_path ( context , BTRFS_SEND_TLV_PATH_TO , context - > root_dir , name , ( uint16_t ) strlen ( name ) ) ;
2017-09-08 08:02:43 +00:00
send_command_finish ( context , pos ) ;
if ( sd - > name )
ExFreePool ( sd - > name ) ;
2019-09-01 12:53:20 +00:00
sd - > namelen = ( uint16_t ) strlen ( name ) ;
2017-09-08 08:02:43 +00:00
sd - > name = ExAllocatePoolWithTag ( PagedPool , sd - > namelen , ALLOC_TAG ) ;
if ( ! sd - > name ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( sd - > name , name , sd - > namelen ) ;
sd - > parent = context - > root_dir ;
} else {
send_command ( context , BTRFS_SEND_CMD_RENAME ) ;
send_add_tlv_path ( context , BTRFS_SEND_TLV_PATH , r - > sd , r - > name , r - > namelen ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv_path ( context , BTRFS_SEND_TLV_PATH_TO , context - > root_dir , name , ( uint16_t ) strlen ( name ) ) ;
2017-09-08 08:02:43 +00:00
send_command_finish ( context , pos ) ;
}
o = ExAllocatePoolWithTag ( PagedPool , sizeof ( orphan ) , ALLOC_TAG ) ;
if ( ! o ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
o - > inode = inode ;
2019-09-01 12:53:20 +00:00
o - > dir = true ;
2017-09-08 08:02:43 +00:00
strcpy ( o - > tmpname , name ) ;
o - > sd = sd ;
add_orphan ( context , o ) ;
return STATUS_SUCCESS ;
}
static NTSTATUS flush_refs ( send_context * context , traverse_ptr * tp1 , traverse_ptr * tp2 ) {
NTSTATUS Status ;
LIST_ENTRY * le ;
ref * nameref = NULL , * nameref2 = NULL ;
if ( context - > lastinode . mode & __S_IFDIR ) { // directory
ref * r = IsListEmpty ( & context - > lastinode . refs ) ? NULL : CONTAINING_RECORD ( context - > lastinode . refs . Flink , ref , list_entry ) ;
ref * or = IsListEmpty ( & context - > lastinode . oldrefs ) ? NULL : CONTAINING_RECORD ( context - > lastinode . oldrefs . Flink , ref , list_entry ) ;
if ( or & & ! context - > lastinode . o ) {
ULONG len = find_path_len ( or - > sd , or - > namelen ) ;
context - > lastinode . path = ExAllocatePoolWithTag ( PagedPool , len + 1 , ALLOC_TAG ) ;
if ( ! context - > lastinode . path ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
find_path ( context - > lastinode . path , or - > sd , or - > name , or - > namelen ) ;
context - > lastinode . path [ len ] = 0 ;
if ( ! context - > lastinode . sd ) {
2019-09-01 12:53:20 +00:00
Status = find_send_dir ( context , context - > lastinode . inode , context - > lastinode . gen , & context - > lastinode . sd , false ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_send_dir returned %08x \n " , Status ) ;
return Status ;
}
}
}
if ( r & & or ) {
2019-09-01 12:53:20 +00:00
uint64_t inode ;
bool dir ;
2017-09-08 08:02:43 +00:00
Status = look_for_collision ( context , r - > sd , r - > name , r - > namelen , & inode , & dir ) ;
if ( ! NT_SUCCESS ( Status ) & & Status ! = STATUS_OBJECT_NAME_COLLISION ) {
ERR ( " look_for_collision returned %08x \n " , Status ) ;
return Status ;
}
if ( Status = = STATUS_OBJECT_NAME_COLLISION & & inode > context - > lastinode . inode ) {
Status = make_file_orphan ( context , inode , dir , context - > parent - > root_item . ctransid , r ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " make_file_orphan returned %08x \n " , Status ) ;
return Status ;
}
}
if ( context - > lastinode . o ) {
Status = found_path ( context , r - > sd , r - > name , r - > namelen ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " found_path returned %08x \n " , Status ) ;
return Status ;
}
if ( ! r - > sd - > dummy )
send_utimes_command_dir ( context , r - > sd , & r - > sd - > atime , & r - > sd - > mtime , & r - > sd - > ctime ) ;
} else if ( r - > sd ! = or - > sd | | r - > namelen ! = or - > namelen | | RtlCompareMemory ( r - > name , or - > name , r - > namelen ) ! = r - > namelen ) { // moved or renamed
ULONG pos = context - > datalen , len ;
send_command ( context , BTRFS_SEND_CMD_RENAME ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , context - > lastinode . path , ( uint16_t ) strlen ( context - > lastinode . path ) ) ;
2017-09-08 08:02:43 +00:00
send_add_tlv_path ( context , BTRFS_SEND_TLV_PATH_TO , r - > sd , r - > name , r - > namelen ) ;
send_command_finish ( context , pos ) ;
if ( ! r - > sd - > dummy )
send_utimes_command_dir ( context , r - > sd , & r - > sd - > atime , & r - > sd - > mtime , & r - > sd - > ctime ) ;
if ( context - > lastinode . sd - > name )
ExFreePool ( context - > lastinode . sd - > name ) ;
context - > lastinode . sd - > name = ExAllocatePoolWithTag ( PagedPool , r - > namelen , ALLOC_TAG ) ;
if ( ! context - > lastinode . sd - > name ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( context - > lastinode . sd - > name , r - > name , r - > namelen ) ;
context - > lastinode . sd - > parent = r - > sd ;
if ( context - > lastinode . path )
ExFreePool ( context - > lastinode . path ) ;
len = find_path_len ( r - > sd , r - > namelen ) ;
context - > lastinode . path = ExAllocatePoolWithTag ( PagedPool , len + 1 , ALLOC_TAG ) ;
if ( ! context - > lastinode . path ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
find_path ( context - > lastinode . path , r - > sd , r - > name , r - > namelen ) ;
context - > lastinode . path [ len ] = 0 ;
}
} else if ( r & & ! or ) { // new
Status = found_path ( context , r - > sd , r - > name , r - > namelen ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " found_path returned %08x \n " , Status ) ;
return Status ;
}
if ( ! r - > sd - > dummy )
send_utimes_command_dir ( context , r - > sd , & r - > sd - > atime , & r - > sd - > mtime , & r - > sd - > ctime ) ;
} else { // deleted
2019-09-01 12:53:20 +00:00
uint64_t last_inode ;
2017-09-08 08:02:43 +00:00
Status = get_dir_last_child ( context , & last_inode ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " get_dir_last_child returned %08x \n " , Status ) ;
return Status ;
}
if ( last_inode < = context - > lastinode . inode ) {
2019-09-01 12:53:20 +00:00
send_rmdir_command ( context , ( uint16_t ) strlen ( context - > lastinode . path ) , context - > lastinode . path ) ;
2017-09-08 08:02:43 +00:00
if ( ! or - > sd - > dummy )
send_utimes_command_dir ( context , or - > sd , & or - > sd - > atime , & or - > sd - > mtime , & or - > sd - > ctime ) ;
} else {
char name [ 64 ] ;
ULONG pos = context - > datalen ;
Status = get_orphan_name ( context , context - > lastinode . inode , context - > lastinode . gen , name ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " get_orphan_name returned %08x \n " , Status ) ;
return Status ;
}
send_command ( context , BTRFS_SEND_CMD_RENAME ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , context - > lastinode . path , ( uint16_t ) strlen ( context - > lastinode . path ) ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_PATH_TO , name , ( uint16_t ) strlen ( name ) ) ;
2017-09-08 08:02:43 +00:00
send_command_finish ( context , pos ) ;
if ( context - > lastinode . sd - > name )
ExFreePool ( context - > lastinode . sd - > name ) ;
context - > lastinode . sd - > name = ExAllocatePoolWithTag ( PagedPool , strlen ( name ) , ALLOC_TAG ) ;
if ( ! context - > lastinode . sd - > name ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( context - > lastinode . sd - > name , name , strlen ( name ) ) ;
2019-09-01 12:53:20 +00:00
context - > lastinode . sd - > namelen = ( uint16_t ) strlen ( name ) ;
context - > lastinode . sd - > dummy = true ;
2017-09-08 08:02:43 +00:00
context - > lastinode . sd - > parent = NULL ;
send_utimes_command ( context , NULL , & context - > root_dir - > atime , & context - > root_dir - > mtime , & context - > root_dir - > ctime ) ;
Status = add_pending_rmdir ( context , last_inode ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " add_pending_rmdir returned %08x \n " , Status ) ;
return Status ;
}
}
}
while ( ! IsListEmpty ( & context - > lastinode . refs ) ) {
r = CONTAINING_RECORD ( RemoveHeadList ( & context - > lastinode . refs ) , ref , list_entry ) ;
ExFreePool ( r ) ;
}
while ( ! IsListEmpty ( & context - > lastinode . oldrefs ) ) {
or = CONTAINING_RECORD ( RemoveHeadList ( & context - > lastinode . oldrefs ) , ref , list_entry ) ;
ExFreePool ( or ) ;
}
return STATUS_SUCCESS ;
} else {
if ( ! IsListEmpty ( & context - > lastinode . oldrefs ) ) {
ref * or = CONTAINING_RECORD ( context - > lastinode . oldrefs . Flink , ref , list_entry ) ;
ULONG len = find_path_len ( or - > sd , or - > namelen ) ;
context - > lastinode . path = ExAllocatePoolWithTag ( PagedPool , len + 1 , ALLOC_TAG ) ;
if ( ! context - > lastinode . path ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
find_path ( context - > lastinode . path , or - > sd , or - > name , or - > namelen ) ;
context - > lastinode . path [ len ] = 0 ;
nameref = or ;
}
// remove unchanged refs
le = context - > lastinode . oldrefs . Flink ;
while ( le ! = & context - > lastinode . oldrefs ) {
ref * or = CONTAINING_RECORD ( le , ref , list_entry ) ;
LIST_ENTRY * le2 ;
2019-09-01 12:53:20 +00:00
bool matched = false ;
2017-09-08 08:02:43 +00:00
le2 = context - > lastinode . refs . Flink ;
while ( le2 ! = & context - > lastinode . refs ) {
ref * r = CONTAINING_RECORD ( le2 , ref , list_entry ) ;
if ( r - > sd = = or - > sd & & r - > namelen = = or - > namelen & & RtlCompareMemory ( r - > name , or - > name , r - > namelen ) = = r - > namelen ) {
RemoveEntryList ( & r - > list_entry ) ;
ExFreePool ( r ) ;
2019-09-01 12:53:20 +00:00
matched = true ;
2017-09-08 08:02:43 +00:00
break ;
}
le2 = le2 - > Flink ;
}
if ( matched ) {
le = le - > Flink ;
RemoveEntryList ( & or - > list_entry ) ;
ExFreePool ( or ) ;
continue ;
}
le = le - > Flink ;
}
while ( ! IsListEmpty ( & context - > lastinode . refs ) ) {
ref * r = CONTAINING_RECORD ( RemoveHeadList ( & context - > lastinode . refs ) , ref , list_entry ) ;
2019-09-01 12:53:20 +00:00
uint64_t inode ;
bool dir ;
2017-09-08 08:02:43 +00:00
if ( context - > parent ) {
Status = look_for_collision ( context , r - > sd , r - > name , r - > namelen , & inode , & dir ) ;
if ( ! NT_SUCCESS ( Status ) & & Status ! = STATUS_OBJECT_NAME_COLLISION ) {
ERR ( " look_for_collision returned %08x \n " , Status ) ;
return Status ;
}
if ( Status = = STATUS_OBJECT_NAME_COLLISION & & inode > context - > lastinode . inode ) {
Status = make_file_orphan ( context , inode , dir , context - > lastinode . gen , r ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " make_file_orphan returned %08x \n " , Status ) ;
return Status ;
}
}
}
if ( context - > datalen > SEND_BUFFER_LENGTH ) {
Status = wait_for_flush ( context , tp1 , tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " wait_for_flush returned %08x \n " , Status ) ;
return Status ;
}
if ( context - > send - > cancelling )
return STATUS_SUCCESS ;
}
Status = found_path ( context , r - > sd , r - > name , r - > namelen ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " found_path returned %08x \n " , Status ) ;
return Status ;
}
if ( ! r - > sd - > dummy )
send_utimes_command_dir ( context , r - > sd , & r - > sd - > atime , & r - > sd - > mtime , & r - > sd - > ctime ) ;
if ( nameref & & ! nameref2 )
nameref2 = r ;
else
ExFreePool ( r ) ;
}
while ( ! IsListEmpty ( & context - > lastinode . oldrefs ) ) {
ref * or = CONTAINING_RECORD ( RemoveHeadList ( & context - > lastinode . oldrefs ) , ref , list_entry ) ;
2019-09-01 12:53:20 +00:00
bool deleted = false ;
2017-09-08 08:02:43 +00:00
le = or - > sd - > deleted_children . Flink ;
while ( le ! = & or - > sd - > deleted_children ) {
deleted_child * dc = CONTAINING_RECORD ( le , deleted_child , list_entry ) ;
if ( dc - > namelen = = or - > namelen & & RtlCompareMemory ( dc - > name , or - > name , or - > namelen ) = = or - > namelen ) {
RemoveEntryList ( & dc - > list_entry ) ;
ExFreePool ( dc ) ;
2019-09-01 12:53:20 +00:00
deleted = true ;
2017-09-08 08:02:43 +00:00
break ;
}
le = le - > Flink ;
}
if ( ! deleted ) {
if ( context - > datalen > SEND_BUFFER_LENGTH ) {
Status = wait_for_flush ( context , tp1 , tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " wait_for_flush returned %08x \n " , Status ) ;
return Status ;
}
if ( context - > send - > cancelling )
return STATUS_SUCCESS ;
}
Status = send_unlink_command ( context , or - > sd , or - > namelen , or - > name ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_unlink_command returned %08x \n " , Status ) ;
return Status ;
}
if ( ! or - > sd - > dummy )
send_utimes_command_dir ( context , or - > sd , & or - > sd - > atime , & or - > sd - > mtime , & or - > sd - > ctime ) ;
}
if ( or = = nameref & & nameref2 ) {
2019-09-01 12:53:20 +00:00
uint16_t len = find_path_len ( nameref2 - > sd , nameref2 - > namelen ) ;
2017-09-08 08:02:43 +00:00
if ( context - > lastinode . path )
ExFreePool ( context - > lastinode . path ) ;
context - > lastinode . path = ExAllocatePoolWithTag ( PagedPool , len + 1 , ALLOC_TAG ) ;
if ( ! context - > lastinode . path ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
find_path ( context - > lastinode . path , nameref2 - > sd , nameref2 - > name , nameref2 - > namelen ) ;
context - > lastinode . path [ len ] = 0 ;
ExFreePool ( nameref2 ) ;
}
ExFreePool ( or ) ;
}
}
return STATUS_SUCCESS ;
}
static NTSTATUS wait_for_flush ( send_context * context , traverse_ptr * tp1 , traverse_ptr * tp2 ) {
NTSTATUS Status ;
KEY key1 , key2 ;
if ( tp1 )
key1 = tp1 - > item - > key ;
if ( tp2 )
key2 = tp2 - > item - > key ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
KeClearEvent ( & context - > send - > cleared_event ) ;
2019-09-01 12:53:20 +00:00
KeSetEvent ( & context - > buffer_event , 0 , true ) ;
KeWaitForSingleObject ( & context - > send - > cleared_event , Executive , KernelMode , false , NULL ) ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
ExAcquireResourceSharedLite ( & context - > Vcb - > tree_lock , true ) ;
2017-09-08 08:02:43 +00:00
if ( context - > send - > cancelling )
return STATUS_SUCCESS ;
if ( tp1 ) {
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > root , tp1 , & key1 , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( keycmp ( tp1 - > item - > key , key1 ) ) {
ERR ( " readonly subvolume changed \n " ) ;
return STATUS_INTERNAL_ERROR ;
}
}
if ( tp2 ) {
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > parent , tp2 , & key2 , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( keycmp ( tp2 - > item - > key , key2 ) ) {
ERR ( " readonly subvolume changed \n " ) ;
return STATUS_INTERNAL_ERROR ;
}
}
return STATUS_SUCCESS ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS add_ext_holes ( device_extension * Vcb , LIST_ENTRY * exts , uint64_t size ) {
uint64_t lastoff = 0 ;
2017-09-08 08:02:43 +00:00
LIST_ENTRY * le ;
le = exts - > Flink ;
while ( le ! = exts ) {
send_ext * ext = CONTAINING_RECORD ( le , send_ext , list_entry ) ;
if ( ext - > offset > lastoff ) {
send_ext * ext2 = ExAllocatePoolWithTag ( PagedPool , offsetof ( send_ext , data . data ) + sizeof ( EXTENT_DATA2 ) , ALLOC_TAG ) ;
EXTENT_DATA2 * ed2 ;
if ( ! ext2 ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
ed2 = ( EXTENT_DATA2 * ) ext2 - > data . data ;
ext2 - > offset = lastoff ;
ext2 - > datalen = offsetof ( EXTENT_DATA , data ) + sizeof ( EXTENT_DATA2 ) ;
ext2 - > data . decoded_size = ed2 - > num_bytes = ext - > offset - lastoff ;
ext2 - > data . type = EXTENT_TYPE_REGULAR ;
ed2 - > address = ed2 - > size = ed2 - > offset = 0 ;
InsertHeadList ( le - > Blink , & ext2 - > list_entry ) ;
}
if ( ext - > data . type = = EXTENT_TYPE_INLINE )
lastoff = ext - > offset + ext - > data . decoded_size ;
else {
EXTENT_DATA2 * ed2 = ( EXTENT_DATA2 * ) ext - > data . data ;
lastoff = ext - > offset + ed2 - > num_bytes ;
}
le = le - > Flink ;
}
if ( size > lastoff ) {
send_ext * ext2 = ExAllocatePoolWithTag ( PagedPool , offsetof ( send_ext , data . data ) + sizeof ( EXTENT_DATA2 ) , ALLOC_TAG ) ;
EXTENT_DATA2 * ed2 ;
if ( ! ext2 ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
ed2 = ( EXTENT_DATA2 * ) ext2 - > data . data ;
ext2 - > offset = lastoff ;
ext2 - > datalen = offsetof ( EXTENT_DATA , data ) + sizeof ( EXTENT_DATA2 ) ;
2018-12-16 11:03:16 +00:00
ext2 - > data . decoded_size = ed2 - > num_bytes = sector_align ( size - lastoff , Vcb - > superblock . sector_size ) ;
2017-09-08 08:02:43 +00:00
ext2 - > data . type = EXTENT_TYPE_REGULAR ;
ed2 - > address = ed2 - > size = ed2 - > offset = 0 ;
InsertTailList ( exts , & ext2 - > list_entry ) ;
}
return STATUS_SUCCESS ;
}
2019-09-01 12:53:20 +00:00
static NTSTATUS divide_ext ( send_ext * ext , uint64_t len , bool trunc ) {
2017-09-08 08:02:43 +00:00
send_ext * ext2 ;
EXTENT_DATA2 * ed2a , * ed2b ;
if ( ext - > data . type = = EXTENT_TYPE_INLINE ) {
if ( ! trunc ) {
ext2 = ExAllocatePoolWithTag ( PagedPool , ( ULONG ) ( offsetof ( send_ext , data . data ) + ext - > data . decoded_size - len ) , ALLOC_TAG ) ;
if ( ! ext2 ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
ext2 - > offset = ext - > offset + len ;
ext2 - > datalen = ( ULONG ) ( ext - > data . decoded_size - len ) ;
ext2 - > data . decoded_size = ext - > data . decoded_size - len ;
ext2 - > data . compression = ext - > data . compression ;
ext2 - > data . encryption = ext - > data . encryption ;
ext2 - > data . encoding = ext - > data . encoding ;
ext2 - > data . type = ext - > data . type ;
RtlCopyMemory ( ext2 - > data . data , ext - > data . data + len , ( ULONG ) ( ext - > data . decoded_size - len ) ) ;
InsertHeadList ( & ext - > list_entry , & ext2 - > list_entry ) ;
}
ext - > data . decoded_size = len ;
return STATUS_SUCCESS ;
}
ed2a = ( EXTENT_DATA2 * ) ext - > data . data ;
if ( ! trunc ) {
ext2 = ExAllocatePoolWithTag ( PagedPool , offsetof ( send_ext , data . data ) + sizeof ( EXTENT_DATA2 ) , ALLOC_TAG ) ;
if ( ! ext2 ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
ed2b = ( EXTENT_DATA2 * ) ext2 - > data . data ;
ext2 - > offset = ext - > offset + len ;
ext2 - > datalen = offsetof ( EXTENT_DATA , data ) + sizeof ( EXTENT_DATA2 ) ;
ext2 - > data . compression = ext - > data . compression ;
ext2 - > data . encryption = ext - > data . encryption ;
ext2 - > data . encoding = ext - > data . encoding ;
ext2 - > data . type = ext - > data . type ;
ed2b - > num_bytes = ed2a - > num_bytes - len ;
if ( ed2a - > size = = 0 ) {
ext2 - > data . decoded_size = ed2b - > num_bytes ;
ext - > data . decoded_size = len ;
ed2b - > address = ed2b - > size = ed2b - > offset = 0 ;
} else {
ext2 - > data . decoded_size = ext - > data . decoded_size ;
ed2b - > address = ed2a - > address ;
ed2b - > size = ed2a - > size ;
ed2b - > offset = ed2a - > offset + len ;
}
InsertHeadList ( & ext - > list_entry , & ext2 - > list_entry ) ;
}
ed2a - > num_bytes = len ;
return STATUS_SUCCESS ;
}
static NTSTATUS sync_ext_cutoff_points ( send_context * context ) {
NTSTATUS Status ;
send_ext * ext1 , * ext2 ;
ext1 = CONTAINING_RECORD ( context - > lastinode . exts . Flink , send_ext , list_entry ) ;
ext2 = CONTAINING_RECORD ( context - > lastinode . oldexts . Flink , send_ext , list_entry ) ;
do {
2019-09-01 12:53:20 +00:00
uint64_t len1 , len2 ;
2017-09-08 08:02:43 +00:00
EXTENT_DATA2 * ed2a , * ed2b ;
ed2a = ext1 - > data . type = = EXTENT_TYPE_INLINE ? NULL : ( EXTENT_DATA2 * ) ext1 - > data . data ;
ed2b = ext2 - > data . type = = EXTENT_TYPE_INLINE ? NULL : ( EXTENT_DATA2 * ) ext2 - > data . data ;
len1 = ed2a ? ed2a - > num_bytes : ext1 - > data . decoded_size ;
len2 = ed2b ? ed2b - > num_bytes : ext2 - > data . decoded_size ;
if ( len1 < len2 ) {
2019-09-01 12:53:20 +00:00
Status = divide_ext ( ext2 , len1 , false ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " divide_ext returned %08x \n " , Status ) ;
return Status ;
}
} else if ( len2 < len1 ) {
2019-09-01 12:53:20 +00:00
Status = divide_ext ( ext1 , len2 , false ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " divide_ext returned %08x \n " , Status ) ;
return Status ;
}
}
if ( ext1 - > list_entry . Flink = = & context - > lastinode . exts | | ext2 - > list_entry . Flink = = & context - > lastinode . oldexts )
break ;
ext1 = CONTAINING_RECORD ( ext1 - > list_entry . Flink , send_ext , list_entry ) ;
ext2 = CONTAINING_RECORD ( ext2 - > list_entry . Flink , send_ext , list_entry ) ;
2019-09-01 12:53:20 +00:00
} while ( true ) ;
2017-09-08 08:02:43 +00:00
ext1 = CONTAINING_RECORD ( context - > lastinode . exts . Blink , send_ext , list_entry ) ;
ext2 = CONTAINING_RECORD ( context - > lastinode . oldexts . Blink , send_ext , list_entry ) ;
2019-09-01 12:53:20 +00:00
Status = divide_ext ( ext1 , context - > lastinode . size - ext1 - > offset , true ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " divide_ext returned %08x \n " , Status ) ;
return Status ;
}
2019-09-01 12:53:20 +00:00
Status = divide_ext ( ext2 , context - > lastinode . size - ext2 - > offset , true ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " divide_ext returned %08x \n " , Status ) ;
return Status ;
}
return STATUS_SUCCESS ;
}
2019-09-01 12:53:20 +00:00
static bool send_add_tlv_clone_path ( send_context * context , root * r , uint64_t inode ) {
2017-09-08 08:02:43 +00:00
NTSTATUS Status ;
KEY searchkey ;
traverse_ptr tp ;
2019-09-01 12:53:20 +00:00
uint16_t len = 0 ;
uint64_t num ;
uint8_t * ptr ;
2017-09-08 08:02:43 +00:00
num = inode ;
while ( num ! = SUBVOL_ROOT_INODE ) {
searchkey . obj_id = num ;
searchkey . obj_type = TYPE_INODE_EXTREF ;
searchkey . offset = 0xffffffffffffffff ;
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , r , & tp , & searchkey , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
2019-09-01 12:53:20 +00:00
return false ;
2017-09-08 08:02:43 +00:00
}
if ( tp . item - > key . obj_id ! = searchkey . obj_id | | ( tp . item - > key . obj_type ! = TYPE_INODE_REF & & tp . item - > key . obj_type ! = TYPE_INODE_EXTREF ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " could not find INODE_REF for inode %I64x \n " , searchkey . obj_id ) ;
return false ;
2017-09-08 08:02:43 +00:00
}
if ( len > 0 )
len + + ;
if ( tp . item - > key . obj_type = = TYPE_INODE_REF ) {
INODE_REF * ir = ( INODE_REF * ) tp . item - > data ;
if ( tp . item - > size < sizeof ( INODE_REF ) | | tp . item - > size < offsetof ( INODE_REF , name [ 0 ] ) + ir - > n ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was truncated \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
return false ;
2017-09-08 08:02:43 +00:00
}
len + = ir - > n ;
num = tp . item - > key . offset ;
} else {
INODE_EXTREF * ier = ( INODE_EXTREF * ) tp . item - > data ;
if ( tp . item - > size < sizeof ( INODE_EXTREF ) | | tp . item - > size < offsetof ( INODE_EXTREF , name [ 0 ] ) + ier - > n ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was truncated \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
return false ;
2017-09-08 08:02:43 +00:00
}
len + = ier - > n ;
num = ier - > dir ;
}
}
send_add_tlv ( context , BTRFS_SEND_TLV_CLONE_PATH , NULL , len ) ;
ptr = & context - > data [ context - > datalen ] ;
num = inode ;
while ( num ! = SUBVOL_ROOT_INODE ) {
searchkey . obj_id = num ;
searchkey . obj_type = TYPE_INODE_EXTREF ;
searchkey . offset = 0xffffffffffffffff ;
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , r , & tp , & searchkey , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
2019-09-01 12:53:20 +00:00
return false ;
2017-09-08 08:02:43 +00:00
}
if ( tp . item - > key . obj_id ! = searchkey . obj_id | | ( tp . item - > key . obj_type ! = TYPE_INODE_REF & & tp . item - > key . obj_type ! = TYPE_INODE_EXTREF ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " could not find INODE_REF for inode %I64x \n " , searchkey . obj_id ) ;
return false ;
2017-09-08 08:02:43 +00:00
}
if ( num ! = inode ) {
ptr - - ;
* ptr = ' / ' ;
}
if ( tp . item - > key . obj_type = = TYPE_INODE_REF ) {
INODE_REF * ir = ( INODE_REF * ) tp . item - > data ;
RtlCopyMemory ( ptr - ir - > n , ir - > name , ir - > n ) ;
ptr - = ir - > n ;
num = tp . item - > key . offset ;
} else {
INODE_EXTREF * ier = ( INODE_EXTREF * ) tp . item - > data ;
RtlCopyMemory ( ptr - ier - > n , ier - > name , ier - > n ) ;
ptr - = ier - > n ;
num = ier - > dir ;
}
}
2019-09-01 12:53:20 +00:00
return true ;
2017-09-08 08:02:43 +00:00
}
2019-09-01 12:53:20 +00:00
static bool try_clone_edr ( send_context * context , send_ext * se , EXTENT_DATA_REF * edr ) {
2017-09-08 08:02:43 +00:00
NTSTATUS Status ;
root * r = NULL ;
KEY searchkey ;
traverse_ptr tp ;
EXTENT_DATA2 * seed2 = ( EXTENT_DATA2 * ) se - > data . data ;
if ( context - > parent & & edr - > root = = context - > parent - > id )
r = context - > parent ;
if ( ! r & & context - > num_clones > 0 ) {
ULONG i ;
for ( i = 0 ; i < context - > num_clones ; i + + ) {
if ( context - > clones [ i ] - > id = = edr - > root & & context - > clones [ i ] ! = context - > root ) {
r = context - > clones [ i ] ;
break ;
}
}
}
if ( ! r )
2019-09-01 12:53:20 +00:00
return false ;
2017-09-08 08:02:43 +00:00
searchkey . obj_id = edr - > objid ;
searchkey . obj_type = TYPE_EXTENT_DATA ;
searchkey . offset = 0 ;
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , r , & tp , & searchkey , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
2019-09-01 12:53:20 +00:00
return false ;
2017-09-08 08:02:43 +00:00
}
2019-09-01 12:53:20 +00:00
while ( true ) {
2017-09-08 08:02:43 +00:00
traverse_ptr next_tp ;
if ( tp . item - > key . obj_id = = searchkey . obj_id & & tp . item - > key . obj_type = = searchkey . obj_type ) {
if ( tp . item - > size < sizeof ( EXTENT_DATA ) )
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) has size %u, not at least %u as expected \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , tp . item - > size , sizeof ( EXTENT_DATA ) ) ;
2017-09-08 08:02:43 +00:00
else {
EXTENT_DATA * ed = ( EXTENT_DATA * ) tp . item - > data ;
if ( ed - > type = = EXTENT_TYPE_REGULAR ) {
if ( tp . item - > size < offsetof ( EXTENT_DATA , data [ 0 ] ) + sizeof ( EXTENT_DATA2 ) )
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) has size %u, not %u as expected \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp . item - > size , offsetof ( EXTENT_DATA , data [ 0 ] ) + sizeof ( EXTENT_DATA2 ) ) ;
else {
EXTENT_DATA2 * ed2 = ( EXTENT_DATA2 * ) ed - > data ;
if ( ed2 - > address = = seed2 - > address & & ed2 - > size = = seed2 - > size & & seed2 - > offset < = ed2 - > offset & & seed2 - > offset + seed2 - > num_bytes > = ed2 - > offset + ed2 - > num_bytes ) {
2019-09-01 12:53:20 +00:00
uint64_t clone_offset = tp . item - > key . offset + ed2 - > offset - seed2 - > offset ;
uint64_t clone_len = min ( context - > lastinode . size - se - > offset , ed2 - > num_bytes ) ;
2017-09-08 08:02:43 +00:00
if ( clone_offset % context - > Vcb - > superblock . sector_size = = 0 & & clone_len % context - > Vcb - > superblock . sector_size = = 0 ) {
ULONG pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_CLONE ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_OFFSET , & se - > offset , sizeof ( uint64_t ) ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_CLONE_LENGTH , & clone_len , sizeof ( uint64_t ) ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , context - > lastinode . path , context - > lastinode . path ? ( uint16_t ) strlen ( context - > lastinode . path ) : 0 ) ;
2017-09-08 08:02:43 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_CLONE_UUID , r - > root_item . rtransid = = 0 ? & r - > root_item . uuid : & r - > root_item . received_uuid , sizeof ( BTRFS_UUID ) ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_CLONE_CTRANSID , & r - > root_item . ctransid , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
if ( ! send_add_tlv_clone_path ( context , r , tp . item - > key . obj_id ) )
context - > datalen = pos ;
else {
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_CLONE_OFFSET , & clone_offset , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
send_command_finish ( context , pos ) ;
2019-09-01 12:53:20 +00:00
return true ;
2017-09-08 08:02:43 +00:00
}
}
}
}
}
}
} else if ( tp . item - > key . obj_id > searchkey . obj_id | | ( tp . item - > key . obj_id = = searchkey . obj_id & & tp . item - > key . obj_type > searchkey . obj_type ) )
break ;
2019-09-01 12:53:20 +00:00
if ( find_next_item ( context - > Vcb , & tp , & next_tp , false , NULL ) )
2017-09-08 08:02:43 +00:00
tp = next_tp ;
else
break ;
}
2019-09-01 12:53:20 +00:00
return false ;
2017-09-08 08:02:43 +00:00
}
2019-09-01 12:53:20 +00:00
static bool try_clone ( send_context * context , send_ext * se ) {
2017-09-08 08:02:43 +00:00
NTSTATUS Status ;
KEY searchkey ;
traverse_ptr tp ;
EXTENT_DATA2 * ed2 = ( EXTENT_DATA2 * ) se - > data . data ;
EXTENT_ITEM * ei ;
2019-09-01 12:53:20 +00:00
uint64_t rc = 0 ;
2017-09-08 08:02:43 +00:00
searchkey . obj_id = ed2 - > address ;
searchkey . obj_type = TYPE_EXTENT_ITEM ;
searchkey . offset = ed2 - > size ;
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > Vcb - > extent_root , & tp , & searchkey , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
2019-09-01 12:53:20 +00:00
return false ;
2017-09-08 08:02:43 +00:00
}
if ( keycmp ( tp . item - > key , searchkey ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) not found \n " , searchkey . obj_id , searchkey . obj_type , searchkey . offset ) ;
return false ;
2017-09-08 08:02:43 +00:00
}
if ( tp . item - > size < sizeof ( EXTENT_ITEM ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected at least %u \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , tp . item - > size , sizeof ( EXTENT_ITEM ) ) ;
return false ;
2017-09-08 08:02:43 +00:00
}
ei = ( EXTENT_ITEM * ) tp . item - > data ;
if ( tp . item - > size > sizeof ( EXTENT_ITEM ) ) {
2019-09-01 12:53:20 +00:00
uint32_t len = tp . item - > size - sizeof ( EXTENT_ITEM ) ;
uint8_t * ptr = ( uint8_t * ) & ei [ 1 ] ;
2017-09-08 08:02:43 +00:00
while ( len > 0 ) {
2019-09-01 12:53:20 +00:00
uint8_t secttype = * ptr ;
2017-09-08 08:02:43 +00:00
ULONG sectlen = get_extent_data_len ( secttype ) ;
2019-09-01 12:53:20 +00:00
uint64_t sectcount = get_extent_data_refcount ( secttype , ptr + sizeof ( uint8_t ) ) ;
2017-09-08 08:02:43 +00:00
len - - ;
if ( sectlen > len ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x): %x bytes left, expecting at least %x \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , len , sectlen ) ;
return false ;
2017-09-08 08:02:43 +00:00
}
if ( sectlen = = 0 ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x): unrecognized extent type %x \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , secttype ) ;
return false ;
2017-09-08 08:02:43 +00:00
}
rc + = sectcount ;
if ( secttype = = TYPE_EXTENT_DATA_REF ) {
2019-09-01 12:53:20 +00:00
EXTENT_DATA_REF * sectedr = ( EXTENT_DATA_REF * ) ( ptr + sizeof ( uint8_t ) ) ;
2017-09-08 08:02:43 +00:00
if ( try_clone_edr ( context , se , sectedr ) )
2019-09-01 12:53:20 +00:00
return true ;
2017-09-08 08:02:43 +00:00
}
len - = sectlen ;
2019-09-01 12:53:20 +00:00
ptr + = sizeof ( uint8_t ) + sectlen ;
2017-09-08 08:02:43 +00:00
}
}
if ( rc > = ei - > refcount )
2019-09-01 12:53:20 +00:00
return false ;
2017-09-08 08:02:43 +00:00
searchkey . obj_type = TYPE_EXTENT_DATA_REF ;
searchkey . offset = 0 ;
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > Vcb - > extent_root , & tp , & searchkey , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
2019-09-01 12:53:20 +00:00
return false ;
2017-09-08 08:02:43 +00:00
}
2019-09-01 12:53:20 +00:00
while ( true ) {
2017-09-08 08:02:43 +00:00
traverse_ptr next_tp ;
if ( tp . item - > key . obj_id = = searchkey . obj_id & & tp . item - > key . obj_type = = searchkey . obj_type ) {
if ( tp . item - > size < sizeof ( EXTENT_DATA_REF ) )
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) has size %u, not %u as expected \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , tp . item - > size , sizeof ( EXTENT_DATA_REF ) ) ;
2017-09-08 08:02:43 +00:00
else {
if ( try_clone_edr ( context , se , ( EXTENT_DATA_REF * ) tp . item - > data ) )
2019-09-01 12:53:20 +00:00
return true ;
2017-09-08 08:02:43 +00:00
}
} else if ( tp . item - > key . obj_id > searchkey . obj_id | | ( tp . item - > key . obj_id = = searchkey . obj_id & & tp . item - > key . obj_type > searchkey . obj_type ) )
break ;
2019-09-01 12:53:20 +00:00
if ( find_next_item ( context - > Vcb , & tp , & next_tp , false , NULL ) )
2017-09-08 08:02:43 +00:00
tp = next_tp ;
else
break ;
}
2019-09-01 12:53:20 +00:00
return false ;
2017-09-08 08:02:43 +00:00
}
static NTSTATUS flush_extents ( send_context * context , traverse_ptr * tp1 , traverse_ptr * tp2 ) {
NTSTATUS Status ;
if ( ( IsListEmpty ( & context - > lastinode . exts ) & & IsListEmpty ( & context - > lastinode . oldexts ) ) | | context - > lastinode . size = = 0 )
return STATUS_SUCCESS ;
if ( context - > parent ) {
2018-12-16 11:03:16 +00:00
Status = add_ext_holes ( context - > Vcb , & context - > lastinode . exts , context - > lastinode . size ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " add_ext_holes returned %08x \n " , Status ) ;
return Status ;
}
2018-12-16 11:03:16 +00:00
Status = add_ext_holes ( context - > Vcb , & context - > lastinode . oldexts , context - > lastinode . size ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " add_ext_holes returned %08x \n " , Status ) ;
return Status ;
}
Status = sync_ext_cutoff_points ( context ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " sync_ext_cutoff_points returned %08x \n " , Status ) ;
return Status ;
}
}
while ( ! IsListEmpty ( & context - > lastinode . exts ) ) {
send_ext * se = CONTAINING_RECORD ( RemoveHeadList ( & context - > lastinode . exts ) , send_ext , list_entry ) ;
send_ext * se2 = context - > parent ? CONTAINING_RECORD ( RemoveHeadList ( & context - > lastinode . oldexts ) , send_ext , list_entry ) : NULL ;
ULONG pos ;
EXTENT_DATA2 * ed2 ;
if ( se2 ) {
if ( se - > data . type = = EXTENT_TYPE_INLINE & & se2 - > data . type = = EXTENT_TYPE_INLINE & &
RtlCompareMemory ( se - > data . data , se2 - > data . data , ( ULONG ) se - > data . decoded_size ) = = ( ULONG ) se - > data . decoded_size ) {
ExFreePool ( se ) ;
ExFreePool ( se2 ) ;
continue ;
}
if ( se - > data . type = = EXTENT_TYPE_REGULAR & & se2 - > data . type = = EXTENT_TYPE_REGULAR ) {
EXTENT_DATA2 * ed2a , * ed2b ;
ed2a = ( EXTENT_DATA2 * ) se - > data . data ;
ed2b = ( EXTENT_DATA2 * ) se2 - > data . data ;
if ( RtlCompareMemory ( ed2a , ed2b , sizeof ( EXTENT_DATA2 ) ) = = sizeof ( EXTENT_DATA2 ) ) {
ExFreePool ( se ) ;
ExFreePool ( se2 ) ;
continue ;
}
}
}
if ( se - > data . type = = EXTENT_TYPE_INLINE ) {
pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_WRITE ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , context - > lastinode . path , context - > lastinode . path ? ( uint16_t ) strlen ( context - > lastinode . path ) : 0 ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_OFFSET , & se - > offset , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
if ( se - > data . compression = = BTRFS_COMPRESSION_NONE )
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_DATA , se - > data . data , ( uint16_t ) se - > data . decoded_size ) ;
2018-12-16 11:03:16 +00:00
else if ( se - > data . compression = = BTRFS_COMPRESSION_ZLIB | | se - > data . compression = = BTRFS_COMPRESSION_LZO | | se - > data . compression = = BTRFS_COMPRESSION_ZSTD ) {
2017-09-08 08:02:43 +00:00
ULONG inlen = se - > datalen - ( ULONG ) offsetof ( EXTENT_DATA , data [ 0 ] ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_DATA , NULL , ( uint16_t ) se - > data . decoded_size ) ;
2017-09-08 08:02:43 +00:00
RtlZeroMemory ( & context - > data [ context - > datalen - se - > data . decoded_size ] , ( ULONG ) se - > data . decoded_size ) ;
if ( se - > data . compression = = BTRFS_COMPRESSION_ZLIB ) {
2019-09-01 12:53:20 +00:00
Status = zlib_decompress ( se - > data . data , inlen , & context - > data [ context - > datalen - se - > data . decoded_size ] , ( uint32_t ) se - > data . decoded_size ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " zlib_decompress returned %08x \n " , Status ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return Status ;
}
} else if ( se - > data . compression = = BTRFS_COMPRESSION_LZO ) {
2019-09-01 12:53:20 +00:00
if ( inlen < sizeof ( uint32_t ) ) {
2017-09-08 08:02:43 +00:00
ERR ( " extent data was truncated \n " ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return STATUS_INTERNAL_ERROR ;
} else
2019-09-01 12:53:20 +00:00
inlen - = sizeof ( uint32_t ) ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
Status = lzo_decompress ( se - > data . data + sizeof ( uint32_t ) , inlen , & context - > data [ context - > datalen - se - > data . decoded_size ] , ( uint32_t ) se - > data . decoded_size , sizeof ( uint32_t ) ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " lzo_decompress returned %08x \n " , Status ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return Status ;
}
2018-12-16 11:03:16 +00:00
} else if ( se - > data . compression = = BTRFS_COMPRESSION_ZSTD ) {
2019-09-01 12:53:20 +00:00
Status = zstd_decompress ( se - > data . data , inlen , & context - > data [ context - > datalen - se - > data . decoded_size ] , ( uint32_t ) se - > data . decoded_size ) ;
2018-12-16 11:03:16 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " zlib_decompress returned %08x \n " , Status ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return Status ;
}
2017-09-08 08:02:43 +00:00
}
} else {
ERR ( " unhandled compression type %x \n " , se - > data . compression ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return STATUS_NOT_IMPLEMENTED ;
}
send_command_finish ( context , pos ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
continue ;
}
ed2 = ( EXTENT_DATA2 * ) se - > data . data ;
if ( ed2 - > size ! = 0 & & ( context - > parent | | context - > num_clones > 0 ) ) {
if ( try_clone ( context , se ) ) {
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
continue ;
}
}
if ( ed2 - > size = = 0 ) { // write sparse
2019-09-01 12:53:20 +00:00
uint64_t off , offset ;
2017-09-08 08:02:43 +00:00
for ( off = ed2 - > offset ; off < ed2 - > offset + ed2 - > num_bytes ; off + = MAX_SEND_WRITE ) {
2019-09-01 12:53:20 +00:00
uint16_t length = ( uint16_t ) min ( min ( ed2 - > offset + ed2 - > num_bytes - off , MAX_SEND_WRITE ) , context - > lastinode . size - se - > offset - off ) ;
2017-09-08 08:02:43 +00:00
if ( context - > datalen > SEND_BUFFER_LENGTH ) {
Status = wait_for_flush ( context , tp1 , tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " wait_for_flush returned %08x \n " , Status ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return Status ;
}
if ( context - > send - > cancelling ) {
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return STATUS_SUCCESS ;
}
}
pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_WRITE ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , context - > lastinode . path , context - > lastinode . path ? ( uint16_t ) strlen ( context - > lastinode . path ) : 0 ) ;
2017-09-08 08:02:43 +00:00
offset = se - > offset + off ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_OFFSET , & offset , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_DATA , NULL , length ) ;
RtlZeroMemory ( & context - > data [ context - > datalen - length ] , length ) ;
send_command_finish ( context , pos ) ;
}
} else if ( se - > data . compression = = BTRFS_COMPRESSION_NONE ) {
2019-09-01 12:53:20 +00:00
uint64_t off , offset ;
uint8_t * buf ;
2017-09-08 08:02:43 +00:00
buf = ExAllocatePoolWithTag ( NonPagedPool , MAX_SEND_WRITE + ( 2 * context - > Vcb - > superblock . sector_size ) , ALLOC_TAG ) ;
if ( ! buf ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
for ( off = ed2 - > offset ; off < ed2 - > offset + ed2 - > num_bytes ; off + = MAX_SEND_WRITE ) {
2019-09-01 12:53:20 +00:00
uint16_t length = ( uint16_t ) min ( ed2 - > offset + ed2 - > num_bytes - off , MAX_SEND_WRITE ) ;
2017-09-08 08:02:43 +00:00
ULONG skip_start ;
2019-09-01 12:53:20 +00:00
uint64_t addr = ed2 - > address + off ;
uint32_t * csum ;
2017-09-08 08:02:43 +00:00
if ( context - > datalen > SEND_BUFFER_LENGTH ) {
Status = wait_for_flush ( context , tp1 , tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " wait_for_flush returned %08x \n " , Status ) ;
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return Status ;
}
if ( context - > send - > cancelling ) {
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return STATUS_SUCCESS ;
}
}
skip_start = addr % context - > Vcb - > superblock . sector_size ;
addr - = skip_start ;
if ( context - > lastinode . flags & BTRFS_INODE_NODATASUM )
csum = NULL ;
else {
2019-09-01 12:53:20 +00:00
uint32_t len ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
len = ( uint32_t ) sector_align ( length + skip_start , context - > Vcb - > superblock . sector_size ) / context - > Vcb - > superblock . sector_size ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
csum = ExAllocatePoolWithTag ( PagedPool , len * sizeof ( uint32_t ) , ALLOC_TAG ) ;
2017-09-08 08:02:43 +00:00
if ( ! csum ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
Status = load_csum ( context - > Vcb , csum , addr , len , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " load_csum returned %08x \n " , Status ) ;
ExFreePool ( csum ) ;
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
}
2019-09-01 12:53:20 +00:00
Status = read_data ( context - > Vcb , addr , ( uint32_t ) sector_align ( length + skip_start , context - > Vcb - > superblock . sector_size ) ,
csum , false , buf , NULL , NULL , NULL , 0 , false , NormalPagePriority ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " read_data returned %08x \n " , Status ) ;
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
if ( csum ) ExFreePool ( csum ) ;
return Status ;
}
if ( csum )
ExFreePool ( csum ) ;
pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_WRITE ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , context - > lastinode . path , context - > lastinode . path ? ( uint16_t ) strlen ( context - > lastinode . path ) : 0 ) ;
2017-09-08 08:02:43 +00:00
offset = se - > offset + off ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_OFFSET , & offset , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
length = ( uint16_t ) min ( context - > lastinode . size - se - > offset - off , length ) ;
2017-09-08 08:02:43 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_DATA , buf + skip_start , length ) ;
send_command_finish ( context , pos ) ;
}
ExFreePool ( buf ) ;
} else {
2019-09-01 12:53:20 +00:00
uint8_t * buf , * compbuf ;
uint64_t off ;
uint32_t * csum ;
2017-09-08 08:02:43 +00:00
buf = ExAllocatePoolWithTag ( PagedPool , ( ULONG ) se - > data . decoded_size , ALLOC_TAG ) ;
if ( ! buf ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
compbuf = ExAllocatePoolWithTag ( PagedPool , ( ULONG ) ed2 - > size , ALLOC_TAG ) ;
if ( ! compbuf ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
if ( context - > lastinode . flags & BTRFS_INODE_NODATASUM )
csum = NULL ;
else {
2019-09-01 12:53:20 +00:00
uint32_t len ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
len = ( uint32_t ) ( ed2 - > size / context - > Vcb - > superblock . sector_size ) ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
csum = ExAllocatePoolWithTag ( PagedPool , len * sizeof ( uint32_t ) , ALLOC_TAG ) ;
2017-09-08 08:02:43 +00:00
if ( ! csum ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( compbuf ) ;
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
Status = load_csum ( context - > Vcb , csum , ed2 - > address , len , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " load_csum returned %08x \n " , Status ) ;
ExFreePool ( csum ) ;
ExFreePool ( compbuf ) ;
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return Status ;
}
}
2019-09-01 12:53:20 +00:00
Status = read_data ( context - > Vcb , ed2 - > address , ( uint32_t ) ed2 - > size , csum , false , compbuf , NULL , NULL , NULL , 0 , false , NormalPagePriority ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " read_data returned %08x \n " , Status ) ;
ExFreePool ( compbuf ) ;
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
if ( csum ) ExFreePool ( csum ) ;
return Status ;
}
if ( csum )
ExFreePool ( csum ) ;
if ( se - > data . compression = = BTRFS_COMPRESSION_ZLIB ) {
2019-09-01 12:53:20 +00:00
Status = zlib_decompress ( compbuf , ( uint32_t ) ed2 - > size , buf , ( uint32_t ) se - > data . decoded_size ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " zlib_decompress returned %08x \n " , Status ) ;
ExFreePool ( compbuf ) ;
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return Status ;
}
} else if ( se - > data . compression = = BTRFS_COMPRESSION_LZO ) {
2019-09-01 12:53:20 +00:00
Status = lzo_decompress ( & compbuf [ sizeof ( uint32_t ) ] , ( uint32_t ) ed2 - > size , buf , ( uint32_t ) se - > data . decoded_size , sizeof ( uint32_t ) ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " lzo_decompress returned %08x \n " , Status ) ;
ExFreePool ( compbuf ) ;
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return Status ;
}
2018-12-16 11:03:16 +00:00
} else if ( se - > data . compression = = BTRFS_COMPRESSION_ZSTD ) {
2019-09-01 12:53:20 +00:00
Status = zstd_decompress ( compbuf , ( uint32_t ) ed2 - > size , buf , ( uint32_t ) se - > data . decoded_size ) ;
2018-12-16 11:03:16 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " zstd_decompress returned %08x \n " , Status ) ;
ExFreePool ( compbuf ) ;
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return Status ;
}
2017-09-08 08:02:43 +00:00
}
ExFreePool ( compbuf ) ;
for ( off = ed2 - > offset ; off < ed2 - > offset + ed2 - > num_bytes ; off + = MAX_SEND_WRITE ) {
2019-09-01 12:53:20 +00:00
uint16_t length = ( uint16_t ) min ( ed2 - > offset + ed2 - > num_bytes - off , MAX_SEND_WRITE ) ;
uint64_t offset ;
2017-09-08 08:02:43 +00:00
if ( context - > datalen > SEND_BUFFER_LENGTH ) {
Status = wait_for_flush ( context , tp1 , tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " wait_for_flush returned %08x \n " , Status ) ;
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return Status ;
}
if ( context - > send - > cancelling ) {
ExFreePool ( buf ) ;
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
return STATUS_SUCCESS ;
}
}
pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_WRITE ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , context - > lastinode . path , context - > lastinode . path ? ( uint16_t ) strlen ( context - > lastinode . path ) : 0 ) ;
2017-09-08 08:02:43 +00:00
offset = se - > offset + off ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_OFFSET , & offset , sizeof ( uint64_t ) ) ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
length = ( uint16_t ) min ( context - > lastinode . size - se - > offset - off , length ) ;
2017-09-08 08:02:43 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_DATA , & buf [ off ] , length ) ;
send_command_finish ( context , pos ) ;
}
ExFreePool ( buf ) ;
}
ExFreePool ( se ) ;
if ( se2 ) ExFreePool ( se2 ) ;
}
return STATUS_SUCCESS ;
}
static NTSTATUS finish_inode ( send_context * context , traverse_ptr * tp1 , traverse_ptr * tp2 ) {
LIST_ENTRY * le ;
if ( ! IsListEmpty ( & context - > lastinode . refs ) | | ! IsListEmpty ( & context - > lastinode . oldrefs ) ) {
NTSTATUS Status = flush_refs ( context , tp1 , tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " flush_refs returned %08x \n " , Status ) ;
return Status ;
}
if ( context - > send - > cancelling )
return STATUS_SUCCESS ;
}
if ( ! context - > lastinode . deleting ) {
if ( context - > lastinode . file ) {
NTSTATUS Status = flush_extents ( context , tp1 , tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " flush_extents returned %08x \n " , Status ) ;
return Status ;
}
if ( context - > send - > cancelling )
return STATUS_SUCCESS ;
send_truncate_command ( context , context - > lastinode . path , context - > lastinode . size ) ;
}
if ( context - > lastinode . new | | context - > lastinode . uid ! = context - > lastinode . olduid | | context - > lastinode . gid ! = context - > lastinode . oldgid )
send_chown_command ( context , context - > lastinode . path , context - > lastinode . uid , context - > lastinode . gid ) ;
if ( ( ( context - > lastinode . mode & __S_IFLNK ) ! = __S_IFLNK | | ( ( context - > lastinode . mode & 07777 ) ! = 0777 ) ) & &
( context - > lastinode . new | | context - > lastinode . mode ! = context - > lastinode . oldmode ) )
send_chmod_command ( context , context - > lastinode . path , context - > lastinode . mode ) ;
send_utimes_command ( context , context - > lastinode . path , & context - > lastinode . atime , & context - > lastinode . mtime , & context - > lastinode . ctime ) ;
}
while ( ! IsListEmpty ( & context - > lastinode . exts ) ) {
ExFreePool ( CONTAINING_RECORD ( RemoveHeadList ( & context - > lastinode . exts ) , send_ext , list_entry ) ) ;
}
while ( ! IsListEmpty ( & context - > lastinode . oldexts ) ) {
ExFreePool ( CONTAINING_RECORD ( RemoveHeadList ( & context - > lastinode . oldexts ) , send_ext , list_entry ) ) ;
}
if ( context - > parent ) {
le = context - > pending_rmdirs . Flink ;
while ( le ! = & context - > pending_rmdirs ) {
pending_rmdir * pr = CONTAINING_RECORD ( le , pending_rmdir , list_entry ) ;
if ( pr - > last_child_inode < = context - > lastinode . inode ) {
le = le - > Flink ;
send_rmdir_command ( context , pr - > sd - > namelen , pr - > sd - > name ) ;
RemoveEntryList ( & pr - > sd - > list_entry ) ;
if ( pr - > sd - > name )
ExFreePool ( pr - > sd - > name ) ;
while ( ! IsListEmpty ( & pr - > sd - > deleted_children ) ) {
deleted_child * dc = CONTAINING_RECORD ( RemoveHeadList ( & pr - > sd - > deleted_children ) , deleted_child , list_entry ) ;
ExFreePool ( dc ) ;
}
ExFreePool ( pr - > sd ) ;
RemoveEntryList ( & pr - > list_entry ) ;
ExFreePool ( pr ) ;
} else
break ;
}
}
context - > lastinode . inode = 0 ;
context - > lastinode . o = NULL ;
if ( context - > lastinode . path ) {
ExFreePool ( context - > lastinode . path ) ;
context - > lastinode . path = NULL ;
}
return STATUS_SUCCESS ;
}
static NTSTATUS send_extent_data ( send_context * context , traverse_ptr * tp , traverse_ptr * tp2 ) {
NTSTATUS Status ;
if ( tp & & tp2 & & tp - > item - > size = = tp2 - > item - > size & & RtlCompareMemory ( tp - > item - > data , tp2 - > item - > data , tp - > item - > size ) = = tp - > item - > size )
return STATUS_SUCCESS ;
if ( ! IsListEmpty ( & context - > lastinode . refs ) | | ! IsListEmpty ( & context - > lastinode . oldrefs ) ) {
Status = flush_refs ( context , tp , tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " flush_refs returned %08x \n " , Status ) ;
return Status ;
}
if ( context - > send - > cancelling )
return STATUS_SUCCESS ;
}
if ( ( context - > lastinode . mode & __S_IFLNK ) = = __S_IFLNK )
return STATUS_SUCCESS ;
if ( tp ) {
EXTENT_DATA * ed ;
EXTENT_DATA2 * ed2 = NULL ;
if ( tp - > item - > size < sizeof ( EXTENT_DATA ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected at least %u \n " , tp - > item - > key . obj_id , tp - > item - > key . obj_type , tp - > item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp - > item - > size , sizeof ( EXTENT_DATA ) ) ;
return STATUS_INTERNAL_ERROR ;
}
ed = ( EXTENT_DATA * ) tp - > item - > data ;
if ( ed - > encryption ! = BTRFS_ENCRYPTION_NONE ) {
ERR ( " unknown encryption type %u \n " , ed - > encryption ) ;
return STATUS_INTERNAL_ERROR ;
}
if ( ed - > encoding ! = BTRFS_ENCODING_NONE ) {
ERR ( " unknown encoding type %u \n " , ed - > encoding ) ;
return STATUS_INTERNAL_ERROR ;
}
2018-12-16 11:03:16 +00:00
if ( ed - > compression ! = BTRFS_COMPRESSION_NONE & & ed - > compression ! = BTRFS_COMPRESSION_ZLIB & &
ed - > compression ! = BTRFS_COMPRESSION_LZO & & ed - > compression ! = BTRFS_COMPRESSION_ZSTD ) {
2017-09-08 08:02:43 +00:00
ERR ( " unknown compression type %u \n " , ed - > compression ) ;
return STATUS_INTERNAL_ERROR ;
}
if ( ed - > type = = EXTENT_TYPE_REGULAR ) {
if ( tp - > item - > size < offsetof ( EXTENT_DATA , data [ 0 ] ) + sizeof ( EXTENT_DATA2 ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected %u \n " , tp - > item - > key . obj_id , tp - > item - > key . obj_type , tp - > item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp - > item - > size , offsetof ( EXTENT_DATA , data [ 0 ] ) + sizeof ( EXTENT_DATA2 ) ) ;
return STATUS_INTERNAL_ERROR ;
}
ed2 = ( EXTENT_DATA2 * ) ed - > data ;
} else if ( ed - > type = = EXTENT_TYPE_INLINE ) {
if ( tp - > item - > size < offsetof ( EXTENT_DATA , data [ 0 ] ) + ed - > decoded_size & & ed - > compression = = BTRFS_COMPRESSION_NONE ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected %u \n " , tp - > item - > key . obj_id , tp - > item - > key . obj_type , tp - > item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp - > item - > size , offsetof ( EXTENT_DATA , data [ 0 ] ) + ed - > decoded_size ) ;
return STATUS_INTERNAL_ERROR ;
}
}
if ( ( ed - > type = = EXTENT_TYPE_INLINE | | ( ed - > type = = EXTENT_TYPE_REGULAR & & ed2 - > size ! = 0 ) ) & & ed - > decoded_size ! = 0 ) {
send_ext * se = ExAllocatePoolWithTag ( PagedPool , offsetof ( send_ext , data ) + tp - > item - > size , ALLOC_TAG ) ;
if ( ! se ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
se - > offset = tp - > item - > key . offset ;
se - > datalen = tp - > item - > size ;
RtlCopyMemory ( & se - > data , tp - > item - > data , tp - > item - > size ) ;
InsertTailList ( & context - > lastinode . exts , & se - > list_entry ) ;
}
}
if ( tp2 ) {
EXTENT_DATA * ed ;
EXTENT_DATA2 * ed2 = NULL ;
if ( tp2 - > item - > size < sizeof ( EXTENT_DATA ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected at least %u \n " , tp2 - > item - > key . obj_id , tp2 - > item - > key . obj_type , tp2 - > item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp2 - > item - > size , sizeof ( EXTENT_DATA ) ) ;
return STATUS_INTERNAL_ERROR ;
}
ed = ( EXTENT_DATA * ) tp2 - > item - > data ;
if ( ed - > encryption ! = BTRFS_ENCRYPTION_NONE ) {
ERR ( " unknown encryption type %u \n " , ed - > encryption ) ;
return STATUS_INTERNAL_ERROR ;
}
if ( ed - > encoding ! = BTRFS_ENCODING_NONE ) {
ERR ( " unknown encoding type %u \n " , ed - > encoding ) ;
return STATUS_INTERNAL_ERROR ;
}
2018-12-16 11:03:16 +00:00
if ( ed - > compression ! = BTRFS_COMPRESSION_NONE & & ed - > compression ! = BTRFS_COMPRESSION_ZLIB & &
ed - > compression ! = BTRFS_COMPRESSION_LZO & & ed - > compression ! = BTRFS_COMPRESSION_ZSTD ) {
2017-09-08 08:02:43 +00:00
ERR ( " unknown compression type %u \n " , ed - > compression ) ;
return STATUS_INTERNAL_ERROR ;
}
if ( ed - > type = = EXTENT_TYPE_REGULAR ) {
if ( tp2 - > item - > size < offsetof ( EXTENT_DATA , data [ 0 ] ) + sizeof ( EXTENT_DATA2 ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected %u \n " , tp2 - > item - > key . obj_id , tp2 - > item - > key . obj_type , tp2 - > item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp2 - > item - > size , offsetof ( EXTENT_DATA , data [ 0 ] ) + sizeof ( EXTENT_DATA2 ) ) ;
return STATUS_INTERNAL_ERROR ;
}
ed2 = ( EXTENT_DATA2 * ) ed - > data ;
} else if ( ed - > type = = EXTENT_TYPE_INLINE ) {
if ( tp2 - > item - > size < offsetof ( EXTENT_DATA , data [ 0 ] ) + ed - > decoded_size ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected %u \n " , tp2 - > item - > key . obj_id , tp2 - > item - > key . obj_type , tp2 - > item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp2 - > item - > size , offsetof ( EXTENT_DATA , data [ 0 ] ) + ed - > decoded_size ) ;
return STATUS_INTERNAL_ERROR ;
}
}
if ( ( ed - > type = = EXTENT_TYPE_INLINE | | ( ed - > type = = EXTENT_TYPE_REGULAR & & ed2 - > size ! = 0 ) ) & & ed - > decoded_size ! = 0 ) {
send_ext * se = ExAllocatePoolWithTag ( PagedPool , offsetof ( send_ext , data ) + tp2 - > item - > size , ALLOC_TAG ) ;
if ( ! se ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
se - > offset = tp2 - > item - > key . offset ;
se - > datalen = tp2 - > item - > size ;
RtlCopyMemory ( & se - > data , tp2 - > item - > data , tp2 - > item - > size ) ;
InsertTailList ( & context - > lastinode . oldexts , & se - > list_entry ) ;
}
}
return STATUS_SUCCESS ;
}
typedef struct {
2019-09-01 12:53:20 +00:00
uint16_t namelen ;
2017-09-08 08:02:43 +00:00
char * name ;
2019-09-01 12:53:20 +00:00
uint16_t value1len ;
2017-09-08 08:02:43 +00:00
char * value1 ;
2019-09-01 12:53:20 +00:00
uint16_t value2len ;
2017-09-08 08:02:43 +00:00
char * value2 ;
LIST_ENTRY list_entry ;
} xattr_cmp ;
static NTSTATUS send_xattr ( send_context * context , traverse_ptr * tp , traverse_ptr * tp2 ) {
if ( tp & & tp2 & & tp - > item - > size = = tp2 - > item - > size & & RtlCompareMemory ( tp - > item - > data , tp2 - > item - > data , tp - > item - > size ) = = tp - > item - > size )
return STATUS_SUCCESS ;
if ( ! IsListEmpty ( & context - > lastinode . refs ) | | ! IsListEmpty ( & context - > lastinode . oldrefs ) ) {
NTSTATUS Status = flush_refs ( context , tp , tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " flush_refs returned %08x \n " , Status ) ;
return Status ;
}
if ( context - > send - > cancelling )
return STATUS_SUCCESS ;
}
if ( tp & & tp - > item - > size < sizeof ( DIR_ITEM ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected at least %u \n " , tp - > item - > key . obj_id , tp - > item - > key . obj_type , tp - > item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp - > item - > size , sizeof ( DIR_ITEM ) ) ;
return STATUS_INTERNAL_ERROR ;
}
if ( tp2 & & tp2 - > item - > size < sizeof ( DIR_ITEM ) ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was %u bytes, expected at least %u \n " , tp2 - > item - > key . obj_id , tp2 - > item - > key . obj_type , tp2 - > item - > key . offset ,
2017-09-08 08:02:43 +00:00
tp2 - > item - > size , sizeof ( DIR_ITEM ) ) ;
return STATUS_INTERNAL_ERROR ;
}
if ( tp & & ! tp2 ) {
ULONG len ;
DIR_ITEM * di ;
len = tp - > item - > size ;
di = ( DIR_ITEM * ) tp - > item - > data ;
do {
ULONG pos ;
if ( len < sizeof ( DIR_ITEM ) | | len < offsetof ( DIR_ITEM , name [ 0 ] ) + di - > m + di - > n ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was truncated \n " , tp - > item - > key . obj_id , tp - > item - > key . obj_type , tp - > item - > key . offset ) ;
2017-09-08 08:02:43 +00:00
return STATUS_INTERNAL_ERROR ;
}
pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_SET_XATTR ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , context - > lastinode . path , context - > lastinode . path ? ( uint16_t ) strlen ( context - > lastinode . path ) : 0 ) ;
2017-09-08 08:02:43 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_XATTR_NAME , di - > name , di - > n ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_XATTR_DATA , & di - > name [ di - > n ] , di - > m ) ;
send_command_finish ( context , pos ) ;
len - = ( ULONG ) offsetof ( DIR_ITEM , name [ 0 ] ) + di - > m + di - > n ;
di = ( DIR_ITEM * ) & di - > name [ di - > m + di - > n ] ;
} while ( len > 0 ) ;
} else if ( ! tp & & tp2 ) {
ULONG len ;
DIR_ITEM * di ;
len = tp2 - > item - > size ;
di = ( DIR_ITEM * ) tp2 - > item - > data ;
do {
ULONG pos ;
if ( len < sizeof ( DIR_ITEM ) | | len < offsetof ( DIR_ITEM , name [ 0 ] ) + di - > m + di - > n ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was truncated \n " , tp2 - > item - > key . obj_id , tp2 - > item - > key . obj_type , tp2 - > item - > key . offset ) ;
2017-09-08 08:02:43 +00:00
return STATUS_INTERNAL_ERROR ;
}
pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_REMOVE_XATTR ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , context - > lastinode . path , context - > lastinode . path ? ( uint16_t ) strlen ( context - > lastinode . path ) : 0 ) ;
2017-09-08 08:02:43 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_XATTR_NAME , di - > name , di - > n ) ;
send_command_finish ( context , pos ) ;
len - = ( ULONG ) offsetof ( DIR_ITEM , name [ 0 ] ) + di - > m + di - > n ;
di = ( DIR_ITEM * ) & di - > name [ di - > m + di - > n ] ;
} while ( len > 0 ) ;
} else {
ULONG len ;
DIR_ITEM * di ;
LIST_ENTRY xattrs ;
InitializeListHead ( & xattrs ) ;
len = tp - > item - > size ;
di = ( DIR_ITEM * ) tp - > item - > data ;
do {
xattr_cmp * xa ;
if ( len < sizeof ( DIR_ITEM ) | | len < offsetof ( DIR_ITEM , name [ 0 ] ) + di - > m + di - > n ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was truncated \n " , tp - > item - > key . obj_id , tp - > item - > key . obj_type , tp - > item - > key . offset ) ;
2017-09-08 08:02:43 +00:00
return STATUS_INTERNAL_ERROR ;
}
xa = ExAllocatePoolWithTag ( PagedPool , sizeof ( xattr_cmp ) , ALLOC_TAG ) ;
if ( ! xa ) {
ERR ( " out of memory \n " ) ;
while ( ! IsListEmpty ( & xattrs ) ) {
ExFreePool ( CONTAINING_RECORD ( RemoveHeadList ( & xattrs ) , xattr_cmp , list_entry ) ) ;
}
return STATUS_INSUFFICIENT_RESOURCES ;
}
xa - > namelen = di - > n ;
xa - > name = di - > name ;
xa - > value1len = di - > m ;
xa - > value1 = di - > name + di - > n ;
xa - > value2len = 0 ;
xa - > value2 = NULL ;
InsertTailList ( & xattrs , & xa - > list_entry ) ;
len - = ( ULONG ) offsetof ( DIR_ITEM , name [ 0 ] ) + di - > m + di - > n ;
di = ( DIR_ITEM * ) & di - > name [ di - > m + di - > n ] ;
} while ( len > 0 ) ;
len = tp2 - > item - > size ;
di = ( DIR_ITEM * ) tp2 - > item - > data ;
do {
xattr_cmp * xa ;
LIST_ENTRY * le ;
2019-09-01 12:53:20 +00:00
bool found = false ;
2017-09-08 08:02:43 +00:00
if ( len < sizeof ( DIR_ITEM ) | | len < offsetof ( DIR_ITEM , name [ 0 ] ) + di - > m + di - > n ) {
2019-09-01 12:53:20 +00:00
ERR ( " (%I64x,%x,%I64x) was truncated \n " , tp2 - > item - > key . obj_id , tp2 - > item - > key . obj_type , tp2 - > item - > key . offset ) ;
2017-09-08 08:02:43 +00:00
return STATUS_INTERNAL_ERROR ;
}
le = xattrs . Flink ;
while ( le ! = & xattrs ) {
xa = CONTAINING_RECORD ( le , xattr_cmp , list_entry ) ;
if ( xa - > namelen = = di - > n & & RtlCompareMemory ( xa - > name , di - > name , di - > n ) = = di - > n ) {
xa - > value2len = di - > m ;
xa - > value2 = di - > name + di - > n ;
2019-09-01 12:53:20 +00:00
found = true ;
2017-09-08 08:02:43 +00:00
break ;
}
le = le - > Flink ;
}
if ( ! found ) {
xa = ExAllocatePoolWithTag ( PagedPool , sizeof ( xattr_cmp ) , ALLOC_TAG ) ;
if ( ! xa ) {
ERR ( " out of memory \n " ) ;
while ( ! IsListEmpty ( & xattrs ) ) {
ExFreePool ( CONTAINING_RECORD ( RemoveHeadList ( & xattrs ) , xattr_cmp , list_entry ) ) ;
}
return STATUS_INSUFFICIENT_RESOURCES ;
}
xa - > namelen = di - > n ;
xa - > name = di - > name ;
xa - > value1len = 0 ;
xa - > value1 = NULL ;
xa - > value2len = di - > m ;
xa - > value2 = di - > name + di - > n ;
InsertTailList ( & xattrs , & xa - > list_entry ) ;
}
len - = ( ULONG ) offsetof ( DIR_ITEM , name [ 0 ] ) + di - > m + di - > n ;
di = ( DIR_ITEM * ) & di - > name [ di - > m + di - > n ] ;
} while ( len > 0 ) ;
while ( ! IsListEmpty ( & xattrs ) ) {
xattr_cmp * xa = CONTAINING_RECORD ( RemoveHeadList ( & xattrs ) , xattr_cmp , list_entry ) ;
if ( xa - > value1len ! = xa - > value2len | | ! xa - > value1 | | ! xa - > value2 | | RtlCompareMemory ( xa - > value1 , xa - > value2 , xa - > value1len ) ! = xa - > value1len ) {
ULONG pos ;
if ( ! xa - > value1 ) {
pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_REMOVE_XATTR ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , context - > lastinode . path , context - > lastinode . path ? ( uint16_t ) strlen ( context - > lastinode . path ) : 0 ) ;
2017-09-08 08:02:43 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_XATTR_NAME , xa - > name , xa - > namelen ) ;
send_command_finish ( context , pos ) ;
} else {
pos = context - > datalen ;
send_command ( context , BTRFS_SEND_CMD_SET_XATTR ) ;
2019-09-01 12:53:20 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_PATH , context - > lastinode . path , context - > lastinode . path ? ( uint16_t ) strlen ( context - > lastinode . path ) : 0 ) ;
2017-09-08 08:02:43 +00:00
send_add_tlv ( context , BTRFS_SEND_TLV_XATTR_NAME , xa - > name , xa - > namelen ) ;
send_add_tlv ( context , BTRFS_SEND_TLV_XATTR_DATA , xa - > value1 , xa - > value1len ) ;
send_command_finish ( context , pos ) ;
}
}
ExFreePool ( xa ) ;
}
}
return STATUS_SUCCESS ;
}
_Function_class_ ( KSTART_ROUTINE )
2019-09-01 12:53:20 +00:00
static void __stdcall send_thread ( void * ctx ) {
2017-09-08 08:02:43 +00:00
send_context * context = ( send_context * ) ctx ;
NTSTATUS Status ;
KEY searchkey ;
traverse_ptr tp , tp2 ;
InterlockedIncrement ( & context - > root - > send_ops ) ;
if ( context - > parent )
InterlockedIncrement ( & context - > parent - > send_ops ) ;
if ( context - > clones ) {
ULONG i ;
for ( i = 0 ; i < context - > num_clones ; i + + ) {
InterlockedIncrement ( & context - > clones [ i ] - > send_ops ) ;
}
}
2019-09-01 12:53:20 +00:00
ExAcquireResourceExclusiveLite ( & context - > Vcb - > tree_lock , true ) ;
2017-09-08 08:02:43 +00:00
flush_subvol_fcbs ( context - > root ) ;
if ( context - > parent )
flush_subvol_fcbs ( context - > parent ) ;
if ( context - > Vcb - > need_write )
Status = do_write ( context - > Vcb , NULL ) ;
else
Status = STATUS_SUCCESS ;
free_trees ( context - > Vcb ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " do_write returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
ExConvertExclusiveToSharedLite ( & context - > Vcb - > tree_lock ) ;
searchkey . obj_id = searchkey . offset = 0 ;
searchkey . obj_type = 0 ;
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > root , & tp , & searchkey , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > parent ) {
2019-09-01 12:53:20 +00:00
bool ended1 = false , ended2 = false ;
Status = find_item ( context - > Vcb , context - > parent , & tp2 , & searchkey , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
do {
traverse_ptr next_tp ;
if ( context - > datalen > SEND_BUFFER_LENGTH ) {
KEY key1 = tp . item - > key , key2 = tp2 . item - > key ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
KeClearEvent ( & context - > send - > cleared_event ) ;
2019-09-01 12:53:20 +00:00
KeSetEvent ( & context - > buffer_event , 0 , true ) ;
KeWaitForSingleObject ( & context - > send - > cleared_event , Executive , KernelMode , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( context - > send - > cancelling )
goto end ;
2019-09-01 12:53:20 +00:00
ExAcquireResourceSharedLite ( & context - > Vcb - > tree_lock , true ) ;
2017-09-08 08:02:43 +00:00
if ( ! ended1 ) {
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > root , & tp , & key1 , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( keycmp ( tp . item - > key , key1 ) ) {
ERR ( " readonly subvolume changed \n " ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
Status = STATUS_INTERNAL_ERROR ;
goto end ;
}
}
if ( ! ended2 ) {
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > parent , & tp2 , & key2 , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( keycmp ( tp2 . item - > key , key2 ) ) {
ERR ( " readonly subvolume changed \n " ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
Status = STATUS_INTERNAL_ERROR ;
goto end ;
}
}
}
while ( ! ended1 & & ! ended2 & & tp . tree - > header . address = = tp2 . tree - > header . address ) {
Status = skip_to_difference ( context - > Vcb , & tp , & tp2 , & ended1 , & ended2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " skip_to_difference returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
}
if ( ! ended1 & & ! ended2 & & ! keycmp ( tp . item - > key , tp2 . item - > key ) ) {
2019-09-01 12:53:20 +00:00
bool no_next = false , no_next2 = false ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
TRACE ( " ~ %I64x,%x,%I64x \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
2017-09-08 08:02:43 +00:00
if ( context - > lastinode . inode ! = 0 & & tp . item - > key . obj_id > context - > lastinode . inode ) {
Status = finish_inode ( context , ended1 ? NULL : & tp , ended2 ? NULL : & tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " finish_inode returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > send - > cancelling ) {
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
}
if ( tp . item - > key . obj_type = = TYPE_INODE_ITEM ) {
if ( tp . item - > size = = tp2 . item - > size & & tp . item - > size > 0 & & RtlCompareMemory ( tp . item - > data , tp2 . item - > data , tp . item - > size ) = = tp . item - > size ) {
2019-09-01 12:53:20 +00:00
uint64_t inode = tp . item - > key . obj_id ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
while ( true ) {
if ( ! find_next_item ( context - > Vcb , & tp , & next_tp , false , NULL ) ) {
ended1 = true ;
2017-09-08 08:02:43 +00:00
break ;
}
tp = next_tp ;
if ( tp . item - > key . obj_id ! = inode )
break ;
}
2019-09-01 12:53:20 +00:00
while ( true ) {
if ( ! find_next_item ( context - > Vcb , & tp2 , & next_tp , false , NULL ) ) {
ended2 = true ;
2017-09-08 08:02:43 +00:00
break ;
}
tp2 = next_tp ;
if ( tp2 . item - > key . obj_id ! = inode )
break ;
}
2019-09-01 12:53:20 +00:00
no_next = true ;
} else if ( tp . item - > size > sizeof ( uint64_t ) & & tp2 . item - > size > sizeof ( uint64_t ) & & * ( uint64_t * ) tp . item - > data ! = * ( uint64_t * ) tp2 . item - > data ) {
uint64_t inode = tp . item - > key . obj_id ;
2017-09-08 08:02:43 +00:00
Status = send_inode ( context , NULL , & tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
2019-09-01 12:53:20 +00:00
while ( true ) {
if ( ! find_next_item ( context - > Vcb , & tp2 , & next_tp , false , NULL ) ) {
ended2 = true ;
2017-09-08 08:02:43 +00:00
break ;
}
tp2 = next_tp ;
if ( tp2 . item - > key . obj_id ! = inode )
break ;
if ( tp2 . item - > key . obj_type = = TYPE_INODE_REF ) {
2019-09-01 12:53:20 +00:00
Status = send_inode_ref ( context , & tp2 , true ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode_ref returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp2 . item - > key . obj_type = = TYPE_INODE_EXTREF ) {
2019-09-01 12:53:20 +00:00
Status = send_inode_extref ( context , & tp2 , true ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode_extref returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
}
}
Status = finish_inode ( context , ended1 ? NULL : & tp , ended2 ? NULL : & tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " finish_inode returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > send - > cancelling ) {
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
2019-09-01 12:53:20 +00:00
no_next2 = true ;
2017-09-08 08:02:43 +00:00
Status = send_inode ( context , & tp , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else {
Status = send_inode ( context , & tp , & tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
}
} else if ( tp . item - > key . obj_type = = TYPE_INODE_REF ) {
2019-09-01 12:53:20 +00:00
Status = send_inode_ref ( context , & tp , false ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode_ref returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
2019-09-01 12:53:20 +00:00
Status = send_inode_ref ( context , & tp2 , true ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode_ref returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp . item - > key . obj_type = = TYPE_INODE_EXTREF ) {
2019-09-01 12:53:20 +00:00
Status = send_inode_extref ( context , & tp , false ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode_extref returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
2019-09-01 12:53:20 +00:00
Status = send_inode_extref ( context , & tp2 , true ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode_extref returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp . item - > key . obj_type = = TYPE_EXTENT_DATA ) {
Status = send_extent_data ( context , & tp , & tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_extent_data returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > send - > cancelling ) {
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp . item - > key . obj_type = = TYPE_XATTR_ITEM ) {
Status = send_xattr ( context , & tp , & tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_xattr returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > send - > cancelling ) {
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
}
if ( ! no_next ) {
2019-09-01 12:53:20 +00:00
if ( find_next_item ( context - > Vcb , & tp , & next_tp , false , NULL ) )
2017-09-08 08:02:43 +00:00
tp = next_tp ;
else
2019-09-01 12:53:20 +00:00
ended1 = true ;
2017-09-08 08:02:43 +00:00
if ( ! no_next2 ) {
2019-09-01 12:53:20 +00:00
if ( find_next_item ( context - > Vcb , & tp2 , & next_tp , false , NULL ) )
2017-09-08 08:02:43 +00:00
tp2 = next_tp ;
else
2019-09-01 12:53:20 +00:00
ended2 = true ;
2017-09-08 08:02:43 +00:00
}
}
} else if ( ended2 | | ( ! ended1 & & ! ended2 & & keycmp ( tp . item - > key , tp2 . item - > key ) = = - 1 ) ) {
2019-09-01 12:53:20 +00:00
TRACE ( " A %I64x,%x,%I64x \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
2017-09-08 08:02:43 +00:00
if ( context - > lastinode . inode ! = 0 & & tp . item - > key . obj_id > context - > lastinode . inode ) {
Status = finish_inode ( context , ended1 ? NULL : & tp , ended2 ? NULL : & tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " finish_inode returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > send - > cancelling ) {
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
}
if ( tp . item - > key . obj_type = = TYPE_INODE_ITEM ) {
Status = send_inode ( context , & tp , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp . item - > key . obj_type = = TYPE_INODE_REF ) {
2019-09-01 12:53:20 +00:00
Status = send_inode_ref ( context , & tp , false ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode_ref returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp . item - > key . obj_type = = TYPE_INODE_EXTREF ) {
2019-09-01 12:53:20 +00:00
Status = send_inode_extref ( context , & tp , false ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode_extref returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp . item - > key . obj_type = = TYPE_EXTENT_DATA ) {
Status = send_extent_data ( context , & tp , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_extent_data returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > send - > cancelling ) {
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp . item - > key . obj_type = = TYPE_XATTR_ITEM ) {
Status = send_xattr ( context , & tp , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_xattr returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > send - > cancelling ) {
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
}
2019-09-01 12:53:20 +00:00
if ( find_next_item ( context - > Vcb , & tp , & next_tp , false , NULL ) )
2017-09-08 08:02:43 +00:00
tp = next_tp ;
else
2019-09-01 12:53:20 +00:00
ended1 = true ;
2017-09-08 08:02:43 +00:00
} else if ( ended1 | | ( ! ended1 & & ! ended2 & & keycmp ( tp . item - > key , tp2 . item - > key ) = = 1 ) ) {
2019-09-01 12:53:20 +00:00
TRACE ( " B %I64x,%x,%I64x \n " , tp2 . item - > key . obj_id , tp2 . item - > key . obj_type , tp2 . item - > key . offset ) ;
2017-09-08 08:02:43 +00:00
if ( context - > lastinode . inode ! = 0 & & tp2 . item - > key . obj_id > context - > lastinode . inode ) {
Status = finish_inode ( context , ended1 ? NULL : & tp , ended2 ? NULL : & tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " finish_inode returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > send - > cancelling ) {
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
}
if ( tp2 . item - > key . obj_type = = TYPE_INODE_ITEM ) {
Status = send_inode ( context , NULL , & tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp2 . item - > key . obj_type = = TYPE_INODE_REF ) {
2019-09-01 12:53:20 +00:00
Status = send_inode_ref ( context , & tp2 , true ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode_ref returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp2 . item - > key . obj_type = = TYPE_INODE_EXTREF ) {
2019-09-01 12:53:20 +00:00
Status = send_inode_extref ( context , & tp2 , true ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode_extref returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp2 . item - > key . obj_type = = TYPE_EXTENT_DATA & & ! context - > lastinode . deleting ) {
Status = send_extent_data ( context , NULL , & tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_extent_data returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > send - > cancelling ) {
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp2 . item - > key . obj_type = = TYPE_XATTR_ITEM & & ! context - > lastinode . deleting ) {
Status = send_xattr ( context , NULL , & tp2 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_xattr returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > send - > cancelling ) {
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
}
2019-09-01 12:53:20 +00:00
if ( find_next_item ( context - > Vcb , & tp2 , & next_tp , false , NULL ) )
2017-09-08 08:02:43 +00:00
tp2 = next_tp ;
else
2019-09-01 12:53:20 +00:00
ended2 = true ;
2017-09-08 08:02:43 +00:00
}
} while ( ! ended1 | | ! ended2 ) ;
} else {
do {
traverse_ptr next_tp ;
if ( context - > datalen > SEND_BUFFER_LENGTH ) {
KEY key = tp . item - > key ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
KeClearEvent ( & context - > send - > cleared_event ) ;
2019-09-01 12:53:20 +00:00
KeSetEvent ( & context - > buffer_event , 0 , true ) ;
KeWaitForSingleObject ( & context - > send - > cleared_event , Executive , KernelMode , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( context - > send - > cancelling )
goto end ;
2019-09-01 12:53:20 +00:00
ExAcquireResourceSharedLite ( & context - > Vcb - > tree_lock , true ) ;
2017-09-08 08:02:43 +00:00
2019-09-01 12:53:20 +00:00
Status = find_item ( context - > Vcb , context - > root , & tp , & key , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_item returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( keycmp ( tp . item - > key , key ) ) {
ERR ( " readonly subvolume changed \n " ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
Status = STATUS_INTERNAL_ERROR ;
goto end ;
}
}
if ( context - > lastinode . inode ! = 0 & & tp . item - > key . obj_id > context - > lastinode . inode ) {
Status = finish_inode ( context , & tp , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " finish_inode returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > send - > cancelling ) {
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
}
if ( tp . item - > key . obj_type = = TYPE_INODE_ITEM ) {
Status = send_inode ( context , & tp , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp . item - > key . obj_type = = TYPE_INODE_REF ) {
2019-09-01 12:53:20 +00:00
Status = send_inode_ref ( context , & tp , false ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode_ref returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp . item - > key . obj_type = = TYPE_INODE_EXTREF ) {
2019-09-01 12:53:20 +00:00
Status = send_inode_extref ( context , & tp , false ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_inode_extref returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp . item - > key . obj_type = = TYPE_EXTENT_DATA ) {
Status = send_extent_data ( context , & tp , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_extent_data returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > send - > cancelling ) {
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
} else if ( tp . item - > key . obj_type = = TYPE_XATTR_ITEM ) {
Status = send_xattr ( context , & tp , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " send_xattr returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
if ( context - > send - > cancelling ) {
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
}
2019-09-01 12:53:20 +00:00
if ( find_next_item ( context - > Vcb , & tp , & next_tp , false , NULL ) )
2017-09-08 08:02:43 +00:00
tp = next_tp ;
else
break ;
2019-09-01 12:53:20 +00:00
} while ( true ) ;
2017-09-08 08:02:43 +00:00
}
if ( context - > lastinode . inode ! = 0 ) {
Status = finish_inode ( context , NULL , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " finish_inode returned %08x \n " , Status ) ;
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
goto end ;
}
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
if ( context - > send - > cancelling )
goto end ;
} else
ExReleaseResourceLite ( & context - > Vcb - > tree_lock ) ;
KeClearEvent ( & context - > send - > cleared_event ) ;
2019-09-01 12:53:20 +00:00
KeSetEvent ( & context - > buffer_event , 0 , true ) ;
KeWaitForSingleObject ( & context - > send - > cleared_event , Executive , KernelMode , false , NULL ) ;
2017-09-08 08:02:43 +00:00
Status = STATUS_SUCCESS ;
end :
if ( ! NT_SUCCESS ( Status ) ) {
2019-09-01 12:53:20 +00:00
KeSetEvent ( & context - > buffer_event , 0 , false ) ;
2017-09-08 08:02:43 +00:00
if ( context - > send - > ccb )
context - > send - > ccb - > send_status = Status ;
}
2019-09-01 12:53:20 +00:00
ExAcquireResourceExclusiveLite ( & context - > Vcb - > send_load_lock , true ) ;
2017-09-08 08:02:43 +00:00
while ( ! IsListEmpty ( & context - > orphans ) ) {
orphan * o = CONTAINING_RECORD ( RemoveHeadList ( & context - > orphans ) , orphan , list_entry ) ;
ExFreePool ( o ) ;
}
while ( ! IsListEmpty ( & context - > dirs ) ) {
send_dir * sd = CONTAINING_RECORD ( RemoveHeadList ( & context - > dirs ) , send_dir , list_entry ) ;
if ( sd - > name )
ExFreePool ( sd - > name ) ;
while ( ! IsListEmpty ( & sd - > deleted_children ) ) {
deleted_child * dc = CONTAINING_RECORD ( RemoveHeadList ( & sd - > deleted_children ) , deleted_child , list_entry ) ;
ExFreePool ( dc ) ;
}
ExFreePool ( sd ) ;
}
ZwClose ( context - > send - > thread ) ;
context - > send - > thread = NULL ;
if ( context - > send - > ccb )
context - > send - > ccb - > send = NULL ;
RemoveEntryList ( & context - > send - > list_entry ) ;
ExFreePool ( context - > send ) ;
ExFreePool ( context - > data ) ;
InterlockedDecrement ( & context - > Vcb - > running_sends ) ;
InterlockedDecrement ( & context - > root - > send_ops ) ;
if ( context - > parent )
InterlockedDecrement ( & context - > parent - > send_ops ) ;
ExReleaseResourceLite ( & context - > Vcb - > send_load_lock ) ;
if ( context - > clones ) {
ULONG i ;
for ( i = 0 ; i < context - > num_clones ; i + + ) {
InterlockedDecrement ( & context - > clones [ i ] - > send_ops ) ;
}
ExFreePool ( context - > clones ) ;
}
ExFreePool ( context ) ;
PsTerminateSystemThread ( STATUS_SUCCESS ) ;
}
NTSTATUS send_subvol ( device_extension * Vcb , void * data , ULONG datalen , PFILE_OBJECT FileObject , PIRP Irp ) {
NTSTATUS Status ;
fcb * fcb ;
ccb * ccb ;
root * parsubvol = NULL ;
send_context * context ;
send_info * send ;
ULONG num_clones = 0 ;
root * * clones = NULL ;
2019-11-12 18:32:46 +00:00
OBJECT_ATTRIBUTES oa ;
2017-09-08 08:02:43 +00:00
if ( ! FileObject | | ! FileObject - > FsContext | | ! FileObject - > FsContext2 | | FileObject - > FsContext = = Vcb - > volume_fcb )
return STATUS_INVALID_PARAMETER ;
if ( ! SeSinglePrivilegeCheck ( RtlConvertLongToLuid ( SE_MANAGE_VOLUME_PRIVILEGE ) , Irp - > RequestorMode ) )
return STATUS_PRIVILEGE_NOT_HELD ;
fcb = FileObject - > FsContext ;
ccb = FileObject - > FsContext2 ;
if ( fcb - > inode ! = SUBVOL_ROOT_INODE | | fcb = = Vcb - > root_fileref - > fcb )
return STATUS_INVALID_PARAMETER ;
if ( ! Vcb - > readonly & & ! ( fcb - > subvol - > root_item . flags & BTRFS_SUBVOL_READONLY ) )
return STATUS_INVALID_PARAMETER ;
if ( data ) {
btrfs_send_subvol * bss = ( btrfs_send_subvol * ) data ;
HANDLE parent ;
# if defined(_WIN64)
if ( IoIs32bitProcess ( Irp ) ) {
btrfs_send_subvol32 * bss32 = ( btrfs_send_subvol32 * ) data ;
if ( datalen < offsetof ( btrfs_send_subvol32 , num_clones ) )
return STATUS_INVALID_PARAMETER ;
parent = Handle32ToHandle ( bss32 - > parent ) ;
if ( datalen > = offsetof ( btrfs_send_subvol32 , clones [ 0 ] ) )
num_clones = bss32 - > num_clones ;
2019-09-01 12:53:20 +00:00
if ( datalen < offsetof ( btrfs_send_subvol32 , clones [ 0 ] ) + ( num_clones * sizeof ( uint32_t ) ) )
2017-09-08 08:02:43 +00:00
return STATUS_INVALID_PARAMETER ;
} else {
# endif
if ( datalen < offsetof ( btrfs_send_subvol , num_clones ) )
return STATUS_INVALID_PARAMETER ;
parent = bss - > parent ;
if ( datalen > = offsetof ( btrfs_send_subvol , clones [ 0 ] ) )
num_clones = bss - > num_clones ;
if ( datalen < offsetof ( btrfs_send_subvol , clones [ 0 ] ) + ( num_clones * sizeof ( HANDLE ) ) )
return STATUS_INVALID_PARAMETER ;
# if defined(_WIN64)
}
# endif
if ( parent ) {
PFILE_OBJECT fileobj ;
struct _fcb * parfcb ;
Status = ObReferenceObjectByHandle ( parent , 0 , * IoFileObjectType , Irp - > RequestorMode , ( void * * ) & fileobj , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ObReferenceObjectByHandle returned %08x \n " , Status ) ;
return Status ;
}
if ( fileobj - > DeviceObject ! = FileObject - > DeviceObject ) {
ObDereferenceObject ( fileobj ) ;
return STATUS_INVALID_PARAMETER ;
}
parfcb = fileobj - > FsContext ;
if ( ! parfcb | | parfcb = = Vcb - > root_fileref - > fcb | | parfcb = = Vcb - > volume_fcb | | parfcb - > inode ! = SUBVOL_ROOT_INODE ) {
ObDereferenceObject ( fileobj ) ;
return STATUS_INVALID_PARAMETER ;
}
parsubvol = parfcb - > subvol ;
ObDereferenceObject ( fileobj ) ;
if ( ! Vcb - > readonly & & ! ( parsubvol - > root_item . flags & BTRFS_SUBVOL_READONLY ) )
return STATUS_INVALID_PARAMETER ;
if ( parsubvol = = fcb - > subvol )
return STATUS_INVALID_PARAMETER ;
}
if ( num_clones > 0 ) {
ULONG i ;
clones = ExAllocatePoolWithTag ( PagedPool , sizeof ( root * ) * num_clones , ALLOC_TAG ) ;
if ( ! clones ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
for ( i = 0 ; i < num_clones ; i + + ) {
HANDLE h ;
PFILE_OBJECT fileobj ;
struct _fcb * clonefcb ;
# if defined(_WIN64)
if ( IoIs32bitProcess ( Irp ) ) {
btrfs_send_subvol32 * bss32 = ( btrfs_send_subvol32 * ) data ;
h = Handle32ToHandle ( bss32 - > clones [ i ] ) ;
} else
# endif
h = bss - > clones [ i ] ;
Status = ObReferenceObjectByHandle ( h , 0 , * IoFileObjectType , Irp - > RequestorMode , ( void * * ) & fileobj , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ObReferenceObjectByHandle returned %08x \n " , Status ) ;
ExFreePool ( clones ) ;
return Status ;
}
if ( fileobj - > DeviceObject ! = FileObject - > DeviceObject ) {
ObDereferenceObject ( fileobj ) ;
ExFreePool ( clones ) ;
return STATUS_INVALID_PARAMETER ;
}
clonefcb = fileobj - > FsContext ;
if ( ! clonefcb | | clonefcb = = Vcb - > root_fileref - > fcb | | clonefcb = = Vcb - > volume_fcb | | clonefcb - > inode ! = SUBVOL_ROOT_INODE ) {
ObDereferenceObject ( fileobj ) ;
ExFreePool ( clones ) ;
return STATUS_INVALID_PARAMETER ;
}
clones [ i ] = clonefcb - > subvol ;
ObDereferenceObject ( fileobj ) ;
if ( ! Vcb - > readonly & & ! ( clones [ i ] - > root_item . flags & BTRFS_SUBVOL_READONLY ) ) {
ExFreePool ( clones ) ;
return STATUS_INVALID_PARAMETER ;
}
}
}
}
2019-09-01 12:53:20 +00:00
ExAcquireResourceExclusiveLite ( & Vcb - > send_load_lock , true ) ;
2017-09-08 08:02:43 +00:00
if ( ccb - > send ) {
WARN ( " send operation already running \n " ) ;
ExReleaseResourceLite ( & Vcb - > send_load_lock ) ;
return STATUS_DEVICE_NOT_READY ;
}
context = ExAllocatePoolWithTag ( NonPagedPool , sizeof ( send_context ) , ALLOC_TAG ) ;
if ( ! context ) {
ERR ( " out of memory \n " ) ;
2018-12-16 11:03:16 +00:00
if ( clones )
ExFreePool ( clones ) ;
2017-09-08 08:02:43 +00:00
ExReleaseResourceLite ( & Vcb - > send_load_lock ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
context - > Vcb = Vcb ;
context - > root = fcb - > subvol ;
context - > parent = parsubvol ;
InitializeListHead ( & context - > orphans ) ;
InitializeListHead ( & context - > dirs ) ;
InitializeListHead ( & context - > pending_rmdirs ) ;
context - > lastinode . inode = 0 ;
context - > lastinode . path = NULL ;
context - > lastinode . sd = NULL ;
context - > root_dir = NULL ;
context - > num_clones = num_clones ;
context - > clones = clones ;
InitializeListHead ( & context - > lastinode . refs ) ;
InitializeListHead ( & context - > lastinode . oldrefs ) ;
InitializeListHead ( & context - > lastinode . exts ) ;
InitializeListHead ( & context - > lastinode . oldexts ) ;
context - > data = ExAllocatePoolWithTag ( PagedPool , SEND_BUFFER_LENGTH + ( 2 * MAX_SEND_WRITE ) , ALLOC_TAG ) ; // give ourselves some wiggle room
if ( ! context - > data ) {
ExFreePool ( context ) ;
ExReleaseResourceLite ( & Vcb - > send_load_lock ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
context - > datalen = 0 ;
send_subvol_header ( context , fcb - > subvol , ccb - > fileref ) ; // FIXME - fileref needs some sort of lock here
2019-09-01 12:53:20 +00:00
KeInitializeEvent ( & context - > buffer_event , NotificationEvent , false ) ;
2017-09-08 08:02:43 +00:00
send = ExAllocatePoolWithTag ( NonPagedPool , sizeof ( send_info ) , ALLOC_TAG ) ;
if ( ! send ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( context - > data ) ;
ExFreePool ( context ) ;
2018-12-16 11:03:16 +00:00
if ( clones )
ExFreePool ( clones ) ;
2017-09-08 08:02:43 +00:00
ExReleaseResourceLite ( & Vcb - > send_load_lock ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
2019-09-01 12:53:20 +00:00
KeInitializeEvent ( & send - > cleared_event , NotificationEvent , false ) ;
2017-09-08 08:02:43 +00:00
send - > context = context ;
context - > send = send ;
ccb - > send = send ;
send - > ccb = ccb ;
ccb - > send_status = STATUS_SUCCESS ;
2019-09-01 12:53:20 +00:00
send - > cancelling = false ;
2017-09-08 08:02:43 +00:00
InterlockedIncrement ( & Vcb - > running_sends ) ;
2019-11-12 18:32:46 +00:00
InitializeObjectAttributes ( & oa , NULL , OBJ_KERNEL_HANDLE , NULL , NULL ) ;
Status = PsCreateSystemThread ( & send - > thread , 0 , & oa , NULL , NULL , send_thread , context ) ;
2017-09-08 08:02:43 +00:00
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " PsCreateSystemThread returned %08x \n " , Status ) ;
ccb - > send = NULL ;
InterlockedDecrement ( & Vcb - > running_sends ) ;
ExFreePool ( send ) ;
ExFreePool ( context - > data ) ;
ExFreePool ( context ) ;
2018-12-16 11:03:16 +00:00
if ( clones )
ExFreePool ( clones ) ;
2017-09-08 08:02:43 +00:00
ExReleaseResourceLite ( & Vcb - > send_load_lock ) ;
return Status ;
}
InsertTailList ( & Vcb - > send_ops , & send - > list_entry ) ;
ExReleaseResourceLite ( & Vcb - > send_load_lock ) ;
return STATUS_SUCCESS ;
}
NTSTATUS read_send_buffer ( device_extension * Vcb , PFILE_OBJECT FileObject , void * data , ULONG datalen , ULONG_PTR * retlen , KPROCESSOR_MODE processor_mode ) {
ccb * ccb ;
send_context * context ;
ccb = FileObject ? FileObject - > FsContext2 : NULL ;
if ( ! ccb )
return STATUS_INVALID_PARAMETER ;
if ( ! SeSinglePrivilegeCheck ( RtlConvertLongToLuid ( SE_MANAGE_VOLUME_PRIVILEGE ) , processor_mode ) )
return STATUS_PRIVILEGE_NOT_HELD ;
2019-09-01 12:53:20 +00:00
ExAcquireResourceExclusiveLite ( & Vcb - > send_load_lock , true ) ;
2017-09-08 08:02:43 +00:00
if ( ! ccb - > send ) {
ExReleaseResourceLite ( & Vcb - > send_load_lock ) ;
return ! NT_SUCCESS ( ccb - > send_status ) ? ccb - > send_status : STATUS_END_OF_FILE ;
}
context = ( send_context * ) ccb - > send - > context ;
2019-09-01 12:53:20 +00:00
KeWaitForSingleObject ( & context - > buffer_event , Executive , KernelMode , false , NULL ) ;
2017-09-08 08:02:43 +00:00
if ( datalen = = 0 ) {
ExReleaseResourceLite ( & Vcb - > send_load_lock ) ;
return STATUS_SUCCESS ;
}
RtlCopyMemory ( data , context - > data , min ( datalen , context - > datalen ) ) ;
if ( datalen < context - > datalen ) { // not empty yet
* retlen = datalen ;
RtlMoveMemory ( context - > data , & context - > data [ datalen ] , context - > datalen - datalen ) ;
context - > datalen - = datalen ;
ExReleaseResourceLite ( & Vcb - > send_load_lock ) ;
} else {
* retlen = context - > datalen ;
context - > datalen = 0 ;
ExReleaseResourceLite ( & Vcb - > send_load_lock ) ;
KeClearEvent ( & context - > buffer_event ) ;
2019-09-01 12:53:20 +00:00
KeSetEvent ( & ccb - > send - > cleared_event , 0 , false ) ;
2017-09-08 08:02:43 +00:00
}
return STATUS_SUCCESS ;
}