2016-03-23 20:35:05 +00:00
/* Copyright (c) Mark Harmstone 2016
*
* 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/>. */
# ifdef _DEBUG
# define DEBUG
# endif
# include "btrfs_drv.h"
# ifndef __REACTOS__
# ifndef _MSC_VER
# include <cpuid.h>
# else
# include <intrin.h>
# endif
# endif
# include "btrfs.h"
# ifndef __REACTOS__
# include <winioctl.h>
# else
# include <rtlfuncs.h>
# endif
# define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \
BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA )
# define COMPAT_RO_SUPPORTED 0
static WCHAR device_name [ ] = { ' \\ ' , ' B ' , ' t ' , ' r ' , ' f ' , ' s ' , 0 } ;
static WCHAR dosdevice_name [ ] = { ' \\ ' , ' D ' , ' o ' , ' s ' , ' D ' , ' e ' , ' v ' , ' i ' , ' c ' , ' e ' , ' s ' , ' \\ ' , ' B ' , ' t ' , ' r ' , ' f ' , ' s ' , 0 } ;
PDRIVER_OBJECT drvobj ;
PDEVICE_OBJECT devobj ;
# ifndef __REACTOS__
BOOL have_sse42 = FALSE ;
# endif
UINT64 num_reads = 0 ;
LIST_ENTRY uid_map_list ;
LIST_ENTRY volumes ;
LIST_ENTRY VcbList ;
ERESOURCE global_loading_lock ;
UINT32 debug_log_level = 0 ;
BOOL log_started = FALSE ;
UNICODE_STRING log_device , log_file ;
# ifdef _DEBUG
PFILE_OBJECT comfo = NULL ;
PDEVICE_OBJECT comdo = NULL ;
HANDLE log_handle = NULL ;
# endif
static NTSTATUS STDCALL close_file ( device_extension * Vcb , PFILE_OBJECT FileObject ) ;
typedef struct {
KEVENT Event ;
IO_STATUS_BLOCK iosb ;
} read_context ;
# ifdef _DEBUG
static NTSTATUS STDCALL dbg_completion ( PDEVICE_OBJECT DeviceObject , PIRP Irp , PVOID conptr ) {
read_context * context = conptr ;
// DbgPrint("dbg_completion\n");
context - > iosb = Irp - > IoStatus ;
KeSetEvent ( & context - > Event , 0 , FALSE ) ;
// return STATUS_SUCCESS;
return STATUS_MORE_PROCESSING_REQUIRED ;
}
# ifdef DEBUG_LONG_MESSAGES
void STDCALL _debug_message ( const char * func , UINT8 priority , const char * file , unsigned int line , char * s , . . . ) {
# else
void STDCALL _debug_message ( const char * func , UINT8 priority , char * s , . . . ) {
# endif
LARGE_INTEGER offset ;
PIO_STACK_LOCATION IrpSp ;
NTSTATUS Status ;
PIRP Irp ;
va_list ap ;
char * buf2 = NULL , * buf ;
read_context * context = NULL ;
UINT32 length ;
if ( log_started & & priority > debug_log_level )
return ;
buf2 = ExAllocatePoolWithTag ( NonPagedPool , 1024 , ALLOC_TAG ) ;
if ( ! buf2 ) {
DbgPrint ( " Couldn't allocate buffer in debug_message \n " ) ;
return ;
}
# ifdef DEBUG_LONG_MESSAGES
sprintf ( buf2 , " %p:%s:%s:%u: " , PsGetCurrentThreadId ( ) , func , file , line ) ;
# else
sprintf ( buf2 , " %p:%s: " , PsGetCurrentThreadId ( ) , func ) ;
# endif
buf = & buf2 [ strlen ( buf2 ) ] ;
va_start ( ap , s ) ;
vsprintf ( buf , s , ap ) ;
if ( ! log_started | | ( log_device . Length = = 0 & & log_file . Length = = 0 ) ) {
DbgPrint ( buf2 ) ;
} else if ( log_device . Length > 0 ) {
if ( ! comdo ) {
DbgPrint ( " comdo is NULL :-( \n " ) ;
DbgPrint ( buf2 ) ;
goto exit2 ;
}
length = ( UINT32 ) strlen ( buf2 ) ;
offset . u . LowPart = 0 ;
offset . u . HighPart = 0 ;
context = ExAllocatePoolWithTag ( NonPagedPool , sizeof ( read_context ) , ALLOC_TAG ) ;
if ( ! context ) {
DbgPrint ( " Couldn't allocate context in debug_message \n " ) ;
return ;
}
RtlZeroMemory ( context , sizeof ( read_context ) ) ;
KeInitializeEvent ( & context - > Event , NotificationEvent , FALSE ) ;
// status = ZwWriteFile(comh, NULL, NULL, NULL, &io, buf2, strlen(buf2), &offset, NULL);
Irp = IoAllocateIrp ( comdo - > StackSize , FALSE ) ;
if ( ! Irp ) {
DbgPrint ( " IoAllocateIrp failed \n " ) ;
goto exit2 ;
}
IrpSp = IoGetNextIrpStackLocation ( Irp ) ;
IrpSp - > MajorFunction = IRP_MJ_WRITE ;
if ( comdo - > Flags & DO_BUFFERED_IO ) {
Irp - > AssociatedIrp . SystemBuffer = buf2 ;
Irp - > Flags = IRP_BUFFERED_IO ;
} else if ( comdo - > Flags & DO_DIRECT_IO ) {
Irp - > MdlAddress = IoAllocateMdl ( buf2 , length , FALSE , FALSE , NULL ) ;
if ( ! Irp - > MdlAddress ) {
DbgPrint ( " IoAllocateMdl failed \n " ) ;
goto exit ;
}
MmProbeAndLockPages ( Irp - > MdlAddress , KernelMode , IoWriteAccess ) ;
} else {
Irp - > UserBuffer = buf2 ;
}
IrpSp - > Parameters . Write . Length = length ;
IrpSp - > Parameters . Write . ByteOffset = offset ;
Irp - > UserIosb = & context - > iosb ;
Irp - > UserEvent = & context - > Event ;
IoSetCompletionRoutine ( Irp , dbg_completion , context , TRUE , TRUE , TRUE ) ;
Status = IoCallDriver ( comdo , Irp ) ;
if ( Status = = STATUS_PENDING ) {
KeWaitForSingleObject ( & context - > Event , Executive , KernelMode , FALSE , NULL ) ;
Status = context - > iosb . Status ;
}
if ( comdo - > Flags & DO_DIRECT_IO ) {
MmUnlockPages ( Irp - > MdlAddress ) ;
IoFreeMdl ( Irp - > MdlAddress ) ;
}
if ( ! NT_SUCCESS ( Status ) ) {
DbgPrint ( " failed to write to COM1 - error %08x \n " , Status ) ;
goto exit ;
}
exit :
IoFreeIrp ( Irp ) ;
} else if ( log_handle ! = NULL ) {
IO_STATUS_BLOCK iosb ;
length = ( UINT32 ) strlen ( buf2 ) ;
Status = ZwWriteFile ( log_handle , NULL , NULL , NULL , & iosb , buf2 , length , NULL , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
DbgPrint ( " failed to write to file - error %08x \n " , Status ) ;
}
}
exit2 :
va_end ( ap ) ;
if ( context )
ExFreePool ( context ) ;
if ( buf2 )
ExFreePool ( buf2 ) ;
}
# endif
ULONG sector_align ( ULONG NumberToBeAligned , ULONG Alignment )
{
if ( Alignment & ( Alignment - 1 ) )
{
//
// Alignment not a power of 2
// Just returning
//
return NumberToBeAligned ;
}
if ( ( NumberToBeAligned & ( Alignment - 1 ) ) ! = 0 )
{
NumberToBeAligned = NumberToBeAligned + Alignment ;
NumberToBeAligned = NumberToBeAligned & ( ~ ( Alignment - 1 ) ) ;
}
return NumberToBeAligned ;
}
int keycmp ( const KEY * key1 , const KEY * key2 ) {
if ( key1 - > obj_id < key2 - > obj_id ) {
return - 1 ;
} else if ( key1 - > obj_id > key2 - > obj_id ) {
return 1 ;
}
if ( key1 - > obj_type < key2 - > obj_type ) {
return - 1 ;
} else if ( key1 - > obj_type > key2 - > obj_type ) {
return 1 ;
}
if ( key1 - > offset < key2 - > offset ) {
return - 1 ;
} else if ( key1 - > offset > key2 - > offset ) {
return 1 ;
}
return 0 ;
}
BOOL is_top_level ( PIRP Irp ) {
if ( ! IoGetTopLevelIrp ( ) ) {
IoSetTopLevelIrp ( Irp ) ;
return TRUE ;
}
return FALSE ;
}
static void STDCALL DriverUnload ( PDRIVER_OBJECT DriverObject ) {
UNICODE_STRING dosdevice_nameW ;
ERR ( " DriverUnload \n " ) ;
free_cache ( ) ;
IoUnregisterFileSystem ( DriverObject - > DeviceObject ) ;
dosdevice_nameW . Buffer = dosdevice_name ;
dosdevice_nameW . Length = dosdevice_nameW . MaximumLength = ( USHORT ) wcslen ( dosdevice_name ) * sizeof ( WCHAR ) ;
IoDeleteSymbolicLink ( & dosdevice_nameW ) ;
IoDeleteDevice ( DriverObject - > DeviceObject ) ;
while ( ! IsListEmpty ( & uid_map_list ) ) {
LIST_ENTRY * le = RemoveHeadList ( & uid_map_list ) ;
uid_map * um = CONTAINING_RECORD ( le , uid_map , listentry ) ;
ExFreePool ( um - > sid ) ;
ExFreePool ( um ) ;
}
// FIXME - free volumes and their devpaths
# ifdef _DEBUG
if ( comfo )
ObDereferenceObject ( comfo ) ;
if ( log_handle )
ZwClose ( log_handle ) ;
# endif
ExDeleteResourceLite ( & global_loading_lock ) ;
if ( log_device . Buffer )
ExFreePool ( log_device . Buffer ) ;
if ( log_file . Buffer )
ExFreePool ( log_file . Buffer ) ;
}
BOOL STDCALL get_last_inode ( device_extension * Vcb , root * r ) {
KEY searchkey ;
traverse_ptr tp , prev_tp ;
NTSTATUS Status ;
// get last entry
searchkey . obj_id = 0xffffffffffffffff ;
searchkey . obj_type = 0xff ;
searchkey . offset = 0xffffffffffffffff ;
Status = find_item ( Vcb , r , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return FALSE ;
}
while ( find_prev_item ( Vcb , & tp , & prev_tp , FALSE ) ) {
free_traverse_ptr ( & tp ) ;
tp = prev_tp ;
TRACE ( " moving on to %llx,%x,%llx \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
if ( tp . item - > key . obj_type = = TYPE_INODE_ITEM ) {
r - > lastinode = tp . item - > key . obj_id ;
free_traverse_ptr ( & tp ) ;
TRACE ( " last inode for tree %llx is %llx \n " , r - > id , r - > lastinode ) ;
return TRUE ;
}
}
free_traverse_ptr ( & tp ) ;
r - > lastinode = SUBVOL_ROOT_INODE ;
WARN ( " no INODE_ITEMs in tree %llx \n " , r - > id ) ;
return TRUE ;
}
BOOL STDCALL get_xattr ( device_extension * Vcb , root * subvol , UINT64 inode , char * name , UINT32 crc32 , UINT8 * * data , UINT16 * datalen ) {
KEY searchkey ;
traverse_ptr tp ;
DIR_ITEM * xa ;
ULONG size , xasize ;
NTSTATUS Status ;
TRACE ( " (%p, %llx, %llx, %s, %08x, %p, %p) \n " , Vcb , subvol - > id , inode , name , crc32 , data , datalen ) ;
searchkey . obj_id = inode ;
searchkey . obj_type = TYPE_XATTR_ITEM ;
searchkey . offset = crc32 ;
Status = find_item ( Vcb , subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return FALSE ;
}
if ( keycmp ( & tp . item - > key , & searchkey ) ) {
TRACE ( " could not find item (%llx,%x,%llx) \n " , searchkey . obj_id , searchkey . obj_type , searchkey . offset ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
if ( tp . item - > size < sizeof ( DIR_ITEM ) ) {
ERR ( " (%llx,%x,%llx) 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 ( DIR_ITEM ) ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
xa = ( DIR_ITEM * ) tp . item - > data ;
size = tp . item - > size ;
while ( TRUE ) {
if ( size < sizeof ( DIR_ITEM ) | | size < ( sizeof ( DIR_ITEM ) - 1 + xa - > m + xa - > n ) ) {
WARN ( " (%llx,%x,%llx) is truncated \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
if ( xa - > n = = strlen ( name ) & & RtlCompareMemory ( name , xa - > name , xa - > n ) = = xa - > n ) {
TRACE ( " found xattr %s in (%llx,%x,%llx) \n " , name , searchkey . obj_id , searchkey . obj_type , searchkey . offset ) ;
* datalen = xa - > m ;
if ( xa - > m > 0 ) {
* data = ExAllocatePoolWithTag ( PagedPool , xa - > m , ALLOC_TAG ) ;
if ( ! * data ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
RtlCopyMemory ( * data , & xa - > name [ xa - > n ] , xa - > m ) ;
} else
* data = NULL ;
free_traverse_ptr ( & tp ) ;
return TRUE ;
}
xasize = sizeof ( DIR_ITEM ) - 1 + xa - > m + xa - > n ;
if ( size > xasize ) {
size - = xasize ;
xa = ( DIR_ITEM * ) & xa - > name [ xa - > m + xa - > n ] ;
} else
break ;
}
TRACE ( " xattr %s not found in (%llx,%x,%llx) \n " , name , searchkey . obj_id , searchkey . obj_type , searchkey . offset ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
NTSTATUS STDCALL set_xattr ( device_extension * Vcb , root * subvol , UINT64 inode , char * name , UINT32 crc32 , UINT8 * data , UINT16 datalen , LIST_ENTRY * rollback ) {
KEY searchkey ;
traverse_ptr tp ;
ULONG xasize ;
DIR_ITEM * xa ;
NTSTATUS Status ;
TRACE ( " (%p, %llx, %llx, %s, %08x, %p, %u) \n " , Vcb , subvol - > id , inode , name , crc32 , data , datalen ) ;
searchkey . obj_id = inode ;
searchkey . obj_type = TYPE_XATTR_ITEM ;
searchkey . offset = crc32 ;
Status = find_item ( Vcb , subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
xasize = sizeof ( DIR_ITEM ) - 1 + ( ULONG ) strlen ( name ) + datalen ;
if ( ! keycmp ( & tp . item - > key , & searchkey ) ) { // key exists
UINT8 * newdata ;
ULONG size = tp . item - > size ;
xa = ( DIR_ITEM * ) tp . item - > data ;
if ( tp . item - > size < sizeof ( DIR_ITEM ) ) {
ERR ( " (%llx,%x,%llx) 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 ( DIR_ITEM ) ) ;
} else {
while ( TRUE ) {
ULONG oldxasize ;
if ( size < sizeof ( DIR_ITEM ) | | size < sizeof ( DIR_ITEM ) - 1 + xa - > m + xa - > n ) {
ERR ( " (%llx,%x,%llx) was truncated \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
break ;
}
oldxasize = sizeof ( DIR_ITEM ) - 1 + xa - > m + xa - > n ;
if ( xa - > n = = strlen ( name ) & & RtlCompareMemory ( name , xa - > name , xa - > n ) = = xa - > n ) {
UINT64 pos ;
// replace
newdata = ExAllocatePoolWithTag ( PagedPool , tp . item - > size + xasize - oldxasize , ALLOC_TAG ) ;
if ( ! newdata ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
pos = ( UINT8 * ) xa - tp . item - > data ;
if ( pos + oldxasize < tp . item - > size ) { // copy after changed xattr
RtlCopyMemory ( newdata + pos + xasize , tp . item - > data + pos + oldxasize , tp . item - > size - pos - oldxasize ) ;
}
if ( pos > 0 ) { // copy before changed xattr
RtlCopyMemory ( newdata , tp . item - > data , pos ) ;
xa = ( DIR_ITEM * ) ( newdata + pos ) ;
} else
xa = ( DIR_ITEM * ) newdata ;
xa - > key . obj_id = 0 ;
xa - > key . obj_type = 0 ;
xa - > key . offset = 0 ;
xa - > transid = Vcb - > superblock . generation ;
xa - > m = datalen ;
xa - > n = ( UINT16 ) strlen ( name ) ;
xa - > type = BTRFS_TYPE_EA ;
RtlCopyMemory ( xa - > name , name , strlen ( name ) ) ;
RtlCopyMemory ( xa - > name + strlen ( name ) , data , datalen ) ;
delete_tree_item ( Vcb , & tp , rollback ) ;
insert_tree_item ( Vcb , subvol , inode , TYPE_XATTR_ITEM , crc32 , newdata , tp . item - > size + xasize - oldxasize , NULL , rollback ) ;
break ;
}
if ( xa - > m + xa - > n > = size ) { // FIXME - test this works
// not found, add to end of data
newdata = ExAllocatePoolWithTag ( PagedPool , tp . item - > size + xasize , ALLOC_TAG ) ;
if ( ! newdata ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( newdata , tp . item - > data , tp . item - > size ) ;
xa = ( DIR_ITEM * ) ( ( UINT8 * ) newdata + tp . item - > size ) ;
xa - > key . obj_id = 0 ;
xa - > key . obj_type = 0 ;
xa - > key . offset = 0 ;
xa - > transid = Vcb - > superblock . generation ;
xa - > m = datalen ;
xa - > n = ( UINT16 ) strlen ( name ) ;
xa - > type = BTRFS_TYPE_EA ;
RtlCopyMemory ( xa - > name , name , strlen ( name ) ) ;
RtlCopyMemory ( xa - > name + strlen ( name ) , data , datalen ) ;
delete_tree_item ( Vcb , & tp , rollback ) ;
insert_tree_item ( Vcb , subvol , inode , TYPE_XATTR_ITEM , crc32 , newdata , tp . item - > size + xasize , NULL , rollback ) ;
break ;
} else {
xa = ( DIR_ITEM * ) & xa - > name [ xa - > m + xa - > n ] ;
size - = oldxasize ;
}
}
}
} else {
// add new DIR_ITEM struct
xa = ExAllocatePoolWithTag ( PagedPool , xasize , ALLOC_TAG ) ;
if ( ! xa ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
xa - > key . obj_id = 0 ;
xa - > key . obj_type = 0 ;
xa - > key . offset = 0 ;
xa - > transid = Vcb - > superblock . generation ;
xa - > m = datalen ;
xa - > n = ( UINT16 ) strlen ( name ) ;
xa - > type = BTRFS_TYPE_EA ;
RtlCopyMemory ( xa - > name , name , strlen ( name ) ) ;
RtlCopyMemory ( xa - > name + strlen ( name ) , data , datalen ) ;
insert_tree_item ( Vcb , subvol , inode , TYPE_XATTR_ITEM , crc32 , xa , xasize , NULL , rollback ) ;
}
free_traverse_ptr ( & tp ) ;
return STATUS_SUCCESS ;
}
BOOL STDCALL delete_xattr ( device_extension * Vcb , root * subvol , UINT64 inode , char * name , UINT32 crc32 , LIST_ENTRY * rollback ) {
KEY searchkey ;
traverse_ptr tp ;
DIR_ITEM * xa ;
NTSTATUS Status ;
TRACE ( " (%p, %llx, %llx, %s, %08x) \n " , Vcb , subvol - > id , inode , name , crc32 ) ;
searchkey . obj_id = inode ;
searchkey . obj_type = TYPE_XATTR_ITEM ;
searchkey . offset = crc32 ;
Status = find_item ( Vcb , subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return FALSE ;
}
if ( ! keycmp ( & tp . item - > key , & searchkey ) ) { // key exists
ULONG size = tp . item - > size ;
if ( tp . item - > size < sizeof ( DIR_ITEM ) ) {
ERR ( " (%llx,%x,%llx) 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 ( DIR_ITEM ) ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
} else {
xa = ( DIR_ITEM * ) tp . item - > data ;
while ( TRUE ) {
ULONG oldxasize ;
if ( size < sizeof ( DIR_ITEM ) | | size < sizeof ( DIR_ITEM ) - 1 + xa - > m + xa - > n ) {
ERR ( " (%llx,%x,%llx) was truncated \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
oldxasize = sizeof ( DIR_ITEM ) - 1 + xa - > m + xa - > n ;
if ( xa - > n = = strlen ( name ) & & RtlCompareMemory ( name , xa - > name , xa - > n ) = = xa - > n ) {
ULONG newsize ;
UINT8 * newdata , * dioff ;
newsize = tp . item - > size - ( sizeof ( DIR_ITEM ) - 1 + xa - > n + xa - > m ) ;
delete_tree_item ( Vcb , & tp , rollback ) ;
if ( newsize = = 0 ) {
TRACE ( " xattr %s deleted \n " , name ) ;
free_traverse_ptr ( & tp ) ;
return TRUE ;
}
// FIXME - deleting collisions almost certainly works, but we should test it properly anyway
newdata = ExAllocatePoolWithTag ( PagedPool , newsize , ALLOC_TAG ) ;
if ( ! newdata ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
if ( ( UINT8 * ) xa > tp . item - > data ) {
RtlCopyMemory ( newdata , tp . item - > data , ( UINT8 * ) xa - tp . item - > data ) ;
dioff = newdata + ( ( UINT8 * ) xa - tp . item - > data ) ;
} else {
dioff = newdata ;
}
if ( ( UINT8 * ) & xa - > name [ xa - > n + xa - > m ] - tp . item - > data < tp . item - > size )
RtlCopyMemory ( dioff , & xa - > name [ xa - > n + xa - > m ] , tp . item - > size - ( ( UINT8 * ) & xa - > name [ xa - > n + xa - > m ] - tp . item - > data ) ) ;
insert_tree_item ( Vcb , subvol , inode , TYPE_XATTR_ITEM , crc32 , newdata , newsize , NULL , rollback ) ;
free_traverse_ptr ( & tp ) ;
return TRUE ;
}
if ( xa - > m + xa - > n > = size ) { // FIXME - test this works
WARN ( " xattr %s not found \n " , name ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
} else {
xa = ( DIR_ITEM * ) & xa - > name [ xa - > m + xa - > n ] ;
size - = oldxasize ;
}
}
}
} else {
WARN ( " xattr %s not found \n " , name ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
}
NTSTATUS add_dir_item ( device_extension * Vcb , root * subvol , UINT64 inode , UINT32 crc32 , DIR_ITEM * di , ULONG disize , LIST_ENTRY * rollback ) {
KEY searchkey ;
traverse_ptr tp ;
UINT8 * di2 ;
NTSTATUS Status ;
searchkey . obj_id = inode ;
searchkey . obj_type = TYPE_DIR_ITEM ;
searchkey . offset = crc32 ;
Status = find_item ( Vcb , subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & tp . item - > key , & searchkey ) ) {
ULONG maxlen = Vcb - > superblock . node_size - sizeof ( tree_header ) - sizeof ( leaf_node ) ;
if ( tp . item - > size + disize > maxlen ) {
WARN ( " DIR_ITEM was longer than maxlen (%u + %u > %u) \n " , tp . item - > size , disize , maxlen ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
di2 = ExAllocatePoolWithTag ( PagedPool , tp . item - > size + disize , ALLOC_TAG ) ;
if ( ! di2 ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
if ( tp . item - > size > 0 )
RtlCopyMemory ( di2 , tp . item - > data , tp . item - > size ) ;
RtlCopyMemory ( di2 + tp . item - > size , di , disize ) ;
delete_tree_item ( Vcb , & tp , rollback ) ;
insert_tree_item ( Vcb , subvol , inode , TYPE_DIR_ITEM , crc32 , di2 , tp . item - > size + disize , NULL , rollback ) ;
ExFreePool ( di ) ;
} else {
insert_tree_item ( Vcb , subvol , inode , TYPE_DIR_ITEM , crc32 , di , disize , NULL , rollback ) ;
}
free_traverse_ptr ( & tp ) ;
return STATUS_SUCCESS ;
}
UINT64 find_next_dir_index ( device_extension * Vcb , root * subvol , UINT64 inode ) {
KEY searchkey ;
traverse_ptr tp , prev_tp ;
UINT64 dirpos ;
NTSTATUS Status ;
searchkey . obj_id = inode ;
searchkey . obj_type = TYPE_DIR_INDEX + 1 ;
searchkey . offset = 0 ;
Status = find_item ( Vcb , subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return 0 ;
}
if ( ! keycmp ( & searchkey , & tp . item - > key ) ) {
if ( ! find_prev_item ( Vcb , & tp , & prev_tp , FALSE ) ) {
free_traverse_ptr ( & tp ) ;
tp = prev_tp ;
TRACE ( " moving back to %llx,%x,%llx \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
}
}
if ( tp . item - > key . obj_id = = searchkey . obj_id & & tp . item - > key . obj_type = = TYPE_DIR_INDEX ) {
dirpos = tp . item - > key . offset + 1 ;
} else
dirpos = 2 ;
free_traverse_ptr ( & tp ) ;
return dirpos ;
}
static NTSTATUS STDCALL drv_close ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
NTSTATUS Status ;
PIO_STACK_LOCATION IrpSp ;
BOOL top_level ;
TRACE ( " close \n " ) ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
if ( DeviceObject = = devobj ) {
TRACE ( " Closing file system \n " ) ;
Status = STATUS_SUCCESS ;
goto exit ;
}
IrpSp = IoGetCurrentIrpStackLocation ( Irp ) ;
// FIXME - unmount if called for volume
// FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
Status = close_file ( DeviceObject - > DeviceExtension , IrpSp - > FileObject ) ;
exit :
Irp - > IoStatus . Status = Status ;
Irp - > IoStatus . Information = 0 ;
IoCompleteRequest ( Irp , IO_DISK_INCREMENT ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
TRACE ( " returning %08x \n " , Status ) ;
return Status ;
}
static NTSTATUS STDCALL drv_write ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
NTSTATUS Status ;
BOOL top_level ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
// ERR("recursive = %s\n", Irp != IoGetTopLevelIrp() ? "TRUE" : "FALSE");
Status = write_file ( DeviceObject , Irp ) ;
Irp - > IoStatus . Status = Status ;
TRACE ( " wrote %u bytes \n " , Irp - > IoStatus . Information ) ;
if ( Status ! = STATUS_PENDING )
IoCompleteRequest ( Irp , IO_NO_INCREMENT ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
TRACE ( " returning %08x \n " , Status ) ;
return Status ;
}
static NTSTATUS STDCALL drv_query_ea ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
NTSTATUS Status ;
BOOL top_level ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
FIXME ( " STUB: query ea \n " ) ;
Status = STATUS_NOT_IMPLEMENTED ;
Irp - > IoStatus . Status = Status ;
Irp - > IoStatus . Information = 0 ;
IoCompleteRequest ( Irp , IO_NO_INCREMENT ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
return Status ;
}
static NTSTATUS STDCALL drv_set_ea ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
NTSTATUS Status ;
device_extension * Vcb = DeviceObject - > DeviceExtension ;
BOOL top_level ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
FIXME ( " STUB: set ea \n " ) ;
Status = STATUS_NOT_IMPLEMENTED ;
if ( Vcb - > readonly )
Status = STATUS_MEDIA_WRITE_PROTECTED ;
// FIXME - return STATUS_ACCESS_DENIED if subvol readonly
Irp - > IoStatus . Status = Status ;
Irp - > IoStatus . Information = 0 ;
IoCompleteRequest ( Irp , IO_NO_INCREMENT ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
return Status ;
}
static NTSTATUS STDCALL drv_flush_buffers ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
NTSTATUS Status ;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation ( Irp ) ;
PFILE_OBJECT FileObject = IrpSp - > FileObject ;
fcb * fcb = FileObject - > FsContext ;
BOOL top_level ;
TRACE ( " flush buffers \n " ) ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
Status = STATUS_SUCCESS ;
Irp - > IoStatus . Status = Status ;
Irp - > IoStatus . Information = 0 ;
if ( fcb - > type ! = BTRFS_TYPE_DIRECTORY ) {
CcFlushCache ( & fcb - > nonpaged - > segment_object , NULL , 0 , & Irp - > IoStatus ) ;
if ( fcb - > Header . PagingIoResource ) {
ExAcquireResourceExclusiveLite ( fcb - > Header . PagingIoResource , TRUE ) ;
ExReleaseResourceLite ( fcb - > Header . PagingIoResource ) ;
}
Status = Irp - > IoStatus . Status ;
}
IoCompleteRequest ( Irp , IO_NO_INCREMENT ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
return Status ;
}
static NTSTATUS STDCALL drv_query_volume_information ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
PIO_STACK_LOCATION IrpSp ;
NTSTATUS Status ;
ULONG BytesCopied = 0 ;
device_extension * Vcb = DeviceObject - > DeviceExtension ;
BOOL top_level ;
# ifndef __REACTOS__
// An unfortunate necessity - we have to lie about our FS type. MPR!MprGetConnection polls for this,
// and compares it to a whitelist. If it doesn't match, it will return ERROR_NO_NET_OR_BAD_PATH,
// which prevents UAC from working.
// FIXME - only lie if we detect that we're being called by mpr.dll
WCHAR * fs_name = L " NTFS " ;
ULONG fs_name_len = 4 * sizeof ( WCHAR ) ;
# else
WCHAR * fs_name = L " Btrfs " ;
ULONG fs_name_len = 5 * sizeof ( WCHAR ) ;
# endif
TRACE ( " query volume information \n " ) ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
IrpSp = IoGetCurrentIrpStackLocation ( Irp ) ;
Status = STATUS_NOT_IMPLEMENTED ;
switch ( IrpSp - > Parameters . QueryVolume . FsInformationClass ) {
case FileFsAttributeInformation :
{
FILE_FS_ATTRIBUTE_INFORMATION * data = Irp - > AssociatedIrp . SystemBuffer ;
BOOL overflow = FALSE ;
ULONG orig_fs_name_len = fs_name_len ;
TRACE ( " FileFsAttributeInformation \n " ) ;
if ( IrpSp - > Parameters . QueryVolume . Length < sizeof ( FILE_FS_ATTRIBUTE_INFORMATION ) - sizeof ( WCHAR ) + fs_name_len ) {
if ( IrpSp - > Parameters . QueryVolume . Length > sizeof ( FILE_FS_ATTRIBUTE_INFORMATION ) - sizeof ( WCHAR ) )
fs_name_len = IrpSp - > Parameters . QueryVolume . Length - sizeof ( FILE_FS_ATTRIBUTE_INFORMATION ) + sizeof ( WCHAR ) ;
else
fs_name_len = 0 ;
overflow = TRUE ;
}
data - > FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH |
FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS | FILE_SUPPORTS_HARD_LINKS | FILE_PERSISTENT_ACLS |
FILE_SUPPORTS_REPARSE_POINTS ;
if ( Vcb - > readonly )
data - > FileSystemAttributes | = FILE_READ_ONLY_VOLUME ;
// should also be FILE_FILE_COMPRESSION when supported
data - > MaximumComponentNameLength = 255 ; // FIXME - check
data - > FileSystemNameLength = orig_fs_name_len ;
RtlCopyMemory ( data - > FileSystemName , fs_name , fs_name_len ) ;
BytesCopied = sizeof ( FILE_FS_ATTRIBUTE_INFORMATION ) - sizeof ( WCHAR ) + fs_name_len ;
Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS ;
break ;
}
case FileFsControlInformation :
FIXME ( " STUB: FileFsControlInformation \n " ) ;
break ;
case FileFsDeviceInformation :
FIXME ( " STUB: FileFsDeviceInformation \n " ) ;
break ;
case FileFsDriverPathInformation :
FIXME ( " STUB: FileFsDriverPathInformation \n " ) ;
break ;
case FileFsFullSizeInformation :
{
FILE_FS_FULL_SIZE_INFORMATION * ffsi = Irp - > AssociatedIrp . SystemBuffer ;
UINT64 totalsize , freespace ;
TRACE ( " FileFsFullSizeInformation \n " ) ;
// FIXME - calculate correctly for RAID
totalsize = Vcb - > superblock . total_bytes / Vcb - > superblock . sector_size ;
freespace = ( Vcb - > superblock . total_bytes - Vcb - > superblock . bytes_used ) / Vcb - > superblock . sector_size ;
ffsi - > TotalAllocationUnits . QuadPart = totalsize ;
ffsi - > ActualAvailableAllocationUnits . QuadPart = freespace ;
ffsi - > CallerAvailableAllocationUnits . QuadPart = ffsi - > ActualAvailableAllocationUnits . QuadPart ;
ffsi - > SectorsPerAllocationUnit = 1 ;
ffsi - > BytesPerSector = Vcb - > superblock . sector_size ;
BytesCopied = sizeof ( FILE_FS_FULL_SIZE_INFORMATION ) ;
Status = STATUS_SUCCESS ;
break ;
}
case FileFsObjectIdInformation :
FIXME ( " STUB: FileFsObjectIdInformation \n " ) ;
break ;
case FileFsSizeInformation :
{
FILE_FS_SIZE_INFORMATION * ffsi = Irp - > AssociatedIrp . SystemBuffer ;
UINT64 totalsize , freespace ;
TRACE ( " FileFsSizeInformation \n " ) ;
// FIXME - calculate correctly for RAID
// FIXME - is this returning the right free space?
totalsize = Vcb - > superblock . dev_item . num_bytes / Vcb - > superblock . sector_size ;
freespace = ( Vcb - > superblock . dev_item . num_bytes - Vcb - > superblock . dev_item . bytes_used ) / Vcb - > superblock . sector_size ;
ffsi - > TotalAllocationUnits . QuadPart = totalsize ;
ffsi - > AvailableAllocationUnits . QuadPart = freespace ;
ffsi - > SectorsPerAllocationUnit = 1 ;
ffsi - > BytesPerSector = Vcb - > superblock . sector_size ;
BytesCopied = sizeof ( FILE_FS_SIZE_INFORMATION ) ;
Status = STATUS_SUCCESS ;
break ;
}
case FileFsVolumeInformation :
{
FILE_FS_VOLUME_INFORMATION * data = Irp - > AssociatedIrp . SystemBuffer ;
FILE_FS_VOLUME_INFORMATION ffvi ;
BOOL overflow = FALSE ;
ULONG label_len , orig_label_len ;
TRACE ( " FileFsVolumeInformation \n " ) ;
TRACE ( " max length = %u \n " , IrpSp - > Parameters . QueryVolume . Length ) ;
acquire_tree_lock ( Vcb , FALSE ) ;
// orig_label_len = label_len = (ULONG)(wcslen(Vcb->label) * sizeof(WCHAR));
RtlUTF8ToUnicodeN ( NULL , 0 , & label_len , Vcb - > superblock . label , ( ULONG ) strlen ( Vcb - > superblock . label ) ) ;
orig_label_len = label_len ;
if ( IrpSp - > Parameters . QueryVolume . Length < sizeof ( FILE_FS_VOLUME_INFORMATION ) - sizeof ( WCHAR ) + label_len ) {
if ( IrpSp - > Parameters . QueryVolume . Length > sizeof ( FILE_FS_VOLUME_INFORMATION ) - sizeof ( WCHAR ) )
label_len = IrpSp - > Parameters . QueryVolume . Length - sizeof ( FILE_FS_VOLUME_INFORMATION ) + sizeof ( WCHAR ) ;
else
label_len = 0 ;
overflow = TRUE ;
}
TRACE ( " label_len = %u \n " , label_len ) ;
ffvi . VolumeCreationTime . QuadPart = 0 ; // FIXME
ffvi . VolumeSerialNumber = Vcb - > superblock . uuid . uuid [ 12 ] < < 24 | Vcb - > superblock . uuid . uuid [ 13 ] < < 16 | Vcb - > superblock . uuid . uuid [ 14 ] < < 8 | Vcb - > superblock . uuid . uuid [ 15 ] ;
ffvi . VolumeLabelLength = orig_label_len ;
ffvi . SupportsObjects = FALSE ;
RtlCopyMemory ( data , & ffvi , min ( sizeof ( FILE_FS_VOLUME_INFORMATION ) - sizeof ( WCHAR ) , IrpSp - > Parameters . QueryVolume . Length ) ) ;
if ( label_len > 0 ) {
ULONG bytecount ;
// RtlCopyMemory(&data->VolumeLabel[0], Vcb->label, label_len);
RtlUTF8ToUnicodeN ( & data - > VolumeLabel [ 0 ] , label_len , & bytecount , Vcb - > superblock . label , ( ULONG ) strlen ( Vcb - > superblock . label ) ) ;
TRACE ( " label = %.*S \n " , label_len / sizeof ( WCHAR ) , data - > VolumeLabel ) ;
}
release_tree_lock ( Vcb , FALSE ) ;
BytesCopied = sizeof ( FILE_FS_VOLUME_INFORMATION ) - sizeof ( WCHAR ) + label_len ;
Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS ;
break ;
}
default :
Status = STATUS_INVALID_PARAMETER ;
WARN ( " unknown FsInformatClass %u \n " , IrpSp - > Parameters . QueryVolume . FsInformationClass ) ;
break ;
}
// if (NT_SUCCESS(Status) && IrpSp->Parameters.QueryVolume.Length < BytesCopied) { // FIXME - should not copy anything if overflow
// WARN("overflow: %u < %u\n", IrpSp->Parameters.QueryVolume.Length, BytesCopied);
// BytesCopied = IrpSp->Parameters.QueryVolume.Length;
// Status = STATUS_BUFFER_OVERFLOW;
// }
Irp - > IoStatus . Status = Status ;
if ( ! NT_SUCCESS ( Status ) & & Status ! = STATUS_BUFFER_OVERFLOW )
Irp - > IoStatus . Information = 0 ;
else
Irp - > IoStatus . Information = BytesCopied ;
IoCompleteRequest ( Irp , IO_DISK_INCREMENT ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
TRACE ( " query volume information returning %08x \n " , Status ) ;
return Status ;
}
static NTSTATUS STDCALL read_completion ( PDEVICE_OBJECT DeviceObject , PIRP Irp , PVOID conptr ) {
read_context * context = conptr ;
// DbgPrint("read_completion\n");
context - > iosb = Irp - > IoStatus ;
KeSetEvent ( & context - > Event , 0 , FALSE ) ;
// return STATUS_SUCCESS;
return STATUS_MORE_PROCESSING_REQUIRED ;
}
// static void test_tree_deletion(device_extension* Vcb) {
// KEY searchkey/*, endkey*/;
// traverse_ptr tp, next_tp;
// root* r;
//
// searchkey.obj_id = 0x100;
// searchkey.obj_type = 0x54;
// searchkey.offset = 0xca4ab2f5;
//
// // endkey.obj_id = 0x100;
// // endkey.obj_type = 0x60;
// // endkey.offset = 0x15a;
//
// r = Vcb->roots;
// while (r && r->id != 0x102)
// r = r->next;
//
// if (!r) {
// ERR("error - could not find root\n");
// return;
// }
//
// if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
// ERR("error - could not find key\n");
// return;
// }
//
// while (TRUE/*keycmp(&tp.item->key, &endkey) < 1*/) {
// tp.item->ignore = TRUE;
// add_to_tree_cache(tc, tp.tree);
//
// if (find_next_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
// free_traverse_ptr(&tp);
// tp = next_tp;
// } else
// break;
// }
//
// free_traverse_ptr(&tp);
// }
// static void test_tree_splitting(device_extension* Vcb) {
// int i;
//
// for (i = 0; i < 1000; i++) {
// char* data = ExAllocatePoolWithTag(PagedPool, 4, ALLOC_TAG);
//
// insert_tree_item(Vcb, Vcb->extent_root, 0, 0xfd, i, data, 4, NULL);
// }
// }
static NTSTATUS STDCALL set_label ( device_extension * Vcb , FILE_FS_LABEL_INFORMATION * ffli ) {
ULONG utf8len ;
NTSTATUS Status ;
TRACE ( " label = %.*S \n " , ffli - > VolumeLabelLength / sizeof ( WCHAR ) , ffli - > VolumeLabel ) ;
Status = RtlUnicodeToUTF8N ( NULL , 0 , & utf8len , ffli - > VolumeLabel , ffli - > VolumeLabelLength ) ;
if ( ! NT_SUCCESS ( Status ) )
goto end ;
if ( utf8len > MAX_LABEL_SIZE ) {
Status = STATUS_INVALID_VOLUME_LABEL ;
goto end ;
}
// FIXME - check for '/' and '\\' and reject
acquire_tree_lock ( Vcb , TRUE ) ;
// utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
Status = RtlUnicodeToUTF8N ( ( PCHAR ) & Vcb - > superblock . label , MAX_LABEL_SIZE * sizeof ( WCHAR ) , & utf8len , ffli - > VolumeLabel , ffli - > VolumeLabelLength ) ;
if ( ! NT_SUCCESS ( Status ) )
goto release ;
if ( utf8len < MAX_LABEL_SIZE * sizeof ( WCHAR ) )
RtlZeroMemory ( Vcb - > superblock . label + utf8len , ( MAX_LABEL_SIZE * sizeof ( WCHAR ) ) - utf8len ) ;
// test_tree_deletion(Vcb); // TESTING
// test_tree_splitting(Vcb);
Status = consider_write ( Vcb ) ;
release :
release_tree_lock ( Vcb , TRUE ) ;
end :
TRACE ( " returning %08x \n " , Status ) ;
return Status ;
}
static NTSTATUS STDCALL drv_set_volume_information ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation ( Irp ) ;
device_extension * Vcb = DeviceObject - > DeviceExtension ;
NTSTATUS Status ;
BOOL top_level ;
TRACE ( " set volume information \n " ) ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
Status = STATUS_NOT_IMPLEMENTED ;
if ( Vcb - > readonly ) {
Status = STATUS_MEDIA_WRITE_PROTECTED ;
goto end ;
}
switch ( IrpSp - > Parameters . SetVolume . FsInformationClass ) {
case FileFsControlInformation :
FIXME ( " STUB: FileFsControlInformation \n " ) ;
break ;
case FileFsLabelInformation :
TRACE ( " FileFsLabelInformation \n " ) ;
Status = set_label ( Vcb , Irp - > AssociatedIrp . SystemBuffer ) ;
break ;
case FileFsObjectIdInformation :
FIXME ( " STUB: FileFsObjectIdInformation \n " ) ;
break ;
default :
WARN ( " Unrecognized FsInformationClass 0x%x \n " , IrpSp - > Parameters . SetVolume . FsInformationClass ) ;
break ;
}
end :
Irp - > IoStatus . Status = Status ;
Irp - > IoStatus . Information = 0 ;
IoCompleteRequest ( Irp , IO_NO_INCREMENT ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
return Status ;
}
NTSTATUS delete_dir_item ( device_extension * Vcb , root * subvol , UINT64 parinode , UINT32 crc32 , PANSI_STRING utf8 , LIST_ENTRY * rollback ) {
KEY searchkey ;
traverse_ptr tp ;
NTSTATUS Status ;
searchkey . obj_id = parinode ;
searchkey . obj_type = TYPE_DIR_ITEM ;
searchkey . offset = crc32 ;
Status = find_item ( Vcb , subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & searchkey , & tp . item - > key ) ) {
if ( tp . item - > size < sizeof ( DIR_ITEM ) ) {
WARN ( " (%llx,%x,%llx) was %u bytes, expected %u \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , tp . item - > size , sizeof ( DIR_ITEM ) ) ;
} else {
DIR_ITEM * di ;
LONG len ;
di = ( DIR_ITEM * ) tp . item - > data ;
len = tp . item - > size ;
do {
if ( di - > n = = utf8 - > Length & & RtlCompareMemory ( di - > name , utf8 - > Buffer , di - > n ) = = di - > n ) {
ULONG newlen = tp . item - > size - ( sizeof ( DIR_ITEM ) - sizeof ( char ) + di - > n + di - > m ) ;
delete_tree_item ( Vcb , & tp , rollback ) ;
if ( newlen = = 0 ) {
TRACE ( " deleting (%llx,%x,%llx) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
} else {
UINT8 * newdi = ExAllocatePoolWithTag ( PagedPool , newlen , ALLOC_TAG ) , * dioff ;
if ( ! newdi ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
TRACE ( " modifying (%llx,%x,%llx) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
if ( ( UINT8 * ) di > tp . item - > data ) {
RtlCopyMemory ( newdi , tp . item - > data , ( UINT8 * ) di - tp . item - > data ) ;
dioff = newdi + ( ( UINT8 * ) di - tp . item - > data ) ;
} else {
dioff = newdi ;
}
if ( ( UINT8 * ) & di - > name [ di - > n + di - > m ] - tp . item - > data < tp . item - > size )
RtlCopyMemory ( dioff , & di - > name [ di - > n + di - > m ] , tp . item - > size - ( ( UINT8 * ) & di - > name [ di - > n + di - > m ] - tp . item - > data ) ) ;
insert_tree_item ( Vcb , subvol , parinode , TYPE_DIR_ITEM , crc32 , newdi , newlen , NULL , rollback ) ;
}
break ;
}
len - = sizeof ( DIR_ITEM ) - sizeof ( char ) + di - > n + di - > m ;
di = ( DIR_ITEM * ) & di - > name [ di - > n + di - > m ] ;
} while ( len > 0 ) ;
}
} else {
WARN ( " could not find DIR_ITEM for crc32 %08x \n " , crc32 ) ;
}
free_traverse_ptr ( & tp ) ;
return STATUS_SUCCESS ;
}
NTSTATUS delete_inode_ref ( device_extension * Vcb , root * subvol , UINT64 inode , UINT64 parinode , PANSI_STRING utf8 , UINT64 * index , LIST_ENTRY * rollback ) {
KEY searchkey ;
traverse_ptr tp ;
BOOL changed = FALSE ;
NTSTATUS Status ;
searchkey . obj_id = inode ;
searchkey . obj_type = TYPE_INODE_REF ;
searchkey . offset = parinode ;
Status = find_item ( Vcb , subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & searchkey , & tp . item - > key ) ) {
if ( tp . item - > size < sizeof ( INODE_REF ) ) {
WARN ( " (%llx,%x,%llx) 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 ( INODE_REF ) ) ;
} else {
INODE_REF * ir ;
ULONG len ;
ir = ( INODE_REF * ) tp . item - > data ;
len = tp . item - > size ;
do {
ULONG itemlen ;
if ( len < sizeof ( INODE_REF ) | | len < sizeof ( INODE_REF ) - 1 + ir - > n ) {
ERR ( " (%llx,%x,%llx) was truncated \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
break ;
}
itemlen = sizeof ( INODE_REF ) - sizeof ( char ) + ir - > n ;
if ( ir - > n = = utf8 - > Length & & RtlCompareMemory ( ir - > name , utf8 - > Buffer , ir - > n ) = = ir - > n ) {
ULONG newlen = tp . item - > size - itemlen ;
delete_tree_item ( Vcb , & tp , rollback ) ;
changed = TRUE ;
if ( newlen = = 0 ) {
TRACE ( " deleting (%llx,%x,%llx) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
} else {
UINT8 * newir = ExAllocatePoolWithTag ( PagedPool , newlen , ALLOC_TAG ) , * iroff ;
if ( ! newir ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
TRACE ( " modifying (%llx,%x,%llx) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
if ( ( UINT8 * ) ir > tp . item - > data ) {
RtlCopyMemory ( newir , tp . item - > data , ( UINT8 * ) ir - tp . item - > data ) ;
iroff = newir + ( ( UINT8 * ) ir - tp . item - > data ) ;
} else {
iroff = newir ;
}
if ( ( UINT8 * ) & ir - > name [ ir - > n ] - tp . item - > data < tp . item - > size )
RtlCopyMemory ( iroff , & ir - > name [ ir - > n ] , tp . item - > size - ( ( UINT8 * ) & ir - > name [ ir - > n ] - tp . item - > data ) ) ;
insert_tree_item ( Vcb , subvol , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , newir , newlen , NULL , rollback ) ;
}
if ( index )
* index = ir - > index ;
break ;
}
if ( len > itemlen ) {
len - = itemlen ;
ir = ( INODE_REF * ) & ir - > name [ ir - > n ] ;
} else
break ;
} while ( len > 0 ) ;
if ( ! changed ) {
WARN ( " found INODE_REF entry, but couldn't find filename \n " ) ;
}
}
} else {
WARN ( " could not find INODE_REF entry for inode %llx in %llx \n " , searchkey . obj_id , searchkey . offset ) ;
}
free_traverse_ptr ( & tp ) ;
if ( changed )
return STATUS_SUCCESS ;
if ( ! ( Vcb - > superblock . incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF ) )
return STATUS_INTERNAL_ERROR ;
searchkey . obj_id = inode ;
searchkey . obj_type = TYPE_INODE_EXTREF ;
searchkey . offset = calc_crc32c ( ( UINT32 ) parinode , ( UINT8 * ) utf8 - > Buffer , utf8 - > Length ) ;
Status = find_item ( Vcb , subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & searchkey , & tp . item - > key ) ) {
if ( tp . item - > size < sizeof ( INODE_EXTREF ) ) {
WARN ( " (%llx,%x,%llx) 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 ( INODE_EXTREF ) ) ;
} else {
INODE_EXTREF * ier ;
ULONG len ;
ier = ( INODE_EXTREF * ) tp . item - > data ;
len = tp . item - > size ;
do {
ULONG itemlen ;
if ( len < sizeof ( INODE_EXTREF ) | | len < sizeof ( INODE_EXTREF ) - 1 + ier - > n ) {
ERR ( " (%llx,%x,%llx) was truncated \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
break ;
}
itemlen = sizeof ( INODE_EXTREF ) - sizeof ( char ) + ier - > n ;
if ( ier - > dir = = parinode & & ier - > n = = utf8 - > Length & & RtlCompareMemory ( ier - > name , utf8 - > Buffer , ier - > n ) = = ier - > n ) {
ULONG newlen = tp . item - > size - itemlen ;
delete_tree_item ( Vcb , & tp , rollback ) ;
changed = TRUE ;
if ( newlen = = 0 ) {
TRACE ( " deleting (%llx,%x,%llx) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
} else {
UINT8 * newier = ExAllocatePoolWithTag ( PagedPool , newlen , ALLOC_TAG ) , * ieroff ;
if ( ! newier ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
TRACE ( " modifying (%llx,%x,%llx) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
if ( ( UINT8 * ) ier > tp . item - > data ) {
RtlCopyMemory ( newier , tp . item - > data , ( UINT8 * ) ier - tp . item - > data ) ;
ieroff = newier + ( ( UINT8 * ) ier - tp . item - > data ) ;
} else {
ieroff = newier ;
}
if ( ( UINT8 * ) & ier - > name [ ier - > n ] - tp . item - > data < tp . item - > size )
RtlCopyMemory ( ieroff , & ier - > name [ ier - > n ] , tp . item - > size - ( ( UINT8 * ) & ier - > name [ ier - > n ] - tp . item - > data ) ) ;
insert_tree_item ( Vcb , subvol , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , newier , newlen , NULL , rollback ) ;
}
if ( index )
* index = ier - > index ;
break ;
}
if ( len > itemlen ) {
len - = itemlen ;
ier = ( INODE_EXTREF * ) & ier - > name [ ier - > n ] ;
} else
break ;
} while ( len > 0 ) ;
}
} else {
WARN ( " couldn't find INODE_EXTREF entry either (offset = %08x) \n " , ( UINT32 ) searchkey . offset ) ;
}
free_traverse_ptr ( & tp ) ;
return changed ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR ;
}
NTSTATUS delete_fcb ( fcb * fcb , PFILE_OBJECT FileObject , LIST_ENTRY * rollback ) {
ULONG bytecount ;
NTSTATUS Status ;
char * utf8 = NULL ;
UINT32 crc32 ;
KEY searchkey ;
traverse_ptr tp , tp2 ;
UINT64 parinode , index ;
INODE_ITEM * ii , * dirii ;
LARGE_INTEGER time ;
BTRFS_TIME now ;
LIST_ENTRY changed_sector_list ;
# ifdef _DEBUG
LARGE_INTEGER freq , time1 , time2 ;
# endif
// FIXME - throw error if try to delete subvol root(?)
// FIXME - delete all children if deleting directory
if ( fcb - > deleted ) {
2016-03-26 11:53:07 +00:00
WARN ( " trying to delete already-deleted file \n " ) ;
return STATUS_SUCCESS ;
2016-03-23 20:35:05 +00:00
}
if ( ! fcb - > par ) {
ERR ( " error - trying to delete root FCB \n " ) ;
return STATUS_INTERNAL_ERROR ;
}
# ifdef _DEBUG
time1 = KeQueryPerformanceCounter ( & freq ) ;
# endif
KeQuerySystemTime ( & time ) ;
win_time_to_unix ( time , & now ) ;
if ( fcb - > ads ) {
char * s ;
TRACE ( " deleting ADS \n " ) ;
s = ExAllocatePoolWithTag ( PagedPool , fcb - > adsxattr . Length + 1 , ALLOC_TAG ) ;
if ( ! s ) {
ERR ( " out of memory \n " ) ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto exit ;
}
RtlCopyMemory ( s , fcb - > adsxattr . Buffer , fcb - > adsxattr . Length ) ;
s [ fcb - > adsxattr . Length ] = 0 ;
if ( ! delete_xattr ( fcb - > Vcb , fcb - > par - > subvol , fcb - > par - > inode , s , fcb - > adshash , rollback ) ) {
ERR ( " failed to delete xattr %s \n " , s ) ;
}
ExFreePool ( s ) ;
fcb - > par - > inode_item . transid = fcb - > Vcb - > superblock . generation ;
fcb - > par - > inode_item . sequence + + ;
fcb - > par - > inode_item . st_ctime = now ;
searchkey . obj_id = fcb - > par - > inode ;
searchkey . obj_type = TYPE_INODE_ITEM ;
searchkey . offset = 0xffffffffffffffff ;
Status = find_item ( fcb - > Vcb , fcb - > par - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
goto exit ;
}
if ( tp . item - > key . obj_id ! = searchkey . obj_id | | tp . item - > key . obj_type ! = searchkey . obj_type ) {
ERR ( " error - could not find INODE_ITEM for inode %llx in subvol %llx \n " , fcb - > par - > inode , fcb - > par - > subvol - > id ) ;
free_traverse_ptr ( & tp ) ;
Status = STATUS_INTERNAL_ERROR ;
goto exit ;
}
ii = ExAllocatePoolWithTag ( PagedPool , sizeof ( INODE_ITEM ) , ALLOC_TAG ) ;
if ( ! ii ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto exit ;
}
RtlCopyMemory ( ii , & fcb - > par - > inode_item , sizeof ( INODE_ITEM ) ) ;
delete_tree_item ( fcb - > Vcb , & tp , rollback ) ;
insert_tree_item ( fcb - > Vcb , fcb - > par - > subvol , searchkey . obj_id , searchkey . obj_type , 0 , ii , sizeof ( INODE_ITEM ) , NULL , rollback ) ;
free_traverse_ptr ( & tp ) ;
fcb - > par - > subvol - > root_item . ctransid = fcb - > Vcb - > superblock . generation ;
fcb - > par - > subvol - > root_item . ctime = now ;
goto success ;
}
Status = RtlUnicodeToUTF8N ( NULL , 0 , & bytecount , fcb - > filepart . Buffer , fcb - > filepart . Length ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " RtlUnicodeToUTF8N failed with error %08x \n " , Status ) ;
return Status ;
}
utf8 = ExAllocatePoolWithTag ( PagedPool , bytecount + 1 , ALLOC_TAG ) ;
if ( ! utf8 ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlUnicodeToUTF8N ( utf8 , bytecount , & bytecount , fcb - > filepart . Buffer , fcb - > filepart . Length ) ;
utf8 [ bytecount ] = 0 ;
crc32 = calc_crc32c ( 0xfffffffe , ( UINT8 * ) utf8 , bytecount ) ;
TRACE ( " deleting %.*S \n " , fcb - > full_filename . Length / sizeof ( WCHAR ) , fcb - > full_filename . Buffer ) ;
if ( fcb - > par - > subvol = = fcb - > subvol )
parinode = fcb - > par - > inode ;
else
parinode = SUBVOL_ROOT_INODE ;
// delete DIR_ITEM (0x54)
Status = delete_dir_item ( fcb - > Vcb , fcb - > subvol , parinode , crc32 , & fcb - > utf8 , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " delete_dir_item returned %08x \n " , Status ) ;
return Status ;
}
// delete INODE_REF (0xc)
index = 0 ;
Status = delete_inode_ref ( fcb - > Vcb , fcb - > subvol , fcb - > inode , parinode , & fcb - > utf8 , & index , rollback ) ;
// delete DIR_INDEX (0x60)
searchkey . obj_id = parinode ;
searchkey . obj_type = TYPE_DIR_INDEX ;
searchkey . offset = index ;
Status = find_item ( fcb - > Vcb , fcb - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
free_traverse_ptr ( & tp ) ;
Status = STATUS_INTERNAL_ERROR ;
goto exit ;
}
if ( ! keycmp ( & searchkey , & tp . item - > key ) ) {
delete_tree_item ( fcb - > Vcb , & tp , rollback ) ;
TRACE ( " deleting (%llx,%x,%llx) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
}
// delete INODE_ITEM (0x1)
searchkey . obj_id = fcb - > inode ;
searchkey . obj_type = TYPE_INODE_ITEM ;
searchkey . offset = 0 ;
Status = find_item ( fcb - > Vcb , fcb - > subvol , & tp2 , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
free_traverse_ptr ( & tp ) ;
goto exit ;
}
free_traverse_ptr ( & tp ) ;
tp = tp2 ;
if ( keycmp ( & searchkey , & tp . item - > key ) ) {
ERR ( " error - INODE_ITEM not found \n " ) ;
free_traverse_ptr ( & tp ) ;
Status = STATUS_INTERNAL_ERROR ;
goto exit ;
}
if ( tp . item - > size < sizeof ( INODE_ITEM ) ) {
ERR ( " (%llx,%x,%llx) was %u bytes, expected %u \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , tp . item - > size , sizeof ( INODE_ITEM ) ) ;
free_traverse_ptr ( & tp ) ;
Status = STATUS_INTERNAL_ERROR ;
goto exit ;
}
ii = ( INODE_ITEM * ) tp . item - > data ;
TRACE ( " nlink = %u \n " , ii - > st_nlink ) ;
if ( ii - > st_nlink > 1 ) {
INODE_ITEM * newii ;
newii = ExAllocatePoolWithTag ( PagedPool , sizeof ( INODE_ITEM ) , ALLOC_TAG ) ;
if ( ! newii ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto exit ;
}
RtlCopyMemory ( newii , ii , sizeof ( INODE_ITEM ) ) ;
newii - > st_nlink - - ;
newii - > transid = fcb - > Vcb - > superblock . generation ;
newii - > sequence + + ;
newii - > st_ctime = now ;
TRACE ( " replacing (%llx,%x,%llx) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
delete_tree_item ( fcb - > Vcb , & tp , rollback ) ;
if ( ! insert_tree_item ( fcb - > Vcb , fcb - > subvol , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , newii , sizeof ( INODE_ITEM ) , NULL , rollback ) )
ERR ( " error - failed to insert item \n " ) ;
free_traverse_ptr ( & tp ) ;
goto success2 ;
}
delete_tree_item ( fcb - > Vcb , & tp , rollback ) ;
TRACE ( " deleting (%llx,%x,%llx) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
// delete XATTR_ITEM (0x18)
while ( find_next_item ( fcb - > Vcb , & tp , & tp2 , FALSE ) ) {
free_traverse_ptr ( & tp ) ;
tp = tp2 ;
if ( tp . item - > key . obj_id = = fcb - > inode ) {
// FIXME - do metadata thing here too?
if ( tp . item - > key . obj_type = = TYPE_XATTR_ITEM ) {
delete_tree_item ( fcb - > Vcb , & tp , rollback ) ;
TRACE ( " deleting (%llx,%x,%llx) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
}
} else
break ;
}
free_traverse_ptr ( & tp ) ;
// excise extents
InitializeListHead ( & changed_sector_list ) ;
2016-03-26 11:53:07 +00:00
if ( fcb - > type ! = BTRFS_TYPE_DIRECTORY & & fcb - > inode_item . st_size > 0 ) {
2016-03-23 20:35:05 +00:00
Status = excise_extents ( fcb - > Vcb , fcb , 0 , sector_align ( fcb - > inode_item . st_size , fcb - > Vcb - > superblock . sector_size ) , & changed_sector_list , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " excise_extents returned %08x \n " , Status ) ;
goto exit ;
}
if ( ! ( fcb - > inode_item . flags & BTRFS_INODE_NODATASUM ) )
update_checksum_tree ( fcb - > Vcb , & changed_sector_list , rollback ) ;
}
success2 :
// update INODE_ITEM of parent
searchkey . obj_id = parinode ;
searchkey . obj_type = TYPE_INODE_ITEM ;
searchkey . offset = 0 ;
Status = find_item ( fcb - > Vcb , fcb - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_tree returned %08x \n " , Status ) ;
goto exit ;
}
if ( keycmp ( & searchkey , & tp . item - > key ) ) {
ERR ( " error - could not find INODE_ITEM for parent directory %llx in subvol %llx \n " , parinode , fcb - > subvol - > id ) ;
free_traverse_ptr ( & tp ) ;
Status = STATUS_INTERNAL_ERROR ;
goto exit ;
}
TRACE ( " fcb->par->inode_item.st_size was %llx \n " , fcb - > par - > inode_item . st_size ) ;
fcb - > par - > inode_item . st_size - = bytecount * 2 ;
TRACE ( " fcb->par->inode_item.st_size now %llx \n " , fcb - > par - > inode_item . st_size ) ;
fcb - > par - > inode_item . transid = fcb - > Vcb - > superblock . generation ;
fcb - > par - > inode_item . sequence + + ;
fcb - > par - > inode_item . st_ctime = now ;
fcb - > par - > inode_item . st_mtime = now ;
dirii = ExAllocatePoolWithTag ( PagedPool , sizeof ( INODE_ITEM ) , ALLOC_TAG ) ;
if ( ! dirii ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto exit ;
}
RtlCopyMemory ( dirii , & fcb - > par - > inode_item , sizeof ( INODE_ITEM ) ) ;
delete_tree_item ( fcb - > Vcb , & tp , rollback ) ;
insert_tree_item ( fcb - > Vcb , fcb - > subvol , searchkey . obj_id , searchkey . obj_type , searchkey . offset , dirii , sizeof ( INODE_ITEM ) , NULL , rollback ) ;
free_traverse_ptr ( & tp ) ;
fcb - > subvol - > root_item . ctransid = fcb - > Vcb - > superblock . generation ;
fcb - > subvol - > root_item . ctime = now ;
success :
consider_write ( fcb - > Vcb ) ;
fcb - > deleted = TRUE ;
fcb - > Header . AllocationSize . QuadPart = 0 ;
fcb - > Header . FileSize . QuadPart = 0 ;
fcb - > Header . ValidDataLength . QuadPart = 0 ;
if ( FileObject & & FileObject - > PrivateCacheMap ) {
CC_FILE_SIZES ccfs ;
ccfs . AllocationSize = fcb - > Header . AllocationSize ;
ccfs . FileSize = fcb - > Header . FileSize ;
ccfs . ValidDataLength = fcb - > Header . ValidDataLength ;
CcSetFileSizes ( FileObject , & ccfs ) ;
}
// FIXME - set deleted flag of any open FCBs for ADS
TRACE ( " sending notification for deletion of %.*S \n " , fcb - > full_filename . Length / sizeof ( WCHAR ) , fcb - > full_filename . Buffer ) ;
FsRtlNotifyFullReportChange ( fcb - > Vcb - > NotifySync , & fcb - > Vcb - > DirNotifyList , ( PSTRING ) & fcb - > full_filename , fcb - > name_offset * sizeof ( WCHAR ) , NULL , NULL ,
fcb - > type = = BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME ,
FILE_ACTION_REMOVED , NULL ) ;
# ifdef _DEBUG
time2 = KeQueryPerformanceCounter ( NULL ) ;
# endif
TRACE ( " time = %u (freq = %u) \n " , ( UINT32 ) ( time2 . QuadPart - time1 . QuadPart ) , ( UINT32 ) freq . QuadPart ) ;
Status = STATUS_SUCCESS ;
exit :
if ( utf8 )
ExFreePool ( utf8 ) ;
return Status ;
}
void _free_fcb ( fcb * fcb , const char * func , const char * file , unsigned int line ) {
ULONG rc ;
rc = InterlockedDecrement ( & fcb - > refcount ) ;
# ifdef DEBUG_FCB_REFCOUNTS
// WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
# ifdef DEBUG_LONG_MESSAGES
2016-03-26 11:53:07 +00:00
_debug_message ( func , 1 , file , line , " fcb %p: refcount now %i (%.*S) \n " , fcb , rc , fcb - > full_filename . Length / sizeof ( WCHAR ) , fcb - > full_filename . Buffer ) ;
2016-03-23 20:35:05 +00:00
# else
2016-03-26 11:53:07 +00:00
_debug_message ( func , 1 , " fcb %p: refcount now %i (%.*S) \n " , fcb , rc , fcb - > full_filename . Length / sizeof ( WCHAR ) , fcb - > full_filename . Buffer ) ;
2016-03-23 20:35:05 +00:00
# endif
# endif
if ( rc > 0 )
return ;
ExAcquireResourceExclusiveLite ( & fcb - > Vcb - > fcb_lock , TRUE ) ;
if ( fcb - > filepart . Buffer )
RtlFreeUnicodeString ( & fcb - > filepart ) ;
ExDeleteResourceLite ( & fcb - > nonpaged - > resource ) ;
ExDeleteResourceLite ( & fcb - > nonpaged - > paging_resource ) ;
ExFreePool ( fcb - > nonpaged ) ;
if ( fcb - > par /* && fcb->par != fcb->par->Vcb->root_fcb*/ ) {
RemoveEntryList ( & fcb - > list_entry ) ;
_free_fcb ( fcb - > par , func , file , line ) ;
}
if ( fcb - > prev )
fcb - > prev - > next = fcb - > next ;
if ( fcb - > next )
fcb - > next - > prev = fcb - > prev ;
if ( fcb - > Vcb - > fcbs = = fcb )
fcb - > Vcb - > fcbs = fcb - > next ;
if ( fcb - > full_filename . Buffer )
ExFreePool ( fcb - > full_filename . Buffer ) ;
if ( fcb - > sd )
ExFreePool ( fcb - > sd ) ;
if ( fcb - > adsxattr . Buffer )
ExFreePool ( fcb - > adsxattr . Buffer ) ;
if ( fcb - > utf8 . Buffer )
ExFreePool ( fcb - > utf8 . Buffer ) ;
FsRtlUninitializeFileLock ( & fcb - > lock ) ;
ExReleaseResourceLite ( & fcb - > Vcb - > fcb_lock ) ;
ExFreePool ( fcb ) ;
# ifdef DEBUG_FCB_REFCOUNTS
# ifdef DEBUG_LONG_MESSAGES
2016-03-26 11:53:07 +00:00
_debug_message ( func , 1 , file , line , " freeing fcb %p \n " , fcb ) ;
2016-03-23 20:35:05 +00:00
# else
2016-03-26 11:53:07 +00:00
_debug_message ( func , 1 , " freeing fcb %p \n " , fcb ) ;
2016-03-23 20:35:05 +00:00
# endif
# endif
}
static NTSTATUS STDCALL close_file ( device_extension * Vcb , PFILE_OBJECT FileObject ) {
fcb * fcb ;
ccb * ccb ;
TRACE ( " FileObject = %p \n " , FileObject ) ;
fcb = FileObject - > FsContext ;
if ( ! fcb ) {
TRACE ( " FCB was NULL, returning success \n " ) ;
return STATUS_SUCCESS ;
}
ccb = FileObject - > FsContext2 ;
TRACE ( " close called for %.*S (fcb == %p) \n " , fcb - > full_filename . Length / sizeof ( WCHAR ) , fcb - > full_filename . Buffer , fcb ) ;
FsRtlNotifyCleanup ( Vcb - > NotifySync , & Vcb - > DirNotifyList , ccb ) ;
// FIXME - make sure notification gets sent if file is being deleted
if ( ccb ) {
if ( ccb - > query_string . Buffer )
RtlFreeUnicodeString ( & ccb - > query_string ) ;
ExFreePool ( ccb ) ;
}
2016-03-26 11:53:07 +00:00
CcUninitializeCacheMap ( FileObject , NULL , NULL ) ;
2016-03-23 20:35:05 +00:00
free_fcb ( fcb ) ;
return STATUS_SUCCESS ;
}
static void STDCALL uninit ( device_extension * Vcb ) {
chunk * c ;
space * s ;
UINT64 i ;
LIST_ENTRY rollback ;
InitializeListHead ( & rollback ) ;
acquire_tree_lock ( Vcb , TRUE ) ;
if ( Vcb - > write_trees > 0 )
do_write ( Vcb , & rollback ) ;
free_tree_cache ( & Vcb - > tree_cache ) ;
clear_rollback ( & rollback ) ;
release_tree_lock ( Vcb , TRUE ) ;
while ( Vcb - > roots ) {
root * r = Vcb - > roots - > next ;
ExDeleteResourceLite ( & Vcb - > roots - > nonpaged - > load_tree_lock ) ;
ExFreePool ( Vcb - > roots - > nonpaged ) ;
ExFreePool ( Vcb - > roots ) ;
Vcb - > roots = r ;
}
while ( ! IsListEmpty ( & Vcb - > chunks ) ) {
LIST_ENTRY * le = RemoveHeadList ( & Vcb - > chunks ) ;
c = CONTAINING_RECORD ( le , chunk , list_entry ) ;
while ( ! IsListEmpty ( & c - > space ) ) {
LIST_ENTRY * le2 = RemoveHeadList ( & c - > space ) ;
s = CONTAINING_RECORD ( le2 , space , list_entry ) ;
ExFreePool ( s ) ;
}
if ( c - > devices )
ExFreePool ( c - > devices ) ;
ExFreePool ( c - > chunk_item ) ;
ExFreePool ( c ) ;
}
free_fcb ( Vcb - > volume_fcb ) ;
free_fcb ( Vcb - > root_fcb ) ;
for ( i = 0 ; i < Vcb - > superblock . num_devices ; i + + ) {
while ( ! IsListEmpty ( & Vcb - > devices [ i ] . disk_holes ) ) {
LIST_ENTRY * le = RemoveHeadList ( & Vcb - > devices [ i ] . disk_holes ) ;
disk_hole * dh = CONTAINING_RECORD ( le , disk_hole , listentry ) ;
ExFreePool ( dh ) ;
}
}
ExFreePool ( Vcb - > devices ) ;
ExDeleteResourceLite ( & Vcb - > fcb_lock ) ;
ExDeleteResourceLite ( & Vcb - > load_lock ) ;
ExDeleteResourceLite ( & Vcb - > tree_lock ) ;
ZwClose ( Vcb - > flush_thread_handle ) ;
}
static NTSTATUS STDCALL drv_cleanup ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
NTSTATUS Status ;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation ( Irp ) ;
PFILE_OBJECT FileObject = IrpSp - > FileObject ;
fcb * fcb ;
BOOL top_level ;
TRACE ( " cleanup \n " ) ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
if ( DeviceObject = = devobj ) {
TRACE ( " closing file system \n " ) ;
Status = STATUS_SUCCESS ;
goto exit ;
}
if ( FileObject ) {
LONG oc ;
fcb = FileObject - > FsContext ;
TRACE ( " cleanup called for FileObject %p \n " , FileObject ) ;
TRACE ( " fcb %p (%.*S), refcount = %u, open_count = %u \n " , fcb , fcb - > full_filename . Length / sizeof ( WCHAR ) , fcb - > full_filename . Buffer , fcb - > refcount , fcb - > open_count ) ;
IoRemoveShareAccess ( FileObject , & fcb - > share_access ) ;
oc = InterlockedDecrement ( & fcb - > open_count ) ;
2016-03-26 11:53:07 +00:00
# ifdef DEBUG_FCB_REFCOUNTS
ERR ( " fcb %p: open_count now %i \n " , fcb , oc ) ;
# endif
2016-03-23 20:35:05 +00:00
if ( oc = = 0 ) {
if ( fcb - > delete_on_close & & fcb ! = fcb - > Vcb - > root_fcb & & fcb ! = fcb - > Vcb - > volume_fcb ) {
LIST_ENTRY rollback ;
InitializeListHead ( & rollback ) ;
acquire_tree_lock ( fcb - > Vcb , TRUE ) ;
Status = delete_fcb ( fcb , FileObject , & rollback ) ;
if ( NT_SUCCESS ( Status ) ) {
LARGE_INTEGER newlength ;
if ( FileObject - > Flags & FO_CACHE_SUPPORTED & & fcb - > nonpaged - > segment_object . DataSectionObject )
CcPurgeCacheSection ( & fcb - > nonpaged - > segment_object , NULL , 0 , FALSE ) ;
newlength . QuadPart = 0 ;
if ( ! CcUninitializeCacheMap ( FileObject , & newlength , NULL ) ) {
TRACE ( " CcUninitializeCacheMap failed \n " ) ;
}
clear_rollback ( & rollback ) ;
} else
do_rollback ( fcb - > Vcb , & rollback ) ;
release_tree_lock ( fcb - > Vcb , TRUE ) ;
} else if ( FileObject - > Flags & FO_CACHE_SUPPORTED & & fcb - > nonpaged - > segment_object . DataSectionObject ) {
IO_STATUS_BLOCK iosb ;
CcFlushCache ( FileObject - > SectionObjectPointer , NULL , 0 , & iosb ) ;
if ( ! NT_SUCCESS ( iosb . Status ) ) {
ERR ( " CcFlushCache returned %08x \n " , iosb . Status ) ;
}
ExAcquireResourceExclusiveLite ( fcb - > Header . PagingIoResource , TRUE ) ;
ExReleaseResourceLite ( fcb - > Header . PagingIoResource ) ;
CcPurgeCacheSection ( & fcb - > nonpaged - > segment_object , NULL , 0 , FALSE ) ;
TRACE ( " flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx) \n " ,
FileObject , fcb , fcb - > Header . AllocationSize . QuadPart , fcb - > Header . FileSize . QuadPart , fcb - > Header . ValidDataLength . QuadPart ) ;
}
2016-03-26 11:53:07 +00:00
if ( fcb - > Vcb & & fcb ! = fcb - > Vcb - > volume_fcb )
CcUninitializeCacheMap ( FileObject , NULL , NULL ) ;
2016-03-23 20:35:05 +00:00
}
FileObject - > Flags | = FO_CLEANUP_COMPLETE ;
}
Status = STATUS_SUCCESS ;
exit :
Irp - > IoStatus . Status = Status ;
Irp - > IoStatus . Information = 0 ;
IoCompleteRequest ( Irp , IO_NO_INCREMENT ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
return Status ;
}
ULONG STDCALL get_file_attributes ( device_extension * Vcb , INODE_ITEM * ii , root * r , UINT64 inode , UINT8 type , BOOL dotfile , BOOL ignore_xa ) {
ULONG att ;
char * eaval ;
UINT16 ealen ;
if ( ! ignore_xa & & get_xattr ( Vcb , r , inode , EA_DOSATTRIB , EA_DOSATTRIB_HASH , ( UINT8 * * ) & eaval , & ealen ) ) {
if ( ealen > 2 ) {
if ( eaval [ 0 ] = = ' 0 ' & & eaval [ 1 ] = = ' x ' ) {
int i ;
ULONG dosnum = 0 ;
for ( i = 2 ; i < ealen ; i + + ) {
dosnum * = 0x10 ;
if ( eaval [ i ] > = ' 0 ' & & eaval [ i ] < = ' 9 ' )
dosnum | = eaval [ i ] - ' 0 ' ;
else if ( eaval [ i ] > = ' a ' & & eaval [ i ] < = ' f ' )
dosnum | = eaval [ i ] + 10 - ' a ' ;
else if ( eaval [ i ] > = ' A ' & & eaval [ i ] < = ' F ' )
dosnum | = eaval [ i ] + 10 - ' a ' ;
}
TRACE ( " DOSATTRIB: %08x \n " , dosnum ) ;
ExFreePool ( eaval ) ;
return dosnum ;
}
}
ExFreePool ( eaval ) ;
}
switch ( type ) {
case BTRFS_TYPE_DIRECTORY :
att = FILE_ATTRIBUTE_DIRECTORY ;
break ;
case BTRFS_TYPE_SYMLINK :
att = FILE_ATTRIBUTE_REPARSE_POINT ;
break ;
default :
att = 0 ;
break ;
}
if ( dotfile ) {
att | = FILE_ATTRIBUTE_HIDDEN ;
}
att | = FILE_ATTRIBUTE_ARCHIVE ;
// FIXME - get READONLY from ii->st_mode
// FIXME - return SYSTEM for block/char devices?
if ( att = = 0 )
att = FILE_ATTRIBUTE_NORMAL ;
return att ;
}
// static int STDCALL utf8icmp(char* a, char* b) {
// return strcmp(a, b); // FIXME - don't treat as ASCII
// }
NTSTATUS sync_read_phys ( PDEVICE_OBJECT DeviceObject , LONGLONG StartingOffset , ULONG Length , PUCHAR Buffer ) {
IO_STATUS_BLOCK * IoStatus ;
LARGE_INTEGER Offset ;
PIRP Irp ;
PIO_STACK_LOCATION IrpSp ;
NTSTATUS Status ;
read_context * context ;
num_reads + + ;
context = ExAllocatePoolWithTag ( NonPagedPool , sizeof ( read_context ) , ALLOC_TAG ) ;
if ( ! context ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlZeroMemory ( context , sizeof ( read_context ) ) ;
KeInitializeEvent ( & context - > Event , NotificationEvent , FALSE ) ;
IoStatus = ExAllocatePoolWithTag ( NonPagedPool , sizeof ( IO_STATUS_BLOCK ) , ALLOC_TAG ) ;
if ( ! IoStatus ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( context ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
Offset . QuadPart = StartingOffset ;
// Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
Irp = IoAllocateIrp ( DeviceObject - > StackSize , FALSE ) ;
if ( ! Irp ) {
ERR ( " IoAllocateIrp failed \n " ) ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto exit ;
}
IrpSp = IoGetNextIrpStackLocation ( Irp ) ;
IrpSp - > MajorFunction = IRP_MJ_READ ;
if ( DeviceObject - > Flags & DO_BUFFERED_IO ) {
FIXME ( " FIXME - buffered IO \n " ) ;
} else if ( DeviceObject - > Flags & DO_DIRECT_IO ) {
// TRACE("direct IO\n");
Irp - > MdlAddress = IoAllocateMdl ( Buffer , Length , FALSE , FALSE , NULL ) ;
if ( ! Irp - > MdlAddress ) {
ERR ( " IoAllocateMdl failed \n " ) ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
// IoFreeIrp(Irp);
goto exit ;
// } else {
// TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
}
MmProbeAndLockPages ( Irp - > MdlAddress , KernelMode , IoWriteAccess ) ;
} else {
// TRACE("neither buffered nor direct IO\n");
Irp - > UserBuffer = Buffer ;
}
IrpSp - > Parameters . Read . Length = Length ;
IrpSp - > Parameters . Read . ByteOffset = Offset ;
Irp - > UserIosb = IoStatus ;
// Irp->Tail.Overlay.Thread = PsGetCurrentThread();
Irp - > UserEvent = & context - > Event ;
// IoQueueThreadIrp(Irp);
IoSetCompletionRoutine ( Irp , read_completion , context , TRUE , TRUE , TRUE ) ;
// if (Override)
// {
// Stack = IoGetNextIrpStackLocation(Irp);
// Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
// }
// TRACE("Calling IO Driver... with irp %p\n", Irp);
Status = IoCallDriver ( DeviceObject , Irp ) ;
// TRACE("Waiting for IO Operation for %p\n", Irp);
if ( Status = = STATUS_PENDING ) {
// TRACE("Operation pending\n");
KeWaitForSingleObject ( & context - > Event , Executive , KernelMode , FALSE , NULL ) ;
// TRACE("Getting IO Status... for %p\n", Irp);
Status = context - > iosb . Status ;
}
if ( DeviceObject - > Flags & DO_DIRECT_IO ) {
MmUnlockPages ( Irp - > MdlAddress ) ;
IoFreeMdl ( Irp - > MdlAddress ) ;
}
exit :
IoFreeIrp ( Irp ) ;
ExFreePool ( IoStatus ) ;
ExFreePool ( context ) ;
return Status ;
}
static NTSTATUS STDCALL read_superblock ( device_extension * Vcb , PDEVICE_OBJECT device ) {
NTSTATUS Status ;
superblock * sb ;
unsigned int i , to_read ;
UINT32 crc32 ;
to_read = sector_align ( sizeof ( superblock ) , device - > SectorSize ) ;
sb = ExAllocatePoolWithTag ( NonPagedPool , to_read , ALLOC_TAG ) ;
if ( ! sb ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
i = 0 ;
while ( superblock_addrs [ i ] > 0 ) {
if ( i > 0 & & superblock_addrs [ i ] + sizeof ( superblock ) > Vcb - > length )
break ;
Status = sync_read_phys ( device , superblock_addrs [ i ] , to_read , ( PUCHAR ) sb ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " Failed to read superblock %u: %08x \n " , i , Status ) ;
ExFreePool ( sb ) ;
return Status ;
}
TRACE ( " got superblock %u! \n " , i ) ;
if ( i = = 0 | | sb - > generation > Vcb - > superblock . generation )
RtlCopyMemory ( & Vcb - > superblock , sb , sizeof ( superblock ) ) ;
i + + ;
}
ExFreePool ( sb ) ;
crc32 = calc_crc32c ( 0xffffffff , ( UINT8 * ) & Vcb - > superblock . uuid , ( ULONG ) sizeof ( superblock ) - sizeof ( Vcb - > superblock . checksum ) ) ;
crc32 = ~ crc32 ;
TRACE ( " crc32 was %08x, expected %08x \n " , crc32 , * ( ( UINT32 * ) Vcb - > superblock . checksum ) ) ;
if ( crc32 ! = * ( ( UINT32 * ) Vcb - > superblock . checksum ) )
return STATUS_INTERNAL_ERROR ; // FIXME - correct error?
TRACE ( " label is %s \n " , Vcb - > superblock . label ) ;
// utf8_to_utf16(Vcb->superblock.label, Vcb->label, MAX_LABEL_SIZE * sizeof(WCHAR));
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL dev_ioctl ( PDEVICE_OBJECT DeviceObject , ULONG ControlCode , PVOID InputBuffer ,
ULONG InputBufferSize , PVOID OutputBuffer , ULONG OutputBufferSize , BOOLEAN Override )
{
PIRP Irp ;
KEVENT Event ;
NTSTATUS Status ;
PIO_STACK_LOCATION Stack ;
IO_STATUS_BLOCK IoStatus ;
KeInitializeEvent ( & Event , NotificationEvent , FALSE ) ;
Irp = IoBuildDeviceIoControlRequest ( ControlCode ,
DeviceObject ,
InputBuffer ,
InputBufferSize ,
OutputBuffer ,
OutputBufferSize ,
FALSE ,
& Event ,
& IoStatus ) ;
if ( ! Irp ) return STATUS_INSUFFICIENT_RESOURCES ;
if ( Override ) {
Stack = IoGetNextIrpStackLocation ( Irp ) ;
Stack - > Flags | = SL_OVERRIDE_VERIFY_VOLUME ;
}
Status = IoCallDriver ( DeviceObject , Irp ) ;
if ( Status = = STATUS_PENDING ) {
KeWaitForSingleObject ( & Event , Executive , KernelMode , FALSE , NULL ) ;
Status = IoStatus . Status ;
}
return Status ;
}
// static void STDCALL find_chunk_root(device_extension* Vcb) {
// UINT32 i;
// KEY* key;
//
// i = 0;
// while (i < Vcb->superblock.n) {
// key = &Vcb->superblock.sys_chunk_array[i];
// i += sizeof(KEY);
// }
//
// // FIXME
// }
// static void STDCALL insert_ltp(device_extension* Vcb, log_to_phys* ltp) {
// if (!Vcb->log_to_phys) {
// Vcb->log_to_phys = ltp;
// ltp->next = NULL;
// return;
// }
//
// // FIXME - these should be ordered
// ltp->next = Vcb->log_to_phys;
// Vcb->log_to_phys = ltp;
// }
static NTSTATUS STDCALL add_root ( device_extension * Vcb , UINT64 id , UINT64 addr , traverse_ptr * tp ) {
root * r = ExAllocatePoolWithTag ( PagedPool , sizeof ( root ) , ALLOC_TAG ) ;
if ( ! r ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
r - > id = id ;
r - > treeholder . address = addr ;
r - > treeholder . tree = NULL ;
init_tree_holder ( & r - > treeholder ) ;
r - > prev = NULL ;
r - > next = Vcb - > roots ;
r - > nonpaged = ExAllocatePoolWithTag ( NonPagedPool , sizeof ( root_nonpaged ) , ALLOC_TAG ) ;
if ( ! r - > nonpaged ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( r ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
ExInitializeResourceLite ( & r - > nonpaged - > load_tree_lock ) ;
r - > lastinode = 0 ;
if ( tp ) {
RtlCopyMemory ( & r - > root_item , tp - > item - > data , min ( sizeof ( ROOT_ITEM ) , tp - > item - > size ) ) ;
if ( tp - > item - > size < sizeof ( ROOT_ITEM ) )
RtlZeroMemory ( ( ( UINT8 * ) & r - > root_item ) + tp - > item - > size , sizeof ( ROOT_ITEM ) - tp - > item - > size ) ;
}
if ( Vcb - > roots )
Vcb - > roots - > prev = r ;
Vcb - > roots = r ;
switch ( r - > id ) {
case BTRFS_ROOT_ROOT :
Vcb - > root_root = r ;
break ;
case BTRFS_ROOT_EXTENT :
Vcb - > extent_root = r ;
break ;
case BTRFS_ROOT_CHUNK :
Vcb - > chunk_root = r ;
break ;
case BTRFS_ROOT_DEVTREE :
Vcb - > dev_root = r ;
break ;
case BTRFS_ROOT_CHECKSUM :
Vcb - > checksum_root = r ;
break ;
}
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL look_for_roots ( device_extension * Vcb ) {
traverse_ptr tp , next_tp ;
KEY searchkey ;
BOOL b ;
NTSTATUS Status ;
searchkey . obj_id = 0 ;
searchkey . obj_type = 0 ;
searchkey . offset = 0 ;
Status = find_item ( Vcb , Vcb - > root_root , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_tree returned %08x \n " , Status ) ;
return Status ;
}
do {
TRACE ( " (%llx,%x,%llx) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
if ( tp . item - > key . obj_type = = TYPE_ROOT_ITEM ) {
ROOT_ITEM * ri = ( ROOT_ITEM * ) tp . item - > data ;
if ( tp . item - > size < offsetof ( ROOT_ITEM , byte_limit ) ) {
ERR ( " (%llx,%x,%llx) 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 , offsetof ( ROOT_ITEM , byte_limit ) ) ;
} else {
TRACE ( " root %llx - address %llx \n " , tp . item - > key . obj_id , ri - > block_number ) ;
Status = add_root ( Vcb , tp . item - > key . obj_id , ri - > block_number , & tp ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " add_root returned %08x \n " , Status ) ;
return Status ;
}
}
}
b = find_next_item ( Vcb , & tp , & next_tp , FALSE ) ;
if ( b ) {
free_traverse_ptr ( & tp ) ;
tp = next_tp ;
}
} while ( b ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_SUCCESS ;
}
static NTSTATUS add_disk_hole ( LIST_ENTRY * disk_holes , UINT64 address , UINT64 size ) {
disk_hole * dh = ExAllocatePoolWithTag ( PagedPool , sizeof ( disk_hole ) , ALLOC_TAG ) ;
if ( ! dh ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
dh - > address = address ;
dh - > size = size ;
dh - > provisional = FALSE ;
InsertTailList ( disk_holes , & dh - > listentry ) ;
return STATUS_SUCCESS ;
}
static NTSTATUS find_disk_holes ( device_extension * Vcb , device * dev ) {
KEY searchkey ;
traverse_ptr tp , next_tp ;
BOOL b ;
UINT64 lastaddr ;
NTSTATUS Status ;
InitializeListHead ( & dev - > disk_holes ) ;
searchkey . obj_id = dev - > devitem . dev_id ;
searchkey . obj_type = TYPE_DEV_EXTENT ;
searchkey . offset = 0 ;
Status = find_item ( Vcb , Vcb - > dev_root , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_tree returned %08x \n " , Status ) ;
return Status ;
}
lastaddr = 0 ;
do {
if ( tp . item - > key . obj_id = = dev - > devitem . dev_id & & tp . item - > key . obj_type = = TYPE_DEV_EXTENT ) {
if ( tp . item - > size > = sizeof ( DEV_EXTENT ) ) {
DEV_EXTENT * de = ( DEV_EXTENT * ) tp . item - > data ;
if ( tp . item - > key . offset > lastaddr ) {
Status = add_disk_hole ( & dev - > disk_holes , lastaddr , tp . item - > key . offset - lastaddr ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " add_disk_hole returned %08x \n " , Status ) ;
return Status ;
}
}
lastaddr = tp . item - > key . offset + de - > length ;
} else {
ERR ( " (%llx,%x,%llx) was %u bytes, expected %u \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , tp . item - > size , sizeof ( DEV_EXTENT ) ) ;
}
}
b = find_next_item ( Vcb , & tp , & next_tp , FALSE ) ;
if ( b ) {
free_traverse_ptr ( & tp ) ;
tp = next_tp ;
if ( tp . item - > key . obj_id > searchkey . obj_id | | tp . item - > key . obj_type > searchkey . obj_type )
break ;
}
} while ( b ) ;
free_traverse_ptr ( & tp ) ;
if ( lastaddr < dev - > devitem . num_bytes ) {
Status = add_disk_hole ( & dev - > disk_holes , lastaddr , dev - > devitem . num_bytes - lastaddr ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " add_disk_hole returned %08x \n " , Status ) ;
return Status ;
}
}
// FIXME - free disk_holes when unmounting
return STATUS_SUCCESS ;
}
device * find_device_from_uuid ( device_extension * Vcb , BTRFS_UUID * uuid ) {
UINT64 i ;
for ( i = 0 ; i < Vcb - > superblock . num_devices ; i + + ) {
TRACE ( " device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x \n " , i ,
Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 0 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 1 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 2 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 3 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 4 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 5 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 6 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 7 ] ,
Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 8 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 9 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 10 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 11 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 12 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 13 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 14 ] , Vcb - > devices [ i ] . devitem . device_uuid . uuid [ 15 ] ) ;
if ( Vcb - > devices [ i ] . devobj & & RtlCompareMemory ( & Vcb - > devices [ i ] . devitem . device_uuid , uuid , sizeof ( BTRFS_UUID ) ) = = sizeof ( BTRFS_UUID ) ) {
TRACE ( " returning device %llx \n " , i ) ;
return & Vcb - > devices [ i ] ;
}
}
WARN ( " could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x \n " ,
uuid - > uuid [ 0 ] , uuid - > uuid [ 1 ] , uuid - > uuid [ 2 ] , uuid - > uuid [ 3 ] , uuid - > uuid [ 4 ] , uuid - > uuid [ 5 ] , uuid - > uuid [ 6 ] , uuid - > uuid [ 7 ] ,
uuid - > uuid [ 8 ] , uuid - > uuid [ 9 ] , uuid - > uuid [ 10 ] , uuid - > uuid [ 11 ] , uuid - > uuid [ 12 ] , uuid - > uuid [ 13 ] , uuid - > uuid [ 14 ] , uuid - > uuid [ 15 ] ) ;
return NULL ;
}
static NTSTATUS STDCALL load_chunk_root ( device_extension * Vcb ) {
traverse_ptr tp , next_tp ;
KEY searchkey ;
BOOL b ;
chunk * c ;
UINT64 i ;
NTSTATUS Status ;
searchkey . obj_id = 0 ;
searchkey . obj_type = 0 ;
searchkey . offset = 0 ;
Status = find_item ( Vcb , Vcb - > chunk_root , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
do {
TRACE ( " (%llx,%x,%llx) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
if ( tp . item - > key . obj_id = = 1 & & tp . item - > key . obj_type = = TYPE_DEV_ITEM & & tp . item - > key . offset = = 1 ) {
// FIXME - this is a hack; make this work with multiple devices!
if ( tp . item - > size > 0 )
RtlCopyMemory ( & Vcb - > devices [ 0 ] . devitem , tp . item - > data , min ( tp . item - > size , sizeof ( DEV_ITEM ) ) ) ;
} else if ( tp . item - > key . obj_type = = TYPE_CHUNK_ITEM ) {
if ( tp . item - > size < sizeof ( CHUNK_ITEM ) ) {
ERR ( " (%llx,%x,%llx) 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 ( CHUNK_ITEM ) ) ;
} else {
c = ExAllocatePoolWithTag ( PagedPool , sizeof ( chunk ) , ALLOC_TAG ) ;
if ( ! c ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
c - > size = tp . item - > size ;
c - > offset = tp . item - > key . offset ;
c - > used = c - > oldused = 0 ;
c - > space_changed = FALSE ;
c - > chunk_item = ExAllocatePoolWithTag ( PagedPool , tp . item - > size , ALLOC_TAG ) ;
if ( ! c - > chunk_item ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( c - > chunk_item , tp . item - > data , tp . item - > size ) ;
if ( c - > chunk_item - > num_stripes > 0 ) {
CHUNK_ITEM_STRIPE * cis = ( CHUNK_ITEM_STRIPE * ) & c - > chunk_item [ 1 ] ;
c - > devices = ExAllocatePoolWithTag ( PagedPool , sizeof ( device * ) * c - > chunk_item - > num_stripes , ALLOC_TAG ) ;
if ( ! c - > devices ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
for ( i = 0 ; i < c - > chunk_item - > num_stripes ; i + + ) {
c - > devices [ i ] = find_device_from_uuid ( Vcb , & cis [ i ] . dev_uuid ) ;
TRACE ( " device %llu = %p \n " , i , c - > devices [ i ] ) ;
}
} else
c - > devices = NULL ;
InitializeListHead ( & c - > space ) ;
InsertTailList ( & Vcb - > chunks , & c - > list_entry ) ;
}
}
b = find_next_item ( Vcb , & tp , & next_tp , FALSE ) ;
if ( b ) {
free_traverse_ptr ( & tp ) ;
tp = next_tp ;
}
} while ( b ) ;
free_traverse_ptr ( & tp ) ;
Vcb - > log_to_phys_loaded = TRUE ;
return STATUS_SUCCESS ;
}
static BOOL load_stored_free_space_cache ( device_extension * Vcb , chunk * c ) {
KEY searchkey ;
traverse_ptr tp , tp2 ;
FREE_SPACE_ITEM * fsi ;
UINT64 inode , num_sectors , i , generation ;
INODE_ITEM * ii ;
UINT8 * data ;
NTSTATUS Status ;
UINT32 * checksums , crc32 ;
# ifdef _DEBUG
FREE_SPACE_ENTRY * fse ;
UINT64 num_entries ;
# endif
TRACE ( " (%p, %llx) \n " , Vcb , c - > offset ) ;
if ( Vcb - > superblock . generation ! = Vcb - > superblock . cache_generation )
return FALSE ;
searchkey . obj_id = FREE_SPACE_CACHE_ID ;
searchkey . obj_type = 0 ;
searchkey . offset = c - > offset ;
Status = find_item ( Vcb , Vcb - > root_root , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return FALSE ;
}
if ( keycmp ( & tp . item - > key , & searchkey ) ) {
WARN ( " (%llx,%x,%llx) not found \n " , searchkey . obj_id , searchkey . obj_type , searchkey . offset ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
if ( tp . item - > size < sizeof ( FREE_SPACE_ITEM ) ) {
WARN ( " (%llx,%x,%llx) was %u bytes, expected %u \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , tp . item - > size , sizeof ( FREE_SPACE_ITEM ) ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
fsi = ( FREE_SPACE_ITEM * ) tp . item - > data ;
if ( fsi - > generation ! = Vcb - > superblock . cache_generation ) {
WARN ( " cache had generation %llx, expecting %llx \n " , fsi - > generation , Vcb - > superblock . cache_generation ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
if ( fsi - > key . obj_type ! = TYPE_INODE_ITEM ) {
WARN ( " cache pointed to something other than an INODE_ITEM \n " ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
if ( fsi - > num_bitmaps > 0 ) {
WARN ( " cache had bitmaps, unsure of how to deal with these \n " ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
inode = fsi - > key . obj_id ;
searchkey = fsi - > key ;
# ifdef _DEBUG
num_entries = fsi - > num_entries ;
# endif
Status = find_item ( Vcb , Vcb - > root_root , & tp2 , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
free_traverse_ptr ( & tp ) ;
return FALSE ;
}
free_traverse_ptr ( & tp ) ;
if ( keycmp ( & tp2 . item - > key , & searchkey ) ) {
WARN ( " (%llx,%x,%llx) not found \n " , searchkey . obj_id , searchkey . obj_type , searchkey . offset ) ;
free_traverse_ptr ( & tp2 ) ;
return FALSE ;
}
if ( tp2 . item - > size < sizeof ( INODE_ITEM ) ) {
WARN ( " (%llx,%x,%llx) was %u bytes, expected %u \n " , tp2 . item - > key . obj_id , tp2 . item - > key . obj_type , tp2 . item - > key . offset , tp2 . item - > size , sizeof ( INODE_ITEM ) ) ;
free_traverse_ptr ( & tp2 ) ;
return FALSE ;
}
ii = ( INODE_ITEM * ) tp2 . item - > data ;
data = ExAllocatePoolWithTag ( PagedPool , ii - > st_size , ALLOC_TAG ) ;
if ( ! data ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp2 ) ;
return FALSE ;
}
Status = read_file ( Vcb , Vcb - > root_root , inode , data , 0 , ii - > st_size , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " read_file returned %08x \n " , Status ) ;
ExFreePool ( data ) ;
free_traverse_ptr ( & tp2 ) ;
return FALSE ;
}
num_sectors = ii - > st_size / Vcb - > superblock . sector_size ;
generation = * ( data + ( num_sectors * sizeof ( UINT32 ) ) ) ;
if ( generation ! = Vcb - > superblock . cache_generation ) {
ERR ( " generation was %llx, expected %llx \n " , generation , Vcb - > superblock . cache_generation ) ;
ExFreePool ( data ) ;
free_traverse_ptr ( & tp2 ) ;
return FALSE ;
}
checksums = ExAllocatePoolWithTag ( PagedPool , sizeof ( UINT32 ) * num_sectors , ALLOC_TAG ) ; // FIXME - get rid of this
if ( ! checksums ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( data ) ;
free_traverse_ptr ( & tp2 ) ;
return FALSE ;
}
RtlCopyMemory ( checksums , data , sizeof ( UINT32 ) * num_sectors ) ;
for ( i = 0 ; i < num_sectors ; i + + ) {
if ( i * Vcb - > superblock . sector_size > sizeof ( UINT32 ) * num_sectors )
crc32 = ~ calc_crc32c ( 0xffffffff , & data [ i * Vcb - > superblock . sector_size ] , Vcb - > superblock . sector_size ) ;
else if ( ( i + 1 ) * Vcb - > superblock . sector_size < sizeof ( UINT32 ) * num_sectors )
crc32 = 0 ; // FIXME - test this
else
crc32 = ~ calc_crc32c ( 0xffffffff , & data [ sizeof ( UINT32 ) * num_sectors ] , ( ( i + 1 ) * Vcb - > superblock . sector_size ) - ( sizeof ( UINT32 ) * num_sectors ) ) ;
if ( crc32 ! = checksums [ i ] ) {
WARN ( " checksum %llu was %08x, expected %08x \n " , i , crc32 , checksums [ i ] ) ;
ExFreePool ( checksums ) ;
ExFreePool ( data ) ;
free_traverse_ptr ( & tp2 ) ;
return FALSE ;
}
}
ExFreePool ( checksums ) ;
# ifdef _DEBUG
fse = ( FREE_SPACE_ENTRY * ) & data [ ( sizeof ( UINT32 ) * num_sectors ) + sizeof ( UINT64 ) ] ;
for ( i = 0 ; i < num_entries ; i + + ) {
TRACE ( " (%llx,%llx,%x) \n " , fse [ i ] . offset , fse [ i ] . size , fse [ i ] . type ) ;
}
# endif
FIXME ( " FIXME - read cache \n " ) ;
ExFreePool ( data ) ;
free_traverse_ptr ( & tp2 ) ;
return FALSE ;
}
static NTSTATUS load_free_space_cache ( device_extension * Vcb , chunk * c ) {
traverse_ptr tp , next_tp ;
KEY searchkey ;
UINT64 lastaddr ;
BOOL b ;
space * s , * s2 ;
LIST_ENTRY * le ;
NTSTATUS Status ;
load_stored_free_space_cache ( Vcb , c ) ;
TRACE ( " generating free space cache for chunk %llx \n " , c - > offset ) ;
searchkey . obj_id = c - > offset ;
searchkey . obj_type = TYPE_EXTENT_ITEM ;
searchkey . offset = 0 ;
Status = find_item ( Vcb , Vcb - > extent_root , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
lastaddr = c - > offset ;
do {
if ( tp . item - > key . obj_id > = c - > offset + c - > chunk_item - > size )
break ;
if ( tp . item - > key . obj_id > = c - > offset & & ( tp . item - > key . obj_type = = TYPE_EXTENT_ITEM | | tp . item - > key . obj_type = = TYPE_METADATA_ITEM ) ) {
if ( tp . item - > key . obj_id > lastaddr ) {
s = ExAllocatePoolWithTag ( PagedPool , sizeof ( space ) , ALLOC_TAG ) ;
if ( ! s ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
s - > offset = lastaddr ;
s - > size = tp . item - > key . obj_id - lastaddr ;
s - > type = SPACE_TYPE_FREE ;
InsertTailList ( & c - > space , & s - > list_entry ) ;
TRACE ( " (%llx,%llx) \n " , s - > offset , s - > size ) ;
}
if ( tp . item - > key . obj_type = = TYPE_METADATA_ITEM )
lastaddr = tp . item - > key . obj_id + Vcb - > superblock . node_size ;
else
lastaddr = tp . item - > key . obj_id + tp . item - > key . offset ;
}
b = find_next_item ( Vcb , & tp , & next_tp , FALSE ) ;
if ( b ) {
free_traverse_ptr ( & tp ) ;
tp = next_tp ;
}
} while ( b ) ;
if ( lastaddr < c - > offset + c - > chunk_item - > size ) {
s = ExAllocatePoolWithTag ( PagedPool , sizeof ( space ) , ALLOC_TAG ) ;
if ( ! s ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
s - > offset = lastaddr ;
s - > size = c - > offset + c - > chunk_item - > size - lastaddr ;
s - > type = SPACE_TYPE_FREE ;
InsertTailList ( & c - > space , & s - > list_entry ) ;
TRACE ( " (%llx,%llx) \n " , s - > offset , s - > size ) ;
}
free_traverse_ptr ( & tp ) ;
// add allocated space
lastaddr = c - > offset ;
le = c - > space . Flink ;
while ( le ! = & c - > space ) {
s = CONTAINING_RECORD ( le , space , list_entry ) ;
if ( s - > offset > lastaddr ) {
s2 = ExAllocatePoolWithTag ( PagedPool , sizeof ( space ) , ALLOC_TAG ) ;
if ( ! s2 ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
s2 - > offset = lastaddr ;
s2 - > size = s - > offset - lastaddr ;
s2 - > type = SPACE_TYPE_USED ;
InsertTailList ( & s - > list_entry , & s2 - > list_entry ) ;
}
lastaddr = s - > offset + s - > size ;
le = le - > Flink ;
}
if ( lastaddr < c - > offset + c - > chunk_item - > size ) {
s = ExAllocatePoolWithTag ( PagedPool , sizeof ( space ) , ALLOC_TAG ) ;
if ( ! s ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
s - > offset = lastaddr ;
s - > size = c - > offset + c - > chunk_item - > size - lastaddr ;
s - > type = SPACE_TYPE_USED ;
InsertTailList ( & c - > space , & s - > list_entry ) ;
}
le = c - > space . Flink ;
while ( le ! = & c - > space ) {
s = CONTAINING_RECORD ( le , space , list_entry ) ;
TRACE ( " %llx,%llx,%u \n " , s - > offset , s - > size , s - > type ) ;
le = le - > Flink ;
}
return STATUS_SUCCESS ;
}
void protect_superblocks ( device_extension * Vcb , chunk * c ) {
int i = 0 , j ;
UINT64 addr ;
// FIXME - this will need modifying for RAID
while ( superblock_addrs [ i ] ! = 0 ) {
CHUNK_ITEM * ci = c - > chunk_item ;
CHUNK_ITEM_STRIPE * cis = ( CHUNK_ITEM_STRIPE * ) & ci [ 1 ] ;
for ( j = 0 ; j < ci - > num_stripes ; j + + ) {
if ( cis [ j ] . offset + ci - > size > superblock_addrs [ i ] & & cis [ j ] . offset < = superblock_addrs [ i ] + sizeof ( superblock ) ) {
2016-03-26 11:53:07 +00:00
UINT32 size ;
2016-03-23 20:35:05 +00:00
TRACE ( " cut out superblock in chunk %llx \n " , c - > offset ) ;
addr = ( superblock_addrs [ i ] - cis [ j ] . offset ) + c - > offset ;
TRACE ( " addr %llx \n " , addr ) ;
2016-03-26 11:53:07 +00:00
// This prevents trees from spanning a stripe boundary, which btrfs check complains
// about. It also prevents the chunk tree being placed at 0x11000, which for some
// reason makes the FS unmountable on Linux (it tries to read 0x10000, i.e. the
// superblock, instead).
if ( ci - > type & BLOCK_FLAG_SYSTEM | | ci - > type & BLOCK_FLAG_METADATA )
size = max ( sizeof ( superblock ) , Vcb - > superblock . node_size ) ;
else
size = sizeof ( superblock ) ;
add_to_space_list ( c , addr , size , SPACE_TYPE_USED ) ;
2016-03-23 20:35:05 +00:00
}
}
i + + ;
}
}
static NTSTATUS STDCALL find_chunk_usage ( device_extension * Vcb ) {
LIST_ENTRY * le = Vcb - > chunks . Flink ;
chunk * c ;
KEY searchkey ;
traverse_ptr tp ;
BLOCK_GROUP_ITEM * bgi ;
NTSTATUS Status ;
// c00000,c0,800000
// block_group_item size=7f0000 chunktreeid=100 flags=1
searchkey . obj_type = TYPE_BLOCK_GROUP_ITEM ;
while ( le ! = & Vcb - > chunks ) {
c = CONTAINING_RECORD ( le , chunk , list_entry ) ;
searchkey . obj_id = c - > offset ;
searchkey . offset = c - > chunk_item - > size ;
Status = find_item ( Vcb , Vcb - > extent_root , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & searchkey , & tp . item - > key ) ) {
if ( tp . item - > size > = sizeof ( BLOCK_GROUP_ITEM ) ) {
bgi = ( BLOCK_GROUP_ITEM * ) tp . item - > data ;
c - > used = c - > oldused = bgi - > used ;
TRACE ( " chunk %llx has %llx bytes used \n " , c - > offset , c - > used ) ;
} else {
ERR ( " (%llx;%llx,%x,%llx) is %u bytes, expected %u \n " ,
Vcb - > extent_root - > id , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , tp . item - > size , sizeof ( BLOCK_GROUP_ITEM ) ) ;
}
}
free_traverse_ptr ( & tp ) ;
// if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
// cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
//
// return (addr - c->offset) + cis->offset;
// }
// FIXME - make sure we free occasionally after doing one of these, or we
// might use up a lot of memory with a big disk.
Status = load_free_space_cache ( Vcb , c ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " load_free_space_cache returned %08x \n " , Status ) ;
return Status ;
}
protect_superblocks ( Vcb , c ) ;
le = le - > Flink ;
}
return STATUS_SUCCESS ;
}
// static void STDCALL root_test(device_extension* Vcb) {
// root* r;
// KEY searchkey;
// traverse_ptr tp, next_tp;
// BOOL b;
//
// r = Vcb->roots;
// while (r) {
// if (r->id == 0x102)
// break;
// r = r->next;
// }
//
// if (!r) {
// ERR("Could not find root tree.\n");
// return;
// }
//
// searchkey.obj_id = 0x1b6;
// searchkey.obj_type = 0xb;
// searchkey.offset = 0;
//
// if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
// ERR("Could not find first item.\n");
// return;
// }
//
// b = TRUE;
// do {
// TRACE("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
//
// b = find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE);
//
// if (b) {
// free_traverse_ptr(&tp);
// tp = next_tp;
// }
// } while (b);
//
// free_traverse_ptr(&tp);
// }
static NTSTATUS load_sys_chunks ( device_extension * Vcb ) {
KEY key ;
ULONG n = Vcb - > superblock . n ;
while ( n > 0 ) {
if ( n > sizeof ( KEY ) ) {
RtlCopyMemory ( & key , & Vcb - > superblock . sys_chunk_array [ Vcb - > superblock . n - n ] , sizeof ( KEY ) ) ;
n - = sizeof ( KEY ) ;
} else
return STATUS_SUCCESS ;
TRACE ( " bootstrap: %llx,%x,%llx \n " , key . obj_id , key . obj_type , key . offset ) ;
if ( key . obj_type = = TYPE_CHUNK_ITEM ) {
CHUNK_ITEM * ci ;
ULONG cisize ;
sys_chunk * sc ;
if ( n < sizeof ( CHUNK_ITEM ) )
return STATUS_SUCCESS ;
ci = ( CHUNK_ITEM * ) & Vcb - > superblock . sys_chunk_array [ Vcb - > superblock . n - n ] ;
cisize = sizeof ( CHUNK_ITEM ) + ( ci - > num_stripes * sizeof ( CHUNK_ITEM_STRIPE ) ) ;
if ( n < cisize )
return STATUS_SUCCESS ;
sc = ExAllocatePoolWithTag ( PagedPool , sizeof ( sys_chunk ) , ALLOC_TAG ) ;
if ( ! sc ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
sc - > key = key ;
sc - > size = cisize ;
sc - > data = ExAllocatePoolWithTag ( PagedPool , sc - > size , ALLOC_TAG ) ;
if ( ! sc - > data ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( sc - > data , ci , sc - > size ) ;
InsertTailList ( & Vcb - > sys_chunks , & sc - > list_entry ) ;
n - = cisize ;
} else {
ERR ( " unexpected item %llx,%x,%llx in bootstrap \n " , key . obj_id , key . obj_type , key . offset ) ;
return STATUS_INTERNAL_ERROR ;
}
}
return STATUS_SUCCESS ;
}
static root * find_default_subvol ( device_extension * Vcb ) {
root * subvol ;
UINT64 inode ;
UINT8 type ;
UNICODE_STRING filename ;
static WCHAR fn [ ] = L " default " ;
static UINT32 crc32 = 0x8dbfc2d2 ;
if ( Vcb - > superblock . incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL ) {
filename . Buffer = fn ;
filename . Length = filename . MaximumLength = ( USHORT ) wcslen ( fn ) * sizeof ( WCHAR ) ;
if ( ! find_file_in_dir_with_crc32 ( Vcb , & filename , crc32 , Vcb - > root_root , Vcb - > superblock . root_dir_objectid , & subvol , & inode , & type , NULL ) )
WARN ( " couldn't find default subvol DIR_ITEM, using default tree \n " ) ;
else
return subvol ;
}
subvol = Vcb - > roots ;
while ( subvol & & subvol - > id ! = BTRFS_ROOT_FSTREE )
subvol = subvol - > next ;
return subvol ;
}
static NTSTATUS STDCALL mount_vol ( PDEVICE_OBJECT DeviceObject , PIRP Irp ) {
PIO_STACK_LOCATION Stack ;
PDEVICE_OBJECT NewDeviceObject = NULL ;
PDEVICE_OBJECT DeviceToMount ;
NTSTATUS Status ;
device_extension * Vcb = NULL ;
PARTITION_INFORMATION_EX piex ;
UINT64 i ;
LIST_ENTRY * le ;
KEY searchkey ;
traverse_ptr tp ;
TRACE ( " mount_vol called \n " ) ;
if ( DeviceObject ! = devobj )
{
Status = STATUS_INVALID_DEVICE_REQUEST ;
goto exit ;
}
Stack = IoGetCurrentIrpStackLocation ( Irp ) ;
DeviceToMount = Stack - > Parameters . MountVolume . DeviceObject ;
// Status = NtfsHasFileSystem(DeviceToMount);
// if (!NT_SUCCESS(Status))
// {
// goto ByeBye;
// }
Status = dev_ioctl ( DeviceToMount , IOCTL_DISK_GET_PARTITION_INFO_EX , NULL , 0 ,
& piex , sizeof ( piex ) , TRUE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error reading partition information: %08x \n " , Status ) ;
goto exit ;
}
Status = IoCreateDevice ( drvobj ,
sizeof ( device_extension ) ,
NULL ,
FILE_DEVICE_DISK_FILE_SYSTEM ,
0 ,
FALSE ,
& NewDeviceObject ) ;
if ( ! NT_SUCCESS ( Status ) )
goto exit ;
// TRACE("DEV_ITEM = %x, superblock = %x\n", sizeof(DEV_ITEM), sizeof(superblock));
NewDeviceObject - > Flags | = DO_DIRECT_IO ;
Vcb = ( PVOID ) NewDeviceObject - > DeviceExtension ;
RtlZeroMemory ( Vcb , sizeof ( device_extension ) ) ;
InitializeListHead ( & Vcb - > tree_cache ) ;
ExInitializeResourceLite ( & Vcb - > tree_lock ) ;
Vcb - > tree_lock_counter = 0 ;
Vcb - > open_trees = 0 ;
Vcb - > write_trees = 0 ;
ExInitializeResourceLite ( & Vcb - > fcb_lock ) ;
ExInitializeResourceLite ( & Vcb - > DirResource ) ;
ExAcquireResourceExclusiveLite ( & global_loading_lock , TRUE ) ;
InsertTailList ( & VcbList , & Vcb - > list_entry ) ;
ExReleaseResourceLite ( & global_loading_lock ) ;
ExInitializeResourceLite ( & Vcb - > load_lock ) ;
ExAcquireResourceExclusiveLite ( & Vcb - > load_lock , TRUE ) ;
// Vcb->Identifier.Type = NTFS_TYPE_VCB;
// Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
//
// Status = NtfsGetVolumeData(DeviceToMount,
// Vcb);
// if (!NT_SUCCESS(Status))
// goto ByeBye;
// Vcb->device = DeviceToMount;
DeviceToMount - > Flags | = DO_DIRECT_IO ;
// Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
// &Vcb->geometry, sizeof(DISK_GEOMETRY), TRUE);
// if (!NT_SUCCESS(Status)) {
// ERR("error reading disk geometry: %08x\n", Status);
// goto exit;
// } else {
// TRACE("media type = %u, cylinders = %u, tracks per cylinder = %u, sectors per track = %u, bytes per sector = %u\n",
// Vcb->geometry.MediaType, Vcb->geometry.Cylinders, Vcb->geometry.TracksPerCylinder,
// Vcb->geometry.SectorsPerTrack, Vcb->geometry.BytesPerSector);
// }
Vcb - > length = piex . PartitionLength . QuadPart ;
TRACE ( " partition length = %u \n " , piex . PartitionLength ) ;
Status = read_superblock ( Vcb , DeviceToMount ) ;
if ( ! NT_SUCCESS ( Status ) ) {
Status = STATUS_UNRECOGNIZED_VOLUME ;
goto exit ;
}
if ( Vcb - > superblock . magic ! = BTRFS_MAGIC ) {
ERR ( " not a BTRFS volume \n " ) ;
Status = STATUS_UNRECOGNIZED_VOLUME ;
goto exit ;
} else {
TRACE ( " btrfs magic found \n " ) ;
}
if ( Vcb - > superblock . incompat_flags & ~ INCOMPAT_SUPPORTED ) {
WARN ( " cannot mount because of unsupported incompat flags (%llx) \n " , Vcb - > superblock . incompat_flags & ~ INCOMPAT_SUPPORTED ) ;
Status = STATUS_UNRECOGNIZED_VOLUME ;
goto exit ;
}
le = volumes . Flink ;
while ( le ! = & volumes ) {
volume * v = CONTAINING_RECORD ( le , volume , list_entry ) ;
if ( RtlCompareMemory ( & Vcb - > superblock . uuid , & v - > fsuuid , sizeof ( BTRFS_UUID ) ) = = sizeof ( BTRFS_UUID ) & & v - > devnum < Vcb - > superblock . dev_item . dev_id ) {
// skipping over device in RAID which isn't the first one
// FIXME - hide this in My Computer
Status = STATUS_UNRECOGNIZED_VOLUME ;
goto exit ;
}
le = le - > Flink ;
}
// FIXME - remove this when we can
if ( Vcb - > superblock . num_devices > 1 ) {
WARN ( " not mounting - multiple devices not yet implemented \n " ) ;
Status = STATUS_UNRECOGNIZED_VOLUME ;
goto exit ;
}
Vcb - > readonly = FALSE ;
if ( Vcb - > superblock . compat_ro_flags & ~ COMPAT_RO_SUPPORTED ) {
WARN ( " mounting read-only because of unsupported flags (%llx) \n " , Vcb - > superblock . compat_ro_flags & ~ COMPAT_RO_SUPPORTED ) ;
Vcb - > readonly = TRUE ;
}
Vcb - > superblock . generation + + ;
Vcb - > superblock . incompat_flags | = BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF ;
Vcb - > devices = ExAllocatePoolWithTag ( PagedPool , sizeof ( device ) * Vcb - > superblock . num_devices , ALLOC_TAG ) ;
if ( ! Vcb - > devices ) {
ERR ( " out of memory \n " ) ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto exit ;
}
Vcb - > devices [ 0 ] . devobj = DeviceToMount ;
RtlCopyMemory ( & Vcb - > devices [ 0 ] . devitem , & Vcb - > superblock . dev_item , sizeof ( DEV_ITEM ) ) ;
if ( Vcb - > superblock . num_devices > 1 )
RtlZeroMemory ( & Vcb - > devices [ 1 ] , sizeof ( DEV_ITEM ) * ( Vcb - > superblock . num_devices - 1 ) ) ;
// FIXME - find other devices, if there are any
TRACE ( " DeviceToMount = %p \n " , DeviceToMount ) ;
TRACE ( " Stack->Parameters.MountVolume.Vpb = %p \n " , Stack - > Parameters . MountVolume . Vpb ) ;
NewDeviceObject - > StackSize = DeviceToMount - > StackSize + 1 ;
NewDeviceObject - > Flags & = ~ DO_DEVICE_INITIALIZING ;
// find_chunk_root(Vcb);
// Vcb->chunk_root_phys_addr = Vcb->superblock.chunk_tree_addr; // FIXME - map from logical to physical (bootstrapped)
// Vcb->root_tree_phys_addr = logical_to_physical(Vcb, Vcb->superblock.root_tree_addr);
Vcb - > roots = NULL ;
Vcb - > log_to_phys_loaded = FALSE ;
Vcb - > max_inline = Vcb - > superblock . node_size / 2 ;
// Vcb->write_trees = NULL;
add_root ( Vcb , BTRFS_ROOT_CHUNK , Vcb - > superblock . chunk_tree_addr , NULL ) ;
if ( ! Vcb - > chunk_root ) {
ERR ( " Could not load chunk root. \n " ) ;
Status = STATUS_INTERNAL_ERROR ;
goto exit ;
}
InitializeListHead ( & Vcb - > sys_chunks ) ;
Status = load_sys_chunks ( Vcb ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " load_sys_chunks returned %08x \n " , Status ) ;
goto exit ;
}
InitializeListHead ( & Vcb - > chunks ) ;
InitializeListHead ( & Vcb - > trees ) ;
InitializeListHead ( & Vcb - > DirNotifyList ) ;
FsRtlNotifyInitializeSync ( & Vcb - > NotifySync ) ;
Status = load_chunk_root ( Vcb ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " load_chunk_root returned %08x \n " , Status ) ;
goto exit ;
}
add_root ( Vcb , BTRFS_ROOT_ROOT , Vcb - > superblock . root_tree_addr , NULL ) ;
if ( ! Vcb - > root_root ) {
ERR ( " Could not load root of roots. \n " ) ;
Status = STATUS_INTERNAL_ERROR ;
goto exit ;
}
Status = look_for_roots ( Vcb ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " look_for_roots returned %08x \n " , Status ) ;
goto exit ;
}
Status = find_chunk_usage ( Vcb ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_chunk_usage returned %08x \n " , Status ) ;
goto exit ;
}
Vcb - > volume_fcb = create_fcb ( ) ;
if ( ! Vcb - > volume_fcb ) {
ERR ( " out of memory \n " ) ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto exit ;
}
Vcb - > volume_fcb - > Vcb = Vcb ;
Vcb - > volume_fcb - > sd = NULL ;
Vcb - > root_fcb = create_fcb ( ) ;
if ( ! Vcb - > root_fcb ) {
ERR ( " out of memory \n " ) ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto exit ;
}
Vcb - > root_fcb - > Vcb = Vcb ;
Vcb - > root_fcb - > inode = SUBVOL_ROOT_INODE ;
Vcb - > root_fcb - > type = BTRFS_TYPE_DIRECTORY ;
Vcb - > root_fcb - > full_filename . Buffer = ExAllocatePoolWithTag ( PagedPool , sizeof ( WCHAR ) , ALLOC_TAG ) ;
if ( ! Vcb - > root_fcb - > full_filename . Buffer ) {
ERR ( " out of memory \n " ) ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto exit ;
}
Vcb - > root_fcb - > full_filename . Buffer [ 0 ] = ' \\ ' ;
Vcb - > root_fcb - > full_filename . Length = Vcb - > root_fcb - > full_filename . MaximumLength = sizeof ( WCHAR ) ;
# ifdef DEBUG_FCB_REFCOUNTS
WARN ( " volume FCB = %p \n " , Vcb - > volume_fcb ) ;
WARN ( " root FCB = %p \n " , Vcb - > root_fcb ) ;
# endif
Vcb - > root_fcb - > subvol = find_default_subvol ( Vcb ) ;
if ( ! Vcb - > root_fcb - > subvol ) {
ERR ( " could not find top subvol \n " ) ;
Status = STATUS_INTERNAL_ERROR ;
goto exit ;
}
Vcb - > fcbs = Vcb - > root_fcb ;
searchkey . obj_id = Vcb - > root_fcb - > inode ;
searchkey . obj_type = TYPE_INODE_ITEM ;
searchkey . offset = 0xffffffffffffffff ;
Status = find_item ( Vcb , Vcb - > root_fcb - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
goto exit ;
}
if ( tp . item - > key . obj_id ! = searchkey . obj_id | | tp . item - > key . obj_type ! = searchkey . obj_type ) {
ERR ( " couldn't find INODE_ITEM for root directory \n " ) ;
Status = STATUS_INTERNAL_ERROR ;
free_traverse_ptr ( & tp ) ;
goto exit ;
}
if ( tp . item - > size > 0 )
RtlCopyMemory ( & Vcb - > root_fcb - > inode_item , tp . item - > data , min ( sizeof ( INODE_ITEM ) , tp . item - > size ) ) ;
free_traverse_ptr ( & tp ) ;
fcb_get_sd ( Vcb - > root_fcb ) ;
Vcb - > root_fcb - > atts = get_file_attributes ( Vcb , & Vcb - > root_fcb - > inode_item , Vcb - > root_fcb - > subvol , Vcb - > root_fcb - > inode , Vcb - > root_fcb - > type ,
FALSE , FALSE ) ;
for ( i = 0 ; i < Vcb - > superblock . num_devices ; i + + ) {
Status = find_disk_holes ( Vcb , & Vcb - > devices [ i ] ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " find_disk_holes returned %08x \n " , Status ) ;
goto exit ;
}
}
// root_test(Vcb);
// Vcb->StreamFileObject = IoCreateStreamFileObject(NULL,
// Vcb->StorageDevice);
//
// InitializeListHead(&Vcb->FcbListHead);
//
// Fcb = NtfsCreateFCB(NULL, Vcb);
// if (Fcb == NULL)
// {
// Status = STATUS_INSUFFICIENT_RESOURCES;
// goto ByeBye;
// }
//
// Ccb = ExAllocatePoolWithTag(NonPagedPool,
// sizeof(NTFS_CCB),
// TAG_CCB);
// if (Ccb == NULL)
// {
// Status = STATUS_INSUFFICIENT_RESOURCES;
// goto ByeBye;
// }
//
// RtlZeroMemory(Ccb, sizeof(NTFS_CCB));
//
// Ccb->Identifier.Type = NTFS_TYPE_CCB;
// Ccb->Identifier.Size = sizeof(NTFS_TYPE_CCB);
//
// Vcb->StreamFileObject->FsContext = Fcb;
// Vcb->StreamFileObject->FsContext2 = Ccb;
// Vcb->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
// Vcb->StreamFileObject->PrivateCacheMap = NULL;
// Vcb->StreamFileObject->Vpb = Vcb->Vpb;
// Ccb->PtrFileObject = Vcb->StreamFileObject;
// Fcb->FileObject = Vcb->StreamFileObject;
// Fcb->Vcb = (PDEVICE_EXTENSION)Vcb->StorageDevice;
//
// Fcb->Flags = FCB_IS_VOLUME_STREAM;
//
// Fcb->RFCB.FileSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
// Fcb->RFCB.ValidDataLength.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
// Fcb->RFCB.AllocationSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; /* Correct? */
//
// CcInitializeCacheMap(Vcb->StreamFileObject,
// (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
// FALSE,
// &(NtfsGlobalData->CacheMgrCallbacks),
// Fcb);
//
// ExInitializeResourceLite(&Vcb->LogToPhysLock);
KeInitializeSpinLock ( & Vcb - > FcbListLock ) ;
//
// /* Get serial number */
// NewDeviceObject->Vpb->SerialNumber = Vcb->NtfsInfo.SerialNumber;
//
// /* Get volume label */
// NewDeviceObject->Vpb->VolumeLabelLength = Vcb->NtfsInfo.VolumeLabelLength;
// RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel,
// Vcb->NtfsInfo.VolumeLabel,
// Vcb->NtfsInfo.VolumeLabelLength);
Status = PsCreateSystemThread ( & Vcb - > flush_thread_handle , 0 , NULL , NULL , NULL , flush_thread , Vcb ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " PsCreateSystemThread returned %08x \n " , Status ) ;
goto exit ;
}
NewDeviceObject - > Vpb = Stack - > Parameters . MountVolume . Vpb ;
Stack - > Parameters . MountVolume . Vpb - > DeviceObject = NewDeviceObject ;
Stack - > Parameters . MountVolume . Vpb - > RealDevice = DeviceToMount ;
Stack - > Parameters . MountVolume . Vpb - > Flags | = VPB_MOUNTED ;
NewDeviceObject - > Vpb - > VolumeLabelLength = 4 ; // FIXME
NewDeviceObject - > Vpb - > VolumeLabel [ 0 ] = ' ? ' ;
NewDeviceObject - > Vpb - > VolumeLabel [ 1 ] = 0 ;
NewDeviceObject - > Vpb - > ReferenceCount + + ; // FIXME - should we deref this at any point?
Status = STATUS_SUCCESS ;
exit :
// if (!NT_SUCCESS(Status))
// {
// /* Cleanup */
// if (Vcb && Vcb->StreamFileObject)
// ObDereferenceObject(Vcb->StreamFileObject);
//
// if (Fcb)
// ExFreePool(Fcb);
//
// if (Ccb)
// ExFreePool(Ccb);
//
// if (NewDeviceObject)
// IoDeleteDevice(NewDeviceObject);
// }
if ( Vcb ) {
ExReleaseResourceLite ( & Vcb - > load_lock ) ;
}
if ( ! NT_SUCCESS ( Status ) ) {
if ( Vcb ) {
if ( Vcb - > root_fcb )
free_fcb ( Vcb - > root_fcb ) ;
if ( Vcb - > volume_fcb )
free_fcb ( Vcb - > volume_fcb ) ;
ExDeleteResourceLite ( & Vcb - > tree_lock ) ;
ExDeleteResourceLite ( & Vcb - > load_lock ) ;
ExDeleteResourceLite ( & Vcb - > fcb_lock ) ;
ExDeleteResourceLite ( & Vcb - > DirResource ) ;
if ( Vcb - > devices )
ExFreePoolWithTag ( Vcb - > devices , ALLOC_TAG ) ;
RemoveEntryList ( & Vcb - > list_entry ) ;
}
if ( NewDeviceObject )
IoDeleteDevice ( NewDeviceObject ) ;
}
TRACE ( " mount_vol done (status: %lx) \n " , Status ) ;
return Status ;
}
static NTSTATUS STDCALL drv_file_system_control ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
PIO_STACK_LOCATION IrpSp ;
NTSTATUS status ;
BOOL top_level ;
TRACE ( " file system control \n " ) ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
status = STATUS_NOT_IMPLEMENTED ;
IrpSp = IoGetCurrentIrpStackLocation ( Irp ) ;
Irp - > IoStatus . Information = 0 ;
switch ( IrpSp - > MinorFunction ) {
case IRP_MN_MOUNT_VOLUME :
TRACE ( " IRP_MN_MOUNT_VOLUME \n " ) ;
// Irp->IoStatus.Status = STATUS_SUCCESS;
status = mount_vol ( DeviceObject , Irp ) ;
// IrpSp->Parameters.MountVolume.DeviceObject = 0x0badc0de;
// IrpSp->Parameters.MountVolume.Vpb = 0xdeadbeef;
// IoCompleteRequest( Irp, IO_DISK_INCREMENT );
// return Irp->IoStatus.Status;
break ;
case IRP_MN_KERNEL_CALL :
TRACE ( " IRP_MN_KERNEL_CALL \n " ) ;
break ;
case IRP_MN_LOAD_FILE_SYSTEM :
TRACE ( " IRP_MN_LOAD_FILE_SYSTEM \n " ) ;
break ;
case IRP_MN_USER_FS_REQUEST :
TRACE ( " IRP_MN_USER_FS_REQUEST \n " ) ;
status = fsctl_request ( DeviceObject , Irp , IrpSp - > Parameters . FileSystemControl . FsControlCode , TRUE ) ;
break ;
case IRP_MN_VERIFY_VOLUME :
TRACE ( " IRP_MN_VERIFY_VOLUME \n " ) ;
break ;
default :
WARN ( " unknown minor %u \n " , IrpSp - > MinorFunction ) ;
break ;
}
Irp - > IoStatus . Status = status ;
IoCompleteRequest ( Irp , IO_NO_INCREMENT ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
return status ;
}
static NTSTATUS STDCALL drv_lock_control ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
NTSTATUS Status ;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation ( Irp ) ;
fcb * fcb = IrpSp - > FileObject - > FsContext ;
BOOL top_level ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
TRACE ( " lock control \n " ) ;
Status = FsRtlProcessFileLock ( & fcb - > lock , Irp , NULL ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
return Status ;
}
static NTSTATUS STDCALL drv_device_control ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
NTSTATUS Status ;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation ( Irp ) ;
PFILE_OBJECT FileObject = IrpSp - > FileObject ;
device_extension * Vcb = DeviceObject - > DeviceExtension ;
fcb * fcb ;
BOOL top_level ;
FIXME ( " STUB: device control \n " ) ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
Irp - > IoStatus . Information = 0 ;
WARN ( " control code = %x \n " , IrpSp - > Parameters . DeviceIoControl . IoControlCode ) ;
if ( ! FileObject ) {
ERR ( " FileObject was NULL \n " ) ;
Status = STATUS_INVALID_PARAMETER ;
goto end ;
}
fcb = FileObject - > FsContext ;
if ( ! fcb ) {
ERR ( " FCB was NULL \n " ) ;
Status = STATUS_INVALID_PARAMETER ;
goto end ;
}
if ( fcb = = Vcb - > volume_fcb ) {
FIXME ( " FIXME - pass through \n " ) ;
Status = STATUS_NOT_IMPLEMENTED ;
} else {
TRACE ( " filename = %.*S \n " , fcb - > full_filename . Length / sizeof ( WCHAR ) , fcb - > full_filename . Buffer ) ;
switch ( IrpSp - > Parameters . DeviceIoControl . IoControlCode ) {
case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME :
TRACE ( " IOCTL_MOUNTDEV_QUERY_DEVICE_NAME \n " ) ;
Status = STATUS_INVALID_PARAMETER ;
break ;
default :
WARN ( " unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x) \n " ,
IrpSp - > Parameters . DeviceIoControl . IoControlCode , ( IrpSp - > Parameters . DeviceIoControl . IoControlCode & 0xff0000 ) > > 16 ,
( IrpSp - > Parameters . DeviceIoControl . IoControlCode & 0xc000 ) > > 14 , ( IrpSp - > Parameters . DeviceIoControl . IoControlCode & 0x3ffc ) > > 2 ,
IrpSp - > Parameters . DeviceIoControl . IoControlCode & 0x3 ) ;
Status = STATUS_INVALID_PARAMETER ;
break ;
}
}
end :
Irp - > IoStatus . Status = Status ;
IoCompleteRequest ( Irp , IO_NO_INCREMENT ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
return Status ;
}
static NTSTATUS STDCALL drv_shutdown ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
NTSTATUS Status ;
BOOL top_level ;
ERR ( " shutdown \n " ) ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
Status = STATUS_SUCCESS ;
while ( ! IsListEmpty ( & VcbList ) ) {
LIST_ENTRY * le = RemoveHeadList ( & VcbList ) ;
device_extension * Vcb = CONTAINING_RECORD ( le , device_extension , list_entry ) ;
TRACE ( " shutting down Vcb %p \n " , Vcb ) ;
uninit ( Vcb ) ;
}
Irp - > IoStatus . Status = Status ;
Irp - > IoStatus . Information = 0 ;
IoCompleteRequest ( Irp , IO_NO_INCREMENT ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
return Status ;
}
static NTSTATUS STDCALL drv_pnp ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation ( Irp ) ;
device_extension * Vcb = DeviceObject - > DeviceExtension ;
NTSTATUS Status ;
BOOL top_level ;
FIXME ( " STUB: pnp \n " ) ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
Status = STATUS_NOT_IMPLEMENTED ;
switch ( IrpSp - > MinorFunction ) {
case IRP_MN_CANCEL_REMOVE_DEVICE :
TRACE ( " IRP_MN_CANCEL_REMOVE_DEVICE \n " ) ;
break ;
case IRP_MN_QUERY_REMOVE_DEVICE :
TRACE ( " IRP_MN_QUERY_REMOVE_DEVICE \n " ) ;
break ;
case IRP_MN_REMOVE_DEVICE :
TRACE ( " IRP_MN_REMOVE_DEVICE \n " ) ;
break ;
case IRP_MN_START_DEVICE :
TRACE ( " IRP_MN_START_DEVICE \n " ) ;
break ;
case IRP_MN_SURPRISE_REMOVAL :
TRACE ( " IRP_MN_SURPRISE_REMOVAL \n " ) ;
break ;
case IRP_MN_QUERY_DEVICE_RELATIONS :
TRACE ( " IRP_MN_QUERY_DEVICE_RELATIONS \n " ) ;
break ;
default :
WARN ( " Unrecognized minor function 0x%x \n " , IrpSp - > MinorFunction ) ;
break ;
}
// Irp->IoStatus.Status = Status;
// Irp->IoStatus.Information = 0;
IoSkipCurrentIrpStackLocation ( Irp ) ;
Status = IoCallDriver ( Vcb - > devices [ 0 ] . devobj , Irp ) ;
// IoCompleteRequest(Irp, IO_NO_INCREMENT);
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
return Status ;
}
# ifdef _DEBUG
static void STDCALL init_serial ( ) {
NTSTATUS Status ;
Status = IoGetDeviceObjectPointer ( & log_device , FILE_WRITE_DATA , & comfo , & comdo ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " IoGetDeviceObjectPointer returned %08x \n " , Status ) ;
}
}
# endif
# ifndef __REACTOS__
static void STDCALL check_cpu ( ) {
unsigned int cpuInfo [ 4 ] ;
# ifndef _MSC_VER
__get_cpuid ( 1 , & cpuInfo [ 0 ] , & cpuInfo [ 1 ] , & cpuInfo [ 2 ] , & cpuInfo [ 3 ] ) ;
have_sse42 = cpuInfo [ 2 ] & bit_SSE4_2 ;
# else
__cpuid ( cpuInfo , 1 ) ;
have_sse42 = cpuInfo [ 2 ] & ( 1 < < 20 ) ;
# endif
if ( have_sse42 )
TRACE ( " SSE4.2 is supported \n " ) ;
else
TRACE ( " SSE4.2 not supported \n " ) ;
}
# endif
static void STDCALL read_registry ( PUNICODE_STRING regpath ) {
HANDLE h ;
UNICODE_STRING us ;
OBJECT_ATTRIBUTES oa ;
ULONG dispos ;
NTSTATUS Status ;
WCHAR * path ;
ULONG kvfilen , retlen , i ;
KEY_VALUE_FULL_INFORMATION * kvfi ;
const WCHAR mappings [ ] = L " \\ Mappings " ;
# ifndef __REACTOS__
static WCHAR def_log_file [ ] = L " \\ ?? \\ C: \\ btrfs.log " ;
# endif
path = ExAllocatePoolWithTag ( PagedPool , regpath - > Length + ( wcslen ( mappings ) * sizeof ( WCHAR ) ) , ALLOC_TAG ) ;
if ( ! path ) {
ERR ( " out of memory \n " ) ;
return ;
}
RtlCopyMemory ( path , regpath - > Buffer , regpath - > Length ) ;
RtlCopyMemory ( ( UINT8 * ) path + regpath - > Length , mappings , wcslen ( mappings ) * sizeof ( WCHAR ) ) ;
us . Buffer = path ;
us . Length = us . MaximumLength = regpath - > Length + ( ( USHORT ) wcslen ( mappings ) * sizeof ( WCHAR ) ) ;
InitializeObjectAttributes ( & oa , & us , OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE , NULL , NULL ) ;
// FIXME - keep open and do notify for changes
Status = ZwCreateKey ( & h , KEY_QUERY_VALUE , & oa , 0 , NULL , REG_OPTION_NON_VOLATILE , & dispos ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ZwCreateKey returned %08x \n " , Status ) ;
ExFreePool ( path ) ;
return ;
}
if ( dispos = = REG_OPENED_EXISTING_KEY ) {
kvfilen = sizeof ( KEY_VALUE_FULL_INFORMATION ) + 256 ;
kvfi = ExAllocatePoolWithTag ( PagedPool , kvfilen , ALLOC_TAG ) ;
if ( ! kvfi ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( path ) ;
ZwClose ( h ) ;
return ;
}
i = 0 ;
do {
Status = ZwEnumerateValueKey ( h , i , KeyValueFullInformation , kvfi , kvfilen , & retlen ) ;
if ( NT_SUCCESS ( Status ) & & kvfi - > DataLength > 0 ) {
UINT32 val = 0 ;
RtlCopyMemory ( & val , ( UINT8 * ) kvfi + kvfi - > DataOffset , min ( kvfi - > DataLength , sizeof ( UINT32 ) ) ) ;
TRACE ( " entry %u = %.*S = %u \n " , i , kvfi - > NameLength / sizeof ( WCHAR ) , kvfi - > Name , val ) ;
add_user_mapping ( kvfi - > Name , kvfi - > NameLength / sizeof ( WCHAR ) , val ) ;
}
i = i + 1 ;
} while ( Status ! = STATUS_NO_MORE_ENTRIES ) ;
}
ZwClose ( h ) ;
ExFreePool ( path ) ;
# ifdef _DEBUG
InitializeObjectAttributes ( & oa , regpath , OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE , NULL , NULL ) ;
Status = ZwCreateKey ( & h , KEY_QUERY_VALUE , & oa , 0 , NULL , REG_OPTION_NON_VOLATILE , & dispos ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ZwCreateKey returned %08x \n " , Status ) ;
return ;
}
RtlInitUnicodeString ( & us , L " DebugLogLevel " ) ;
kvfi = NULL ;
kvfilen = 0 ;
Status = ZwQueryValueKey ( h , & us , KeyValueFullInformation , kvfi , kvfilen , & kvfilen ) ;
if ( ( Status = = STATUS_BUFFER_TOO_SMALL | | Status = = STATUS_BUFFER_OVERFLOW ) & & kvfilen > 0 ) {
kvfi = ExAllocatePoolWithTag ( PagedPool , kvfilen , ALLOC_TAG ) ;
if ( ! kvfi ) {
ERR ( " out of memory \n " ) ;
ZwClose ( h ) ;
return ;
}
Status = ZwQueryValueKey ( h , & us , KeyValueFullInformation , kvfi , kvfilen , & kvfilen ) ;
if ( NT_SUCCESS ( Status ) ) {
if ( kvfi - > Type = = REG_DWORD & & kvfi - > DataLength > = sizeof ( UINT32 ) ) {
RtlCopyMemory ( & debug_log_level , ( ( UINT8 * ) kvfi ) + kvfi - > DataOffset , sizeof ( UINT32 ) ) ;
} else {
Status = ZwDeleteValueKey ( h , & us ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ZwDeleteValueKey returned %08x \n " , Status ) ;
}
Status = ZwSetValueKey ( h , & us , 0 , REG_DWORD , & debug_log_level , sizeof ( debug_log_level ) ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ZwSetValueKey reutrned %08x \n " , Status ) ;
}
}
}
ExFreePool ( kvfi ) ;
} else if ( Status = = STATUS_OBJECT_NAME_NOT_FOUND ) {
Status = ZwSetValueKey ( h , & us , 0 , REG_DWORD , & debug_log_level , sizeof ( debug_log_level ) ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ZwSetValueKey reutrned %08x \n " , Status ) ;
}
} else {
ERR ( " ZwQueryValueKey returned %08x \n " , Status ) ;
}
RtlInitUnicodeString ( & us , L " LogDevice " ) ;
kvfi = NULL ;
kvfilen = 0 ;
Status = ZwQueryValueKey ( h , & us , KeyValueFullInformation , kvfi , kvfilen , & kvfilen ) ;
if ( ( Status = = STATUS_BUFFER_TOO_SMALL | | Status = = STATUS_BUFFER_OVERFLOW ) & & kvfilen > 0 ) {
kvfi = ExAllocatePoolWithTag ( PagedPool , kvfilen , ALLOC_TAG ) ;
if ( ! kvfi ) {
ERR ( " out of memory \n " ) ;
ZwClose ( h ) ;
return ;
}
Status = ZwQueryValueKey ( h , & us , KeyValueFullInformation , kvfi , kvfilen , & kvfilen ) ;
if ( NT_SUCCESS ( Status ) ) {
if ( ( kvfi - > Type = = REG_SZ | | kvfi - > Type = = REG_EXPAND_SZ ) & & kvfi - > DataLength > = sizeof ( WCHAR ) ) {
log_device . Length = log_device . MaximumLength = kvfi - > DataLength ;
log_device . Buffer = ExAllocatePoolWithTag ( PagedPool , kvfi - > DataLength , ALLOC_TAG ) ;
if ( ! log_device . Buffer ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( kvfi ) ;
ZwClose ( h ) ;
return ;
}
RtlCopyMemory ( log_device . Buffer , ( ( UINT8 * ) kvfi ) + kvfi - > DataOffset , kvfi - > DataLength ) ;
if ( log_device . Buffer [ ( log_device . Length / sizeof ( WCHAR ) ) - 1 ] = = 0 )
log_device . Length - = sizeof ( WCHAR ) ;
} else {
ERR ( " LogDevice was type %u, length %u \n " , kvfi - > Type , kvfi - > DataLength ) ;
Status = ZwDeleteValueKey ( h , & us ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ZwDeleteValueKey returned %08x \n " , Status ) ;
}
}
}
ExFreePool ( kvfi ) ;
} else if ( Status ! = STATUS_OBJECT_NAME_NOT_FOUND ) {
ERR ( " ZwQueryValueKey returned %08x \n " , Status ) ;
}
RtlInitUnicodeString ( & us , L " LogFile " ) ;
kvfi = NULL ;
kvfilen = 0 ;
Status = ZwQueryValueKey ( h , & us , KeyValueFullInformation , kvfi , kvfilen , & kvfilen ) ;
if ( ( Status = = STATUS_BUFFER_TOO_SMALL | | Status = = STATUS_BUFFER_OVERFLOW ) & & kvfilen > 0 ) {
kvfi = ExAllocatePoolWithTag ( PagedPool , kvfilen , ALLOC_TAG ) ;
if ( ! kvfi ) {
ERR ( " out of memory \n " ) ;
ZwClose ( h ) ;
return ;
}
Status = ZwQueryValueKey ( h , & us , KeyValueFullInformation , kvfi , kvfilen , & kvfilen ) ;
if ( NT_SUCCESS ( Status ) ) {
if ( ( kvfi - > Type = = REG_SZ | | kvfi - > Type = = REG_EXPAND_SZ ) & & kvfi - > DataLength > = sizeof ( WCHAR ) ) {
log_file . Length = log_file . MaximumLength = kvfi - > DataLength ;
log_file . Buffer = ExAllocatePoolWithTag ( PagedPool , kvfi - > DataLength , ALLOC_TAG ) ;
if ( ! log_file . Buffer ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( kvfi ) ;
ZwClose ( h ) ;
return ;
}
RtlCopyMemory ( log_file . Buffer , ( ( UINT8 * ) kvfi ) + kvfi - > DataOffset , kvfi - > DataLength ) ;
if ( log_file . Buffer [ ( log_file . Length / sizeof ( WCHAR ) ) - 1 ] = = 0 )
log_file . Length - = sizeof ( WCHAR ) ;
} else {
ERR ( " LogFile was type %u, length %u \n " , kvfi - > Type , kvfi - > DataLength ) ;
Status = ZwDeleteValueKey ( h , & us ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ZwDeleteValueKey returned %08x \n " , Status ) ;
}
}
}
ExFreePool ( kvfi ) ;
} else if ( Status = = STATUS_OBJECT_NAME_NOT_FOUND ) {
Status = ZwSetValueKey ( h , & us , 0 , REG_SZ , def_log_file , ( wcslen ( def_log_file ) + 1 ) * sizeof ( WCHAR ) ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ZwSetValueKey returned %08x \n " , Status ) ;
}
} else {
ERR ( " ZwQueryValueKey returned %08x \n " , Status ) ;
}
if ( log_file . Length = = 0 ) {
log_file . Length = log_file . MaximumLength = wcslen ( def_log_file ) * sizeof ( WCHAR ) ;
log_file . Buffer = ExAllocatePoolWithTag ( PagedPool , log_file . MaximumLength , ALLOC_TAG ) ;
if ( ! log_file . Buffer ) {
ERR ( " out of memory \n " ) ;
ZwClose ( h ) ;
return ;
}
RtlCopyMemory ( log_file . Buffer , def_log_file , log_file . Length ) ;
}
ZwClose ( h ) ;
# endif
}
# ifdef _DEBUG
static void init_logging ( ) {
if ( log_device . Length > 0 )
init_serial ( ) ;
else if ( log_file . Length > 0 ) {
NTSTATUS Status ;
OBJECT_ATTRIBUTES oa ;
IO_STATUS_BLOCK iosb ;
char * dateline ;
LARGE_INTEGER time ;
TIME_FIELDS tf ;
InitializeObjectAttributes ( & oa , & log_file , OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE , NULL , NULL ) ;
Status = ZwCreateFile ( & log_handle , FILE_WRITE_DATA , & oa , & iosb , NULL , FILE_ATTRIBUTE_NORMAL , FILE_SHARE_READ ,
FILE_OPEN_IF , FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_ALERT , NULL , 0 ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ZwCreateFile returned %08x \n " , Status ) ;
return ;
}
if ( iosb . Information = = FILE_OPENED ) { // already exists
FILE_STANDARD_INFORMATION fsi ;
FILE_POSITION_INFORMATION fpi ;
static char delim [ ] = " \n --- \n " ;
// move to end of file
Status = ZwQueryInformationFile ( log_handle , & iosb , & fsi , sizeof ( FILE_STANDARD_INFORMATION ) , FileStandardInformation ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ZwQueryInformationFile returned %08x \n " , Status ) ;
return ;
}
fpi . CurrentByteOffset = fsi . EndOfFile ;
Status = ZwSetInformationFile ( log_handle , & iosb , & fpi , sizeof ( FILE_POSITION_INFORMATION ) , FilePositionInformation ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ZwSetInformationFile returned %08x \n " , Status ) ;
return ;
}
Status = ZwWriteFile ( log_handle , NULL , NULL , NULL , & iosb , delim , strlen ( delim ) , NULL , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ZwWriteFile returned %08x \n " , Status ) ;
return ;
}
}
dateline = ExAllocatePoolWithTag ( PagedPool , 256 , ALLOC_TAG ) ;
if ( ! dateline ) {
ERR ( " out of memory \n " ) ;
return ;
}
KeQuerySystemTime ( & time ) ;
RtlTimeToTimeFields ( & time , & tf ) ;
sprintf ( dateline , " Starting logging at %04u-%02u-%02u %02u:%02u:%02u \n " , tf . Year , tf . Month , tf . Day , tf . Hour , tf . Minute , tf . Second ) ;
Status = ZwWriteFile ( log_handle , NULL , NULL , NULL , & iosb , dateline , strlen ( dateline ) , NULL , NULL ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " ZwWriteFile returned %08x \n " , Status ) ;
return ;
}
ExFreePool ( dateline ) ;
}
}
# endif
NTSTATUS STDCALL DriverEntry ( PDRIVER_OBJECT DriverObject , PUNICODE_STRING RegistryPath ) {
NTSTATUS Status ;
PDEVICE_OBJECT DeviceObject ;
UNICODE_STRING device_nameW ;
UNICODE_STRING dosdevice_nameW ;
InitializeListHead ( & uid_map_list ) ;
log_device . Buffer = NULL ;
log_device . Length = log_device . MaximumLength = 0 ;
log_file . Buffer = NULL ;
log_file . Length = log_file . MaximumLength = 0 ;
read_registry ( RegistryPath ) ;
# ifdef _DEBUG
if ( debug_log_level > 0 )
init_logging ( ) ;
log_started = TRUE ;
# endif
TRACE ( " DriverEntry \n " ) ;
# ifndef __REACTOS__
check_cpu ( ) ;
# endif
// TRACE("check CRC32C: %08x\n", calc_crc32c((UINT8*)"123456789", 9)); // should be e3069283
drvobj = DriverObject ;
DriverObject - > DriverUnload = DriverUnload ;
DriverObject - > MajorFunction [ IRP_MJ_CREATE ] = ( PDRIVER_DISPATCH ) drv_create ;
DriverObject - > MajorFunction [ IRP_MJ_CLOSE ] = ( PDRIVER_DISPATCH ) drv_close ;
DriverObject - > MajorFunction [ IRP_MJ_READ ] = ( PDRIVER_DISPATCH ) drv_read ;
DriverObject - > MajorFunction [ IRP_MJ_WRITE ] = ( PDRIVER_DISPATCH ) drv_write ;
DriverObject - > MajorFunction [ IRP_MJ_QUERY_INFORMATION ] = ( PDRIVER_DISPATCH ) drv_query_information ;
DriverObject - > MajorFunction [ IRP_MJ_SET_INFORMATION ] = ( PDRIVER_DISPATCH ) drv_set_information ;
DriverObject - > MajorFunction [ IRP_MJ_QUERY_EA ] = ( PDRIVER_DISPATCH ) drv_query_ea ;
DriverObject - > MajorFunction [ IRP_MJ_SET_EA ] = ( PDRIVER_DISPATCH ) drv_set_ea ;
DriverObject - > MajorFunction [ IRP_MJ_FLUSH_BUFFERS ] = ( PDRIVER_DISPATCH ) drv_flush_buffers ;
DriverObject - > MajorFunction [ IRP_MJ_QUERY_VOLUME_INFORMATION ] = ( PDRIVER_DISPATCH ) drv_query_volume_information ;
DriverObject - > MajorFunction [ IRP_MJ_SET_VOLUME_INFORMATION ] = ( PDRIVER_DISPATCH ) drv_set_volume_information ;
DriverObject - > MajorFunction [ IRP_MJ_CLEANUP ] = ( PDRIVER_DISPATCH ) drv_cleanup ;
DriverObject - > MajorFunction [ IRP_MJ_DIRECTORY_CONTROL ] = ( PDRIVER_DISPATCH ) drv_directory_control ;
DriverObject - > MajorFunction [ IRP_MJ_FILE_SYSTEM_CONTROL ] = ( PDRIVER_DISPATCH ) drv_file_system_control ;
DriverObject - > MajorFunction [ IRP_MJ_LOCK_CONTROL ] = ( PDRIVER_DISPATCH ) drv_lock_control ;
DriverObject - > MajorFunction [ IRP_MJ_DEVICE_CONTROL ] = ( PDRIVER_DISPATCH ) drv_device_control ;
DriverObject - > MajorFunction [ IRP_MJ_SHUTDOWN ] = ( PDRIVER_DISPATCH ) drv_shutdown ;
DriverObject - > MajorFunction [ IRP_MJ_PNP ] = ( PDRIVER_DISPATCH ) drv_pnp ;
DriverObject - > MajorFunction [ IRP_MJ_QUERY_SECURITY ] = ( PDRIVER_DISPATCH ) drv_query_security ;
DriverObject - > MajorFunction [ IRP_MJ_SET_SECURITY ] = ( PDRIVER_DISPATCH ) drv_set_security ;
init_fast_io_dispatch ( & DriverObject - > FastIoDispatch ) ;
device_nameW . Buffer = device_name ;
device_nameW . Length = device_nameW . MaximumLength = ( USHORT ) wcslen ( device_name ) * sizeof ( WCHAR ) ;
dosdevice_nameW . Buffer = dosdevice_name ;
dosdevice_nameW . Length = dosdevice_nameW . MaximumLength = ( USHORT ) wcslen ( dosdevice_name ) * sizeof ( WCHAR ) ;
Status = IoCreateDevice ( DriverObject , 0 , & device_nameW , FILE_DEVICE_DISK_FILE_SYSTEM , 0 , FALSE , & DeviceObject ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " IoCreateDevice returned %08x \n " , Status ) ;
return Status ;
}
devobj = DeviceObject ;
DeviceObject - > Flags & = ~ DO_DEVICE_INITIALIZING ;
Status = IoCreateSymbolicLink ( & dosdevice_nameW , & device_nameW ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " IoCreateSymbolicLink returned %08x \n " , Status ) ;
return Status ;
}
Status = init_cache ( ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " init_cache returned %08x \n " , Status ) ;
return Status ;
}
InitializeListHead ( & volumes ) ;
look_for_vols ( & volumes ) ;
InitializeListHead ( & VcbList ) ;
ExInitializeResourceLite ( & global_loading_lock ) ;
IoRegisterFileSystem ( DeviceObject ) ;
return STATUS_SUCCESS ;
}