[BTRFS]
authorPierre Schweitzer <pierre@reactos.org>
Sat, 26 Mar 2016 11:53:07 +0000 (11:53 +0000)
committerPierre Schweitzer <pierre@reactos.org>
Sat, 26 Mar 2016 11:53:07 +0000 (11:53 +0000)
Upgrade the WinBtrfs to release 0.3.

Mostly bugfixes

CORE-11024 #resolve #comment Committed in r71052

svn path=/trunk/; revision=71052

reactos/drivers/filesystems/btrfs/btrfs.c
reactos/drivers/filesystems/btrfs/btrfs_drv.h
reactos/drivers/filesystems/btrfs/btrfsioctl.h [new file with mode: 0644]
reactos/drivers/filesystems/btrfs/cache.c
reactos/drivers/filesystems/btrfs/create.c
reactos/drivers/filesystems/btrfs/fastio.c
reactos/drivers/filesystems/btrfs/fileinfo.c
reactos/drivers/filesystems/btrfs/fsctl.c
reactos/drivers/filesystems/btrfs/read.c
reactos/drivers/filesystems/btrfs/treefuncs.c
reactos/drivers/filesystems/btrfs/write.c

index 90bb09e..6a1d256 100644 (file)
@@ -1550,8 +1550,8 @@ NTSTATUS delete_fcb(fcb* fcb, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
     // FIXME - delete all children if deleting directory
     
     if (fcb->deleted) {
-        ERR("trying to delete already-deleted file\n");
-        return STATUS_INTERNAL_ERROR;
+        WARN("trying to delete already-deleted file\n");
+        return STATUS_SUCCESS;
     }
     
     if (!fcb->par) {
@@ -1772,7 +1772,7 @@ NTSTATUS delete_fcb(fcb* fcb, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
     
     InitializeListHead(&changed_sector_list);
     
-    if (fcb->type != BTRFS_TYPE_DIRECTORY) {
+    if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) {
         Status = excise_extents(fcb->Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), &changed_sector_list, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("excise_extents returned %08x\n", Status);
@@ -1879,9 +1879,9 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line)
 #ifdef DEBUG_FCB_REFCOUNTS
 //     WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
 #ifdef DEBUG_LONG_MESSAGES
-    _debug_message(func, file, line, "fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+    _debug_message(func, 1, file, line, "fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
 #else
-    _debug_message(func, "fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+    _debug_message(func, 1, "fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
 #endif
 #endif
     
@@ -1930,9 +1930,9 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line)
     ExFreePool(fcb);
 #ifdef DEBUG_FCB_REFCOUNTS
 #ifdef DEBUG_LONG_MESSAGES
-    _debug_message(func, file, line, "freeing fcb %p\n", fcb);
+    _debug_message(func, 1, file, line, "freeing fcb %p\n", fcb);
 #else
-    _debug_message(func, "freeing fcb %p\n", fcb);
+    _debug_message(func, 1, "freeing fcb %p\n", fcb);
 #endif
 #endif
 }
@@ -1964,13 +1964,10 @@ static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObjec
         ExFreePool(ccb);
     }
     
-    if (fcb->refcount == 1)
-        CcUninitializeCacheMap(FileObject, NULL, NULL);
+    CcUninitializeCacheMap(FileObject, NULL, NULL);
     
     free_fcb(fcb);
     
-    FileObject->FsContext = NULL;
-    
     return STATUS_SUCCESS;
 }
 
@@ -2072,6 +2069,9 @@ static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
         IoRemoveShareAccess(FileObject, &fcb->share_access);
         
         oc = InterlockedDecrement(&fcb->open_count);
+#ifdef DEBUG_FCB_REFCOUNTS
+        ERR("fcb %p: open_count now %i\n", fcb, oc);
+#endif
         
         if (oc == 0) {
             if (fcb->delete_on_close && fcb != fcb->Vcb->root_fcb && fcb != fcb->Vcb->volume_fcb) {
@@ -2115,11 +2115,11 @@ static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
                 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
                       FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
             }
+            
+            if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
+                CcUninitializeCacheMap(FileObject, NULL, NULL);
         }
         
-        if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
-            CcUninitializeCacheMap(FileObject, NULL, NULL);
-        
         FileObject->Flags |= FO_CLEANUP_COMPLETE;
     }
     
@@ -3029,12 +3029,23 @@ void protect_superblocks(device_extension* Vcb, chunk* c) {
         
         for (j = 0; j < ci->num_stripes; j++) {
             if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
+                UINT32 size;
+                
                 TRACE("cut out superblock in chunk %llx\n", c->offset);
                 
                 addr = (superblock_addrs[i] - cis[j].offset) + c->offset;
                 TRACE("addr %llx\n", addr);
                 
-                add_to_space_list(c, addr, sizeof(superblock), SPACE_TYPE_USED);
+                // This prevents trees from spanning a stripe boundary, which btrfs check complains
+                // about. It also prevents the chunk tree being placed at 0x11000, which for some
+                // reason makes the FS unmountable on Linux (it tries to read 0x10000, i.e. the 
+                // superblock, instead).
+                if (ci->type & BLOCK_FLAG_SYSTEM || ci->type & BLOCK_FLAG_METADATA)
+                    size = max(sizeof(superblock), Vcb->superblock.node_size);
+                else
+                    size = sizeof(superblock);
+                
+                add_to_space_list(c, addr, size, SPACE_TYPE_USED);
             }
         }
         
