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/>. */
# include "btrfs_drv.h"
static NTSTATUS STDCALL move_subvol ( device_extension * Vcb , fcb * fcb , root * destsubvol , UINT64 destinode , PANSI_STRING utf8 , UINT32 crc32 , UINT32 oldcrc32 , BTRFS_TIME * now , BOOL ReplaceIfExists , LIST_ENTRY * rollback ) ;
static NTSTATUS STDCALL set_basic_information ( device_extension * Vcb , PIRP Irp , PFILE_OBJECT FileObject , LIST_ENTRY * rollback ) {
FILE_BASIC_INFORMATION * fbi = Irp - > AssociatedIrp . SystemBuffer ;
fcb * fcb = FileObject - > FsContext ;
ULONG defda ;
BOOL inode_item_changed = FALSE ;
NTSTATUS Status ;
if ( fcb - > ads )
fcb = fcb - > par ;
TRACE ( " file = %.*S, attributes = %x \n " , fcb - > full_filename . Length / sizeof ( WCHAR ) , fcb - > full_filename . Buffer , fbi - > FileAttributes ) ;
if ( fbi - > FileAttributes & FILE_ATTRIBUTE_DIRECTORY & & fcb - > type ! = BTRFS_TYPE_DIRECTORY ) {
WARN ( " attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory \n " ) ;
return STATUS_INVALID_PARAMETER ;
}
// FIXME - what if FCB is volume or root?
// FIXME - what about subvol roots?
// FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
// FIXME - handle times == -1
// FileAttributes == 0 means don't set - undocumented, but seen in fastfat
if ( fbi - > FileAttributes ! = 0 ) {
LARGE_INTEGER time ;
BTRFS_TIME now ;
defda = get_file_attributes ( Vcb , & fcb - > inode_item , fcb - > subvol , fcb - > inode , fcb - > type , fcb - > filepart . Length > 0 & & fcb - > filepart . Buffer [ 0 ] = = ' . ' , TRUE ) ;
if ( fcb - > type = = BTRFS_TYPE_DIRECTORY )
fbi - > FileAttributes | = FILE_ATTRIBUTE_DIRECTORY ;
else if ( fcb - > type = = BTRFS_TYPE_SYMLINK )
fbi - > FileAttributes | = FILE_ATTRIBUTE_REPARSE_POINT ;
// create new xattr
if ( defda ! = fbi - > FileAttributes ) {
char val [ 64 ] ;
TRACE ( " inserting new DOSATTRIB xattr \n " ) ;
sprintf ( val , " 0x%lx " , fbi - > FileAttributes ) ;
Status = set_xattr ( Vcb , fcb - > subvol , fcb - > inode , EA_DOSATTRIB , EA_DOSATTRIB_HASH , ( UINT8 * ) val , strlen ( val ) , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " set_xattr returned %08x \n " , Status ) ;
return Status ;
}
} else
delete_xattr ( Vcb , fcb - > subvol , fcb - > inode , EA_DOSATTRIB , EA_DOSATTRIB_HASH , rollback ) ;
fcb - > atts = fbi - > FileAttributes ;
KeQuerySystemTime ( & time ) ;
win_time_to_unix ( time , & now ) ;
fcb - > inode_item . st_ctime = now ;
fcb - > subvol - > root_item . ctransid = Vcb - > superblock . generation ;
fcb - > subvol - > root_item . ctime = now ;
inode_item_changed = TRUE ;
}
// FIXME - CreationTime
// FIXME - LastAccessTime
// FIXME - LastWriteTime
// FIXME - ChangeTime
if ( inode_item_changed ) {
KEY searchkey ;
traverse_ptr tp ;
INODE_ITEM * ii ;
fcb - > inode_item . transid = Vcb - > superblock . generation ;
fcb - > inode_item . sequence + + ;
searchkey . obj_id = fcb - > inode ;
searchkey . obj_type = TYPE_INODE_ITEM ;
searchkey . offset = 0xffffffffffffffff ;
Status = find_item ( Vcb , fcb - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( tp . item - > key . obj_id = = searchkey . obj_id & & tp . item - > key . obj_type = = searchkey . obj_type )
delete_tree_item ( Vcb , & tp , rollback ) ;
else
WARN ( " couldn't find old INODE_ITEM \n " ) ;
free_traverse_ptr ( & tp ) ;
ii = ExAllocatePoolWithTag ( PagedPool , sizeof ( INODE_ITEM ) , ALLOC_TAG ) ;
if ( ! ii ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( ii , & fcb - > inode_item , sizeof ( INODE_ITEM ) ) ;
if ( ! insert_tree_item ( Vcb , fcb - > subvol , fcb - > inode , TYPE_INODE_ITEM , 0 , ii , sizeof ( INODE_ITEM ) , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
ExFreePool ( ii ) ;
return STATUS_INTERNAL_ERROR ;
}
}
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL set_disposition_information ( device_extension * Vcb , PIRP Irp , PFILE_OBJECT FileObject ) {
FILE_DISPOSITION_INFORMATION * fdi = Irp - > AssociatedIrp . SystemBuffer ;
fcb * fcb = FileObject - > FsContext ;
ULONG atts ;
TRACE ( " changing delete_on_close to %s for %.*S (fcb %p) \n " , fdi - > DeleteFile ? " TRUE " : " FALSE " , fcb - > full_filename . Length / sizeof ( WCHAR ) , fcb - > full_filename . Buffer , fcb ) ;
atts = fcb - > ads ? fcb - > par - > atts : fcb - > atts ;
TRACE ( " atts = %x \n " , atts ) ;
if ( atts & FILE_ATTRIBUTE_READONLY )
return STATUS_CANNOT_DELETE ;
if ( fcb - > type = = BTRFS_TYPE_DIRECTORY & & fcb - > inode_item . st_size > 0 )
return STATUS_DIRECTORY_NOT_EMPTY ;
if ( ! MmFlushImageSection ( & fcb - > nonpaged - > segment_object , MmFlushForDelete ) ) {
WARN ( " trying to delete file which is being mapped as an image \n " ) ;
return STATUS_CANNOT_DELETE ;
}
if ( fcb - > inode = = SUBVOL_ROOT_INODE ) {
FIXME ( " FIXME - subvol deletion not yet supported \n " ) ;
return STATUS_INTERNAL_ERROR ;
}
fcb - > delete_on_close = fdi - > DeleteFile ;
// FIXME - should this fail if file opened with FILE_DELETE_ON_CLOSE?
FileObject - > DeletePending = fdi - > DeleteFile ;
return STATUS_SUCCESS ;
}
static NTSTATUS add_inode_extref ( device_extension * Vcb , root * subvol , UINT64 inode , UINT64 parinode , UINT64 index , PANSI_STRING utf8 , LIST_ENTRY * rollback ) {
KEY searchkey ;
traverse_ptr tp ;
INODE_EXTREF * ier ;
NTSTATUS Status ;
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 ) ) {
ULONG iersize = tp . item - > size + sizeof ( INODE_EXTREF ) - 1 + utf8 - > Length ;
UINT8 * ier2 ;
UINT32 maxlen = Vcb - > superblock . node_size - sizeof ( tree_header ) - sizeof ( leaf_node ) ;
if ( iersize > maxlen ) {
ERR ( " item would be too long (%u > %u) \n " , iersize , maxlen ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
ier2 = ExAllocatePoolWithTag ( PagedPool , iersize , ALLOC_TAG ) ;
if ( ! ier2 ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
if ( tp . item - > size > 0 )
RtlCopyMemory ( ier2 , tp . item - > data , tp . item - > size ) ;
ier = ( INODE_EXTREF * ) & ier2 [ tp . item - > size ] ;
ier - > dir = parinode ;
ier - > index = index ;
ier - > n = utf8 - > Length ;
RtlCopyMemory ( ier - > name , utf8 - > Buffer , utf8 - > Length ) ;
delete_tree_item ( Vcb , & tp , rollback ) ;
if ( ! insert_tree_item ( Vcb , subvol , searchkey . obj_id , searchkey . obj_type , searchkey . offset , ier2 , iersize , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
} else {
ier = ExAllocatePoolWithTag ( PagedPool , sizeof ( INODE_EXTREF ) - 1 + utf8 - > Length , ALLOC_TAG ) ;
if ( ! ier ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
ier - > dir = parinode ;
ier - > index = index ;
ier - > n = utf8 - > Length ;
RtlCopyMemory ( ier - > name , utf8 - > Buffer , utf8 - > Length ) ;
if ( ! insert_tree_item ( Vcb , subvol , searchkey . obj_id , searchkey . obj_type , searchkey . offset , ier , sizeof ( INODE_EXTREF ) - 1 + utf8 - > Length , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
}
free_traverse_ptr ( & tp ) ;
return STATUS_SUCCESS ;
}
NTSTATUS add_inode_ref ( device_extension * Vcb , root * subvol , UINT64 inode , UINT64 parinode , UINT64 index , PANSI_STRING utf8 , LIST_ENTRY * rollback ) {
KEY searchkey ;
traverse_ptr tp ;
INODE_REF * ir ;
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 ) ) {
ULONG irsize = tp . item - > size + sizeof ( INODE_REF ) - 1 + utf8 - > Length ;
UINT8 * ir2 ;
UINT32 maxlen = Vcb - > superblock . node_size - sizeof ( tree_header ) - sizeof ( leaf_node ) ;
if ( irsize > maxlen ) {
if ( Vcb - > superblock . incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF ) {
TRACE ( " INODE_REF too long, creating INODE_EXTREF \n " ) ;
free_traverse_ptr ( & tp ) ;
return add_inode_extref ( Vcb , subvol , inode , parinode , index , utf8 , rollback ) ;
} else {
ERR ( " item would be too long (%u > %u) \n " , irsize , maxlen ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
}
ir2 = ExAllocatePoolWithTag ( PagedPool , irsize , ALLOC_TAG ) ;
if ( ! ir2 ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
if ( tp . item - > size > 0 )
RtlCopyMemory ( ir2 , tp . item - > data , tp . item - > size ) ;
ir = ( INODE_REF * ) & ir2 [ tp . item - > size ] ;
ir - > index = index ;
ir - > n = utf8 - > Length ;
RtlCopyMemory ( ir - > name , utf8 - > Buffer , utf8 - > Length ) ;
delete_tree_item ( Vcb , & tp , rollback ) ;
if ( ! insert_tree_item ( Vcb , subvol , searchkey . obj_id , searchkey . obj_type , searchkey . offset , ir2 , irsize , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
} else {
ir = ExAllocatePoolWithTag ( PagedPool , sizeof ( INODE_REF ) - 1 + utf8 - > Length , ALLOC_TAG ) ;
if ( ! ir ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
ir - > index = index ;
ir - > n = utf8 - > Length ;
RtlCopyMemory ( ir - > name , utf8 - > Buffer , utf8 - > Length ) ;
if ( ! insert_tree_item ( Vcb , subvol , searchkey . obj_id , searchkey . obj_type , searchkey . offset , ir , sizeof ( INODE_REF ) - 1 + ir - > n , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
}
free_traverse_ptr ( & tp ) ;
return STATUS_SUCCESS ;
}
static NTSTATUS get_fcb_from_dir_item ( device_extension * Vcb , fcb * * pfcb , fcb * parent , root * subvol , DIR_ITEM * di ) {
LIST_ENTRY * le ;
fcb * sf2 ;
struct _fcb * c ;
KEY searchkey ;
traverse_ptr tp ;
NTSTATUS Status ;
le = parent - > children . Flink ;
while ( le ! = & parent - > children ) {
c = CONTAINING_RECORD ( le , struct _fcb , list_entry ) ;
if ( c - > refcount > 0 & & c - > inode = = di - > key . obj_id & & c - > subvol = = subvol ) {
c - > refcount + + ;
# ifdef DEBUG_FCB_REFCOUNTS
WARN ( " fcb %p: refcount now %i (%.*S) \n " , c , c - > refcount , c - > full_filename . Length / sizeof ( WCHAR ) , c - > full_filename . Buffer ) ;
# endif
* pfcb = c ;
return STATUS_SUCCESS ;
}
le = le - > Flink ;
}
sf2 = create_fcb ( ) ;
if ( ! sf2 ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
sf2 - > Vcb = Vcb ;
sf2 - > utf8 . Length = sf2 - > utf8 . MaximumLength = di - > n ;
sf2 - > utf8 . Buffer = ExAllocatePoolWithTag ( PagedPool , di - > n , ALLOC_TAG ) ;
if ( ! sf2 - > utf8 . Buffer ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( sf2 - > utf8 . Buffer , di - > name , di - > n ) ;
sf2 - > par = parent ;
parent - > refcount + + ;
# ifdef DEBUG_FCB_REFCOUNTS
WARN ( " fcb %p: refcount now %i (%.*S) \n " , parent , parent - > refcount , parent - > full_filename . Length / sizeof ( WCHAR ) , parent - > full_filename . Buffer ) ;
# endif
if ( di - > key . obj_type = = TYPE_ROOT_ITEM ) {
root * fcbroot = Vcb - > roots ;
while ( fcbroot & & fcbroot - > id ! = di - > key . obj_id )
fcbroot = fcbroot - > next ;
sf2 - > subvol = fcbroot ;
sf2 - > inode = SUBVOL_ROOT_INODE ;
} else {
sf2 - > subvol = subvol ;
sf2 - > inode = di - > key . obj_id ;
}
sf2 - > type = di - > type ;
if ( Vcb - > fcbs )
Vcb - > fcbs - > prev = sf2 ;
sf2 - > next = Vcb - > fcbs ;
Vcb - > fcbs = sf2 ;
sf2 - > name_offset = parent - > full_filename . Length / sizeof ( WCHAR ) ;
if ( parent ! = Vcb - > root_fcb )
sf2 - > name_offset + + ;
InsertTailList ( & parent - > children , & sf2 - > list_entry ) ;
searchkey . obj_id = sf2 - > inode ;
searchkey . obj_type = TYPE_INODE_ITEM ;
searchkey . offset = 0xffffffffffffffff ;
Status = find_item ( Vcb , sf2 - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
free_fcb ( sf2 ) ;
return Status ;
}
if ( tp . item - > key . obj_id ! = searchkey . obj_id | | tp . item - > key . obj_type ! = searchkey . obj_type ) {
ERR ( " couldn't find INODE_ITEM for inode %llx in subvol %llx \n " , sf2 - > inode , sf2 - > subvol - > id ) ;
free_traverse_ptr ( & tp ) ;
free_fcb ( sf2 ) ;
return STATUS_INTERNAL_ERROR ;
}
if ( tp . item - > size > 0 )
RtlCopyMemory ( & sf2 - > inode_item , tp . item - > data , min ( sizeof ( INODE_ITEM ) , tp . item - > size ) ) ;
free_traverse_ptr ( & tp ) ;
// This is just a quick function for the sake of move_across_subvols. As such, we don't bother
// with sf2->atts, sf2->sd, or sf2->full_filename.
* pfcb = sf2 ;
return STATUS_SUCCESS ;
}
// static LONG get_tree_count(device_extension* Vcb, LIST_ENTRY* tc) {
// LONG rc = 0;
// LIST_ENTRY* le = Vcb->trees.Flink;
//
// while (le != &Vcb->trees) {
// tree* t = CONTAINING_RECORD(le, tree, list_entry);
//
// rc += t->refcount;
//
// le = le->Flink;
// }
//
// le = tc->Flink;
// while (le != tc) {
// tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
// tree* t;
//
// rc--;
//
// t = tc2->tree->parent;
// while (t) {
// rc--;
// t = t->parent;
// }
//
// le = le->Flink;
// }
//
// return rc;
// }
static NTSTATUS STDCALL move_inode_across_subvols ( device_extension * Vcb , fcb * fcb , root * destsubvol , UINT64 destinode , UINT64 inode , UINT64 oldparinode , PANSI_STRING utf8 , UINT32 crc32 , BTRFS_TIME * now , LIST_ENTRY * rollback ) {
UINT64 oldindex , index ;
UINT32 oldcrc32 ;
INODE_ITEM * ii ;
BOOL has_hardlink = FALSE ;
DIR_ITEM * di ;
KEY searchkey ;
traverse_ptr tp , next_tp ;
NTSTATUS Status ;
BOOL b ;
// move INODE_ITEM
fcb - > inode_item . transid = Vcb - > superblock . generation ;
fcb - > inode_item . sequence + + ;
fcb - > inode_item . st_ctime = * now ;
searchkey . obj_id = fcb - > inode ;
searchkey . obj_type = TYPE_INODE_ITEM ;
searchkey . offset = 0 ;
Status = find_item ( Vcb , fcb - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & searchkey , & tp . item - > key ) ) {
delete_tree_item ( Vcb , & tp , rollback ) ;
if ( fcb - > inode_item . st_nlink > 1 ) {
fcb - > inode_item . st_nlink - - ;
ii = ExAllocatePoolWithTag ( PagedPool , sizeof ( INODE_ITEM ) , ALLOC_TAG ) ;
if ( ! ii ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( ii , & fcb - > inode_item , sizeof ( INODE_ITEM ) ) ;
if ( ! insert_tree_item ( Vcb , fcb - > subvol , fcb - > inode , TYPE_INODE_ITEM , 0 , ii , sizeof ( INODE_ITEM ) , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
has_hardlink = TRUE ;
}
} else {
WARN ( " couldn't find old INODE_ITEM \n " ) ;
}
free_traverse_ptr ( & tp ) ;
fcb - > inode_item . st_nlink = 1 ;
ii = ExAllocatePoolWithTag ( PagedPool , sizeof ( INODE_ITEM ) , ALLOC_TAG ) ;
if ( ! ii ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( ii , & fcb - > inode_item , sizeof ( INODE_ITEM ) ) ;
if ( ! insert_tree_item ( Vcb , destsubvol , inode , TYPE_INODE_ITEM , 0 , ii , sizeof ( INODE_ITEM ) , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
return STATUS_INTERNAL_ERROR ;
}
oldcrc32 = calc_crc32c ( 0xfffffffe , ( UINT8 * ) fcb - > utf8 . Buffer , ( ULONG ) fcb - > utf8 . Length ) ;
// delete old DIR_ITEM
Status = delete_dir_item ( Vcb , fcb - > subvol , oldparinode , oldcrc32 , & fcb - > utf8 , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " delete_dir_item returned %08x \n " , Status ) ;
return Status ;
}
// create new DIR_ITEM
di = ExAllocatePoolWithTag ( PagedPool , sizeof ( DIR_ITEM ) - 1 + utf8 - > Length , ALLOC_TAG ) ;
if ( ! di ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
di - > key . obj_id = inode ;
di - > key . obj_type = TYPE_INODE_ITEM ;
di - > key . offset = 0 ;
di - > transid = Vcb - > superblock . generation ;
di - > m = 0 ;
di - > n = utf8 - > Length ;
di - > type = fcb - > type ;
RtlCopyMemory ( di - > name , utf8 - > Buffer , utf8 - > Length ) ;
Status = add_dir_item ( Vcb , destsubvol , destinode , crc32 , di , sizeof ( DIR_ITEM ) - 1 + utf8 - > Length , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " add_dir_item returned %08x \n " , Status ) ;
return Status ;
}
Status = delete_inode_ref ( Vcb , fcb - > subvol , fcb - > inode , oldparinode , & fcb - > utf8 , & oldindex , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " delete_inode_ref returned %08x \n " , Status ) ;
return Status ;
}
// delete DIR_INDEX
if ( oldindex = = 0 ) {
WARN ( " couldn't find old INODE_REF \n " ) ;
} else {
searchkey . obj_id = oldparinode ;
searchkey . obj_type = TYPE_DIR_INDEX ;
searchkey . offset = oldindex ;
Status = find_item ( Vcb , fcb - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & searchkey , & tp . item - > key ) )
delete_tree_item ( Vcb , & tp , rollback ) ;
else
WARN ( " couldn't find old DIR_INDEX \n " ) ;
free_traverse_ptr ( & tp ) ;
}
// get new index
searchkey . obj_id = destinode ;
searchkey . obj_type = TYPE_DIR_INDEX + 1 ;
searchkey . offset = 0 ;
Status = find_item ( Vcb , destsubvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & searchkey , & tp . item - > key ) ) {
if ( find_prev_item ( Vcb , & tp , & next_tp , FALSE ) ) {
free_traverse_ptr ( & tp ) ;
tp = next_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 ) {
index = tp . item - > key . offset + 1 ;
} else
index = 2 ;
free_traverse_ptr ( & tp ) ;
// create INODE_REF
Status = add_inode_ref ( Vcb , destsubvol , inode , destinode , index , utf8 , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " add_inode_ref returned %08x \n " , Status ) ;
return Status ;
}
// create DIR_INDEX
di = ExAllocatePoolWithTag ( PagedPool , sizeof ( DIR_ITEM ) - 1 + utf8 - > Length , ALLOC_TAG ) ;
if ( ! di ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
di - > key . obj_id = inode ;
di - > key . obj_type = TYPE_INODE_ITEM ;
di - > key . offset = 0 ;
di - > transid = Vcb - > superblock . generation ;
di - > m = 0 ;
di - > n = utf8 - > Length ;
di - > type = fcb - > type ;
RtlCopyMemory ( di - > name , utf8 - > Buffer , utf8 - > Length ) ;
if ( ! insert_tree_item ( Vcb , destsubvol , destinode , TYPE_DIR_INDEX , index , di , sizeof ( DIR_ITEM ) - 1 + utf8 - > Length , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
return STATUS_INTERNAL_ERROR ;
}
// move XATTR_ITEMs
searchkey . obj_id = fcb - > inode ;
searchkey . obj_type = TYPE_XATTR_ITEM ;
searchkey . offset = 0 ;
Status = find_item ( Vcb , fcb - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
do {
if ( tp . item - > key . obj_id = = fcb - > inode & & tp . item - > key . obj_type = = TYPE_XATTR_ITEM & & tp . item - > size > 0 ) {
di = ExAllocatePoolWithTag ( PagedPool , tp . item - > size , ALLOC_TAG ) ;
if ( ! di ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( di , tp . item - > data , tp . item - > size ) ;
if ( ! insert_tree_item ( Vcb , destsubvol , inode , TYPE_XATTR_ITEM , tp . item - > key . offset , di , tp . item - > size , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
if ( ! has_hardlink )
delete_tree_item ( Vcb , & tp , rollback ) ;
}
b = find_next_item ( Vcb , & tp , & next_tp , FALSE ) ;
if ( b ) {
free_traverse_ptr ( & tp ) ;
tp = next_tp ;
if ( next_tp . item - > key . obj_id > fcb - > inode | | next_tp . item - > key . obj_type > TYPE_XATTR_ITEM )
break ;
}
} while ( b ) ;
free_traverse_ptr ( & tp ) ;
// do extents
searchkey . obj_id = fcb - > inode ;
searchkey . obj_type = TYPE_EXTENT_DATA ;
searchkey . offset = 0 ;
Status = find_item ( Vcb , fcb - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
do {
if ( tp . item - > key . obj_id = = fcb - > inode & & tp . item - > key . obj_type = = TYPE_EXTENT_DATA ) {
if ( tp . item - > size < sizeof ( EXTENT_DATA ) ) {
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 ( EXTENT_DATA ) ) ;
} else {
EXTENT_DATA * ed = ExAllocatePoolWithTag ( PagedPool , tp . item - > size , ALLOC_TAG ) ;
if ( ! ed ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( ed , tp . item - > data , tp . item - > size ) ;
// FIXME - update ed's generation
if ( ! insert_tree_item ( Vcb , destsubvol , inode , TYPE_EXTENT_DATA , tp . item - > key . offset , ed , tp . item - > size , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
if ( ( ed - > type = = EXTENT_TYPE_REGULAR | | ed - > type = = EXTENT_TYPE_PREALLOC ) & & tp . item - > size > = sizeof ( EXTENT_DATA ) - 1 + sizeof ( EXTENT_DATA2 ) ) {
EXTENT_DATA2 * ed2 = ( EXTENT_DATA2 * ) ed - > data ;
if ( ed2 - > address ! = 0 ) {
Status = add_extent_ref ( Vcb , ed2 - > address , ed2 - > size , destsubvol , inode , tp . item - > key . offset , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " add_extent_ref returned %08x \n " , Status ) ;
free_traverse_ptr ( & tp ) ;
return Status ;
}
if ( ! has_hardlink ) {
Status = remove_extent_ref ( Vcb , ed2 - > address , ed2 - > size , fcb - > subvol , fcb - > inode , tp . item - > key . offset , NULL , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " remove_extent_ref returned %08x \n " , Status ) ;
free_traverse_ptr ( & tp ) ;
return Status ;
}
}
}
}
if ( ! has_hardlink )
delete_tree_item ( Vcb , & tp , rollback ) ;
}
}
b = find_next_item ( Vcb , & tp , & next_tp , FALSE ) ;
if ( b ) {
free_traverse_ptr ( & tp ) ;
tp = next_tp ;
if ( next_tp . item - > key . obj_id > fcb - > inode | | next_tp . item - > key . obj_type > TYPE_EXTENT_DATA )
break ;
}
} while ( b ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_SUCCESS ;
}
typedef struct {
fcb * fcb ;
UINT8 level ;
UINT32 crc32 ;
UINT64 newinode ;
UINT64 newparinode ;
BOOL subvol ;
ANSI_STRING utf8 ;
LIST_ENTRY list_entry ;
} dir_list ;
static NTSTATUS add_to_dir_list ( fcb * fcb , UINT8 level , LIST_ENTRY * dl , UINT64 newparinode , BOOL * empty ) {
KEY searchkey ;
traverse_ptr tp , next_tp ;
BOOL b ;
NTSTATUS Status ;
* empty = TRUE ;
searchkey . obj_id = fcb - > inode ;
searchkey . obj_type = TYPE_DIR_INDEX ;
searchkey . offset = 2 ;
Status = find_item ( fcb - > Vcb , fcb - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
do {
if ( tp . item - > key . obj_id = = fcb - > inode & & tp . item - > key . obj_type = = TYPE_DIR_INDEX ) {
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 {
DIR_ITEM * di = ( DIR_ITEM * ) tp . item - > data ;
struct _fcb * child ;
dir_list * dl2 ;
if ( tp . item - > size < sizeof ( DIR_ITEM ) - 1 + di - > n + di - > m ) {
ERR ( " (%llx,%x,%llx) was truncated \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
} else {
if ( di - > key . obj_type = = TYPE_INODE_ITEM | | di - > key . obj_type = = TYPE_ROOT_ITEM ) {
if ( di - > key . obj_type = = TYPE_ROOT_ITEM )
TRACE ( " moving subvol %llx \n " , di - > key . obj_id ) ;
else
TRACE ( " moving inode %llx \n " , di - > key . obj_id ) ;
* empty = FALSE ;
Status = get_fcb_from_dir_item ( fcb - > Vcb , & child , fcb , fcb - > subvol , di ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " get_fcb_from_dir_item returned %08x \n " , Status ) ;
free_traverse_ptr ( & tp ) ;
return Status ;
}
dl2 = ExAllocatePoolWithTag ( PagedPool , sizeof ( dir_list ) , ALLOC_TAG ) ;
if ( ! dl2 ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
dl2 - > fcb = child ;
dl2 - > level = level ;
dl2 - > newparinode = newparinode ;
dl2 - > subvol = di - > key . obj_type = = TYPE_ROOT_ITEM ;
dl2 - > utf8 . Length = dl2 - > utf8 . MaximumLength = di - > n ;
dl2 - > utf8 . Buffer = ExAllocatePoolWithTag ( PagedPool , dl2 - > utf8 . MaximumLength , ALLOC_TAG ) ;
if ( ! dl2 - > utf8 . Buffer ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( dl2 - > utf8 . Buffer , di - > name , dl2 - > utf8 . Length ) ;
dl2 - > crc32 = calc_crc32c ( 0xfffffffe , ( UINT8 * ) dl2 - > utf8 . Buffer , ( ULONG ) dl2 - > utf8 . Length ) ;
InsertTailList ( dl , & dl2 - > list_entry ) ;
}
}
}
}
b = find_next_item ( fcb - > 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 ) ;
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL move_across_subvols ( device_extension * Vcb , fcb * fcb , root * destsubvol , UINT64 destinode , PANSI_STRING utf8 , UINT32 crc32 , BTRFS_TIME * now , LIST_ENTRY * rollback ) {
UINT64 inode , oldparinode ;
NTSTATUS Status ;
LIST_ENTRY dl ;
if ( destsubvol - > lastinode = = 0 )
get_last_inode ( Vcb , destsubvol ) ;
inode = destsubvol - > lastinode + 1 ;
destsubvol - > lastinode + + ;
oldparinode = fcb - > subvol = = fcb - > par - > subvol ? fcb - > par - > inode : SUBVOL_ROOT_INODE ;
Status = move_inode_across_subvols ( Vcb , fcb , destsubvol , destinode , inode , oldparinode , utf8 , crc32 , now , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " move_inode_across_subvols returned %08x \n " , Status ) ;
return Status ;
}
if ( fcb - > type = = BTRFS_TYPE_DIRECTORY & & fcb - > inode_item . st_size > 0 ) {
BOOL b , empty ;
UINT8 level , max_level ;
LIST_ENTRY * le ;
InitializeListHead ( & dl ) ;
add_to_dir_list ( fcb , 0 , & dl , inode , & b ) ;
level = 0 ;
do {
empty = TRUE ;
le = dl . Flink ;
while ( le ! = & dl ) {
dir_list * dl2 = CONTAINING_RECORD ( le , dir_list , list_entry ) ;
if ( dl2 - > level = = level & & ! dl2 - > subvol ) {
inode + + ;
destsubvol - > lastinode + + ;
dl2 - > newinode = inode ;
if ( dl2 - > fcb - > type = = BTRFS_TYPE_DIRECTORY ) {
add_to_dir_list ( dl2 - > fcb , level + 1 , & dl , dl2 - > newinode , & b ) ;
if ( ! b ) empty = FALSE ;
}
}
le = le - > Flink ;
}
if ( ! empty ) level + + ;
} while ( ! empty ) ;
max_level = level ;
for ( level = 0 ; level < = max_level ; level + + ) {
TRACE ( " level %u \n " , level ) ;
le = dl . Flink ;
while ( le ! = & dl ) {
dir_list * dl2 = CONTAINING_RECORD ( le , dir_list , list_entry ) ;
if ( dl2 - > level = = level ) {
if ( dl2 - > subvol ) {
TRACE ( " subvol %llx \n " , dl2 - > fcb - > subvol - > id ) ;
Status = move_subvol ( Vcb , dl2 - > fcb , destsubvol , dl2 - > newparinode , & dl2 - > utf8 , dl2 - > crc32 , dl2 - > crc32 , now , FALSE , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " move_subvol returned %08x \n " , Status ) ;
return Status ;
}
} else {
TRACE ( " inode %llx \n " , dl2 - > fcb - > inode ) ;
Status = move_inode_across_subvols ( Vcb , dl2 - > fcb , destsubvol , dl2 - > newparinode , dl2 - > newinode , dl2 - > fcb - > par - > inode , & dl2 - > utf8 , dl2 - > crc32 , now , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " move_inode_across_subvols returned %08x \n " , Status ) ;
return Status ;
}
}
}
le = le - > Flink ;
}
}
while ( ! IsListEmpty ( & dl ) ) {
dir_list * dl2 ;
le = RemoveHeadList ( & dl ) ;
dl2 = CONTAINING_RECORD ( le , dir_list , list_entry ) ;
ExFreePool ( dl2 - > utf8 . Buffer ) ;
free_fcb ( dl2 - > fcb ) ;
ExFreePool ( dl2 ) ;
}
}
fcb - > inode = inode ;
fcb - > subvol = destsubvol ;
fcb - > subvol - > root_item . ctransid = Vcb - > superblock . generation ;
fcb - > subvol - > root_item . ctime = * now ;
return STATUS_SUCCESS ;
}
static NTSTATUS delete_root_ref ( device_extension * Vcb , UINT64 subvolid , UINT64 parsubvolid , UINT64 parinode , PANSI_STRING utf8 , UINT64 * index , LIST_ENTRY * rollback ) {
KEY searchkey ;
traverse_ptr tp ;
NTSTATUS Status ;
searchkey . obj_id = parsubvolid ;
searchkey . obj_type = TYPE_ROOT_REF ;
searchkey . offset = subvolid ;
Status = find_item ( Vcb , Vcb - > root_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 ( ROOT_REF ) ) {
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 ( ROOT_REF ) ) ;
} else {
ROOT_REF * rr ;
ULONG len ;
rr = ( ROOT_REF * ) tp . item - > data ;
len = tp . item - > size ;
do {
ULONG itemlen ;
if ( len < sizeof ( ROOT_REF ) | | len < sizeof ( ROOT_REF ) - 1 + rr - > 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 ( ROOT_REF ) - sizeof ( char ) + rr - > n ;
if ( rr - > dir = = parinode & & rr - > n = = utf8 - > Length & & RtlCompareMemory ( rr - > name , utf8 - > Buffer , rr - > n ) = = rr - > n ) {
ULONG newlen = tp . item - > size - itemlen ;
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 * newrr = ExAllocatePoolWithTag ( PagedPool , newlen , ALLOC_TAG ) , * rroff ;
if ( ! newrr ) {
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 * ) rr > tp . item - > data ) {
RtlCopyMemory ( newrr , tp . item - > data , ( UINT8 * ) rr - tp . item - > data ) ;
rroff = newrr + ( ( UINT8 * ) rr - tp . item - > data ) ;
} else {
rroff = newrr ;
}
if ( ( UINT8 * ) & rr - > name [ rr - > n ] - tp . item - > data < tp . item - > size )
RtlCopyMemory ( rroff , & rr - > name [ rr - > n ] , tp . item - > size - ( ( UINT8 * ) & rr - > name [ rr - > n ] - tp . item - > data ) ) ;
insert_tree_item ( Vcb , Vcb - > root_root , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset , newrr , newlen , NULL , rollback ) ;
}
if ( index )
* index = rr - > index ;
break ;
}
if ( len > itemlen ) {
len - = itemlen ;
rr = ( ROOT_REF * ) & rr - > name [ rr - > n ] ;
} else
break ;
} while ( len > 0 ) ;
}
} else {
WARN ( " could not find ROOT_REF entry for subvol %llx in %llx \n " , searchkey . offset , searchkey . obj_id ) ;
}
free_traverse_ptr ( & tp ) ;
return STATUS_SUCCESS ;
}
static NTSTATUS add_root_ref ( device_extension * Vcb , UINT64 subvolid , UINT64 parsubvolid , ROOT_REF * rr , LIST_ENTRY * rollback ) {
KEY searchkey ;
traverse_ptr tp ;
NTSTATUS Status ;
searchkey . obj_id = parsubvolid ;
searchkey . obj_type = TYPE_ROOT_REF ;
searchkey . offset = subvolid ;
Status = find_item ( Vcb , Vcb - > root_root , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & searchkey , & tp . item - > key ) ) {
ULONG rrsize = tp . item - > size + sizeof ( ROOT_REF ) - 1 + rr - > n ;
UINT8 * rr2 ;
rr2 = ExAllocatePoolWithTag ( PagedPool , rrsize , ALLOC_TAG ) ;
if ( ! rr2 ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
if ( tp . item - > size > 0 )
RtlCopyMemory ( rr2 , tp . item - > data , tp . item - > size ) ;
RtlCopyMemory ( rr2 + tp . item - > size , rr , sizeof ( ROOT_REF ) - 1 + rr - > n ) ;
ExFreePool ( rr ) ;
delete_tree_item ( Vcb , & tp , rollback ) ;
if ( ! insert_tree_item ( Vcb , Vcb - > root_root , searchkey . obj_id , searchkey . obj_type , searchkey . offset , rr2 , rrsize , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
ExFreePool ( rr2 ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
} else {
if ( ! insert_tree_item ( Vcb , Vcb - > root_root , searchkey . obj_id , searchkey . obj_type , searchkey . offset , rr , sizeof ( ROOT_REF ) - 1 + rr - > n , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
ExFreePool ( rr ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
}
free_traverse_ptr ( & tp ) ;
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL update_root_backref ( device_extension * Vcb , UINT64 subvolid , UINT64 parsubvolid , LIST_ENTRY * rollback ) {
KEY searchkey ;
traverse_ptr tp ;
UINT8 * data ;
ULONG datalen ;
NTSTATUS Status ;
searchkey . obj_id = parsubvolid ;
searchkey . obj_type = TYPE_ROOT_REF ;
searchkey . offset = subvolid ;
Status = find_item ( Vcb , Vcb - > root_root , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & tp . item - > key , & searchkey ) & & tp . item - > size > 0 ) {
datalen = tp . item - > size ;
data = ExAllocatePoolWithTag ( PagedPool , datalen , ALLOC_TAG ) ;
if ( ! data ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( data , tp . item - > data , datalen ) ;
} else {
datalen = 0 ;
}
free_traverse_ptr ( & tp ) ;
searchkey . obj_id = subvolid ;
searchkey . obj_type = TYPE_ROOT_BACKREF ;
searchkey . offset = parsubvolid ;
Status = find_item ( Vcb , Vcb - > root_root , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & tp . item - > key , & searchkey ) )
delete_tree_item ( Vcb , & tp , rollback ) ;
free_traverse_ptr ( & tp ) ;
if ( datalen > 0 ) {
if ( ! insert_tree_item ( Vcb , Vcb - > root_root , subvolid , TYPE_ROOT_BACKREF , parsubvolid , data , datalen , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
ExFreePool ( data ) ;
return STATUS_INTERNAL_ERROR ;
}
}
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL move_subvol ( device_extension * Vcb , fcb * fcb , root * destsubvol , UINT64 destinode , PANSI_STRING utf8 , UINT32 crc32 , UINT32 oldcrc32 , BTRFS_TIME * now , BOOL ReplaceIfExists , LIST_ENTRY * rollback ) {
DIR_ITEM * di ;
NTSTATUS Status ;
KEY searchkey ;
traverse_ptr tp ;
UINT64 oldindex , index ;
ROOT_REF * rr ;
// delete old DIR_ITEM
Status = delete_dir_item ( Vcb , fcb - > par - > subvol , fcb - > par - > inode , oldcrc32 , & fcb - > utf8 , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " delete_dir_item returned %08x \n " , Status ) ;
return Status ;
}
// create new DIR_ITEM
di = ExAllocatePoolWithTag ( PagedPool , sizeof ( DIR_ITEM ) - 1 + utf8 - > Length , ALLOC_TAG ) ;
if ( ! di ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
di - > key . obj_id = fcb - > subvol - > id ;
di - > key . obj_type = TYPE_ROOT_ITEM ;
di - > key . offset = 0 ;
di - > transid = Vcb - > superblock . generation ;
di - > m = 0 ;
di - > n = utf8 - > Length ;
di - > type = fcb - > type ;
RtlCopyMemory ( di - > name , utf8 - > Buffer , utf8 - > Length ) ;
Status = add_dir_item ( Vcb , destsubvol , destinode , crc32 , di , sizeof ( DIR_ITEM ) - 1 + utf8 - > Length , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " add_dir_item returned %08x \n " , Status ) ;
return Status ;
}
// delete old ROOT_REF
oldindex = 0 ;
Status = delete_root_ref ( Vcb , fcb - > subvol - > id , fcb - > par - > subvol - > id , fcb - > par - > inode , & fcb - > utf8 , & oldindex , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " delete_root_ref returned %08x \n " , Status ) ;
return Status ;
}
TRACE ( " root index = %llx \n " , oldindex ) ;
// delete old DIR_INDEX
if ( oldindex ! = 0 ) {
searchkey . obj_id = fcb - > par - > inode ;
searchkey . obj_type = TYPE_DIR_INDEX ;
searchkey . offset = oldindex ;
Status = find_item ( Vcb , fcb - > par - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & searchkey , & tp . item - > key ) ) {
TRACE ( " deleting (%llx,%x,%llx) \n " , tp . item - > key . obj_id , tp . item - > key . obj_type , tp . item - > key . offset ) ;
delete_tree_item ( Vcb , & tp , rollback ) ;
} else {
WARN ( " could not find old DIR_INDEX entry \n " ) ;
}
free_traverse_ptr ( & tp ) ;
}
// create new DIR_INDEX
if ( fcb - > par - > subvol = = destsubvol & & fcb - > par - > inode = = destinode ) {
index = oldindex ;
} else {
index = find_next_dir_index ( Vcb , destsubvol , destinode ) ;
}
di = ExAllocatePoolWithTag ( PagedPool , sizeof ( DIR_ITEM ) - 1 + utf8 - > Length , ALLOC_TAG ) ;
if ( ! di ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
di - > key . obj_id = fcb - > subvol - > id ;
di - > key . obj_type = TYPE_ROOT_ITEM ;
di - > key . offset = 0 ;
di - > transid = Vcb - > superblock . generation ;
di - > m = 0 ;
di - > n = utf8 - > Length ;
di - > type = fcb - > type ;
RtlCopyMemory ( di - > name , utf8 - > Buffer , utf8 - > Length ) ;
if ( ! insert_tree_item ( Vcb , destsubvol , destinode , TYPE_DIR_INDEX , index , di , sizeof ( DIR_ITEM ) - 1 + utf8 - > Length , NULL , rollback ) ) {
ERR ( " error - failed to insert item \n " ) ;
return STATUS_INTERNAL_ERROR ;
}
// create new ROOT_REF
rr = ExAllocatePoolWithTag ( PagedPool , sizeof ( ROOT_REF ) - 1 + utf8 - > Length , ALLOC_TAG ) ;
if ( ! rr ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
rr - > dir = destinode ;
rr - > index = index ;
rr - > n = utf8 - > Length ;
RtlCopyMemory ( rr - > name , utf8 - > Buffer , utf8 - > Length ) ;
Status = add_root_ref ( Vcb , fcb - > subvol - > id , destsubvol - > id , rr , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " add_root_ref returned %08x \n " , Status ) ;
return Status ;
}
Status = update_root_backref ( Vcb , fcb - > subvol - > id , fcb - > par - > subvol - > id , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " update_root_backref 1 returned %08x \n " , Status ) ;
return Status ;
}
if ( fcb - > par - > subvol ! = destsubvol ) {
Status = update_root_backref ( Vcb , fcb - > subvol - > id , destsubvol - > id , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " update_root_backref 1 returned %08x \n " , Status ) ;
return Status ;
}
fcb - > par - > subvol - > root_item . ctransid = Vcb - > superblock . generation ;
fcb - > par - > subvol - > root_item . ctime = * now ;
}
destsubvol - > root_item . ctransid = Vcb - > superblock . generation ;
destsubvol - > root_item . ctime = * now ;
return STATUS_SUCCESS ;
}
static BOOL has_open_children ( fcb * fcb ) {
LIST_ENTRY * le = fcb - > children . Flink ;
struct _fcb * c ;
while ( le ! = & fcb - > children ) {
c = CONTAINING_RECORD ( le , struct _fcb , list_entry ) ;
if ( c - > refcount > 0 ) {
if ( c - > open_count > 0 )
return TRUE ;
if ( has_open_children ( c ) )
return TRUE ;
}
le = le - > Flink ;
}
return FALSE ;
}
static NTSTATUS STDCALL set_rename_information ( device_extension * Vcb , PIRP Irp , PFILE_OBJECT FileObject , PFILE_OBJECT tfo , BOOL ReplaceIfExists , LIST_ENTRY * rollback ) {
FILE_RENAME_INFORMATION * fri = Irp - > AssociatedIrp . SystemBuffer ;
fcb * fcb = FileObject - > FsContext , * tfofcb , * oldparfcb , * oldfcb ;
root * parsubvol ;
UINT64 parinode , dirpos ;
WCHAR * fn ;
UNICODE_STRING fnus ;
ULONG fnlen , utf8len , disize ;
NTSTATUS Status ;
ANSI_STRING utf8 ;
UINT32 crc32 , oldcrc32 ;
KEY searchkey ;
traverse_ptr tp , next_tp ;
DIR_ITEM * di ;
LARGE_INTEGER time ;
BTRFS_TIME now ;
BOOL across_directories ;
INODE_ITEM * ii ;
// FIXME - MSDN says we should be able to rename streams here, but I can't get it to work.
TRACE ( " tfo = %p \n " , tfo ) ;
TRACE ( " ReplaceIfExists = %u \n " , ReplaceIfExists ) ;
TRACE ( " RootDirectory = %p \n " , fri - > RootDirectory ) ;
TRACE ( " FileName = %.*S \n " , fri - > FileNameLength / sizeof ( WCHAR ) , fri - > FileName ) ;
KeQuerySystemTime ( & time ) ;
win_time_to_unix ( time , & now ) ;
utf8 . Buffer = NULL ;
if ( ! fcb - > par ) {
ERR ( " error - tried to rename file with no parent \n " ) ;
Status = STATUS_ACCESS_DENIED ;
goto end ;
}
fn = fri - > FileName ;
fnlen = fri - > FileNameLength / sizeof ( WCHAR ) ;
if ( ! tfo ) {
parsubvol = fcb - > par - > subvol ;
parinode = fcb - > par - > inode ;
tfofcb = NULL ;
across_directories = FALSE ;
} else {
LONG i ;
tfofcb = tfo - > FsContext ;
parsubvol = tfofcb - > subvol ;
parinode = tfofcb - > inode ;
for ( i = fnlen - 1 ; i > = 0 ; i - - ) {
if ( fri - > FileName [ i ] = = ' \\ ' | | fri - > FileName [ i ] = = ' / ' ) {
fn = & fri - > FileName [ i + 1 ] ;
fnlen = ( fri - > FileNameLength / sizeof ( WCHAR ) ) - i - 1 ;
break ;
}
}
across_directories = parsubvol ! = fcb - > par - > subvol | | parinode ! = fcb - > par - > inode ;
}
fnus . Buffer = fn ;
fnus . Length = fnus . MaximumLength = fnlen * sizeof ( WCHAR ) ;
TRACE ( " fnus = %.*S \n " , fnus . Length / sizeof ( WCHAR ) , fnus . Buffer ) ;
Status = RtlUnicodeToUTF8N ( NULL , 0 , & utf8len , fn , ( ULONG ) fnlen * sizeof ( WCHAR ) ) ;
if ( ! NT_SUCCESS ( Status ) )
goto end ;
utf8 . MaximumLength = utf8 . Length = utf8len ;
utf8 . Buffer = ExAllocatePoolWithTag ( PagedPool , utf8 . MaximumLength , ALLOC_TAG ) ;
if ( ! utf8 . Buffer ) {
ERR ( " out of memory \n " ) ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto end ;
}
Status = RtlUnicodeToUTF8N ( utf8 . Buffer , utf8len , & utf8len , fn , ( ULONG ) fnlen * sizeof ( WCHAR ) ) ;
if ( ! NT_SUCCESS ( Status ) )
goto end ;
crc32 = calc_crc32c ( 0xfffffffe , ( UINT8 * ) utf8 . Buffer , ( ULONG ) utf8 . Length ) ;
// FIXME - set to crc32 if utf8 and oldutf8 are identical
oldcrc32 = calc_crc32c ( 0xfffffffe , ( UINT8 * ) fcb - > utf8 . Buffer , ( ULONG ) fcb - > utf8 . Length ) ;
// TRACE("utf8 fn = %s (%08x), old utf8 fn = %s (%08x)\n", utf8, crc32, oldutf8, oldcrc32);
oldfcb = NULL ;
Status = get_fcb ( Vcb , & oldfcb , & fnus , tfo ? tfo - > FsContext : NULL , FALSE ) ;
if ( NT_SUCCESS ( Status ) ) {
WARN ( " destination file %.*S already exists \n " , oldfcb - > full_filename . Length / sizeof ( WCHAR ) , oldfcb - > full_filename . Buffer ) ;
if ( fcb ! = oldfcb & & ! ( oldfcb - > open_count = = 0 & & oldfcb - > deleted ) ) {
if ( ! ReplaceIfExists ) {
Status = STATUS_OBJECT_NAME_COLLISION ;
goto end ;
} else if ( oldfcb - > open_count > = 1 & & ! oldfcb - > deleted ) {
WARN ( " trying to overwrite open file \n " ) ;
Status = STATUS_ACCESS_DENIED ;
goto end ;
}
if ( oldfcb - > type = = BTRFS_TYPE_DIRECTORY ) {
WARN ( " trying to overwrite directory \n " ) ;
Status = STATUS_ACCESS_DENIED ;
goto end ;
}
}
}
if ( has_open_children ( fcb ) ) {
WARN ( " trying to rename file with open children \n " ) ;
Status = STATUS_ACCESS_DENIED ;
goto end ;
}
if ( oldfcb ) {
Status = delete_fcb ( oldfcb , NULL , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " delete_fcb returned %08x \n " , Status ) ;
goto end ;
}
}
if ( fcb - > inode = = SUBVOL_ROOT_INODE ) {
UNICODE_STRING filename ;
filename . Buffer = fn ;
filename . MaximumLength = filename . Length = fnlen * sizeof ( WCHAR ) ;
Status = move_subvol ( Vcb , fcb , tfofcb - > subvol , tfofcb - > inode , & utf8 , crc32 , oldcrc32 , & now , ReplaceIfExists , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " move_subvol returned %08x \n " , Status ) ;
goto end ;
}
} else if ( parsubvol ! = fcb - > subvol ) {
UNICODE_STRING filename ;
filename . Buffer = fn ;
filename . MaximumLength = filename . Length = fnlen * sizeof ( WCHAR ) ;
Status = move_across_subvols ( Vcb , fcb , tfofcb - > subvol , tfofcb - > inode , & utf8 , crc32 , & now , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " move_across_subvols returned %08x \n " , Status ) ;
goto end ;
}
} else {
UINT64 oldindex ;
INODE_ITEM * ii ;
// delete old DIR_ITEM entry
Status = delete_dir_item ( Vcb , fcb - > subvol , fcb - > par - > inode , oldcrc32 , & fcb - > utf8 , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " delete_dir_item returned %08x \n " , Status ) ;
return Status ;
}
// FIXME - make sure fcb's filepart matches the case on disk
// create new DIR_ITEM entry
di = ExAllocatePoolWithTag ( PagedPool , sizeof ( DIR_ITEM ) - 1 + utf8 . Length , ALLOC_TAG ) ;
if ( ! di ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
di - > key . obj_id = fcb - > inode ;
di - > key . obj_type = TYPE_INODE_ITEM ;
di - > key . offset = 0 ;
di - > transid = Vcb - > superblock . generation ;
di - > m = 0 ;
di - > n = utf8 . Length ;
di - > type = fcb - > type ;
RtlCopyMemory ( di - > name , utf8 . Buffer , utf8 . Length ) ;
Status = add_dir_item ( Vcb , parsubvol , parinode , crc32 , di , sizeof ( DIR_ITEM ) - 1 + utf8 . Length , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " add_dir_item returned %08x \n " , Status ) ;
return Status ;
}
oldindex = 0 ;
Status = delete_inode_ref ( Vcb , fcb - > subvol , fcb - > inode , fcb - > par - > inode , & fcb - > utf8 , & oldindex , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " delete_inode_ref returned %08x \n " , Status ) ;
return Status ;
}
// delete old DIR_INDEX entry
if ( oldindex ! = 0 ) {
searchkey . obj_id = fcb - > par - > inode ;
searchkey . obj_type = TYPE_DIR_INDEX ;
searchkey . offset = oldindex ;
Status = find_item ( Vcb , fcb - > par - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
goto end ;
}
if ( ! keycmp ( & tp . item - > key , & searchkey ) )
delete_tree_item ( Vcb , & tp , rollback ) ;
else {
WARN ( " couldn't find DIR_INDEX \n " ) ;
}
free_traverse_ptr ( & tp ) ;
} else {
WARN ( " couldn't get index from INODE_REF \n " ) ;
}
// create new DIR_INDEX entry
if ( parsubvol ! = fcb - > par - > subvol | | parinode ! = fcb - > par - > inode ) {
searchkey . obj_id = parinode ;
searchkey . obj_type = TYPE_DIR_INDEX + 1 ;
searchkey . offset = 0 ;
Status = find_item ( Vcb , parsubvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
goto end ;
}
dirpos = 2 ;
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 = = searchkey . obj_id & & tp . item - > key . obj_type = = TYPE_DIR_INDEX ) {
dirpos = tp . item - > key . offset + 1 ;
break ;
}
if ( find_prev_item ( Vcb , & tp , & next_tp , FALSE ) ) {
free_traverse_ptr ( & tp ) ;
tp = next_tp ;
} else
break ;
} while ( tp . item - > key . obj_id > = parinode & & tp . item - > key . obj_type > = TYPE_DIR_INDEX ) ;
free_traverse_ptr ( & tp ) ;
} else
dirpos = oldindex ;
disize = ( ULONG ) ( sizeof ( DIR_ITEM ) - 1 + utf8 . Length ) ;
di = ExAllocatePoolWithTag ( PagedPool , disize , ALLOC_TAG ) ;
if ( ! di ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
di - > key . obj_id = fcb - > inode ;
di - > key . obj_type = TYPE_INODE_ITEM ;
di - > key . offset = 0 ;
di - > transid = Vcb - > superblock . generation ;
di - > m = 0 ;
di - > n = ( UINT16 ) utf8 . Length ;
di - > type = fcb - > type ;
RtlCopyMemory ( di - > name , utf8 . Buffer , utf8 . Length ) ;
if ( ! insert_tree_item ( Vcb , parsubvol , parinode , TYPE_DIR_INDEX , dirpos , di , disize , NULL , rollback ) )
ERR ( " error - failed to insert item \n " ) ;
// create new INODE_REF entry
Status = add_inode_ref ( Vcb , parsubvol , fcb - > inode , parinode , dirpos , & utf8 , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " add_inode_ref returned %08x \n " , Status ) ;
return Status ;
}
fcb - > inode_item . transid = Vcb - > superblock . generation ;
fcb - > inode_item . sequence + + ;
fcb - > inode_item . st_ctime = now ;
searchkey . obj_id = fcb - > inode ;
searchkey . obj_type = TYPE_INODE_ITEM ;
searchkey . offset = 0xffffffffffffffff ;
Status = find_item ( Vcb , parsubvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
goto end ;
}
if ( tp . item - > key . obj_id = = searchkey . obj_id & & tp . item - > key . obj_type = = searchkey . obj_type )
delete_tree_item ( Vcb , & tp , rollback ) ;
free_traverse_ptr ( & tp ) ;
ii = ExAllocatePoolWithTag ( PagedPool , sizeof ( INODE_ITEM ) , ALLOC_TAG ) ;
if ( ! ii ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( ii , & fcb - > inode_item , sizeof ( INODE_ITEM ) ) ;
if ( ! insert_tree_item ( Vcb , parsubvol , fcb - > inode , TYPE_INODE_ITEM , 0 , ii , sizeof ( INODE_ITEM ) , NULL , rollback ) ) {
WARN ( " insert_tree_item failed \n " ) ;
}
}
// update directory INODE_ITEMs
fcb - > par - > inode_item . transid = Vcb - > superblock . generation ;
fcb - > par - > inode_item . sequence + + ;
fcb - > par - > inode_item . st_ctime = now ;
fcb - > par - > inode_item . st_mtime = now ;
TRACE ( " fcb->par->inode_item.st_size was %llx \n " , fcb - > par - > inode_item . st_size ) ;
if ( ! tfofcb | | ( fcb - > par - > inode = = tfofcb - > inode & & fcb - > par - > subvol = = tfofcb - > subvol ) ) {
fcb - > par - > inode_item . st_size + = 2 * ( utf8 . Length - fcb - > utf8 . Length ) ;
} else {
fcb - > par - > inode_item . st_size - = 2 * fcb - > utf8 . Length ;
TRACE ( " tfofcb->inode_item.st_size was %llx \n " , tfofcb - > inode_item . st_size ) ;
tfofcb - > inode_item . st_size + = 2 * utf8 . Length ;
TRACE ( " tfofcb->inode_item.st_size now %llx \n " , tfofcb - > inode_item . st_size ) ;
tfofcb - > inode_item . transid = Vcb - > superblock . generation ;
tfofcb - > inode_item . sequence + + ;
tfofcb - > inode_item . st_ctime = now ;
tfofcb - > inode_item . st_mtime = now ;
}
TRACE ( " fcb->par->inode_item.st_size now %llx \n " , fcb - > par - > inode_item . st_size ) ;
if ( oldfcb & & oldfcb - > par ! = fcb - > par ) {
TRACE ( " oldfcb->par->inode_item.st_size was %llx \n " , oldfcb - > par - > inode_item . st_size ) ;
oldfcb - > par - > inode_item . st_size - = 2 * oldfcb - > utf8 . Length ;
TRACE ( " oldfcb->par->inode_item.st_size now %llx \n " , oldfcb - > par - > inode_item . st_size ) ;
}
searchkey . obj_id = fcb - > par - > inode ;
searchkey . obj_type = TYPE_INODE_ITEM ;
searchkey . offset = 0xffffffffffffffff ;
Status = find_item ( Vcb , fcb - > par - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( tp . item - > key . obj_id = = searchkey . obj_id & & tp . item - > key . obj_type = = searchkey . obj_type )
delete_tree_item ( Vcb , & tp , rollback ) ;
ii = ExAllocatePoolWithTag ( PagedPool , sizeof ( INODE_ITEM ) , ALLOC_TAG ) ;
if ( ! ii ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( ii , & fcb - > par - > inode_item , sizeof ( INODE_ITEM ) ) ;
if ( ! insert_tree_item ( Vcb , fcb - > par - > subvol , fcb - > par - > inode , TYPE_INODE_ITEM , 0 , ii , sizeof ( INODE_ITEM ) , NULL , rollback ) )
WARN ( " insert_tree_item failed \n " ) ;
free_traverse_ptr ( & tp ) ;
if ( tfofcb & & ( fcb - > par - > inode ! = tfofcb - > inode | | fcb - > par - > subvol ! = tfofcb - > subvol ) ) {
searchkey . obj_id = tfofcb - > inode ;
searchkey . obj_type = TYPE_INODE_ITEM ;
searchkey . offset = 0xffffffffffffffff ;
Status = find_item ( Vcb , tfofcb - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( tp . item - > key . obj_id = = searchkey . obj_id & & tp . item - > key . obj_type = = searchkey . obj_type )
delete_tree_item ( Vcb , & tp , rollback ) ;
ii = ExAllocatePoolWithTag ( PagedPool , sizeof ( INODE_ITEM ) , ALLOC_TAG ) ;
if ( ! ii ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( ii , & tfofcb - > inode_item , sizeof ( INODE_ITEM ) ) ;
if ( ! insert_tree_item ( Vcb , tfofcb - > subvol , tfofcb - > inode , TYPE_INODE_ITEM , 0 , ii , sizeof ( INODE_ITEM ) , NULL , rollback ) )
WARN ( " insert_tree_item failed \n " ) ;
free_traverse_ptr ( & tp ) ;
}
fcb - > subvol - > root_item . ctransid = Vcb - > superblock . generation ;
fcb - > subvol - > root_item . ctime = now ;
// FIXME - handle overwrite by rename here
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 ,
across_directories ? FILE_ACTION_REMOVED : FILE_ACTION_RENAMED_OLD_NAME , NULL ) ;
// FIXME - change full_filename and name_offset of open children
if ( fnlen ! = fcb - > filepart . Length / sizeof ( WCHAR ) | | RtlCompareMemory ( fn , fcb - > filepart . Buffer , fcb - > filepart . Length ) ! = fcb - > filepart . Length ) {
RtlFreeUnicodeString ( & fcb - > filepart ) ;
fcb - > filepart . Length = fcb - > filepart . MaximumLength = ( USHORT ) ( fnlen * sizeof ( WCHAR ) ) ;
fcb - > filepart . Buffer = ExAllocatePoolWithTag ( PagedPool , fcb - > filepart . Length , ALLOC_TAG ) ;
if ( ! fcb - > filepart . Buffer ) {
ERR ( " out of memory \n " ) ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto end ;
}
RtlCopyMemory ( fcb - > filepart . Buffer , fn , fcb - > filepart . Length ) ;
}
if ( tfo & & tfofcb ! = fcb - > par ) {
oldparfcb = fcb - > par ;
fcb - > par = tfofcb ;
fcb - > par - > refcount + + ;
RemoveEntryList ( & fcb - > list_entry ) ;
InsertTailList ( & fcb - > par - > children , & fcb - > list_entry ) ;
# ifdef DEBUG_FCB_REFCOUNTS
WARN ( " fcb %p: refcount now %i (%.*S) \n " , fcb - > par , fcb - > par - > refcount , fcb - > par - > full_filename . Length / sizeof ( WCHAR ) , fcb - > par - > full_filename . Buffer ) ;
# endif
free_fcb ( oldparfcb ) ;
}
ExFreePool ( fcb - > utf8 . Buffer ) ;
fcb - > utf8 = utf8 ;
utf8 . Buffer = NULL ;
// change fcb->full_filename
fcb - > full_filename . MaximumLength = fcb - > par - > full_filename . Length + fcb - > filepart . Length ;
if ( fcb - > par - > par ) fcb - > full_filename . MaximumLength + = sizeof ( WCHAR ) ;
ExFreePool ( fcb - > full_filename . Buffer ) ;
fcb - > full_filename . Buffer = ExAllocatePoolWithTag ( PagedPool , fcb - > full_filename . MaximumLength , ALLOC_TAG ) ;
if ( ! fcb - > full_filename . Buffer ) {
ERR ( " out of memory \n " ) ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto end ;
}
RtlCopyMemory ( fcb - > full_filename . Buffer , fcb - > par - > full_filename . Buffer , fcb - > par - > full_filename . Length ) ;
fcb - > full_filename . Length = fcb - > par - > full_filename . Length ;
if ( fcb - > par - > par ) {
fcb - > full_filename . Buffer [ fcb - > full_filename . Length / sizeof ( WCHAR ) ] = ' \\ ' ;
fcb - > full_filename . Length + = sizeof ( WCHAR ) ;
}
fcb - > name_offset = fcb - > full_filename . Length / sizeof ( WCHAR ) ;
RtlAppendUnicodeStringToString ( & fcb - > full_filename , & fcb - > filepart ) ;
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 ,
across_directories ? FILE_ACTION_ADDED : FILE_ACTION_RENAMED_NEW_NAME , NULL ) ;
Status = STATUS_SUCCESS ;
end :
if ( utf8 . Buffer )
ExFreePool ( utf8 . Buffer ) ;
if ( oldfcb )
free_fcb ( oldfcb ) ;
return Status ;
}
static NTSTATUS STDCALL stream_set_end_of_file_information ( device_extension * Vcb , PIRP Irp , PFILE_OBJECT FileObject , BOOL advance_only , LIST_ENTRY * rollback ) {
FILE_END_OF_FILE_INFORMATION * feofi = Irp - > AssociatedIrp . SystemBuffer ;
fcb * fcb = FileObject - > FsContext ;
LARGE_INTEGER time ;
BTRFS_TIME now ;
KEY searchkey ;
traverse_ptr tp ;
INODE_ITEM * ii ;
CC_FILE_SIZES ccfs ;
UINT8 * data = NULL ;
UINT16 datalen ;
NTSTATUS Status ;
TRACE ( " setting new end to %llx bytes (currently %x) \n " , feofi - > EndOfFile . QuadPart , fcb - > adssize ) ;
if ( feofi - > EndOfFile . QuadPart < fcb - > adssize ) {
if ( advance_only )
return STATUS_SUCCESS ;
TRACE ( " truncating stream to %llx bytes \n " , feofi - > EndOfFile . QuadPart ) ;
if ( feofi - > EndOfFile . QuadPart > 0 ) {
if ( ! get_xattr ( Vcb , fcb - > subvol , fcb - > inode , fcb - > adsxattr . Buffer , fcb - > adshash , & data , & datalen ) ) {
ERR ( " get_xattr failed \n " ) ;
return STATUS_INTERNAL_ERROR ;
}
}
Status = set_xattr ( Vcb , fcb - > subvol , fcb - > inode , fcb - > adsxattr . Buffer , fcb - > adshash , data , feofi - > EndOfFile . QuadPart , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " set_xattr returned %08x \n " , Status ) ;
return Status ;
}
fcb - > adssize = feofi - > EndOfFile . QuadPart ;
if ( data )
ExFreePool ( data ) ;
} else if ( feofi - > EndOfFile . QuadPart > fcb - > adssize ) {
UINT16 maxlen ;
UINT8 * data2 ;
TRACE ( " extending stream to %llx bytes \n " , feofi - > EndOfFile . QuadPart ) ;
// find maximum length of xattr
maxlen = Vcb - > superblock . node_size - sizeof ( tree_header ) - sizeof ( leaf_node ) ;
searchkey . obj_id = fcb - > inode ;
searchkey . obj_type = TYPE_XATTR_ITEM ;
searchkey . offset = fcb - > adshash ;
Status = find_item ( fcb - > Vcb , fcb - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
if ( keycmp ( & tp . item - > key , & searchkey ) ) {
ERR ( " error - could not find key for xattr \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
2016-03-23 21:24:28 +00:00
# ifdef __REACTOS__
// BUGBUG: FIXME!!
ERR ( " BUGBUG: datalen is uninitialized! Set it to zero temporarily... \n " ) ;
datalen = 0 ;
# endif
2016-03-23 20:35:05 +00:00
if ( tp . item - > size < datalen ) {
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 , datalen ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INTERNAL_ERROR ;
}
maxlen - = tp . item - > size - datalen ; // subtract XATTR_ITEM overhead
free_traverse_ptr ( & tp ) ;
if ( feofi - > EndOfFile . QuadPart > maxlen ) {
ERR ( " error - xattr too long (%llu > %u) \n " , feofi - > EndOfFile . QuadPart , maxlen ) ;
return STATUS_DISK_FULL ;
}
if ( ! get_xattr ( Vcb , fcb - > subvol , fcb - > inode , fcb - > adsxattr . Buffer , fcb - > adshash , & data , & datalen ) ) {
ERR ( " get_xattr failed \n " ) ;
return STATUS_INTERNAL_ERROR ;
}
data2 = ExAllocatePoolWithTag ( PagedPool , feofi - > EndOfFile . QuadPart , ALLOC_TAG ) ;
if ( ! data2 ) {
ERR ( " out of memory \n " ) ;
ExFreePool ( data ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( data2 , data , datalen ) ;
ExFreePool ( data ) ;
RtlZeroMemory ( & data2 [ datalen ] , feofi - > EndOfFile . QuadPart - datalen ) ;
Status = set_xattr ( Vcb , fcb - > subvol , fcb - > inode , fcb - > adsxattr . Buffer , fcb - > adshash , data2 , feofi - > EndOfFile . QuadPart , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " set_xattr returned %08x \n " , Status ) ;
return Status ;
}
fcb - > adssize = feofi - > EndOfFile . QuadPart ;
ExFreePool ( data2 ) ;
}
ccfs . AllocationSize = fcb - > Header . AllocationSize ;
ccfs . FileSize = fcb - > Header . FileSize ;
ccfs . ValidDataLength = fcb - > Header . ValidDataLength ;
CcSetFileSizes ( FileObject , & ccfs ) ;
KeQuerySystemTime ( & time ) ;
win_time_to_unix ( time , & now ) ;
fcb - > par - > inode_item . transid = Vcb - > superblock . generation ;
fcb - > par - > inode_item . sequence + + ;
fcb - > par - > inode_item . st_ctime = now ;
searchkey . obj_id = fcb - > inode ;
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_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & tp . item - > key , & searchkey ) )
delete_tree_item ( Vcb , & tp , rollback ) ;
else
WARN ( " couldn't find existing INODE_ITEM \n " ) ;
ii = ExAllocatePoolWithTag ( PagedPool , sizeof ( INODE_ITEM ) , ALLOC_TAG ) ;
if ( ! ii ) {
ERR ( " out of memory \n " ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( ii , & fcb - > par - > inode_item , sizeof ( INODE_ITEM ) ) ;
insert_tree_item ( Vcb , fcb - > subvol , fcb - > inode , TYPE_INODE_ITEM , 0 , ii , sizeof ( INODE_ITEM ) , NULL , rollback ) ;
free_traverse_ptr ( & tp ) ;
fcb - > par - > subvol - > root_item . ctransid = Vcb - > superblock . generation ;
fcb - > par - > subvol - > root_item . ctime = now ;
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL set_end_of_file_information ( device_extension * Vcb , PIRP Irp , PFILE_OBJECT FileObject , BOOL advance_only , LIST_ENTRY * rollback ) {
FILE_END_OF_FILE_INFORMATION * feofi = Irp - > AssociatedIrp . SystemBuffer ;
fcb * fcb = FileObject - > FsContext ;
NTSTATUS Status ;
LARGE_INTEGER time ;
KEY searchkey ;
traverse_ptr tp ;
INODE_ITEM * ii ;
CC_FILE_SIZES ccfs ;
if ( fcb - > deleted )
return STATUS_FILE_CLOSED ;
if ( fcb - > ads )
return stream_set_end_of_file_information ( Vcb , Irp , FileObject , advance_only , rollback ) ;
TRACE ( " filename %.*S \n " , fcb - > full_filename . Length / sizeof ( WCHAR ) , fcb - > full_filename . Buffer ) ;
TRACE ( " paging IO: %s \n " , Irp - > Flags & IRP_PAGING_IO ? " TRUE " : " FALSE " ) ;
TRACE ( " FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx \n " ,
fcb - > Header . AllocationSize . QuadPart , fcb - > Header . FileSize . QuadPart , fcb - > Header . ValidDataLength . QuadPart ) ;
// int3;
TRACE ( " setting new end to %llx bytes (currently %llx) \n " , feofi - > EndOfFile . QuadPart , fcb - > inode_item . st_size ) ;
// if (feofi->EndOfFile.QuadPart==0x36c000)
// int3;
if ( feofi - > EndOfFile . QuadPart < fcb - > inode_item . st_size ) {
if ( advance_only )
return STATUS_SUCCESS ;
TRACE ( " truncating file to %llx bytes \n " , feofi - > EndOfFile . QuadPart ) ;
Status = truncate_file ( fcb , feofi - > EndOfFile . QuadPart , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - truncate_file failed \n " ) ;
return Status ;
}
} else if ( feofi - > EndOfFile . QuadPart > fcb - > inode_item . st_size ) {
if ( Irp - > Flags & IRP_PAGING_IO ) {
TRACE ( " paging IO tried to extend file size \n " ) ;
return STATUS_SUCCESS ;
}
TRACE ( " extending file to %llx bytes \n " , feofi - > EndOfFile . QuadPart ) ;
// FIXME - pass flag to say that new extents should be prealloc rather than sparse
Status = extend_file ( fcb , feofi - > EndOfFile . QuadPart , rollback ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - extend_file failed \n " ) ;
return Status ;
}
}
ccfs . AllocationSize = fcb - > Header . AllocationSize ;
ccfs . FileSize = fcb - > Header . FileSize ;
ccfs . ValidDataLength = fcb - > Header . ValidDataLength ;
CcSetFileSizes ( FileObject , & ccfs ) ;
TRACE ( " setting FileSize for %.*S to %llx \n " , fcb - > full_filename . Length / sizeof ( WCHAR ) , fcb - > full_filename . Buffer , ccfs . FileSize ) ;
KeQuerySystemTime ( & time ) ;
win_time_to_unix ( time , & fcb - > inode_item . st_mtime ) ;
searchkey . obj_id = fcb - > inode ;
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_item returned %08x \n " , Status ) ;
return Status ;
}
if ( ! keycmp ( & tp . item - > key , & searchkey ) )
delete_tree_item ( Vcb , & tp , rollback ) ;
else
WARN ( " couldn't find existing INODE_ITEM \n " ) ;
ii = ExAllocatePoolWithTag ( PagedPool , sizeof ( INODE_ITEM ) , ALLOC_TAG ) ;
if ( ! ii ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlCopyMemory ( ii , & fcb - > inode_item , sizeof ( INODE_ITEM ) ) ;
insert_tree_item ( Vcb , fcb - > subvol , fcb - > inode , TYPE_INODE_ITEM , 0 , ii , sizeof ( INODE_ITEM ) , NULL , rollback ) ;
free_traverse_ptr ( & tp ) ;
return STATUS_SUCCESS ;
}
// static NTSTATUS STDCALL set_allocation_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
// FILE_ALLOCATION_INFORMATION* fai = (FILE_ALLOCATION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
// fcb* fcb = FileObject->FsContext;
//
// FIXME("FIXME\n");
// ERR("fcb = %p (%.*S)\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
// ERR("AllocationSize = %llx\n", fai->AllocationSize.QuadPart);
//
// return STATUS_NOT_IMPLEMENTED;
// }
static NTSTATUS STDCALL set_position_information ( device_extension * Vcb , PIRP Irp , PFILE_OBJECT FileObject ) {
FILE_POSITION_INFORMATION * fpi = ( FILE_POSITION_INFORMATION * ) Irp - > AssociatedIrp . SystemBuffer ;
# ifdef DEBUG_LONG_MESSAGES
fcb * fcb = FileObject - > FsContext ;
TRACE ( " setting the position on %.*S to %llx \n " , fcb - > full_filename . Length / sizeof ( WCHAR ) , fcb - > full_filename . Buffer , fpi - > CurrentByteOffset . QuadPart ) ;
# endif
// FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING
FileObject - > CurrentByteOffset = fpi - > CurrentByteOffset ;
return STATUS_SUCCESS ;
}
NTSTATUS STDCALL drv_set_information ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
NTSTATUS Status ;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation ( Irp ) ;
device_extension * Vcb = DeviceObject - > DeviceExtension ;
fcb * fcb = IrpSp - > FileObject - > FsContext ;
BOOL top_level ;
LIST_ENTRY rollback ;
InitializeListHead ( & rollback ) ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
if ( Vcb - > readonly ) {
Status = STATUS_MEDIA_WRITE_PROTECTED ;
goto end ;
}
if ( fcb - > subvol - > root_item . flags & BTRFS_SUBVOL_READONLY ) {
Status = STATUS_ACCESS_DENIED ;
goto end ;
}
Irp - > IoStatus . Information = 0 ;
Status = STATUS_NOT_IMPLEMENTED ;
TRACE ( " set information \n " ) ;
acquire_tree_lock ( Vcb , TRUE ) ;
switch ( IrpSp - > Parameters . SetFile . FileInformationClass ) {
case FileAllocationInformation :
TRACE ( " FileAllocationInformation \n " ) ;
Status = set_end_of_file_information ( Vcb , Irp , IrpSp - > FileObject , FALSE , & rollback ) ;
break ;
case FileBasicInformation :
TRACE ( " FileBasicInformation \n " ) ;
Status = set_basic_information ( Vcb , Irp , IrpSp - > FileObject , & rollback ) ;
break ;
case FileDispositionInformation :
TRACE ( " FileDispositionInformation \n " ) ;
Status = set_disposition_information ( Vcb , Irp , IrpSp - > FileObject ) ;
break ;
case FileEndOfFileInformation :
TRACE ( " FileEndOfFileInformation \n " ) ;
Status = set_end_of_file_information ( Vcb , Irp , IrpSp - > FileObject , IrpSp - > Parameters . SetFile . AdvanceOnly , & rollback ) ;
break ;
case FileLinkInformation :
FIXME ( " STUB: FileLinkInformation \n " ) ;
break ;
case FilePositionInformation :
TRACE ( " FilePositionInformation \n " ) ;
Status = set_position_information ( Vcb , Irp , IrpSp - > FileObject ) ;
break ;
case FileRenameInformation :
TRACE ( " FileRenameInformation \n " ) ;
// FIXME - make this work with streams
Status = set_rename_information ( Vcb , Irp , IrpSp - > FileObject , IrpSp - > Parameters . SetFile . FileObject , IrpSp - > Parameters . SetFile . ReplaceIfExists , & rollback ) ;
break ;
case FileValidDataLengthInformation :
FIXME ( " STUB: FileValidDataLengthInformation \n " ) ;
break ;
# if (NTDDI_VERSION >= NTDDI_VISTA)
case FileNormalizedNameInformation :
FIXME ( " STUB: FileNormalizedNameInformation \n " ) ;
break ;
# endif
# if (NTDDI_VERSION >= NTDDI_WIN7)
case FileStandardLinkInformation :
FIXME ( " STUB: FileStandardLinkInformation \n " ) ;
break ;
case FileRemoteProtocolInformation :
2016-03-26 11:53:07 +00:00
TRACE ( " FileRemoteProtocolInformation \n " ) ;
2016-03-23 20:35:05 +00:00
break ;
# endif
default :
WARN ( " unknown FileInformationClass %u \n " , IrpSp - > Parameters . SetFile . FileInformationClass ) ;
}
if ( NT_SUCCESS ( Status ) )
Status = consider_write ( Vcb ) ;
if ( NT_SUCCESS ( Status ) )
clear_rollback ( & rollback ) ;
else
do_rollback ( Vcb , & rollback ) ;
release_tree_lock ( Vcb , TRUE ) ;
end :
Irp - > IoStatus . Status = Status ;
IoCompleteRequest ( Irp , IO_NO_INCREMENT ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
return Status ;
}
static NTSTATUS STDCALL fill_in_file_basic_information ( FILE_BASIC_INFORMATION * fbi , INODE_ITEM * ii , LONG * length , fcb * fcb ) {
RtlZeroMemory ( fbi , sizeof ( FILE_BASIC_INFORMATION ) ) ;
* length - = sizeof ( FILE_BASIC_INFORMATION ) ;
fbi - > CreationTime . QuadPart = unix_time_to_win ( & ii - > otime ) ;
fbi - > LastAccessTime . QuadPart = unix_time_to_win ( & ii - > st_atime ) ;
fbi - > LastWriteTime . QuadPart = unix_time_to_win ( & ii - > st_mtime ) ;
fbi - > ChangeTime . QuadPart = 0 ;
fbi - > FileAttributes = fcb - > ads ? fcb - > par - > atts : fcb - > atts ;
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL fill_in_file_network_open_information ( FILE_NETWORK_OPEN_INFORMATION * fnoi , fcb * fcb , LONG * length ) {
INODE_ITEM * ii ;
if ( * length < sizeof ( FILE_NETWORK_OPEN_INFORMATION ) ) {
WARN ( " overflow \n " ) ;
return STATUS_BUFFER_OVERFLOW ;
}
RtlZeroMemory ( fnoi , sizeof ( FILE_NETWORK_OPEN_INFORMATION ) ) ;
* length - = sizeof ( FILE_NETWORK_OPEN_INFORMATION ) ;
if ( fcb - > ads )
ii = & fcb - > par - > inode_item ;
else
ii = & fcb - > inode_item ;
fnoi - > CreationTime . QuadPart = unix_time_to_win ( & ii - > otime ) ;
fnoi - > LastAccessTime . QuadPart = unix_time_to_win ( & ii - > st_atime ) ;
fnoi - > LastWriteTime . QuadPart = unix_time_to_win ( & ii - > st_mtime ) ;
fnoi - > ChangeTime . QuadPart = 0 ;
if ( fcb - > ads ) {
fnoi - > AllocationSize . QuadPart = fnoi - > EndOfFile . QuadPart = fcb - > adssize ;
fnoi - > FileAttributes = fcb - > par - > atts ;
} else {
fnoi - > AllocationSize . QuadPart = S_ISDIR ( fcb - > inode_item . st_mode ) ? 0 : sector_align ( fcb - > inode_item . st_size , fcb - > Vcb - > superblock . sector_size ) ;
fnoi - > EndOfFile . QuadPart = S_ISDIR ( fcb - > inode_item . st_mode ) ? 0 : fcb - > inode_item . st_size ;
fnoi - > FileAttributes = fcb - > atts ;
}
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL fill_in_file_standard_information ( FILE_STANDARD_INFORMATION * fsi , fcb * fcb , LONG * length ) {
RtlZeroMemory ( fsi , sizeof ( FILE_STANDARD_INFORMATION ) ) ;
* length - = sizeof ( FILE_STANDARD_INFORMATION ) ;
if ( fcb - > ads ) {
fsi - > AllocationSize . QuadPart = fsi - > EndOfFile . QuadPart = fcb - > adssize ;
fsi - > NumberOfLinks = fcb - > par - > inode_item . st_nlink ;
fsi - > Directory = S_ISDIR ( fcb - > par - > inode_item . st_mode ) ;
} else {
fsi - > AllocationSize . QuadPart = S_ISDIR ( fcb - > inode_item . st_mode ) ? 0 : sector_align ( fcb - > inode_item . st_size , fcb - > Vcb - > superblock . sector_size ) ;
fsi - > EndOfFile . QuadPart = S_ISDIR ( fcb - > inode_item . st_mode ) ? 0 : fcb - > inode_item . st_size ;
fsi - > NumberOfLinks = fcb - > inode_item . st_nlink ;
fsi - > Directory = S_ISDIR ( fcb - > inode_item . st_mode ) ;
}
TRACE ( " length = %llu \n " , fsi - > EndOfFile . QuadPart ) ;
fsi - > DeletePending = fcb - > delete_on_close ;
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL fill_in_file_internal_information ( FILE_INTERNAL_INFORMATION * fii , UINT64 inode , LONG * length ) {
* length - = sizeof ( FILE_INTERNAL_INFORMATION ) ;
fii - > IndexNumber . QuadPart = inode ;
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL fill_in_file_ea_information ( FILE_EA_INFORMATION * eai , LONG * length ) {
* length - = sizeof ( FILE_EA_INFORMATION ) ;
eai - > EaSize = 0 ;
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL fill_in_file_access_information ( FILE_ACCESS_INFORMATION * fai , LONG * length ) {
* length - = sizeof ( FILE_ACCESS_INFORMATION ) ;
fai - > AccessFlags = GENERIC_READ ;
return STATUS_NOT_IMPLEMENTED ;
}
static NTSTATUS STDCALL fill_in_file_position_information ( FILE_POSITION_INFORMATION * fpi , PFILE_OBJECT FileObject , LONG * length ) {
RtlZeroMemory ( fpi , sizeof ( FILE_POSITION_INFORMATION ) ) ;
* length - = sizeof ( FILE_POSITION_INFORMATION ) ;
fpi - > CurrentByteOffset = FileObject - > CurrentByteOffset ;
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL fill_in_file_mode_information ( FILE_MODE_INFORMATION * fmi , ccb * ccb , LONG * length ) {
RtlZeroMemory ( fmi , sizeof ( FILE_MODE_INFORMATION ) ) ;
* length - = sizeof ( FILE_MODE_INFORMATION ) ;
if ( ccb - > options & FILE_WRITE_THROUGH )
fmi - > Mode | = FILE_WRITE_THROUGH ;
if ( ccb - > options & FILE_SEQUENTIAL_ONLY )
fmi - > Mode | = FILE_SEQUENTIAL_ONLY ;
if ( ccb - > options & FILE_NO_INTERMEDIATE_BUFFERING )
fmi - > Mode | = FILE_NO_INTERMEDIATE_BUFFERING ;
if ( ccb - > options & FILE_SYNCHRONOUS_IO_ALERT )
fmi - > Mode | = FILE_SYNCHRONOUS_IO_ALERT ;
if ( ccb - > options & FILE_SYNCHRONOUS_IO_NONALERT )
fmi - > Mode | = FILE_SYNCHRONOUS_IO_NONALERT ;
if ( ccb - > options & FILE_DELETE_ON_CLOSE )
fmi - > Mode | = FILE_DELETE_ON_CLOSE ;
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL fill_in_file_alignment_information ( FILE_ALIGNMENT_INFORMATION * fai , device_extension * Vcb , LONG * length ) {
RtlZeroMemory ( fai , sizeof ( FILE_ALIGNMENT_INFORMATION ) ) ;
* length - = sizeof ( FILE_ALIGNMENT_INFORMATION ) ;
fai - > AlignmentRequirement = Vcb - > devices [ 0 ] . devobj - > AlignmentRequirement ;
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL fill_in_file_name_information ( FILE_NAME_INFORMATION * fni , fcb * fcb , LONG * length ) {
# ifdef _DEBUG
ULONG retlen = 0 ;
# endif
static WCHAR datasuf [ ] = { ' : ' , ' $ ' , ' D ' , ' A ' , ' T ' , ' A ' , 0 } ;
ULONG datasuflen = wcslen ( datasuf ) * sizeof ( WCHAR ) ;
RtlZeroMemory ( fni , sizeof ( FILE_NAME_INFORMATION ) ) ;
* length - = ( LONG ) offsetof ( FILE_NAME_INFORMATION , FileName [ 0 ] ) ;
TRACE ( " maximum length is %u \n " , * length ) ;
fni - > FileNameLength = 0 ;
fni - > FileName [ 0 ] = 0 ;
if ( * length > = ( LONG ) fcb - > full_filename . Length ) {
RtlCopyMemory ( fni - > FileName , fcb - > full_filename . Buffer , fcb - > full_filename . Length ) ;
# ifdef _DEBUG
retlen = fcb - > full_filename . Length ;
# endif
* length - = fcb - > full_filename . Length ;
} else {
if ( * length > 0 ) {
RtlCopyMemory ( fni - > FileName , fcb - > full_filename . Buffer , * length ) ;
# ifdef _DEBUG
retlen = * length ;
# endif
}
* length = - 1 ;
}
fni - > FileNameLength = fcb - > full_filename . Length ;
if ( fcb - > ads ) {
if ( * length > = ( LONG ) datasuflen ) {
RtlCopyMemory ( & fni - > FileName [ fcb - > full_filename . Length / sizeof ( WCHAR ) ] , datasuf , datasuflen ) ;
# ifdef _DEBUG
retlen + = datasuflen ;
# endif
* length - = datasuflen ;
} else {
if ( * length > 0 ) {
RtlCopyMemory ( & fni - > FileName [ fcb - > full_filename . Length / sizeof ( WCHAR ) ] , datasuf , * length ) ;
# ifdef _DEBUG
retlen + = * length ;
# endif
}
* length = - 1 ;
}
}
TRACE ( " %.*S \n " , retlen / sizeof ( WCHAR ) , fni - > FileName ) ;
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL fill_in_file_attribute_information ( FILE_ATTRIBUTE_TAG_INFORMATION * ati , fcb * fcb , LONG * length ) {
* length - = sizeof ( FILE_ATTRIBUTE_TAG_INFORMATION ) ;
ati - > FileAttributes = fcb - > ads ? fcb - > par - > atts : fcb - > atts ;
ati - > ReparseTag = 0 ;
return STATUS_SUCCESS ;
}
typedef struct {
UNICODE_STRING name ;
UINT64 size ;
} stream_info ;
static NTSTATUS STDCALL fill_in_file_stream_information ( FILE_STREAM_INFORMATION * fsi , fcb * fcb , LONG * length ) {
ULONG reqsize ;
UINT64 i , num_streams ;
stream_info * streams ;
FILE_STREAM_INFORMATION * entry ;
NTSTATUS Status ;
KEY searchkey ;
traverse_ptr tp , next_tp ;
BOOL b ;
static WCHAR datasuf [ ] = { ' : ' , ' $ ' , ' D ' , ' A ' , ' T ' , ' A ' , 0 } ;
static char xapref [ ] = " user. " ;
UNICODE_STRING suf ;
suf . Buffer = datasuf ;
suf . Length = suf . MaximumLength = wcslen ( datasuf ) * sizeof ( WCHAR ) ;
num_streams = 1 ;
searchkey . obj_id = fcb - > inode ;
searchkey . obj_type = TYPE_XATTR_ITEM ;
searchkey . offset = 0 ;
Status = find_item ( fcb - > Vcb , fcb - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
do {
if ( tp . item - > key . obj_id = = fcb - > inode & & tp . item - > key . obj_type = = TYPE_XATTR_ITEM ) {
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 {
ULONG len = tp . item - > size ;
DIR_ITEM * xa = ( DIR_ITEM * ) tp . item - > data ;
do {
if ( len < sizeof ( DIR_ITEM ) | | len < 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 ;
}
if ( xa - > n > strlen ( xapref ) & & RtlCompareMemory ( xa - > name , xapref , strlen ( xapref ) ) = = strlen ( xapref ) ) {
if ( tp . item - > key . offset ! = EA_DOSATTRIB_HASH | | xa - > n ! = strlen ( EA_DOSATTRIB ) | | RtlCompareMemory ( xa - > name , EA_DOSATTRIB , xa - > n ) ! = xa - > n ) {
num_streams + + ;
}
}
len - = sizeof ( DIR_ITEM ) - sizeof ( char ) + xa - > n + xa - > m ;
xa = ( DIR_ITEM * ) & xa - > name [ xa - > n + xa - > m ] ; // FIXME - test xattr hash collisions work
} while ( len > 0 ) ;
}
}
b = find_next_item ( fcb - > Vcb , & tp , & next_tp , FALSE ) ;
if ( b ) {
free_traverse_ptr ( & tp ) ;
tp = next_tp ;
if ( next_tp . item - > key . obj_id > fcb - > inode | | next_tp . item - > key . obj_type > TYPE_XATTR_ITEM )
break ;
}
} while ( b ) ;
free_traverse_ptr ( & tp ) ;
Status = find_item ( fcb - > Vcb , fcb - > subvol , & tp , & searchkey , FALSE ) ;
if ( ! NT_SUCCESS ( Status ) ) {
ERR ( " error - find_item returned %08x \n " , Status ) ;
return Status ;
}
streams = ExAllocatePoolWithTag ( PagedPool , sizeof ( stream_info ) * num_streams , ALLOC_TAG ) ;
if ( ! streams ) {
ERR ( " out of memory \n " ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
reqsize = 0 ;
streams [ 0 ] . name . Length = streams [ 0 ] . name . MaximumLength = 0 ;
streams [ 0 ] . name . Buffer = NULL ;
streams [ 0 ] . size = fcb - > inode_item . st_size ;
reqsize + = sizeof ( FILE_STREAM_INFORMATION ) - sizeof ( WCHAR ) + suf . Length + sizeof ( WCHAR ) + streams [ 0 ] . name . Length ;
i = 1 ;
do {
if ( tp . item - > key . obj_id = = fcb - > inode & & tp . item - > key . obj_type = = TYPE_XATTR_ITEM ) {
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 {
ULONG len = tp . item - > size ;
DIR_ITEM * xa = ( DIR_ITEM * ) tp . item - > data ;
ULONG stringlen ;
do {
if ( len < sizeof ( DIR_ITEM ) | | len < 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 ;
}
if ( xa - > n > strlen ( xapref ) & & RtlCompareMemory ( xa - > name , xapref , strlen ( xapref ) ) = = strlen ( xapref ) & &
( tp . item - > key . offset ! = EA_DOSATTRIB_HASH | | xa - > n ! = strlen ( EA_DOSATTRIB ) | | RtlCompareMemory ( xa - > name , EA_DOSATTRIB , xa - > n ) ! = xa - > n ) ) {
Status = RtlUTF8ToUnicodeN ( NULL , 0 , & stringlen , & xa - > name [ strlen ( xapref ) ] , xa - > n - strlen ( xapref ) ) ;
if ( ! NT_SUCCESS ( Status ) ) {
UINT64 j ;
ERR ( " RtlUTF8ToUnicodeN 1 returned %08x \n " , Status ) ;
for ( j = i ; j < num_streams ; j + + )
streams [ j ] . name . Buffer = NULL ;
goto end ;
}
streams [ i ] . name . Buffer = ExAllocatePoolWithTag ( PagedPool , stringlen , ALLOC_TAG ) ;
if ( ! streams [ i ] . name . Buffer ) {
UINT64 j ;
ERR ( " out of memory \n " ) ;
for ( j = i + 1 ; j < num_streams ; j + + )
streams [ j ] . name . Buffer = NULL ;
Status = STATUS_INSUFFICIENT_RESOURCES ;
goto end ;
}
Status = RtlUTF8ToUnicodeN ( streams [ i ] . name . Buffer , stringlen , & stringlen , & xa - > name [ strlen ( xapref ) ] , xa - > n - strlen ( xapref ) ) ;
if ( ! NT_SUCCESS ( Status ) ) {
UINT64 j ;
ERR ( " RtlUTF8ToUnicodeN 2 returned %08x \n " , Status ) ;
ExFreePool ( streams [ i ] . name . Buffer ) ;
for ( j = i ; j < num_streams ; j + + )
streams [ j ] . name . Buffer = NULL ;
goto end ;
}
streams [ i ] . name . Length = streams [ i ] . name . MaximumLength = stringlen ;
streams [ i ] . size = xa - > m ;
reqsize = sector_align ( reqsize , sizeof ( LONGLONG ) ) ;
reqsize + = sizeof ( FILE_STREAM_INFORMATION ) - sizeof ( WCHAR ) + suf . Length + sizeof ( WCHAR ) + streams [ i ] . name . Length ;
TRACE ( " streams[%llu].name = %.*S (length = %u) \n " , i , streams [ i ] . name . Length / sizeof ( WCHAR ) , streams [ i ] . name . Buffer , streams [ i ] . name . Length / sizeof ( WCHAR ) ) ;
i + + ;
}
len - = sizeof ( DIR_ITEM ) - sizeof ( char ) + xa - > n + xa - > m ;
xa = ( DIR_ITEM * ) & xa - > name [ xa - > n + xa - > m ] ; // FIXME - test xattr hash collisions work
} while ( len > 0 ) ;
}
}
b = find_next_item ( fcb - > Vcb , & tp , & next_tp , FALSE ) ;
if ( b ) {
free_traverse_ptr ( & tp ) ;
tp = next_tp ;
if ( next_tp . item - > key . obj_id > fcb - > inode | | next_tp . item - > key . obj_type > TYPE_XATTR_ITEM )
break ;
}
} while ( b ) ;
free_traverse_ptr ( & tp ) ;
TRACE ( " length = %i, reqsize = %u \n " , * length , reqsize ) ;
if ( reqsize > * length ) {
Status = STATUS_BUFFER_OVERFLOW ;
goto end ;
}
entry = fsi ;
for ( i = 0 ; i < num_streams ; i + + ) {
entry - > StreamNameLength = streams [ i ] . name . Length + suf . Length + sizeof ( WCHAR ) ;
entry - > StreamSize . QuadPart = streams [ i ] . size ;
if ( i = = 0 )
entry - > StreamAllocationSize . QuadPart = sector_align ( fcb - > inode_item . st_size , fcb - > Vcb - > superblock . sector_size ) ;
else
entry - > StreamAllocationSize . QuadPart = streams [ i ] . size ;
entry - > StreamName [ 0 ] = ' : ' ;
if ( streams [ i ] . name . Length > 0 )
RtlCopyMemory ( & entry - > StreamName [ 1 ] , streams [ i ] . name . Buffer , streams [ i ] . name . Length ) ;
RtlCopyMemory ( & entry - > StreamName [ 1 + ( streams [ i ] . name . Length / sizeof ( WCHAR ) ) ] , suf . Buffer , suf . Length ) ;
if ( i = = num_streams - 1 )
entry - > NextEntryOffset = 0 ;
else {
entry - > NextEntryOffset = sector_align ( sizeof ( FILE_STREAM_INFORMATION ) - sizeof ( WCHAR ) + suf . Length + sizeof ( WCHAR ) + streams [ i ] . name . Length , sizeof ( LONGLONG ) ) ;
entry = ( FILE_STREAM_INFORMATION * ) ( ( UINT8 * ) entry + entry - > NextEntryOffset ) ;
}
}
* length - = reqsize ;
Status = STATUS_SUCCESS ;
end :
for ( i = 0 ; i < num_streams ; i + + ) {
if ( streams [ i ] . name . Buffer )
ExFreePool ( streams [ i ] . name . Buffer ) ;
}
ExFreePool ( streams ) ;
return Status ;
}
static NTSTATUS STDCALL fill_in_file_standard_link_information ( FILE_STANDARD_LINK_INFORMATION * fsli , fcb * fcb , LONG * length ) {
TRACE ( " FileStandardLinkInformation \n " ) ;
// FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close
fsli - > NumberOfAccessibleLinks = fcb - > inode_item . st_nlink ;
fsli - > TotalNumberOfLinks = fcb - > inode_item . st_nlink ;
fsli - > DeletePending = fcb - > delete_on_close ;
fsli - > Directory = fcb - > type = = BTRFS_TYPE_DIRECTORY ? TRUE : FALSE ;
* length - = sizeof ( FILE_STANDARD_LINK_INFORMATION ) ;
return STATUS_SUCCESS ;
}
static NTSTATUS STDCALL query_info ( device_extension * Vcb , PFILE_OBJECT FileObject , PIRP Irp ) {
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation ( Irp ) ;
LONG length = IrpSp - > Parameters . QueryFile . Length ;
fcb * fcb = FileObject - > FsContext ;
ccb * ccb = FileObject - > FsContext2 ;
NTSTATUS Status ;
TRACE ( " (%p, %p, %p) \n " , Vcb , FileObject , Irp ) ;
TRACE ( " fcb = %p \n " , fcb ) ;
if ( fcb = = Vcb - > volume_fcb )
return STATUS_INVALID_PARAMETER ;
switch ( IrpSp - > Parameters . QueryFile . FileInformationClass ) {
case FileAllInformation :
{
FILE_ALL_INFORMATION * fai = Irp - > AssociatedIrp . SystemBuffer ;
INODE_ITEM * ii ;
TRACE ( " FileAllInformation \n " ) ;
if ( fcb - > ads )
ii = & fcb - > par - > inode_item ;
else
ii = & fcb - > inode_item ;
if ( length > 0 )
fill_in_file_basic_information ( & fai - > BasicInformation , ii , & length , fcb ) ;
if ( length > 0 )
fill_in_file_standard_information ( & fai - > StandardInformation , fcb , & length ) ;
if ( length > 0 )
fill_in_file_internal_information ( & fai - > InternalInformation , fcb - > inode , & length ) ;
if ( length > 0 )
fill_in_file_ea_information ( & fai - > EaInformation , & length ) ;
if ( length > 0 )
fill_in_file_access_information ( & fai - > AccessInformation , & length ) ;
if ( length > 0 )
fill_in_file_position_information ( & fai - > PositionInformation , FileObject , & length ) ;
if ( length > 0 )
fill_in_file_mode_information ( & fai - > ModeInformation , ccb , & length ) ;
if ( length > 0 )
fill_in_file_alignment_information ( & fai - > AlignmentInformation , Vcb , & length ) ;
if ( length > 0 )
fill_in_file_name_information ( & fai - > NameInformation , fcb , & length ) ;
Status = STATUS_SUCCESS ;
break ;
}
case FileAttributeTagInformation :
{
FILE_ATTRIBUTE_TAG_INFORMATION * ati = Irp - > AssociatedIrp . SystemBuffer ;
TRACE ( " FileAttributeTagInformation \n " ) ;
Status = fill_in_file_attribute_information ( ati , fcb , & length ) ;
break ;
}
case FileBasicInformation :
{
FILE_BASIC_INFORMATION * fbi = Irp - > AssociatedIrp . SystemBuffer ;
INODE_ITEM * ii ;
TRACE ( " FileBasicInformation \n " ) ;
if ( IrpSp - > Parameters . QueryFile . Length < sizeof ( FILE_BASIC_INFORMATION ) ) {
WARN ( " overflow \n " ) ;
Status = STATUS_BUFFER_OVERFLOW ;
goto exit ;
}
if ( fcb - > ads )
ii = & fcb - > par - > inode_item ;
else
ii = & fcb - > inode_item ;
Status = fill_in_file_basic_information ( fbi , ii , & length , fcb ) ;
break ;
}
case FileCompressionInformation :
FIXME ( " STUB: FileCompressionInformation \n " ) ;
Status = STATUS_INVALID_PARAMETER ;
goto exit ;
case FileEaInformation :
{
FILE_EA_INFORMATION * eai = Irp - > AssociatedIrp . SystemBuffer ;
TRACE ( " FileEaInformation \n " ) ;
Status = fill_in_file_ea_information ( eai , & length ) ;
break ;
}
case FileInternalInformation :
{
FILE_INTERNAL_INFORMATION * fii = Irp - > AssociatedIrp . SystemBuffer ;
TRACE ( " FileInternalInformation \n " ) ;
Status = fill_in_file_internal_information ( fii , fcb - > inode , & length ) ;
break ;
}
case FileNameInformation :
{
FILE_NAME_INFORMATION * fni = Irp - > AssociatedIrp . SystemBuffer ;
TRACE ( " FileNameInformation \n " ) ;
Status = fill_in_file_name_information ( fni , fcb , & length ) ;
break ;
}
case FileNetworkOpenInformation :
{
FILE_NETWORK_OPEN_INFORMATION * fnoi = Irp - > AssociatedIrp . SystemBuffer ;
TRACE ( " FileNetworkOpenInformation \n " ) ;
Status = fill_in_file_network_open_information ( fnoi , fcb , & length ) ;
break ;
}
case FilePositionInformation :
{
FILE_POSITION_INFORMATION * fpi = Irp - > AssociatedIrp . SystemBuffer ;
TRACE ( " FilePositionInformation \n " ) ;
Status = fill_in_file_position_information ( fpi , FileObject , & length ) ;
break ;
}
case FileStandardInformation :
{
FILE_STANDARD_INFORMATION * fsi = Irp - > AssociatedIrp . SystemBuffer ;
TRACE ( " FileStandardInformation \n " ) ;
if ( IrpSp - > Parameters . QueryFile . Length < sizeof ( FILE_STANDARD_INFORMATION ) ) {
WARN ( " overflow \n " ) ;
Status = STATUS_BUFFER_OVERFLOW ;
goto exit ;
}
Status = fill_in_file_standard_information ( fsi , fcb , & length ) ;
break ;
}
case FileStreamInformation :
{
FILE_STREAM_INFORMATION * fsi = Irp - > AssociatedIrp . SystemBuffer ;
TRACE ( " FileStreamInformation \n " ) ;
Status = fill_in_file_stream_information ( fsi , fcb , & length ) ;
break ;
}
# if (NTDDI_VERSION >= NTDDI_VISTA)
case FileHardLinkInformation :
FIXME ( " STUB: FileHardLinkInformation \n " ) ;
Status = STATUS_INVALID_PARAMETER ;
goto exit ;
case FileNormalizedNameInformation :
{
FILE_NAME_INFORMATION * fni = Irp - > AssociatedIrp . SystemBuffer ;
TRACE ( " FileNormalizedNameInformation \n " ) ;
Status = fill_in_file_name_information ( fni , fcb , & length ) ;
break ;
}
# endif
# if (NTDDI_VERSION >= NTDDI_WIN7)
case FileStandardLinkInformation :
{
FILE_STANDARD_LINK_INFORMATION * fsli = Irp - > AssociatedIrp . SystemBuffer ;
TRACE ( " FileStandardLinkInformation \n " ) ;
Status = fill_in_file_standard_link_information ( fsli , fcb , & length ) ;
break ;
}
case FileRemoteProtocolInformation :
2016-03-26 11:53:07 +00:00
TRACE ( " FileRemoteProtocolInformation \n " ) ;
2016-03-23 20:35:05 +00:00
Status = STATUS_INVALID_PARAMETER ;
goto exit ;
# endif
default :
WARN ( " unknown FileInformationClass %u \n " , IrpSp - > Parameters . QueryFile . FileInformationClass ) ;
Status = STATUS_INVALID_PARAMETER ;
goto exit ;
}
if ( length < 0 ) {
length = 0 ;
Status = STATUS_BUFFER_OVERFLOW ;
}
Irp - > IoStatus . Information = IrpSp - > Parameters . QueryFile . Length - length ;
exit :
TRACE ( " query_info returning %08x \n " , Status ) ;
return Status ;
}
NTSTATUS STDCALL drv_query_information ( IN PDEVICE_OBJECT DeviceObject , IN PIRP Irp ) {
PIO_STACK_LOCATION IrpSp ;
NTSTATUS Status ;
fcb * fcb ;
device_extension * Vcb = DeviceObject - > DeviceExtension ;
BOOL top_level ;
FsRtlEnterFileSystem ( ) ;
top_level = is_top_level ( Irp ) ;
Irp - > IoStatus . Information = 0 ;
TRACE ( " query information \n " ) ;
IrpSp = IoGetCurrentIrpStackLocation ( Irp ) ;
acquire_tree_lock ( Vcb , FALSE ) ;
fcb = IrpSp - > FileObject - > FsContext ;
TRACE ( " fcb = %p \n " , fcb ) ;
TRACE ( " fcb->subvol = %p \n " , fcb - > subvol ) ;
Status = query_info ( fcb - > Vcb , IrpSp - > FileObject , Irp ) ;
TRACE ( " returning %08x \n " , Status ) ;
Irp - > IoStatus . Status = Status ;
IoCompleteRequest ( Irp , IO_NO_INCREMENT ) ;
release_tree_lock ( Vcb , FALSE ) ;
if ( top_level )
IoSetTopLevelIrp ( NULL ) ;
FsRtlExitFileSystem ( ) ;
return Status ;
}