index 8eec01b..df44b60 100644 (file)
@@ -204,6 +204,7 @@ typedef struct _tree {
 //     UINT8 level;
     tree_header header;
     LONG refcount;
+    BOOL has_address;
     UINT32 size;
     struct _device_extension* Vcb;
     struct _tree* parent;
@@ -213,6 +214,7 @@ typedef struct _tree {
     LIST_ENTRY itemlist;
     LIST_ENTRY list_entry;
     UINT64 new_address;
+    BOOL has_new_address;
     UINT64 flags;
 } tree;
 
diff --git a/reactos/drivers/filesystems/btrfs/btrfsioctl.h b/reactos/drivers/filesystems/btrfs/btrfsioctl.h
new file mode 100644 (file)
index 0000000..c1171a3
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef BTRFSIOCTL_H_DEFINED
+#define BTRFSIOCTL_H_DEFINED
+
+#define FSCTL_BTRFS_GET_FILE_IDS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x829, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+
+typedef struct {
+    UINT64 subvol;
+    UINT64 inode;
+    BOOL top;
+} btrfs_get_file_ids;
+
+#endif
index de6cfa6..2a6ba4d 100644 (file)
@@ -26,7 +26,10 @@ static BOOLEAN STDCALL acquire_for_lazy_write(PVOID Context, BOOLEAN Wait) {
     
     TRACE("(%p, %u)\n", Context, Wait);
     
-    if (!fcb || FileObject->Flags & FO_CLEANUP_COMPLETE)
+//     if (!fcb || FileObject->Flags & FO_CLEANUP_COMPLETE)
+//         return FALSE;
+
+    if (!ExAcquireResourceSharedLite(fcb->Header.PagingIoResource, Wait))
         return FALSE;
     
     fcb->lazy_writer_thread = KeGetCurrentThread();
@@ -40,10 +43,12 @@ static void STDCALL release_from_lazy_write(PVOID Context) {
     
     TRACE("(%p)\n", Context);
     
-    if (!fcb || FileObject->Flags & FO_CLEANUP_COMPLETE)
-        return;
+//     if (!fcb || FileObject->Flags & FO_CLEANUP_COMPLETE)
+//         return;
     
     fcb->lazy_writer_thread = NULL;
+    
+    ExReleaseResourceLite(fcb->Header.PagingIoResource);
 }
 
 static BOOLEAN STDCALL acquire_for_read_ahead(PVOID Context, BOOLEAN Wait) {
index a1106f7..3756bdf 100644 (file)
@@ -648,6 +648,7 @@ static NTSTATUS split_path(PUNICODE_STRING path, UNICODE_STRING** parts, ULONG*
 static fcb* search_fcb_children(fcb* dir, PUNICODE_STRING name) {
     LIST_ENTRY* le;
     fcb *c, *deleted = NULL;
+    ULONG rc;
     
     le = dir->children.Flink;
     while (le != &dir->children) {
@@ -657,9 +658,9 @@ static fcb* search_fcb_children(fcb* dir, PUNICODE_STRING name) {
             if (c->deleted) {
                 deleted = c;
             } else {
-                c->refcount++;
+                rc = InterlockedIncrement(&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);
+                WARN("fcb %p: refcount now %i (%.*S)\n", c, rc, c->full_filename.Length / sizeof(WCHAR), c->full_filename.Buffer);
 #endif
                 return c;
             }
@@ -668,20 +669,27 @@ static fcb* search_fcb_children(fcb* dir, PUNICODE_STRING name) {
         le = le->Flink;
     }
     
-    return deleted;
-}
-
+    if (deleted) {
+        rc = InterlockedIncrement(&deleted->refcount);
 #ifdef DEBUG_FCB_REFCOUNTS
-static void print_fcbs(device_extension* Vcb) {
-    fcb* fcb = Vcb->fcbs;
-    
-    while (fcb) {
-        ERR("fcb %p (%.*S): refcount %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount);
-        
-        fcb = fcb->next;
+        WARN("fcb %p: refcount now %i (%.*S)\n", deleted, rc, deleted->full_filename.Length / sizeof(WCHAR), deleted->full_filename.Buffer);
+#endif
     }
+    
+    return deleted;
 }
-#endif
+
+// #ifdef DEBUG_FCB_REFCOUNTS
+// static void print_fcbs(device_extension* Vcb) {
+//     fcb* fcb = Vcb->fcbs;
+//     
+//     while (fcb) {
+//         ERR("fcb %p (%.*S): refcount %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount);
+//         
+//         fcb = fcb->next;
+//     }
+// }
+// #endif
 
 NTSTATUS get_fcb(device_extension* Vcb, fcb** pfcb, PUNICODE_STRING fnus, fcb* relatedfcb, BOOL parent) {
     fcb *dir, *sf, *sf2;
@@ -693,9 +701,9 @@ NTSTATUS get_fcb(device_extension* Vcb, fcb** pfcb, PUNICODE_STRING fnus, fcb* r
     
     TRACE("(%p, %p, %.*S, %p, %s)\n", Vcb, pfcb, fnus->Length / sizeof(WCHAR), fnus->Buffer, relatedfcb, parent ? "TRUE" : "FALSE");
     
-#ifdef DEBUG_FCB_REFCOUNTS
-    print_fcbs(Vcb);
-#endif
+// #ifdef DEBUG_FCB_REFCOUNTS
+//     print_fcbs(Vcb);
+// #endif
     
     fnus2 = *fnus;
     
@@ -713,10 +721,12 @@ NTSTATUS get_fcb(device_extension* Vcb, fcb** pfcb, PUNICODE_STRING fnus, fcb* r
         }
         
         if (fnus2.Length == sizeof(WCHAR)) {
+            LONG rc;
+            
             *pfcb = Vcb->root_fcb;
-            Vcb->root_fcb->refcount++;
+            rc = InterlockedIncrement(&Vcb->root_fcb->refcount);
 #ifdef DEBUG_FCB_REFCOUNTS
-            WARN("fcb %p: refcount now %i (root)\n", Vcb->root_fcb, Vcb->root_fcb->refcount);
+            WARN("fcb %p: refcount now %i (root)\n", Vcb->root_fcb, rc);
 #endif
             return STATUS_SUCCESS;
         }
@@ -1028,9 +1038,9 @@ end2:
     if (parts)
         ExFreePool(parts);
     
-#ifdef DEBUG_FCB_REFCOUNTS
-    print_fcbs(Vcb);
-#endif
+// #ifdef DEBUG_FCB_REFCOUNTS
+//     print_fcbs(Vcb);
+// #endif
     
     TRACE("returning %08x\n", Status);
     
@@ -1064,6 +1074,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     ANSI_STRING utf8as;
     ULONG defda;
+    LONG rc;
     
     Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fpus->Buffer, fpus->Length);
     if (!NT_SUCCESS(Status))
@@ -1281,9 +1292,9 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
         fcb->delete_on_close = TRUE;
     
     fcb->par = parfcb;
-    parfcb->refcount++;
+    rc = InterlockedIncrement(&parfcb->refcount);
 #ifdef DEBUG_FCB_REFCOUNTS
-    WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, parfcb->refcount, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
+    WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, rc, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
 #endif
     fcb->subvol = parfcb->subvol;
     fcb->inode = inode;
@@ -1355,6 +1366,7 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     ccb* ccb;
     static WCHAR datasuf[] = {':','$','D','A','T','A',0};
     UNICODE_STRING dsus, fpus, stream;
+    LONG oc;
             
     TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp, Vcb, FileObject, fnus->Length / sizeof(WCHAR), fnus->Buffer, disposition, options);
     
@@ -1444,6 +1456,7 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
         KEY searchkey;
         traverse_ptr tp;
         INODE_ITEM* ii;
+        LONG rc;
         
         TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
         TRACE("stream = %.*S\n", stream.Length / sizeof(WCHAR), stream.Buffer);
@@ -1497,9 +1510,9 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
             fcb->delete_on_close = TRUE;
         
         fcb->par = parfcb;
-        parfcb->refcount++;
+        rc = InterlockedIncrement(&parfcb->refcount);
 #ifdef DEBUG_FCB_REFCOUNTS
-        WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, parfcb->refcount, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
+        WARN("fcb %p: refcount now %i (%.*S)\n", parfcb, rc, parfcb->full_filename.Length / sizeof(WCHAR), parfcb->full_filename.Buffer);
 #endif
         fcb->subvol = parfcb->subvol;
         fcb->inode = parfcb->inode;
@@ -1658,7 +1671,10 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     ccb->has_wildcard = FALSE;
     ccb->specific_file = FALSE;
     
-    InterlockedIncrement(&fcb->open_count);
+    oc = InterlockedIncrement(&fcb->open_count);
+#ifdef DEBUG_FCB_REFCOUNTS
+    ERR("fcb %p: open_count now %i\n", fcb, oc);
+#endif
     
     FileObject->FsContext2 = ccb;
 
@@ -1884,6 +1900,7 @@ static NTSTATUS STDCALL create_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_
     PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
     ULONG access;
     PACCESS_STATE access_state = Stack->Parameters.Create.SecurityContext->AccessState;
+    LONG oc;
     
     Irp->IoStatus.Information = 0;
     
@@ -1948,6 +1965,7 @@ static NTSTATUS STDCALL create_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_
     if (NT_SUCCESS(Status) && fcb->deleted) {
         free_fcb(fcb);
         Status = STATUS_OBJECT_NAME_NOT_FOUND;
+        goto exit;
     }
     
     if (NT_SUCCESS(Status)) {
@@ -2217,7 +2235,10 @@ static NTSTATUS STDCALL create_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_
             }
         }
         
-        InterlockedIncrement(&fcb->open_count);
+        oc = InterlockedIncrement(&fcb->open_count);
+#ifdef DEBUG_FCB_REFCOUNTS
+        ERR("fcb %p: open_count now %i\n", fcb, oc);
+#endif
     } else {
         Status = file_create(Irp, DeviceObject->DeviceExtension, FileObject, &FileObject->FileName, RequestedDisposition, options, rollback);
         Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
@@ -2316,6 +2337,7 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     if (IrpSp->FileObject->FileName.Length == 0 && !IrpSp->FileObject->RelatedFileObject) {
         ULONG RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
         ULONG RequestedOptions = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
+        LONG rc, oc;
         
         TRACE("open operation for volume\n");
 
@@ -2332,14 +2354,18 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
             goto exit;
         }
 
-        Vcb->volume_fcb->refcount++;
+        rc = InterlockedIncrement(&Vcb->volume_fcb->refcount);
+        oc = InterlockedIncrement(&Vcb->volume_fcb->open_count);
 #ifdef DEBUG_FCB_REFCOUNTS
-        WARN("fcb %p: refcount now %i (volume)\n", Vcb->volume_fcb, Vcb->volume_fcb->refcount);
+        WARN("fcb %p: refcount now %i (volume)\n", Vcb->volume_fcb, rc);
+        WARN("fcb %p: open_count now %i (volume)\n", Vcb->volume_fcb, oc);
 #endif
         attach_fcb_to_fileobject(Vcb, Vcb->volume_fcb, IrpSp->FileObject);
 // //         NtfsAttachFCBToFileObject(DeviceExt, DeviceExt->VolumeFcb, FileObject);
 // //         DeviceExt->VolumeFcb->RefCount++;
-// 
+        
+        IrpSp->FileObject->SectionObjectPointer = &Vcb->volume_fcb->nonpaged->segment_object;
+
         Irp->IoStatus.Information = FILE_OPENED;
         Status = STATUS_SUCCESS;
     } else {
index acb13a1..f895fed 100644 (file)
@@ -137,22 +137,58 @@ static NTSTATUS STDCALL fast_io_release_for_mod_write(PFILE_OBJECT FileObject, s
 
 static NTSTATUS STDCALL fast_io_acquire_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject){
     TRACE("STUB: fast_io_acquire_for_ccflush\n");
-    return STATUS_NOT_IMPLEMENTED;
+    return STATUS_SUCCESS;
 }
 
 static NTSTATUS STDCALL fast_io_release_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject){
     TRACE("STUB: fast_io_release_for_ccflush\n");
-    return STATUS_NOT_IMPLEMENTED;
+    return STATUS_SUCCESS;
+}
+
+#ifdef DEBUG
+static BOOLEAN STDCALL fast_io_read(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait, ULONG LockKey, PVOID Buffer, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
+    TRACE("(%p, %p, %x, %x, %x, %p, %p, %p)\n", FileObject, FileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject);
+
+    return FsRtlCopyRead(FileObject, FileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject);
+}
+
+static BOOLEAN STDCALL fast_io_write(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait, ULONG LockKey, PVOID Buffer, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
+    TRACE("(%p, %p, %x, %x, %x, %p, %p, %p)\n", FileObject, FileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject);
+
+    return FsRtlCopyWrite(FileObject, FileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject);
+}
+
+static BOOLEAN STDCALL fast_io_mdl_read(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, ULONG LockKey, PMDL* MdlChain, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
+    TRACE("(%p, %p, %x, %x, %p, %p, %p)\n", FileObject, FileOffset, Length, LockKey, MdlChain, IoStatus, DeviceObject);
+
+    return FsRtlMdlReadDev(FileObject, FileOffset, Length, LockKey, MdlChain, IoStatus, DeviceObject);
+}
+
+static BOOLEAN STDCALL fast_io_mdl_read_complete(PFILE_OBJECT FileObject, PMDL* MdlChain, PDEVICE_OBJECT DeviceObject) {
+    TRACE("(%p, %p, %p)\n", FileObject, MdlChain, DeviceObject);
+
+    return FsRtlMdlReadCompleteDev(FileObject, MdlChain, DeviceObject);
+}
+
+static BOOLEAN STDCALL fast_io_prepare_mdl_write(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, ULONG LockKey, PMDL* MdlChain, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
+    TRACE("(%p, %p, %x, %x, %p, %p, %p)\n", FileObject, FileOffset, Length, LockKey, MdlChain, IoStatus, DeviceObject);
+
+    return FsRtlPrepareMdlWriteDev(FileObject, FileOffset, Length, LockKey, MdlChain, IoStatus, DeviceObject);
 }
 
+static BOOLEAN STDCALL fast_io_mdl_write_complete(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PMDL* MdlChain, PDEVICE_OBJECT DeviceObject) {
+    TRACE("(%p, %p, %p, %p)\n", FileObject, FileOffset, MdlChain, DeviceObject);
+
+    return FsRtlMdlWriteCompleteDev(FileObject, FileOffset, MdlChain, DeviceObject);
+}
+#endif
+
 void __stdcall init_fast_io_dispatch(FAST_IO_DISPATCH** fiod) {
     RtlZeroMemory(&FastIoDispatch, sizeof(FastIoDispatch));
 
     FastIoDispatch.SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH);
 
     FastIoDispatch.FastIoCheckIfPossible = fast_io_check_if_possible;
-    FastIoDispatch.FastIoRead = FsRtlCopyRead;
-    FastIoDispatch.FastIoWrite = FsRtlCopyWrite;
     FastIoDispatch.FastIoQueryBasicInfo = fast_query_basic_info;
     FastIoDispatch.FastIoQueryStandardInfo = fast_query_standard_info;
     FastIoDispatch.FastIoLock = fast_io_lock;
@@ -165,10 +201,6 @@ void __stdcall init_fast_io_dispatch(FAST_IO_DISPATCH** fiod) {
     FastIoDispatch.FastIoDetachDevice = fast_io_detach_device;
     FastIoDispatch.FastIoQueryNetworkOpenInfo = fast_io_query_network_open_info;
     FastIoDispatch.AcquireForModWrite = fast_io_acquire_for_mod_write;
-    FastIoDispatch.MdlRead = FsRtlMdlReadDev;
-    FastIoDispatch.MdlReadComplete = FsRtlMdlReadCompleteDev;
-    FastIoDispatch.PrepareMdlWrite = FsRtlPrepareMdlWriteDev;
-    FastIoDispatch.MdlWriteComplete = FsRtlMdlWriteCompleteDev;
     FastIoDispatch.FastIoReadCompressed = fast_io_read_compressed;
     FastIoDispatch.FastIoWriteCompressed = fast_io_write_compressed;
     FastIoDispatch.MdlReadCompleteCompressed = fast_io_mdl_read_complete_compressed;
@@ -178,5 +210,21 @@ void __stdcall init_fast_io_dispatch(FAST_IO_DISPATCH** fiod) {
     FastIoDispatch.AcquireForCcFlush = fast_io_acquire_for_ccflush;
     FastIoDispatch.ReleaseForCcFlush = fast_io_release_for_ccflush;
     
+#ifdef DEBUG
+    FastIoDispatch.FastIoRead = fast_io_read;
+    FastIoDispatch.FastIoWrite = fast_io_write;
+    FastIoDispatch.MdlRead = fast_io_mdl_read;
+    FastIoDispatch.MdlReadComplete = fast_io_mdl_read_complete;
+    FastIoDispatch.PrepareMdlWrite = fast_io_prepare_mdl_write;
+    FastIoDispatch.MdlWriteComplete = fast_io_mdl_write_complete;
+#else
+    FastIoDispatch.FastIoRead = FsRtlCopyRead;
+    FastIoDispatch.FastIoWrite = FsRtlCopyWrite;
+    FastIoDispatch.MdlRead = FsRtlMdlReadDev;
+    FastIoDispatch.MdlReadComplete = FsRtlMdlReadCompleteDev;
+    FastIoDispatch.PrepareMdlWrite = FsRtlPrepareMdlWriteDev;
+    FastIoDispatch.MdlWriteComplete = FsRtlMdlWriteCompleteDev;
+#endif
+    
     *fiod = &FastIoDispatch;
-}
\ No newline at end of file
+}
index 9f6e54f..e21c88b 100644 (file)
@@ -2194,7 +2194,7 @@ NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp
             break;
             
         case FileRemoteProtocolInformation:
-            FIXME("STUB: FileRemoteProtocolInformation\n");
+            TRACE("FileRemoteProtocolInformation\n");
             break;
 #endif
             
@@ -2884,7 +2884,7 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec
         }
         
         case FileRemoteProtocolInformation:
-            FIXME("STUB: FileRemoteProtocolInformation\n");
+            TRACE("FileRemoteProtocolInformation\n");
             Status = STATUS_INVALID_PARAMETER;
             goto exit;
 #endif
index 9405e72..9afacee 100644 (file)
  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include "btrfs_drv.h"
+#include "btrfsioctl.h"
 
 #ifndef FSCTL_CSV_CONTROL
 #define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
 #endif
 
+static NTSTATUS get_file_ids(PFILE_OBJECT FileObject, void* data, ULONG length) {
+    btrfs_get_file_ids* bgfi;
+    fcb* fcb;
+    
+    if (length < sizeof(btrfs_get_file_ids))
+        return STATUS_BUFFER_OVERFLOW;
+    
+    if (!FileObject)
+        return STATUS_INVALID_PARAMETER;
+    
+    fcb = FileObject->FsContext;
+    
+    if (!fcb)
+        return STATUS_INVALID_PARAMETER;
+    
+    bgfi = data;
+    
+    bgfi->subvol = fcb->subvol->id;
+    bgfi->inode = fcb->inode;
+    bgfi->top = fcb->Vcb->root_fcb == fcb ? TRUE : FALSE;
+    
+    return STATUS_SUCCESS;
+}
+
 NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user) {
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     NTSTATUS Status;
@@ -496,6 +521,9 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL
             Status = STATUS_NOT_IMPLEMENTED;
             break;
 #endif
+        case FSCTL_BTRFS_GET_FILE_IDS:
+            Status = get_file_ids(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.DeviceIoControl.OutputBufferLength);
+            break;
 
         default:
             WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
index 1b46034..16e045f 100644 (file)
@@ -368,6 +368,9 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
 //     }
     
     do {
+        UINT64 len;
+        EXTENT_DATA2* ed2;
+        
         ed = (EXTENT_DATA*)tp.item->data;
         
         if (tp.item->size < sizeof(EXTENT_DATA)) {
@@ -384,7 +387,11 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
             goto exit;
         }
         
-        if (tp.item->key.offset + ed->decoded_size < start) {
+        ed2 = (EXTENT_DATA2*)ed->data;
+        
+        len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
+        
+        if (tp.item->key.offset + len < start) {
             ERR("Tried to read beyond end of file\n");
             free_traverse_ptr(&tp);
             Status = STATUS_END_OF_FILE;
@@ -416,9 +423,8 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
             case EXTENT_TYPE_INLINE:
             {
                 UINT64 off = start + bytes_read - tp.item->key.offset;
-                UINT64 read;
+                UINT64 read = len - off;
                 
-                read = ed->decoded_size - off;
                 if (read > length) read = length;
                 
                 RtlCopyMemory(data + bytes_read, &ed->data[off], read);
@@ -430,12 +436,11 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
             
             case EXTENT_TYPE_REGULAR:
             {
-                EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
                 UINT64 off = start + bytes_read - tp.item->key.offset;
                 UINT32 to_read, read;
                 UINT8* buf;
                 
-                read = ed->decoded_size - off;
+                read = len - off;
                 if (read > length) read = length;
                 
                 if (ed2->address == 0) {
@@ -476,9 +481,8 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
             case EXTENT_TYPE_PREALLOC:
             {
                 UINT64 off = start + bytes_read - tp.item->key.offset;
-                UINT32 read;
+                UINT32 read = len - off;
                 
-                read = ed->decoded_size - off;
                 if (read > length) read = length;
 
                 RtlZeroMemory(data + bytes_read, read);
@@ -503,7 +507,7 @@ NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UI
                 break;
             else if (next_tp.item->key.obj_id != inode ||
                 next_tp.item->key.obj_type != TYPE_EXTENT_DATA ||
-                next_tp.item->key.offset != tp.item->key.offset + ed->decoded_size
+                next_tp.item->key.offset != tp.item->key.offset + len
             ) {
                 free_traverse_ptr(&next_tp);
                 break;
index e5b5ac6..ecaf18a 100644 (file)
@@ -386,6 +386,7 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
 //     t->address = addr;
 //     t->level = th->level;
     t->refcount = 1;
+    t->has_address = TRUE;
     t->Vcb = Vcb;
     t->parent = NULL;
     t->root = r;
@@ -393,6 +394,7 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
     t->paritem = NULL;
     t->size = 0;
     t->new_address = 0;
+    t->has_new_address = FALSE;
 #ifdef DEBUG_TREE_REFCOUNTS   
 #ifdef DEBUG_LONG_MESSAGES
     _debug_message(func, file, line, "loaded tree %p (%llx)\n", t, addr);
@@ -698,11 +700,20 @@ static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traver
         }
 
         if (t->header.level == 0 && cmp == 0 && !ignore && td && td->ignore) {
+            tree_data* origtd = td;
+            
             while (td && td->ignore)
                 td = next_item(t, td);
             
-            if (td)
+            if (td) {
                 cmp = keycmp(searchkey, &td->key);
+                
+                if (cmp != 0) {
+                    td = origtd;
+                    cmp = 0;
+                }
+            } else
+                td = origtd;
         }
     } while (td && cmp == 1);
     
index bff0518..855e6b0 100644 (file)
@@ -1093,13 +1093,13 @@ static BOOL trees_consistent(device_extension* Vcb) {
         tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
         
         if (tc2->write) {
-            if (tc2->tree->header.num_items == 0)
+            if (tc2->tree->header.num_items == 0 && tc2->tree->parent)
                 return FALSE;
             
             if (tc2->tree->size > maxsize)
                 return FALSE;
             
-            if (tc2->tree->new_address == 0)
+            if (!tc2->tree->has_new_address)
                 return FALSE;
         }
         
@@ -1263,6 +1263,7 @@ static BOOL insert_tree_extent_skinny(device_extension* Vcb, tree* t, chunk* c,
     free_traverse_ptr(&insert_tp);
     
     t->new_address = address;
+    t->has_new_address = TRUE;
     
     return TRUE;
 }
@@ -1272,7 +1273,7 @@ static BOOL insert_tree_extent(device_extension* Vcb, tree* t, chunk* c, LIST_EN
     EXTENT_ITEM_TREE2* eit2;
     traverse_ptr insert_tp;
     
-    TRACE("(%p, %p, %p, %p)\n", Vcb, t, c);
+    TRACE("(%p, %p, %p, %p)\n", Vcb, t, c, rollback);
     
     if (!find_address_in_chunk(Vcb, c, Vcb->superblock.node_size, &address))
         return FALSE;
@@ -1316,6 +1317,7 @@ static BOOL insert_tree_extent(device_extension* Vcb, tree* t, chunk* c, LIST_EN
     free_traverse_ptr(&insert_tp);
     
     t->new_address = address;
+    t->has_new_address = TRUE;
     
     return TRUE;
 }
@@ -1341,7 +1343,7 @@ static NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY*
 //         }
 //     }
     
-    if (t->header.address != 0) {
+    if (t->has_address) {
         origchunk = get_chunk_from_address(Vcb, t->header.address);
         
         if (insert_tree_extent(Vcb, t, origchunk, rollback))
@@ -1384,7 +1386,7 @@ static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tre
     
     searchkey.obj_id = address;
     searchkey.obj_type = TYPE_METADATA_ITEM;
-    searchkey.offset = t->header.level;
+    searchkey.offset = 0xffffffffffffffff;
     
     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
     if (!NT_SUCCESS(Status)) {
@@ -1392,7 +1394,7 @@ static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tre
         return FALSE;
     }
     
-    if (keycmp(&tp.item->key, &searchkey)) {
+    if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
         TRACE("could not find %llx,%x,%llx in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
         free_traverse_ptr(&tp);
         return FALSE;
@@ -1765,7 +1767,7 @@ static NTSTATUS allocate_tree_extents(device_extension* Vcb, LIST_ENTRY* rollbac
     while (le != &Vcb->tree_cache) {
         tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
         
-        if (tc2->write && tc2->tree->new_address == 0) {
+        if (tc2->write && !tc2->tree->has_new_address) {
             chunk* c;
             
             Status = get_tree_new_address(Vcb, tc2->tree, rollback);
@@ -1776,7 +1778,7 @@ static NTSTATUS allocate_tree_extents(device_extension* Vcb, LIST_ENTRY* rollbac
             
             TRACE("allocated extent %llx\n", tc2->tree->new_address);
             
-            if (tc2->tree->header.address != 0) {
+            if (tc2->tree->has_address) {
                 Status = reduce_tree_extent(Vcb, tc2->tree->header.address, tc2->tree, rollback);
                 
                 if (!NT_SUCCESS(Status)) {
@@ -2088,7 +2090,7 @@ static NTSTATUS write_trees(device_extension* Vcb) {
                 traverse_ptr tp;
                 EXTENT_ITEM_TREE* eit;
                 
-                if (tc2->tree->new_address == 0) {
+                if (!tc2->tree->has_new_address) {
                     ERR("error - tried to write tree with no new address\n");
                     int3;
                 }
@@ -2226,13 +2228,8 @@ static NTSTATUS write_trees(device_extension* Vcb) {
                 crash = TRUE;
             }
             
-            if (tc2->tree->new_address == 0) {
-                ERR("tree %llx, level %x: tried to write tree to address 0\n", tc2->tree->root->id, tc2->tree->header.level);
-                crash = TRUE;
-            }
-            
-            if (tc2->tree->header.num_items == 0) {
-                ERR("tree %llx, level %x: tried to write empty tree\n", tc2->tree->root->id, tc2->tree->header.level);
+            if (tc2->tree->header.num_items == 0 && tc2->tree->parent) {
+                ERR("tree %llx, level %x: tried to write empty tree with parent\n", tc2->tree->root->id, tc2->tree->header.level);
                 crash = TRUE;
             }
             
@@ -2257,6 +2254,7 @@ static NTSTATUS write_trees(device_extension* Vcb) {
             tc2->tree->header.address = tc2->tree->new_address;
             tc2->tree->header.generation = Vcb->superblock.generation;
             tc2->tree->header.flags |= HEADER_FLAG_MIXED_BACKREF;
+            tc2->tree->has_address = TRUE;
             
             data = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
             if (!data) {
@@ -2564,11 +2562,13 @@ static NTSTATUS STDCALL split_tree_at(device_extension* Vcb, tree* t, tree_data*
     nt->header.flags = HEADER_FLAG_MIXED_BACKREF;
     
     nt->refcount = 0;
+    nt->has_address = FALSE;
     nt->Vcb = Vcb;
     nt->parent = t->parent;
     nt->root = t->root;
 //     nt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
     nt->new_address = 0;
+    nt->has_new_address = FALSE;
     nt->flags = t->flags;
     InitializeListHead(&nt->itemlist);
     
@@ -2637,6 +2637,22 @@ static NTSTATUS STDCALL split_tree_at(device_extension* Vcb, tree* t, tree_data*
     
 // //     TRACE("last item is now (%x,%x,%x)\n", (UINT32)oldlastitem->key.obj_id, oldlastitem->key.obj_type, (UINT32)oldlastitem->key.offset);
     
+    if (nt->header.level > 0) {
+        LIST_ENTRY* le = nt->itemlist.Flink;
+        
+        while (le != &nt->itemlist) {
+            tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry);
+            
+            if (td2->treeholder.tree) {
+                td2->treeholder.tree->parent = nt;
+                increase_tree_rc(nt);
+                free_tree(t);
+            }
+            
+            le = le->Flink;
+        }
+    }
+    
     if (nt->parent) {
         increase_tree_rc(nt->parent);
         
@@ -2648,7 +2664,7 @@ static NTSTATUS STDCALL split_tree_at(device_extension* Vcb, tree* t, tree_data*
     
         td->key = newfirstitem->key;
         
-        InsertAfter(&t->itemlist, &td->list_entry, &t->paritem->list_entry);
+        InsertHeadList(&t->paritem->list_entry, &td->list_entry);
         
         td->ignore = FALSE;
         td->inserted = TRUE;
@@ -2683,11 +2699,13 @@ static NTSTATUS STDCALL split_tree_at(device_extension* Vcb, tree* t, tree_data*
     pt->header.flags = HEADER_FLAG_MIXED_BACKREF;
     
     pt->refcount = 2;
+    pt->has_address = FALSE;
     pt->Vcb = Vcb;
     pt->parent = NULL;
     pt->paritem = NULL;
     pt->root = t->root;
     pt->new_address = 0;
+    pt->has_new_address = FALSE;
 //     pt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
     pt->size = pt->header.num_items * sizeof(internal_node);
     pt->flags = t->flags;
@@ -2743,6 +2761,7 @@ static NTSTATUS STDCALL split_tree_at(device_extension* Vcb, tree* t, tree_data*
     nt->parent = pt;
     
 end:
+    t->root->root_item.bytes_used += Vcb->superblock.node_size;
 
 // #ifdef DEBUG_PARANOID
 //     lastkey2.obj_id = 0xffffffffffffffff;
@@ -2857,6 +2876,22 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY*
         t->header.num_items += next_tree->header.num_items;
         t->size += next_tree->size;
         
+        if (next_tree->header.level > 0) {
+            le = next_tree->itemlist.Flink;
+            
+            while (le != &next_tree->itemlist) {
+                tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry);
+                
+                if (td2->treeholder.tree) {
+                    td2->treeholder.tree->parent = t;
+                    increase_tree_rc(t);
+                    free_tree(next_tree);
+                }
+                
+                le = le->Flink;
+            }
+        }
+        
         t->itemlist.Blink->Flink = next_tree->itemlist.Flink;
         t->itemlist.Blink->Flink->Blink = t->itemlist.Blink;
         t->itemlist.Blink = next_tree->itemlist.Blink;
@@ -2877,7 +2912,7 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY*
         next_tree->header.num_items = 0;
         next_tree->size = 0;
         
-        if (next_tree->new_address != 0) { // delete associated EXTENT_ITEM
+        if (next_tree->has_new_address) { // delete associated EXTENT_ITEM
             Status = reduce_tree_extent(Vcb, next_tree->new_address, next_tree, rollback);
             
             if (!NT_SUCCESS(Status)) {
@@ -2885,7 +2920,7 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY*
                 free_tree(next_tree);
                 return Status;
             }
-        } else if (next_tree->header.address != 0) {
+        } else if (next_tree->has_address) {
             Status = reduce_tree_extent(Vcb, next_tree->header.address, next_tree, rollback);
             
             if (!NT_SUCCESS(Status)) {
@@ -2911,6 +2946,8 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY*
         ExFreePool(next_tree->paritem);
         next_tree->paritem = NULL;
         
+        next_tree->root->root_item.bytes_used -= Vcb->superblock.node_size;
+        
         free_tree(next_tree);
         
         // remove next_tree from tree cache
@@ -2951,6 +2988,12 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY*
                 RemoveEntryList(&td->list_entry);
                 InsertTailList(&t->itemlist, &td->list_entry);
                 
+                if (next_tree->header.level > 0 && td->treeholder.tree) {
+                    td->treeholder.tree->parent = t;
+                    increase_tree_rc(t);
+                    free_tree(next_tree);
+                }
+                
                 if (!td->ignore) {
                     next_tree->size -= size;
                     t->size += size;
@@ -2993,6 +3036,106 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY*
     return STATUS_SUCCESS;
 }
 
+static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* t, UINT8 level, LIST_ENTRY* rollback) {
+    KEY searchkey;
+    traverse_ptr tp;
+    NTSTATUS Status;
+    
+    if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
+        searchkey.obj_id = address;
+        searchkey.obj_type = TYPE_METADATA_ITEM;
+        searchkey.offset = t->header.level;
+        
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        if (!NT_SUCCESS(Status)) {
+            ERR("error - find_item returned %08x\n", Status);
+            return Status;
+        }
+        
+        if (!keycmp(&tp.item->key, &searchkey)) {
+            EXTENT_ITEM_SKINNY_METADATA* eism;
+            
+            if (tp.item->size > 0) {
+                eism = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+                
+                if (!eism) {
+                    ERR("out of memory\n");
+                    free_traverse_ptr(&tp);
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+                
+                RtlCopyMemory(eism, tp.item->data, tp.item->size);
+            } else
+                eism = NULL;
+            
+            delete_tree_item(Vcb, &tp, rollback);
+            
+            if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, tp.item->size, NULL, rollback)) {
+                ERR("insert_tree_item failed\n");
+                ExFreePool(eism);
+                free_traverse_ptr(&tp);
+                return STATUS_INTERNAL_ERROR;
+            }
+            
+            free_traverse_ptr(&tp);
+            return STATUS_SUCCESS;
+        }
+        
+        free_traverse_ptr(&tp);
+    }
+    
+    searchkey.obj_id = address;
+    searchkey.obj_type = TYPE_EXTENT_ITEM;
+    searchkey.offset = 0xffffffffffffffff;
+    
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("error - find_item returned %08x\n", Status);
+        return Status;
+    }
+    
+    if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+        EXTENT_ITEM_TREE* eit;
+        
+        if (tp.item->size < sizeof(EXTENT_ITEM_TREE)) {
+            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_ITEM_TREE));
+            free_traverse_ptr(&tp);
+            return STATUS_INTERNAL_ERROR;
+        }
+        
+        eit = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+                
+        if (!eit) {
+            ERR("out of memory\n");
+            free_traverse_ptr(&tp);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlCopyMemory(eit, tp.item->data, tp.item->size);
+        
+        delete_tree_item(Vcb, &tp, rollback);
+        
+        eit->level = level;
+        
+        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, eit, tp.item->size, NULL, rollback)) {
+            ERR("insert_tree_item failed\n");
+            ExFreePool(eit);
+            free_traverse_ptr(&tp);
+            return STATUS_INTERNAL_ERROR;
+        }
+        
+        free_traverse_ptr(&tp);
+    
+        return STATUS_SUCCESS;
+    }
+    
+    ERR("could not find EXTENT_ITEM for address %llx\n", address);
+    
+    free_traverse_ptr(&tp);
+    
+    return STATUS_INTERNAL_ERROR;
+}
+
 static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
 //     LIST_ENTRY *le, *le2;
 //     write_tree* wt;
@@ -3025,38 +3168,39 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
                 empty = FALSE;
                 
                 if (tc2->tree->header.num_items == 0) {
-                    LIST_ENTRY* le2;
-                    KEY firstitem = {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
-                    
-                    done_deletions = TRUE;
-        
-                    le2 = tc2->tree->itemlist.Flink;
-                    while (le2 != &tc2->tree->itemlist) {
-                        tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
-                        firstitem = td->key;
-                        break;
-                    }
-                    
-                    ERR("deleting tree in root %llx (first item was %llx,%x,%llx)\n",
-                        tc2->tree->root->id, firstitem.obj_id, firstitem.obj_type, firstitem.offset);
-                    
-                    if (tc2->tree->new_address != 0) { // delete associated EXTENT_ITEM
-                        Status = reduce_tree_extent(Vcb, tc2->tree->new_address, tc2->tree, rollback);
+                    if (tc2->tree->parent) {
+                        LIST_ENTRY* le2;
+                        KEY firstitem = {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
                         
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("reduce_tree_extent returned %08x\n", Status);
-                            return Status;
+                        done_deletions = TRUE;
+            
+                        le2 = tc2->tree->itemlist.Flink;
+                        while (le2 != &tc2->tree->itemlist) {
+                            tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
+                            firstitem = td->key;
+                            break;
                         }
-                    } else if (tc2->tree->header.address != 0) {
-                        Status = reduce_tree_extent(Vcb,tc2->tree->header.address, tc2->tree, rollback);
+                        TRACE("deleting tree in root %llx (first item was %llx,%x,%llx)\n",
+                              tc2->tree->root->id, firstitem.obj_id, firstitem.obj_type, firstitem.offset);
                         
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("reduce_tree_extent returned %08x\n", Status);
-                            return Status;
+                        tc2->tree->root->root_item.bytes_used -= Vcb->superblock.node_size;
+                        
+                        if (tc2->tree->has_new_address) { // delete associated EXTENT_ITEM
+                            Status = reduce_tree_extent(Vcb, tc2->tree->new_address, tc2->tree, rollback);
+                            
+                            if (!NT_SUCCESS(Status)) {
+                                ERR("reduce_tree_extent returned %08x\n", Status);
+                                return Status;
+                            }
+                        } else if (tc2->tree->has_address) {
+                            Status = reduce_tree_extent(Vcb,tc2->tree->header.address, tc2->tree, rollback);
+                            
+                            if (!NT_SUCCESS(Status)) {
+                                ERR("reduce_tree_extent returned %08x\n", Status);
+                                return Status;
+                            }
                         }
-                    }
-                    
-                    if (tc2->tree->parent) {
+                        
                         if (!tc2->tree->paritem->ignore) {
                             tc2->tree->paritem->ignore = TRUE;
                             tc2->tree->parent->header.num_items--;
@@ -3071,19 +3215,25 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
                         
                         RemoveEntryList(le);
                         ExFreePool(tc2);
-                    } else {
-                        FIXME("trying to delete top root, not sure what to do here\n"); // FIXME
-                        return STATUS_INTERNAL_ERROR;
+                    } else if (tc2->tree->header.level != 0) {
+                        if (tc2->tree->has_new_address) {
+                            Status = update_extent_level(Vcb, tc2->tree->new_address, tc2->tree, 0, rollback);
+                            
+                            if (!NT_SUCCESS(Status)) {
+                                ERR("update_extent_level returned %08x\n", Status);
+                                return Status;
+                            }
+                        }
+                        
+                        tc2->tree->header.level = 0;
                     }
-                } else {
-                    if (tc2->tree->size > Vcb->superblock.node_size - sizeof(tree_header)) {
-                        TRACE("splitting overlarge tree (%x > %x)\n", tc2->tree->size, Vcb->superblock.node_size - sizeof(tree_header));
-                        Status = split_tree(Vcb, tc2->tree);
+                } else if (tc2->tree->size > Vcb->superblock.node_size - sizeof(tree_header)) {
+                    TRACE("splitting overlarge tree (%x > %x)\n", tc2->tree->size, Vcb->superblock.node_size - sizeof(tree_header));
+                    Status = split_tree(Vcb, tc2->tree);
 
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("split_tree returned %08x\n", Status);
-                            return Status;
-                        }
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("split_tree returned %08x\n", Status);
+                        return Status;
                     }
                 }
             }
@@ -3145,16 +3295,16 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
                             le2 = le2->Flink;
                         }
                         
-                        ERR("deleting top-level tree in root %llx with one item\n", tc2->tree->root->id);
+                        TRACE("deleting top-level tree in root %llx with one item\n", tc2->tree->root->id);
                         
-                        if (tc2->tree->new_address != 0) { // delete associated EXTENT_ITEM
+                        if (tc2->tree->has_new_address) { // delete associated EXTENT_ITEM
                             Status = reduce_tree_extent(Vcb, tc2->tree->new_address, tc2->tree, rollback);
                             
                             if (!NT_SUCCESS(Status)) {
                                 ERR("reduce_tree_extent returned %08x\n", Status);
                                 return Status;
                             }
-                        } else if (tc2->tree->header.address != 0) {
+                        } else if (tc2->tree->has_address) {
                             Status = reduce_tree_extent(Vcb,tc2->tree->header.address, tc2->tree, rollback);
                             
                             if (!NT_SUCCESS(Status)) {
@@ -3180,8 +3330,11 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
                         
                         if (child_tree) {
                             child_tree->parent = NULL;
+                            child_tree->paritem = NULL;
                             free_tree(tc2->tree);
                         }
+                        
+                        tc2->tree->root->root_item.bytes_used -= Vcb->superblock.node_size;
 
                         free_tree(tc2->tree);
                         
@@ -3208,7 +3361,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
     TRACE("(%p)\n", Vcb);
     
     // If only changing superblock, e.g. changing label, we still need to rewrite
-    // the root tree so the generations mach. Otherwise you won't be able to mount on Linux.
+    // the root tree so the generations match, otherwise you won't be able to mount on Linux.
     if (Vcb->write_trees > 0) {
         KEY searchkey;
         traverse_ptr tp;
@@ -4236,6 +4389,8 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
 
     do {
         EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data;
+        EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+        UINT64 len;
         
         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));
@@ -4251,7 +4406,9 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
         
         b = find_next_item(Vcb, &tp, &next_tp, FALSE);
         
-        if (tp.item->key.offset < end_data && tp.item->key.offset + ed->decoded_size >= start_data) {
+        len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
+        
+        if (tp.item->key.offset < end_data && tp.item->key.offset + len >= start_data) {
             if (ed->compression != BTRFS_COMPRESSION_NONE) {
                 FIXME("FIXME - compression not supported at present\n");
                 Status = STATUS_NOT_SUPPORTED;
@@ -4270,20 +4427,18 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                 goto end;
             }
             
-            // FIXME - is ed->decoded_size the size of the whole extent, or just this bit of it?
-            
             if (ed->type == EXTENT_TYPE_INLINE) {
-                if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + ed->decoded_size) { // remove all
+                if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + len) { // remove all
                     delete_tree_item(Vcb, &tp, rollback);
                     
-                    fcb->inode_item.st_blocks -= ed->decoded_size;
-                } else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + ed->decoded_size) { // remove beginning
+                    fcb->inode_item.st_blocks -= len;
+                } else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + len) { // remove beginning
                     EXTENT_DATA* ned;
                     UINT64 size;
                     
                     delete_tree_item(Vcb, &tp, rollback);
                     
-                    size = ed->decoded_size - (end_data - tp.item->key.offset);
+                    size = len - (end_data - tp.item->key.offset);
                     
                     ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
                     if (!ned) {
@@ -4309,7 +4464,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                     }
                     
                     fcb->inode_item.st_blocks -= end_data - tp.item->key.offset;
-                } else if (start_data > tp.item->key.offset && end_data >= tp.item->key.offset + ed->decoded_size) { // remove end
+                } else if (start_data > tp.item->key.offset && end_data >= tp.item->key.offset + len) { // remove end
                     EXTENT_DATA* ned;
                     UINT64 size;
                     
@@ -4340,8 +4495,8 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                         goto end;
                     }
                     
-                    fcb->inode_item.st_blocks -= tp.item->key.offset + ed->decoded_size - start_data;
-                } else if (start_data > tp.item->key.offset && end_data < tp.item->key.offset + ed->decoded_size) { // remove middle
+                    fcb->inode_item.st_blocks -= tp.item->key.offset + len - start_data;
+                } else if (start_data > tp.item->key.offset && end_data < tp.item->key.offset + len) { // remove middle
                     EXTENT_DATA* ned;
                     UINT64 size;
                     
@@ -4372,7 +4527,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                         goto end;
                     }
                     
-                    size = tp.item->key.offset + ed->decoded_size - end_data;
+                    size = tp.item->key.offset + len - end_data;
                     
                     ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
                     if (!ned) {
@@ -4400,39 +4555,24 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                     fcb->inode_item.st_blocks -= end_data - start_data;
                 }
             } else if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
-                EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
-                
-                if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + ed->decoded_size) { // remove all
+                if (start_data <= tp.item->key.offset && end_data >= tp.item->key.offset + len) { // remove all
                     if (ed2->address != 0) {
-                        Status = remove_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset, changed_sector_list, rollback);
+                        Status = remove_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset - ed2->offset, changed_sector_list, rollback);
                         if (!NT_SUCCESS(Status)) {
                             ERR("remove_extent_ref returned %08x\n", Status);
                             goto end;
                         }
                         
-                        fcb->inode_item.st_blocks -= ed->decoded_size;
+                        fcb->inode_item.st_blocks -= len;
                     }
                     
                     delete_tree_item(Vcb, &tp, rollback);
-                } else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + ed->decoded_size) { // remove beginning
+                } else if (start_data <= tp.item->key.offset && end_data < tp.item->key.offset + len) { // remove beginning
                     EXTENT_DATA* ned;
                     EXTENT_DATA2* ned2;
                     
-                    if (ed2->address != 0) {
-                        Status = add_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, end_data, rollback);
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("add_extent_ref returned %08x\n", Status);
-                            goto end;
-                        }
-                        
-                        Status = remove_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, tp.item->key.offset, changed_sector_list, rollback);
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("remove_extent_ref returned %08x\n", Status);
-                            goto end;
-                        }
-                        
+                    if (ed2->address != 0)
                         fcb->inode_item.st_blocks -= end_data - tp.item->key.offset;
-                    }
                     
                     delete_tree_item(Vcb, &tp, rollback);
                     
@@ -4446,7 +4586,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                     ned2 = (EXTENT_DATA2*)&ned->data[0];
                     
                     ned->generation = Vcb->superblock.generation;
-                    ned->decoded_size = ed->decoded_size - (end_data - tp.item->key.offset);
+                    ned->decoded_size = ed->decoded_size;
                     ned->compression = ed->compression;
                     ned->encryption = ed->encryption;
                     ned->encoding = ed->encoding;
@@ -4462,12 +4602,12 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                         Status = STATUS_INTERNAL_ERROR;
                         goto end;
                     }
-                } else if (start_data > tp.item->key.offset && end_data >= tp.item->key.offset + ed->decoded_size) { // remove end
+                } else if (start_data > tp.item->key.offset && end_data >= tp.item->key.offset + len) { // remove end
                     EXTENT_DATA* ned;
                     EXTENT_DATA2* ned2;
                     
                     if (ed2->address != 0)
-                        fcb->inode_item.st_blocks -= tp.item->key.offset + ed->decoded_size - start_data;
+                        fcb->inode_item.st_blocks -= tp.item->key.offset + len - start_data;
                     
                     delete_tree_item(Vcb, &tp, rollback);
                     
@@ -4481,7 +4621,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                     ned2 = (EXTENT_DATA2*)&ned->data[0];
                     
                     ned->generation = Vcb->superblock.generation;
-                    ned->decoded_size = start_data - tp.item->key.offset;
+                    ned->decoded_size = ed->decoded_size;
                     ned->compression = ed->compression;
                     ned->encryption = ed->encryption;
                     ned->encoding = ed->encoding;
@@ -4497,19 +4637,12 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                         Status = STATUS_INTERNAL_ERROR;
                         goto end;
                     }
-                } else if (start_data > tp.item->key.offset && end_data < tp.item->key.offset + ed->decoded_size) { // remove middle
+                } else if (start_data > tp.item->key.offset && end_data < tp.item->key.offset + len) { // remove middle
                     EXTENT_DATA* ned;
                     EXTENT_DATA2* ned2;
                     
-                    if (ed2->address != 0) {
-                        Status = add_extent_ref(Vcb, ed2->address, ed2->size, fcb->subvol, fcb->inode, end_data, rollback);
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("add_extent_ref returned %08x\n", Status);
-                            goto end;
-                        }
-                        
+                    if (ed2->address != 0)
                         fcb->inode_item.st_blocks -= end_data - start_data;
-                    }
                     
                     delete_tree_item(Vcb, &tp, rollback);
                     
@@ -4523,7 +4656,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                     ned2 = (EXTENT_DATA2*)&ned->data[0];
                     
                     ned->generation = Vcb->superblock.generation;
-                    ned->decoded_size = start_data - tp.item->key.offset;
+                    ned->decoded_size = ed->decoded_size;
                     ned->compression = ed->compression;
                     ned->encryption = ed->encryption;
                     ned->encoding = ed->encoding;
@@ -4550,7 +4683,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                     ned2 = (EXTENT_DATA2*)&ned->data[0];
                     
                     ned->generation = Vcb->superblock.generation;
-                    ned->decoded_size = tp.item->key.offset + ed->decoded_size - end_data;
+                    ned->decoded_size = ed->decoded_size;
                     ned->compression = ed->compression;
                     ned->encryption = ed->encryption;
                     ned->encoding = ed->encoding;
@@ -4558,7 +4691,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                     ned2->address = ed2->address;
                     ned2->size = ed2->size;
                     ned2->offset = ed2->address == 0 ? 0 : (ed2->offset + (end_data - tp.item->key.offset));
-                    ned2->num_bytes = tp.item->key.offset + ed->decoded_size - end_data;
+                    ned2->num_bytes = tp.item->key.offset + len - end_data;
                     
                     if (!insert_tree_item(Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, end_data, ned, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, rollback)) {
                         ERR("insert_tree_item failed\n");
@@ -4830,9 +4963,16 @@ static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data,
         TRACE("not extending extent which is not EXTENT_TYPE_REGULAR\n");
         goto end;
     }
+    
+    ed2 = (EXTENT_DATA2*)ed->data;
+    
+    if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
+        ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
+        goto end;
+    }
 
-    if (tp.item->key.offset + ed->decoded_size != start_data) {
-        TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", tp.item->key.offset, ed->decoded_size, start_data);
+    if (tp.item->key.offset + ed2->num_bytes != start_data) {
+        TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", tp.item->key.offset, ed2->num_bytes, start_data);
         goto end;
     }
     
@@ -4851,14 +4991,7 @@ static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data,
         goto end;
     }
     
-    ed2 = (EXTENT_DATA2*)ed->data;
-    
-    if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
-        ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
-        goto end;
-    }
-    
-    if (ed2->size - ed2->offset != ed->decoded_size) {
+    if (ed2->size - ed2->offset != ed2->num_bytes) {
         TRACE("last EXTENT_DATA does not run all the way to the end of the extent\n");
         goto end;
     }
@@ -5033,6 +5166,7 @@ static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data
         traverse_ptr tp;
         NTSTATUS Status;
         EXTENT_DATA* ed;
+        UINT64 len;
         
         searchkey.obj_id = fcb->inode;
         searchkey.obj_type = TYPE_EXTENT_DATA;
@@ -5065,19 +5199,24 @@ static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data
 //             return STATUS_INTERNAL_ERROR;
 //         }
 
-        if (tp.item->key.obj_type == TYPE_EXTENT_DATA && tp.item->size >= sizeof(EXTENT_DATA))
+        if (tp.item->key.obj_type == TYPE_EXTENT_DATA && tp.item->size >= sizeof(EXTENT_DATA)) {
+            EXTENT_DATA2* ed2;
+            
             ed = (EXTENT_DATA*)tp.item->data;
-        else
+            ed2 = (EXTENT_DATA2*)ed->data;
+            
+            len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
+        } else
             ed = NULL;
         
-        if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || !ed || tp.item->key.offset + ed->decoded_size < start_data) {
+        if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || !ed || tp.item->key.offset + len < start_data) {
             if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA)
                 Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, 0, start_data, rollback);
             else if (!ed)
                 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 {
-                Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, tp.item->key.offset + ed->decoded_size,
-                                              start_data - tp.item->key.offset - ed->decoded_size, rollback);
+                Status = insert_sparse_extent(Vcb, fcb->subvol, fcb->inode, tp.item->key.offset + len,
+                                              start_data - tp.item->key.offset - len, rollback);
             }
             if (!NT_SUCCESS(Status)) {
                 ERR("insert_sparse_extent returned %08x\n", Status);
@@ -5388,14 +5527,17 @@ NTSTATUS extend_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback) {
         
         oldalloc = 0;
         if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_EXTENT_DATA) {
+            EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data;
+            EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->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));
                 free_traverse_ptr(&tp);
                 return STATUS_INTERNAL_ERROR;
             }
             
-            oldalloc = tp.item->key.offset + ((EXTENT_DATA*)tp.item->data)->decoded_size;
-            cur_inline = ((EXTENT_DATA*)tp.item->data)->type == EXTENT_TYPE_INLINE;
+            oldalloc = tp.item->key.offset + (ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes);
+            cur_inline = ed->type == EXTENT_TYPE_INLINE;
         
             if (cur_inline && end > fcb->Vcb->max_inline) {
                 LIST_ENTRY changed_sector_list;
@@ -5405,7 +5547,7 @@ NTSTATUS extend_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback) {
                 
                 TRACE("giving inline file proper extents\n");
                 
-                origlength = ((EXTENT_DATA*)tp.item->data)->decoded_size;
+                origlength = ed->decoded_size;
                 
                 cur_inline = FALSE;
                 
@@ -5426,7 +5568,7 @@ NTSTATUS extend_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback) {
                 if (length > origlength)
                     RtlZeroMemory(data + origlength, length - origlength);
                 
-                RtlCopyMemory(data, ((EXTENT_DATA*)tp.item->data)->data, origlength);
+                RtlCopyMemory(data, ed->data, origlength);
                 
                 fcb->inode_item.st_blocks -= origlength;
                 
@@ -5447,7 +5589,6 @@ NTSTATUS extend_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback) {
             }
             
             if (cur_inline) {
-                EXTENT_DATA* ed;
                 ULONG edsize;
                 
                 if (end > oldalloc) {
@@ -5868,7 +6009,7 @@ static void check_extents_consistent(device_extension* Vcb, fcb* fcb) {
     ed = (EXTENT_DATA*)tp.item->data;
     ed2 = (EXTENT_DATA2*)&ed->data[0];
     
-    length = oldlength = ed->decoded_size;
+    length = oldlength = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
     lastoff = tp.item->key.offset;
     
     TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length);
@@ -5895,7 +6036,7 @@ static void check_extents_consistent(device_extension* Vcb, fcb* fcb) {
         ed = (EXTENT_DATA*)tp.item->data;
         ed2 = (EXTENT_DATA2*)&ed->data[0];
     
-        length = ed->decoded_size;
+        length = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
     
         TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length);