[BTRFS]
authorPierre Schweitzer <pierre@reactos.org>
Sun, 4 Sep 2016 15:27:46 +0000 (15:27 +0000)
committerPierre Schweitzer <pierre@reactos.org>
Sun, 4 Sep 2016 15:27:46 +0000 (15:27 +0000)
Sync btrfs to 0.6.

CORE-11937

svn path=/trunk/; revision=72576

24 files changed:
reactos/drivers/filesystems/btrfs/CMakeLists.txt
reactos/drivers/filesystems/btrfs/btrfs.c
reactos/drivers/filesystems/btrfs/btrfs.rc
reactos/drivers/filesystems/btrfs/btrfs_drv.h
reactos/drivers/filesystems/btrfs/btrfsioctl.h
reactos/drivers/filesystems/btrfs/compress.c [new file with mode: 0755]
reactos/drivers/filesystems/btrfs/create.c
reactos/drivers/filesystems/btrfs/dirctrl.c
reactos/drivers/filesystems/btrfs/extent-tree.c
reactos/drivers/filesystems/btrfs/fastio.c
reactos/drivers/filesystems/btrfs/fileinfo.c
reactos/drivers/filesystems/btrfs/flushthread.c
reactos/drivers/filesystems/btrfs/free-space.c
reactos/drivers/filesystems/btrfs/fsctl.c
reactos/drivers/filesystems/btrfs/pnp.c
reactos/drivers/filesystems/btrfs/read.c
reactos/drivers/filesystems/btrfs/registry.c
reactos/drivers/filesystems/btrfs/reparse.c
reactos/drivers/filesystems/btrfs/security.c
reactos/drivers/filesystems/btrfs/treefuncs.c
reactos/drivers/filesystems/btrfs/worker-thread.c
reactos/drivers/filesystems/btrfs/write.c
reactos/drivers/filesystems/btrfs/zlib/CMakeLists.txt [new file with mode: 0644]
reactos/media/doc/README.FSD

index 1156757..7e98893 100644 (file)
@@ -1,10 +1,13 @@
+add_subdirectory(zlib)
 
 include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/drivers
+                    ${REACTOS_SOURCE_DIR}/sdk/include/reactos/libs/zlib
                     inc)
 
 list(APPEND SOURCE
     btrfs.c
     cache.c
+    compress.c
     crc32c.c
     create.c
     dirctrl.c
@@ -29,7 +32,7 @@ add_library(btrfs SHARED ${SOURCE} btrfs.rc)
 
 add_definitions(-D__KERNEL__)
 set_module_type(btrfs kernelmodedriver)
-target_link_libraries(btrfs ntoskrnl_vista ${PSEH_LIB})
+target_link_libraries(btrfs ntoskrnl_vista zlib_solo ${PSEH_LIB})
 add_importlibs(btrfs ntoskrnl hal)
 add_pch(btrfs btrfs_drv.h SOURCE)
 add_cd_file(TARGET btrfs DESTINATION reactos/system32/drivers NO_CAB FOR all)
index 190db9c..2426362 100644 (file)
@@ -35,8 +35,9 @@
 #endif
 #include <mountdev.h>
 
-#define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \
-                            BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
+#define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
+                            BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | \
+                            BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES)
 #define COMPAT_RO_SUPPORTED 0
 
 static WCHAR device_name[] = {'\\','B','t','r','f','s',0};
@@ -53,6 +54,12 @@ LIST_ENTRY volumes;
 LIST_ENTRY VcbList;
 ERESOURCE global_loading_lock;
 UINT32 debug_log_level = 0;
+UINT32 mount_compress = 0;
+UINT32 mount_compress_force = 0;
+UINT32 mount_compress_type = 0;
+UINT32 mount_zlib_level = 3;
+UINT32 mount_flush_interval = 30;
+UINT32 mount_max_inline = 2048;
 BOOL log_started = FALSE;
 UNICODE_STRING log_device, log_file, registry_path;
 
@@ -313,7 +320,7 @@ static void STDCALL DriverUnload(PDRIVER_OBJECT DriverObject) {
         ExFreePool(registry_path.Buffer);
 }
 
-BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) {
+BOOL STDCALL get_last_inode(device_extension* Vcb, root* r, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, prev_tp;
     NTSTATUS Status;
@@ -323,13 +330,13 @@ BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) {
     searchkey.obj_type = 0xff;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return FALSE;
     }
     
-    while (find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
+    while (find_prev_item(Vcb, &tp, &prev_tp, FALSE, Irp)) {
         tp = prev_tp;
         
         TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
@@ -348,7 +355,7 @@ BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) {
     return TRUE;
 }
 
-BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen) {
+BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     DIR_ITEM* xa;
@@ -361,7 +368,7 @@ BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char*
     searchkey.obj_type = TYPE_XATTR_ITEM;
     searchkey.offset = crc32;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return FALSE;
@@ -419,7 +426,7 @@ BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char*
     return FALSE;
 }
 
-NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback) {
+NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     UINT8* di2;
@@ -429,7 +436,7 @@ NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32
     searchkey.obj_type = TYPE_DIR_ITEM;
     searchkey.offset = crc32;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -456,11 +463,11 @@ NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL, rollback);
+        insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL, Irp, rollback);
         
         ExFreePool(di);
     } else {
-        insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL, rollback);
+        insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL, Irp, rollback);
     }
     
     return STATUS_SUCCESS;
@@ -704,8 +711,24 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj
             break;
 
         case FileFsDeviceInformation:
-            FIXME("STUB: FileFsDeviceInformation\n");
+        {
+            FILE_FS_DEVICE_INFORMATION* ffdi = Irp->AssociatedIrp.SystemBuffer;
+            
+            TRACE("FileFsDeviceInformation\n");
+            
+            ffdi->DeviceType = FILE_DEVICE_DISK;
+            ffdi->Characteristics = Vcb->devices[0].devobj->Characteristics;
+            
+            if (Vcb->readonly)
+                ffdi->Characteristics |= FILE_READ_ONLY_DEVICE;
+            else
+                ffdi->Characteristics &= ~FILE_READ_ONLY_DEVICE;
+
+            BytesCopied = sizeof(FILE_FS_DEVICE_INFORMATION);
+            Status = STATUS_SUCCESS;
+            
             break;
+        }
 
         case FileFsDriverPathInformation:
             FIXME("STUB: FileFsDriverPathInformation\n");
@@ -919,7 +942,7 @@ static NTSTATUS STDCALL read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, P
 //     }
 // }
 
-NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, LIST_ENTRY* rollback) {
+NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, PIRP Irp, LIST_ENTRY* rollback) {
     root* r;
     tree* t;
     ROOT_ITEM* ri;
@@ -975,7 +998,7 @@ NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_t
     // We ask here for a traverse_ptr to the item we're inserting, so we can
     // copy some of the tree's variables
     
-    if (!insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp, rollback)) {
         ERR("insert_tree_item failed\n");
         ExFreePool(ri);
         
@@ -1159,30 +1182,46 @@ NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_t
 static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) {
     ULONG utf8len;
     NTSTATUS Status;
+    USHORT vollen, i;
     
     TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel);
     
-    Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
-    if (!NT_SUCCESS(Status))
-        goto end;
+    vollen = ffli->VolumeLabelLength;
     
-    if (utf8len > MAX_LABEL_SIZE) {
-        Status = STATUS_INVALID_VOLUME_LABEL;
-        goto end;
+    for (i = 0; i < ffli->VolumeLabelLength / sizeof(WCHAR); i++) {
+        if (ffli->VolumeLabel[i] == 0) {
+            vollen = i * sizeof(WCHAR);
+            break;
+        } else if (ffli->VolumeLabel[i] == '/' || ffli->VolumeLabel[i] == '\\') {
+            Status = STATUS_INVALID_VOLUME_LABEL;
+            goto end;
+        }
     }
     
-    // FIXME - check for '/' and '\\' and reject
-    
-//     utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
-    
-    Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE * sizeof(WCHAR), &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
-    if (!NT_SUCCESS(Status))
-        goto release;
+    if (vollen == 0) {
+        utf8len = 0;
+    } else {
+        Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, vollen);
+        if (!NT_SUCCESS(Status))
+            goto end;
+        
+        if (utf8len > MAX_LABEL_SIZE) {
+            Status = STATUS_INVALID_VOLUME_LABEL;
+            goto end;
+        }
+    }
     
     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
     
-    if (utf8len < MAX_LABEL_SIZE * sizeof(WCHAR))
-        RtlZeroMemory(Vcb->superblock.label + utf8len, (MAX_LABEL_SIZE * sizeof(WCHAR)) - utf8len);
+    if (utf8len > 0) {
+        Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE, &utf8len, ffli->VolumeLabel, vollen);
+        if (!NT_SUCCESS(Status))
+            goto release;
+    } else
+        Status = STATUS_SUCCESS;
+    
+    if (utf8len < MAX_LABEL_SIZE)
+        RtlZeroMemory(Vcb->superblock.label + utf8len, MAX_LABEL_SIZE - utf8len);
     
 //     test_tree_deletion(Vcb); // TESTING
 //     test_tree_splitting(Vcb);
@@ -1266,7 +1305,7 @@ exit:
     return Status;
 }
 
-NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -1275,7 +1314,7 @@ NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, U
     searchkey.obj_type = TYPE_DIR_ITEM;
     searchkey.offset = crc32;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -1319,7 +1358,7 @@ NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, U
                         if ((UINT8*)&di->name[di->n + di->m] - tp.item->data < tp.item->size)
                             RtlCopyMemory(dioff, &di->name[di->n + di->m], tp.item->size - ((UINT8*)&di->name[di->n + di->m] - tp.item->data));
                         
-                        insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL, rollback);
+                        insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL, Irp, rollback);
                     }
                     
                     break;
@@ -1336,7 +1375,7 @@ NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, U
     return STATUS_SUCCESS;
 }
 
-NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     BOOL changed = FALSE;
@@ -1346,7 +1385,7 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN
     searchkey.obj_type = TYPE_INODE_REF;
     searchkey.offset = parinode;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -1400,7 +1439,7 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN
                         if ((UINT8*)&ir->name[ir->n] - tp.item->data < tp.item->size)
                             RtlCopyMemory(iroff, &ir->name[ir->n], tp.item->size - ((UINT8*)&ir->name[ir->n] - tp.item->data));
                         
-                        insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, rollback);
+                        insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, Irp, rollback);
                     }
                     
                     break;
@@ -1431,7 +1470,7 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN
     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);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -1485,7 +1524,7 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN
                         if ((UINT8*)&ier->name[ier->n] - tp.item->data < tp.item->size)
                             RtlCopyMemory(ieroff, &ier->name[ier->n], tp.item->size - ((UINT8*)&ier->name[ier->n] - tp.item->data));
                         
-                        insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, rollback);
+                        insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, Irp, rollback);
                     }
                     
                     break;
@@ -1612,7 +1651,7 @@ void send_notification_fcb(file_ref* fileref, ULONG filter_match, ULONG action)
         hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
         file_ref* parfr;
         
-        Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr);
+        Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, NULL);
         
         if (!NT_SUCCESS(Status)) {
             ERR("open_fileref_by_inode returned %08x\n", Status);
@@ -1732,6 +1771,13 @@ void mark_fileref_dirty(file_ref* fileref) {
 void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) {
     LONG rc;
 
+// #ifdef DEBUG    
+//     if (!ExIsResourceAcquiredExclusiveLite(&fcb->Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&fcb->Vcb->tree_lock)) {
+//         ERR("fcb_lock not acquired exclusively\n");
+//         int3;
+//     }
+// #endif
+
     rc = InterlockedDecrement(&fcb->refcount);
     
 #ifdef DEBUG_FCB_REFCOUNTS
@@ -1746,7 +1792,7 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line)
     if (rc > 0)
         return;
     
-    ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
+//     ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
     
     if (fcb->list_entry.Flink)
         RemoveEntryList(&fcb->list_entry);
@@ -1754,7 +1800,7 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line)
     if (fcb->list_entry_all.Flink)
         RemoveEntryList(&fcb->list_entry_all);
     
-    ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
+//     ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
    
     ExDeleteResourceLite(&fcb->nonpaged->resource);
     ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
@@ -1821,6 +1867,13 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line)
 void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line) {
     LONG rc;
 
+// #ifdef DEBUG    
+//     if (!ExIsResourceAcquiredExclusiveLite(&fr->fcb->Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&fr->fcb->Vcb->tree_lock) && !fr->dirty) {
+//         ERR("fcb_lock not acquired exclusively\n");
+//         int3;
+//     }
+// #endif
+
     rc = InterlockedDecrement(&fr->refcount);
     
 #ifdef DEBUG_FCB_REFCOUNTS
@@ -1915,6 +1968,9 @@ static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObjec
     
     CcUninitializeCacheMap(FileObject, NULL, NULL);
     
+    if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
+        return STATUS_SUCCESS;
+    
     ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
     
     if (fileref)
@@ -1935,9 +1991,9 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) {
     LIST_ENTRY* le;
     LARGE_INTEGER time;
     
-#ifndef __REACTOS__
+    Vcb->removing = TRUE;
+    
     RemoveEntryList(&Vcb->list_entry);
-#endif
     
     Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
     if (!NT_SUCCESS(Status))
@@ -1948,8 +2004,8 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) {
         
         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
 
-        if (Vcb->need_write)
-            do_write(Vcb, &rollback);
+        if (Vcb->need_write && !Vcb->readonly)
+            do_write(Vcb, NULL, &rollback);
         
         free_trees(Vcb);
         
@@ -1969,8 +2025,6 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) {
     
     ExFreePool(Vcb->threads.threads);
     
-    Vcb->removing = TRUE;
-    
     time.QuadPart = 0;
     KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
     KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, FALSE, NULL);
@@ -2027,10 +2081,9 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) {
         if (c->cache)
             free_fcb(c->cache);
         
-        ExDeleteResourceLite(&c->nonpaged->lock);
-        ExDeleteResourceLite(&c->nonpaged->changed_extents_lock);
+        ExDeleteResourceLite(&c->lock);
+        ExDeleteResourceLite(&c->changed_extents_lock);
         
-        ExFreePool(c->nonpaged);
         ExFreePool(c->chunk_item);
         ExFreePool(c);
     }
@@ -2064,7 +2117,7 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) {
     ZwClose(Vcb->flush_thread_handle);
 }
 
-NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
+NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, PIRP Irp, LIST_ENTRY* rollback) {
     LARGE_INTEGER newlength, time;
     BTRFS_TIME now;
     NTSTATUS Status;
@@ -2103,7 +2156,7 @@ NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY*
                 // excise extents
                 
                 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
-                    Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), rollback);
+                    Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), Irp, rollback);
                     if (!NT_SUCCESS(Status)) {
                         ERR("excise_extents returned %08x\n", Status);
                         ExReleaseResourceLite(fileref->fcb->Header.Resource);
@@ -2265,7 +2318,7 @@ static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
                     
                     ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
                     
-                    Status = delete_fileref(fileref, FileObject, &rollback);
+                    Status = delete_fileref(fileref, FileObject, Irp, &rollback);
                     if (!NT_SUCCESS(Status)) {
                         ERR("delete_fileref returned %08x\n", Status);
                         do_rollback(Vcb, &rollback);
@@ -2319,14 +2372,14 @@ exit2:
     return Status;
 }
 
-ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa) {
+ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa, PIRP Irp) {
     ULONG att;
     char* eaval;
     UINT16 ealen;
     
     // ii can be NULL
     
-    if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen)) {
+    if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen, Irp)) {
         if (ealen > 2) {
             if (eaval[0] == '0' && eaval[1] == 'x') {
                 int i;
@@ -2388,7 +2441,7 @@ ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r
     return att;
 }
 
-NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer) {
+static NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer, BOOL override) {
     IO_STATUS_BLOCK* IoStatus;
     LARGE_INTEGER Offset;
     PIRP Irp;
@@ -2425,9 +2478,13 @@ NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, UL
         goto exit;
     }
     
+    Irp->Flags |= IRP_NOCACHE;
     IrpSp = IoGetNextIrpStackLocation(Irp);
     IrpSp->MajorFunction = IRP_MJ_READ;
     
+    if (override)
+        IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+    
     if (DeviceObject->Flags & DO_BUFFERED_IO) {
         FIXME("FIXME - buffered IO\n");
     } else if (DeviceObject->Flags & DO_DIRECT_IO) {
@@ -2461,20 +2518,10 @@ NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, UL
     
     IoSetCompletionRoutine(Irp, read_completion, context, TRUE, TRUE, TRUE);
 
-//     if (Override)
-//     {
-//         Stack = IoGetNextIrpStackLocation(Irp);
-//         Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
-//     }
-
-//     TRACE("Calling IO Driver... with irp %p\n", Irp);
     Status = IoCallDriver(DeviceObject, Irp);
 
-//     TRACE("Waiting for IO Operation for %p\n", Irp);
     if (Status == STATUS_PENDING) {
-//         TRACE("Operation pending\n");
         KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
-//         TRACE("Getting IO Status... for %p\n", Irp);
         Status = context->iosb.Status;
     }
     
@@ -2512,7 +2559,7 @@ static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT de
         if (i > 0 && superblock_addrs[i] + sizeof(superblock) > length)
             break;
         
-        Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb);
+        Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb, FALSE);
         if (!NT_SUCCESS(Status)) {
             ERR("Failed to read superblock %u: %08x\n", i, Status);
             ExFreePool(sb);
@@ -2646,7 +2693,7 @@ static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr,
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
+static NTSTATUS STDCALL look_for_roots(device_extension* Vcb, PIRP Irp) {
     traverse_ptr tp, next_tp;
     KEY searchkey;
     BOOL b;
@@ -2656,7 +2703,7 @@ static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
     searchkey.obj_type = 0;
     searchkey.offset = 0;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_tree returned %08x\n", Status);
         return Status;
@@ -2681,7 +2728,7 @@ static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
             }
         }
     
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         
         if (b)
             tp = next_tp;
@@ -2690,7 +2737,7 @@ static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
+static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
     BOOL b;
@@ -2703,7 +2750,7 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
     searchkey.obj_type = TYPE_DEV_EXTENT;
     searchkey.offset = 0;
     
-    Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_tree returned %08x\n", Status);
         return Status;
@@ -2730,7 +2777,7 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
             }
         }
     
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         
         if (b) {
             tp = next_tp;
@@ -2858,7 +2905,7 @@ static void init_device(device_extension* Vcb, device* dev, BOOL get_length) {
     }
 }
 
-static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
+static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb, PIRP Irp) {
     traverse_ptr tp, next_tp;
     KEY searchkey;
     BOOL b;
@@ -2872,7 +2919,7 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
     
     Vcb->data_flags = 0;
     
-    Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -2950,33 +2997,24 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
             if (tp.item->size < sizeof(CHUNK_ITEM)) {
                 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(CHUNK_ITEM));
             } else {            
-                c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
+                c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
                 
                 if (!c) {
                     ERR("out of memory\n");
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
                 
-                c->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk_nonpaged), ALLOC_TAG);
-                
-                if (!c->nonpaged) {
-                    ERR("out of memory\n");
-                    ExFreePool(c);
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-                
                 c->size = tp.item->size;
                 c->offset = tp.item->key.offset;
                 c->used = c->oldused = 0;
                 c->cache = NULL;
                 c->created = FALSE;
                 
-                c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+                c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, tp.item->size, ALLOC_TAG);
                 
                 if (!c->chunk_item) {
                     ERR("out of memory\n");
                     ExFreePool(c);
-                    ExFreePool(c->nonpaged);
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
             
@@ -2988,12 +3026,11 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
                 if (c->chunk_item->num_stripes > 0) {
                     CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
                     
-                    c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
+                    c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
                     
                     if (!c->devices) {
                         ERR("out of memory\n");
                         ExFreePool(c);
-                        ExFreePool(c->nonpaged);
                         ExFreePool(c->chunk_item);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
@@ -3005,8 +3042,8 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
                 } else
                     c->devices = NULL;
                 
-                ExInitializeResourceLite(&c->nonpaged->lock);
-                ExInitializeResourceLite(&c->nonpaged->changed_extents_lock);
+                ExInitializeResourceLite(&c->lock);
+                ExInitializeResourceLite(&c->changed_extents_lock);
                 
                 InitializeListHead(&c->space);
                 InitializeListHead(&c->space_size);
@@ -3019,7 +3056,7 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
             }
         }
     
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         
         if (b)
             tp = next_tp;
@@ -3094,7 +3131,7 @@ void protect_superblocks(device_extension* Vcb, chunk* c) {
     }
 }
 
-static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) {
+static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb, PIRP Irp) {
     LIST_ENTRY* le = Vcb->chunks.Flink;
     chunk* c;
     KEY searchkey;
@@ -3113,7 +3150,7 @@ static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) {
         searchkey.obj_id = c->offset;
         searchkey.offset = c->chunk_item->size;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -3141,7 +3178,7 @@ static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) {
         // FIXME - make sure we free occasionally after doing one of these, or we
         // might use up a lot of memory with a big disk.
         
-        Status = load_free_space_cache(Vcb, c);
+        Status = load_free_space_cache(Vcb, c, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("load_free_space_cache returned %08x\n", Status);
             return Status;
@@ -3253,12 +3290,24 @@ static NTSTATUS load_sys_chunks(device_extension* Vcb) {
     return STATUS_SUCCESS;
 }
 
-static root* find_default_subvol(device_extension* Vcb) {
+static root* find_default_subvol(device_extension* Vcb, PIRP Irp) {
     LIST_ENTRY* le;
     
     static char fn[] = "default";
     static UINT32 crc32 = 0x8dbfc2d2;
     
+    if (Vcb->options.subvol_id != 0) {
+        le = Vcb->roots.Flink;
+        while (le != &Vcb->roots) {
+            root* r = CONTAINING_RECORD(le, root, list_entry);
+            
+            if (r->id == Vcb->options.subvol_id)
+                return r;
+            
+            le = le->Flink;
+        }
+    }
+    
     if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
         NTSTATUS Status;
         KEY searchkey;
@@ -3269,7 +3318,7 @@ static root* find_default_subvol(device_extension* Vcb) {
         searchkey.obj_type = TYPE_DIR_ITEM;
         searchkey.offset = crc32;
         
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             goto end;
@@ -3334,7 +3383,7 @@ static NTSTATUS create_worker_threads(PDEVICE_OBJECT DeviceObject) {
     ULONG i;
     NTSTATUS Status;
     
-    Vcb->threads.num_threads = max(3, KeQueryActiveProcessorCount(NULL)); // FIXME - number of processors?
+    Vcb->threads.num_threads = max(3, KeQueryActiveProcessorCount(NULL));
     
     Vcb->threads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_thread) * Vcb->threads.num_threads, ALLOC_TAG);
     if (!Vcb->threads.threads) {
@@ -3366,6 +3415,8 @@ static NTSTATUS create_worker_threads(PDEVICE_OBJECT DeviceObject) {
         }
     }
     
+    Vcb->threads.pending_jobs = 0;
+    
     return STATUS_SUCCESS;
 }
 
@@ -3375,6 +3426,9 @@ BOOL add_thread_job(device_extension* Vcb, PIRP Irp) {
     
     threadnum = InterlockedIncrement(&Vcb->threads.next_thread) % Vcb->threads.num_threads;
     
+    if (Vcb->threads.pending_jobs >= Vcb->threads.num_threads)
+        return FALSE;
+    
     if (Vcb->threads.threads[threadnum].quit)
         return FALSE;
     
@@ -3388,6 +3442,8 @@ BOOL add_thread_job(device_extension* Vcb, PIRP Irp) {
     
     tj->Irp = Irp;
     
+    InterlockedIncrement(&Vcb->threads.pending_jobs);
+    
     ExInterlockedInsertTailList(&Vcb->threads.threads[threadnum].jobs, &tj->list_entry, &Vcb->threads.threads[threadnum].spin_lock);
     KeSetEvent(&Vcb->threads.threads[threadnum].event, 0, FALSE);
     
@@ -3527,7 +3583,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         TRACE("btrfs magic found\n");
     }
     
-    Status = registry_load_volume_options(&Vcb->superblock.uuid, &Vcb->options);
+    Status = registry_load_volume_options(Vcb);
     if (!NT_SUCCESS(Status)) {
         ERR("registry_load_volume_options returned %08x\n", Status);
         goto exit;
@@ -3564,10 +3620,13 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         Vcb->readonly = TRUE;
     }
     
+    if (Vcb->options.readonly)
+        Vcb->readonly = TRUE;
+    
     Vcb->superblock.generation++;
     Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF;
     
-    Vcb->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG);
+    Vcb->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG);
     if (!Vcb->devices) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -3595,8 +3654,6 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     
     Vcb->log_to_phys_loaded = FALSE;
     
-    Vcb->max_inline = Vcb->superblock.node_size / 2;
-
     add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, NULL);
     
     if (!Vcb->chunk_root) {
@@ -3629,7 +3686,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
 
     FsRtlNotifyInitializeSync(&Vcb->NotifySync);
     
-    Status = load_chunk_root(Vcb);
+    Status = load_chunk_root(Vcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("load_chunk_root returned %08x\n", Status);
         goto exit;
@@ -3663,29 +3720,31 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         goto exit;
     }
     
-    Status = look_for_roots(Vcb);
+    Status = look_for_roots(Vcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("look_for_roots returned %08x\n", Status);
         goto exit;
     }
     
-    Status = find_chunk_usage(Vcb);
-    if (!NT_SUCCESS(Status)) {
-        ERR("find_chunk_usage returned %08x\n", Status);
-        goto exit;
+    if (!Vcb->readonly) {
+        Status = find_chunk_usage(Vcb, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("find_chunk_usage returned %08x\n", Status);
+            goto exit;
+        }
     }
     
     // We've already increased the generation by one
     if (!Vcb->readonly && Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation) {
         WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb->superblock.generation - 1, Vcb->superblock.cache_generation);
-        Status = clear_free_space_cache(Vcb);
+        Status = clear_free_space_cache(Vcb, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("clear_free_space_cache returned %08x\n", Status);
             goto exit;
         }
     }
     
-    Vcb->volume_fcb = create_fcb();
+    Vcb->volume_fcb = create_fcb(NonPagedPool);
     if (!Vcb->volume_fcb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -3695,7 +3754,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     Vcb->volume_fcb->Vcb = Vcb;
     Vcb->volume_fcb->sd = NULL;
     
-    root_fcb = create_fcb();
+    root_fcb = create_fcb(NonPagedPool);
     if (!root_fcb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -3711,7 +3770,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     WARN("root FCB = %p\n", root_fcb);
 #endif
     
-    root_fcb->subvol = find_default_subvol(Vcb);
+    root_fcb->subvol = find_default_subvol(Vcb, Irp);
 
     if (!root_fcb->subvol) {
         ERR("could not find top subvol\n");
@@ -3723,7 +3782,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     searchkey.obj_type = TYPE_INODE_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto exit;
@@ -3738,9 +3797,9 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     if (tp.item->size > 0)
         RtlCopyMemory(&root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
     
-    fcb_get_sd(root_fcb, NULL);
+    fcb_get_sd(root_fcb, NULL, Irp);
     
-    root_fcb->atts = get_file_attributes(Vcb, &root_fcb->inode_item, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE);
+    root_fcb->atts = get_file_attributes(Vcb, &root_fcb->inode_item, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE, Irp);
     
     Vcb->root_fileref = create_fileref();
     if (!Vcb->root_fileref) {
@@ -3764,18 +3823,13 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     
     Vcb->root_file = IoCreateStreamFileObject(NULL, DeviceToMount);
     Vcb->root_file->FsContext = root_fcb;
-#ifdef __REACTOS__
     Vcb->root_file->SectionObjectPointer = &root_fcb->nonpaged->segment_object;
     Vcb->root_file->Vpb = DeviceObject->Vpb;
-#endif
     
     RtlZeroMemory(root_ccb, sizeof(ccb));
     root_ccb->NodeType = BTRFS_NODE_TYPE_CCB;
     root_ccb->NodeSize = sizeof(ccb);
     
-#ifndef __REACTOS__
-    Vcb->root_file->FsContext = root_ccb;
-#else
     Vcb->root_file->FsContext2 = root_ccb;
     
     _SEH2_TRY {
@@ -3784,10 +3838,9 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         Status = _SEH2_GetExceptionCode();
         goto exit;
     } _SEH2_END;
-#endif
     
     for (i = 0; i < Vcb->superblock.num_devices; i++) {
-        Status = find_disk_holes(Vcb, &Vcb->devices[i]);
+        Status = find_disk_holes(Vcb, &Vcb->devices[i], Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("find_disk_holes returned %08x\n", Status);
             goto exit;
@@ -3867,6 +3920,92 @@ exit:
     return Status;
 }
 
+static NTSTATUS verify_volume(PDEVICE_OBJECT device) {
+    device_extension* Vcb = device->DeviceExtension;
+    ULONG cc, to_read;
+    IO_STATUS_BLOCK iosb;
+    NTSTATUS Status;
+    superblock* sb;
+    UINT32 crc32;
+    UINT64 i;
+    
+    if (Vcb->removing)
+        return STATUS_WRONG_VOLUME;
+    
+    Status = dev_ioctl(Vcb->Vpb->RealDevice, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+    
+    if (!NT_SUCCESS(Status)) {
+        ERR("dev_ioctl returned %08x\n", Status);
+        return Status;
+    }
+    
+    to_read = sector_align(sizeof(superblock), device->SectorSize);
+    
+    sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+    if (!sb) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    Status = sync_read_phys(Vcb->Vpb->RealDevice, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("Failed to read superblock: %08x\n", Status);
+        ExFreePool(sb);
+        return Status;
+    }
+    
+    if (sb->magic != BTRFS_MAGIC) {
+        ERR("not a BTRFS volume\n");
+        ExFreePool(sb);
+        return STATUS_WRONG_VOLUME;
+    }
+    
+    if (RtlCompareMemory(&sb->uuid, &Vcb->superblock.uuid, sizeof(BTRFS_UUID)) != sizeof(BTRFS_UUID)) {
+        ERR("different UUIDs\n");
+        ExFreePool(sb);
+        return STATUS_WRONG_VOLUME;
+    }
+    
+    crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+    TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
+    
+    if (crc32 != *((UINT32*)sb->checksum)) {
+        ERR("different UUIDs\n");
+        ExFreePool(sb);
+        return STATUS_WRONG_VOLUME;
+    }
+    
+    ExFreePool(sb);
+    
+    for (i = 0; i < Vcb->superblock.num_devices; i++) {
+        if (Vcb->devices[i].removable) {
+            NTSTATUS Status;
+            ULONG cc;
+            IO_STATUS_BLOCK iosb;
+            
+            Status = dev_ioctl(Vcb->devices[i].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("dev_ioctl returned %08x\n", Status);
+                return Status;
+            }
+            
+            if (iosb.Information < sizeof(ULONG)) {
+                ERR("iosb.Information was too short\n");
+                return STATUS_INTERNAL_ERROR;
+            }
+            
+            Vcb->devices[i].change_count = cc;
+        }
+        
+        Vcb->devices[i].devobj->Flags &= ~DO_VERIFY_VOLUME;
+    }
+    
+    Vcb->Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
+    
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     PIO_STACK_LOCATION IrpSp;
     NTSTATUS Status;
@@ -3910,7 +4049,15 @@ static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject,
             break;
             
         case IRP_MN_VERIFY_VOLUME:
-            FIXME("STUB: IRP_MN_VERIFY_VOLUME\n");
+            TRACE("IRP_MN_VERIFY_VOLUME\n");
+            
+            Status = verify_volume(DeviceObject);
+            
+            if (!NT_SUCCESS(Status) && Vcb->Vpb->Flags & VPB_MOUNTED) {
+                uninit(Vcb, FALSE);
+//                 Vcb->Vpb->Flags &= ~VPB_MOUNTED;
+            }
+            
             break;
            
         default:
@@ -4136,8 +4283,7 @@ static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp
     Status = STATUS_SUCCESS;
 
     while (!IsListEmpty(&VcbList)) {
-        LIST_ENTRY* le = RemoveHeadList(&VcbList);
-        Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
+        Vcb = CONTAINING_RECORD(VcbList.Flink, device_extension, list_entry);
         
         TRACE("shutting down Vcb %p\n", Vcb);
         
index 3246f5d..4d7d89e 100644 (file)
@@ -75,7 +75,7 @@ BEGIN
             VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016"
             VALUE "OriginalFilename", "btrfs.sys"
             VALUE "ProductName", "WinBtrfs"
-            VALUE "ProductVersion", "0.5"
+            VALUE "ProductVersion", "0.6"
         END
     END
     BLOCK "VarFileInfo"
index d0b62fa..1b01d92 100644 (file)
@@ -56,6 +56,7 @@
 #define BTRFS_NODE_TYPE_FCB 0x2296
 
 #define ALLOC_TAG 0x7442484D //'MHBt'
+#define ALLOC_TAG_ZLIB 0x7A42484D //'MHBz'
 
 #define STDCALL __stdcall
 
 #define EA_REPARSE "system.reparse"
 #define EA_REPARSE_HASH 0x786f6167
 
-#define READ_AHEAD_GRANULARITY 0x10000 // 64 KB
-
 #define MAX_EXTENT_SIZE 0x8000000 // 128 MB
+#define COMPRESSED_EXTENT_SIZE 0x20000 // 128 KB
+
+#define READ_AHEAD_GRANULARITY COMPRESSED_EXTENT_SIZE // really ought to be a multiple of COMPRESSED_EXTENT_SIZE
 
 #ifdef _MSC_VER
 #define try __try
@@ -87,7 +89,7 @@
 
 // #pragma pack(push, 1)
 
-struct device_extension;
+struct _device_extension;
 
 typedef struct {
     BTRFS_UUID fsuuid;
@@ -176,6 +178,7 @@ typedef struct _fcb {
     
     BOOL ads;
     UINT32 adshash;
+    ULONG adsmaxlen;
     ANSI_STRING adsxattr;
     ANSI_STRING adsdata;
     
@@ -344,11 +347,6 @@ typedef struct {
     LIST_ENTRY space;
 } device;
 
-typedef struct {
-    ERESOURCE lock;
-    ERESOURCE changed_extents_lock;
-} chunk_nonpaged;
-
 typedef struct {
     CHUNK_ITEM* chunk_item;
     UINT32 size;
@@ -361,7 +359,8 @@ typedef struct {
     LIST_ENTRY space_size;
     LIST_ENTRY deleting;
     LIST_ENTRY changed_extents;
-    chunk_nonpaged* nonpaged;
+    ERESOURCE lock;
+    ERESOURCE changed_extents_lock;
     BOOL created;
     
     LIST_ENTRY list_entry;
@@ -424,10 +423,19 @@ typedef struct {
     ULONG num_threads;
     LONG next_thread;
     drv_thread* threads;
+    LONG pending_jobs;
 } drv_threads;
 
 typedef struct {
     BOOL ignore;
+    BOOL compress;
+    BOOL compress_force;
+    UINT8 compress_type;
+    BOOL readonly;
+    UINT32 zlib_level;
+    UINT32 flush_interval;
+    UINT32 max_inline;
+    UINT64 subvol_id;
 } mount_options;
 
 #define VCB_TYPE_VOLUME     1
@@ -471,7 +479,6 @@ typedef struct _device_extension {
     root* dev_root;
     root* uuid_root;
     BOOL log_to_phys_loaded;
-    UINT32 max_inline;
     LIST_ENTRY sys_chunks;
     LIST_ENTRY chunks;
     LIST_ENTRY chunks_changed;
@@ -528,10 +535,10 @@ enum write_data_status {
     WriteDataStatus_Ignore
 };
 
-struct write_data_context;
+struct _write_data_context;
 
 typedef struct {
-    struct write_data_context* context;
+    struct _write_data_context* context;
     UINT8* buf;
     BOOL need_free;
     device* device;
@@ -541,7 +548,7 @@ typedef struct {
     LIST_ENTRY list_entry;
 } write_data_stripe;
 
-typedef struct {
+typedef struct _write_data_context {
     KEVENT Event;
     LIST_ENTRY stripes;
     LONG stripes_left;
@@ -596,7 +603,7 @@ static __inline void get_raid0_offset(UINT64 off, UINT64 stripe_length, UINT16 n
     startoff = off % (num_stripes * stripe_length);
     initoff = (off / (num_stripes * stripe_length)) * stripe_length;
     
-    *stripe = startoff / stripe_length;
+    *stripe = (UINT16)(startoff / stripe_length);
     *stripeoff = initoff + startoff - (*stripe * stripe_length);
 }
 
@@ -604,19 +611,19 @@ static __inline void get_raid0_offset(UINT64 off, UINT64 stripe_length, UINT16 n
 device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid);
 UINT64 sector_align( UINT64 NumberToBeAligned, UINT64 Alignment );
 int keycmp(const KEY* key1, const KEY* key2);
-ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa);
-BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen);
+ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa, PIRP Irp);
+BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen, PIRP Irp);
 void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line);
 void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line);
-BOOL STDCALL get_last_inode(device_extension* Vcb, root* r);
-NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback);
-NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback);
-NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback);
-fcb* create_fcb();
+BOOL STDCALL get_last_inode(device_extension* Vcb, root* r, PIRP Irp);
+NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback);
+fcb* create_fcb(POOL_TYPE pool_type);
 file_ref* create_fileref();
 void protect_superblocks(device_extension* Vcb, chunk* c);
 BOOL is_top_level(PIRP Irp);
-NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, LIST_ENTRY* rollback);
+NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, PIRP Irp, LIST_ENTRY* rollback);
 void STDCALL uninit(device_extension* Vcb, BOOL flush);
 NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer,
                            ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override, IO_STATUS_BLOCK* iosb);
@@ -629,7 +636,7 @@ BOOL add_thread_job(device_extension* Vcb, PIRP Irp);
 NTSTATUS part0_passthrough(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 void mark_fcb_dirty(fcb* fcb);
 void mark_fileref_dirty(file_ref* fileref);
-NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback);
+NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, PIRP Irp, LIST_ENTRY* rollback);
 
 #ifdef _MSC_VER
 #define funcname __FUNCTION__
@@ -641,6 +648,13 @@ NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY*
 #define free_fcb(fcb) _free_fcb(fcb, funcname, __FILE__, __LINE__)
 #define free_fileref(fileref) _free_fileref(fileref, funcname, __FILE__, __LINE__)
 
+extern UINT32 mount_compress;
+extern UINT32 mount_compress_force;
+extern UINT32 mount_compress_type;
+extern UINT32 mount_zlib_level;
+extern UINT32 mount_flush_interval;
+extern UINT32 mount_max_inline;
+
 #ifdef _DEBUG
 
 extern BOOL log_started;
@@ -696,34 +710,50 @@ void STDCALL init_fast_io_dispatch(FAST_IO_DISPATCH** fiod);
 // in crc32c.c
 UINT32 STDCALL calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen);
 
+typedef struct {
+    LIST_ENTRY* list;
+    LIST_ENTRY* list_size;
+    UINT64 address;
+    UINT64 length;
+    chunk* chunk;
+} rollback_space;
+
+typedef struct {
+    fcb* fcb;
+    extent* ext;
+} rollback_extent;
+
 enum rollback_type {
     ROLLBACK_INSERT_ITEM,
     ROLLBACK_DELETE_ITEM,
     ROLLBACK_INSERT_EXTENT,
-    ROLLBACK_DELETE_EXTENT
+    ROLLBACK_DELETE_EXTENT,
+    ROLLBACK_ADD_SPACE,
+    ROLLBACK_SUBTRACT_SPACE
 };
 
 // in treefuncs.c
-NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line);
-BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
-BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
+NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, PIRP Irp, const char* func, const char* file, unsigned int line);
+BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, PIRP Irp, const char* func, const char* file, unsigned int line);
+BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, PIRP Irp, const char* func, const char* file, unsigned int line);
 void STDCALL free_trees(device_extension* Vcb);
-BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp, LIST_ENTRY* rollback);
+BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp, PIRP Irp, LIST_ENTRY* rollback);
 void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp, LIST_ENTRY* rollback);
 tree* STDCALL _free_tree(tree* t, const char* func, const char* file, unsigned int line);
-NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, const char* func, const char* file, unsigned int line);
-NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, const char* func, const char* file, unsigned int line);
+NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, PIRP Irp, const char* func, const char* file, unsigned int line);
+NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, PIRP Irp,
+                               const char* func, const char* file, unsigned int line);
 void clear_rollback(LIST_ENTRY* rollback);
 void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback);
 void free_trees_root(device_extension* Vcb, root* r);
 void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr);
 
-#define find_item(Vcb, r, tp, searchkey, ignore) _find_item(Vcb, r, tp, searchkey, ignore, funcname, __FILE__, __LINE__)
-#define find_next_item(Vcb, tp, next_tp, ignore) _find_next_item(Vcb, tp, next_tp, ignore, funcname, __FILE__, __LINE__)
-#define find_prev_item(Vcb, tp, prev_tp, ignore) _find_prev_item(Vcb, tp, prev_tp, ignore, funcname, __FILE__, __LINE__)
+#define find_item(Vcb, r, tp, searchkey, ignore, Irp) _find_item(Vcb, r, tp, searchkey, ignore, Irp, funcname, __FILE__, __LINE__)
+#define find_next_item(Vcb, tp, next_tp, ignore, Irp) _find_next_item(Vcb, tp, next_tp, ignore, Irp, funcname, __FILE__, __LINE__)
+#define find_prev_item(Vcb, tp, prev_tp, ignore, Irp) _find_prev_item(Vcb, tp, prev_tp, ignore, Irp, funcname, __FILE__, __LINE__)
 #define free_tree(t) _free_tree(t, funcname, __FILE__, __LINE__)
-#define load_tree(t, addr, r, pt, parent) _load_tree(t, addr, r, pt, parent, funcname, __FILE__, __LINE__)
-#define do_load_tree(Vcb, th, r, t, td, loaded) _do_load_tree(Vcb, th, r, t, td, loaded, funcname, __FILE__, __LINE__)  
+#define load_tree(t, addr, r, pt, parent, Irp) _load_tree(t, addr, r, pt, parent, Irp, funcname, __FILE__, __LINE__)
+#define do_load_tree(Vcb, th, r, t, td, loaded, Irp) _do_load_tree(Vcb, th, r, t, td, loaded, Irp, funcname, __FILE__, __LINE__)  
 
 // in search.c
 void STDCALL look_for_vols(PDRIVER_OBJECT DriverObject, LIST_ENTRY* volumes);
@@ -734,51 +764,52 @@ void STDCALL free_cache();
 extern CACHE_MANAGER_CALLBACKS* cache_callbacks;
 
 // in write.c
-NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback);
+NTSTATUS STDCALL do_write(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback);
 NTSTATUS write_file(device_extension* Vcb, PIRP Irp, BOOL wait, BOOL deferred_write);
 NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache,
                      BOOL wait, BOOL deferred_write, LIST_ENTRY* rollback);
-NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback);
+NTSTATUS truncate_file(fcb* fcb, UINT64 end, PIRP Irp, LIST_ENTRY* rollback);
 NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIRP Irp, LIST_ENTRY* rollback);
-NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* rollback);
+NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, PIRP Irp, LIST_ENTRY* rollback);
 void commit_checksum_changes(device_extension* Vcb, LIST_ENTRY* changed_sector_list);
-NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback);
 chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address);
-chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback);
-NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp);
-NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp);
+chunk* alloc_chunk(device_extension* Vcb, UINT64 flags);
+NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp, chunk* c);
+NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp, chunk* c);
 void free_write_data_stripes(write_data_context* wtc);
-NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollback);
+NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback);
 NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback);
+void flush_fcb(fcb* fcb, BOOL cache, PIRP Irp, LIST_ENTRY* rollback);
 BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, BOOL prealloc, void* data, LIST_ENTRY* changed_sector_list,
-                         PIRP Irp, LIST_ENTRY* rollback);
-NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
+                         PIRP Irp, LIST_ENTRY* rollback, UINT8 compression, UINT64 decoded_size);
 NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
-void remove_fcb_extent(extent* ext, LIST_ENTRY* rollback);
 NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset,
-                                   signed long long count, BOOL no_csum, UINT64 new_size);
+                                   signed long long count, BOOL no_csum, UINT64 new_size, PIRP Irp);
+NTSTATUS do_write_file(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS write_compressed(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
 
 // in dirctrl.c
 NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts, PIRP Irp);
 
 // in security.c
 NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-void fcb_get_sd(fcb* fcb, struct _fcb* parent);
+void fcb_get_sd(fcb* fcb, struct _fcb* parent, PIRP Irp);
 // UINT32 STDCALL get_uid();
 void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid);
 UINT32 sid_to_uid(PSID sid);
+void uid_to_sid(UINT32 uid, PSID* sid);
 NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* parfileref, ACCESS_STATE* as);
 
 // in fileinfo.c
 NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback);
+NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback);
 BOOL has_open_children(file_ref* fileref);
 NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT64 end, fcb* fcb, file_ref* fileref, PFILE_OBJECT FileObject, BOOL advance_only, LIST_ENTRY* rollback);
 NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset);
-NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr);
+NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp);
 
 // in reparse.c
 NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen);
@@ -787,15 +818,14 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp);
 
 // in create.c
 NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr, root** subvol,
-                                             UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8);
 NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, file_ref* fr,
-                                  root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8);
-NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed);
-NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb);
-NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr, UINT32 streamhash, fcb* parent, fcb** pfcb);
+                                  root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8, PIRP Irp);
+NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed, ULONG* fn_offset, PIRP Irp);
+NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, PIRP Irp);
+NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr, UINT32 streamhash, fcb* parent, fcb** pfcb, PIRP Irp);
 void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock);
-NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index);
+NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index, PIRP Irp);
+NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp);
 
 // in fsctl.c
 NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user);
@@ -814,29 +844,29 @@ NTSTATUS do_read(PIRP Irp, BOOL wait, ULONG* bytes_read);
 NTSTATUS STDCALL drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp);
 
 // in free-space.c
-NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c);
-NTSTATUS clear_free_space_cache(device_extension* Vcb);
-NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollback);
-NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback);
+NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp);
+NTSTATUS clear_free_space_cache(device_extension* Vcb, PIRP Irp);
+NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS update_chunk_caches(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback);
 NTSTATUS add_space_entry(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 offset, UINT64 size);
 void _space_list_add(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
-void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
+void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func);
 void _space_list_subtract(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
-void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
+void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func);
 
 #define space_list_add(Vcb, c, deleting, address, length, rollback) _space_list_add(Vcb, c, deleting, address, length, rollback, funcname)
-#define space_list_add2(list, list_size, address, length, rollback) _space_list_add2(list, list_size, address, length, rollback, funcname)
+#define space_list_add2(list, list_size, address, length, rollback) _space_list_add2(list, list_size, address, length, NULL, rollback, funcname)
 #define space_list_subtract(Vcb, c, deleting, address, length, rollback) _space_list_subtract(Vcb, c, deleting, address, length, rollback, funcname)
-#define space_list_subtract2(list, list_size, address, length, rollback) _space_list_subtract2(list, list_size, address, length, rollback, funcname)
+#define space_list_subtract2(list, list_size, address, length, rollback) _space_list_subtract2(list, list_size, address, length, NULL, rollback, funcname)
 
 // in extent-tree.c
-NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback);
-NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback);
-NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, LIST_ENTRY* rollback);
-NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, LIST_ENTRY* rollback);
+NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, PIRP Irp, LIST_ENTRY* rollback);
 void decrease_chunk_usage(chunk* c, UINT64 delta);
-NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback);
-UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset);
+NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp, LIST_ENTRY* rollback);
+UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, PIRP Irp);
 
 // in worker-thread.c
 void STDCALL worker_thread(void* context);
@@ -847,7 +877,11 @@ void do_write_job(device_extension* Vcb, PIRP Irp);
 void STDCALL read_registry(PUNICODE_STRING regpath);
 NTSTATUS registry_mark_volume_mounted(BTRFS_UUID* uuid);
 NTSTATUS registry_mark_volume_unmounted(BTRFS_UUID* uuid);
-NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options);
+NTSTATUS registry_load_volume_options(device_extension* Vcb);
+
+// in compress.c
+NTSTATUS decompress(UINT8 type, UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen);
+NTSTATUS write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
 
 #define fast_io_possible(fcb) (!FsRtlAreThereCurrentFileLocks(&fcb->lock) && !fcb->Vcb->readonly ? FastIoIsPossible : FastIoIsQuestionable)
 
@@ -874,6 +908,23 @@ static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY*
         head->Blink = item;
 }
 
+static __inline BOOL write_fcb_compressed(fcb* fcb) {
+    // make sure we don't accidentally write the cache inodes or pagefile compressed
+    if (fcb->subvol->id == BTRFS_ROOT_ROOT || fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE)
+        return FALSE;
+    
+    if (fcb->Vcb->options.compress_force)
+        return TRUE;
+    
+    if (fcb->inode_item.flags & BTRFS_INODE_NOCOMPRESS)
+        return FALSE;
+    
+    if (fcb->inode_item.flags & BTRFS_INODE_COMPRESS || fcb->Vcb->options.compress)
+        return TRUE;
+    
+    return FALSE;
+}
+
 #ifdef DEBUG_FCB_REFCOUNTS
 #ifdef DEBUG_LONG_MESSAGES
 #define increase_fileref_refcount(fileref) {\
index 3071431..bc35ad0 100644 (file)
@@ -1,9 +1,13 @@
+// No copyright claimed in this file - do what you want with it.
+
 #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)
 #define FSCTL_BTRFS_CREATE_SUBVOL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82a, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
 #define FSCTL_BTRFS_CREATE_SNAPSHOT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82b, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
+#define FSCTL_BTRFS_GET_INODE_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82c, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+#define FSCTL_BTRFS_SET_INODE_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82d, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
 
 typedef struct {
     UINT64 subvol;
@@ -17,4 +21,29 @@ typedef struct {
     WCHAR name[1];
 } btrfs_create_snapshot;
 
+typedef struct {
+    UINT64 subvol;
+    UINT64 inode;
+    BOOL top;
+    UINT8 type;
+    UINT32 st_uid;
+    UINT32 st_gid;
+    UINT32 st_mode;
+    UINT64 st_rdev;
+    UINT64 flags;
+    UINT32 inline_length;
+    UINT64 disk_size[3];
+} btrfs_inode_info;
+
+typedef struct {
+    UINT64 flags;
+    BOOL flags_changed;
+    UINT32 st_uid;
+    BOOL uid_changed;
+    UINT32 st_gid;
+    BOOL gid_changed;
+    UINT32 st_mode;
+    BOOL mode_changed;
+} btrfs_set_inode_info;
+
 #endif
diff --git a/reactos/drivers/filesystems/btrfs/compress.c b/reactos/drivers/filesystems/btrfs/compress.c
new file mode 100755 (executable)
index 0000000..7b7a00a
--- /dev/null
@@ -0,0 +1,898 @@
+/* Copyright (c) Mark Harmstone 2016
+ * Copyright (c) Reimar Doeffinger 2006
+ * Copyright (c) Markus Oberhumer 1996
+ * 
+ * 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/>. */
+
+// Portion of the LZO decompression code here were cribbed from code in
+// libavcodec, also under the LGPL. Thank you, Reimar Doeffinger.
+
+// The LZO compression code comes from v0.22 of lzo, written way back in
+// 1996, and available here:
+// https://www.ibiblio.org/pub/historic-linux/ftp-archives/sunsite.unc.edu/Sep-29-1996/libs/lzo-0.22.tar.gz
+// Modern versions of lzo are licensed under the GPL, but the very oldest
+// versions are under the LGPL and hence okay to use here.
+
+#include "btrfs_drv.h"
+
+#define Z_SOLO
+#define ZLIB_INTERNAL
+
+#ifndef __REACTOS__
+#include "zlib/zlib.h"
+#include "zlib/inftrees.h"
+#include "zlib/inflate.h"
+#else
+#include <zlib.h>
+#endif
+
+#define LINUX_PAGE_SIZE 4096
+
+typedef struct {
+    UINT8* in;
+    UINT32 inlen;
+    UINT32 inpos;
+    UINT8* out;
+    UINT32 outlen;
+    UINT32 outpos;
+    BOOL error;
+    void* wrkmem;
+} lzo_stream;
+
+#define LZO1X_MEM_COMPRESS ((UINT32) (16384L * sizeof(UINT8*)))
+
+#define M1_MAX_OFFSET 0x0400
+#define M2_MAX_OFFSET 0x0800
+#define M3_MAX_OFFSET 0x4000
+#define M4_MAX_OFFSET 0xbfff
+
+#define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET)
+
+#define M1_MARKER 0
+#define M2_MARKER 64
+#define M3_MARKER 32
+#define M4_MARKER 16
+
+#define _DV2(p, shift1, shift2) (((( (UINT32)(p[2]) << shift1) ^ p[1]) << shift2) ^ p[0])
+#define DVAL_NEXT(dv, p) dv ^= p[-1]; dv = (((dv) >> 5) ^ ((UINT32)(p[2]) << (2*5)))
+#define _DV(p, shift) _DV2(p, shift, shift)
+#define DVAL_FIRST(dv, p) dv = _DV((p), 5)
+#define _DINDEX(dv, p) ((40799u * (dv)) >> 5)
+#define DINDEX(dv, p) (((_DINDEX(dv, p)) & 0x3fff) << 0)
+#define UPDATE_D(dict, cycle, dv, p) dict[DINDEX(dv, p)] = (p)
+#define UPDATE_I(dict, cycle, index, p) dict[index] = (p)
+
+#define LZO_CHECK_MPOS_NON_DET(m_pos, m_off, in, ip, max_offset) \
+    ((void*) m_pos < (void*) in || \
+    (m_off = (UINT8*) ip - (UINT8*) m_pos) <= 0 || \
+    m_off > max_offset)
+        
+#define LZO_BYTE(x) ((unsigned char) (x))
+
+static UINT8 lzo_nextbyte(lzo_stream* stream) {
+    UINT8 c;
+    
+    if (stream->inpos >= stream->inlen) {
+        stream->error = TRUE;
+        return 0;
+    }
+    
+    c = stream->in[stream->inpos];
+    stream->inpos++;
+    
+    return c;
+}
+
+static int lzo_len(lzo_stream* stream, int byte, int mask) {
+    int len = byte & mask;
+    
+    if (len == 0) {
+        while (!(byte = lzo_nextbyte(stream))) {
+            if (stream->error) return 0;
+            
+            len += 255;
+        }
+        
+        len += mask + byte;
+    }
+    
+    return len;
+}
+
+static void lzo_copy(lzo_stream* stream, int len) {
+    if (stream->inpos + len > stream->inlen) {
+        stream->error = TRUE;
+        return;
+    }
+    
+    if (stream->outpos + len > stream->outlen) {
+        stream->error = TRUE;
+        return;
+    }
+    
+    do {
+        stream->out[stream->outpos] = stream->in[stream->inpos];
+        stream->inpos++;
+        stream->outpos++;
+        len--;
+    } while (len > 0);
+}
+
+static void lzo_copyback(lzo_stream* stream, int back, int len) {
+    if (stream->outpos < back) {
+        stream->error = TRUE;
+        return;
+    }
+    
+    if (stream->outpos + len > stream->outlen) {
+        stream->error = TRUE;
+        return;
+    }
+    
+    do {
+        stream->out[stream->outpos] = stream->out[stream->outpos - back];
+        stream->outpos++;
+        len--;
+    } while (len > 0);
+}
+
+static NTSTATUS do_lzo_decompress(lzo_stream* stream) {
+    UINT8 byte;
+    UINT32 len, back;
+    BOOL backcopy = FALSE;
+    
+    stream->error = FALSE;
+    
+    byte = lzo_nextbyte(stream);
+    if (stream->error) return STATUS_INTERNAL_ERROR;
+    
+    if (byte > 17) {
+        lzo_copy(stream, byte - 17);
+        if (stream->error) return STATUS_INTERNAL_ERROR;
+        
+        byte = lzo_nextbyte(stream);
+        if (stream->error) return STATUS_INTERNAL_ERROR;
+        
+        if (byte < 16) return STATUS_INTERNAL_ERROR;
+    }
+    
+    while (1) {
+        if (byte >> 4) {
+            backcopy = TRUE;
+            if (byte >> 6) {
+                len = (byte >> 5) - 1;
+                back = (lzo_nextbyte(stream) << 3) + ((byte >> 2) & 7) + 1;
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+            } else if (byte >> 5) {
+                len = lzo_len(stream, byte, 31);
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+                
+                byte = lzo_nextbyte(stream);
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+                
+                back = (lzo_nextbyte(stream) << 6) + (byte >> 2) + 1;
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+            } else {
+                len = lzo_len(stream, byte, 7);
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+                
+                back = (1 << 14) + ((byte & 8) << 11);
+                
+                byte = lzo_nextbyte(stream);
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+                
+                back += (lzo_nextbyte(stream) << 6) + (byte >> 2);
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+                
+                if (back == (1 << 14)) {
+                    if (len != 1)
+                        return STATUS_INTERNAL_ERROR;
+                    break;
+                }
+            }
+        } else if (backcopy) {
+            len = 0;
+            back = (lzo_nextbyte(stream) << 2) + (byte >> 2) + 1;
+            if (stream->error) return STATUS_INTERNAL_ERROR;
+        } else {
+            len = lzo_len(stream, byte, 15);
+            if (stream->error) return STATUS_INTERNAL_ERROR;
+            
+            lzo_copy(stream, len + 3);
+            if (stream->error) return STATUS_INTERNAL_ERROR;
+            
+            byte = lzo_nextbyte(stream);
+            if (stream->error) return STATUS_INTERNAL_ERROR;
+            
+            if (byte >> 4)
+                continue;
+            
+            len = 1;
+            back = (1 << 11) + (lzo_nextbyte(stream) << 2) + (byte >> 2) + 1;
+            if (stream->error) return STATUS_INTERNAL_ERROR;
+            
+            break;
+        }
+        
+        lzo_copyback(stream, back, len + 2);
+        if (stream->error) return STATUS_INTERNAL_ERROR;
+        
+        len = byte & 3;
+        
+        if (len) {
+            lzo_copy(stream, len);
+            if (stream->error) return STATUS_INTERNAL_ERROR;
+        } else
+            backcopy = !backcopy;
+        
+        byte = lzo_nextbyte(stream);
+        if (stream->error) return STATUS_INTERNAL_ERROR;
+    }
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS lzo_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) {
+    NTSTATUS Status;
+    UINT32 extlen, partlen, inoff, outoff;
+    lzo_stream stream;
+    
+    extlen = *((UINT32*)inbuf);
+    if (inlen < extlen) {
+        ERR("compressed extent was %llx, should have been at least %x\n", inlen, extlen);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    inoff = sizeof(UINT32);
+    outoff = 0;
+    
+    do {
+        partlen = *(UINT32*)&inbuf[inoff];
+        
+        if (partlen + inoff > inlen) {
+            ERR("overflow: %x + %x > %llx\n", partlen, inoff, inlen);
+            return STATUS_INTERNAL_ERROR;
+        }
+        
+        inoff += sizeof(UINT32);
+    
+        stream.in = &inbuf[inoff];
+        stream.inlen = partlen;
+        stream.inpos = 0;
+        stream.out = &outbuf[outoff];
+        stream.outlen = LINUX_PAGE_SIZE;
+        stream.outpos = 0;
+        
+        Status = do_lzo_decompress(&stream);
+        if (!NT_SUCCESS(Status)) {
+            ERR("do_lzo_decompress returned %08x\n", Status);
+            return Status;
+        }
+        
+        if (stream.outpos < stream.outlen)
+            RtlZeroMemory(&stream.out[stream.outpos], stream.outlen - stream.outpos);
+        
+        inoff += partlen;
+        outoff += stream.outlen;
+        
+        if (LINUX_PAGE_SIZE - (inoff % LINUX_PAGE_SIZE) < sizeof(UINT32))
+            inoff = ((inoff / LINUX_PAGE_SIZE) + 1) * LINUX_PAGE_SIZE;
+    } while (inoff < extlen);
+    
+    return STATUS_SUCCESS;
+}
+
+static void* zlib_alloc(void* opaque, unsigned int items, unsigned int size) {
+    return ExAllocatePoolWithTag(PagedPool, items * size, ALLOC_TAG_ZLIB);
+}
+
+static void zlib_free(void* opaque, void* ptr) {
+    ExFreePool(ptr);
+}
+
+static NTSTATUS zlib_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) {
+    z_stream c_stream;
+    int ret;
+
+    c_stream.zalloc = zlib_alloc;
+    c_stream.zfree = zlib_free;
+    c_stream.opaque = (voidpf)0;
+
+    ret = inflateInit(&c_stream);
+    
+    if (ret != Z_OK) {
+        ERR("inflateInit returned %08x\n", ret);
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    c_stream.next_in = inbuf;
+    c_stream.avail_in = inlen;
+    
+    c_stream.next_out = outbuf;
+    c_stream.avail_out = outlen;
+    
+    do {
+        ret = inflate(&c_stream, Z_NO_FLUSH);
+        
+        if (ret != Z_OK && ret != Z_STREAM_END) {
+            ERR("inflate returned %08x\n", ret);
+            inflateEnd(&c_stream);
+            return STATUS_INTERNAL_ERROR;
+        }
+    } while (ret != Z_STREAM_END);
+
+    ret = inflateEnd(&c_stream);
+    
+    if (ret != Z_OK) {
+        ERR("inflateEnd returned %08x\n", ret);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    // FIXME - if we're short, should we zero the end of outbuf so we don't leak information into userspace?
+    
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS decompress(UINT8 type, UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) {
+    if (type == BTRFS_COMPRESSION_ZLIB)
+        return zlib_decompress(inbuf, inlen, outbuf, outlen);
+    else if (type == BTRFS_COMPRESSION_LZO)
+        return lzo_decompress(inbuf, inlen, outbuf, outlen);
+    else {
+        ERR("unsupported compression type %x\n", type);
+        return STATUS_NOT_SUPPORTED;
+    }
+}
+
+static NTSTATUS zlib_write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
+    UINT8 compression;
+    UINT64 comp_length;
+    UINT8* comp_data;
+    UINT32 out_left;
+    LIST_ENTRY* le;
+    chunk* c;
+    z_stream c_stream;
+    int ret;
+    
+    comp_data = ExAllocatePoolWithTag(PagedPool, end_data - start_data, ALLOC_TAG);
+    if (!comp_data) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
+    if (!NT_SUCCESS(Status)) {
+        ERR("excise_extents returned %08x\n", Status);
+        ExFreePool(comp_data);
+        return Status;
+    }
+    
+    c_stream.zalloc = zlib_alloc;
+    c_stream.zfree = zlib_free;
+    c_stream.opaque = (voidpf)0;
+
+    ret = deflateInit(&c_stream, fcb->Vcb->options.zlib_level);
+    
+    if (ret != Z_OK) {
+        ERR("deflateInit returned %08x\n", ret);
+        ExFreePool(comp_data);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    c_stream.avail_in = end_data - start_data;
+    c_stream.next_in = data;
+    c_stream.avail_out = end_data - start_data;
+    c_stream.next_out = comp_data;
+    
+    do {
+        ret = deflate(&c_stream, Z_FINISH);
+        
+        if (ret == Z_STREAM_ERROR) {
+            ERR("deflate returned %x\n", ret);
+            ExFreePool(comp_data);
+            return STATUS_INTERNAL_ERROR;
+        }
+    } while (c_stream.avail_in > 0 && c_stream.avail_out > 0);
+    
+    out_left = c_stream.avail_out;
+    
+    ret = deflateEnd(&c_stream);
+    
+    if (ret != Z_OK) {
+        ERR("deflateEnd returned %08x\n", ret);
+        ExFreePool(comp_data);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    if (out_left < fcb->Vcb->superblock.sector_size) { // compressed extent would be larger than or same size as uncompressed extent
+        ExFreePool(comp_data);
+        
+        comp_length = end_data - start_data;
+        comp_data = data;
+        compression = BTRFS_COMPRESSION_NONE;
+        
+        *compressed = FALSE;
+    } else {
+        UINT32 cl;
+        
+        compression = BTRFS_COMPRESSION_ZLIB;
+        cl = end_data - start_data - out_left;
+        comp_length = sector_align(cl, fcb->Vcb->superblock.sector_size);
+        
+        RtlZeroMemory(comp_data + cl, comp_length - cl);
+        
+        *compressed = TRUE;
+    }
+    
+    ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
+    
+    le = fcb->Vcb->chunks.Flink;
+    while (le != &fcb->Vcb->chunks) {
+        c = CONTAINING_RECORD(le, chunk, list_entry);
+        
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        
+        if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) {
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) {
+                ExReleaseResourceLite(&c->lock);
+                ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+                
+                if (compression != BTRFS_COMPRESSION_NONE)
+                    ExFreePool(comp_data);
+                
+                return STATUS_SUCCESS;
+            }
+        }
+        
+        ExReleaseResourceLite(&c->lock);
+
+        le = le->Flink;
+    }
+    
+    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+    
+    ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+    
+    if ((c = alloc_chunk(fcb->Vcb, fcb->Vcb->data_flags))) {
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+        
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        
+        if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) {
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) {
+                ExReleaseResourceLite(&c->lock);
+                
+                if (compression != BTRFS_COMPRESSION_NONE)
+                    ExFreePool(comp_data);
+                
+                return STATUS_SUCCESS;
+            }
+        }
+        
+        ExReleaseResourceLite(&c->lock);
+    } else
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+    
+    WARN("couldn't find any data chunks with %llx bytes free\n", comp_length);
+
+    return STATUS_DISK_FULL;
+}
+
+static NTSTATUS lzo_do_compress(const UINT8* in, UINT32 in_len, UINT8* out, UINT32* out_len, void* wrkmem) {
+    const UINT8* ip;
+    UINT32 dv;
+    UINT8* op;
+    const UINT8* in_end = in + in_len;
+    const UINT8* ip_end = in + in_len - 9 - 4;
+    const UINT8* ii;
+    const UINT8** dict = (const UINT8**)wrkmem;
+
+    op = out;
+    ip = in;
+    ii = ip;
+
+    DVAL_FIRST(dv, ip); UPDATE_D(dict, cycle, dv, ip); ip++;
+    DVAL_NEXT(dv, ip);  UPDATE_D(dict, cycle, dv, ip); ip++;
+    DVAL_NEXT(dv, ip);  UPDATE_D(dict, cycle, dv, ip); ip++;
+    DVAL_NEXT(dv, ip);  UPDATE_D(dict, cycle, dv, ip); ip++;
+
+    while (1) {
+        const UINT8* m_pos;
+        UINT32 m_len;
+        ptrdiff_t m_off;
+        UINT32 lit, dindex;
+
+        dindex = DINDEX(dv, ip);
+        m_pos = dict[dindex];
+        UPDATE_I(dict, cycle, dindex, ip);
+
+        if (!LZO_CHECK_MPOS_NON_DET(m_pos, m_off, in, ip, M4_MAX_OFFSET) && m_pos[0] == ip[0] && m_pos[1] == ip[1] && m_pos[2] == ip[2]) {
+            lit = ip - ii;
+            m_pos += 3;
+            if (m_off <= M2_MAX_OFFSET)
+                goto match;
+
+            if (lit == 3) { /* better compression, but slower */
+                if (op - 2 <= out)
+                    return STATUS_INTERNAL_ERROR;
+                
+                op[-2] |= LZO_BYTE(3);
+                *op++ = *ii++; *op++ = *ii++; *op++ = *ii++;
+                goto code_match;
+            }
+
+            if (*m_pos == ip[3])
+                goto match;
+        }
+
+        /* a literal */
+        ++ip;
+        if (ip >= ip_end)
+            break;
+        DVAL_NEXT(dv, ip);
+        continue;
+
+        /* a match */
+match:
+        /* store current literal run */
+        if (lit > 0) {
+            UINT32 t = lit;
+
+            if (t <= 3) {
+                if (op - 2 <= out)
+                    return STATUS_INTERNAL_ERROR;
+
+                op[-2] |= LZO_BYTE(t);
+            } else if (t <= 18)
+                *op++ = LZO_BYTE(t - 3);
+            else {
+                UINT32 tt = t - 18;
+
+                *op++ = 0;
+                while (tt > 255) {
+                    tt -= 255;
+                    *op++ = 0;
+                }
+                
+                if (tt <= 0)
+                    return STATUS_INTERNAL_ERROR;
+
+                *op++ = LZO_BYTE(tt);
+            }
+            
+            do {
+                *op++ = *ii++;
+            } while (--t > 0);
+        }
+
+
+        /* code the match */
+code_match:
+        if (ii != ip)
+            return STATUS_INTERNAL_ERROR;
+        
+        ip += 3;
+        if (*m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++ ||
+            *m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++) {
+            --ip;
+            m_len = ip - ii;
+        
+            if (m_len < 3 || m_len > 8)
+                return STATUS_INTERNAL_ERROR;
+
+            if (m_off <= M2_MAX_OFFSET) {
+                m_off -= 1;
+                *op++ = LZO_BYTE(((m_len - 1) << 5) | ((m_off & 7) << 2));
+                *op++ = LZO_BYTE(m_off >> 3);
+            } else if (m_off <= M3_MAX_OFFSET) {
+                m_off -= 1;
+                *op++ = LZO_BYTE(M3_MARKER | (m_len - 2));
+                goto m3_m4_offset;
+            } else {
+                m_off -= 0x4000;
+                
+                if (m_off <= 0 || m_off > 0x7fff)
+                    return STATUS_INTERNAL_ERROR;
+                    
+                *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2));
+                goto m3_m4_offset;
+            }
+        } else {
+            const UINT8* end;
+            end = in_end;
+            while (ip < end && *m_pos == *ip)
+                m_pos++, ip++;
+            m_len = (ip - ii);
+            
+            if (m_len < 3)
+                return STATUS_INTERNAL_ERROR;
+
+            if (m_off <= M3_MAX_OFFSET) {
+                m_off -= 1;
+                if (m_len <= 33)
+                    *op++ = LZO_BYTE(M3_MARKER | (m_len - 2));
+                else {
+                    m_len -= 33;
+                    *op++ = M3_MARKER | 0;
+                    goto m3_m4_len;
+                }
+            } else {
+                m_off -= 0x4000;
+                
+                if (m_off <= 0 || m_off > 0x7fff)
+                    return STATUS_INTERNAL_ERROR;
+
+                if (m_len <= 9)
+                    *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2));
+                else {
+                    m_len -= 9;
+                    *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11));
+m3_m4_len:
+                    while (m_len > 255) {
+                        m_len -= 255;
+                        *op++ = 0;
+                    }
+                    
+                    if (m_len <= 0)
+                        return STATUS_INTERNAL_ERROR;
+                    
+                    *op++ = LZO_BYTE(m_len);
+                }
+            }
+
+m3_m4_offset:
+            *op++ = LZO_BYTE((m_off & 63) << 2);
+            *op++ = LZO_BYTE(m_off >> 6);
+        }
+
+        ii = ip;
+        if (ip >= ip_end)
+            break;
+        DVAL_FIRST(dv, ip);
+    }
+
+    /* store final literal run */
+    if (in_end - ii > 0) {
+        UINT32 t = in_end - ii;
+
+        if (op == out && t <= 238)
+            *op++ = LZO_BYTE(17 + t);
+        else if (t <= 3)
+            op[-2] |= LZO_BYTE(t);
+        else if (t <= 18)
+            *op++ = LZO_BYTE(t - 3);
+        else {
+            UINT32 tt = t - 18;
+
+            *op++ = 0;
+            while (tt > 255) {
+                tt -= 255;
+                *op++ = 0;
+            }
+            
+            if (tt <= 0)
+                return STATUS_INTERNAL_ERROR;
+
+            *op++ = LZO_BYTE(tt);
+        }
+        
+        do {
+            *op++ = *ii++;
+        } while (--t > 0);
+    }
+
+    *out_len = op - out;
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS lzo1x_1_compress(lzo_stream* stream) {
+    UINT8 *op = stream->out;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    if (stream->inlen <= 0)
+        stream->outlen = 0;
+    else if (stream->inlen <= 9 + 4) {
+        *op++ = LZO_BYTE(17 + stream->inlen);
+        
+        stream->inpos = 0;
+        do {
+            *op++ = stream->in[stream->inpos];
+            stream->inpos++;
+        } while (stream->inlen < stream->inpos);
+        stream->outlen = op - stream->out;
+    } else
+        Status = lzo_do_compress(stream->in, stream->inlen, stream->out, &stream->outlen, stream->wrkmem);
+    
+    if (Status == STATUS_SUCCESS) {
+        op = stream->out + stream->outlen;
+        *op++ = M4_MARKER | 1;
+        *op++ = 0;
+        *op++ = 0;
+        stream->outlen += 3;
+    }
+
+    return Status;
+}
+
+static __inline UINT32 lzo_max_outlen(UINT32 inlen) {
+    return inlen + (inlen / 16) + 64 + 3; // formula comes from LZO.FAQ
+}
+
+static NTSTATUS lzo_write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
+    UINT8 compression;
+    UINT64 comp_length;
+    ULONG comp_data_len, num_pages, i;
+    UINT8* comp_data;
+    BOOL skip_compression = FALSE;
+    lzo_stream stream;
+    UINT32* out_size;
+    LIST_ENTRY* le;
+    chunk* c;
+    
+    num_pages = (sector_align(end_data - start_data, LINUX_PAGE_SIZE)) / LINUX_PAGE_SIZE;
+    
+    // Four-byte overall header
+    // Another four-byte header page
+    // Each page has a maximum size of lzo_max_outlen(LINUX_PAGE_SIZE)
+    // Plus another four bytes for possible padding
+    comp_data_len = sizeof(UINT32) + ((lzo_max_outlen(LINUX_PAGE_SIZE) + (2 * sizeof(UINT32))) * num_pages);
+    
+    comp_data = ExAllocatePoolWithTag(PagedPool, comp_data_len, ALLOC_TAG);
+    if (!comp_data) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    stream.wrkmem = ExAllocatePoolWithTag(PagedPool, LZO1X_MEM_COMPRESS, ALLOC_TAG);
+    if (!stream.wrkmem) {
+        ERR("out of memory\n");
+        ExFreePool(comp_data);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
+    if (!NT_SUCCESS(Status)) {
+        ERR("excise_extents returned %08x\n", Status);
+        ExFreePool(comp_data);
+        ExFreePool(stream.wrkmem);
+        return Status;
+    }
+    
+    out_size = (UINT32*)comp_data;
+    *out_size = sizeof(UINT32);
+    
+    stream.in = data;
+    stream.out = comp_data + (2 * sizeof(UINT32));
+    
+    for (i = 0; i < num_pages; i++) {
+        UINT32* pagelen = (UINT32*)(stream.out - sizeof(UINT32));
+        
+        stream.inlen = min(LINUX_PAGE_SIZE, end_data - start_data - (i * LINUX_PAGE_SIZE));
+        
+        Status = lzo1x_1_compress(&stream);
+        if (!NT_SUCCESS(Status)) {
+            ERR("lzo1x_1_compress returned %08x\n", Status);
+            skip_compression = TRUE;
+            break;
+        }
+        
+        *pagelen = stream.outlen;
+        *out_size += stream.outlen + sizeof(UINT32);
+        
+        stream.in += LINUX_PAGE_SIZE;
+        stream.out += stream.outlen + sizeof(UINT32);
+        
+        if (LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE) < sizeof(UINT32)) {
+            RtlZeroMemory(stream.out, LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE));
+            stream.out += LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE);
+            *out_size += LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE);
+        }
+    }
+    
+    ExFreePool(stream.wrkmem);
+    
+    if (skip_compression || *out_size >= end_data - start_data - fcb->Vcb->superblock.sector_size) { // compressed extent would be larger than or same size as uncompressed extent
+        ExFreePool(comp_data);
+        
+        comp_length = end_data - start_data;
+        comp_data = data;
+        compression = BTRFS_COMPRESSION_NONE;
+        
+        *compressed = FALSE;
+    } else {
+        compression = BTRFS_COMPRESSION_LZO;
+        comp_length = sector_align(*out_size, fcb->Vcb->superblock.sector_size);
+        
+        RtlZeroMemory(comp_data + *out_size, comp_length - *out_size);
+        
+        *compressed = TRUE;
+    }
+    
+    ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
+    
+    le = fcb->Vcb->chunks.Flink;
+    while (le != &fcb->Vcb->chunks) {
+        c = CONTAINING_RECORD(le, chunk, list_entry);
+        
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        
+        if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) {
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) {
+                ExReleaseResourceLite(&c->lock);
+                ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+                
+                if (compression != BTRFS_COMPRESSION_NONE)
+                    ExFreePool(comp_data);
+                
+                return STATUS_SUCCESS;
+            }
+        }
+        
+        ExReleaseResourceLite(&c->lock);
+
+        le = le->Flink;
+    }
+    
+    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+    ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+    
+    if ((c = alloc_chunk(fcb->Vcb, fcb->Vcb->data_flags))) {
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+        
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        
+        if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) {
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) {
+                ExReleaseResourceLite(&c->lock);
+                
+                if (compression != BTRFS_COMPRESSION_NONE)
+                    ExFreePool(comp_data);
+                
+                return STATUS_SUCCESS;
+            }
+        }
+        
+        ExReleaseResourceLite(&c->lock);
+    } else
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+    
+    WARN("couldn't find any data chunks with %llx bytes free\n", comp_length);
+
+    return STATUS_DISK_FULL;
+}
+
+NTSTATUS write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+    UINT8 type;
+
+    if (fcb->Vcb->options.compress_type != 0)
+        type = fcb->Vcb->options.compress_type;
+    else {
+        if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO)
+            type = BTRFS_COMPRESSION_LZO;
+        else
+            type = BTRFS_COMPRESSION_ZLIB;
+    }
+    
+    if (type == BTRFS_COMPRESSION_LZO) {
+        fcb->Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO;
+        return lzo_write_compressed_bit(fcb, start_data, end_data, data, compressed, changed_sector_list, Irp, rollback);
+    } else
+        return zlib_write_compressed_bit(fcb, start_data, end_data, data, compressed, changed_sector_list, Irp, rollback);
+}
index 39b6cf2..5576833 100644 (file)
@@ -25,7 +25,7 @@
 
 extern PDEVICE_OBJECT devobj;
 
-static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
+static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -35,7 +35,7 @@ static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode
     searchkey.obj_type = TYPE_INODE_REF;
     searchkey.offset = parinode;
     
-    Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -82,7 +82,7 @@ static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode
         return STATUS_NOT_FOUND;
 }
 
-static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
+static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -92,7 +92,7 @@ static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT6
     searchkey.obj_type = TYPE_INODE_EXTREF;
     searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
     
-    Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -139,7 +139,7 @@ static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT6
         return STATUS_NOT_FOUND;
 }
 
-static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 subvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
+static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 subvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -149,7 +149,7 @@ static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 sub
     searchkey.obj_type = TYPE_ROOT_REF;
     searchkey.offset = subvolid;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -181,7 +181,7 @@ static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 sub
         return STATUS_NOT_FOUND;
 }
 
-static NTSTATUS load_index_list(fcb* fcb) {
+static NTSTATUS load_index_list(fcb* fcb, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
     NTSTATUS Status;
@@ -191,14 +191,14 @@ static NTSTATUS load_index_list(fcb* fcb) {
     searchkey.obj_type = TYPE_DIR_INDEX;
     searchkey.offset = 2;
     
-    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
     }
 
     if (keycmp(&tp.item->key, &searchkey) == -1) {
-        if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE)) {
+        if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp)) {
             tp = next_tp;
             
             TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
@@ -315,7 +315,7 @@ static NTSTATUS load_index_list(fcb* fcb) {
         }
         
 nextitem:
-        b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp);
          
         if (b) {
             tp = next_tp;
@@ -343,7 +343,7 @@ end:
 }
 
 static NTSTATUS STDCALL find_file_in_dir_index(file_ref* fr, PUNICODE_STRING filename, root** subvol, UINT64* inode, UINT8* type,
-                                               UINT64* pindex, PANSI_STRING utf8) {
+                                               UINT64* pindex, PANSI_STRING utf8, PIRP Irp) {
     LIST_ENTRY* le;
     NTSTATUS Status;
     UNICODE_STRING us;
@@ -360,7 +360,7 @@ static NTSTATUS STDCALL find_file_in_dir_index(file_ref* fr, PUNICODE_STRING fil
     ExAcquireResourceExclusiveLite(&fr->fcb->nonpaged->index_lock, TRUE);
     
     if (!fr->fcb->index_loaded) {
-        Status = load_index_list(fr->fcb);
+        Status = load_index_list(fr->fcb, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("load_index_list returned %08x\n", Status);
             goto end;
@@ -472,8 +472,8 @@ end:
     return Status;
 }
 
-NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr,
-                                             root** subvol, UINT64* inode, UINT8* type, UINT64* pindex, PANSI_STRING utf8) {
+static NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr,
+                                                    root** subvol, UINT64* inode, UINT8* type, UINT64* pindex, PANSI_STRING utf8, PIRP Irp) {
     DIR_ITEM* di;
     KEY searchkey;
     traverse_ptr tp;
@@ -487,7 +487,7 @@ NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STR
     searchkey.obj_type = TYPE_DIR_ITEM;
     searchkey.offset = crc32;
     
-    Status = find_item(Vcb, fr->fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, fr->fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -533,6 +533,8 @@ NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STR
                     if (!NT_SUCCESS(Status)) {
                         ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
                     } else {
+                        ANSI_STRING nutf8;
+                        
                         us.Buffer = utf16;
                         us.Length = us.MaximumLength = (USHORT)stringlen;
                         
@@ -591,17 +593,20 @@ NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STR
                             index = 0;
                                 
                             if (fr->fcb->subvol != Vcb->root_root) {
+                                nutf8.Buffer = di->name;
+                                nutf8.Length = nutf8.MaximumLength = di->n;
+                                
                                 if (di->key.obj_type == TYPE_ROOT_ITEM) {
-                                    Status = find_subvol_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
+                                    Status = find_subvol_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp);
                                     if (!NT_SUCCESS(Status)) {
                                         ERR("find_subvol_dir_index returned %08x\n", Status);
                                         return Status;
                                     }
                                 } else {
-                                    Status = find_file_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
+                                    Status = find_file_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp);
                                     if (!NT_SUCCESS(Status)) {
                                         if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
-                                            Status = find_file_dir_index_extref(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
+                                            Status = find_file_dir_index_extref(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp);
                                             
                                             if (!NT_SUCCESS(Status)) {
                                                 ERR("find_file_dir_index_extref returned %08x\n", Status);
@@ -651,7 +656,7 @@ NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STR
     }
     
 byindex:
-    Status = find_file_in_dir_index(fr, filename, subvol, inode, type, pindex, utf8);
+    Status = find_file_in_dir_index(fr, filename, subvol, inode, type, pindex, utf8, Irp);
     if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
         ERR("find_file_in_dir_index returned %08x\n", Status);
         return Status;
@@ -660,7 +665,7 @@ byindex:
     return Status;
 }
 
-fcb* create_fcb() {
+fcb* create_fcb(POOL_TYPE pool_type) {
     fcb* fcb;
     
     fcb = ExAllocatePoolWithTag(PagedPool, sizeof(struct _fcb), ALLOC_TAG);
@@ -742,7 +747,7 @@ file_ref* create_fileref() {
 }
 
 NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, file_ref* fr,
-                                  root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8) {
+                                  root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8, PIRP Irp) {
     char* fn;
     UINT32 crc32;
     ULONG utf8len;
@@ -772,10 +777,10 @@ NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filenam
     crc32 = calc_crc32c(0xfffffffe, (UINT8*)fn, (ULONG)utf8len);
     TRACE("crc32c(%.*s) = %08x\n", utf8len, fn, crc32);
     
-    return find_file_in_dir_with_crc32(Vcb, filename, crc32, fr, subvol, inode, type, index, utf8);
+    return find_file_in_dir_with_crc32(Vcb, filename, crc32, fr, subvol, inode, type, index, utf8, Irp);
 }
 
-static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* hash, PANSI_STRING xattr) {
+static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* hash, PANSI_STRING xattr, PIRP Irp) {
     NTSTATUS Status;
     ULONG utf8len;
     char* utf8;
@@ -823,7 +828,7 @@ static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream,
     searchkey.obj_type = TYPE_XATTR_ITEM;
     searchkey.offset = crc32;
     
-    Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto end;
@@ -876,7 +881,7 @@ static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream,
     
     searchkey.offset = 0;
     
-    Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto end;
@@ -959,7 +964,7 @@ static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream,
             }
         }
         
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         if (b) {
             tp = next_tp;
             
@@ -1133,7 +1138,7 @@ static file_ref* search_fileref_children(file_ref* dir, PUNICODE_STRING name) {
     return deleted;
 }
 
-static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size) {
+static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -1143,7 +1148,7 @@ static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64
     searchkey.obj_type = TYPE_EXTENT_ITEM;
     searchkey.offset = size;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return 0;
@@ -1169,7 +1174,7 @@ static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64
     return ei->refcount;
 }
 
-NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb) {
+NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -1199,7 +1204,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
         }
     }
     
-    fcb = create_fcb();
+    fcb = create_fcb(PagedPool);
     if (!fcb) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
@@ -1215,7 +1220,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
     searchkey.obj_type = TYPE_INODE_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         free_fcb(fcb);
@@ -1248,15 +1253,15 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
             fcb->type = BTRFS_TYPE_FILE;
     }
     
-    fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', FALSE);
+    fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', FALSE, Irp);
     
-    fcb_get_sd(fcb, parent);
+    fcb_get_sd(fcb, parent, Irp);
     
     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
         UINT8* xattrdata;
         UINT16 xattrlen;
         
-        if (get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &xattrdata, &xattrlen)) {
+        if (get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &xattrdata, &xattrlen, Irp)) {
             fcb->reparse_xattr.Buffer = (char*)xattrdata;
             fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = xattrlen;
         }
@@ -1279,7 +1284,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
         searchkey.obj_type = TYPE_EXTENT_DATA;
         searchkey.offset = 0;
         
-        Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+        Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             free_fcb(fcb);
@@ -1312,8 +1317,11 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
                         return STATUS_INTERNAL_ERROR;
                     }
                     
+                    if (ed2->address == 0 && ed2->size == 0) // sparse
+                        goto nextitem;
+                    
                     if (ed2->size != 0)
-                        unique = get_extent_refcount(fcb->Vcb, ed2->address, ed2->size) == 1;
+                        unique = get_extent_refcount(fcb->Vcb, ed2->address, ed2->size, Irp) == 1;
                 }
                 
                 ext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
@@ -1340,7 +1348,8 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
                 InsertTailList(&fcb->extents, &ext->list_entry);
             }
             
-            b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+nextitem:
+            b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
          
             if (b) {
                 tp = next_tp;
@@ -1365,7 +1374,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
     searchkey.obj_type = TYPE_INODE_REF;
     searchkey.offset = 0;
     
-    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         free_fcb(fcb);
@@ -1509,7 +1518,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
             }
         }
         
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         
         if (b) {
             tp = next_tp;
@@ -1524,10 +1533,13 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
 }
 
 NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr,
-                         UINT32 streamhash, fcb* parent, fcb** pfcb) {
+                         UINT32 streamhash, fcb* parent, fcb** pfcb, PIRP Irp) {
     fcb* fcb;
     UINT8* xattrdata;
-    UINT16 xattrlen;
+    UINT16 xattrlen, overhead;
+    NTSTATUS Status;
+    KEY searchkey;
+    traverse_ptr tp;
     
     if (!IsListEmpty(&subvol->fcbs)) {
         LIST_ENTRY* le = subvol->fcbs.Flink;
@@ -1553,13 +1565,13 @@ NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI
         }
     }
     
-    fcb = create_fcb();
+    fcb = create_fcb(PagedPool);
     if (!fcb) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
     }
       
-    if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr->Buffer, streamhash, &xattrdata, &xattrlen)) {
+    if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr->Buffer, streamhash, &xattrdata, &xattrlen, Irp)) {
         ERR("get_xattr failed\n");
         free_fcb(fcb);
         return STATUS_INTERNAL_ERROR;
@@ -1574,6 +1586,35 @@ NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI
     fcb->adshash = streamhash;
     fcb->adsxattr = *xattr;
     
+    // find XATTR_ITEM overhead and hence calculate maximum length
+    
+    searchkey.obj_id = parent->inode;
+    searchkey.obj_type = TYPE_XATTR_ITEM;
+    searchkey.offset = streamhash;
+
+    Status = find_item(Vcb, parent->subvol, &tp, &searchkey, FALSE, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("find_item returned %08x\n", Status);
+        free_fcb(fcb);
+        return Status;
+    }
+    
+    if (keycmp(&tp.item->key, &searchkey)) {
+        ERR("error - could not find key for xattr\n");
+        free_fcb(fcb);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    if (tp.item->size < xattrlen) {
+        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, xattrlen);
+        free_fcb(fcb);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    overhead = tp.item->size - xattrlen;
+    
+    fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - overhead;
+    
     fcb->adsdata.Buffer = (char*)xattrdata;
     fcb->adsdata.Length = fcb->adsdata.MaximumLength = xattrlen;
     
@@ -1623,7 +1664,7 @@ void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock) {
         ExReleaseResourceLite(&parent->nonpaged->children_lock);
 }
 
-NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed) {
+NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed, ULONG* fn_offset, PIRP Irp) {
     UNICODE_STRING fnus2;
     file_ref *dir, *sf, *sf2;
     ULONG i, num_parts;
@@ -1632,6 +1673,13 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
     NTSTATUS Status;
     
     TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, unparsed);
+
+#ifdef DEBUG    
+    if (!ExIsResourceAcquiredExclusiveLite(&Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
+        ERR("fcb_lock not acquired exclusively\n");
+        int3;
+    }
+#endif
     
     if (Vcb->removing || Vcb->locked)
         return STATUS_ACCESS_DENIED;
@@ -1743,7 +1791,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                 
                 // FIXME - check if already opened
                 
-                if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamhash, &xattr)) {
+                if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamhash, &xattr, Irp)) {
                     TRACE("could not find stream %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
                     
                     Status = STATUS_OBJECT_NAME_NOT_FOUND;
@@ -1759,7 +1807,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                         goto end;
                     }
                     
-                    Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb);
+                    Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb, Irp);
                     if (!NT_SUCCESS(Status)) {
                         ERR("open_fcb_stream returned %08x\n", Status);
                         goto end;
@@ -1810,7 +1858,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                 UINT8 type;
                 ANSI_STRING utf8;
                 
-                Status = find_file_in_dir(Vcb, &parts[i], sf, &subvol, &inode, &type, &index, &utf8);
+                Status = find_file_in_dir(Vcb, &parts[i], sf, &subvol, &inode, &type, &index, &utf8, Irp);
                 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
                     TRACE("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
 
@@ -1823,19 +1871,19 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                     fcb* fcb;
                     ULONG strlen;
                     
-                    if (type != BTRFS_TYPE_DIRECTORY && !lastpart) {
-                        WARN("passed path including file as subdirectory\n");
-                        
-                        Status = STATUS_OBJECT_PATH_NOT_FOUND;
-                        goto end;
-                    }
-                    
-                    Status = open_fcb(Vcb, subvol, inode, type, &utf8, sf->fcb, &fcb);
+                    Status = open_fcb(Vcb, subvol, inode, type, &utf8, sf->fcb, &fcb, Irp);
                     if (!NT_SUCCESS(Status)) {
                         ERR("open_fcb returned %08x\n", Status);
                         goto end;
                     }
                     
+                    if (type != BTRFS_TYPE_DIRECTORY && !lastpart && !(fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
+                        WARN("passed path including file as subdirectory\n");
+                        free_fcb(fcb);
+                        Status = STATUS_OBJECT_PATH_NOT_FOUND;
+                        goto end;
+                    }
+
                     sf2 = create_fileref();
                     if (!sf2) {
                         ERR("out of memory\n");
@@ -1891,8 +1939,12 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
             }
         }
         
-        if (i == num_parts - 1)
+        if (i == num_parts - 1) {
+            if (fn_offset)
+                *fn_offset = parts[has_stream ? (num_parts - 2) : (num_parts - 1)].Buffer - fnus->Buffer;
+            
             break;
+        }
         
         if (sf2->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
             Status = STATUS_REPARSE;
@@ -1912,8 +1964,8 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
     *pfr = sf2;
     
 end:
-    ExReleaseResourceLite(&Vcb->fcb_lock);
     free_fileref(sf);
+    ExReleaseResourceLite(&Vcb->fcb_lock);
     
 end2:
     if (parts)
@@ -1924,7 +1976,7 @@ end2:
     return Status;
 }
 
-NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index) {
+NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, prev_tp;
     NTSTATUS Status;
@@ -1942,14 +1994,14 @@ NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index) {
     searchkey.obj_type = TYPE_DIR_INDEX + 1;
     searchkey.offset = 0;
     
-    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
     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_id == searchkey.obj_id && tp.item->key.obj_type >= searchkey.obj_type)) {
-        if (find_prev_item(fcb->Vcb, &tp, &prev_tp, FALSE))
+        if (find_prev_item(fcb->Vcb, &tp, &prev_tp, FALSE, Irp))
             tp = prev_tp;
     }
     
@@ -1979,6 +2031,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     LARGE_INTEGER time;
     BTRFS_TIME now;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
     ULONG defda;
     file_ref* fileref;
     hardlink* hl;
@@ -1990,7 +2043,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     if (!NT_SUCCESS(Status))
         return Status;
     
-    utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
+    utf8 = ExAllocatePoolWithTag(pool_type, utf8len + 1, ALLOC_TAG);
     if (!utf8) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
@@ -2004,7 +2057,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     
     utf8[utf8len] = 0;
     
-    Status = fcb_get_last_dir_index(parfileref->fcb, &dirpos);
+    Status = fcb_get_last_dir_index(parfileref->fcb, &dirpos, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("fcb_get_last_dir_index returned %08x\n", Status);
         ExFreePool(utf8);
@@ -2028,7 +2081,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     mark_fcb_dirty(parfileref->fcb);
     
     if (parfileref->fcb->subvol->lastinode == 0)
-        get_last_inode(Vcb, parfileref->fcb->subvol);
+        get_last_inode(Vcb, parfileref->fcb->subvol, Irp);
     
     inode = parfileref->fcb->subvol->lastinode + 1;
     
@@ -2057,15 +2110,18 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     
     parfileref->fcb->subvol->lastinode++;
     
-    fcb = create_fcb();
+    fcb = create_fcb(pool_type);
     if (!fcb) {
         ERR("out of memory\n");
         ExFreePool(utf8);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-        
+
     fcb->Vcb = Vcb;
-    
+
+    if (IrpSp->Flags & SL_OPEN_PAGING_FILE)
+        fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
+
     fcb->inode_item.generation = Vcb->superblock.generation;
     fcb->inode_item.transid = Vcb->superblock.generation;
     fcb->inode_item.st_size = 0;
@@ -2090,12 +2146,19 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
         fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
     }
     
-    // inherit nodatacow flag from parent directory
-    if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
-        fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
+    if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
+        fcb->inode_item.flags = BTRFS_INODE_NODATACOW | BTRFS_INODE_NODATASUM | BTRFS_INODE_NOCOMPRESS;
+    } else {
+        // inherit nodatacow flag from parent directory
+        if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
+            fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
+            
+            if (type != BTRFS_TYPE_DIRECTORY)
+                fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
+        }
         
-        if (type != BTRFS_TYPE_DIRECTORY)
-            fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
+        if (parfileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
+            fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
     }
     
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
@@ -2120,16 +2183,24 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     
     if (!NT_SUCCESS(Status)) {
         ERR("fcb_get_new_sd returned %08x\n", Status);
+        
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        
         return Status;
     }
     
     fcb->sd_dirty = TRUE;
     
-    hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
+    hl = ExAllocatePoolWithTag(pool_type, sizeof(hardlink), ALLOC_TAG);
     if (!hl) {
         ERR("out of memory\n");
+        
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
@@ -2137,24 +2208,32 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     hl->index = dirpos;
     
     hl->utf8.Length = hl->utf8.MaximumLength = utf8len;
-    hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG);
+    hl->utf8.Buffer = ExAllocatePoolWithTag(pool_type, utf8len, ALLOC_TAG);
     
     if (!hl->utf8.Buffer) {
         ERR("out of memory\n");
         ExFreePool(hl);
+        
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     RtlCopyMemory(hl->utf8.Buffer, utf8, utf8len);
     
     hl->name.Length = hl->name.MaximumLength = fpus->Length;
-    hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fpus->Length, ALLOC_TAG);
+    hl->name.Buffer = ExAllocatePoolWithTag(pool_type, fpus->Length, ALLOC_TAG);
     
     if (!hl->name.Buffer) {
         ERR("out of memory\n");
         ExFreePool(hl->utf8.Buffer);
         ExFreePool(hl);
+        
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
@@ -2165,7 +2244,11 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     fileref = create_fileref();
     if (!fileref) {
         ERR("out of memory\n");
+        
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
@@ -2175,21 +2258,43 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     fileref->utf8.MaximumLength = fileref->utf8.Length = utf8len;
     fileref->utf8.Buffer = utf8;
     
-    fileref->filepart = *fpus;
+    fileref->filepart.Length = fileref->filepart.MaximumLength = fpus->Length;
+    
+    if (fileref->filepart.Length == 0)
+        fileref->filepart.Buffer = NULL;
+    else {
+        fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.Length, ALLOC_TAG);
+        
+        if (!fileref->filepart.Buffer) {
+            ERR("out of memory\n");
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fcb(fcb);
+            ExReleaseResource(&Vcb->fcb_lock);
+            
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlCopyMemory(fileref->filepart.Buffer, fpus->Buffer, fpus->Length);
+    }
     
     Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
     if (!NT_SUCCESS(Status)) {
         ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(fileref);
+        ExReleaseResource(&Vcb->fcb_lock);
         return Status;
     }
         
-    if (Irp->Overlay.AllocationSize.QuadPart > 0) {
+    if (Irp->Overlay.AllocationSize.QuadPart > 0 && !write_fcb_compressed(fcb)) {
         Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
         
         if (!NT_SUCCESS(Status)) {
             ERR("extend_file returned %08x\n", Status);
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fileref(fileref);
+            ExReleaseResource(&Vcb->fcb_lock);
             return Status;
         }
     }
@@ -2204,29 +2309,8 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     fcb->subvol->root_item.ctime = now;
     
     fileref->parent = parfileref;
-    
-    ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
-    
-    if (IsListEmpty(&parfileref->children))
-        InsertTailList(&parfileref->children, &fileref->list_entry);
-    else {
-        LIST_ENTRY* le = parfileref->children.Flink;
-        file_ref* fr1 = CONTAINING_RECORD(le, file_ref, list_entry);
-        
-        while (le != &parfileref->children) {
-            file_ref* fr2 = (le->Flink == &parfileref->children) ? NULL : CONTAINING_RECORD(le->Flink, file_ref, list_entry);
-            
-            if (fileref->index > fr1->index && (!fr2 || fr2->index > fileref->index)) {
-                InsertHeadList(&fr1->list_entry, &fileref->list_entry);
-                break;
-            }
-            
-            fr1 = fr2;
-            le = le->Flink;
-        }
-    }
-    
-    ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
+
+    insert_fileref_child(parfileref, fileref, TRUE);
     
     increase_fileref_refcount(parfileref);
  
@@ -2243,21 +2327,250 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     return STATUS_SUCCESS;
 }
 
+static NTSTATUS create_stream(device_extension* Vcb, file_ref** pfileref, file_ref** pparfileref, PUNICODE_STRING fpus, PUNICODE_STRING stream,
+                              PIRP Irp, ULONG options, POOL_TYPE pool_type, LIST_ENTRY* rollback) {
+    file_ref *fileref, *newpar, *parfileref;
+    fcb* fcb;
+    static char xapref[] = "user.";
+    ULONG xapreflen = strlen(xapref), overhead;
+    LARGE_INTEGER time;
+    BTRFS_TIME now;
+    ULONG utf8len;
+    NTSTATUS Status;
+    KEY searchkey;
+    traverse_ptr tp;
+#ifdef DEBUG_FCB_REFCOUNTS
+    LONG rc;
+#endif
+    
+    TRACE("fpus = %.*S\n", fpus->Length / sizeof(WCHAR), fpus->Buffer);
+    TRACE("stream = %.*S\n", stream->Length / sizeof(WCHAR), stream->Buffer);
+    
+    parfileref = *pparfileref;
+    
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    Status = open_fileref(Vcb, &newpar, fpus, parfileref, FALSE, NULL, NULL, Irp);
+    ExReleaseResource(&Vcb->fcb_lock);
+    
+    if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+        UNICODE_STRING fpus2;
+        
+        if (!is_file_name_valid(fpus))
+            return STATUS_OBJECT_NAME_INVALID;
+        
+        fpus2.Length = fpus2.MaximumLength = fpus->Length;
+        fpus2.Buffer = ExAllocatePoolWithTag(pool_type, fpus2.MaximumLength, ALLOC_TAG);
+        
+        if (!fpus2.Buffer) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlCopyMemory(fpus2.Buffer, fpus->Buffer, fpus2.Length);
+        
+        Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, &newpar, rollback);
+    
+        if (!NT_SUCCESS(Status)) {
+            ERR("file_create2 returned %08x\n", Status);
+            ExFreePool(fpus2.Buffer);
+            return Status;
+        }
+        
+        send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
+        send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
+    } else if (!NT_SUCCESS(Status)) {
+        ERR("open_fileref returned %08x\n", Status);
+        return Status;
+    }
+    
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    free_fileref(parfileref);
+    ExReleaseResource(&Vcb->fcb_lock);
+    
+    parfileref = newpar;
+    *pparfileref = parfileref;
+    
+    if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK) {
+        WARN("parent not file or symlink\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+    
+    if (options & FILE_DIRECTORY_FILE) {
+        WARN("tried to create directory as stream\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+        
+    fcb = create_fcb(pool_type);
+    if (!fcb) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    fcb->Vcb = Vcb;
+    
+    fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
+    fcb->Header.AllocationSize.QuadPart = 0;
+    fcb->Header.FileSize.QuadPart = 0;
+    fcb->Header.ValidDataLength.QuadPart = 0;
+    
+#ifdef DEBUG_FCB_REFCOUNTS
+    rc = InterlockedIncrement(&parfileref->fcb->refcount);
+    WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref));
+#else
+    InterlockedIncrement(&parfileref->fcb->refcount);
+#endif
+    fcb->subvol = parfileref->fcb->subvol;
+    fcb->inode = parfileref->fcb->inode;
+    fcb->type = parfileref->fcb->type;
+    
+    fcb->ads = TRUE;
+    
+    Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream->Buffer, stream->Length);
+    if (!NT_SUCCESS(Status)) {
+        ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return Status;
+    }
+    
+    fcb->adsxattr.Length = utf8len + xapreflen;
+    fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
+    fcb->adsxattr.Buffer = ExAllocatePoolWithTag(pool_type, fcb->adsxattr.MaximumLength, ALLOC_TAG);
+    if (!fcb->adsxattr.Buffer) {
+        ERR("out of memory\n");
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen);
+    
+    Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream->Buffer, stream->Length);
+    if (!NT_SUCCESS(Status)) {
+        ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return Status;
+    }
+    
+    fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
+    
+    TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
+    
+    fcb->adshash = calc_crc32c(0xfffffffe, (UINT8*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
+    TRACE("adshash = %08x\n", fcb->adshash);
+    
+    searchkey.obj_id = parfileref->fcb->inode;
+    searchkey.obj_type = TYPE_XATTR_ITEM;
+    searchkey.offset = fcb->adshash;
+    
+    Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("find_item returned %08x\n", Status);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return Status;
+    }
+    
+    if (!keycmp(&tp.item->key, &searchkey))
+        overhead = tp.item->size;
+    else
+        overhead = 0;
+    
+    fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - (sizeof(DIR_ITEM) - 1);
+    
+    if (utf8len + xapreflen + overhead > fcb->adsmaxlen) {
+        WARN("not enough room for new DIR_ITEM (%u + %u > %u)", utf8len + xapreflen, overhead, fcb->adsmaxlen);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return STATUS_DISK_FULL;
+    } else
+        fcb->adsmaxlen -= overhead + utf8len + xapreflen;
+    
+    fileref = create_fileref();
+    if (!fileref) {
+        ERR("out of memory\n");
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    fileref->fcb = fcb;
+
+    fileref->filepart.MaximumLength = fileref->filepart.Length = stream->Length;
+    fileref->filepart.Buffer = ExAllocatePoolWithTag(pool_type, fileref->filepart.MaximumLength, ALLOC_TAG);
+    if (!fileref->filepart.Buffer) {
+        ERR("out of memory\n");
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fileref(fileref);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlCopyMemory(fileref->filepart.Buffer, stream->Buffer, stream->Length);
+    
+    Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fileref(fileref);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return Status;
+    }
+    
+    mark_fcb_dirty(fcb);
+    mark_fileref_dirty(fileref);
+    
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+    ExReleaseResource(&Vcb->fcb_lock);
+    
+    KeQuerySystemTime(&time);
+    win_time_to_unix(time, &now);
+    
+    parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
+    parfileref->fcb->inode_item.sequence++;
+    parfileref->fcb->inode_item.st_ctime = now;
+    
+    mark_fcb_dirty(parfileref->fcb);
+    
+    parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+    parfileref->fcb->subvol->root_item.ctime = now;
+    
+    fileref->parent = (struct _file_ref*)parfileref;
+    
+    insert_fileref_child(parfileref, fileref, TRUE);
+    
+    increase_fileref_refcount(parfileref);
+    
+    *pfileref = fileref;
+    
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJECT FileObject, PUNICODE_STRING fnus, ULONG disposition, ULONG options, LIST_ENTRY* rollback) {
     NTSTATUS Status;
 //     fcb *fcb, *parfcb = NULL;
     file_ref *fileref, *parfileref = NULL, *related;
-    ULONG i, j;
+    ULONG i, j, fn_offset;
 //     ULONG utf8len;
     ccb* ccb;
     static WCHAR datasuf[] = {':','$','D','A','T','A',0};
     UNICODE_STRING dsus, fpus, stream;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
     PACCESS_STATE access_state = IrpSp->Parameters.Create.SecurityContext->AccessState;
 #ifdef DEBUG_FCB_REFCOUNTS
     LONG oc;
 #endif
-            
+
     TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp, Vcb, FileObject, fnus->Length / sizeof(WCHAR), fnus->Buffer, disposition, options);
     
     if (Vcb->readonly)
@@ -2274,7 +2587,10 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     } else
         related = NULL;
     
-    Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL);
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL, NULL, Irp);
+    ExReleaseResource(&Vcb->fcb_lock);
+    
     if (!NT_SUCCESS(Status))
         goto end;
     
@@ -2296,7 +2612,7 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     while (i > 0 && fnus->Buffer[i-1] != '\\' && fnus->Buffer[i-1] != '/') { i--; }
     
     fpus.MaximumLength = (j - i + 2) * sizeof(WCHAR);
-    fpus.Buffer = ExAllocatePoolWithTag(PagedPool, fpus.MaximumLength, ALLOC_TAG);
+    fpus.Buffer = ExAllocatePoolWithTag(pool_type, fpus.MaximumLength, ALLOC_TAG);
     if (!fpus.Buffer) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -2308,6 +2624,8 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR));
     fpus.Buffer[j - i + 1] = 0;
     
+    fn_offset = i;
+    
     if (fpus.Length > dsus.Length) { // check for :$DATA suffix
         UNICODE_STRING lb;
                 
@@ -2341,183 +2659,11 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     }
     
     if (stream.Length > 0) {
-        file_ref* newpar;
-        fcb* fcb;
-        static char xapref[] = "user.";
-        ULONG xapreflen = strlen(xapref);
-        LARGE_INTEGER time;
-        BTRFS_TIME now;
-        ULONG utf8len;
-#ifdef DEBUG_FCB_REFCOUNTS
-        LONG rc;
-#endif
-        
-        TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
-        TRACE("stream = %.*S\n", stream.Length / sizeof(WCHAR), stream.Buffer);
-        
-        Status = open_fileref(Vcb, &newpar, &fpus, parfileref, FALSE, NULL);
-        
-        if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
-            UNICODE_STRING fpus2;
-            
-            if (!is_file_name_valid(&fpus))
-                return STATUS_OBJECT_NAME_INVALID;
-            
-            fpus2.Length = fpus2.MaximumLength = fpus.Length;
-            fpus2.Buffer = ExAllocatePoolWithTag(PagedPool, fpus2.MaximumLength, ALLOC_TAG);
-            
-            if (!fpus2.Buffer) {
-                ERR("out of memory\n");
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto end;
-            }
-            
-            RtlCopyMemory(fpus2.Buffer, fpus.Buffer, fpus2.Length);
-            
-            Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, &newpar, rollback);
-        
-            if (!NT_SUCCESS(Status)) {
-                ERR("file_create2 returned %08x\n", Status);
-                ExFreePool(fpus2.Buffer);
-                goto end;
-            }
-            
-            send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
-            send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
-        } else if (!NT_SUCCESS(Status)) {
-            ERR("open_fileref returned %08x\n", Status);
-            goto end;
-        }
-        
-        free_fileref(parfileref);
-        parfileref = newpar;
-        
-        if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK) {
-            WARN("parent not file or symlink\n");
-            Status = STATUS_INVALID_PARAMETER;
-            goto end;
-        }
-        
-        if (options & FILE_DIRECTORY_FILE) {
-            WARN("tried to create directory as stream\n");
-            Status = STATUS_INVALID_PARAMETER;
-            goto end;
-        }
-            
-        fcb = create_fcb();
-        if (!fcb) {
-            ERR("out of memory\n");
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto end;
-        }
-        
-        fcb->Vcb = Vcb;
-        
-        fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
-        fcb->Header.AllocationSize.QuadPart = 0;
-        fcb->Header.FileSize.QuadPart = 0;
-        fcb->Header.ValidDataLength.QuadPart = 0;
-        
-#ifdef DEBUG_FCB_REFCOUNTS
-        rc = InterlockedIncrement(&parfileref->fcb->refcount);
-        WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref));
-#else
-        InterlockedIncrement(&parfileref->fcb->refcount);
-#endif
-        fcb->subvol = parfileref->fcb->subvol;
-        fcb->inode = parfileref->fcb->inode;
-        fcb->type = parfileref->fcb->type;
-        
-        fcb->ads = TRUE;
-        
-        Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream.Buffer, stream.Length);
+        Status = create_stream(Vcb, &fileref, &parfileref, &fpus, &stream, Irp, options, pool_type, rollback);
         if (!NT_SUCCESS(Status)) {
-            ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
-            free_fcb(fcb);
+            ERR("create_stream returned %08x\n", Status);
             goto end;
         }
-        
-        fcb->adsxattr.Length = utf8len + xapreflen;
-        fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
-        fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
-        if (!fcb->adsxattr.Buffer) {
-            ERR("out of memory\n");
-            free_fcb(fcb);
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto end;
-        }
-        
-        RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen);
-        
-        Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream.Buffer, stream.Length);
-        if (!NT_SUCCESS(Status)) {
-            ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
-            free_fcb(fcb);
-            goto end;
-        }
-        
-        fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
-        
-        TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
-        
-        fcb->adshash = calc_crc32c(0xfffffffe, (UINT8*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
-        TRACE("adshash = %08x\n", fcb->adshash);
-
-        fileref = create_fileref();
-        if (!fileref) {
-            ERR("out of memory\n");
-            free_fcb(fcb);
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto end;
-        }
-        
-        fileref->fcb = fcb;
-
-        fileref->filepart.MaximumLength = fileref->filepart.Length = stream.Length;
-        fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.MaximumLength, ALLOC_TAG);
-        if (!fileref->filepart.Buffer) {
-            ERR("out of memory\n");
-            free_fileref(fileref);
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto end;
-        }
-        
-        RtlCopyMemory(fileref->filepart.Buffer, stream.Buffer, stream.Length);
-        
-        Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
-        if (!NT_SUCCESS(Status)) {
-            ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-            free_fileref(fileref);
-            goto end;
-        }
-       
-        mark_fcb_dirty(fcb);
-        
-        InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
-        InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
-        
-        KeQuerySystemTime(&time);
-        win_time_to_unix(time, &now);
-        
-        parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
-        parfileref->fcb->inode_item.sequence++;
-        parfileref->fcb->inode_item.st_ctime = now;
-        
-        mark_fcb_dirty(parfileref->fcb);
-        
-        parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
-        parfileref->fcb->subvol->root_item.ctime = now;
-        
-        fileref->parent = (struct _file_ref*)parfileref;
-        
-        ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
-        InsertTailList(&parfileref->children, &fileref->list_entry);
-        ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
-        
-        increase_fileref_refcount(parfileref);
-        
-        ExFreePool(fpus.Buffer);
-        fpus.Buffer = NULL;
     } else {
         if (!is_file_name_valid(&fpus)) {
             Status = STATUS_OBJECT_NAME_INVALID;
@@ -2541,7 +2687,9 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     if (!ccb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(fileref);
+        ExReleaseResource(&Vcb->fcb_lock);
         goto end;
     }
     
@@ -2567,6 +2715,11 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
 #endif
     
     FileObject->FsContext2 = ccb;
+    
+    if (fn_offset > 0) {
+        FileObject->FileName.Length -= fn_offset * sizeof(WCHAR);
+        RtlMoveMemory(&FileObject->FileName.Buffer[0], &FileObject->FileName.Buffer[fn_offset], FileObject->FileName.Length);
+    }
 
     FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
     
@@ -2603,8 +2756,11 @@ end:
         ExFreePool(fpus.Buffer);
     
 end2:
-    if (parfileref)
+    if (parfileref) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(parfileref);
+        ExReleaseResource(&Vcb->fcb_lock);
+    }
     
     return Status;
 }
@@ -2740,7 +2896,7 @@ static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) {
     if (fcb->type == BTRFS_TYPE_FILE || fcb->type == BTRFS_TYPE_SYMLINK) {
         ULONG size, bytes_read, i;
         
-        if (fcb->inode_item.st_size < sizeof(ULONG)) {
+        if (fcb->type == BTRFS_TYPE_FILE && fcb->inode_item.st_size < sizeof(ULONG)) {
             WARN("file was too short to be a reparse point\n");
             return STATUS_INVALID_PARAMETER;
         }
@@ -2861,6 +3017,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
     device_extension* Vcb = DeviceObject->DeviceExtension;
     PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
     USHORT unparsed;
+    ULONG fn_offset = 0;
     file_ref *related, *fileref;
 #ifdef DEBUG_FCB_REFCOUNTS
     LONG oc;
@@ -2927,8 +3084,12 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         goto exit;
     }
     
-    // FIXME - if Vcb->readonly or subvol readonly, don't allow the write ACCESS_MASK flags
-    
+    if (Vcb->readonly && Stack->Parameters.Create.SecurityContext->DesiredAccess &
+        (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC)) {
+        Status = STATUS_MEDIA_WRITE_PROTECTED;
+        goto exit;
+    }
+
     if (options & FILE_OPEN_BY_FILE_ID) {
         if (FileObject->FileName.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) {
             UINT64 inode;
@@ -2942,8 +3103,11 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
                 fileref = related->parent;
                 increase_fileref_refcount(fileref);
                 Status = STATUS_SUCCESS;
-            } else
-                Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref);
+            } else {
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref, Irp);
+                ExReleaseResource(&Vcb->fcb_lock);
+            }
         } else {
             WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
             Status = STATUS_NOT_IMPLEMENTED;
@@ -2955,7 +3119,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             goto exit;
         }
         
-        Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed, &fn_offset, Irp);
+        ExReleaseResource(&Vcb->fcb_lock);
     }
     
     if (Status == STATUS_REPARSE) {
@@ -2999,7 +3165,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         if (RequestedDisposition == FILE_CREATE) {
             TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref));
             Status = STATUS_OBJECT_NAME_COLLISION;
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fileref(fileref);
+            ExReleaseResource(&Vcb->fcb_lock);
             goto exit;
         }
     } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
@@ -3007,9 +3175,12 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
             goto exit;
         }
-    } else {
+    } else if (Status == STATUS_OBJECT_PATH_NOT_FOUND) {
         TRACE("open_fileref returned %08x\n", Status);
         goto exit;
+    } else {
+        ERR("open_fileref returned %08x\n", Status);
+        goto exit;
     }
     
     if (NT_SUCCESS(Status)) { // file already exists
@@ -3018,17 +3189,30 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
             if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
                 Status = STATUS_ACCESS_DENIED;
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fileref(fileref);
+                ExReleaseResource(&Vcb->fcb_lock);
                 goto exit;
             }
             
             if (Vcb->readonly) {
                 Status = STATUS_MEDIA_WRITE_PROTECTED;
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fileref(fileref);
+                ExReleaseResource(&Vcb->fcb_lock);
                 goto exit;
             }
         }
         
+        if (fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && Stack->Parameters.Create.SecurityContext->DesiredAccess &
+            (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC)) {
+            Status = STATUS_ACCESS_DENIED;
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fileref(fileref);
+            ExReleaseResource(&Vcb->fcb_lock);
+            goto exit;
+        }
+        
         TRACE("deleted = %s\n", fileref->deleted ? "TRUE" : "FALSE");
         
         sf = fileref;
@@ -3057,39 +3241,24 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             
             if (Stack->Parameters.Create.SecurityContext->DesiredAccess & ~allowed) {
                 Status = STATUS_ACCESS_DENIED;
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fileref(fileref);
+                ExReleaseResource(&Vcb->fcb_lock);
                 goto exit;
             }
         }
         
-        if (options & FILE_NON_DIRECTORY_FILE && fileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
-            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-            free_fileref(fileref);
-            ExReleaseResourceLite(&Vcb->fcb_lock);
-            
-            Status = STATUS_FILE_IS_A_DIRECTORY;
-            goto exit;
-        } else if (options & FILE_DIRECTORY_FILE && fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
-            TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref));
-            
-            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-            free_fileref(fileref);
-            ExReleaseResourceLite(&Vcb->fcb_lock);
-            
-            Status = STATUS_NOT_A_DIRECTORY;
-            goto exit;
-        }
-        
         if (options & FILE_DELETE_ON_CLOSE && (fileref == Vcb->root_fileref || Vcb->readonly ||
             fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY || fileref->fcb->atts & FILE_ATTRIBUTE_READONLY)) {
             Status = STATUS_CANNOT_DELETE;
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fileref(fileref);
+            ExReleaseResource(&Vcb->fcb_lock);
             goto exit;
         }
         
-        
         if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT))  {
-            UINT8* data;
+            REPARSE_DATA_BUFFER* data;
             
             /* How reparse points work from the point of view of the filesystem appears to
              * undocumented. When returning STATUS_REPARSE, MSDN encourages us to return
@@ -3098,7 +3267,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
              * a pointer to the reparse data buffer in Irp->Tail.Overlay.AuxiliaryBuffer,
              * IopSymlinkProcessReparse will do the translation for us. */
             
-            Status = get_reparse_block(fileref->fcb, &data);
+            Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
             if (!NT_SUCCESS(Status)) {
                 ERR("get_reparse_block returned %08x\n", Status);
                 
@@ -3109,7 +3278,10 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             }
             
             Status = STATUS_REPARSE;
-            RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
+            Irp->IoStatus.Information = data->ReparseTag;
+            
+            if (FileObject->FileName.Buffer[(FileObject->FileName.Length / sizeof(WCHAR)) - 1] == '\\')
+                data->Reserved = sizeof(WCHAR);
             
             Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
             
@@ -3119,6 +3291,26 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             goto exit;
         }
         
+        if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
+            if (options & FILE_NON_DIRECTORY_FILE && !(fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
+                Status = STATUS_FILE_IS_A_DIRECTORY;
+                goto exit;
+            }
+        } else if (options & FILE_DIRECTORY_FILE) {
+            TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref));
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fileref(fileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
+            Status = STATUS_NOT_A_DIRECTORY;
+            goto exit;
+        }
+        
         if (fileref->fcb->open_count > 0) {
             Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
                                         Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, TRUE);
@@ -3155,7 +3347,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
                 WARN("cannot overwrite readonly file\n");
                 Status = STATUS_ACCESS_DENIED;
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fileref(fileref);
+                ExReleaseResource(&Vcb->fcb_lock);
                 goto exit;
             }
     
@@ -3168,10 +3362,12 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
 //             }
             
             // FIXME - make sure not ADS!
-            Status = truncate_file(fileref->fcb, fileref->fcb->inode_item.st_size, rollback);
+            Status = truncate_file(fileref->fcb, 0, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("truncate_file returned %08x\n", Status);
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fileref(fileref);
+                ExReleaseResource(&Vcb->fcb_lock);
                 goto exit;
             }
             
@@ -3180,7 +3376,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
                 
                 if (!NT_SUCCESS(Status)) {
                     ERR("extend_file returned %08x\n", Status);
+                    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                     free_fileref(fileref);
+                    ExReleaseResource(&Vcb->fcb_lock);
                     goto exit;
                 }
             }
@@ -3192,7 +3390,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             oldatts = fileref->fcb->atts;
             
             defda = get_file_attributes(Vcb, &fileref->fcb->inode_item, fileref->fcb->subvol, fileref->fcb->inode, fileref->fcb->type,
-                                        fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE);
+                                        fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE, Irp);
             
             if (RequestedDisposition == FILE_SUPERSEDE)
                 fileref->fcb->atts = Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
@@ -3249,6 +3447,11 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         
         FileObject->FsContext2 = ccb;
         
+        if (fn_offset > 0) {
+            FileObject->FileName.Length -= fn_offset * sizeof(WCHAR);
+            RtlMoveMemory(&FileObject->FileName.Buffer[0], &FileObject->FileName.Buffer[fn_offset], FileObject->FileName.Length);
+        }
+        
         FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
         
         if (NT_SUCCESS(Status)) {
@@ -3295,6 +3498,53 @@ exit:
     return Status;
 }
 
+NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp) {
+    UINT64 i;
+    
+    for (i = 0; i < Vcb->superblock.num_devices; i++) {
+        if (Vcb->devices[i].removable) {
+            NTSTATUS Status;
+            ULONG cc;
+            IO_STATUS_BLOCK iosb;
+            
+            Status = dev_ioctl(Vcb->devices[i].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("dev_ioctl returned %08x\n", Status);
+                return Status;
+            }
+            
+            if (iosb.Information < sizeof(ULONG)) {
+                ERR("iosb.Information was too short\n");
+                return STATUS_INTERNAL_ERROR;
+            }
+            
+            if (cc != Vcb->devices[i].change_count) {
+                PDEVICE_OBJECT dev;
+                
+                Vcb->devices[i].devobj->Flags |= DO_VERIFY_VOLUME;
+                
+                dev = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
+                IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL);
+                
+                if (!dev) {
+                    dev = IoGetDeviceToVerify(PsGetCurrentThread());
+                    IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
+                }
+                
+                dev = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
+                
+                if (dev)
+                    IoVerifyVolume(dev, FALSE);
+                
+                return STATUS_VERIFY_REQUIRED;
+            }
+        }
+    }
+    
+    return STATUS_SUCCESS;
+}
+
 NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     NTSTATUS Status;
     PIO_STACK_LOCATION IrpSp;
@@ -3321,6 +3571,13 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     }
     
     Vcb = DeviceObject->DeviceExtension;
+    
+    Status = verify_vcb(Vcb, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("verify_vcb returned %08x\n", Status);
+        goto exit;
+    }
+    
     ExAcquireResourceSharedLite(&Vcb->load_lock, TRUE);
     locked = TRUE;
     
index 972bc6f..5cc54e9 100644 (file)
@@ -32,7 +32,7 @@ typedef struct {
     enum DirEntryType dir_entry_type;
 } dir_entry;
 
-static ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts) {
+ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts, PIRP Irp) {
     fcb* fcb;
     ULONG tag = 0, br;
     NTSTATUS Status;
@@ -49,7 +49,7 @@ static ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64
         return 0;
     
     ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-    Status = open_fcb(Vcb, subvol, inode, type, NULL, NULL, &fcb);
+    Status = open_fcb(Vcb, subvol, inode, type, NULL, NULL, &fcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
         ExReleaseResourceLite(&Vcb->fcb_lock);
@@ -78,7 +78,9 @@ static ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64
 end:
     ExReleaseResourceLite(fcb->Header.Resource);
 
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
     free_fcb(fcb);
+    ExReleaseResourceLite(&Vcb->fcb_lock);
     
     return tag;
 }
@@ -154,7 +156,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
                     searchkey.obj_type = TYPE_INODE_ITEM;
                     searchkey.offset = 0xffffffffffffffff;
                     
-                    Status = find_item(fcb->Vcb, r, &tp, &searchkey, FALSE);
+                    Status = find_item(fcb->Vcb, r, &tp, &searchkey, FALSE, Irp);
                     if (!NT_SUCCESS(Status)) {
                         ERR("error - find_item returned %08x\n", Status);
                         return Status;
@@ -177,7 +179,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
                         
                         BOOL dotfile = de->namelen > 1 && de->name[0] == '.';
 
-                        atts = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+                        atts = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE, Irp);
                     }
                 }
                 
@@ -238,12 +240,12 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             fbdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
             fbdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
             fbdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
-            fbdi->ChangeTime.QuadPart = 0;
+            fbdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
             fbdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
             fbdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
             fbdi->FileAttributes = atts;
             fbdi->FileNameLength = stringlen;
-            fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
+            fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp);
             fbdi->ShortNameLength = 0;
 //             fibdi->ShortName[12];
             
@@ -277,7 +279,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             fdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
             fdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
             fdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
-            fdi->ChangeTime.QuadPart = 0;
+            fdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
             fdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
             fdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
             fdi->FileAttributes = atts;
@@ -313,12 +315,12 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             ffdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
             ffdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
             ffdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
-            ffdi->ChangeTime.QuadPart = 0;
+            ffdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
             ffdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
             ffdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
             ffdi->FileAttributes = atts;
             ffdi->FileNameLength = stringlen;
-            ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
+            ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp);
             
             Status = RtlUTF8ToUnicodeN(ffdi->FileName, stringlen, &stringlen, de->name, de->namelen);
 
@@ -353,12 +355,12 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             fibdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
             fibdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
             fibdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
-            fibdi->ChangeTime.QuadPart = 0;
+            fibdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
             fibdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
             fibdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
             fibdi->FileAttributes = atts;
             fibdi->FileNameLength = stringlen;
-            fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
+            fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp);
             fibdi->ShortNameLength = 0;
 //             fibdi->ShortName[12];
             fibdi->FileId.QuadPart = inode;
@@ -428,7 +430,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
     return STATUS_NO_MORE_FILES;
 }
 
-static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_entry* de) {
+static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_entry* de, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
     DIR_ITEM* di;
@@ -520,14 +522,14 @@ static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_en
         searchkey.obj_type = TYPE_DIR_INDEX;
         searchkey.offset = *offset;
         
-        Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE);
+        Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             goto end;
         }
         
         if (keycmp(&tp.item->key, &searchkey) == -1) {
-            if (find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE))
+            if (find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp))
                 tp = next_tp;
         }
         
@@ -766,7 +768,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     }
     
     newoffset = ccb->query_dir_offset;
-    Status = next_dir_entry(fileref, &newoffset, &de);
+    Status = next_dir_entry(fileref, &newoffset, &de, Irp);
     
     if (!NT_SUCCESS(Status)) {
         if (Status == STATUS_NO_MORE_FILES && initial)
@@ -845,7 +847,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
             ExFreePool(us.Buffer);
         
         if (!found) {
-            Status = find_file_in_dir(fcb->Vcb, &ccb->query_string, fileref, &found_subvol, &found_inode, &found_type, &found_index, &utf8);
+            Status = find_file_in_dir(fcb->Vcb, &ccb->query_string, fileref, &found_subvol, &found_inode, &found_type, &found_index, &utf8, Irp);
             
             if (!NT_SUCCESS(Status)) {
                 Status = STATUS_NO_SUCH_FILE;
@@ -901,7 +903,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
                 ExFreePool(de.name);
             
             newoffset = ccb->query_dir_offset;
-            Status = next_dir_entry(fileref, &newoffset, &de);
+            Status = next_dir_entry(fileref, &newoffset, &de, Irp);
             
             ExFreePool(uni_fn);
             if (NT_SUCCESS(Status)) {
@@ -976,7 +978,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
                 UNICODE_STRING di_uni_fn;
                 
                 newoffset = ccb->query_dir_offset;
-                Status = next_dir_entry(fileref, &newoffset, &de);
+                Status = next_dir_entry(fileref, &newoffset, &de, Irp);
                 if (NT_SUCCESS(Status)) {
                     if (has_wildcard) {
                         ULONG stringlen;
index 2bc760e..992b33d 100644 (file)
@@ -91,7 +91,7 @@ static UINT64 get_extent_hash(UINT8 type, void* data) {
     }
 }
 
-static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem, UINT8 level, LIST_ENTRY* rollback) {
+static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
@@ -113,7 +113,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
     searchkey.obj_type = TYPE_EXTENT_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -153,7 +153,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
         *ptr = type;
         RtlCopyMemory(ptr + 1, data, datalen);
         
-        if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -184,13 +184,13 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), NULL, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), NULL, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             ExFreePool(ei);
             return STATUS_INTERNAL_ERROR;
         }
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -265,7 +265,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
                     
                     delete_tree_item(Vcb, &tp, rollback);
                     
-                    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+                    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
                         ERR("insert_tree_item failed\n");
                         return STATUS_INTERNAL_ERROR;
                     }
@@ -335,7 +335,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size + sizeof(UINT8) + datalen, NULL, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size + sizeof(UINT8) + datalen, NULL, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -352,7 +352,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
         searchkey.obj_type = type;
         searchkey.offset = offset;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -381,7 +381,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
             
             delete_tree_item(Vcb, &tp2, rollback);
             
-            if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, data2, tp2.item->size, NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, data2, tp2.item->size, NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 return STATUS_INTERNAL_ERROR;
             }
@@ -394,7 +394,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
             
             delete_tree_item(Vcb, &tp, rollback);
             
-            if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 return STATUS_INTERNAL_ERROR;
             }
@@ -408,7 +408,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
     data2 = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG);
     RtlCopyMemory(data2, data, datalen);
     
-    if (!insert_tree_item(Vcb, Vcb->extent_root, address, type, offset, data2, datalen, NULL, rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->extent_root, address, type, offset, data2, datalen, NULL, Irp, rollback)) {
         ERR("insert_tree_item failed\n");
         return STATUS_INTERNAL_ERROR;
     }
@@ -421,7 +421,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
     
     delete_tree_item(Vcb, &tp, rollback);
     
-    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
         ERR("insert_tree_item failed\n");
         return STATUS_INTERNAL_ERROR;
     }
@@ -429,7 +429,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
     return STATUS_SUCCESS;
 }
 
-NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback) {
+NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback) {
     EXTENT_DATA_REF edr;
     
     edr.root = root;
@@ -437,7 +437,7 @@ NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UI
     edr.offset = offset;
     edr.count = refcount;
     
-    return increase_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, rollback);
+    return increase_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, Irp, rollback);
 }
 
 void decrease_chunk_usage(chunk* c, UINT64 delta) {
@@ -447,7 +447,7 @@ void decrease_chunk_usage(chunk* c, UINT64 delta) {
 }
 
 static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem,
-                                         UINT8 level, UINT64 parent, LIST_ENTRY* rollback) {
+                                         UINT8 level, UINT64 parent, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     NTSTATUS Status;
     traverse_ptr tp, tp2;
@@ -464,7 +464,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
     searchkey.obj_type = TYPE_EXTENT_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -498,13 +498,13 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), &tp, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), &tp, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             ExFreePool(ei);
             return STATUS_INTERNAL_ERROR;
         }
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -604,7 +604,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
                     
                     delete_tree_item(Vcb, &tp, rollback);
                     
-                    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, rollback)) {
+                    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, Irp, rollback)) {
                         ERR("insert_tree_item failed\n");
                         return STATUS_INTERNAL_ERROR;
                     }
@@ -643,7 +643,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
                     
                     delete_tree_item(Vcb, &tp, rollback);
                     
-                    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, rollback)) {
+                    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, Irp, rollback)) {
                         ERR("insert_tree_item failed\n");
                         return STATUS_INTERNAL_ERROR;
                     }
@@ -670,7 +670,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
     searchkey.obj_type = type;
     searchkey.offset = (type == TYPE_SHARED_DATA_REF || type == TYPE_EXTENT_REF_V0) ? parent : get_extent_hash(type, data);
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -717,7 +717,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
                 
                 newedr->count -= edr->count;
                 
-                if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, newedr, tp2.item->size, NULL, rollback)) {
+                if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, newedr, tp2.item->size, NULL, Irp, rollback)) {
                     ERR("insert_tree_item failed\n");
                     return STATUS_INTERNAL_ERROR;
                 }
@@ -736,7 +736,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
             
             delete_tree_item(Vcb, &tp, rollback);
             
-            if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 return STATUS_INTERNAL_ERROR;
             }
@@ -775,7 +775,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
             
             delete_tree_item(Vcb, &tp, rollback);
             
-            if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 return STATUS_INTERNAL_ERROR;
             }
@@ -810,7 +810,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -823,7 +823,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
 }
 
 NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode,
-                                       UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback) {
+                                       UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback) {
     EXTENT_DATA_REF edr;
     
     edr.root = root;
@@ -831,20 +831,20 @@ NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UI
     edr.offset = offset;
     edr.count = refcount;
     
-    return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, 0, rollback);
+    return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, 0, Irp, rollback);
 }
 
-NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, LIST_ENTRY* rollback) {
+NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, PIRP Irp, LIST_ENTRY* rollback) {
     SHARED_DATA_REF sdr;
 
     sdr.offset = treeaddr;
     sdr.count = 1;
     
-    return decrease_extent_refcount(Vcb, address, size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, parent, rollback);
+    return decrease_extent_refcount(Vcb, address, size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, parent, Irp, rollback);
 }
 
-NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, LIST_ENTRY* rollback) {
-    return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_REF_V0, NULL, NULL, 0, treeaddr, rollback);
+NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, PIRP Irp, LIST_ENTRY* rollback) {
+    return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_REF_V0, NULL, NULL, 0, treeaddr, Irp, rollback);
 }
 
 typedef struct {
@@ -918,7 +918,7 @@ static NTSTATUS add_data_extent_ref(LIST_ENTRY* extent_refs, UINT64 tree_id, UIN
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 flags, LIST_ENTRY* extent_refs, LIST_ENTRY* rollback) {
+static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 flags, LIST_ENTRY* extent_refs, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY *le, *next_le;
     UINT64 refcount;
     ULONG inline_len;
@@ -1005,7 +1005,7 @@ static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UIN
         le = le->Flink;
     }
     
-    if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, inline_len, NULL, rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, inline_len, NULL, Irp, rollback)) {
         ERR("error - failed to insert item\n");
         ExFreePool(ei);
         return STATUS_INTERNAL_ERROR;
@@ -1017,7 +1017,7 @@ static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UIN
         while (le != extent_refs) {
             extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
             
-            if (!insert_tree_item(Vcb, Vcb->extent_root, address, er->type, er->hash, er->data, get_extent_data_len(er->type), NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->extent_root, address, er->type, er->hash, er->data, get_extent_data_len(er->type), NULL, Irp, rollback)) {
                 ERR("error - failed to insert item\n");
                 return STATUS_INTERNAL_ERROR;
             }
@@ -1081,7 +1081,7 @@ static NTSTATUS populate_extent_refs_from_tree(device_extension* Vcb, UINT64 tre
     return STATUS_SUCCESS;
 }
 
-NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback) {
+NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
     BOOL b;
@@ -1092,7 +1092,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s
     searchkey.obj_type = TYPE_EXTENT_ITEM;
     searchkey.offset = size;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -1114,7 +1114,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s
     searchkey.obj_type = TYPE_EXTENT_REF_V0;
     searchkey.offset = 0;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -1123,7 +1123,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s
     InitializeListHead(&extent_refs);
     
     do {
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         
         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
             Status = populate_extent_refs_from_tree(Vcb, tp.item->key.offset, address, &extent_refs);
@@ -1143,7 +1143,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s
         }
     } while (b);
     
-    Status = construct_extent_item(Vcb, address, size, EXTENT_ITEM_DATA, &extent_refs, rollback);
+    Status = construct_extent_item(Vcb, address, size, EXTENT_ITEM_DATA, &extent_refs, Irp, rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("construct_extent_item returned %08x\n", Status);
         free_extent_refs(&extent_refs);
@@ -1155,7 +1155,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s
     return STATUS_SUCCESS;
 }
 
-UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset) {
+UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, PIRP Irp) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
@@ -1166,14 +1166,14 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s
     searchkey.obj_type = TYPE_EXTENT_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return 0;
     }
     
     if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
-        ERR("could not find address %llx in extent tree\n", address);
+        TRACE("could not find address %llx in extent tree\n", address);
         return 0;
     }
     
@@ -1249,7 +1249,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s
     searchkey.obj_type = TYPE_EXTENT_DATA_REF;
     searchkey.offset = get_extent_data_ref_hash2(root, objid, offset);
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return 0;
@@ -1272,7 +1272,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s
         searchkey.obj_type = TYPE_EXTENT_REF_V0;
         searchkey.offset = 0;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return 0;
@@ -1281,7 +1281,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s
         do {
             traverse_ptr next_tp;
             
-            b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+            b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
             
             if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
                 if (tp.item->size >= sizeof(EXTENT_REF_V0)) {
@@ -1335,7 +1335,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s
         searchkey.obj_type = TYPE_SHARED_DATA_REF;
         searchkey.offset = 0;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return 0;
@@ -1344,7 +1344,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s
         do {
             traverse_ptr next_tp;
             
-            b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+            b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
             
             if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
                 if (tp.item->size >= sizeof(SHARED_DATA_REF)) {
index 682489a..2b0608d 100644 (file)
@@ -67,7 +67,7 @@ static BOOLEAN STDCALL fast_query_basic_info(PFILE_OBJECT FileObject, BOOLEAN wa
     fbi->CreationTime.QuadPart = unix_time_to_win(&fcb->inode_item.otime);
     fbi->LastAccessTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_atime);
     fbi->LastWriteTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_mtime);
-    fbi->ChangeTime.QuadPart = 0;
+    fbi->ChangeTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_ctime);
     fbi->FileAttributes = fcb->atts;
 
     IoStatus->Status = STATUS_SUCCESS;
@@ -212,9 +212,22 @@ static BOOLEAN STDCALL fast_io_query_network_open_info(PFILE_OBJECT FileObject,
     return FALSE;
 }
 
-static NTSTATUS STDCALL fast_io_acquire_for_mod_write(PFILE_OBJECT FileObject, PLARGE_INTEGER EndingOffset, struct _ERESOURCE **ResourceToRelease, PDEVICE_OBJECT DeviceObject){
-    TRACE("STUB: fast_io_acquire_for_mod_write\n");
-    return STATUS_NOT_IMPLEMENTED;
+static NTSTATUS STDCALL fast_io_acquire_for_mod_write(PFILE_OBJECT FileObject, PLARGE_INTEGER EndingOffset, struct _ERESOURCE **ResourceToRelease, PDEVICE_OBJECT DeviceObject) {
+    fcb* fcb;
+    
+    TRACE("(%p, %llx, %p, %p)\n", FileObject, EndingOffset->QuadPart, ResourceToRelease, DeviceObject);
+    
+    fcb = FileObject->FsContext;
+    
+    if (!fcb)
+        return STATUS_INVALID_PARAMETER;
+    
+    *ResourceToRelease = fcb->Header.PagingIoResource;
+    
+    if (!ExAcquireResourceSharedLite(*ResourceToRelease, FALSE))
+        return STATUS_CANT_WAIT;
+    
+    return STATUS_SUCCESS;
 }
 
 static BOOLEAN STDCALL fast_io_read_compressed(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, ULONG LockKey, PVOID Buffer, PMDL *MdlChain, PIO_STATUS_BLOCK IoStatus, struct _COMPRESSED_DATA_INFO *CompressedDataInfo, ULONG CompressedDataInfoLength, PDEVICE_OBJECT DeviceObject){
index 4d0e323..092ed28 100644 (file)
@@ -24,7 +24,7 @@
 #endif
 #endif
 
-static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us);
+static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us, PIRP Irp);
 
 static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
     FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
@@ -65,7 +65,7 @@ static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, P
         LARGE_INTEGER time;
         BTRFS_TIME now;
         
-        defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE);
+        defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE, Irp);
         
         if (fcb->type == BTRFS_TYPE_DIRECTORY)
             fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
@@ -91,10 +91,37 @@ static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, P
         filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
     }
     
-//     FIXME - CreationTime
-//     FIXME - LastAccessTime
-//     FIXME - LastWriteTime
-//     FIXME - ChangeTime
+    if (fbi->CreationTime.QuadPart == -1) {
+        FIXME("FIXME - support CreationTime == -1\n"); // FIXME - set ccb flag
+    } else if (fbi->CreationTime.QuadPart != 0) {
+        win_time_to_unix(fbi->CreationTime, &fcb->inode_item.otime);
+        inode_item_changed = TRUE;
+        filter |= FILE_NOTIFY_CHANGE_CREATION;
+    }
+    
+    if (fbi->LastAccessTime.QuadPart == -1) {
+        FIXME("FIXME - support LastAccessTime == -1\n"); // FIXME - set ccb flag
+    } else if (fbi->LastAccessTime.QuadPart != 0) {
+        win_time_to_unix(fbi->LastAccessTime, &fcb->inode_item.st_atime);
+        inode_item_changed = TRUE;
+        filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
+    }
+    
+    if (fbi->LastWriteTime.QuadPart == -1) {
+        FIXME("FIXME - support LastWriteTime == -1\n"); // FIXME - set ccb flag
+    } else if (fbi->LastWriteTime.QuadPart != 0) {
+        win_time_to_unix(fbi->LastWriteTime, &fcb->inode_item.st_mtime);
+        inode_item_changed = TRUE;
+        filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+    }
+    
+    if (fbi->ChangeTime.QuadPart == -1) {
+        FIXME("FIXME - support ChangeTime == -1\n"); // FIXME - set ccb flag
+    } else if (fbi->ChangeTime.QuadPart != 0) {
+        win_time_to_unix(fbi->ChangeTime, &fcb->inode_item.st_ctime);
+        inode_item_changed = TRUE;
+        // no filter for this
+    }
 
     if (inode_item_changed) {
         fcb->inode_item.transid = Vcb->superblock.generation;
@@ -171,7 +198,7 @@ end:
     return Status;
 }
 
-static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     INODE_EXTREF* ier;
@@ -181,7 +208,7 @@ static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 ino
     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);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -214,7 +241,7 @@ static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 ino
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier2, iersize, NULL, rollback)) {
+        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier2, iersize, NULL, Irp, rollback)) {
             ERR("error - failed to insert item\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -230,7 +257,7 @@ static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 ino
         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)) {
+        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier, sizeof(INODE_EXTREF) - 1 + utf8->Length, NULL, Irp, rollback)) {
             ERR("error - failed to insert item\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -239,7 +266,7 @@ static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 ino
     return STATUS_SUCCESS;
 }
 
-NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     INODE_REF* ir;
@@ -249,7 +276,7 @@ NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64
     searchkey.obj_type = TYPE_INODE_REF;
     searchkey.offset = parinode;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -263,7 +290,7 @@ NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64
         if (irsize > maxlen) {
             if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
                 TRACE("INODE_REF too long, creating INODE_EXTREF\n");
-                return add_inode_extref(Vcb, subvol, inode, parinode, index, utf8, rollback);
+                return add_inode_extref(Vcb, subvol, inode, parinode, index, utf8, Irp, rollback);
             } else {
                 ERR("item would be too long (%u > %u)\n", irsize, maxlen);
                 return STATUS_INTERNAL_ERROR;
@@ -286,7 +313,7 @@ NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir2, irsize, NULL, rollback)) {
+        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir2, irsize, NULL, Irp, rollback)) {
             ERR("error - failed to insert item\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -301,7 +328,7 @@ NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64
         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)) {
+        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir, sizeof(INODE_REF) - 1 + ir->n, NULL, Irp, rollback)) {
             ERR("error - failed to insert item\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -332,18 +359,19 @@ BOOL has_open_children(file_ref* fileref) {
 }
 
 static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
+    device_extension* Vcb = oldfcb->Vcb;
     fcb* fcb;
     LIST_ENTRY* le;
     
     // FIXME - we can skip a lot of this if the inode is about to be deleted
     
-    fcb = create_fcb();
+    fcb = create_fcb(PagedPool); // FIXME - what if we duplicate the paging file?
     if (!fcb) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    fcb->Vcb = oldfcb->Vcb;
+    fcb->Vcb = Vcb;
 
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
     fcb->Header.AllocationSize = oldfcb->Header.AllocationSize;
@@ -355,6 +383,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
     if (oldfcb->ads) {
         fcb->ads = TRUE;
         fcb->adshash = oldfcb->adshash;
+        fcb->adsmaxlen = oldfcb->adsmaxlen;
         
         if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) {
             fcb->adsxattr.Length = oldfcb->adsxattr.Length;
@@ -363,7 +392,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
             
             if (!fcb->adsxattr.Buffer) {
                 ERR("out of memory\n");
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fcb(fcb);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
             
@@ -377,7 +410,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
             
             if (!fcb->adsdata.Buffer) {
                 ERR("out of memory\n");
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fcb(fcb);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
             
@@ -393,7 +430,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
         fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG);
         if (!fcb->sd) {
             ERR("out of memory\n");
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fcb(fcb);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
@@ -411,7 +452,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
             
             if (!ext2) {
                 ERR("out of memory\n");
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fcb(fcb);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
             
@@ -423,7 +468,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
                 
                 if (!ext2->data) {
                     ERR("out of memory\n");
+                    
+                    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                     free_fcb(fcb);
+                    ExReleaseResourceLite(&Vcb->fcb_lock);
+                    
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
                 
@@ -448,7 +497,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
         
         if (!hl2) {
             ERR("out of memory\n");
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fcb(fcb);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
@@ -461,7 +514,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
         if (!hl2->name.Buffer) {
             ERR("out of memory\n");
             ExFreePool(hl2);
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fcb(fcb);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
@@ -474,7 +531,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
             ERR("out of memory\n");
             ExFreePool(hl2->name.Buffer);
             ExFreePool(hl2);
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fcb(fcb);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
@@ -493,41 +554,16 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
         fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG);
         if (!fcb->reparse_xattr.Buffer) {
             ERR("out of memory\n");
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fcb(fcb);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
         RtlCopyMemory(fcb->reparse_xattr.Buffer, oldfcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
     }
-    
-    if (oldfcb->ads) {
-        fcb->ads = TRUE;
-        fcb->adshash = oldfcb->adshash;
-        
-        if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) {
-            fcb->adsxattr.Length = fcb->adsxattr.MaximumLength = oldfcb->adsxattr.Length;
-            fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
-            if (!fcb->adsxattr.Buffer) {
-                ERR("out of memory\n");
-                free_fcb(fcb);
-                return STATUS_INSUFFICIENT_RESOURCES;
-            }
-            
-            RtlCopyMemory(fcb->adsxattr.Buffer, oldfcb->adsxattr.Buffer, fcb->adsxattr.Length);
-        }
-        
-        if (oldfcb->adsdata.Buffer && oldfcb->adsdata.Length > 0) {
-            fcb->adsdata.Length = fcb->adsdata.MaximumLength = oldfcb->adsdata.Length;
-            fcb->adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsdata.MaximumLength, ALLOC_TAG);
-            if (!fcb->adsdata.Buffer) {
-                ERR("out of memory\n");
-                free_fcb(fcb);
-                return STATUS_INSUFFICIENT_RESOURCES;
-            }
-            
-            RtlCopyMemory(fcb->adsdata.Buffer, oldfcb->adsdata.Buffer, fcb->adsdata.Length);
-        }
-    }
 
 end:
     *pfcb = fcb;
@@ -543,7 +579,7 @@ typedef struct _move_entry {
     LIST_ENTRY list_entry;
 } move_entry;
 
-static NTSTATUS add_children_to_move_list(move_entry* me) {
+static NTSTATUS add_children_to_move_list(move_entry* me, PIRP Irp) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
@@ -586,7 +622,7 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
     searchkey.obj_type = TYPE_XATTR_ITEM;
     searchkey.offset = 0;
     
-    Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto end;
@@ -652,8 +688,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         RtlCopyMemory(xattr.Buffer, xa->name, xa->n);
                         xattr.Buffer[xa->n] = 0;
                         
+                        ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                         Status = open_fcb_stream(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, me->fileref->fcb->inode, &xattr,
-                                                 tp.item->key.offset, me->fileref->fcb, &fcb);
+                                                 tp.item->key.offset, me->fileref->fcb, &fcb, Irp);
+                        ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                        
                         if (!NT_SUCCESS(Status)) {
                             ERR("open_fcb_stream returned %08x\n", Status);
                             ExFreePool(xattr.Buffer);
@@ -663,7 +702,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         fr = create_fileref();
                         if (!fr) {
                             ERR("out of memory\n");
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fcb(fcb);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             Status = STATUS_INSUFFICIENT_RESOURCES;
                             goto end;
                         }
@@ -673,7 +716,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, &xa->name[xapreflen], xa->n - xapreflen);
                         if (!NT_SUCCESS(Status)) {
                             ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -681,14 +728,22 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         if (!fr->filepart.Buffer) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
                         Status = RtlUTF8ToUnicodeN(fr->filepart.Buffer, stringlen, &stringlen, &xa->name[xapreflen], xa->n - xapreflen);
                         if (!NT_SUCCESS(Status)) {
                             ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -697,7 +752,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE);
                         if (!NT_SUCCESS(Status)) {
                             ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
 
@@ -710,7 +769,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         if (!me) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -730,7 +793,7 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
             } while (len > 0);
         }
         
-        b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp);
         if (b) {
             tp = next_tp;
             
@@ -744,7 +807,7 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
         searchkey.obj_type = TYPE_DIR_INDEX;
         searchkey.offset = 2;
         
-        Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE);
+        Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             goto end;
@@ -842,7 +905,9 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                             inode = di->key.obj_id;
                         }
                         
-                        Status = open_fcb(me->fileref->fcb->Vcb, subvol, inode, di->type, &utf8, me->fileref->fcb, &fcb);
+                        ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
+                        Status = open_fcb(me->fileref->fcb->Vcb, subvol, inode, di->type, &utf8, me->fileref->fcb, &fcb, Irp);
+                        ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
                         
                         if (!NT_SUCCESS(Status)) {
                             ERR("open_fcb returned %08x\n", Status);
@@ -855,7 +920,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
                             ExFreePool(utf8.Buffer);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fcb(fcb);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -865,7 +934,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, utf8.Buffer, utf8.Length);
                         if (!NT_SUCCESS(Status)) {
                             ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -873,7 +946,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         if (!fr->filepart.Buffer) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -881,7 +958,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         
                         if (!NT_SUCCESS(Status)) {
                             ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -891,7 +972,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         
                         if (!NT_SUCCESS(Status)) {
                             ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -909,7 +994,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         if (!me) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -927,7 +1016,7 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                 }
             }
             
-            b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE);
+            b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp);
             if (b) {
                 tp = next_tp;
                 
@@ -945,7 +1034,7 @@ end:
     return Status;
 }
 
-static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, LIST_ENTRY* rollback) {
+static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     LIST_ENTRY move_list, *le;
     move_entry* me;
@@ -980,7 +1069,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
         ExAcquireResourceSharedLite(me->fileref->fcb->Header.Resource, TRUE);
         
         if (!me->fileref->fcb->ads && me->fileref->fcb->subvol == origparent->fcb->subvol) {
-            Status = add_children_to_move_list(me);
+            Status = add_children_to_move_list(me, Irp);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("add_children_to_move_list returned %08x\n", Status);
@@ -1031,14 +1120,14 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
                     LIST_ENTRY* le2;
                     
                     if (destdir->fcb->subvol->lastinode == 0)
-                        get_last_inode(destdir->fcb->Vcb, destdir->fcb->subvol);
+                        get_last_inode(destdir->fcb->Vcb, destdir->fcb->subvol, Irp);
 
                     me->fileref->fcb->subvol = destdir->fcb->subvol;
                     me->fileref->fcb->inode = ++destdir->fcb->subvol->lastinode; // FIXME - do proper function for this
                     me->fileref->fcb->inode_item.st_nlink = 1;
                     
                     defda = get_file_attributes(me->fileref->fcb->Vcb, &me->fileref->fcb->inode_item, me->fileref->fcb->subvol, me->fileref->fcb->inode,
-                                                me->fileref->fcb->type, me->fileref->filepart.Length > 0 && me->fileref->filepart.Buffer[0] == '.', TRUE);
+                                                me->fileref->fcb->type, me->fileref->filepart.Length > 0 && me->fileref->filepart.Buffer[0] == '.', TRUE, Irp);
                     
                     me->fileref->fcb->sd_dirty = !!me->fileref->fcb->sd;
                     me->fileref->fcb->atts_changed = defda != me->fileref->fcb->atts;
@@ -1060,7 +1149,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
                                     ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
                                 } else {
                                     Status = update_changed_extent_ref(me->fileref->fcb->Vcb, c, ed2->address, ed2->size, me->fileref->fcb->subvol->id, me->fileref->fcb->inode,
-                                                                       ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
+                                                                       ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
                                     
                                     if (!NT_SUCCESS(Status)) {
                                         ERR("update_changed_extent_ref returned %08x\n", Status);
@@ -1221,13 +1310,15 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
         if (!me->parent) {
             RemoveEntryList(&me->fileref->list_entry);
             
+            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
             free_fileref(me->fileref->parent);
+            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
             
             me->fileref->parent = destdir;
             
             increase_fileref_refcount(destdir);
             
-            Status = fcb_get_last_dir_index(me->fileref->parent->fcb, &me->fileref->index);
+            Status = fcb_get_last_dir_index(me->fileref->parent->fcb, &me->fileref->index, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("fcb_get_last_dir_index returned %08x\n", Status);
                 goto end;
@@ -1249,7 +1340,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
             me->fileref->fcb->subvol->root_item.num_references++;
 
         if (!me->dummyfileref->fcb->ads) {
-            Status = delete_fileref(me->dummyfileref, NULL, rollback);
+            Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_fileref returned %08x\n", Status);
                 goto end;
@@ -1303,7 +1394,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
         me = CONTAINING_RECORD(le, move_entry, list_entry);
         
         if (me->dummyfileref->fcb->ads && me->parent->dummyfileref->fcb->deleted) {
-            Status = delete_fileref(me->dummyfileref, NULL, rollback);
+            Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_fileref returned %08x\n", Status);
                 goto end;
@@ -1326,23 +1417,36 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
     
 end:
     while (!IsListEmpty(&move_list)) {
+        device_extension* Vcb;
+        
         le = RemoveHeadList(&move_list);
         me = CONTAINING_RECORD(le, move_entry, list_entry);
+        Vcb = me->fileref->fcb->Vcb;
         
-        if (me->dummyfcb)
+        if (me->dummyfcb) {
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fcb(me->dummyfcb);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+        }
         
-        if (me->dummyfileref)
+        if (me->dummyfileref) {
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fileref(me->dummyfileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+        }
         
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(me->fileref);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+        
         ExFreePool(me);
     }
     
     return Status;
 }
 
-static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, BOOL ReplaceIfExists) {
+static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer;
     fcb *fcb = FileObject->FsContext;
     ccb* ccb = FileObject->FsContext2;
@@ -1360,11 +1464,8 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     
     InitializeListHead(&rollback);
     
-    // FIXME - check fri length
-    // FIXME - don't ignore fri->RootDirectory
-    
     TRACE("tfo = %p\n", tfo);
-    TRACE("ReplaceIfExists = %u\n", ReplaceIfExists);
+    TRACE("ReplaceIfExists = %u\n", IrpSp->Parameters.SetFile.ReplaceIfExists);
     TRACE("RootDirectory = %p\n", fri->RootDirectory);
     TRACE("FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName);
     
@@ -1379,6 +1480,12 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     } else {
         LONG i;
         
+        while (fnlen > 0 && (fri->FileName[fnlen - 1] == '/' || fri->FileName[fnlen - 1] == '\\'))
+            fnlen--;
+        
+        if (fnlen == 0)
+            return STATUS_INVALID_PARAMETER;
+        
         for (i = fnlen - 1; i >= 0; i--) {
             if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') {
                 fn = &fri->FileName[i+1];
@@ -1425,13 +1532,15 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
         increase_fileref_refcount(related);
     }
 
-    Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL);
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, Irp);
+    ExReleaseResourceLite(&Vcb->fcb_lock);
 
     if (NT_SUCCESS(Status)) {
         TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref));
         
         if (fileref != oldfileref && !oldfileref->deleted) {
-            if (!ReplaceIfExists) {
+            if (!IrpSp->Parameters.SetFile.ReplaceIfExists) {
                 Status = STATUS_OBJECT_NAME_COLLISION;
                 goto end;
             } else if ((oldfileref->fcb->open_count >= 1 || has_open_children(oldfileref)) && !oldfileref->deleted) {
@@ -1447,14 +1556,18 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
             }
         }
         
-        if (fileref == oldfileref || !oldfileref->deleted) {
+        if (fileref == oldfileref || oldfileref->deleted) {
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fileref(oldfileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
             oldfileref = NULL;
         }
     }
     
     if (!related) {
-        Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, Irp);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
 
         if (!NT_SUCCESS(Status)) {
             ERR("open_fileref returned %08x\n", Status);
@@ -1469,8 +1582,21 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     }
     
     if (oldfileref) {
-        // FIXME - check we have permissions for this
-        Status = delete_fileref(oldfileref, NULL, &rollback);
+        ACCESS_MASK access;
+        SECURITY_SUBJECT_CONTEXT subjcont;
+        
+        SeCaptureSubjectContext(&subjcont);
+
+        if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL,
+                           IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
+            SeReleaseSubjectContext(&subjcont);
+            WARN("SeAccessCheck failed, returning %08x\n", Status);
+            goto end;
+        }
+
+        SeReleaseSubjectContext(&subjcont);
+        
+        Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("delete_fileref returned %08x\n", Status);
             goto end;
@@ -1478,7 +1604,7 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     }
     
     if (fileref->parent->fcb->subvol != related->fcb->subvol && fileref->fcb->subvol == fileref->parent->fcb->subvol) {
-        Status = move_across_subvols(fileref, related, &utf8, &fnus, &rollback);
+        Status = move_across_subvols(fileref, related, &utf8, &fnus, Irp, &rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("move_across_subvols returned %08x\n", Status);
         }
@@ -1593,7 +1719,7 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     if (fr2->fcb->type == BTRFS_TYPE_DIRECTORY)
         fr2->fcb->fileref = fr2;
 
-    Status = fcb_get_last_dir_index(related->fcb, &index);
+    Status = fcb_get_last_dir_index(related->fcb, &index, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("fcb_get_last_dir_index returned %08x\n", Status);
         goto end;
@@ -1726,7 +1852,9 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     fr2->parent->fcb->inode_item.st_ctime = now;
     fr2->parent->fcb->inode_item.st_mtime = now;
     
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
     free_fileref(fr2);
+    ExReleaseResourceLite(&Vcb->fcb_lock);
     
     mark_fcb_dirty(fr2->parent->fcb);
     
@@ -1738,14 +1866,23 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     Status = STATUS_SUCCESS;
     
 end:
-    if (oldfileref)
+    if (oldfileref) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(oldfileref);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
     
-    if (!NT_SUCCESS(Status) && related)
+    if (!NT_SUCCESS(Status) && related) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(related);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
     
-    if (!NT_SUCCESS(Status) && fr2)
+    if (!NT_SUCCESS(Status) && fr2) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(fr2);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
     
     if (NT_SUCCESS(Status))
         clear_rollback(&rollback);
@@ -1778,39 +1915,12 @@ NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT6
         
         fcb->adsdata.Length = end;
     } else if (end > fcb->adsdata.Length) {
-//         UINT16 maxlen;
-        
         TRACE("extending stream to %llx bytes\n", end);
-//         
-//         // 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");
-//             return STATUS_INTERNAL_ERROR;
-//         }
-//         
-//         if (!get_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) {
-//             ERR("get_xattr failed\n");
-//             return STATUS_INTERNAL_ERROR;
-//         }
-//         
-//         maxlen -= tp.item->size - datalen; // subtract XATTR_ITEM overhead
-//         
-//         if (end > maxlen) {
-//             ERR("error - xattr too long (%llu > %u)\n", end, maxlen);
-//             return STATUS_DISK_FULL;
-//         }
+
+        if (end > fcb->adsmaxlen) {
+            ERR("error - xattr too long (%llu > %u)\n", end, fcb->adsmaxlen);
+            return STATUS_DISK_FULL;
+        }
 
         if (end > fcb->adsdata.MaximumLength) {
             char* data = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG);
@@ -1913,7 +2023,7 @@ static NTSTATUS STDCALL set_end_of_file_information(device_extension* Vcb, PIRP
         
         TRACE("truncating file to %llx bytes\n", feofi->EndOfFile.QuadPart);
         
-        Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, &rollback);
+        Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, Irp, &rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("error - truncate_file failed\n");
             goto end;
@@ -2031,6 +2141,12 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF
         tfofcb = tfo->FsContext;
         parfcb = tfofcb;
         
+        while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\'))
+            fnlen--;
+        
+        if (fnlen == 0)
+            return STATUS_INVALID_PARAMETER;
+        
         for (i = fnlen - 1; i >= 0; i--) {
             if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') {
                 fn = &fli->FileName[i+1];
@@ -2083,7 +2199,9 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF
         increase_fileref_refcount(related);
     }
 
-    Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL);
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, Irp);
+    ExReleaseResourceLite(&Vcb->fcb_lock);
 
     if (NT_SUCCESS(Status)) {
         if (!oldfileref->deleted) {
@@ -2107,13 +2225,17 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF
                 goto end;
             }
         } else {
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fileref(oldfileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
             oldfileref = NULL;
         }
     }
     
     if (!related) {
-        Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, Irp);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
 
         if (!NT_SUCCESS(Status)) {
             ERR("open_fileref returned %08x\n", Status);
@@ -2150,14 +2272,14 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF
 
         SeReleaseSubjectContext(&subjcont);
         
-        Status = delete_fileref(oldfileref, NULL, &rollback);
+        Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("delete_fileref returned %08x\n", Status);
             goto end;
         }
     }
     
-    Status = fcb_get_last_dir_index(related->fcb, &index);
+    Status = fcb_get_last_dir_index(related->fcb, &index, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("fcb_get_last_dir_index returned %08x\n", Status);
         goto end;
@@ -2259,14 +2381,23 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF
     Status = STATUS_SUCCESS;
     
 end:
-    if (oldfileref)
+    if (oldfileref) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(oldfileref);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
     
-    if (!NT_SUCCESS(Status) && related)
+    if (!NT_SUCCESS(Status) && related) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(related);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
     
-    if (!NT_SUCCESS(Status) && fr2)
+    if (!NT_SUCCESS(Status) && fr2) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(fr2);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
     
     if (NT_SUCCESS(Status))
         clear_rollback(&rollback);
@@ -2296,7 +2427,12 @@ NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp
         goto exit;
     }
     
-    if (Vcb->readonly) {
+    if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
+        Status = STATUS_ACCESS_DENIED;
+        goto end;
+    }
+    
+    if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) {
         Status = STATUS_MEDIA_WRITE_PROTECTED;
         goto end;
     }
@@ -2313,7 +2449,7 @@ NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp
         goto end;
     }
     
-    if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+    if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) {
         Status = STATUS_ACCESS_DENIED;
         goto end;
     }
@@ -2408,7 +2544,7 @@ NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp
         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);
+            Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject);
             break;
 
         case FileValidDataLengthInformation:
@@ -2457,7 +2593,7 @@ static NTSTATUS STDCALL fill_in_file_basic_information(FILE_BASIC_INFORMATION* f
     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->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
     
     if (fcb->ads) {
         if (!fileref || !fileref->parent) {
@@ -2496,7 +2632,7 @@ static NTSTATUS STDCALL fill_in_file_network_open_information(FILE_NETWORK_OPEN_
     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;
+    fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
     
     if (fcb->ads) {
         fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length;
@@ -2776,7 +2912,7 @@ static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, file_ref* fileref, LONG* length) {
+static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, file_ref* fileref, PIRP Irp, LONG* length) {
     *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
     
     if (fcb->ads) {
@@ -2789,7 +2925,10 @@ static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_IN
     } else
         ati->FileAttributes = fcb->atts;
     
-    ati->ReparseTag = 0; // FIXME
+    if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+        ati->ReparseTag = 0;
+    else
+        ati->ReparseTag = get_reparse_tag(fcb->Vcb, fcb->subvol, fcb->inode, fcb->type, fcb->atts, Irp);
     
     return STATUS_SUCCESS;
 }
@@ -2801,7 +2940,7 @@ typedef struct {
     LIST_ENTRY list_entry;
 } stream_info;
 
-static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) {
+static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, PIRP Irp, LONG* length) {
     ULONG reqsize;
     LIST_ENTRY streamlist, *le;
     FILE_STREAM_INFORMATION *entry, *lastentry;
@@ -2832,7 +2971,7 @@ static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION*
     searchkey.obj_type = TYPE_XATTR_ITEM;
     searchkey.offset = 0;
 
-    Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto end;
@@ -2916,7 +3055,7 @@ static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION*
             }
         }
         
-        b = find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp);
         if (b) {
             tp = next_tp;
             
@@ -3086,7 +3225,7 @@ typedef struct {
     LIST_ENTRY list_entry;
 } name_bit;
 
-static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol) {
+static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -3109,7 +3248,7 @@ static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol) {
     searchkey.obj_type = TYPE_ROOT_BACKREF;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -3156,7 +3295,7 @@ static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol) {
     
     // FIXME - recursion
 
-    Status = get_inode_dir_path(Vcb, parsubvol, rr->dir, &dirpath);
+    Status = get_inode_dir_path(Vcb, parsubvol, rr->dir, &dirpath, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("get_inode_dir_path returned %08x\n", Status);
         return Status;
@@ -3206,7 +3345,7 @@ end:
     return Status;
 }
 
-static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us) {
+static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us, PIRP Irp) {
     KEY searchkey;
     NTSTATUS Status;
     UINT64 in;
@@ -3222,7 +3361,7 @@ static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 i
     
     // FIXME - start with subvol prefix
     if (!subvol->path.Buffer) {
-        Status = get_subvol_path(Vcb, subvol);
+        Status = get_subvol_path(Vcb, subvol, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("get_subvol_path returned %08x\n", Status);
             return Status;
@@ -3234,7 +3373,7 @@ static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 i
         searchkey.obj_type = TYPE_INODE_EXTREF;
         searchkey.offset = 0xffffffffffffffff;
         
-        Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             goto end;
@@ -3359,13 +3498,13 @@ end:
     return Status;
 }
 
-NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr) {
+NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp) {
     NTSTATUS Status;
     fcb* fcb;
     hardlink* hl;
     file_ref *parfr, *fr;
     
-    Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb);
+    Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
         return Status;
@@ -3390,7 +3529,7 @@ NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode
     if (hl->parent == inode) // root of subvol
         parfr = NULL;
     else {
-        Status = open_fileref_by_inode(Vcb, subvol, hl->parent, &parfr);
+        Status = open_fileref_by_inode(Vcb, subvol, hl->parent, &parfr, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("open_fileref_by_inode returned %08x\n", Status);
             free_fcb(fcb);
@@ -3453,7 +3592,7 @@ NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode
 }
 
 #ifndef __REACTOS__
-static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, LONG* length) {
+static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) {
     NTSTATUS Status;
     LIST_ENTRY* le;
     ULONG bytes_needed;
@@ -3517,7 +3656,7 @@ static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fl
             
             TRACE("parent %llx, index %llx, name %.*S\n", hl->parent, hl->index, hl->name.Length / sizeof(WCHAR), hl->name.Buffer);
             
-            Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr);
+            Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("open_fileref_by_inode returned %08x\n", Status);
@@ -3707,7 +3846,7 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec
                 goto exit;
             }
             
-            Status = fill_in_file_attribute_information(ati, fcb, fileref, &length);
+            Status = fill_in_file_attribute_information(ati, fcb, fileref, Irp, &length);
             
             break;
         }
@@ -3841,7 +3980,7 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec
             
             TRACE("FileStreamInformation\n");
             
-            Status = fill_in_file_stream_information(fsi, fileref, &length);
+            Status = fill_in_file_stream_information(fsi, fileref, Irp, &length);
 
             break;
         }
@@ -3853,7 +3992,7 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec
             
             TRACE("FileHardLinkInformation\n");
             
-            Status = fill_in_hard_link_information(fli, fileref, &length);
+            Status = fill_in_hard_link_information(fli, fileref, Irp, &length);
             
             break;
         }
index 16795eb..48eabad 100644 (file)
@@ -17,8 +17,6 @@
 
 #include "btrfs_drv.h"
 
-#define INTERVAL 15000 // in milliseconds
-
 static void do_flush(device_extension* Vcb) {
     LIST_ENTRY rollback;
     
@@ -28,8 +26,8 @@ static void do_flush(device_extension* Vcb) {
 
     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
 
-    if (Vcb->need_write)
-        do_write(Vcb, &rollback);
+    if (Vcb->need_write && !Vcb->readonly)
+        do_write(Vcb, NULL, &rollback);
     
     free_trees(Vcb);
     
@@ -49,7 +47,7 @@ void STDCALL flush_thread(void* context) {
     
     KeInitializeTimer(&Vcb->flush_thread_timer);
     
-    due_time.QuadPart = -INTERVAL * 10000;
+    due_time.QuadPart = (UINT64)Vcb->options.flush_interval * -10000000;
     
     KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
     
index 5f583c1..6982ae0 100644 (file)
 
 // #define DEBUG_SPACE_LISTS
 
-static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIST_ENTRY* rollback) {
+static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     fcb* fcb;
     
-    Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &fcb);
+    Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &fcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
         return Status;
@@ -36,7 +36,7 @@ static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIS
     fcb->dirty = TRUE;
     
     if (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), rollback);
+        Status = excise_extents(fcb->Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), Irp, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("excise_extents returned %08x\n", Status);
             return Status;
@@ -45,14 +45,14 @@ static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIS
     
     fcb->deleted = TRUE;
     
-    flush_fcb(fcb, FALSE, rollback);
+    flush_fcb(fcb, FALSE, Irp, rollback);
     
     free_fcb(fcb);
 
     return STATUS_SUCCESS;
 }
 
-NTSTATUS clear_free_space_cache(device_extension* Vcb) {
+NTSTATUS clear_free_space_cache(device_extension* Vcb, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
     NTSTATUS Status;
@@ -65,7 +65,7 @@ NTSTATUS clear_free_space_cache(device_extension* Vcb) {
     searchkey.obj_type = 0;
     searchkey.offset = 0;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -86,7 +86,7 @@ NTSTATUS clear_free_space_cache(device_extension* Vcb) {
                 else {
                     LIST_ENTRY* le;
                     
-                    Status = remove_free_space_inode(Vcb, fsi->key.obj_id, &rollback);
+                    Status = remove_free_space_inode(Vcb, fsi->key.obj_id, Irp, &rollback);
                     
                     if (!NT_SUCCESS(Status)) {
                         ERR("remove_free_space_inode for (%llx,%x,%llx) returned %08x\n", fsi->key.obj_id, fsi->key.obj_type, fsi->key.offset, Status);
@@ -109,7 +109,7 @@ NTSTATUS clear_free_space_cache(device_extension* Vcb) {
                 WARN("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(FREE_SPACE_ITEM));
         }
         
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         if (b)
             tp = next_tp;
     } while (b);
@@ -245,7 +245,7 @@ static void order_space_entry(space* s, LIST_ENTRY* list_size) {
     InsertTailList(list_size, &s->list_entry_size);
 }
 
-static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
+static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     FREE_SPACE_ITEM* fsi;
@@ -266,7 +266,7 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
     searchkey.obj_type = 0;
     searchkey.offset = c->offset;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -293,10 +293,10 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
     num_entries = fsi->num_entries;
     num_bitmaps = fsi->num_bitmaps;
     
-    Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &c->cache);
+    Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &c->cache, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
-        return Status;
+        return STATUS_NOT_FOUND;
     }
     
     if (c->cache->inode_item.st_size == 0) {
@@ -329,7 +329,7 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
         
         free_fcb(c->cache);
         c->cache = NULL;
-        return Status;
+        return STATUS_NOT_FOUND;
     }
     
     if (size > c->cache->inode_item.st_size)
@@ -446,7 +446,7 @@ clearcache:
     
     InitializeListHead(&rollback);
     
-    Status = excise_extents(Vcb, c->cache, 0, c->cache->inode_item.st_size, &rollback);
+    Status = excise_extents(Vcb, c->cache, 0, c->cache->inode_item.st_size, Irp, &rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("excise_extents returned %08x\n", Status);
         do_rollback(Vcb, &rollback);
@@ -463,7 +463,7 @@ clearcache:
     return STATUS_NOT_FOUND;
 }
 
-NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) {
+NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp) {
     traverse_ptr tp, next_tp;
     KEY searchkey;
     UINT64 lastaddr;
@@ -473,7 +473,7 @@ NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) {
 //     LIST_ENTRY* le;
     
     if (Vcb->superblock.generation - 1 == Vcb->superblock.cache_generation) {
-        Status = load_stored_free_space_cache(Vcb, c);
+        Status = load_stored_free_space_cache(Vcb, c, Irp);
         
         if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) {
             ERR("load_stored_free_space_cache returned %08x\n", Status);
@@ -489,7 +489,7 @@ NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) {
         searchkey.obj_type = TYPE_EXTENT_ITEM;
         searchkey.offset = 0;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -525,7 +525,7 @@ NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) {
                     lastaddr = tp.item->key.obj_id + tp.item->key.offset;
             }
             
-            b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+            b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
             if (b)
                 tp = next_tp;
         } while (b);
@@ -568,48 +568,52 @@ static NTSTATUS insert_cache_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_
     
     flags = fcb->Vcb->data_flags;
     
-    ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+    ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
     
     while (le != &fcb->Vcb->chunks) {
         c = CONTAINING_RECORD(le, chunk, list_entry);
         
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
         if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
-            if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback)) {
-                ExReleaseResourceLite(&c->nonpaged->lock);
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, length)) {
+                ExReleaseResourceLite(&c->lock);
                 ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
                 return STATUS_SUCCESS;
             }
         }
         
-        ExReleaseResourceLite(&c->nonpaged->lock);
+        ExReleaseResourceLite(&c->lock);
         
         le = le->Flink;
     }
     
-    if ((c = alloc_chunk(fcb->Vcb, flags, rollback))) {
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+    ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+    
+    if ((c = alloc_chunk(fcb->Vcb, flags))) {
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+        
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
         if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
-            if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback)) {
-                ExReleaseResourceLite(&c->nonpaged->lock);
-                ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, length)) {
+                ExReleaseResourceLite(&c->lock);
                 return STATUS_SUCCESS;
             }
         }
         
-        ExReleaseResourceLite(&c->nonpaged->lock);
-    }
-    
-    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+        ExReleaseResourceLite(&c->lock);
+    } else   
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
     
     WARN("couldn't find any data chunks with %llx bytes free\n", length);
 
     return STATUS_DISK_FULL;
 }
 
-static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* changed, LIST_ENTRY* rollback) {
+static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* changed, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY* le;
     NTSTATUS Status;
     UINT64 num_entries, new_cache_size, i;
@@ -669,7 +673,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         
         // create new inode
         
-        c->cache = create_fcb();
+        c->cache = create_fcb(PagedPool);
         if (!c->cache) {
             ERR("out of memory\n");
             return STATUS_INSUFFICIENT_RESOURCES;
@@ -691,7 +695,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         c->cache->subvol = Vcb->root_root;
         
         if (Vcb->root_root->lastinode == 0)
-            get_last_inode(Vcb, Vcb->root_root);
+            get_last_inode(Vcb, Vcb->root_root, Irp);
         
         c->cache->inode = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
         
@@ -712,9 +716,12 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         searchkey.obj_type = 0;
         searchkey.offset = c->offset;
         
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
+            ExFreePool(fsi);
+            free_fcb(c->cache);
+            c->cache = NULL;
             return Status;
         }
         
@@ -725,7 +732,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         fsi->key.obj_type = TYPE_INODE_ITEM;
         fsi->key.offset = 0;
         
-        if (!insert_tree_item(Vcb, Vcb->root_root, FREE_SPACE_CACHE_ID, 0, c->offset, fsi, sizeof(FREE_SPACE_ITEM), NULL, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->root_root, FREE_SPACE_CACHE_ID, 0, c->offset, fsi, sizeof(FREE_SPACE_ITEM), NULL, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             free_fcb(c->cache);
             c->cache = NULL;
@@ -746,7 +753,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         
         Vcb->root_root->lastinode = c->cache->inode;
         
-        flush_fcb(c->cache, TRUE, rollback);
+        flush_fcb(c->cache, TRUE, Irp, rollback);
         
         *changed = TRUE;
     } else if (new_cache_size > c->cache->inode_item.st_size) {
@@ -764,7 +771,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         searchkey.obj_type = 0;
         searchkey.offset = c->offset;
         
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -795,7 +802,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         c->cache->inode_item.st_size = new_cache_size;
         c->cache->inode_item.st_blocks = new_cache_size;
         
-        flush_fcb(c->cache, TRUE, rollback);
+        flush_fcb(c->cache, TRUE, Irp, rollback);
     
         *changed = TRUE;
     } else {
@@ -808,7 +815,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         searchkey.obj_type = TYPE_INODE_ITEM;
         searchkey.offset = 0;
         
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -820,10 +827,12 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
             ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
             RtlCopyMemory(ii, &c->cache->inode_item, sizeof(INODE_ITEM));
             
-            if (!insert_tree_item(Vcb, Vcb->root_root, c->cache->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->root_root, c->cache->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 return STATUS_INTERNAL_ERROR;
             }
+            
+            *changed = TRUE;
         } else {        
             if (tp.item->size < sizeof(INODE_ITEM)) {
                 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_ITEM));
@@ -837,7 +846,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         searchkey.obj_type = 0;
         searchkey.offset = c->offset;
         
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -861,7 +870,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
     return STATUS_SUCCESS;
 }
 
-NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollback) {
+NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY* le = Vcb->chunks_changed.Flink;
     NTSTATUS Status;
 
@@ -871,9 +880,9 @@ NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollba
         BOOL b;
         chunk* c = CONTAINING_RECORD(le, chunk, list_entry_changed);
 
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
-        Status = allocate_cache_chunk(Vcb, c, &b, rollback);
-        ExReleaseResourceLite(&c->nonpaged->lock);
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        Status = allocate_cache_chunk(Vcb, c, &b, Irp, rollback);
+        ExReleaseResourceLite(&c->lock);
         
         if (b)
             *changed = TRUE;
@@ -889,7 +898,25 @@ NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollba
     return STATUS_SUCCESS;
 }
 
-void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) {
+static void add_rollback_space(LIST_ENTRY* rollback, BOOL add, LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c) {
+    rollback_space* rs;
+    
+    rs = ExAllocatePoolWithTag(PagedPool, sizeof(rollback_space), ALLOC_TAG);
+    if (!rs) {
+        ERR("out of memory\n");
+        return;
+    }
+    
+    rs->list = list;
+    rs->list_size = list_size;
+    rs->address = address;
+    rs->length = length;
+    rs->chunk = c;
+    
+    add_rollback(rollback, add ? ROLLBACK_ADD_SPACE : ROLLBACK_SUBTRACT_SPACE, rs);
+}
+
+void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func) {
     LIST_ENTRY* le;
     space *s, *s2;
     
@@ -912,7 +939,8 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
         if (list_size)
             InsertTailList(list_size, &s->list_entry_size);
         
-        // FIXME - insert rollback entry
+        if (rollback)
+            add_rollback_space(rollback, TRUE, list, list_size, address, length, c);
         
         return;
     }
@@ -928,9 +956,11 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
         // new entry envelops old one completely
         if (address <= s2->address && address + length >= s2->address + s2->size) {
             if (address < s2->address) {
+                if (rollback)
+                    add_rollback_space(rollback, TRUE, list, list_size, address, s2->address - address, c);
+                
                 s2->size += s2->address - address;
                 s2->address = address;
-                // FIXME - insert rollback
                 
                 while (s2->list_entry.Blink != list) {
                     space* s3 = CONTAINING_RECORD(s2->list_entry.Blink, space, list_entry);
@@ -951,8 +981,10 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
             }
             
             if (length > s2->size) {
+                if (rollback)
+                    add_rollback_space(rollback, TRUE, list, list_size, s2->address + s2->size, address + length - s2->address - s2->size, c);
+                
                 s2->size = length;
-                // FIXME - insert rollback
                 
                 while (s2->list_entry.Flink != list) {
                     space* s3 = CONTAINING_RECORD(s2->list_entry.Flink, space, list_entry);
@@ -981,9 +1013,11 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
         
         // new entry overlaps start of old one
         if (address < s2->address && address + length >= s2->address) {
+            if (rollback)
+                add_rollback_space(rollback, TRUE, list, list_size, address, s2->address - address, c);
+            
             s2->size += s2->address - address;
             s2->address = address;
-            // FIXME - insert rollback
             
             while (s2->list_entry.Blink != list) {
                 space* s3 = CONTAINING_RECORD(s2->list_entry.Blink, space, list_entry);
@@ -1012,8 +1046,10 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
         
         // new entry overlaps end of old one
         if (address <= s2->address + s2->size && address + length > s2->address + s2->size) {
+            if (rollback)
+                add_rollback_space(rollback, TRUE, list, list_size, address, s2->address + s2->size - address, c);
+            
             s2->size = address + length - s2->address;
-            // FIXME - insert rollback
             
             while (s2->list_entry.Flink != list) {
                 space* s3 = CONTAINING_RECORD(s2->list_entry.Flink, space, list_entry);
@@ -1048,7 +1084,9 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
                 return;
             }
             
-            // FIXME - insert rollback
+            if (rollback)
+                add_rollback_space(rollback, TRUE, list, list_size, address, length, c);
+            
             s->address = address;
             s->size = length;
             InsertHeadList(s2->list_entry.Blink, &s->list_entry);
@@ -1065,7 +1103,6 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
     // check if contiguous with last entry
     if (s2->address + s2->size == address) {
         s2->size += length;
-        // FIXME - insert rollback
         
         if (list_size) {
             RemoveEntryList(&s2->list_entry_size);
@@ -1090,7 +1127,8 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
     if (list_size)
         order_space_entry(s, list_size);
     
-    // FIXME - insert rollback
+    if (rollback)
+        add_rollback_space(rollback, TRUE, list, list_size, address, length, c);
 }
 
 static void space_list_merge(LIST_ENTRY* spacelist, LIST_ENTRY* spacelist_size, LIST_ENTRY* deleting) {
@@ -1108,7 +1146,7 @@ static void space_list_merge(LIST_ENTRY* spacelist, LIST_ENTRY* spacelist_size,
     }
 }
 
-static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* now, LIST_ENTRY* rollback) {
+static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* now, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
@@ -1159,7 +1197,7 @@ static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME*
     c->cache->inode_item.sequence++;
     c->cache->inode_item.st_ctime = *now;
     
-    flush_fcb(c->cache, TRUE, rollback);
+    flush_fcb(c->cache, TRUE, Irp, rollback);
     
     // update free_space item
     
@@ -1167,7 +1205,7 @@ static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME*
     searchkey.obj_type = 0;
     searchkey.offset = c->offset;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -1211,9 +1249,9 @@ static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME*
     
     // write cache
     
-    Status = do_nocow_write(Vcb, c->cache, 0, c->cache->inode_item.st_size, data, NULL, NULL, rollback);
+    Status = do_write_file(c->cache, 0, c->cache->inode_item.st_size, data, NULL, NULL, rollback);
     if (!NT_SUCCESS(Status)) {
-        ERR("do_nocow_write returned %08x\n", Status);
+        ERR("do_write_file returned %08x\n", Status);
         return Status;
     }
 
@@ -1222,7 +1260,7 @@ static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME*
     return STATUS_SUCCESS;
 }
 
-NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback) {
+NTSTATUS update_chunk_caches(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY* le = Vcb->chunks_changed.Flink;
     NTSTATUS Status;
     chunk* c;
@@ -1235,9 +1273,9 @@ NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback) {
     while (le != &Vcb->chunks_changed) {
         c = CONTAINING_RECORD(le, chunk, list_entry_changed);
         
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
-        Status = update_chunk_cache(Vcb, c, &now, rollback);
-        ExReleaseResourceLite(&c->nonpaged->lock);
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        Status = update_chunk_cache(Vcb, c, &now, Irp, rollback);
+        ExReleaseResourceLite(&c->lock);
 
         if (!NT_SUCCESS(Status)) {
             ERR("update_chunk_cache(%llx) returned %08x\n", c->offset, Status);
@@ -1260,10 +1298,10 @@ void _space_list_add(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 addr
     if (!c->list_entry_changed.Flink)
         InsertTailList(&Vcb->chunks_changed, &c->list_entry_changed);
     
-    _space_list_add2(list, deleting ? NULL : &c->space_size, address, length, rollback, func);
+    _space_list_add2(list, deleting ? NULL : &c->space_size, address, length, c, rollback, func);
 }
 
-void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) {
+void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func) {
     LIST_ENTRY *le, *le2;
     space *s, *s2;
     
@@ -1283,7 +1321,9 @@ void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 addre
             return;
         
         if (s2->address >= address && s2->address + s2->size <= address + length) { // remove entry entirely
-            // FIXME - insert rollback
+            if (rollback)
+                add_rollback_space(rollback, FALSE, list, list_size, s2->address, s2->size, c);
+            
             RemoveEntryList(&s2->list_entry);
             
             if (list_size)
@@ -1292,7 +1332,8 @@ void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 addre
             ExFreePool(s2);
         } else if (address + length > s2->address && address + length < s2->address + s2->size) {
             if (address > s2->address) { // cut out hole
-                // FIXME - insert rollback
+                if (rollback)
+                    add_rollback_space(rollback, FALSE, list, list_size, address, length, c);
                 
                 s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
 
@@ -1316,9 +1357,11 @@ void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 addre
                 
                 return;
             } else { // remove start of entry
+                if (rollback)
+                    add_rollback_space(rollback, FALSE, list, list_size, s2->address, address + length - s2->address, c);
+                
                 s2->size -= address + length - s2->address;
                 s2->address = address + length;
-                // FIXME - insert rollback
                 
                 if (list_size) {
                     RemoveEntryList(&s2->list_entry_size);
@@ -1326,7 +1369,9 @@ void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 addre
                 }
             }
         } else if (address > s2->address && address < s2->address + s2->size) { // remove end of entry
-            // FIXME - insert rollback
+            if (rollback)
+                add_rollback_space(rollback, FALSE, list, list_size, address, s2->address + s2->size - address, c);
+            
             s2->size = address - s2->address;
             
             if (list_size) {
@@ -1347,5 +1392,5 @@ void _space_list_subtract(device_extension* Vcb, chunk* c, BOOL deleting, UINT64
     if (!c->list_entry_changed.Flink)
         InsertTailList(&Vcb->chunks_changed, &c->list_entry_changed);
     
-    _space_list_subtract2(list, deleting ? NULL : &c->space_size, address, length, rollback, func);
+    _space_list_subtract2(list, deleting ? NULL : &c->space_size, address, length, c, rollback, func);
 }
index 27c1644..aa42414 100644 (file)
@@ -27,6 +27,8 @@
 
 #define DOTDOT ".."
 
+#define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?)
+
 extern LIST_ENTRY VcbList;
 extern ERESOURCE global_loading_lock;
 
@@ -68,7 +70,7 @@ static void get_uuid(BTRFS_UUID* uuid) {
     }
 }
 
-static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* subvol, UINT64 dupflags, UINT64* newaddr, LIST_ENTRY* rollback) {
+static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* subvol, UINT64 dupflags, UINT64* newaddr, PIRP Irp, LIST_ENTRY* rollback) {
     UINT8* buf;
     NTSTATUS Status;
     write_data_context* wtc;
@@ -90,7 +92,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, NULL);
+    Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("read_data returned %08x\n", Status);
         goto end;
@@ -104,7 +106,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub
     t.header.level = th->level;
     t.header.tree_id = t.root->id;
     
-    Status = get_tree_new_address(Vcb, &t, rollback);
+    Status = get_tree_new_address(Vcb, &t, Irp, rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("get_tree_new_address returned %08x\n", Status);
         goto end;
@@ -143,7 +145,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub
                     EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
                     
                     if (ed2->size != 0) { // not sparse
-                        Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol->id, ln[i].key.obj_id, ln[i].key.offset - ed2->offset, 1, rollback);
+                        Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol->id, ln[i].key.obj_id, ln[i].key.offset - ed2->offset, 1, Irp, rollback);
                         
                         if (!NT_SUCCESS(Status)) {
                             ERR("increase_extent_refcount_data returned %08x\n", Status);
@@ -159,7 +161,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub
         internal_node* in = (internal_node*)&th[1];
         
         for (i = 0; i < th->num_items; i++) {
-            Status = snapshot_tree_copy(Vcb, in[i].address, subvol, dupflags, &newaddr, rollback);
+            Status = snapshot_tree_copy(Vcb, in[i].address, subvol, dupflags, &newaddr, Irp, rollback);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("snapshot_tree_copy returned %08x\n", Status);
@@ -178,7 +180,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub
     wtc->tree = TRUE;
     wtc->stripes_left = 0;
     
-    Status = write_data(Vcb, t.new_address, buf, FALSE, Vcb->superblock.node_size, wtc, NULL);
+    Status = write_data(Vcb, t.new_address, buf, FALSE, Vcb->superblock.node_size, wtc, NULL, NULL);
     if (!NT_SUCCESS(Status)) {
         ERR("write_data returned %08x\n", Status);
         goto end;
@@ -243,7 +245,7 @@ static void flush_subvol_fcbs(root* subvol, LIST_ENTRY* rollback) {
     }
 }
 
-static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, UINT32 crc32, PANSI_STRING utf8, PUNICODE_STRING name) {
+static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, PANSI_STRING utf8, PUNICODE_STRING name, PIRP Irp) {
     LIST_ENTRY rollback;
     UINT64 id;
     NTSTATUS Status;
@@ -272,8 +274,6 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     
     InitializeListHead(&rollback);
     
-    ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
-    
     // flush open files on this subvol
     
     flush_subvol_fcbs(subvol, &rollback);
@@ -281,7 +281,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     // flush metadata
     
     if (Vcb->need_write)
-        do_write(Vcb, &rollback);
+        do_write(Vcb, Irp, &rollback);
     
     free_trees(Vcb);
     
@@ -292,10 +292,10 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     // create new root
     
     if (Vcb->root_root->lastinode == 0)
-        get_last_inode(Vcb, Vcb->root_root);
+        get_last_inode(Vcb, Vcb->root_root, Irp);
     
     id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
-    Status = create_root(Vcb, id, &r, TRUE, Vcb->superblock.generation, &rollback);
+    Status = create_root(Vcb, id, &r, TRUE, Vcb->superblock.generation, Irp, &rollback);
     
     if (!NT_SUCCESS(Status)) {
         ERR("create_root returned %08x\n", Status);
@@ -307,7 +307,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
         
         TRACE("uuid root doesn't exist, creating it\n");
         
-        Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, &rollback);
+        Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, Irp, &rollback);
         
         if (!NT_SUCCESS(Status)) {
             ERR("create_root returned %08x\n", Status);
@@ -333,12 +333,12 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
         searchkey.obj_type = TYPE_SUBVOL_UUID;
         RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
         
-        Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp);
     } while (NT_SUCCESS(Status) && !keycmp(&searchkey, &tp.item->key));
     
     *root_num = r->id;
     
-    if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, &rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, Irp, &rollback)) {
         ERR("insert_tree_item failed\n");
         ExFreePool(root_num);
         Status = STATUS_INTERNAL_ERROR;
@@ -349,13 +349,13 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     searchkey.obj_type = TYPE_ROOT_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto end;
     }
     
-    Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, tp.tree->flags, &address, &rollback);
+    Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, tp.tree->flags, &address, Irp, &rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("snapshot_tree_copy returned %08x\n", Status);
         goto end;
@@ -405,7 +405,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     searchkey.obj_type = 0;
     searchkey.offset = 0;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto end;
@@ -433,14 +433,14 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     
     RtlCopyMemory(fr->utf8.Buffer, utf8->Buffer, utf8->Length);
     
-    Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, fcb, &fr->fcb);
+    Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, fcb, &fr->fcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
         free_fileref(fr);
         goto end;
     }
     
-    Status = fcb_get_last_dir_index(fcb, &dirpos);
+    Status = fcb_get_last_dir_index(fcb, &dirpos, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("fcb_get_last_dir_index returned %08x\n", Status);
         free_fileref(fr);
@@ -514,7 +514,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
         le = le->Flink;
     }
     
-    do_write(Vcb, &rollback);
+    do_write(Vcb, Irp, &rollback);
     
     free_trees(Vcb);
     
@@ -526,12 +526,10 @@ end:
     else
         do_rollback(Vcb, &rollback);
 
-    ExReleaseResourceLite(&Vcb->tree_lock);
-    
     return Status;
 }
 
-static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length) {
+static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
     PFILE_OBJECT subvol_obj;
     NTSTATUS Status;
     btrfs_create_snapshot* bcs = data;
@@ -539,10 +537,9 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject,
     ANSI_STRING utf8;
     UNICODE_STRING nameus;
     ULONG len;
-    UINT32 crc32;
     fcb* fcb;
     ccb* ccb;
-    file_ref* fileref;
+    file_ref *fileref, *fr2;
     
     if (length < offsetof(btrfs_create_snapshot, name))
         return STATUS_INVALID_PARAMETER;
@@ -606,24 +603,29 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject,
         ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
         goto end2;
     }
+
+    ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+
+    // no need for fcb_lock as we have tree_lock exclusively
+    Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, FALSE, NULL, NULL, Irp);
     
-    crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, utf8.Length);
-    
-    Status = find_file_in_dir_with_crc32(Vcb, &nameus, crc32, fileref, NULL, NULL, NULL, NULL, NULL);
-        
     if (NT_SUCCESS(Status)) {
-        WARN("file already exists\n");
-        Status = STATUS_OBJECT_NAME_COLLISION;
-        goto end2;
+        if (!fr2->deleted) {
+            WARN("file already exists\n");
+            free_fileref(fr2);
+            Status = STATUS_OBJECT_NAME_COLLISION;
+            goto end3;
+        } else
+            free_fileref(fr2);
     } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
-        ERR("find_file_in_dir_with_crc32 returned %08x\n", Status);
-        goto end2;
+        ERR("open_fileref returned %08x\n", Status);
+        goto end3;
     }
     
     Status = ObReferenceObjectByHandle(bcs->subvol, 0, *IoFileObjectType, UserMode, (void**)&subvol_obj, NULL);
     if (!NT_SUCCESS(Status)) {
         ERR("ObReferenceObjectByHandle returned %08x\n", Status);
-        goto end2;
+        goto end3;
     }
     
     subvol_fcb = subvol_obj->FsContext;
@@ -651,12 +653,13 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject,
         goto end;
     }
     
-    Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, crc32, &utf8, &nameus);
+    Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, &utf8, &nameus, Irp);
     
     if (NT_SUCCESS(Status)) {
         file_ref* fr;
 
-        Status = open_fileref(Vcb, &fr, &nameus, fileref, FALSE, NULL);
+        Status = open_fileref(Vcb, &fr, &nameus, fileref, FALSE, NULL, NULL, Irp);
+        
         if (!NT_SUCCESS(Status)) {
             ERR("open_fileref returned %08x\n", Status);
             Status = STATUS_SUCCESS;
@@ -669,13 +672,16 @@ static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject,
 end:
     ObDereferenceObject(subvol_obj);
     
+end3:
+    ExReleaseResourceLite(&Vcb->tree_lock);
+    
 end2:
     ExFreePool(utf8.Buffer);
     
     return Status;
 }
 
-static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WCHAR* name, ULONG length) {
+static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WCHAR* name, ULONG length, PIRP Irp) {
     fcb *fcb, *rootfcb;
     ccb* ccb;
     file_ref* fileref;
@@ -689,7 +695,6 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC
     UNICODE_STRING nameus;
     ANSI_STRING utf8;
     UINT64 dirpos;
-    UINT32 crc32;
     INODE_REF* ir;
     KEY searchkey;
     traverse_ptr tp;
@@ -697,7 +702,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC
     PSID owner;
     BOOLEAN defaulted;
     UINT64* root_num;
-    file_ref* fr;
+    file_ref *fr = NULL, *fr2;
     
     fcb = FileObject->FsContext;
     if (!fcb) {
@@ -773,26 +778,29 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC
     
     InitializeListHead(&rollback);
     
-    crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8.Buffer, utf8.Length);
-    
-    Status = find_file_in_dir_with_crc32(fcb->Vcb, &nameus, crc32, fileref, NULL, NULL, NULL, NULL, NULL);
+    // no need for fcb_lock as we have tree_lock exclusively
+    Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, FALSE, NULL, NULL, Irp);
     
     if (NT_SUCCESS(Status)) {
-        WARN("file already exists\n");
-        Status = STATUS_OBJECT_NAME_COLLISION;
-        goto end;
+        if (!fr2->deleted) {
+            WARN("file already exists\n");
+            free_fileref(fr2);
+            Status = STATUS_OBJECT_NAME_COLLISION;
+            goto end;
+        } else
+            free_fileref(fr2);
     } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
-        ERR("find_file_in_dir_with_crc32 returned %08x\n", Status);
+        ERR("open_fileref returned %08x\n", Status);
         goto end;
     }
     
     if (Vcb->root_root->lastinode == 0)
-        get_last_inode(Vcb, Vcb->root_root);
+        get_last_inode(Vcb, Vcb->root_root, Irp);
     
     // FIXME - make sure rollback removes new roots from internal structures
     
     id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
-    Status = create_root(Vcb, id, &r, FALSE, 0, &rollback);
+    Status = create_root(Vcb, id, &r, FALSE, 0, Irp, &rollback);
     
     if (!NT_SUCCESS(Status)) {
         ERR("create_root returned %08x\n", Status);
@@ -806,7 +814,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC
         
         TRACE("uuid root doesn't exist, creating it\n");
         
-        Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, &rollback);
+        Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, Irp, &rollback);
         
         if (!NT_SUCCESS(Status)) {
             ERR("create_root returned %08x\n", Status);
@@ -832,12 +840,12 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC
         searchkey.obj_type = TYPE_SUBVOL_UUID;
         RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
         
-        Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp);
     } while (NT_SUCCESS(Status) && !keycmp(&searchkey, &tp.item->key));
     
     *root_num = r->id;
     
-    if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, &rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, Irp, &rollback)) {
         ERR("insert_tree_item failed\n");
         Status = STATUS_INTERNAL_ERROR;
         goto end;
@@ -859,7 +867,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC
     
     // add .. inode to new subvol
     
-    rootfcb = create_fcb();
+    rootfcb = create_fcb(PagedPool);
     if (!rootfcb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -879,7 +887,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC
     rootfcb->inode_item.st_atime = rootfcb->inode_item.st_ctime = rootfcb->inode_item.st_mtime = rootfcb->inode_item.otime = now;
     rootfcb->inode_item.st_gid = GID_NOBODY; // FIXME?
     
-    rootfcb->atts = get_file_attributes(Vcb, &rootfcb->inode_item, rootfcb->subvol, rootfcb->inode, rootfcb->type, FALSE, TRUE);
+    rootfcb->atts = get_file_attributes(Vcb, &rootfcb->inode_item, rootfcb->subvol, rootfcb->inode, rootfcb->type, FALSE, TRUE, Irp);
     
     SeCaptureSubjectContext(&subjcont);
     
@@ -901,13 +909,15 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC
         ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
         rootfcb->inode_item.st_uid = UID_NOBODY;
     } else {
-        rootfcb->inode_item.st_uid = sid_to_uid(&owner);
+        rootfcb->inode_item.st_uid = sid_to_uid(owner);
     }
     
     rootfcb->sd_dirty = TRUE;
 
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
     InsertTailList(&r->fcbs, &rootfcb->list_entry);
     InsertTailList(&Vcb->all_fcbs, &rootfcb->list_entry_all);
+    ExReleaseResourceLite(&Vcb->fcb_lock);
     
     rootfcb->Header.IsFastIoPossible = fast_io_possible(rootfcb);
     rootfcb->Header.AllocationSize.QuadPart = 0;
@@ -930,7 +940,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC
     ir->n = strlen(DOTDOT);
     RtlCopyMemory(ir->name, DOTDOT, ir->n);
 
-    if (!insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, &rollback)) {
+    if (!insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, Irp, &rollback)) {
         ERR("insert_tree_item failed\n");
         Status = STATUS_INTERNAL_ERROR;
         goto end;
@@ -941,7 +951,11 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC
     fr = create_fileref();
     if (!fr) {
         ERR("out of memory\n");
+        
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fcb(rootfcb);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+        
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end;
     }
@@ -950,10 +964,12 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC
     
     mark_fcb_dirty(rootfcb);
     
-    Status = fcb_get_last_dir_index(fcb, &dirpos);
+    Status = fcb_get_last_dir_index(fcb, &dirpos, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("fcb_get_last_dir_index returned %08x\n", Status);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(fr);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
         goto end;
     }
     
@@ -964,7 +980,9 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC
     fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fr->filepart.MaximumLength, ALLOC_TAG);
     if (!fr->filepart.Buffer) {
         ERR("out of memory\n");
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(fr);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end;
     }
@@ -974,7 +992,9 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WC
     Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE);
     if (!NT_SUCCESS(Status)) {
         ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(fr);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
         goto end;
     }
     
@@ -1022,8 +1042,204 @@ end:
     }
     
 end2:
-    if (fr)
+    if (fr) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(fr);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
+        
+    return Status;
+}
+
+static NTSTATUS get_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length) {
+    btrfs_inode_info* bii = data;
+    fcb* fcb;
+    ccb* ccb;
+    
+    if (length < sizeof(btrfs_inode_info))
+        return STATUS_BUFFER_OVERFLOW;
+    
+    if (!FileObject)
+        return STATUS_INVALID_PARAMETER;
+    
+    fcb = FileObject->FsContext;
+    
+    if (!fcb)
+        return STATUS_INVALID_PARAMETER;
+    
+    ccb = FileObject->FsContext2;
+    
+    if (!ccb)
+        return STATUS_INVALID_PARAMETER;
+    
+    if (!(ccb->access & FILE_READ_ATTRIBUTES)) {
+        WARN("insufficient privileges\n");
+        return STATUS_ACCESS_DENIED;
+    }
+    
+    ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
+    
+    bii->subvol = fcb->subvol->id;
+    bii->inode = fcb->inode;
+    bii->top = fcb->Vcb->root_fileref->fcb == fcb ? TRUE : FALSE;
+    bii->type = fcb->type;
+    bii->st_uid = fcb->inode_item.st_uid;
+    bii->st_gid = fcb->inode_item.st_gid;
+    bii->st_mode = fcb->inode_item.st_mode;
+    bii->st_rdev = fcb->inode_item.st_rdev;
+    bii->flags = fcb->inode_item.flags;
+    
+    bii->inline_length = 0;
+    bii->disk_size[0] = 0;
+    bii->disk_size[1] = 0;
+    bii->disk_size[2] = 0;
+    
+    if (fcb->type != BTRFS_TYPE_DIRECTORY) {
+        LIST_ENTRY* le;
+        
+        le = fcb->extents.Flink;
+        while (le != &fcb->extents) {
+            extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+            
+            if (!ext->ignore) {
+                if (ext->data->type == EXTENT_TYPE_INLINE) {
+                    bii->inline_length += ext->data->decoded_size;
+                } else {
+                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
+                    
+                    // FIXME - compressed extents with a hole in them are counted more than once
+                    if (ed2->size != 0) {
+                        if (ext->data->compression == BTRFS_COMPRESSION_NONE) {
+                            bii->disk_size[0] += ed2->num_bytes;
+                        } else if (ext->data->compression == BTRFS_COMPRESSION_ZLIB) {
+                            bii->disk_size[1] += ed2->size;
+                        } else if (ext->data->compression == BTRFS_COMPRESSION_LZO) {
+                            bii->disk_size[2] += ed2->size;
+                        }
+                    }
+                }
+            }
+            
+            le = le->Flink;
+        }
+    }
+    
+    ExReleaseResourceLite(fcb->Header.Resource);
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS set_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length) {
+    btrfs_set_inode_info* bsii = data;
+    NTSTATUS Status;
+    fcb* fcb;
+    ccb* ccb;
+    
+    if (length < sizeof(btrfs_set_inode_info))
+        return STATUS_BUFFER_OVERFLOW;
+    
+    if (!FileObject)
+        return STATUS_INVALID_PARAMETER;
+    
+    fcb = FileObject->FsContext;
+    
+    if (!fcb)
+        return STATUS_INVALID_PARAMETER;
+    
+    ccb = FileObject->FsContext2;
+    
+    if (!ccb)
+        return STATUS_INVALID_PARAMETER;
+    
+    if (bsii->flags_changed && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
+        WARN("insufficient privileges\n");
+        return STATUS_ACCESS_DENIED;
+    }
+    
+    if (bsii->mode_changed && !(ccb->access & WRITE_DAC)) {
+        WARN("insufficient privileges\n");
+        return STATUS_ACCESS_DENIED;
+    }
+    
+    if ((bsii->uid_changed || bsii->gid_changed) && !(ccb->access & WRITE_OWNER)) {
+        WARN("insufficient privileges\n");
+        return STATUS_ACCESS_DENIED;
+    }
+    
+    ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
+    
+    if (bsii->flags_changed) {
+        if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 &&
+            (bsii->flags & BTRFS_INODE_NODATACOW) != (fcb->inode_item.flags & BTRFS_INODE_NODATACOW)) {
+            WARN("trying to change nocow flag on non-empty file\n");
+            Status = STATUS_INVALID_PARAMETER;
+            goto end;
+        }
+        
+        fcb->inode_item.flags = bsii->flags;
+        
+        if (fcb->inode_item.flags & BTRFS_INODE_NODATACOW)
+            fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
+        else 
+            fcb->inode_item.flags &= ~(UINT64)BTRFS_INODE_NODATASUM;
+    }
+    
+    if (bsii->mode_changed) {
+        UINT32 allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
+        
+        fcb->inode_item.st_mode &= ~allowed;
+        fcb->inode_item.st_mode |= bsii->st_mode & allowed;
+    }
+    
+    if (bsii->uid_changed) {
+        PSID sid; 
+        SECURITY_INFORMATION secinfo;
+        SECURITY_DESCRIPTOR sd;
+        void* oldsd;
+        
+        fcb->inode_item.st_uid = bsii->st_uid;
+        
+        uid_to_sid(bsii->st_uid, &sid);
+        
+        Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
+        if (!NT_SUCCESS(Status)) {
+            ERR("RtlCreateSecurityDescriptor returned %08x\n", Status);
+            goto end;
+        }
+        
+        Status = RtlSetOwnerSecurityDescriptor(&sd, sid, FALSE);
+        if (!NT_SUCCESS(Status)) {
+            ERR("RtlSetOwnerSecurityDescriptor returned %08x\n", Status);
+            goto end;
+        }
+        
+        oldsd = fcb->sd;
+        
+        secinfo = OWNER_SECURITY_INFORMATION;
+        Status = SeSetSecurityDescriptorInfoEx(NULL, &secinfo, &sd, (void**)&fcb->sd, SEF_AVOID_PRIVILEGE_CHECK, PagedPool, IoGetFileObjectGenericMapping());
+        
+        if (!NT_SUCCESS(Status)) {
+            ERR("SeSetSecurityDescriptorInfo returned %08x\n", Status);
+            goto end;
+        }
+        
+        ExFreePool(oldsd);
+        
+        fcb->sd_dirty = TRUE;
+        
+        send_notification_fcb(ccb->fileref, FILE_NOTIFY_CHANGE_SECURITY, FILE_ACTION_MODIFIED);
+    }
+    
+    if (bsii->gid_changed)
+        fcb->inode_item.st_gid = bsii->st_gid;
+    
+    if (bsii->flags_changed || bsii->mode_changed || bsii->uid_changed || bsii->gid_changed)
+        mark_fcb_dirty(fcb);
+    
+    Status = STATUS_SUCCESS;
+    
+end:
+    ExReleaseResourceLite(fcb->Header.Resource);
     
     return Status;
 }
@@ -1084,7 +1300,7 @@ static NTSTATUS fs_get_statistics(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT File
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS set_sparse(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length) {
+static NTSTATUS set_sparse(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) {
     FILE_SET_SPARSE_BUFFER* fssb = data;
     NTSTATUS Status;
     BOOL set;
@@ -1146,7 +1362,7 @@ static NTSTATUS set_sparse(device_extension* Vcb, PFILE_OBJECT FileObject, void*
         fcb->atts_changed = TRUE;
         
         defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type,
-                                    fileref && fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE);
+                                    fileref && fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE, Irp);
         
         fcb->atts_deleted = defda == fcb->atts;
     }
@@ -1164,173 +1380,58 @@ end:
 }
 
 static NTSTATUS zero_data(device_extension* Vcb, fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
-    LIST_ENTRY* le;
+    NTSTATUS Status;
+    BOOL compress = write_fcb_compressed(fcb);
+    UINT64 start_data, end_data;
+    UINT8* data;
+        
+    if (compress) {
+        start_data = start & ~(UINT64)(COMPRESSED_EXTENT_SIZE - 1);
+        end_data = min(sector_align(start + length, COMPRESSED_EXTENT_SIZE),
+                       sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size));
+    } else {
+        start_data = start & ~(UINT64)(fcb->Vcb->superblock.sector_size - 1);
+        end_data = sector_align(start + length, fcb->Vcb->superblock.sector_size);
+    }
+
+    data = ExAllocatePoolWithTag(PagedPool, end_data - start_data, ALLOC_TAG);
+    if (!data) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
     
-    le = fcb->extents.Flink;
+    RtlZeroMemory(data, end_data - start_data);
     
-    while (le != &fcb->extents) {
-        LIST_ENTRY* le2 = le->Flink;
-        extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+    if (start > start_data || start + length < end_data) {
+        Status = read_file(fcb, data, start_data, end_data - start_data, NULL, Irp);
         
-        if (!ext->ignore) {
-            EXTENT_DATA* ed = ext->data;
-            EXTENT_DATA2* ed2;
-            UINT64 len;
-            
-            if (ext->datalen < sizeof(EXTENT_DATA)) {
-                ERR("extent at %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
-                return STATUS_INTERNAL_ERROR;
-            }
-            
-            if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
-                if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
-                    ERR("extent at %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
-                    return STATUS_INTERNAL_ERROR;
-                }
-                
-                ed2 = (EXTENT_DATA2*)ed->data;
-            }
-            
-            len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
-            
-            if (ext->offset < start + length && ext->offset + len >= start) {
-                if (ed->compression != BTRFS_COMPRESSION_NONE) {
-                    FIXME("FIXME - compression not supported at present\n");
-                    return STATUS_NOT_SUPPORTED;
-                }
-                
-                if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
-                    WARN("encryption not supported (type %x)\n", ext->offset, ed->encryption);
-                    return STATUS_NOT_SUPPORTED;
-                }
-                
-                if (ed->encoding != BTRFS_ENCODING_NONE) {
-                    WARN("other encodings not supported\n");
-                    return STATUS_NOT_SUPPORTED;
-                }
-                
-                // We can ignore prealloc and sparse extents - they're already counted as zeroed
-                
-                if (ed->type == EXTENT_TYPE_INLINE) {
-                    extent* ext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
-                    EXTENT_DATA* data;
-                    UINT64 s2, e2;
-                    
-                    if (!ext2) {
-                        ERR("out of memory\n");
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    data = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
-                    
-                    if (!data) {
-                        ERR("out of memory\n");
-                        ExFreePool(ext2);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    RtlCopyMemory(data, ext->data, ext->datalen);
-                    
-                    s2 = max(ext->offset, start);
-                    e2 = min(ext->offset + ed->decoded_size, start + length);
-                    RtlZeroMemory((UINT8*)data + sizeof(EXTENT_DATA) - 1 + s2 - ext->offset, e2 - s2);
-                    
-                    ext2->offset = ext->offset;
-                    ext2->data = data;
-                    ext2->datalen = ext->datalen;
-                    ext2->unique = ext->unique;
-                    ext2->ignore = FALSE;
-                    
-                    InsertHeadList(&ext->list_entry, &ext2->list_entry);
-                    remove_fcb_extent(ext, rollback);
-                } else if (ed->type == EXTENT_TYPE_REGULAR && ed2->size != 0) {
-                    NTSTATUS Status;
-                    BOOL nocow = fcb->inode_item.flags & BTRFS_INODE_NODATACOW && ext->unique;
-                    UINT64 s1 = max(ext->offset, start);
-                    UINT64 e1 = min(ext->offset + len, start + length);
-                    UINT64 s2 = (s1 / Vcb->superblock.sector_size) * Vcb->superblock.sector_size;
-                    UINT64 e2 = sector_align(e1, Vcb->superblock.sector_size);
-                    UINT8* data;
-                    
-                    data = ExAllocatePoolWithTag(PagedPool, e2 - s2, ALLOC_TAG);
-                    if (!data) {
-                        ERR("out of memory\n");
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    Status = read_file(fcb, data, s2, e2 - s2, NULL, Irp);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("read_file returned %08x\n", Status);
-                        ExFreePool(data);
-                        return Status;
-                    }
-                    
-                    RtlZeroMemory(data + s1 - s2, e1 - s1);
-                    
-                    if (nocow) {
-                        UINT64 writeaddr = ed2->address + ed2->offset + s2 - ext->offset;
-
-                        Status = write_data_complete(Vcb, writeaddr, data, e2 - s2, Irp);
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("write_data_complete returned %08x\n", Status);
-                            ExFreePool(data);
-                            return Status;
-                        }
-                        
-                        if (changed_sector_list) {
-                            unsigned int i;
-                            changed_sector* sc;
-                            
-                            sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
-                            if (!sc) {
-                                ERR("out of memory\n");
-                                ExFreePool(data);
-                                return STATUS_INSUFFICIENT_RESOURCES;
-                            }
-                            
-                            sc->ol.key = writeaddr;
-                            sc->length = (e2 - s2) / Vcb->superblock.sector_size;
-                            sc->deleted = FALSE;
-                            
-                            sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
-                            if (!sc->checksums) {
-                                ERR("out of memory\n");
-                                ExFreePool(sc);
-                                ExFreePool(data);
-                                return STATUS_INSUFFICIENT_RESOURCES;
-                            }
-                            
-                            for (i = 0; i < sc->length; i++) {
-                                sc->checksums[i] = ~calc_crc32c(0xffffffff, data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
-                            }
-
-                            insert_into_ordered_list(changed_sector_list, &sc->ol);
-                        }
-                        
-                        ExFreePool(data);
-                    } else {
-                        Status = excise_extents(Vcb, fcb, s2, e2, rollback);
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("excise_extents returned %08x\n", Status);
-                            ExFreePool(data);
-                            return Status;
-                        }
-                        
-                        Status = insert_extent(Vcb, fcb, s2, e2 - s2, data, changed_sector_list, Irp, rollback);
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("insert_extent returned %08x\n", Status);
-                            ExFreePool(data);
-                            return Status;
-                        }
-                        
-                        ExFreePool(data);
-                    }
-                }
-            } else if (ext->offset >= start + length)
-                return STATUS_SUCCESS;
+        if (!NT_SUCCESS(Status)) {
+            ERR("read_file returned %08x\n", Status);
+            ExFreePool(data);
+            return Status;
+        }
+    }
+    
+    RtlZeroMemory(data + start - start_data, length);
+    
+    if (compress) {
+        Status = write_compressed(fcb, start_data, end_data, data, changed_sector_list, Irp, rollback);
+        
+        ExFreePool(data);
+        
+        if (!NT_SUCCESS(Status)) {
+            ERR("write_compressed returned %08x\n", Status);
+            return Status;
         }
+    } else {
+        Status = do_write_file(fcb, start_data, end_data, data, changed_sector_list, Irp, rollback);
         
-        le = le2;
+        ExFreePool(data);
+        
+        if (!NT_SUCCESS(Status)) {
+            ERR("do_write_file returned %08x\n", Status);
+            return Status;
+        }
     }
     
     return STATUS_SUCCESS;
@@ -1350,8 +1451,6 @@ static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, vo
     BOOL nocsum;
     IO_STATUS_BLOCK iosb;
     
-    // FIXME - check permissions
-    
     if (!data || length < sizeof(FILE_ZERO_DATA_INFORMATION))
         return STATUS_INVALID_PARAMETER;
     
@@ -1373,7 +1472,18 @@ static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, vo
     }
     
     ccb = FileObject->FsContext2;
-    fileref = ccb ? ccb->fileref : NULL;
+    
+    if (!ccb) {
+        ERR("ccb was NULL\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+    
+    if (!(ccb->access & FILE_WRITE_DATA)) {
+        WARN("insufficient privileges\n");
+        return STATUS_ACCESS_DENIED;
+    }
+    
+    fileref = ccb->fileref;
     
     if (!fileref) {
         ERR("fileref was NULL\n");
@@ -1457,7 +1567,7 @@ static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, vo
             }
             
             if (end < fzdi->BeyondFinalZero.QuadPart) {
-                Status = zero_data(Vcb, fcb, fzdi->BeyondFinalZero.QuadPart, fzdi->BeyondFinalZero.QuadPart - end, nocsum ? NULL : &changed_sector_list, Irp, &rollback);
+                Status = zero_data(Vcb, fcb, end, fzdi->BeyondFinalZero.QuadPart - end, nocsum ? NULL : &changed_sector_list, Irp, &rollback);
                 if (!NT_SUCCESS(Status)) {
                     ERR("zero_data returned %08x\n", Status);
                     goto end;
@@ -1465,17 +1575,11 @@ static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, vo
             }
             
             if (end > start) {
-                Status = excise_extents(Vcb, fcb, start, end, &rollback);
+                Status = excise_extents(Vcb, fcb, start, end, Irp, &rollback);
                 if (!NT_SUCCESS(Status)) {
                     ERR("excise_extents returned %08x\n", Status);
                     goto end;
                 }
-                
-                Status = insert_sparse_extent(fcb, start, end - start, &rollback);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("insert_sparse_extent returned %08x\n", Status);
-                    goto end;
-                }
             }
         }
     }
@@ -1524,8 +1628,7 @@ static NTSTATUS query_ranges(device_extension* Vcb, PFILE_OBJECT FileObject, FIL
     LIST_ENTRY* le;
     FILE_ALLOCATED_RANGE_BUFFER* ranges = outbuf;
     ULONG i = 0;
-    BOOL sparse, finish_off;
-    UINT64 nonsparse_run_start;
+    UINT64 last_start, last_end;
     
     TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n");
     
@@ -1566,51 +1669,41 @@ static NTSTATUS query_ranges(device_extension* Vcb, PFILE_OBJECT FileObject, FIL
     
     le = fcb->extents.Flink;
     
-    sparse = FALSE;
-    nonsparse_run_start = 0xffffffffffffffff;
-    finish_off = TRUE;
+    last_start = 0;
+    last_end = 0;
     
     while (le != &fcb->extents) {
         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
         
         if (!ext->ignore) {
-            EXTENT_DATA2* ed2 = ext->data->type == EXTENT_TYPE_REGULAR ? (EXTENT_DATA2*)ext->data->data : NULL;
+            EXTENT_DATA2* ed2 = (ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ext->data->data : NULL;
+            UINT64 len = ed2 ? ed2->num_bytes : ext->data->decoded_size;
             
-            if (ed2 && ed2->size == 0) { // sparse
-                if (!sparse) {
-                    if (nonsparse_run_start < ext->offset) {
-                        if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
-                            ranges[i].FileOffset.QuadPart = nonsparse_run_start;
-                            ranges[i].Length.QuadPart = ext->offset - nonsparse_run_start;
-                            i++;
-                        } else {
-                            Status = STATUS_BUFFER_TOO_SMALL;
-                            goto end;
-                        }
-                    }
-                    
-                    if (ext->offset > inbuf->FileOffset.QuadPart + inbuf->Length.QuadPart) {
-                        finish_off = FALSE;
-                        break;
+            if (ext->offset > last_end) { // first extent after a hole
+                if (last_end > last_start) {
+                    if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
+                        ranges[i].FileOffset.QuadPart = last_start;
+                        ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
+                        i++;
+                    } else {
+                        Status = STATUS_BUFFER_TOO_SMALL;
+                        goto end;
                     }
-                    
-                    sparse = TRUE;
-                }
-            } else { // not sparse
-                if (sparse) {
-                    sparse = FALSE;
-                    nonsparse_run_start = ext->offset;
                 }
+                
+                last_start = ext->offset;
             }
+            
+            last_end = ext->offset + len;
         }
         
         le = le->Flink;
     }
     
-    if (finish_off && nonsparse_run_start < fcb->inode_item.st_size) {
+    if (last_end > last_start) {
         if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) {
-            ranges[i].FileOffset.QuadPart = nonsparse_run_start;
-            ranges[i].Length.QuadPart = fcb->inode_item.st_size - nonsparse_run_start;
+            ranges[i].FileOffset.QuadPart = last_start;
+            ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start;
             i++;
         } else {
             Status = STATUS_BUFFER_TOO_SMALL;
@@ -1710,8 +1803,8 @@ static NTSTATUS lock_volume(device_extension* Vcb, PIRP Irp) {
     
     flush_fcb_caches(Vcb);
     
-    if (Vcb->need_write)
-        do_write(Vcb, &rollback);
+    if (Vcb->need_write && !Vcb->readonly)
+        do_write(Vcb, Irp, &rollback);
     
     free_trees(Vcb);
     
@@ -1817,7 +1910,7 @@ static NTSTATUS invalidate_volumes(PIRP Irp) {
     while (le != &VcbList) {
         device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
         
-        if (Vcb->Vpb->RealDevice == devobj) {
+        if (Vcb->Vpb && Vcb->Vpb->RealDevice == devobj) {
             if (Vcb->Vpb == devobj->Vpb) {
                 KIRQL irql;
                 PVPB newvpb;
@@ -1851,8 +1944,8 @@ static NTSTATUS invalidate_volumes(PIRP Irp) {
                 
                 flush_fcb_caches(Vcb);
                 
-                if (Vcb->need_write)
-                    do_write(Vcb, &rollback);
+                if (Vcb->need_write && !Vcb->readonly)
+                    do_write(Vcb, Irp, &rollback);
                 
                 free_trees(Vcb);
                 
@@ -1896,6 +1989,33 @@ end:
     return Status;
 }
 
+static NTSTATUS is_volume_dirty(device_extension* Vcb, PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    ULONG* volstate;
+
+    if (Irp->AssociatedIrp.SystemBuffer) {
+        volstate = Irp->AssociatedIrp.SystemBuffer;
+    } else if (Irp->MdlAddress != NULL) {
+        volstate = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
+
+        if (!volstate)
+            return STATUS_INSUFFICIENT_RESOURCES;
+    } else
+        return STATUS_INVALID_USER_BUFFER;
+
+    if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG))
+        return STATUS_INVALID_PARAMETER;
+
+    *volstate = 0;
+    
+    if (IrpSp->FileObject->FsContext != Vcb->volume_fcb)
+        return STATUS_INVALID_PARAMETER;
+
+    Irp->IoStatus.Information = sizeof(ULONG);
+
+    return STATUS_SUCCESS;
+}
+
 NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user) {
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     NTSTATUS Status;
@@ -2028,8 +2148,7 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL
             break;
 
         case FSCTL_IS_VOLUME_DIRTY:
-            WARN("STUB: FSCTL_IS_VOLUME_DIRTY\n");
-            Status = STATUS_NOT_IMPLEMENTED;
+            Status = is_volume_dirty(DeviceObject->DeviceExtension, Irp);
             break;
 
         case FSCTL_ALLOW_EXTENDED_DASD_IO:
@@ -2097,7 +2216,7 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL
 
         case FSCTL_SET_SPARSE:
             Status = set_sparse(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
-                                IrpSp->Parameters.FileSystemControl.InputBufferLength);
+                                IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp);
             break;
 
         case FSCTL_SET_ZERO_DATA:
@@ -2372,11 +2491,19 @@ NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL
             break;
             
         case FSCTL_BTRFS_CREATE_SUBVOL:
-            Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
+            Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp);
             break;
             
         case FSCTL_BTRFS_CREATE_SNAPSHOT:
-            Status = create_snapshot(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
+            Status = create_snapshot(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp);
+            break;
+            
+        case FSCTL_BTRFS_GET_INODE_INFO:
+            Status = get_inode_info(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
+            break;
+            
+        case FSCTL_BTRFS_SET_INODE_INFO:
+            Status = set_inode_info(IrpSp->FileObject, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
             break;
 
         default:
index c63f097..c84f499 100644 (file)
@@ -1,3 +1,20 @@
+/* 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"
 
 struct pnp_context;
@@ -176,8 +193,8 @@ static NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     
     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
 
-    if (Vcb->need_write)
-        do_write(Vcb, &rollback);
+    if (Vcb->need_write && !Vcb->readonly)
+        do_write(Vcb, Irp, &rollback);
     
     clear_rollback(&rollback);
 
@@ -219,7 +236,14 @@ static NTSTATUS pnp_start_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
 }
 
 static NTSTATUS pnp_surprise_removal(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
-    FIXME("STUB\n");
+    device_extension* Vcb = DeviceObject->DeviceExtension;
+    
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+    
+    if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
+        uninit(Vcb, FALSE);
+        Vcb->Vpb->Flags &= ~VPB_MOUNTED;
+    }
 
     return STATUS_SUCCESS;
 }
index 3d50d0a..869f97a 100644 (file)
@@ -1,3 +1,20 @@
+/* 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"
 
 enum read_data_status {
@@ -159,6 +176,12 @@ NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UI
     UINT64 *stripestart = NULL, *stripeend = NULL;
     UINT16 startoffstripe;
     
+    Status = verify_vcb(Vcb, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("verify_vcb returned %08x\n", Status);
+        return Status;
+    }
+    
     if (Vcb->log_to_phys_loaded) {
         chunk* c = get_chunk_from_address(Vcb, addr);
         
@@ -433,7 +456,23 @@ NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UI
     
     for (i = 0; i < ci->num_stripes; i++) {
         if (context->stripes[i].status == ReadDataStatus_Error && IoIsErrorUserInduced(context->stripes[i].iosb.Status)) {
-            IoSetHardErrorOrVerifyDevice(context->stripes[i].Irp, devices[i]->devobj);
+            if (Irp && context->stripes[i].iosb.Status == STATUS_VERIFY_REQUIRED) {
+                PDEVICE_OBJECT dev;
+                
+                dev = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
+                IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL);
+                
+                if (!dev) {
+                    dev = IoGetDeviceToVerify(PsGetCurrentThread());
+                    IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
+                }
+                
+                dev = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
+                
+                if (dev)
+                    IoVerifyVolume(dev, FALSE);
+            }
+//             IoSetHardErrorOrVerifyDevice(context->stripes[i].Irp, devices[i]->devobj);
             
             Status = context->stripes[i].iosb.Status;
             goto exit;
@@ -702,7 +741,7 @@ static NTSTATUS STDCALL read_stream(fcb* fcb, UINT8* data, UINT64 start, ULONG l
     return Status;
 }
 
-static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length) {
+static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length, PIRP Irp) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp, next_tp;
@@ -713,7 +752,7 @@ static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64
     searchkey.obj_type = TYPE_EXTENT_CSUM;
     searchkey.offset = start;
     
-    Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -742,7 +781,7 @@ static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64
                 break;
         }
         
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         
         if (b)
             tp = next_tp;
@@ -756,7 +795,7 @@ static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS load_csum(device_extension* Vcb, UINT64 start, UINT64 length, UINT32** pcsum) {
+static NTSTATUS load_csum(device_extension* Vcb, UINT64 start, UINT64 length, UINT32** pcsum, PIRP Irp) {
     UINT32* csum = NULL;
     NTSTATUS Status;
     UINT64 end;
@@ -827,7 +866,7 @@ static NTSTATUS load_csum(device_extension* Vcb, UINT64 start, UINT64 length, UI
     runlength = RtlFindFirstRunClear(&bmp, &index);
             
     while (runlength != 0) {
-        Status = load_csum_from_disk(Vcb, &csum[index], start + (index * Vcb->superblock.sector_size), runlength);
+        Status = load_csum_from_disk(Vcb, &csum[index], start + (index * Vcb->superblock.sector_size), runlength, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("load_csum_from_disk returned %08x\n", Status);
             goto end;
@@ -854,72 +893,23 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U
     NTSTATUS Status;
     EXTENT_DATA* ed;
     UINT64 bytes_read = 0;
+    UINT64 last_end;
     LIST_ENTRY* le;
     
     TRACE("(%p, %p, %llx, %llx, %p)\n", fcb, data, start, length, pbr);
     
     if (pbr)
         *pbr = 0;
-
-    le = fcb->extents.Flink;
     
-    while (le != &fcb->extents) {
-        extent* ext = CONTAINING_RECORD(le, extent, list_entry);
-        
-        if (!ext->ignore) {
-            if (ext->offset == start)
-                break;
-            else if (ext->offset > start) {
-                LIST_ENTRY* le2 = le->Blink;
-                
-                ext = NULL;
-                
-                while (le2 != &fcb->extents) {
-                    extent* ext2 = CONTAINING_RECORD(le2, extent, list_entry);
-                    
-                    if (!ext2->ignore) {
-                        le = le2;
-                        ext = ext2;
-                        break;
-                    }
-                    
-                    le2 = le2->Blink;
-                }
-                
-                if (!ext) {
-                    ERR("first extent was after offset\n");
-                    Status = STATUS_INTERNAL_ERROR;
-                    goto exit;
-                } else
-                    break;
-            }
-        }
-        
-        le = le->Flink;
+    if (start >= fcb->inode_item.st_size) {
+        WARN("Tried to read beyond end of file\n");
+        Status = STATUS_END_OF_FILE;
+        goto exit;        
     }
-    
-    if (le == &fcb->extents) {
-        LIST_ENTRY* le2 = le->Blink;
-        extent* ext = NULL;
 
-        while (le2 != &fcb->extents) {
-            extent* ext2 = CONTAINING_RECORD(le2, extent, list_entry);
-            
-            if (!ext2->ignore) {
-                le = le2;
-                ext = ext2;
-                break;
-            }
-            
-            le2 = le2->Blink;
-        }
-        
-        if (!ext) {
-            ERR("could not find extent\n");
-            Status = STATUS_INTERNAL_ERROR;
-            goto exit;
-        }
-    }
+    le = fcb->extents.Flink;
+
+    last_end = start;
 
     while (le != &fcb->extents) {
         UINT64 len;
@@ -929,34 +919,26 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U
         if (!ext->ignore) {
             ed = ext->data;
             
-            if (ext->datalen < sizeof(EXTENT_DATA)) {
-                ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
-                Status = STATUS_INTERNAL_ERROR;
-                goto exit;
-            }
-            
-            if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
-                ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
-                Status = STATUS_INTERNAL_ERROR;
-                goto exit;
-            }
-            
-            ed2 = (EXTENT_DATA2*)ed->data;
+            ed2 = (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ed->data : NULL;
             
-            len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
+            len = ed2 ? ed2->num_bytes : ed->decoded_size;
             
-            if (ext->offset + len < start) {
-                ERR("Tried to read beyond end of file\n");
-                Status = STATUS_END_OF_FILE;
-                goto exit;
+            if (ext->offset + len <= start) {
+                last_end = ext->offset + len;
+                goto nextitem;
             }
             
-            if (ed->compression != BTRFS_COMPRESSION_NONE) {
-                FIXME("FIXME - compression not yet supported\n");
-                Status = STATUS_NOT_IMPLEMENTED;
-                goto exit;
+            if (ext->offset > last_end && ext->offset > start + bytes_read) {
+                UINT32 read = min(length, ext->offset - max(start, last_end));
+                
+                RtlZeroMemory(data + bytes_read, read);
+                bytes_read += read;
+                length -= read;
             }
             
+            if (length == 0 || ext->offset > start + bytes_read + length)
+                break;
+            
             if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
                 WARN("Encryption not supported\n");
                 Status = STATUS_NOT_IMPLEMENTED;
@@ -979,6 +961,8 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U
                     
                     RtlCopyMemory(data + bytes_read, &ed->data[off], read);
                     
+                    // FIXME - can we have compressed inline extents?
+                    
                     bytes_read += read;
                     length -= read;
                     break;
@@ -989,16 +973,13 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U
                     UINT64 off = start + bytes_read - ext->offset;
                     UINT32 to_read, read;
                     UINT8* buf;
+                    UINT32 *csum, bumpoff = 0;
+                    UINT64 addr;
                     
                     read = len - off;
                     if (read > length) read = length;
                     
-                    if (ed2->address == 0) {
-                        RtlZeroMemory(data + bytes_read, read);
-                    } else {
-                        UINT32 *csum, bumpoff = 0;
-                        UINT64 addr;
-                        
+                    if (ed->compression == BTRFS_COMPRESSION_NONE) {
                         addr = ed2->address + ed2->offset + off;
                         to_read = sector_align(read, fcb->Vcb->superblock.sector_size);
                         
@@ -1007,41 +988,71 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U
                             addr -= bumpoff;
                             to_read = sector_align(read + bumpoff, fcb->Vcb->superblock.sector_size);
                         }
+                    } else {
+                        addr = ed2->address;
+                        to_read = sector_align(ed2->size, fcb->Vcb->superblock.sector_size);
+                    }
+                    
+                    buf = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG);
+                    
+                    if (!buf) {
+                        ERR("out of memory\n");
+                        Status = STATUS_INSUFFICIENT_RESOURCES;
+                        goto exit;
+                    }
+                    
+                    if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
+                        Status = load_csum(fcb->Vcb, addr, to_read / fcb->Vcb->superblock.sector_size, &csum, Irp);
                         
-                        buf = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("load_csum returned %08x\n", Status);
+                            ExFreePool(buf);
+                            goto exit;
+                        }
+                    } else
+                        csum = NULL;
+                    
+                    Status = read_data(fcb->Vcb, addr, to_read, csum, FALSE, buf, NULL, Irp);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("read_data returned %08x\n", Status);
+                        ExFreePool(buf);
+                        goto exit;
+                    }
+                    
+                    if (ed->compression == BTRFS_COMPRESSION_NONE) {
+                        RtlCopyMemory(data + bytes_read, buf + bumpoff, read);
+                    } else {
+                        UINT8* decomp = NULL;
+                        
+                        // FIXME - don't mess around with decomp if we're reading the whole extent
                         
-                        if (!buf) {
+                        decomp = ExAllocatePoolWithTag(PagedPool, ed->decoded_size, ALLOC_TAG);
+                        if (!decomp) {
                             ERR("out of memory\n");
+                            ExFreePool(buf);
                             Status = STATUS_INSUFFICIENT_RESOURCES;
                             goto exit;
                         }
                         
-                        if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
-                            Status = load_csum(fcb->Vcb, addr, to_read / fcb->Vcb->superblock.sector_size, &csum);
-                            
-                            if (!NT_SUCCESS(Status)) {
-                                ERR("load_csum returned %08x\n", Status);
-                                ExFreePool(buf);
-                                goto exit;
-                            }
-                        } else
-                            csum = NULL;
+                        Status = decompress(ed->compression, buf, ed2->size, decomp, ed->decoded_size);
                         
-                        Status = read_data(fcb->Vcb, addr, to_read, csum, FALSE, buf, NULL, Irp);
                         if (!NT_SUCCESS(Status)) {
-                            ERR("read_data returned %08x\n", Status);
+                            ERR("decompress returned %08x\n", Status);
                             ExFreePool(buf);
+                            ExFreePool(decomp);
                             goto exit;
                         }
                         
-                        RtlCopyMemory(data + bytes_read, buf + bumpoff, read);
+                        RtlCopyMemory(data + bytes_read, decomp + ed2->offset + off, min(read, ed2->num_bytes - off));
                         
-                        ExFreePool(buf);
-                        
-                        if (csum)
-                            ExFreePool(csum);
+                        ExFreePool(decomp);
                     }
                     
+                    ExFreePool(buf);
+                    
+                    if (csum)
+                        ExFreePool(csum);
+                    
                     bytes_read += read;
                     length -= read;
                     
@@ -1068,14 +1079,26 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U
                     Status = STATUS_NOT_IMPLEMENTED;
                     goto exit;
             }
+
+            last_end = ext->offset + len;
             
             if (length == 0)
                 break;
         }
 
+nextitem:
         le = le->Flink;
     }
     
+    if (length > 0 && start + bytes_read < fcb->inode_item.st_size) {
+        UINT32 read = min(fcb->inode_item.st_size - start - bytes_read, length);
+        
+        RtlZeroMemory(data + bytes_read, read);
+        
+        bytes_read += read;
+        length -= read;
+    }
+    
     Status = STATUS_SUCCESS;
     if (pbr)
         *pbr = bytes_read;
@@ -1131,7 +1154,7 @@ NTSTATUS do_read(PIRP Irp, BOOL wait, ULONG* bytes_read) {
         }
         
         if (start >= fcb->Header.ValidDataLength.QuadPart) {
-            length = min(start + length, fcb->Header.FileSize.QuadPart) - fcb->Header.ValidDataLength.QuadPart;
+            length = min(length, min(start + length, fcb->Header.FileSize.QuadPart) - fcb->Header.ValidDataLength.QuadPart);
             RtlZeroMemory(data, length);
             Irp->IoStatus.Information = *bytes_read = length;
             return STATUS_SUCCESS;
@@ -1233,7 +1256,7 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     BOOL top_level;
     fcb* fcb;
     ccb* ccb;
-    BOOL tree_lock = FALSE, fcb_lock = FALSE;
+    BOOL tree_lock = FALSE, fcb_lock = FALSE, pagefile;
     
     FsRtlEnterFileSystem();
     
@@ -1266,6 +1289,12 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         goto exit;
     }
     
+    if (fcb == Vcb->volume_fcb) {
+        TRACE("not allowing read of volume FCB\n");
+        Status = STATUS_INVALID_PARAMETER;
+        goto exit;
+    }
+    
     ccb = FileObject->FsContext2;
     
     if (!ccb) {
@@ -1280,14 +1309,18 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         goto exit;
     }
     
+    pagefile = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE && Irp->Flags & IRP_PAGING_IO;
+    
     if (Irp->Flags & IRP_NOCACHE) {
-        if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, IoIsOperationSynchronous(Irp))) {
-            Status = STATUS_PENDING;
-            IoMarkIrpPending(Irp);
-            goto exit;
+        if (!pagefile) {
+            if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, IoIsOperationSynchronous(Irp))) {
+                Status = STATUS_PENDING;
+                IoMarkIrpPending(Irp);
+                goto exit;
+            }
+            
+            tree_lock = TRUE;
         }
-        
-        tree_lock = TRUE;
     
         if (!ExAcquireResourceSharedLite(fcb->Header.Resource, IoIsOperationSynchronous(Irp))) {
             Status = STATUS_PENDING;
index a959c70..1719690 100644 (file)
@@ -1,20 +1,48 @@
+/* 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"
 
 extern UNICODE_STRING log_device, log_file, registry_path;
 
 static WCHAR option_mounted[] = L"Mounted";
-static WCHAR option_ignore[] = L"Ignore";
 
 #define hex_digit(c) ((c) >= 0 && (c) <= 9) ? ((c) + '0') : ((c) - 10 + 'a')
 
-NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options) {
-    UNICODE_STRING path, ignoreus;
+NTSTATUS registry_load_volume_options(device_extension* Vcb) {
+    BTRFS_UUID* uuid = &Vcb->superblock.uuid;
+    mount_options* options = &Vcb->options;
+    UNICODE_STRING path, ignoreus, compressus, compressforceus, compresstypeus, readonlyus, zliblevelus, flushintervalus,
+                   maxinlineus, subvolidus;
     OBJECT_ATTRIBUTES oa;
     NTSTATUS Status;
     ULONG i, j, kvfilen, index, retlen;
     KEY_VALUE_FULL_INFORMATION* kvfi = NULL;
     HANDLE h;
     
+    options->compress = mount_compress;
+    options->compress_force = mount_compress_force;
+    options->compress_type = mount_compress_type > BTRFS_COMPRESSION_LZO ? 0 : mount_compress_type;
+    options->readonly = FALSE;
+    options->zlib_level = mount_zlib_level;
+    options->flush_interval = mount_flush_interval;
+    options->max_inline = min(mount_max_inline, Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - sizeof(EXTENT_DATA) + 1);
+    options->subvol_id = 0;
+    
     path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
     path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
     
@@ -62,8 +90,15 @@ NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options)
     
     index = 0;
     
-    ignoreus.Buffer = option_ignore;
-    ignoreus.Length = ignoreus.MaximumLength = wcslen(option_ignore) * sizeof(WCHAR);
+    RtlInitUnicodeString(&ignoreus, L"Ignore");
+    RtlInitUnicodeString(&compressus, L"Compress");
+    RtlInitUnicodeString(&compressforceus, L"CompressForce");
+    RtlInitUnicodeString(&compresstypeus, L"CompressType");
+    RtlInitUnicodeString(&readonlyus, L"Readonly");
+    RtlInitUnicodeString(&zliblevelus, L"ZlibLevel");
+    RtlInitUnicodeString(&flushintervalus, L"FlushInterval");
+    RtlInitUnicodeString(&maxinlineus, L"MaxInline");
+    RtlInitUnicodeString(&subvolidus, L"SubvolId");
     
     do {
         Status = ZwEnumerateValueKey(h, index, KeyValueFullInformation, kvfi, kvfilen, &retlen);
@@ -80,12 +115,53 @@ NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options)
                 DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
                 
                 options->ignore = *val != 0 ? TRUE : FALSE;
+            } else if (FsRtlAreNamesEqual(&compressus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+                DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+                
+                options->compress = *val != 0 ? TRUE : FALSE;
+            } else if (FsRtlAreNamesEqual(&compressforceus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+                DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+                
+                options->compress_force = *val != 0 ? TRUE : FALSE;
+            } else if (FsRtlAreNamesEqual(&compresstypeus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+                DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+                
+                options->compress_type = *val > BTRFS_COMPRESSION_LZO ? 0 : *val;
+            } else if (FsRtlAreNamesEqual(&readonlyus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+                DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+                
+                options->readonly = *val != 0 ? TRUE : FALSE;
+            } else if (FsRtlAreNamesEqual(&zliblevelus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+                DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+                
+                options->zlib_level = *val;
+            } else if (FsRtlAreNamesEqual(&flushintervalus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+                DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+                
+                options->flush_interval = *val;
+            } else if (FsRtlAreNamesEqual(&maxinlineus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+                DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+                
+                options->max_inline = min(*val, Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - sizeof(EXTENT_DATA) + 1);
+            } else if (FsRtlAreNamesEqual(&subvolidus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_QWORD) {
+                UINT64* val = (UINT64*)((UINT8*)kvfi + kvfi->DataOffset);
+                
+                options->subvol_id = *val;
             }
         } else if (Status != STATUS_NO_MORE_ENTRIES) {
             ERR("ZwEnumerateValueKey returned %08x\n", Status);
             goto end2;
         }
     } while (NT_SUCCESS(Status));
+    
+    if (!options->compress && options->compress_force)
+        options->compress = TRUE;
+    
+    if (options->zlib_level > 9)
+        options->zlib_level = 9;
+    
+    if (options->flush_interval == 0)
+        options->flush_interval = mount_flush_interval;
 
     Status = STATUS_SUCCESS;
     
@@ -472,38 +548,13 @@ static void read_mappings(PUNICODE_STRING regpath) {
     ExFreePool(path);
 }
 
-void STDCALL read_registry(PUNICODE_STRING regpath) {
-#ifndef __REACTOS__
-    UNICODE_STRING us;
-#endif
-    OBJECT_ATTRIBUTES oa;
-    NTSTATUS Status;
-    HANDLE h;
-    ULONG dispos;
-#ifndef __REACTOS__
+static void get_registry_value(HANDLE h, WCHAR* string, ULONG type, void* val, ULONG size) {
     ULONG kvfilen;
     KEY_VALUE_FULL_INFORMATION* kvfi;
-#endif
-    
-#ifndef __REACTOS__
-    static WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log";
-#endif
-    
-    read_mappings(regpath);
-    
-    InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
-    
-    Status = ZwCreateKey(&h, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
-    
-    if (!NT_SUCCESS(Status)) {
-        ERR("ZwCreateKey returned %08x\n", Status);
-        return;
-    }
+    UNICODE_STRING us;
+    NTSTATUS Status;
     
-    reset_subkeys(h, regpath);
-    
-#ifdef _DEBUG
-    RtlInitUnicodeString(&us, L"DebugLogLevel");
+    RtlInitUnicodeString(&us, string);
     
     kvfi = NULL;
     kvfilen = 0;
@@ -521,31 +572,75 @@ void STDCALL read_registry(PUNICODE_STRING regpath) {
         Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
         
         if (NT_SUCCESS(Status)) {
-            if (kvfi->Type == REG_DWORD && kvfi->DataLength >= sizeof(UINT32)) {
-                RtlCopyMemory(&debug_log_level, ((UINT8*)kvfi) + kvfi->DataOffset, sizeof(UINT32));
+            if (kvfi->Type == type && kvfi->DataLength >= size) {
+                RtlCopyMemory(val, ((UINT8*)kvfi) + kvfi->DataOffset, size);
             } else {
                 Status = ZwDeleteValueKey(h, &us);
                 if (!NT_SUCCESS(Status)) {
                     ERR("ZwDeleteValueKey returned %08x\n", Status);
                 }
 
-                Status = ZwSetValueKey(h, &us, 0, REG_DWORD, &debug_log_level, sizeof(debug_log_level));
+                Status = ZwSetValueKey(h, &us, 0, type, val, size);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("ZwSetValueKey reutrned %08x\n", Status);
+                    ERR("ZwSetValueKey returned %08x\n", Status);
                 }
             }
         }
         
         ExFreePool(kvfi);
     } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
-        Status = ZwSetValueKey(h, &us, 0, REG_DWORD, &debug_log_level, sizeof(debug_log_level));
+        Status = ZwSetValueKey(h, &us, 0, type, val, size);
         
         if (!NT_SUCCESS(Status)) {
-            ERR("ZwSetValueKey reutrned %08x\n", Status);
+            ERR("ZwSetValueKey returned %08x\n", Status);
         }
     } else {
         ERR("ZwQueryValueKey returned %08x\n", Status);
     }
+}
+
+void STDCALL read_registry(PUNICODE_STRING regpath) {
+#ifndef __REACTOS__
+    UNICODE_STRING us;
+#endif
+    OBJECT_ATTRIBUTES oa;
+    NTSTATUS Status;
+    HANDLE h;
+    ULONG dispos;
+#ifndef __REACTOS__
+    ULONG kvfilen;
+    KEY_VALUE_FULL_INFORMATION* kvfi;
+#endif
+    
+#ifndef __REACTOS__    
+    static WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log";
+#endif
+    
+    read_mappings(regpath);
+    
+    InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+    
+    Status = ZwCreateKey(&h, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
+    
+    if (!NT_SUCCESS(Status)) {
+        ERR("ZwCreateKey returned %08x\n", Status);
+        return;
+    }
+    
+    reset_subkeys(h, regpath);
+    
+    get_registry_value(h, L"Compress", REG_DWORD, &mount_compress, sizeof(mount_compress));
+    get_registry_value(h, L"CompressForce", REG_DWORD, &mount_compress_force, sizeof(mount_compress_force));
+    get_registry_value(h, L"CompressType", REG_DWORD, &mount_compress_type, sizeof(mount_compress_type));
+    get_registry_value(h, L"ZlibLevel", REG_DWORD, &mount_zlib_level, sizeof(mount_zlib_level));
+    get_registry_value(h, L"FlushInterval", REG_DWORD, &mount_flush_interval, sizeof(mount_flush_interval));
+    get_registry_value(h, L"MaxInline", REG_DWORD, &mount_max_inline, sizeof(mount_max_inline));
+    
+    if (mount_flush_interval == 0)
+        mount_flush_interval = 1;
+    
+#ifdef _DEBUG
+    get_registry_value(h, L"DebugLogLevel", REG_DWORD, &debug_log_level, sizeof(debug_log_level));
     
     RtlInitUnicodeString(&us, L"LogDevice");
     
index e281dc3..0aa0e26 100644 (file)
@@ -26,8 +26,6 @@ NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
     char* data;
     NTSTATUS Status;
     
-    // FIXME - check permissions
-    
     TRACE("(%p, %p, %p, %x, %p)\n", DeviceObject, FileObject, buffer, buflen, retlen);
     
     ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
@@ -134,6 +132,7 @@ end:
 static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, REPARSE_DATA_BUFFER* rdb, ULONG buflen, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     ULONG minlen;
+    ULONG tlength;
     UNICODE_STRING subname;
     ANSI_STRING target;
     LARGE_INTEGER offset, time;
@@ -155,7 +154,7 @@ static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, REPARSE_DATA_BUFFER* rd
     
     fileref->fcb->inode_item.st_mode |= __S_IFLNK;
     
-    Status = truncate_file(fileref->fcb, 0, rollback);
+    Status = truncate_file(fileref->fcb, 0, Irp, rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("truncate_file returned %08x\n", Status);
         return Status;
@@ -187,7 +186,8 @@ static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, REPARSE_DATA_BUFFER* rd
     }
     
     offset.QuadPart = 0;
-    Status = write_file2(fileref->fcb->Vcb, Irp, offset, target.Buffer, (ULONG*)&target.Length, FALSE, TRUE,
+    tlength = target.Length;
+    Status = write_file2(fileref->fcb->Vcb, Irp, offset, target.Buffer, &tlength, FALSE, TRUE,
                          TRUE, FALSE, rollback);
     ExFreePool(target.Buffer);
     
@@ -221,9 +221,6 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     ULONG tag;
     LIST_ENTRY rollback;
     
-    // FIXME - send notification if this succeeds? The attributes will have changed.
-    // FIXME - check permissions
-    
     TRACE("(%p, %p)\n", DeviceObject, Irp);
     
     InitializeListHead(&rollback);
@@ -241,6 +238,13 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         return STATUS_INVALID_PARAMETER;
     }
     
+    // It isn't documented what permissions FSCTL_SET_REPARSE_POINT needs, but CreateSymbolicLinkW
+    // creates a file with FILE_WRITE_ATTRIBUTES | DELETE | SYNCHRONIZE.
+    if (!(ccb->access & FILE_WRITE_ATTRIBUTES)) {
+        WARN("insufficient privileges\n");
+        return STATUS_ACCESS_DENIED;
+    }
+    
     fileref = ccb->fileref;
     
     if (!fileref) {
@@ -300,7 +304,7 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
             
             Status = STATUS_SUCCESS;
         } else { // otherwise, store as file data
-            Status = truncate_file(fcb, 0, &rollback);
+            Status = truncate_file(fcb, 0, Irp, &rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("truncate_file returned %08x\n", Status);
                 goto end;
@@ -356,8 +360,6 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     file_ref* fileref;
     LIST_ENTRY rollback;
     
-    // FIXME - check permissions
-    
     TRACE("(%p, %p)\n", DeviceObject, Irp);
     
     InitializeListHead(&rollback);
@@ -368,13 +370,25 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     }
     
     fcb = FileObject->FsContext;
+    
+    if (!fcb) {
+        ERR("fcb was NULL\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+    
     ccb = FileObject->FsContext2;
-    fileref = ccb ? ccb->fileref : NULL;
     
-    ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
-    ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
+    if (!ccb) {
+        ERR("ccb was NULL\n");
+        return STATUS_INVALID_PARAMETER;
+    }
     
-    TRACE("%S\n", file_desc(FileObject));
+    if (!(ccb->access & FILE_WRITE_ATTRIBUTES)) {
+        WARN("insufficient privileges\n");
+        return STATUS_ACCESS_DENIED;
+    }
+    
+    fileref = ccb->fileref;
     
     if (!fileref) {
         ERR("fileref was NULL\n");
@@ -382,6 +396,11 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         goto end;
     }
     
+    ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
+    ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
+    
+    TRACE("%S\n", file_desc(FileObject));
+    
     if (buflen < offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)) {
         ERR("buffer was too short\n");
         Status = STATUS_INVALID_PARAMETER;
@@ -433,7 +452,7 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         
         // FIXME - do we need to check that the reparse tags match?
         
-        Status = truncate_file(fcb, 0, &rollback);
+        Status = truncate_file(fcb, 0, Irp, &rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("truncate_file returned %08x\n", Status);
             goto end;
index 9b6b0cf..b2d0d78 100644 (file)
@@ -201,6 +201,7 @@ void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid) {
     um = ExAllocatePoolWithTag(PagedPool, sizeof(uid_map), ALLOC_TAG);
     if (!um) {
         ERR("out of memory\n");
+        ExFreePool(sid);
         return;
     }
     
@@ -210,7 +211,7 @@ void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid) {
     InsertTailList(&uid_map_list, &um->listentry);
 }
 
-static void uid_to_sid(UINT32 uid, PSID* sid) {
+void uid_to_sid(UINT32 uid, PSID* sid) {
     LIST_ENTRY* le;
     uid_map* um;
     sid_header* sh;
@@ -400,12 +401,12 @@ static ACL* load_default_acl() {
 //     }
 // }
 
-static BOOL get_sd_from_xattr(fcb* fcb) {
+static BOOL get_sd_from_xattr(fcb* fcb, PIRP Irp) {
     ULONG buflen;
     NTSTATUS Status;
     PSID sid, usersid;
     
-    if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8**)&fcb->sd, (UINT16*)&buflen))
+    if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8**)&fcb->sd, (UINT16*)&buflen, Irp))
         return FALSE;
     
     TRACE("using xattr " EA_NTACL " for security descriptor\n");
@@ -654,12 +655,12 @@ end:
         ExFreePool(groupsid);
 }
 
-void fcb_get_sd(fcb* fcb, struct _fcb* parent) {
+void fcb_get_sd(fcb* fcb, struct _fcb* parent, PIRP Irp) {
     NTSTATUS Status;
     PSID usersid = NULL, groupsid = NULL;
     SECURITY_SUBJECT_CONTEXT subjcont;
     
-    if (get_sd_from_xattr(fcb))
+    if (get_sd_from_xattr(fcb, Irp))
         return;
     
     if (!parent) {
@@ -833,8 +834,7 @@ static NTSTATUS STDCALL set_file_security(device_extension* Vcb, PFILE_OBJECT Fi
             fcb = fileref->parent->fcb;
         else {
             ERR("could not find parent fcb for stream\n");
-            Status = STATUS_INTERNAL_ERROR;
-            goto end;
+            return STATUS_INTERNAL_ERROR;
         }
     }
     
@@ -985,7 +985,7 @@ NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* parfileref, ACCESS_STATE* as) {
         ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status);
         fcb->inode_item.st_uid = UID_NOBODY;
     } else {
-        fcb->inode_item.st_uid = sid_to_uid(&owner);
+        fcb->inode_item.st_uid = sid_to_uid(owner);
     }
     
     return STATUS_SUCCESS;
index d3a63df..c9c988b 100644 (file)
@@ -25,7 +25,7 @@ typedef struct {
     LIST_ENTRY list_entry;
 } rollback_item;
 
-NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, const char* func, const char* file, unsigned int line) {
+NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, PIRP Irp, const char* func, const char* file, unsigned int line) {
     UINT8* buf;
     NTSTATUS Status;
     tree_header* th;
@@ -42,7 +42,7 @@ NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, &c, NULL);
+    Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, &c, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("read_data returned 0x%08x\n", Status);
         ExFreePool(buf);
@@ -288,7 +288,8 @@ static tree* free_tree2(tree* t, const char* func, const char* file, unsigned in
     return NULL;
 }
 
-NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, const char* func, const char* file, unsigned int line) {
+NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, PIRP Irp,
+                               const char* func, const char* file, unsigned int line) {
 //     KIRQL irql;
 //     tree_holder_nonpaged* thnp = th->nonpaged;
     BOOL ret;
@@ -325,7 +326,7 @@ NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r,
     if (!th->tree) {
         NTSTATUS Status;
         
-        Status = _load_tree(Vcb, th->address, r, &th->tree, t, func, file, line);
+        Status = _load_tree(Vcb, th->address, r, &th->tree, t, Irp, func, file, line);
         if (!NT_SUCCESS(Status)) {
             ERR("load_tree returned %08x\n", Status);
             ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
@@ -389,7 +390,8 @@ static __inline tree_data* next_item(tree* t, tree_data* td) {
     return CONTAINING_RECORD(le, tree_data, list_entry);
 }
 
-static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line) {
+static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, PIRP Irp,
+                                          const char* func, const char* file, unsigned int line) {
     int cmp;
     tree_data *td, *lasttd;
     
@@ -399,7 +401,7 @@ static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traver
     td = first_item(t);
     lasttd = NULL;
     
-    if (!td) return STATUS_INTERNAL_ERROR;
+    if (!td) return STATUS_NOT_FOUND;
     
     do {
         cmp = keycmp(searchkey, &td->key);
@@ -437,7 +439,7 @@ static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traver
             oldtp.tree = t;
             oldtp.item = td;
             
-            while (_find_prev_item(Vcb, &oldtp, tp, TRUE, func, file, line)) {
+            while (_find_prev_item(Vcb, &oldtp, tp, TRUE, Irp, func, file, line)) {
                 if (!tp->item->ignore)
                     return STATUS_SUCCESS;
                 
@@ -449,14 +451,14 @@ static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traver
             oldtp.tree = t;
             oldtp.item = td;
             
-            while (_find_next_item(Vcb, &oldtp, tp, TRUE, func, file, line)) {
+            while (_find_next_item(Vcb, &oldtp, tp, TRUE, Irp, func, file, line)) {
                 if (!tp->item->ignore)
                     return STATUS_SUCCESS;
                 
                 oldtp = *tp;
             }
             
-            return STATUS_INTERNAL_ERROR;
+            return STATUS_NOT_FOUND;
         } else {
             tp->tree = t;
             tp->item = td;
@@ -472,24 +474,24 @@ static NTSTATUS STDCALL find_item_in_tree(device_extension* Vcb, tree* t, traver
         }
         
         if (!td)
-            return STATUS_INTERNAL_ERROR;
+            return STATUS_NOT_FOUND;
         
 //         if (i > 0)
 //             TRACE("entering tree from (%x,%x,%x) to (%x,%x,%x) (%p)\n", (UINT32)t->items[i].key.obj_id, t->items[i].key.obj_type, (UINT32)t->items[i].key.offset, (UINT32)t->items[i+1].key.obj_id, t->items[i+1].key.obj_type, (UINT32)t->items[i+1].key.offset, t->items[i].tree);
         
-        Status = _do_load_tree(Vcb, &td->treeholder, t->root, t, td, &loaded, func, file, line);
+        Status = _do_load_tree(Vcb, &td->treeholder, t->root, t, td, &loaded, Irp, func, file, line);
         if (!NT_SUCCESS(Status)) {
             ERR("do_load_tree returned %08x\n", Status);
             return Status;
         }
         
-        Status = find_item_in_tree(Vcb, td->treeholder.tree, tp, searchkey, ignore, func, file, line);
+        Status = find_item_in_tree(Vcb, td->treeholder.tree, tp, searchkey, ignore, Irp, func, file, line);
         
         return Status;
     }
 }
 
-NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line) {
+NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, PIRP Irp, const char* func, const char* file, unsigned int line) {
     NTSTATUS Status;
     BOOL loaded;
 //     KIRQL irql;
@@ -497,15 +499,15 @@ NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, co
     TRACE("(%p, %p, %p, %p)\n", Vcb, r, tp, searchkey);
     
     if (!r->treeholder.tree) {
-        Status = _do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, func, file, line);
+        Status = _do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, Irp, func, file, line);
         if (!NT_SUCCESS(Status)) {
             ERR("do_load_tree returned %08x\n", Status);
             return Status;
         }
     }
 
-    Status = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, func, file, line);
-    if (!NT_SUCCESS(Status)) {
+    Status = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, Irp, func, file, line);
+    if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) {
         ERR("find_item_in_tree returned %08x\n", Status);
     }
     
@@ -519,7 +521,8 @@ NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, co
     return Status;
 }
 
-BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, const char* func, const char* file, unsigned int line) {
+BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, PIRP Irp,
+                             const char* func, const char* file, unsigned int line) {
     tree* t;
     tree_data *td, *next;
     NTSTATUS Status;
@@ -563,7 +566,7 @@ BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, trav
     if (!t)
         return FALSE;
     
-    Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, &loaded, func, file, line);
+    Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, &loaded, Irp, func, file, line);
     if (!NT_SUCCESS(Status)) {
         ERR("do_load_tree returned %08x\n", Status);
         return FALSE;
@@ -576,7 +579,7 @@ BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, trav
        
         fi = first_item(t);
         
-        Status = _do_load_tree(Vcb, &fi->treeholder, t->parent->root, t, fi, &loaded, func, file, line);
+        Status = _do_load_tree(Vcb, &fi->treeholder, t->parent->root, t, fi, &loaded, Irp, func, file, line);
         if (!NT_SUCCESS(Status)) {
             ERR("do_load_tree returned %08x\n", Status);
             return FALSE;
@@ -592,7 +595,7 @@ BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, trav
         traverse_ptr ntp2;
         BOOL b;
         
-        while ((b = _find_next_item(Vcb, next_tp, &ntp2, TRUE, func, file, line))) {
+        while ((b = _find_next_item(Vcb, next_tp, &ntp2, TRUE, Irp, func, file, line))) {
             *next_tp = ntp2;
             
             if (!next_tp->item->ignore)
@@ -622,7 +625,8 @@ static __inline tree_data* last_item(tree* t) {
     return CONTAINING_RECORD(le, tree_data, list_entry);
 }
 
-BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, const char* func, const char* file, unsigned int line) {
+BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, PIRP Irp,
+                             const char* func, const char* file, unsigned int line) {
     tree* t;
     tree_data* td;
     NTSTATUS Status;
@@ -649,7 +653,7 @@ BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, trav
     
     td = prev_item(t->parent, t->paritem);
     
-    Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t, td, &loaded, func, file, line);
+    Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t, td, &loaded, Irp, func, file, line);
     if (!NT_SUCCESS(Status)) {
         ERR("do_load_tree returned %08x\n", Status);
         return FALSE;
@@ -662,7 +666,7 @@ BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, trav
         
         li = last_item(t);
         
-        Status = _do_load_tree(Vcb, &li->treeholder, t->parent->root, t, li, &loaded, func, file, line);
+        Status = _do_load_tree(Vcb, &li->treeholder, t->parent->root, t, li, &loaded, Irp, func, file, line);
         if (!NT_SUCCESS(Status)) {
             ERR("do_load_tree returned %08x\n", Status);
             return FALSE;
@@ -793,7 +797,7 @@ void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr) {
     InsertTailList(rollback, &ri->list_entry);
 }
 
-BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp, LIST_ENTRY* rollback) {
+BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp, PIRP Irp, LIST_ENTRY* rollback) {
     traverse_ptr tp;
     KEY searchkey;
     int cmp;
@@ -820,13 +824,13 @@ BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UIN
     searchkey.obj_type = obj_type;
     searchkey.offset = offset;
     
-    Status = find_item(Vcb, r, &tp, &searchkey, TRUE);
-    if (!NT_SUCCESS(Status)) {
+    Status = find_item(Vcb, r, &tp, &searchkey, TRUE, Irp);
+    if (Status == STATUS_NOT_FOUND) {
         if (r) {
             if (!r->treeholder.tree) {
                 BOOL loaded;
                 
-                Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded);
+                Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, Irp);
                 
                 if (!NT_SUCCESS(Status)) {
                     ERR("do_load_tree returned %08x\n", Status);
@@ -845,6 +849,9 @@ BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UIN
             ERR("error: find_item returned %08x\n", Status);
             goto end;
         }
+    } else if (!NT_SUCCESS(Status)) {
+        ERR("find_item returned %08x\n", Status);
+        goto end;
     }
     
     TRACE("tp.item = %p\n", tp.item);
@@ -855,6 +862,7 @@ BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UIN
         
         if (cmp == 0 && !tp.item->ignore) { // FIXME - look for all items of the same key to make sure none are non-ignored
             ERR("error: key (%llx,%x,%llx) already present\n", obj_id, obj_type, offset);
+            int3;
             goto end;
         }
     } else
@@ -970,10 +978,10 @@ void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp, LIST_ENTR
     TRACE("deleting item %llx,%x,%llx (ignore = %s)\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, tp->item->ignore ? "TRUE" : "FALSE");
     
 #ifdef DEBUG_PARANOID
-    if (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
-        ERR("ERROR - tree_lock not held exclusively\n");
-        int3;
-    }
+//     if (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
+//         ERR("ERROR - tree_lock not held exclusively\n");
+//         int3;
+//     }
 
     if (tp->item->ignore) {
         ERR("trying to delete already-deleted item %llx,%x,%llx\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
@@ -1025,6 +1033,10 @@ void clear_rollback(LIST_ENTRY* rollback) {
         switch (ri->type) {
             case ROLLBACK_INSERT_ITEM:
             case ROLLBACK_DELETE_ITEM:
+            case ROLLBACK_ADD_SPACE:
+            case ROLLBACK_SUBTRACT_SPACE:
+            case ROLLBACK_INSERT_EXTENT:
+            case ROLLBACK_DELETE_EXTENT:
                 ExFreePool(ri->ptr);
                 break;
 
@@ -1037,6 +1049,7 @@ void clear_rollback(LIST_ENTRY* rollback) {
 }
 
 void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
     rollback_item* ri;
     
     while (!IsListEmpty(rollback)) {
@@ -1082,17 +1095,105 @@ void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback) {
             
             case ROLLBACK_INSERT_EXTENT:
             {
-                extent* ext = ri->ptr;
+                rollback_extent* re = ri->ptr;
                 
-                ext->ignore = TRUE;
+                re->ext->ignore = TRUE;
+                
+                if (re->ext->data->type == EXTENT_TYPE_REGULAR || re->ext->data->type == EXTENT_TYPE_PREALLOC) {
+                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)re->ext->data->data;
+                    
+                    if (ed2->size != 0) {
+                        chunk* c = get_chunk_from_address(Vcb, ed2->address);
+                        
+                        if (c) {
+                            Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, re->fcb->subvol->id,
+                                                               re->fcb->inode, re->ext->offset - ed2->offset, -1,
+                                                               re->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, NULL);
+                            
+                            if (!NT_SUCCESS(Status))
+                                ERR("update_changed_extent_ref returned %08x\n", Status);
+                        }
+                        
+                        re->fcb->inode_item.st_blocks -= ed2->num_bytes;
+                    }
+                }
+                
+                ExFreePool(re);
                 break;
             }
             
             case ROLLBACK_DELETE_EXTENT:
             {
-                extent* ext = ri->ptr;
+                rollback_extent* re = ri->ptr;
+                
+                re->ext->ignore = FALSE;
+                
+                if (re->ext->data->type == EXTENT_TYPE_REGULAR || re->ext->data->type == EXTENT_TYPE_PREALLOC) {
+                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)re->ext->data->data;
+                    
+                    if (ed2->size != 0) {
+                        chunk* c = get_chunk_from_address(Vcb, ed2->address);
+                        
+                        if (c) {
+                            Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, re->fcb->subvol->id,
+                                                               re->fcb->inode, re->ext->offset - ed2->offset, 1,
+                                                               re->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, NULL);
+                            
+                            if (!NT_SUCCESS(Status))
+                                ERR("update_changed_extent_ref returned %08x\n", Status);
+                        }
+                        
+                        re->fcb->inode_item.st_blocks += ed2->num_bytes;
+                    }
+                }
+                
+                ExFreePool(re);
+                break;
+            }
+
+            case ROLLBACK_ADD_SPACE:
+            case ROLLBACK_SUBTRACT_SPACE:
+            {
+                rollback_space* rs = ri->ptr;
+                
+                if (rs->chunk)
+                    ExAcquireResourceExclusiveLite(&rs->chunk->lock, TRUE);
+                
+                if (ri->type == ROLLBACK_ADD_SPACE)
+                    space_list_subtract2(rs->list, rs->list_size, rs->address, rs->length, NULL);
+                else
+                    space_list_add2(rs->list, rs->list_size, rs->address, rs->length, NULL);
+                
+                if (rs->chunk) {
+                    LIST_ENTRY* le2 = le->Blink;
+                    
+                    while (le2 != rollback) {
+                        LIST_ENTRY* le3 = le2->Blink;
+                        rollback_item* ri2 = CONTAINING_RECORD(le2, rollback_item, list_entry);
+                        
+                        if (ri2->type == ROLLBACK_ADD_SPACE || ri2->type == ROLLBACK_SUBTRACT_SPACE) {
+                            rollback_space* rs2 = ri2->ptr;
+                            
+                            if (rs2->chunk == rs->chunk) {
+                                if (ri2->type == ROLLBACK_ADD_SPACE)
+                                    space_list_subtract2(rs2->list, rs2->list_size, rs2->address, rs2->length, NULL);
+                                else
+                                    space_list_add2(rs2->list, rs2->list_size, rs2->address, rs2->length, NULL);
+                                
+                                ExFreePool(rs2);
+                                RemoveEntryList(&ri2->list_entry);
+                                ExFreePool(ri2);
+                            }
+                        }
+                        
+                        le2 = le3;
+                    }
+                    
+                    ExReleaseResourceLite(&rs->chunk->lock);
+                }
+                    
+                ExFreePool(rs);
                 
-                ext->ignore = FALSE;
                 break;
             }
         }
index 84c2893..2895fb4 100644 (file)
@@ -1,3 +1,20 @@
+/* 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"
 
 void do_read_job(PIRP Irp) {
@@ -78,6 +95,7 @@ void STDCALL worker_thread(void* context) {
         
         while (TRUE) {
             LIST_ENTRY* le;
+            device_extension* Vcb = thread->DeviceObject->DeviceExtension;
             
             KeAcquireSpinLock(&thread->spin_lock, &irql);
             
@@ -91,6 +109,7 @@ void STDCALL worker_thread(void* context) {
             
             KeReleaseSpinLock(&thread->spin_lock, irql);
             
+            InterlockedDecrement(&Vcb->threads.pending_jobs);
             do_job(thread, le);
         }
         
index 7df8f5e..9e14b21 100644 (file)
@@ -49,7 +49,8 @@ typedef struct {
 
 // static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len);
 static NTSTATUS STDCALL write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr);
-static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback);
+static void update_checksum_tree(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback);
+static void remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback);
 
 static NTSTATUS STDCALL write_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
     write_context* context = conptr;
@@ -274,7 +275,7 @@ static UINT64 find_new_chunk_address(device_extension* Vcb, UINT64 size) {
     return lastaddr;
 }
 
-static NTSTATUS update_dev_item(device_extension* Vcb, device* device, LIST_ENTRY* rollback) {
+static NTSTATUS update_dev_item(device_extension* Vcb, device* device, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     DEV_ITEM* di;
@@ -284,7 +285,7 @@ static NTSTATUS update_dev_item(device_extension* Vcb, device* device, LIST_ENTR
     searchkey.obj_type = TYPE_DEV_ITEM;
     searchkey.offset = device->devitem.dev_id;
     
-    Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -305,7 +306,7 @@ static NTSTATUS update_dev_item(device_extension* Vcb, device* device, LIST_ENTR
     
     RtlCopyMemory(di, &device->devitem, sizeof(DEV_ITEM));
     
-    if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, device->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, device->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp, rollback)) {
         ERR("insert_tree_item failed\n");
         return STATUS_INTERNAL_ERROR;
     }
@@ -483,7 +484,7 @@ static BOOL find_new_stripe(device_extension* Vcb, stripe* stripes, UINT16 i, UI
     return TRUE;
 }
 
-chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) {
+chunk* alloc_chunk(device_extension* Vcb, UINT64 flags) {
     UINT64 max_stripe_size, max_chunk_size, stripe_size, stripe_length, factor;
     UINT64 total_size = 0, i, logaddr;
     UINT16 type, num_stripes, sub_stripes, max_stripes, min_stripes;
@@ -501,6 +502,8 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) {
     }
     TRACE("total_size = %llx\n", total_size);
     
+    // We purposely check for DATA first - mixed blocks have the same size
+    // as DATA ones.
     if (flags & BLOCK_FLAG_DATA) {
         max_stripe_size = 0x40000000; // 1 GB
         max_chunk_size = 10 * max_stripe_size;
@@ -523,12 +526,12 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) {
     if (flags & BLOCK_FLAG_DUPLICATE) {
         min_stripes = 2;
         max_stripes = 2;
-        sub_stripes = 1;
+        sub_stripes = 0;
         type = BLOCK_FLAG_DUPLICATE;
     } else if (flags & BLOCK_FLAG_RAID0) {
         min_stripes = 2;
         max_stripes = Vcb->superblock.num_devices;
-        sub_stripes = 1;
+        sub_stripes = 0;
         type = BLOCK_FLAG_RAID0;
     } else if (flags & BLOCK_FLAG_RAID1) {
         min_stripes = 2;
@@ -585,22 +588,14 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) {
         goto end;
     }
     
-    c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
+    c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
     if (!c) {
         ERR("out of memory\n");
         goto end;
     }
     
-    c->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk_nonpaged), ALLOC_TAG);
-    if (!c->nonpaged) {
-        ERR("out of memory\n");
-        goto end;
-    }
-    
-    // add CHUNK_ITEM to tree 3
-    
     cisize = sizeof(CHUNK_ITEM) + (num_stripes * sizeof(CHUNK_ITEM_STRIPE));
-    c->chunk_item = ExAllocatePoolWithTag(PagedPool, cisize, ALLOC_TAG);
+    c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, cisize, ALLOC_TAG);
     if (!c->chunk_item) {
         ERR("out of memory\n");
         goto end;
@@ -640,7 +635,7 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) {
     c->chunk_item->num_stripes = num_stripes;
     c->chunk_item->sub_stripes = sub_stripes;
     
-    c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * num_stripes, ALLOC_TAG);
+    c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * num_stripes, ALLOC_TAG);
     if (!c->devices) {
         ERR("out of memory\n");
         goto end;
@@ -673,10 +668,10 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) {
     InitializeListHead(&c->deleting);
     InitializeListHead(&c->changed_extents);
     
-    ExInitializeResourceLite(&c->nonpaged->lock);
-    ExInitializeResourceLite(&c->nonpaged->changed_extents_lock);
+    ExInitializeResourceLite(&c->lock);
+    ExInitializeResourceLite(&c->changed_extents_lock);
     
-    s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+    s = ExAllocatePoolWithTag(NonPagedPool, sizeof(space), ALLOC_TAG);
     if (!s) {
         ERR("out of memory\n");
         goto end;
@@ -692,7 +687,7 @@ chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback) {
     for (i = 0; i < num_stripes; i++) {
         stripes[i].device->devitem.bytes_used += stripe_size;
         
-        space_list_subtract2(&stripes[i].device->space, NULL, cis[i].offset, stripe_size, rollback);
+        space_list_subtract2(&stripes[i].device->space, NULL, cis[i].offset, stripe_size, NULL);
     }
     
     success = TRUE;
@@ -734,10 +729,9 @@ end:
     return success ? c : NULL;
 }
 
-NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp) {
+NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp, chunk* c) {
     NTSTATUS Status;
     UINT32 i;
-    chunk* c;
     CHUNK_ITEM_STRIPE* cis;
     write_data_stripe* stripe;
     UINT64 *stripestart = NULL, *stripeend = NULL;
@@ -746,10 +740,12 @@ NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, B
     
     TRACE("(%p, %llx, %p, %x)\n", Vcb, address, data, length);
     
-    c = get_chunk_from_address(Vcb, address);
     if (!c) {
-        ERR("could not get chunk for address %llx\n", address);
-        return STATUS_INTERNAL_ERROR;
+        c = get_chunk_from_address(Vcb, address);
+        if (!c) {
+            ERR("could not get chunk for address %llx\n", address);
+            return STATUS_INTERNAL_ERROR;
+        }
     }
     
     if (c->chunk_item->type & BLOCK_FLAG_RAID5) {
@@ -973,7 +969,7 @@ NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, B
             stripe->Irp = NULL;
             stripe->buf = NULL;
         } else {
-            stripe->context = (struct write_data_context*)wtc;
+            stripe->context = (struct _write_data_context*)wtc;
             stripe->buf = stripedata[i];
             stripe->need_free = need_free2;
             stripe->device = c->devices[i];
@@ -1046,7 +1042,7 @@ end:
     return Status;
 }
 
-NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp) {
+NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp, chunk* c) {
     write_data_context* wtc;
     NTSTATUS Status;
     
@@ -1061,7 +1057,7 @@ NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void
     wtc->tree = FALSE;
     wtc->stripes_left = 0;
     
-    Status = write_data(Vcb, address, data, FALSE, length, wtc, Irp);
+    Status = write_data(Vcb, address, data, FALSE, length, wtc, Irp, c);
     if (!NT_SUCCESS(Status)) {
         ERR("write_data returned %08x\n", Status);
         free_write_data_stripes(wtc);
@@ -1123,13 +1119,13 @@ static void clean_space_cache(device_extension* Vcb) {
     while (!IsListEmpty(&Vcb->chunks_changed)) {
         c = CONTAINING_RECORD(Vcb->chunks_changed.Flink, chunk, list_entry_changed);
         
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
         clean_space_cache_chunk(Vcb, c);
         RemoveEntryList(&c->list_entry_changed);
         c->list_entry_changed.Flink = NULL;
         
-        ExReleaseResourceLite(&c->nonpaged->lock);
+        ExReleaseResourceLite(&c->lock);
     }
 }
 
@@ -1173,7 +1169,6 @@ static BOOL trees_consistent(device_extension* Vcb, LIST_ENTRY* rollback) {
 static NTSTATUS add_parents(device_extension* Vcb, LIST_ENTRY* rollback) {
     UINT8 level;
     LIST_ENTRY* le;
-    NTSTATUS Status;
     
     for (level = 0; level <= 255; level++) {
         BOOL nothing_found = TRUE;
@@ -1194,46 +1189,6 @@ static NTSTATUS add_parents(device_extension* Vcb, LIST_ENTRY* rollback) {
                         TRACE("adding tree %p (level %x)\n", t->parent, t->header.level);
                         
                     t->parent->write = TRUE;
-                } else if (t->root != Vcb->chunk_root && t->root != Vcb->root_root) {
-                    KEY searchkey;
-                    traverse_ptr tp;
-                    
-                    searchkey.obj_id = t->root->id;
-                    searchkey.obj_type = TYPE_ROOT_ITEM;
-                    searchkey.offset = 0xffffffffffffffff;
-                    
-                    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 (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
-                        ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
-                        return STATUS_INTERNAL_ERROR;
-                    }
-                    
-                    if (tp.item->size < sizeof(ROOT_ITEM)) { // if not full length, create new entry with new bits zeroed
-                        ROOT_ITEM* ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
-                        if (!ri) {
-                            ERR("out of memory\n");
-                            return STATUS_INSUFFICIENT_RESOURCES;
-                        }
-                        
-                        if (tp.item->size > 0)
-                            RtlCopyMemory(ri, tp.item->data, tp.item->size);
-                        
-                        RtlZeroMemory(((UINT8*)ri) + tp.item->size, sizeof(ROOT_ITEM) - tp.item->size);
-                        
-                        delete_tree_item(Vcb, &tp, rollback);
-                        
-                        if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, rollback)) {
-                            ERR("insert_tree_item failed\n");
-                            return STATUS_INTERNAL_ERROR;
-                        }
-                    } else {
-                        tp.tree->write = TRUE;
-                    }
                 }
             }
             
@@ -1248,37 +1203,13 @@ static NTSTATUS add_parents(device_extension* Vcb, LIST_ENTRY* rollback) {
 }
 
 static void add_parents_to_cache(device_extension* Vcb, tree* t) {
-    KEY searchkey;
-    traverse_ptr tp;
-    NTSTATUS Status;
-    
     while (t->parent) {
         t = t->parent;
         t->write = TRUE;
     }
-    
-    if (t->root == Vcb->root_root || t->root == Vcb->chunk_root)
-        return;
-    
-    searchkey.obj_id = t->root->id;
-    searchkey.obj_type = TYPE_ROOT_ITEM;
-    searchkey.offset = 0xffffffffffffffff;
-    
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return;
-    }
-    
-    if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
-        ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
-        return;
-    }
-    
-    tp.tree->write = TRUE;
 }
 
-static BOOL insert_tree_extent_skinny(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64 address, LIST_ENTRY* rollback) {
+static BOOL insert_tree_extent_skinny(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64 address, PIRP Irp, LIST_ENTRY* rollback) {
     EXTENT_ITEM_SKINNY_METADATA* eism;
     traverse_ptr insert_tp;
     
@@ -1294,24 +1225,24 @@ static BOOL insert_tree_extent_skinny(device_extension* Vcb, UINT8 level, UINT64
     eism->type = TYPE_TREE_BLOCK_REF;
     eism->tbr.offset = root_id;
     
-    if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, Irp, rollback)) {
         ERR("insert_tree_item failed\n");
         ExFreePool(eism);
         return FALSE;
     }
     
-    ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+    ExAcquireResourceExclusiveLite(&c->lock, TRUE);
     
     space_list_subtract(Vcb, c, FALSE, address, Vcb->superblock.node_size, rollback);
 
-    ExReleaseResourceLite(&c->nonpaged->lock);
+    ExReleaseResourceLite(&c->lock);
     
     add_parents_to_cache(Vcb, insert_tp.tree);
     
     return TRUE;
 }
 
-static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64* new_address, LIST_ENTRY* rollback) {
+static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64* new_address, PIRP Irp, LIST_ENTRY* rollback) {
     UINT64 address;
     EXTENT_ITEM_TREE2* eit2;
     traverse_ptr insert_tp;
@@ -1322,7 +1253,7 @@ static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_i
         return FALSE;
     
     if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
-        BOOL b = insert_tree_extent_skinny(Vcb, level, root_id, c, address, rollback);
+        BOOL b = insert_tree_extent_skinny(Vcb, level, root_id, c, address, Irp, rollback);
         
         if (b)
             *new_address = address;
@@ -1352,17 +1283,17 @@ static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_i
 //     }
 // #endif
     
-    if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, Irp, rollback)) {
         ERR("insert_tree_item failed\n");
         ExFreePool(eit2);
         return FALSE;
     }
     
-    ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+    ExAcquireResourceExclusiveLite(&c->lock, TRUE);
     
     space_list_subtract(Vcb, c, FALSE, address, Vcb->superblock.node_size, rollback);
     
-    ExReleaseResourceLite(&c->nonpaged->lock);
+    ExReleaseResourceLite(&c->lock);
 
     add_parents_to_cache(Vcb, insert_tp.tree);
     
@@ -1371,13 +1302,19 @@ static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_i
     return TRUE;
 }
 
-NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollback) {
+NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
     chunk *origchunk = NULL, *c;
     LIST_ENTRY* le;
     UINT64 flags = t->flags, addr;
     
-    if (flags == 0)
-        flags = (t->root->id == BTRFS_ROOT_CHUNK ? BLOCK_FLAG_SYSTEM : BLOCK_FLAG_METADATA) | BLOCK_FLAG_DUPLICATE;
+    if (flags == 0) {
+        if (t->root->id == BTRFS_ROOT_CHUNK)
+            flags = BLOCK_FLAG_SYSTEM | BLOCK_FLAG_DUPLICATE;
+        else if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS)
+            flags = BLOCK_FLAG_DATA | BLOCK_FLAG_METADATA;
+        else
+            flags = BLOCK_FLAG_METADATA | BLOCK_FLAG_DUPLICATE;
+    }
     
 //     TRACE("flags = %x\n", (UINT32)wt->flags);
     
@@ -1395,7 +1332,7 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollba
     if (t->has_address) {
         origchunk = get_chunk_from_address(Vcb, t->header.address);
         
-        if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, origchunk, &addr, rollback)) {
+        if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, origchunk, &addr, Irp, rollback)) {
             t->new_address = addr;
             t->has_new_address = TRUE;
             return STATUS_SUCCESS;
@@ -1408,11 +1345,11 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollba
     while (le != &Vcb->chunks) {
         c = CONTAINING_RECORD(le, chunk, list_entry);
         
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
         if (c != origchunk && c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
-            if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, rollback)) {
-                ExReleaseResourceLite(&c->nonpaged->lock);
+            if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, Irp, rollback)) {
+                ExReleaseResourceLite(&c->lock);
                 ExReleaseResourceLite(&Vcb->chunk_lock);
                 t->new_address = addr;
                 t->has_new_address = TRUE;
@@ -1420,18 +1357,18 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollba
             }
         }
         
-        ExReleaseResourceLite(&c->nonpaged->lock);
+        ExReleaseResourceLite(&c->lock);
 
         le = le->Flink;
     }
     
     // allocate new chunk if necessary
-    if ((c = alloc_chunk(Vcb, flags, rollback))) {
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+    if ((c = alloc_chunk(Vcb, flags))) {
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
         if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
-            if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, rollback)) {
-                ExReleaseResourceLite(&c->nonpaged->lock);
+            if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, Irp, rollback)) {
+                ExReleaseResourceLite(&c->lock);
                 ExReleaseResourceLite(&Vcb->chunk_lock);
                 t->new_address = addr;
                 t->has_new_address = TRUE;
@@ -1439,7 +1376,7 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollba
             }
         }
         
-        ExReleaseResourceLite(&c->nonpaged->lock);
+        ExReleaseResourceLite(&c->lock);
     }
     
     ExReleaseResourceLite(&Vcb->chunk_lock);
@@ -1449,7 +1386,7 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollba
     return STATUS_DISK_FULL;
 }
 
-static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tree* t, LIST_ENTRY* rollback) {
+static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     chunk* c;
@@ -1459,7 +1396,7 @@ static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tre
     searchkey.obj_type = TYPE_METADATA_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return FALSE;
@@ -1480,13 +1417,13 @@ static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tre
     c = get_chunk_from_address(Vcb, address);
     
     if (c) {
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
         decrease_chunk_usage(c, Vcb->superblock.node_size);
         
         space_list_add(Vcb, c, TRUE, address, Vcb->superblock.node_size, rollback);
         
-        ExReleaseResourceLite(&c->nonpaged->lock);
+        ExReleaseResourceLite(&c->lock);
     } else
         ERR("could not find chunk for address %llx\n", address);
     
@@ -1526,7 +1463,7 @@ static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tre
 //     }    
 // }
 
-static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* t, LIST_ENTRY* rollback) {
+static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp, tp2, insert_tp;
     EXTENT_REF_V0* erv0;
@@ -1538,7 +1475,7 @@ static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree*
     searchkey.obj_type = TYPE_EXTENT_REF_V0;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return;
@@ -1553,7 +1490,7 @@ static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree*
     searchkey.obj_type = TYPE_EXTENT_ITEM;
     searchkey.offset = Vcb->superblock.node_size;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return;
@@ -1588,7 +1525,7 @@ static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree*
         eism->type = TYPE_TREE_BLOCK_REF;
         eism->tbr.offset = t->header.tree_id;
         
-        if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_METADATA_ITEM, t->header.level -1, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_METADATA_ITEM, t->header.level -1, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             return;
         }
@@ -1608,7 +1545,7 @@ static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree*
         eit2->type = TYPE_TREE_BLOCK_REF;
         eit2->tbr.offset = t->header.tree_id;
 
-        if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             return;
         }
@@ -1619,7 +1556,7 @@ static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree*
     add_parents_to_cache(Vcb, tp2.tree);
 }
 
-static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* t, LIST_ENTRY* rollback) {
+static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     EXTENT_ITEM* ei;
@@ -1632,7 +1569,7 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree*
     TRACE("(%p, %llx, %p)\n", Vcb, address, t);
     
     if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
-        if (reduce_tree_extent_skinny(Vcb, address, t, rollback)) {
+        if (reduce_tree_extent_skinny(Vcb, address, t, Irp, rollback)) {
             return STATUS_SUCCESS;
         }
     }
@@ -1641,7 +1578,7 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree*
     searchkey.obj_type = TYPE_EXTENT_ITEM;
     searchkey.offset = Vcb->superblock.node_size;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -1684,7 +1621,7 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree*
         searchkey.obj_type = TYPE_EXTENT_REF_V0;
         searchkey.offset = 0xffffffffffffffff;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -1708,7 +1645,7 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree*
             
             if (!td->ignore && !td->inserted) {
                 if (t->header.level > 0) {
-                    convert_old_tree_extent(Vcb, td, t, rollback);
+                    convert_old_tree_extent(Vcb, td, t, Irp, rollback);
                 } else if (td->key.obj_type == TYPE_EXTENT_DATA && td->size >= sizeof(EXTENT_DATA)) {
                     EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
                     
@@ -1717,7 +1654,7 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree*
                         
                         if (ed2->address != 0) {
                             TRACE("trying to convert old data extent %llx,%llx\n", ed2->address, ed2->size);
-                            convert_old_data_extent(Vcb, ed2->address, ed2->size, rollback);
+                            convert_old_data_extent(Vcb, ed2->address, ed2->size, Irp, rollback);
                         }
                     }
                 }
@@ -1730,20 +1667,20 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree*
     c = get_chunk_from_address(Vcb, address);
     
     if (c) {
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
         decrease_chunk_usage(c, tp.item->key.offset);
         
         space_list_add(Vcb, c, TRUE, address, tp.item->key.offset, rollback);
         
-        ExReleaseResourceLite(&c->nonpaged->lock);
+        ExReleaseResourceLite(&c->lock);
     } else
         ERR("could not find chunk for address %llx\n", address);
     
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS allocate_tree_extents(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS allocate_tree_extents(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY* le;
     NTSTATUS Status;
     
@@ -1756,7 +1693,7 @@ static NTSTATUS allocate_tree_extents(device_extension* Vcb, LIST_ENTRY* rollbac
         if (t->write && !t->has_new_address) {
             chunk* c;
             
-            Status = get_tree_new_address(Vcb, t, rollback);
+            Status = get_tree_new_address(Vcb, t, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("get_tree_new_address returned %08x\n", Status);
                 return Status;
@@ -1765,7 +1702,7 @@ static NTSTATUS allocate_tree_extents(device_extension* Vcb, LIST_ENTRY* rollbac
             TRACE("allocated extent %llx\n", t->new_address);
             
             if (t->has_address) {
-                Status = reduce_tree_extent(Vcb, t->header.address, t, rollback);
+                Status = reduce_tree_extent(Vcb, t->header.address, t, Irp, rollback);
                 
                 if (!NT_SUCCESS(Status)) {
                     ERR("reduce_tree_extent returned %08x\n", Status);
@@ -1789,7 +1726,7 @@ static NTSTATUS allocate_tree_extents(device_extension* Vcb, LIST_ENTRY* rollbac
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS update_root_root(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS update_root_root(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY* le;
     NTSTATUS Status;
     
@@ -1808,7 +1745,7 @@ static NTSTATUS update_root_root(device_extension* Vcb, LIST_ENTRY* rollback) {
                 searchkey.obj_type = TYPE_ROOT_ITEM;
                 searchkey.offset = 0xffffffffffffffff;
                 
-                Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+                Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
                 if (!NT_SUCCESS(Status)) {
                     ERR("error - find_item returned %08x\n", Status);
                     return Status;
@@ -1816,6 +1753,7 @@ static NTSTATUS update_root_root(device_extension* Vcb, LIST_ENTRY* rollback) {
                 
                 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
                     ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
+                    int3;
                     return STATUS_INTERNAL_ERROR;
                 }
                 
@@ -1838,7 +1776,7 @@ static NTSTATUS update_root_root(device_extension* Vcb, LIST_ENTRY* rollback) {
                     
                     delete_tree_item(Vcb, &tp, rollback);
                     
-                    if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, 0, ri, sizeof(ROOT_ITEM), NULL, rollback)) {
+                    if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, 0, ri, sizeof(ROOT_ITEM), NULL, Irp, rollback)) {
                         ERR("insert_tree_item failed\n");
                         return STATUS_INTERNAL_ERROR;
                     }
@@ -1852,7 +1790,7 @@ static NTSTATUS update_root_root(device_extension* Vcb, LIST_ENTRY* rollback) {
         le = le->Flink;
     }
     
-    Status = update_chunk_caches(Vcb, rollback);
+    Status = update_chunk_caches(Vcb, Irp, rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("update_chunk_caches returned %08x\n", Status);
         return Status;
@@ -1945,7 +1883,7 @@ void free_write_data_stripes(write_data_context* wtc) {
     }
 }
 
-static NTSTATUS write_trees(device_extension* Vcb) {
+static NTSTATUS write_trees(device_extension* Vcb, PIRP Irp) {
     UINT8 level;
     UINT8 *data, *body;
     UINT32 crc32;
@@ -1996,7 +1934,7 @@ static NTSTATUS write_trees(device_extension* Vcb) {
                     searchkey.obj_type = TYPE_EXTENT_ITEM;
                     searchkey.offset = Vcb->superblock.node_size;
                     
-                    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+                    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
                     if (!NT_SUCCESS(Status)) {
                         ERR("error - find_item returned %08x\n", Status);
                         return Status;
@@ -2193,7 +2131,7 @@ static NTSTATUS write_trees(device_extension* Vcb) {
             *((UINT32*)data) = crc32;
             TRACE("setting crc32 to %08x\n", crc32);
             
-            Status = write_data(Vcb, t->new_address, data, TRUE, Vcb->superblock.node_size, wtc, NULL);
+            Status = write_data(Vcb, t->new_address, data, TRUE, Vcb->superblock.node_size, wtc, NULL, NULL);
             if (!NT_SUCCESS(Status)) {
                 ERR("write_data returned %08x\n", Status);
                 goto end;
@@ -2240,7 +2178,7 @@ end:
     return Status;
 }
 
-static void update_backup_superblock(device_extension* Vcb, superblock_backup* sb) {
+static void update_backup_superblock(device_extension* Vcb, superblock_backup* sb, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     
@@ -2258,7 +2196,7 @@ static void update_backup_superblock(device_extension* Vcb, superblock_backup* s
     searchkey.obj_type = TYPE_ROOT_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) {
+    if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) {
         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
             ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
             
@@ -2270,7 +2208,7 @@ static void update_backup_superblock(device_extension* Vcb, superblock_backup* s
 
     searchkey.obj_id = BTRFS_ROOT_FSTREE;
     
-    if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) {
+    if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) {
         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
             ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
             
@@ -2282,7 +2220,7 @@ static void update_backup_superblock(device_extension* Vcb, superblock_backup* s
     
     searchkey.obj_id = BTRFS_ROOT_DEVTREE;
     
-    if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) {
+    if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) {
         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
             ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
             
@@ -2294,7 +2232,7 @@ static void update_backup_superblock(device_extension* Vcb, superblock_backup* s
 
     searchkey.obj_id = BTRFS_ROOT_CHECKSUM;
     
-    if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE))) {
+    if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) {
         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
             ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
             
@@ -2309,7 +2247,7 @@ static void update_backup_superblock(device_extension* Vcb, superblock_backup* s
     sb->num_devices = Vcb->superblock.num_devices;
 }
 
-static NTSTATUS write_superblocks(device_extension* Vcb) {
+static NTSTATUS write_superblocks(device_extension* Vcb, PIRP Irp) {
     UINT64 i;
     NTSTATUS Status;
     LIST_ENTRY* le;
@@ -2338,7 +2276,7 @@ static NTSTATUS write_superblocks(device_extension* Vcb) {
         RtlCopyMemory(&Vcb->superblock.backup[i], &Vcb->superblock.backup[i+1], sizeof(superblock_backup));
     }
     
-    update_backup_superblock(Vcb, &Vcb->superblock.backup[BTRFS_NUM_BACKUP_ROOTS - 1]);
+    update_backup_superblock(Vcb, &Vcb->superblock.backup[BTRFS_NUM_BACKUP_ROOTS - 1], Irp);
     
     for (i = 0; i < Vcb->superblock.num_devices; i++) {
         if (Vcb->devices[i].devobj) {
@@ -2353,7 +2291,7 @@ static NTSTATUS write_superblocks(device_extension* Vcb) {
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_extent* ce, LIST_ENTRY* rollback) {
+static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_extent* ce, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY *le, *le2;
     NTSTATUS Status;
     UINT64 old_size;
@@ -2382,7 +2320,7 @@ static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_ex
         old_size = ce->old_count > 0 ? ce->old_size : ce->size;
         
         if (cer->edr.count > old_count) {
-            Status = increase_extent_refcount_data(Vcb, ce->address, old_size, cer->edr.root, cer->edr.objid, cer->edr.offset, cer->edr.count - old_count, rollback);
+            Status = increase_extent_refcount_data(Vcb, ce->address, old_size, cer->edr.root, cer->edr.objid, cer->edr.offset, cer->edr.count - old_count, Irp, rollback);
                         
             if (!NT_SUCCESS(Status)) {
                 ERR("increase_extent_refcount_data returned %08x\n", Status);
@@ -2390,7 +2328,7 @@ static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_ex
             }
         } else if (cer->edr.count < old_count) {
             Status = decrease_extent_refcount_data(Vcb, ce->address, old_size, cer->edr.root, cer->edr.objid, cer->edr.offset,
-                                                   old_count - cer->edr.count, rollback);
+                                                   old_count - cer->edr.count, Irp, rollback);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("decrease_extent_refcount_data returned %08x\n", Status);
@@ -2407,7 +2345,7 @@ static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_ex
             searchkey.obj_type = TYPE_EXTENT_ITEM;
             searchkey.offset = ce->old_size;
             
-            Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+            Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("error - find_item returned %08x\n", Status);
                 return Status;
@@ -2430,7 +2368,7 @@ static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_ex
             } else
                 data = NULL;
             
-            if (!insert_tree_item(Vcb, Vcb->extent_root, ce->address, TYPE_EXTENT_ITEM, ce->size, data, tp.item->size, NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->extent_root, ce->address, TYPE_EXTENT_ITEM, ce->size, data, tp.item->size, NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 return STATUS_INTERNAL_ERROR;
             }
@@ -2484,7 +2422,7 @@ static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_ex
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY *le = Vcb->chunks.Flink, *le2;
     chunk* c;
     KEY searchkey;
@@ -2500,17 +2438,17 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback)
     while (le != &Vcb->chunks) {
         c = CONTAINING_RECORD(le, chunk, list_entry);
         
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
         le2 = c->changed_extents.Flink;
         while (le2 != &c->changed_extents) {
             LIST_ENTRY* le3 = le2->Flink;
             changed_extent* ce = CONTAINING_RECORD(le2, changed_extent, list_entry);
             
-            Status = flush_changed_extent(Vcb, c, ce, rollback);
+            Status = flush_changed_extent(Vcb, c, ce, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("flush_changed_extent returned %08x\n", Status);
-                ExReleaseResourceLite(&c->nonpaged->lock);
+                ExReleaseResourceLite(&c->lock);
                 goto end;
             }
             
@@ -2524,10 +2462,10 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback)
             searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
             searchkey.offset = c->chunk_item->size;
             
-            Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+            Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("error - find_item returned %08x\n", Status);
-                ExReleaseResourceLite(&c->nonpaged->lock);
+                ExReleaseResourceLite(&c->lock);
                 goto end;
             }
             
@@ -2535,14 +2473,14 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback)
                 ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
                 int3;
                 Status = STATUS_INTERNAL_ERROR;
-                ExReleaseResourceLite(&c->nonpaged->lock);
+                ExReleaseResourceLite(&c->lock);
                 goto end;
             }
             
             if (tp.item->size < sizeof(BLOCK_GROUP_ITEM)) {
                 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(BLOCK_GROUP_ITEM));
                 Status = STATUS_INTERNAL_ERROR;
-                ExReleaseResourceLite(&c->nonpaged->lock);
+                ExReleaseResourceLite(&c->lock);
                 goto end;
             }
             
@@ -2550,7 +2488,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback)
             if (!bgi) {
                 ERR("out of memory\n");
                 Status = STATUS_INSUFFICIENT_RESOURCES;
-                ExReleaseResourceLite(&c->nonpaged->lock);
+                ExReleaseResourceLite(&c->lock);
                 goto end;
             }
     
@@ -2561,11 +2499,11 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback)
             
             delete_tree_item(Vcb, &tp, rollback);
             
-            if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, bgi, tp.item->size, NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, bgi, tp.item->size, NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 ExFreePool(bgi);
                 Status = STATUS_INTERNAL_ERROR;
-                ExReleaseResourceLite(&c->nonpaged->lock);
+                ExReleaseResourceLite(&c->lock);
                 goto end;
             }
             
@@ -2580,13 +2518,13 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback)
                 FIXME("RAID5 not yet supported\n");
                 ExFreePool(bgi);
                 Status = STATUS_INTERNAL_ERROR;
-                ExReleaseResourceLite(&c->nonpaged->lock);
+                ExReleaseResourceLite(&c->lock);
                 goto end;
             } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
                 FIXME("RAID6 not yet supported\n");
                 ExFreePool(bgi);
                 Status = STATUS_INTERNAL_ERROR;
-                ExReleaseResourceLite(&c->nonpaged->lock);
+                ExReleaseResourceLite(&c->lock);
                 goto end;
             } else { // SINGLE
                 Vcb->superblock.bytes_used += c->used - c->oldused;
@@ -2597,7 +2535,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback)
             c->oldused = c->used;
         }
         
-        ExReleaseResourceLite(&c->nonpaged->lock);
+        ExReleaseResourceLite(&c->lock);
         
         le = le->Flink;
     }
@@ -2605,7 +2543,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, LIST_ENTRY* rollback)
     if (flushed_extents) {
         ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE);
         if (!IsListEmpty(&Vcb->sector_checksums)) {
-            update_checksum_tree(Vcb, rollback);
+            update_checksum_tree(Vcb, Irp, rollback);
         }
         ExReleaseResourceLite(&Vcb->checksum_lock);
     }
@@ -2923,7 +2861,7 @@ static NTSTATUS STDCALL split_tree(device_extension* Vcb, tree* t) {
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY* rollback) {
+static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY* le;
     tree_data* nextparitem = NULL;
     NTSTATUS Status;
@@ -2956,7 +2894,7 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY*
     
 //     ExAcquireResourceExclusiveLite(&t->parent->nonpaged->load_tree_lock, TRUE);
     
-    Status = do_load_tree(Vcb, &nextparitem->treeholder, t->root, t->parent, nextparitem, &loaded);
+    Status = do_load_tree(Vcb, &nextparitem->treeholder, t->root, t->parent, nextparitem, &loaded, NULL);
     if (!NT_SUCCESS(Status)) {
         ERR("do_load_tree returned %08x\n", Status);
         return Status;
@@ -3006,14 +2944,14 @@ static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, LIST_ENTRY*
         next_tree->size = 0;
         
         if (next_tree->has_new_address) { // delete associated EXTENT_ITEM
-            Status = reduce_tree_extent(Vcb, next_tree->new_address, next_tree, rollback);
+            Status = reduce_tree_extent(Vcb, next_tree->new_address, next_tree, Irp, rollback);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("reduce_tree_extent returned %08x\n", Status);
                 return Status;
             }
         } else if (next_tree->has_address) {
-            Status = reduce_tree_extent(Vcb, next_tree->header.address, next_tree, rollback);
+            Status = reduce_tree_extent(Vcb, next_tree->header.address, next_tree, Irp, rollback);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("reduce_tree_extent returned %08x\n", Status);
@@ -3107,7 +3045,7 @@ 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) {
+static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* t, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -3117,7 +3055,7 @@ static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree*
         searchkey.obj_type = TYPE_METADATA_ITEM;
         searchkey.offset = t->header.level;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -3140,7 +3078,7 @@ static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree*
             
             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)) {
+            if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, tp.item->size, NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 ExFreePool(eism);
                 return STATUS_INTERNAL_ERROR;
@@ -3154,7 +3092,7 @@ static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree*
     searchkey.obj_type = TYPE_EXTENT_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -3181,7 +3119,7 @@ static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree*
         
         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)) {
+        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, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             ExFreePool(eit);
             return STATUS_INTERNAL_ERROR;
@@ -3195,7 +3133,7 @@ static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree*
     return STATUS_INTERNAL_ERROR;
 }
 
-static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS STDCALL do_splits(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
 //     LIST_ENTRY *le, *le2;
 //     write_tree* wt;
 //     tree_data* td;
@@ -3249,7 +3187,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
                         t->root->root_item.bytes_used -= Vcb->superblock.node_size;
                         
                         if (t->has_new_address) { // delete associated EXTENT_ITEM
-                            Status = reduce_tree_extent(Vcb, t->new_address, t, rollback);
+                            Status = reduce_tree_extent(Vcb, t->new_address, t, Irp, rollback);
                             
                             if (!NT_SUCCESS(Status)) {
                                 ERR("reduce_tree_extent returned %08x\n", Status);
@@ -3258,7 +3196,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
                             
                             t->has_new_address = FALSE;
                         } else if (t->has_address) {
-                            Status = reduce_tree_extent(Vcb,t->header.address, t, rollback);
+                            Status = reduce_tree_extent(Vcb,t->header.address, t, Irp, rollback);
                             
                             if (!NT_SUCCESS(Status)) {
                                 ERR("reduce_tree_extent returned %08x\n", Status);
@@ -3281,7 +3219,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
                         free_tree(t);
                     } else if (t->header.level != 0) {
                         if (t->has_new_address) {
-                            Status = update_extent_level(Vcb, t->new_address, t, 0, rollback);
+                            Status = update_extent_level(Vcb, t->new_address, t, 0, Irp, rollback);
                             
                             if (!NT_SUCCESS(Status)) {
                                 ERR("update_extent_level returned %08x\n", Status);
@@ -3324,7 +3262,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
             t = CONTAINING_RECORD(le, tree, list_entry);
             
             if (t->write && t->header.level == level && t->header.num_items > 0 && t->parent && t->size < min_size) {
-                Status = try_tree_amalgamate(Vcb, t, rollback);
+                Status = try_tree_amalgamate(Vcb, t, Irp, rollback);
                 if (!NT_SUCCESS(Status)) {
                     ERR("try_tree_amalgamate returned %08x\n", Status);
                     return Status;
@@ -3362,7 +3300,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
                         TRACE("deleting top-level tree in root %llx with one item\n", t->root->id);
                         
                         if (t->has_new_address) { // delete associated EXTENT_ITEM
-                            Status = reduce_tree_extent(Vcb, t->new_address, t, rollback);
+                            Status = reduce_tree_extent(Vcb, t->new_address, t, Irp, rollback);
                             
                             if (!NT_SUCCESS(Status)) {
                                 ERR("reduce_tree_extent returned %08x\n", Status);
@@ -3371,7 +3309,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
                             
                             t->has_new_address = FALSE;
                         } else if (t->has_address) {
-                            Status = reduce_tree_extent(Vcb,t->header.address, t, rollback);
+                            Status = reduce_tree_extent(Vcb,t->header.address, t, Irp, rollback);
                             
                             if (!NT_SUCCESS(Status)) {
                                 ERR("reduce_tree_extent returned %08x\n", Status);
@@ -3385,7 +3323,7 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
                             KEY searchkey = {0,0,0};
                             traverse_ptr tp;
                             
-                            Status = find_item(Vcb, t->root, &tp, &searchkey, FALSE);
+                            Status = find_item(Vcb, t->root, &tp, &searchkey, FALSE, Irp);
                             if (!NT_SUCCESS(Status)) {
                                 ERR("error - find_item returned %08x\n", Status);
                                 return Status;
@@ -3416,12 +3354,12 @@ static NTSTATUS STDCALL do_splits(device_extension* Vcb, LIST_ENTRY* rollback) {
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder* th, UINT8 level, LIST_ENTRY* rollback) {
+static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder* th, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     
     if (level > 0) {
         if (!th->tree) {
-            Status = load_tree(Vcb, th->address, r, &th->tree, NULL);
+            Status = load_tree(Vcb, th->address, r, &th->tree, NULL, NULL);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("load_tree(%llx) returned %08x\n", th->address, Status);
@@ -3436,7 +3374,7 @@ static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder*
                 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
                 
                 if (!td->ignore) {
-                    Status = remove_root_extents(Vcb, r, &td->treeholder, th->tree->header.level - 1, rollback);
+                    Status = remove_root_extents(Vcb, r, &td->treeholder, th->tree->header.level - 1, Irp, rollback);
                     
                     if (!NT_SUCCESS(Status)) {
                         ERR("remove_root_extents returned %08x\n", Status);
@@ -3450,7 +3388,7 @@ static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder*
     }
     
     if (!th->tree || th->tree->has_address) {
-        Status = reduce_tree_extent(Vcb, th->address, NULL, rollback);
+        Status = reduce_tree_extent(Vcb, th->address, NULL, Irp, rollback);
         
         if (!NT_SUCCESS(Status)) {
             ERR("reduce_tree_extent(%llx) returned %08x\n", th->address, Status);
@@ -3461,12 +3399,12 @@ static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder*
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS drop_root(device_extension* Vcb, root* r, LIST_ENTRY* rollback) {
+static NTSTATUS drop_root(device_extension* Vcb, root* r, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
     
-    Status = remove_root_extents(Vcb, r, &r->treeholder, r->root_item.root_level, rollback);
+    Status = remove_root_extents(Vcb, r, &r->treeholder, r->root_item.root_level, Irp, rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("remove_root_extents returned %08x\n", Status);
         return Status;
@@ -3479,7 +3417,7 @@ static NTSTATUS drop_root(device_extension* Vcb, root* r, LIST_ENTRY* rollback)
         RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
         
         if (searchkey.obj_id != 0 || searchkey.offset != 0) {
-            Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE);
+            Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp);
             if (!NT_SUCCESS(Status)) {
                 WARN("find_item returned %08x\n", Status);
             } else {
@@ -3497,7 +3435,7 @@ static NTSTATUS drop_root(device_extension* Vcb, root* r, LIST_ENTRY* rollback)
     searchkey.obj_type = TYPE_ROOT_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("find_item returned %08x\n", Status);
         return Status;
@@ -3515,7 +3453,7 @@ static NTSTATUS drop_root(device_extension* Vcb, root* r, LIST_ENTRY* rollback)
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS drop_roots(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS drop_roots(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY *le = Vcb->drop_roots.Flink, *le2;
     NTSTATUS Status;
     
@@ -3524,7 +3462,7 @@ static NTSTATUS drop_roots(device_extension* Vcb, LIST_ENTRY* rollback) {
         
         le2 = le->Flink;
         
-        Status = drop_root(Vcb, r, rollback);
+        Status = drop_root(Vcb, r, Irp, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("drop_root(%llx) returned %08x\n", r->id, Status);
             return Status;
@@ -3536,7 +3474,7 @@ static NTSTATUS drop_roots(device_extension* Vcb, LIST_ENTRY* rollback) {
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback) {
+static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, PIRP Irp, LIST_ENTRY* rollback) {
     CHUNK_ITEM* ci;
     CHUNK_ITEM_STRIPE* cis;
     BLOCK_GROUP_ITEM* bgi;
@@ -3551,7 +3489,7 @@ static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollba
     
     RtlCopyMemory(ci, c->chunk_item, c->size);
     
-    if (!insert_tree_item(Vcb, Vcb->chunk_root, 0x100, TYPE_CHUNK_ITEM, c->offset, ci, c->size, NULL, rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->chunk_root, 0x100, TYPE_CHUNK_ITEM, c->offset, ci, c->size, NULL, Irp, rollback)) {
         ERR("insert_tree_item failed\n");
         ExFreePool(ci);
         return STATUS_INTERNAL_ERROR;
@@ -3577,7 +3515,7 @@ static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollba
     bgi->chunk_tree = 0x100;
     bgi->flags = c->chunk_item->type;
     
-    if (!insert_tree_item(Vcb, Vcb->extent_root, c->offset, TYPE_BLOCK_GROUP_ITEM, c->chunk_item->size, bgi, sizeof(BLOCK_GROUP_ITEM), NULL, rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->extent_root, c->offset, TYPE_BLOCK_GROUP_ITEM, c->chunk_item->size, bgi, sizeof(BLOCK_GROUP_ITEM), NULL, Irp, rollback)) {
         ERR("insert_tree_item failed\n");
         ExFreePool(bgi);
         return STATUS_INSUFFICIENT_RESOURCES;
@@ -3609,14 +3547,14 @@ static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollba
         de->length = c->chunk_item->size / factor;
         de->chunktree_uuid = Vcb->chunk_root->treeholder.tree->header.chunk_tree_uuid;
 
-        if (!insert_tree_item(Vcb, Vcb->dev_root, c->devices[i]->devitem.dev_id, TYPE_DEV_EXTENT, cis[i].offset, de, sizeof(DEV_EXTENT), NULL, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->dev_root, c->devices[i]->devitem.dev_id, TYPE_DEV_EXTENT, cis[i].offset, de, sizeof(DEV_EXTENT), NULL, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             ExFreePool(de);
             return STATUS_INTERNAL_ERROR;
         }
         
         // FIXME - no point in calling this twice for the same device
-        Status = update_dev_item(Vcb, c->devices[i], rollback);
+        Status = update_dev_item(Vcb, c->devices[i], Irp, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("update_dev_item returned %08x\n", Status);
             return Status;
@@ -3651,7 +3589,7 @@ static void remove_from_bootstrap(device_extension* Vcb, UINT64 obj_id, UINT8 ob
     }
 }
 
-static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback) {
+static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
@@ -3664,7 +3602,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback
     if (c->cache) {
         c->cache->deleted = TRUE;
         
-        flush_fcb(c->cache, TRUE, rollback);
+        flush_fcb(c->cache, TRUE, Irp, rollback);
         
         free_fcb(c->cache);
         
@@ -3672,7 +3610,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback
         searchkey.obj_type = 0;
         searchkey.offset = c->offset;
         
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -3698,7 +3636,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback
             searchkey.obj_type = TYPE_DEV_EXTENT;
             searchkey.offset = cis[i].offset;
             
-            Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE);
+            Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("error - find_item returned %08x\n", Status);
                 return Status;
@@ -3734,7 +3672,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback
             searchkey.obj_type = TYPE_DEV_ITEM;
             searchkey.offset = c->devices[i]->devitem.dev_id;
             
-            Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
+            Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("error - find_item returned %08x\n", Status);
                 return Status;
@@ -3755,7 +3693,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback
             
             RtlCopyMemory(di, &c->devices[i]->devitem, sizeof(DEV_ITEM));
             
-            if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, c->devices[i]->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, c->devices[i]->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 return STATUS_INTERNAL_ERROR;
             }
@@ -3773,7 +3711,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback
         searchkey.obj_type = TYPE_CHUNK_ITEM;
         searchkey.offset = c->offset;
         
-        Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -3789,7 +3727,7 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback
         searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
         searchkey.offset = 0xffffffffffffffff;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -3826,16 +3764,15 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* rollback
         ExFreePool(s);
     }
     
-    ExDeleteResourceLite(&c->nonpaged->lock);
-    ExDeleteResourceLite(&c->nonpaged->changed_extents_lock);
-    ExFreePool(c->nonpaged);
+    ExDeleteResourceLite(&c->lock);
+    ExDeleteResourceLite(&c->changed_extents_lock);
 
     ExFreePool(c);
     
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS update_chunks(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY *le = Vcb->chunks_changed.Flink, *le2;
     NTSTATUS Status;
     UINT64 used_minus_cache;
@@ -3849,7 +3786,7 @@ static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY* rollback) {
         
         le2 = le->Flink;
         
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
         used_minus_cache = c->used;
         
@@ -3887,25 +3824,25 @@ static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY* rollback) {
         }
         
         if (used_minus_cache == 0) {
-            Status = drop_chunk(Vcb, c, rollback);
+            Status = drop_chunk(Vcb, c, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("drop_chunk returned %08x\n", Status);
-                ExReleaseResourceLite(&c->nonpaged->lock);
+                ExReleaseResourceLite(&c->lock);
                 ExReleaseResourceLite(&Vcb->chunk_lock);
                 return Status;
             }
         } else if (c->created) {
-            Status = create_chunk(Vcb, c, rollback);
+            Status = create_chunk(Vcb, c, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("create_chunk returned %08x\n", Status);
-                ExReleaseResourceLite(&c->nonpaged->lock);
+                ExReleaseResourceLite(&c->lock);
                 ExReleaseResourceLite(&Vcb->chunk_lock);
                 return Status;
             }
         }
         
         if (used_minus_cache > 0)
-            ExReleaseResourceLite(&c->nonpaged->lock);
+            ExReleaseResourceLite(&c->lock);
 
         le = le2;
     }
@@ -3915,10 +3852,10 @@ static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY* rollback) {
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, LIST_ENTRY* rollback) {
+static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
-    ULONG xasize;
+    ULONG xasize, maxlen;
     DIR_ITEM* xa;
     NTSTATUS Status;
     
@@ -3928,15 +3865,14 @@ static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 in
     searchkey.obj_type = TYPE_XATTR_ITEM;
     searchkey.offset = crc32;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
     }
     
     xasize = sizeof(DIR_ITEM) - 1 + (ULONG)strlen(name) + datalen;
-    
-    // FIXME - make sure xasize not too big
+    maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
     
     if (!keycmp(&tp.item->key, &searchkey)) { // key exists
         UINT8* newdata;
@@ -3961,6 +3897,12 @@ static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 in
                     UINT64 pos;
                     
                     // replace
+                    
+                    if (tp.item->size + xasize - oldxasize > maxlen) {
+                        ERR("DIR_ITEM would be over maximum size (%u + %u - %u > %u)\n", tp.item->size, xasize, oldxasize, maxlen);
+                        return STATUS_INTERNAL_ERROR;
+                    }
+                    
                     newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize - oldxasize, ALLOC_TAG);
                     if (!newdata) {
                         ERR("out of memory\n");
@@ -3989,13 +3931,19 @@ static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 in
                     RtlCopyMemory(xa->name + strlen(name), data, datalen);
                     
                     delete_tree_item(Vcb, &tp, rollback);
-                    insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize - oldxasize, NULL, rollback);
+                    insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize - oldxasize, NULL, Irp, rollback);
                     
                     break;
                 }
                 
-                if (xa->m + xa->n >= size) { // FIXME - test this works
+                if ((UINT8*)xa - (UINT8*)tp.item->data + oldxasize >= size) {
                     // not found, add to end of data
+                    
+                    if (tp.item->size + xasize > maxlen) {
+                        ERR("DIR_ITEM would be over maximum size (%u + %u > %u)\n", tp.item->size, xasize, maxlen);
+                        return STATUS_INTERNAL_ERROR;
+                    }
+                    
                     newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize, ALLOC_TAG);
                     if (!newdata) {
                         ERR("out of memory\n");
@@ -4016,7 +3964,7 @@ static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 in
                     RtlCopyMemory(xa->name + strlen(name), data, datalen);
                     
                     delete_tree_item(Vcb, &tp, rollback);
-                    insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize, NULL, rollback);
+                    insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize, NULL, Irp, rollback);
                     
                     break;
                 } else {
@@ -4026,7 +3974,10 @@ static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 in
             }
         }
     } else {
-        // add new DIR_ITEM struct
+        if (xasize > maxlen) {
+            ERR("DIR_ITEM would be over maximum size (%u > %u)\n", xasize, maxlen);
+            return STATUS_INTERNAL_ERROR;
+        }
         
         xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG);
         if (!xa) {
@@ -4044,13 +3995,13 @@ static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 in
         RtlCopyMemory(xa->name, name, strlen(name));
         RtlCopyMemory(xa->name + strlen(name), data, datalen);
         
-        insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, NULL, rollback);
+        insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, NULL, Irp, rollback);
     }
     
     return STATUS_SUCCESS;
 }
 
-static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, LIST_ENTRY* rollback) {
+static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     DIR_ITEM* xa;
@@ -4062,7 +4013,7 @@ static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 ino
     searchkey.obj_type = TYPE_XATTR_ITEM;
     searchkey.offset = crc32;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return FALSE;
@@ -4120,7 +4071,7 @@ static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 ino
                     if ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data < tp.item->size)
                         RtlCopyMemory(dioff, &xa->name[xa->n+xa->m], tp.item->size - ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data));
                     
-                    insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, newsize, NULL, rollback);
+                    insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, newsize, NULL, Irp, rollback);
                     
                         
                     return TRUE;
@@ -4143,7 +4094,40 @@ static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 ino
     }
 }
 
-void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
+static NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, PIRP Irp, LIST_ENTRY* rollback) {
+    EXTENT_DATA* ed;
+    EXTENT_DATA2* ed2;
+    
+    TRACE("((%llx, %llx), %llx, %llx)\n", fcb->subvol->id, fcb->inode, start, length);
+    
+    ed = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
+    if (!ed) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    ed->generation = fcb->Vcb->superblock.generation;
+    ed->decoded_size = length;
+    ed->compression = BTRFS_COMPRESSION_NONE;
+    ed->encryption = BTRFS_ENCRYPTION_NONE;
+    ed->encoding = BTRFS_ENCODING_NONE;
+    ed->type = EXTENT_TYPE_REGULAR;
+    
+    ed2 = (EXTENT_DATA2*)ed->data;
+    ed2->address = 0;
+    ed2->size = 0;
+    ed2->offset = 0;
+    ed2->num_bytes = length;
+    
+    if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, Irp, rollback)) {
+        ERR("insert_tree_item failed\n");
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+void flush_fcb(fcb* fcb, BOOL cache, PIRP Irp, LIST_ENTRY* rollback) {
     traverse_ptr tp;
     KEY searchkey;
     NTSTATUS Status;
@@ -4169,9 +4153,9 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
     
     if (fcb->ads) {
         if (fcb->deleted)
-            delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, rollback);
+            delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, Irp, rollback);
         else {
-            Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)fcb->adsdata.Buffer, fcb->adsdata.Length, rollback);
+            Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)fcb->adsdata.Buffer, fcb->adsdata.Length, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("set_xattr returned %08x\n", Status);
                 goto end;
@@ -4188,7 +4172,8 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
         BOOL b;
         traverse_ptr next_tp;
         LIST_ENTRY* le;
-        BOOL prealloc = FALSE;
+        BOOL prealloc = FALSE, extents_inline = FALSE;
+        UINT64 last_end;
         
         // delete ignored extent items
         le = fcb->extents.Flink;
@@ -4217,10 +4202,8 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
                     EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
                     EXTENT_DATA2* ned2 = (EXTENT_DATA2*)nextext->data->data;
                     
-                    if (ed2->address == 0 && ned2->address == 0 && ed2->size == 0 && ned2->size == 0) {
-                        // FIXME - merge together adjacent sparse extents
-                    } else if (ed2->address == ned2->address && ed2->size == ned2->size && nextext->offset == ext->offset + ed2->num_bytes &&
-                        ned2->offset == ed2->offset + ed2->num_bytes) {
+                    if (ed2->size != 0 && ed2->address == ned2->address && ed2->size == ned2->size &&
+                        nextext->offset == ext->offset + ed2->num_bytes && ned2->offset == ed2->offset + ed2->num_bytes) {
                         chunk* c;
                     
                         ext->data->generation = fcb->Vcb->superblock.generation;
@@ -4236,7 +4219,7 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
                             ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
                         } else {
                             Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, -1,
-                                                               fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
+                                                               fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
                             if (!NT_SUCCESS(Status)) {
                                 ERR("update_changed_extent_ref returned %08x\n", Status);
                                 goto end;
@@ -4257,7 +4240,7 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
         searchkey.obj_type = TYPE_EXTENT_DATA;
         searchkey.offset = 0;
         
-        Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+        Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             goto end;
@@ -4267,7 +4250,7 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
             if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
                 delete_tree_item(fcb->Vcb, &tp, rollback);
             
-            b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+            b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp);
             
             if (b) {
                 tp = next_tp;
@@ -4277,40 +4260,74 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
             }
         } while (b);
         
-        // add new EXTENT_DATAs
-        
-        le = fcb->extents.Flink;
-        while (le != &fcb->extents) {
-            extent* ext = CONTAINING_RECORD(le, extent, list_entry);
-            EXTENT_DATA* ed;
+        if (!fcb->deleted) {
+            // add new EXTENT_DATAs
+            
+            last_end = 0;
+            
+            le = fcb->extents.Flink;
+            while (le != &fcb->extents) {
+                extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+                EXTENT_DATA* ed;
                 
-            ed = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
-            if (!ed) {
-                ERR("out of memory\n");
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto end;
+                if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES) && ext->offset > last_end) {
+                    Status = insert_sparse_extent(fcb, last_end, ext->offset - last_end, Irp, rollback);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("insert_sparse_extent returned %08x\n", Status);
+                        goto end;
+                    }
+                }
+                    
+                ed = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+                if (!ed) {
+                    ERR("out of memory\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto end;
+                }
+                
+                RtlCopyMemory(ed, ext->data, ext->datalen);
+                
+                if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, ext->offset, ed, ext->datalen, NULL, Irp, rollback)) {
+                    ERR("insert_tree_item failed\n");
+                    goto end;
+                }
+                
+                if (ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_PREALLOC)
+                    prealloc = TRUE;
+                
+                if (ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_INLINE)
+                    extents_inline = TRUE;
+                
+                if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES)) {
+                    if (ed->type == EXTENT_TYPE_INLINE)
+                        last_end = ext->offset + ed->decoded_size;
+                    else {
+                        EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+                        
+                        last_end = ext->offset + ed2->num_bytes;
+                    }
+                }
+                
+                le = le->Flink;
             }
             
-            RtlCopyMemory(ed, ext->data, ext->datalen);
-            
-            if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, ext->offset, ed, ext->datalen, NULL, rollback)) {
-                ERR("insert_tree_item failed\n");
-                goto end;
+            if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES) && !extents_inline &&
+                sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) > last_end) {
+                Status = insert_sparse_extent(fcb, last_end, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) - last_end, Irp, rollback);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("insert_sparse_extent returned %08x\n", Status);
+                    goto end;
+                }
             }
             
-            if (!prealloc && ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_PREALLOC)
-                prealloc = TRUE;
+            // update prealloc flag in INODE_ITEM
             
-            le = le->Flink;
+            if (!prealloc)
+                fcb->inode_item.flags &= ~BTRFS_INODE_PREALLOC;
+            else
+                fcb->inode_item.flags |= BTRFS_INODE_PREALLOC;
         }
         
-        // update prealloc flag in INODE_ITEM
-        
-        if (!prealloc)
-            fcb->inode_item.flags &= ~BTRFS_INODE_PREALLOC;
-        else
-            fcb->inode_item.flags |= BTRFS_INODE_PREALLOC;
-        
         fcb->extents_changed = FALSE;
     }
     
@@ -4319,7 +4336,7 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
         searchkey.obj_type = TYPE_INODE_ITEM;
         searchkey.offset = 0xffffffffffffffff;
         
-        Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+        Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             goto end;
@@ -4335,7 +4352,7 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
                 
                 RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
                 
-                if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) {
+                if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp, rollback)) {
                     ERR("insert_tree_item failed\n");
                     goto end;
                 }
@@ -4362,7 +4379,7 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
             searchkey.obj_type = TYPE_INODE_ITEM;
             searchkey.offset = ii_offset;
             
-            Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+            Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("error - find_item returned %08x\n", Status);
                 goto end;
@@ -4395,13 +4412,13 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
         searchkey.obj_type = TYPE_XATTR_ITEM;
         searchkey.offset = 0;
         
-        Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+        Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             goto end;
         }
     
-        while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE)) {
+        while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE, Irp)) {
             tp = tp2;
             
             if (tp.item->key.obj_id == fcb->inode) {
@@ -4426,14 +4443,14 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
         
         RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
         
-        if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, ii_offset, ii, sizeof(INODE_ITEM), NULL, rollback)) {
+        if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, ii_offset, ii, sizeof(INODE_ITEM), NULL, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             goto end;
         }
     }
     
     if (fcb->sd_dirty) {
-        Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), rollback);
+        Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), Irp, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("set_xattr returned %08x\n", Status);
         }
@@ -4448,13 +4465,13 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
             TRACE("inserting new DOSATTRIB xattr\n");
             sprintf(val, "0x%lx", fcb->atts);
         
-            Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback);
+            Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("set_xattr returned %08x\n", Status);
                 goto end;
             }
         } else
-            delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, rollback);
+            delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, Irp, rollback);
         
         fcb->atts_changed = FALSE;
         fcb->atts_deleted = FALSE;
@@ -4462,13 +4479,13 @@ void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback) {
     
     if (fcb->reparse_xattr_changed) {
         if (fcb->reparse_xattr.Buffer && fcb->reparse_xattr.Length > 0) {
-            Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, (UINT8*)fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length, rollback);
+            Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, (UINT8*)fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("set_xattr returned %08x\n", Status);
                 goto end;
             }
         } else
-            delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, rollback);
+            delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, Irp, rollback);
         
         fcb->reparse_xattr_changed = FALSE;
     }
@@ -4480,7 +4497,7 @@ end:
     return;
 }
 
-static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -4489,7 +4506,7 @@ static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 p
     searchkey.obj_type = TYPE_ROOT_REF;
     searchkey.offset = subvolid;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -4543,7 +4560,7 @@ static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 p
                         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);
+                        insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newrr, newlen, NULL, Irp, rollback);
                     }
                     
                     break;
@@ -4564,7 +4581,7 @@ static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 p
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr, LIST_ENTRY* rollback) {
+static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -4573,7 +4590,7 @@ static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 pars
     searchkey.obj_type = TYPE_ROOT_REF;
     searchkey.offset = subvolid;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -4597,13 +4614,13 @@ static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 pars
         
         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)) {
+        if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL, Irp, rollback)) {
             ERR("error - failed to insert item\n");
             ExFreePool(rr2);
             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)) {
+        if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr, sizeof(ROOT_REF) - 1 + rr->n, NULL, Irp, rollback)) {
             ERR("error - failed to insert item\n");
             ExFreePool(rr);
             return STATUS_INTERNAL_ERROR;
@@ -4613,7 +4630,7 @@ static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 pars
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, LIST_ENTRY* rollback) {
+static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     UINT8* data;
@@ -4624,7 +4641,7 @@ static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvol
     searchkey.obj_type = TYPE_ROOT_REF;
     searchkey.offset = subvolid;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -4648,9 +4665,13 @@ static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvol
     searchkey.obj_type = TYPE_ROOT_BACKREF;
     searchkey.offset = parsubvolid;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
+        
+        if (datalen > 0)
+            ExFreePool(data);
+        
         return Status;
     }
     
@@ -4658,7 +4679,7 @@ static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvol
         delete_tree_item(Vcb, &tp, rollback);
     
     if (datalen > 0) {
-        if (!insert_tree_item(Vcb, Vcb->root_root, subvolid, TYPE_ROOT_BACKREF, parsubvolid, data, datalen, NULL, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->root_root, subvolid, TYPE_ROOT_BACKREF, parsubvolid, data, datalen, NULL, Irp, rollback)) {
             ERR("error - failed to insert item\n");
             ExFreePool(data);
             return STATUS_INTERNAL_ERROR;
@@ -4668,7 +4689,7 @@ static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvol
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) {
+static NTSTATUS flush_fileref(file_ref* fileref, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     
     // if fileref created and then immediately deleted, do nothing
@@ -4720,20 +4741,20 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) {
         
         RtlCopyMemory(di2, di, disize);
               
-        if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di, disize, NULL, rollback)) {
+        if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di, disize, NULL, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             Status = STATUS_INTERNAL_ERROR;
             return Status;
         }
         
-        Status = add_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di2, disize, rollback);
+        Status = add_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di2, disize, Irp, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("add_dir_item returned %08x\n", Status);
             return Status;
         }
         
         if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
-            Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, rollback);
+            Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("add_inode_ref returned %08x\n", Status);
                 return Status;
@@ -4755,13 +4776,13 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) {
             rr->n = fileref->utf8.Length;
             RtlCopyMemory(rr->name, fileref->utf8.Buffer, fileref->utf8.Length);
             
-            Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, rollback);
+            Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("add_root_ref returned %08x\n", Status);
                 return Status;
             }
             
-            Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rollback);
+            Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("update_root_backref returned %08x\n", Status);
                 return Status;
@@ -4786,7 +4807,7 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) {
         
         // delete DIR_ITEM (0x54)
         
-        Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, name, rollback);
+        Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, name, Irp, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("delete_dir_item returned %08x\n", Status);
             return Status;
@@ -4795,18 +4816,18 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) {
         if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
             // delete INODE_REF (0xc)
             
-            Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, name, rollback);
+            Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, name, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_inode_ref returned %08x\n", Status);
                 return Status;
             }
         } else { // subvolume
-            Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, name, rollback);
+            Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, name, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_root_ref returned %08x\n", Status);
             }
             
-            Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rollback);
+            Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("update_root_backref returned %08x\n", Status);
                 return Status;
@@ -4819,7 +4840,7 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) {
         searchkey.obj_type = TYPE_DIR_INDEX;
         searchkey.offset = fileref->index;
 
-        Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE);        
+        Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             Status = STATUS_INTERNAL_ERROR;
@@ -4835,145 +4856,150 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) {
             ExFreePool(fileref->oldutf8.Buffer);
             fileref->oldutf8.Buffer = NULL;
         }
-    } else { // rename
-        if (fileref->oldutf8.Buffer) {
-            UINT32 crc32, oldcrc32;
-            ULONG disize;
-            DIR_ITEM *di, *di2;
-            KEY searchkey;
-            traverse_ptr tp;
-            
-            crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length);
+    } else { // rename or change type
+        PANSI_STRING oldutf8 = fileref->oldutf8.Buffer ? &fileref->oldutf8 : &fileref->utf8;
+        UINT32 crc32, oldcrc32;
+        ULONG disize;
+        DIR_ITEM *di, *di2;
+        KEY searchkey;
+        traverse_ptr tp;
+        
+        crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length);
+        
+        if (!fileref->oldutf8.Buffer)
+            oldcrc32 = crc32;
+        else
             oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->oldutf8.Buffer, fileref->oldutf8.Length);
 
-            // delete DIR_ITEM (0x54)
+        // delete DIR_ITEM (0x54)
+        
+        Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, oldcrc32, oldutf8, Irp, rollback);
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_dir_item returned %08x\n", Status);
+            return Status;
+        }
+        
+        // add DIR_ITEM (0x54)
+        
+        disize = sizeof(DIR_ITEM) - 1 + fileref->utf8.Length;
+        di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+        if (!di) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
+        if (!di2) {
+            ERR("out of memory\n");
+            ExFreePool(di);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
+            di->key.obj_id = fileref->fcb->inode;
+            di->key.obj_type = TYPE_INODE_ITEM;
+            di->key.offset = 0;
+        } else { // subvolume
+            di->key.obj_id = fileref->fcb->subvol->id;
+            di->key.obj_type = TYPE_ROOT_ITEM;
+            di->key.offset = 0xffffffffffffffff;
+        }
+        
+        di->transid = fileref->fcb->Vcb->superblock.generation;
+        di->m = 0;
+        di->n = (UINT16)fileref->utf8.Length;
+        di->type = fileref->fcb->type;
+        RtlCopyMemory(di->name, fileref->utf8.Buffer, fileref->utf8.Length);
+        
+        RtlCopyMemory(di2, di, disize);
+        
+        Status = add_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di, disize, Irp, rollback);
+        if (!NT_SUCCESS(Status)) {
+            ERR("add_dir_item returned %08x\n", Status);
+            return Status;
+        }
+        
+        if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
+            // delete INODE_REF (0xc)
             
-            Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, oldcrc32, &fileref->oldutf8, rollback);
+            Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, oldutf8, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
-                ERR("delete_dir_item returned %08x\n", Status);
+                ERR("delete_inode_ref returned %08x\n", Status);
                 return Status;
             }
             
-            // add DIR_ITEM (0x54)
+            // add INODE_REF (0xc)
             
-            disize = sizeof(DIR_ITEM) - 1 + fileref->utf8.Length;
-            di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
-            if (!di) {
-                ERR("out of memory\n");
-                return STATUS_INSUFFICIENT_RESOURCES;
+            Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, Irp, rollback);
+            if (!NT_SUCCESS(Status)) {
+                ERR("add_inode_ref returned %08x\n", Status);
+                return Status;
             }
+        } else { // subvolume
+            ULONG rrlen;
+            ROOT_REF* rr;
             
-            di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
-            if (!di2) {
-                ERR("out of memory\n");
-                ExFreePool(di);
-                return STATUS_INSUFFICIENT_RESOURCES;
-            }
+            // FIXME - make sure this works with duff subvols within snapshots
             
-            if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
-                di->key.obj_id = fileref->fcb->inode;
-                di->key.obj_type = TYPE_INODE_ITEM;
-                di->key.offset = 0;
-            } else { // subvolume
-                di->key.obj_id = fileref->fcb->subvol->id;
-                di->key.obj_type = TYPE_ROOT_ITEM;
-                di->key.offset = 0xffffffffffffffff;
+            Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, oldutf8, Irp, rollback);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_root_ref returned %08x\n", Status);
             }
             
-            di->transid = fileref->fcb->Vcb->superblock.generation;
-            di->m = 0;
-            di->n = (UINT16)fileref->utf8.Length;
-            di->type = fileref->fcb->type;
-            RtlCopyMemory(di->name, fileref->utf8.Buffer, fileref->utf8.Length);
+            rrlen = sizeof(ROOT_REF) - 1 + fileref->utf8.Length;
+            
+            rr = ExAllocatePoolWithTag(PagedPool, rrlen, ALLOC_TAG);
+            if (!rr) {
+                ERR("out of memory\n");
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
             
-            RtlCopyMemory(di2, di, disize);
+            rr->dir = fileref->parent->fcb->inode;
+            rr->index = fileref->index;
+            rr->n = fileref->utf8.Length;
+            RtlCopyMemory(rr->name, fileref->utf8.Buffer, fileref->utf8.Length);
             
-            Status = add_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di, disize, rollback);
+            Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
-                ERR("add_dir_item returned %08x\n", Status);
+                ERR("add_root_ref returned %08x\n", Status);
                 return Status;
             }
             
-            if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
-                // delete INODE_REF (0xc)
-                
-                Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, &fileref->oldutf8, rollback);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("delete_inode_ref returned %08x\n", Status);
-                    return Status;
-                }
-                
-                // add INODE_REF (0xc)
-                
-                Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, rollback);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("add_inode_ref returned %08x\n", Status);
-                    return Status;
-                }
-            } else { // subvolume
-                ULONG rrlen;
-                ROOT_REF* rr;
-                
-                // FIXME - make sure this works with duff subvols within snapshots
-                
-                Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, &fileref->oldutf8, rollback);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("delete_root_ref returned %08x\n", Status);
-                }
-                
-                rrlen = sizeof(ROOT_REF) - 1 + fileref->utf8.Length;
-                
-                rr = ExAllocatePoolWithTag(PagedPool, rrlen, ALLOC_TAG);
-                if (!rr) {
-                    ERR("out of memory\n");
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-                
-                rr->dir = fileref->parent->fcb->inode;
-                rr->index = fileref->index;
-                rr->n = fileref->utf8.Length;
-                RtlCopyMemory(rr->name, fileref->utf8.Buffer, fileref->utf8.Length);
-                
-                Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, rollback);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("add_root_ref returned %08x\n", Status);
-                    return Status;
-                }
-                
-                Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rollback);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("update_root_backref returned %08x\n", Status);
-                    return Status;
-                }
-            }
-            
-            // delete DIR_INDEX (0x60)
-            
-            searchkey.obj_id = fileref->parent->fcb->inode;
-            searchkey.obj_type = TYPE_DIR_INDEX;
-            searchkey.offset = fileref->index;
-            
-            Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE);
+            Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
-                ERR("error - find_item returned %08x\n", Status);
-                Status = STATUS_INTERNAL_ERROR;
-                return Status;
-            }
-            
-            if (!keycmp(&searchkey, &tp.item->key)) {
-                delete_tree_item(fileref->fcb->Vcb, &tp, rollback);
-                TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-            } else
-                WARN("could not find (%llx,%x,%llx) in subvol %llx\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, fileref->fcb->subvol->id);
-            
-            // add DIR_INDEX (0x60)
-            
-            if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di2, disize, NULL, rollback)) {
-                ERR("insert_tree_item failed\n");
-                Status = STATUS_INTERNAL_ERROR;
+                ERR("update_root_backref returned %08x\n", Status);
                 return Status;
             }
+        }
+        
+        // delete DIR_INDEX (0x60)
+        
+        searchkey.obj_id = fileref->parent->fcb->inode;
+        searchkey.obj_type = TYPE_DIR_INDEX;
+        searchkey.offset = fileref->index;
+        
+        Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("error - find_item returned %08x\n", Status);
+            Status = STATUS_INTERNAL_ERROR;
+            return Status;
+        }
+        
+        if (!keycmp(&searchkey, &tp.item->key)) {
+            delete_tree_item(fileref->fcb->Vcb, &tp, rollback);
+            TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+        } else
+            WARN("could not find (%llx,%x,%llx) in subvol %llx\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, fileref->fcb->subvol->id);
+        
+        // add DIR_INDEX (0x60)
+        
+        if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di2, disize, NULL, Irp, rollback)) {
+            ERR("insert_tree_item failed\n");
+            Status = STATUS_INTERNAL_ERROR;
+            return Status;
+        }
 
+        if (fileref->oldutf8.Buffer) {
             ExFreePool(fileref->oldutf8.Buffer);
             fileref->oldutf8.Buffer = NULL;
         }
@@ -4984,7 +5010,7 @@ static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* rollback) {
     return STATUS_SUCCESS;
 }
 
-static void convert_shared_data_refs(device_extension* Vcb, LIST_ENTRY* rollback) {
+static void convert_shared_data_refs(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY* le;
     NTSTATUS Status;
     
@@ -5009,18 +5035,18 @@ static void convert_shared_data_refs(device_extension* Vcb, LIST_ENTRY* rollback
                         TRACE("tree %llx; root %llx, objid %llx, offset %llx, count %x\n",
                               t->header.address, sde->edr.root, sde->edr.objid, sde->edr.offset, sde->edr.count);
                         
-                        Status = increase_extent_refcount_data(Vcb, sde->address, sde->size, sde->edr.root, sde->edr.objid, sde->edr.offset, sde->edr.count, rollback);
+                        Status = increase_extent_refcount_data(Vcb, sde->address, sde->size, sde->edr.root, sde->edr.objid, sde->edr.offset, sde->edr.count, Irp, rollback);
                         
                         if (!NT_SUCCESS(Status))
                             WARN("increase_extent_refcount_data returned %08x\n", Status);
                         
                         if (old) {
-                            Status = decrease_extent_refcount_old(Vcb, sde->address, sde->size, sd->address, rollback);
+                            Status = decrease_extent_refcount_old(Vcb, sde->address, sde->size, sd->address, Irp, rollback);
                             
                             if (!NT_SUCCESS(Status))
                                 WARN("decrease_extent_refcount_old returned %08x\n", Status);
                         } else {
-                            Status = decrease_extent_refcount_shared_data(Vcb, sde->address, sde->size, sd->address, sd->parent, rollback);
+                            Status = decrease_extent_refcount_shared_data(Vcb, sde->address, sde->size, sd->address, sd->parent, Irp, rollback);
                             
                             if (!NT_SUCCESS(Status))
                                 WARN("decrease_extent_refcount_shared_data returned %08x\n", Status);
@@ -5042,7 +5068,82 @@ static void convert_shared_data_refs(device_extension* Vcb, LIST_ENTRY* rollback
     }
 }
 
-NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
+static NTSTATUS add_root_item_to_cache(device_extension* Vcb, UINT64 root, PIRP Irp, LIST_ENTRY* rollback) {
+    KEY searchkey;
+    traverse_ptr tp;
+    NTSTATUS Status;
+    
+    searchkey.obj_id = root;
+    searchkey.obj_type = TYPE_ROOT_ITEM;
+    searchkey.offset = 0xffffffffffffffff;
+    
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
+    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) {
+        ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
+        int3;
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    if (tp.item->size < sizeof(ROOT_ITEM)) { // if not full length, create new entry with new bits zeroed
+        ROOT_ITEM* ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
+        if (!ri) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        if (tp.item->size > 0)
+            RtlCopyMemory(ri, tp.item->data, tp.item->size);
+        
+        RtlZeroMemory(((UINT8*)ri) + tp.item->size, sizeof(ROOT_ITEM) - tp.item->size);
+        
+        delete_tree_item(Vcb, &tp, rollback);
+        
+        if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, Irp, rollback)) {
+            ERR("insert_tree_item failed\n");
+            return STATUS_INTERNAL_ERROR;
+        }
+    } else {
+        tp.tree->write = TRUE;
+    }
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS add_root_items_to_cache(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
+    LIST_ENTRY* le;
+    NTSTATUS Status;
+    
+    le = Vcb->trees.Flink;
+    while (le != &Vcb->trees) {
+        tree* t = CONTAINING_RECORD(le, tree, list_entry);
+        
+        if (t->write && t->root != Vcb->chunk_root && t->root != Vcb->root_root) {
+            Status = add_root_item_to_cache(Vcb, t->root->id, Irp, rollback);
+            if (!NT_SUCCESS(Status)) {
+                ERR("add_root_item_to_cache returned %08x\n", Status);
+                return Status;
+            }
+        }
+        
+        le = le->Flink;
+    }
+    
+    // make sure we always update the extent tree
+    Status = add_root_item_to_cache(Vcb, BTRFS_ROOT_EXTENT, Irp, rollback);
+    if (!NT_SUCCESS(Status)) {
+        ERR("add_root_item_to_cache returned %08x\n", Status);
+        return Status;
+    }
+    
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS STDCALL do_write(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     LIST_ENTRY* le;
     BOOL cache_changed = FALSE;
@@ -5060,11 +5161,32 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
         
         dirt = CONTAINING_RECORD(le, dirty_fileref, list_entry);
         
-        flush_fileref(dirt->fileref, rollback);
+        flush_fileref(dirt->fileref, Irp, rollback);
         free_fileref(dirt->fileref);
         ExFreePool(dirt);
     }
     
+    // We process deleted streams first, so we don't run over our xattr
+    // limit unless we absolutely have to.
+    
+    le = Vcb->dirty_fcbs.Flink;
+    while (le != &Vcb->dirty_fcbs) {
+        dirty_fcb* dirt;
+        LIST_ENTRY* le2 = le->Flink;
+        
+        dirt = CONTAINING_RECORD(le, dirty_fcb, list_entry);
+        
+        if (dirt->fcb->deleted && dirt->fcb->ads) {
+            RemoveEntryList(le);
+            
+            flush_fcb(dirt->fcb, FALSE, Irp, rollback);
+            free_fcb(dirt->fcb);
+            ExFreePool(dirt);
+        }
+        
+        le = le2;
+    }
+    
     le = Vcb->dirty_fcbs.Flink;
     while (le != &Vcb->dirty_fcbs) {
         dirty_fcb* dirt;
@@ -5075,7 +5197,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
         if (dirt->fcb->subvol != Vcb->root_root || dirt->fcb->deleted) {
             RemoveEntryList(le);
             
-            flush_fcb(dirt->fcb, FALSE, rollback);
+            flush_fcb(dirt->fcb, FALSE, Irp, rollback);
             free_fcb(dirt->fcb);
             ExFreePool(dirt);
         }
@@ -5083,16 +5205,16 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
         le = le2;
     }
     
-    convert_shared_data_refs(Vcb, rollback);
+    convert_shared_data_refs(Vcb, Irp, rollback);
     
     ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE);
     if (!IsListEmpty(&Vcb->sector_checksums)) {
-        update_checksum_tree(Vcb, rollback);
+        update_checksum_tree(Vcb, Irp, rollback);
     }
     ExReleaseResourceLite(&Vcb->checksum_lock);
     
     if (!IsListEmpty(&Vcb->drop_roots)) {
-        Status = drop_roots(Vcb, rollback);
+        Status = drop_roots(Vcb, Irp, rollback);
         
         if (!NT_SUCCESS(Status)) {
             ERR("drop_roots returned %08x\n", Status);
@@ -5101,7 +5223,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
     }
     
     if (!IsListEmpty(&Vcb->chunks_changed)) {
-        Status = update_chunks(Vcb, rollback);
+        Status = update_chunks(Vcb, Irp, rollback);
         
         if (!NT_SUCCESS(Status)) {
             ERR("update_chunks returned %08x\n", Status);
@@ -5120,7 +5242,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
         searchkey.obj_type = 0;
         searchkey.offset = 0;
         
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -5129,6 +5251,12 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
         Vcb->root_root->treeholder.tree->write = TRUE;
     }
     
+    Status = add_root_items_to_cache(Vcb, Irp, rollback);
+    if (!NT_SUCCESS(Status)) {
+        ERR("add_root_items_to_cache returned %08x\n", Status);
+        return Status;
+    }
+    
     do {
         Status = add_parents(Vcb, rollback);
         if (!NT_SUCCESS(Status)) {
@@ -5136,25 +5264,25 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
             goto end;
         }
         
-        Status = do_splits(Vcb, rollback);
+        Status = do_splits(Vcb, Irp, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("do_splits returned %08x\n", Status);
             goto end;
         }
         
-        Status = allocate_tree_extents(Vcb, rollback);
+        Status = allocate_tree_extents(Vcb, Irp, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("add_parents returned %08x\n", Status);
             goto end;
         }
         
-        Status = update_chunk_usage(Vcb, rollback);
+        Status = update_chunk_usage(Vcb, Irp, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("update_chunk_usage returned %08x\n", Status);
             goto end;
         }
         
-        Status = allocate_cache(Vcb, &cache_changed, rollback);
+        Status = allocate_cache(Vcb, &cache_changed, Irp, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("allocate_cache returned %08x\n", Status);
             goto end;
@@ -5174,13 +5302,13 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
     
     TRACE("trees consistent\n");
     
-    Status = update_root_root(Vcb, rollback);
+    Status = update_root_root(Vcb, Irp, rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("update_root_root returned %08x\n", Status);
         goto end;
     }
     
-    Status = write_trees(Vcb);
+    Status = write_trees(Vcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("write_trees returned %08x\n", Status);
         goto end;
@@ -5188,7 +5316,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
     
     Vcb->superblock.cache_generation = Vcb->superblock.generation;
     
-    Status = write_superblocks(Vcb);
+    Status = write_superblocks(Vcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("write_superblocks returned %08x\n", Status);
         goto end;
@@ -5212,7 +5340,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
         searchkey.obj_type = TYPE_METADATA_ITEM;
         searchkey.offset = 0xffffffffffffffff;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             int3;
@@ -5223,7 +5351,7 @@ NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback) {
             searchkey.obj_type = TYPE_EXTENT_ITEM;
             searchkey.offset = 0xffffffffffffffff;
             
-            Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+            Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("error - find_item returned %08x\n", Status);
                 int3;
@@ -5311,7 +5439,7 @@ static changed_extent* get_changed_extent_item(chunk* c, UINT64 address, UINT64
 }
 
 NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, signed long long count,
-                                   BOOL no_csum, UINT64 new_size) {
+                                   BOOL no_csum, UINT64 new_size, PIRP Irp) {
     LIST_ENTRY* le;
     changed_extent* ce;
     changed_extent_ref* cer;
@@ -5320,7 +5448,7 @@ NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 addre
     traverse_ptr tp;
     UINT64 old_count;
     
-    ExAcquireResourceExclusiveLite(&c->nonpaged->changed_extents_lock, TRUE);
+    ExAcquireResourceExclusiveLite(&c->changed_extents_lock, TRUE);
     
     ce = get_changed_extent_item(c, address, size, no_csum);
     
@@ -5335,7 +5463,7 @@ NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 addre
         searchkey.obj_type = TYPE_EXTENT_ITEM;
         searchkey.offset = 0xffffffffffffffff;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             goto end;
@@ -5384,7 +5512,7 @@ NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 addre
         le = le->Flink;
     }
     
-    old_count = find_extent_data_refcount(Vcb, address, size, root, objid, offset);
+    old_count = find_extent_data_refcount(Vcb, address, size, root, objid, offset, Irp);
     
     if (old_count > 0) {
         cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG);
@@ -5423,12 +5551,12 @@ NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 addre
     Status = STATUS_SUCCESS;
     
 end:
-    ExReleaseResourceLite(&c->nonpaged->changed_extents_lock);
+    ExReleaseResourceLite(&c->changed_extents_lock);
     
     return Status;
 }
 
-NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* rollback) {
+NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     LIST_ENTRY* le;
     
@@ -5460,28 +5588,10 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
             
             len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
             
-            if (ext->offset < end_data && ext->offset + len >= start_data) {
-                if (ed->compression != BTRFS_COMPRESSION_NONE) {
-                    FIXME("FIXME - compression not supported at present\n");
-                    Status = STATUS_NOT_SUPPORTED;
-                    goto end;
-                }
-                
-                if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
-                    WARN("root %llx, inode %llx, extent %llx: encryption not supported (type %x)\n", fcb->subvol->id, fcb->inode, ext->offset, ed->encryption);
-                    Status = STATUS_NOT_SUPPORTED;
-                    goto end;
-                }
-                
-                if (ed->encoding != BTRFS_ENCODING_NONE) {
-                    WARN("other encodings not supported\n");
-                    Status = STATUS_NOT_SUPPORTED;
-                    goto end;
-                }
-                
+            if (ext->offset < end_data && ext->offset + len > start_data) {
                 if (ed->type == EXTENT_TYPE_INLINE) {
                     if (start_data <= ext->offset && end_data >= ext->offset + len) { // remove all
-                        remove_fcb_extent(ext, rollback);
+                        remove_fcb_extent(fcb, ext, rollback);
                         
                         fcb->inode_item.st_blocks -= len;
                     } else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning
@@ -5522,7 +5632,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                         newext->ignore = FALSE;
                         InsertHeadList(&ext->list_entry, &newext->list_entry);
                         
-                        remove_fcb_extent(ext, rollback);
+                        remove_fcb_extent(fcb, ext, rollback);
                         
                         fcb->inode_item.st_blocks -= end_data - ext->offset;
                     } else if (start_data > ext->offset && end_data >= ext->offset + len) { // remove end
@@ -5563,7 +5673,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                         newext->ignore = FALSE;
                         InsertHeadList(&ext->list_entry, &newext->list_entry);
                         
-                        remove_fcb_extent(ext, rollback);
+                        remove_fcb_extent(fcb, ext, rollback);
                         
                         fcb->inode_item.st_blocks -= ext->offset + len - start_data;
                     } else if (start_data > ext->offset && end_data < ext->offset + len) { // remove middle
@@ -5642,7 +5752,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                         InsertHeadList(&ext->list_entry, &newext1->list_entry);
                         InsertHeadList(&newext1->list_entry, &newext2->list_entry);
                         
-                        remove_fcb_extent(ext, rollback);
+                        remove_fcb_extent(fcb, ext, rollback);
                         
                         fcb->inode_item.st_blocks -= end_data - start_data;
                     }
@@ -5659,7 +5769,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                                 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
                             } else {
                                 Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, -1,
-                                                                   fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
+                                                                   fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
                                 if (!NT_SUCCESS(Status)) {
                                     ERR("update_changed_extent_ref returned %08x\n", Status);
                                     goto end;
@@ -5667,7 +5777,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                             }
                         }
                         
-                        remove_fcb_extent(ext, rollback);
+                        remove_fcb_extent(fcb, ext, rollback);
                     } else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning
                         EXTENT_DATA* ned;
                         EXTENT_DATA2* ned2;
@@ -5711,7 +5821,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                         newext->ignore = FALSE;
                         InsertHeadList(&ext->list_entry, &newext->list_entry);
                         
-                        remove_fcb_extent(ext, rollback);
+                        remove_fcb_extent(fcb, ext, rollback);
                     } else if (start_data > ext->offset && end_data >= ext->offset + len) { // remove end
                         EXTENT_DATA* ned;
                         EXTENT_DATA2* ned2;
@@ -5755,7 +5865,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                         newext->ignore = FALSE;
                         InsertHeadList(&ext->list_entry, &newext->list_entry);
                         
-                        remove_fcb_extent(ext, rollback);
+                        remove_fcb_extent(fcb, ext, rollback);
                     } else if (start_data > ext->offset && end_data < ext->offset + len) { // remove middle
                         EXTENT_DATA *neda, *nedb;
                         EXTENT_DATA2 *neda2, *nedb2;
@@ -5772,7 +5882,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                                 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
                             } else {
                                 Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
-                                                                   fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
+                                                                   fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
                                 if (!NT_SUCCESS(Status)) {
                                     ERR("update_changed_extent_ref returned %08x\n", Status);
                                     goto end;
@@ -5855,7 +5965,7 @@ NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
                         InsertHeadList(&ext->list_entry, &newext1->list_entry);
                         InsertHeadList(&newext1->list_entry, &newext2->list_entry);
                         
-                        remove_fcb_extent(ext, rollback);
+                        remove_fcb_extent(fcb, ext, rollback);
                     }
                 }
             }
@@ -5880,7 +5990,7 @@ static NTSTATUS do_write_data(device_extension* Vcb, UINT64 address, void* data,
     changed_sector* sc;
     int i;
     
-    Status = write_data_complete(Vcb, address, data, length, Irp);
+    Status = write_data_complete(Vcb, address, data, length, Irp, NULL);
     if (!NT_SUCCESS(Status)) {
         ERR("write_data returned %08x\n", Status);
         return Status;
@@ -5914,6 +6024,21 @@ static NTSTATUS do_write_data(device_extension* Vcb, UINT64 address, void* data,
     return STATUS_SUCCESS;
 }
 
+static void add_insert_extent_rollback(LIST_ENTRY* rollback, fcb* fcb, extent* ext) {
+    rollback_extent* re;
+    
+    re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG);
+    if (!re) {
+        ERR("out of memory\n");
+        return;
+    }
+    
+    re->fcb = fcb;
+    re->ext = ext;
+    
+    add_rollback(rollback, ROLLBACK_INSERT_EXTENT, re);
+}
+
 static BOOL add_extent_to_fcb(fcb* fcb, UINT64 offset, EXTENT_DATA* ed, ULONG edsize, BOOL unique, LIST_ENTRY* rollback) {
     extent* ext;
     LIST_ENTRY* le;
@@ -5947,15 +6072,27 @@ static BOOL add_extent_to_fcb(fcb* fcb, UINT64 offset, EXTENT_DATA* ed, ULONG ed
     InsertTailList(&fcb->extents, &ext->list_entry);
     
 end:
-    add_rollback(rollback, ROLLBACK_INSERT_EXTENT, ext);
+    add_insert_extent_rollback(rollback, fcb, ext);
 
     return TRUE;
 }
 
-void remove_fcb_extent(extent* ext, LIST_ENTRY* rollback) {
+static void remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback) {
     if (!ext->ignore) {
+        rollback_extent* re;
+        
         ext->ignore = TRUE;
-        add_rollback(rollback, ROLLBACK_DELETE_EXTENT, ext);
+        
+        re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG);
+        if (!re) {
+            ERR("out of memory\n");
+            return;
+        }
+        
+        re->fcb = fcb;
+        re->ext = ext;
+        
+        add_rollback(rollback, ROLLBACK_DELETE_EXTENT, re);
     }
 }
 
@@ -6002,7 +6139,7 @@ static void add_changed_extent_ref(chunk* c, UINT64 address, UINT64 size, UINT64
 }
 
 BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, BOOL prealloc, void* data,
-                         LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+                         LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback, UINT8 compression, UINT64 decoded_size) {
     UINT64 address;
     NTSTATUS Status;
     EXTENT_DATA* ed;
@@ -6048,8 +6185,8 @@ BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start
     }
     
     ed->generation = Vcb->superblock.generation;
-    ed->decoded_size = length;
-    ed->compression = BTRFS_COMPRESSION_NONE;
+    ed->decoded_size = decoded_size;
+    ed->compression = compression;
     ed->encryption = BTRFS_ENCRYPTION_NONE;
     ed->encoding = BTRFS_ENCODING_NONE;
     ed->type = prealloc ? EXTENT_TYPE_PREALLOC : EXTENT_TYPE_REGULAR;
@@ -6058,7 +6195,7 @@ BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start
     ed2->address = address;
     ed2->size = length;
     ed2->offset = 0;
-    ed2->num_bytes = length;
+    ed2->num_bytes = decoded_size;
     
     if (!add_extent_to_fcb(fcb, start_data, ed, edsize, TRUE, rollback)) {
         ERR("add_extent_to_fcb failed\n");
@@ -6069,16 +6206,16 @@ BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start
     increase_chunk_usage(c, length);
     space_list_subtract(Vcb, c, FALSE, address, length, rollback);
     
-    fcb->inode_item.st_blocks += length;
+    fcb->inode_item.st_blocks += decoded_size;
     
     fcb->extents_changed = TRUE;
     mark_fcb_dirty(fcb);
     
-    ExAcquireResourceExclusiveLite(&c->nonpaged->changed_extents_lock, TRUE);
+    ExAcquireResourceExclusiveLite(&c->changed_extents_lock, TRUE);
     
     add_changed_extent_ref(c, address, length, fcb->subvol->id, fcb->inode, start_data, 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM);
     
-    ExReleaseResourceLite(&c->nonpaged->changed_extents_lock);
+    ExReleaseResourceLite(&c->changed_extents_lock);
 
     return TRUE;
 }
@@ -6086,14 +6223,42 @@ BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start
 static BOOL extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
                         LIST_ENTRY* changed_sector_list, extent* ext, chunk* c, PIRP Irp, LIST_ENTRY* rollback) {
     EXTENT_DATA* ed;
-    EXTENT_DATA2* ed2;
+    EXTENT_DATA2 *ed2, *ed2orig;
     extent* newext;
-    UINT64 addr;
+    UINT64 addr, origsize;
     NTSTATUS Status;
+    LIST_ENTRY* le;
     
     TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data,
                                                               length, data, changed_sector_list, ext, c, rollback);
     
+    ed2orig = (EXTENT_DATA2*)ext->data->data;
+    
+    origsize = ed2orig->size;
+    addr = ed2orig->address + ed2orig->size;
+    
+    Status = write_data_complete(Vcb, addr, data, length, Irp, c);
+    if (!NT_SUCCESS(Status)) {
+        ERR("write_data returned %08x\n", Status);
+        return FALSE;
+    }
+    
+    le = fcb->extents.Flink;
+    while (le != &fcb->extents) {
+        extent* ext2 = CONTAINING_RECORD(le, extent, list_entry);
+            
+        if (!ext2->ignore && (ext2->data->type == EXTENT_TYPE_REGULAR || ext2->data->type == EXTENT_TYPE_PREALLOC)) {
+            EXTENT_DATA2* ed2b = (EXTENT_DATA2*)ext2->data->data;
+            
+            if (ed2b->address == ed2orig->address) {
+                ed2b->size = origsize + length;
+                ext2->data->decoded_size = origsize + length;
+            }
+        }
+                
+        le = le->Flink;
+    }
+    
     ed = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
     if (!ed) {
         ERR("out of memory\n");
@@ -6108,32 +6273,21 @@ static BOOL extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
     }
     
     RtlCopyMemory(ed, ext->data, ext->datalen);
-    
-    ed->decoded_size += length;
+
     ed2 = (EXTENT_DATA2*)ed->data;
-    
-    addr = ed2->address + ed2->size;
-     
-    Status = write_data_complete(Vcb, addr, data, length, Irp);
-    if (!NT_SUCCESS(Status)) {
-        ERR("write_data returned %08x\n", Status);
-        ExFreePool(newext);
-        ExFreePool(ed);
-        return FALSE;
-    }
-    
-    ed2->size += length;
-    ed2->num_bytes += length;
+    ed2->offset = ed2orig->offset + ed2orig->num_bytes;
+    ed2->num_bytes = length;
     
     RtlCopyMemory(newext, ext, sizeof(extent));
+    newext->offset = ext->offset + ed2orig->num_bytes;
     newext->data = ed;
     
     InsertHeadList(&ext->list_entry, &newext->list_entry);
     
-    remove_fcb_extent(ext, rollback);
+    add_insert_extent_rollback(rollback, fcb, newext);
     
-    Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size - length, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 0,
-                                       fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
+    Status = update_changed_extent_ref(Vcb, c, ed2orig->address, origsize, fcb->subvol->id, fcb->inode, newext->offset - ed2->offset,
+                                       1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
 
     if (!NT_SUCCESS(Status)) {
         ERR("update_changed_extent_ref returned %08x\n", Status);
@@ -6167,7 +6321,7 @@ static BOOL extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT
     
     increase_chunk_usage(c, length);
       
-    space_list_subtract(Vcb, c, FALSE, addr, length, rollback);
+    space_list_subtract(Vcb, c, FALSE, addr, length, NULL); // no rollback as we don't reverse extending the extent
      
     fcb->inode_item.st_blocks += length;
     
@@ -6202,68 +6356,66 @@ static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data,
         le = le->Flink;
     }
     
-    if (!ext) {
-        WARN("previous EXTENT_DATA not found\n");
-        goto end;
-    }
-    
+    if (!ext)
+        return FALSE;
+
     if (!ext->unique) {
         TRACE("extent was not unique\n");
-        goto end;
+        return FALSE;
     }
     
     ed = ext->data;
     
     if (ext->datalen < sizeof(EXTENT_DATA)) {
         ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
-        goto end;
+        return FALSE;
     }
     
     if (ed->type != EXTENT_TYPE_REGULAR) {
         TRACE("not extending extent which is not EXTENT_TYPE_REGULAR\n");
-        goto end;
+        return FALSE;
     }
     
     ed2 = (EXTENT_DATA2*)ed->data;
     
     if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
         ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
-        goto end;
+        return FALSE;
     }
 
     if (ext->offset + ed2->num_bytes != start_data) {
         TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", ext->offset, ed2->num_bytes, start_data);
-        goto end;
+        return FALSE;
     }
     
     if (ed->compression != BTRFS_COMPRESSION_NONE) {
-        FIXME("FIXME: compression not yet supported\n");
-        goto end;
+        TRACE("not extending a compressed extent\n");
+        return FALSE;
     }
     
     if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
         WARN("encryption not supported\n");
-        goto end;
+        return FALSE;
     }
     
     if (ed->encoding != BTRFS_ENCODING_NONE) {
         WARN("other encodings not supported\n");
-        goto end;
+        return FALSE;
     }
     
     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;
+        return FALSE;
     }
     
     if (ed2->size >= MAX_EXTENT_SIZE) {
         TRACE("extent size was too large to extend (%llx >= %llx)\n", ed2->size, (UINT64)MAX_EXTENT_SIZE);
-        goto end;
+        return FALSE;
     }
     
     c = get_chunk_from_address(Vcb, ed2->address);
     
-    ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+    ExAcquireResourceExclusiveLite(&c->lock, TRUE);
     
     le = c->space.Flink;
     while (le != &c->space) {
@@ -6284,10 +6436,8 @@ static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data,
         le = le->Flink;
     }
     
-    ExReleaseResourceLite(&c->nonpaged->lock);
+    ExReleaseResourceLite(&c->lock);
     
-end:
-        
     return success;
 }
 
@@ -6300,47 +6450,56 @@ static NTSTATUS insert_prealloc_extent(fcb* fcb, UINT64 start, UINT64 length, LI
     UINT64 flags, origlength = length;
 #endif
     NTSTATUS Status;
+    BOOL page_file = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE;
     
     flags = fcb->Vcb->data_flags;
     
     // FIXME - try and maximize contiguous ranges first. If we can't do that,
     // allocate all the free space we find until it's enough.
     
-    ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
-    
     do {
         UINT64 extlen = min(MAX_EXTENT_SIZE, length);
         
+        ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
+        
         le = fcb->Vcb->chunks.Flink;
         while (le != &fcb->Vcb->chunks) {
             c = CONTAINING_RECORD(le, chunk, list_entry);
             
-            ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
             
             if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) {
-                if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, TRUE, NULL, NULL, NULL, rollback)) {
-                    ExReleaseResourceLite(&c->nonpaged->lock);
+                if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen)) {
+                    ExReleaseResourceLite(&c->lock);
+                    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
                     goto cont;
                 }
             }
             
-            ExReleaseResourceLite(&c->nonpaged->lock);
+            ExReleaseResourceLite(&c->lock);
 
             le = le->Flink;
         }
         
-        if ((c = alloc_chunk(fcb->Vcb, flags, rollback))) {
-            ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+        
+        ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+        
+        if ((c = alloc_chunk(fcb->Vcb, flags))) {
+            ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+            
+            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
             
             if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) {
-                if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, TRUE, NULL, NULL, NULL, rollback)) {
-                    ExReleaseResourceLite(&c->nonpaged->lock);
+                if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen)) {
+                    ExReleaseResourceLite(&c->lock);
                     goto cont;
                 }
             }
             
-            ExReleaseResourceLite(&c->nonpaged->lock);
-        }
+            ExReleaseResourceLite(&c->lock);
+        } else
+            ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
         
         WARN("couldn't find any data chunks with %llx bytes free\n", origlength);
         Status = STATUS_DISK_FULL;
@@ -6354,47 +6513,9 @@ cont:
     Status = STATUS_SUCCESS;
     
 end:
-    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
-
     return Status;
 }
 
-NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback) {
-    EXTENT_DATA* ed;
-    EXTENT_DATA2* ed2;
-    
-    TRACE("((%llx, %llx), %llx, %llx)\n", fcb->subvol->id, fcb->inode, start, length);
-    
-    ed = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
-    if (!ed) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    ed->generation = fcb->Vcb->superblock.generation;
-    ed->decoded_size = length;
-    ed->compression = BTRFS_COMPRESSION_NONE;
-    ed->encryption = BTRFS_ENCRYPTION_NONE;
-    ed->encoding = BTRFS_ENCODING_NONE;
-    ed->type = EXTENT_TYPE_REGULAR;
-    
-    ed2 = (EXTENT_DATA2*)ed->data;
-    ed2->address = 0;
-    ed2->size = 0;
-    ed2->offset = 0;
-    ed2->num_bytes = length;
-
-    if (!add_extent_to_fcb(fcb, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), FALSE, rollback)) {
-        ERR("add_extent_to_fcb failed\n");
-        return STATUS_INTERNAL_ERROR;
-    }
-    
-    fcb->extents_changed = TRUE;
-    mark_fcb_dirty(fcb);
-    
-    return STATUS_SUCCESS;
-}
-
 // static void print_tree(tree* t) {
 //     LIST_ENTRY* le = t->itemlist.Flink;
 //     while (le != &t->itemlist) {
@@ -6425,61 +6546,8 @@ NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT6
         }
     }
     
-    // if there is a gap before start_data, plug it with a sparse extent
-    // FIXME - don't do this if no_holes set
-    if (start_data > 0) {
-        NTSTATUS Status;
-        EXTENT_DATA* ed;
-        extent* lastext = NULL;
-        UINT64 len;
-        
-        le = fcb->extents.Flink;
-        while (le != &fcb->extents) {
-            extent* ext = CONTAINING_RECORD(le, extent, list_entry);
-            
-            if (!ext->ignore) {
-                if (ext->offset == start_data) {
-                    lastext = ext;
-                    break;
-                } else if (ext->offset > start_data)
-                    break;
-                
-                lastext = ext;
-            }
-            
-            le = le->Flink;
-        }
-
-        if (lastext && lastext->datalen >= sizeof(EXTENT_DATA)) {
-            EXTENT_DATA2* ed2;
-            
-            ed = lastext->data;
-            ed2 = (EXTENT_DATA2*)ed->data;
-            
-            len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
-        } else
-            ed = NULL;
-        
-        if (!lastext || !ed || lastext->offset + len < start_data) {
-            if (!lastext)
-                Status = insert_sparse_extent(fcb, 0, start_data, rollback);
-            else if (!ed) {
-                ERR("extent at %llx was %u bytes, expected at least %u\n", lastext->offset, lastext->datalen, sizeof(EXTENT_DATA));
-                return STATUS_INTERNAL_ERROR;
-            } else
-                Status = insert_sparse_extent(fcb, lastext->offset + len, start_data - lastext->offset - len, rollback);
-
-            if (!NT_SUCCESS(Status)) {
-                ERR("insert_sparse_extent returned %08x\n", Status);
-                return Status;
-            }
-        }
-    }
-    
     flags = Vcb->data_flags;
     
-    ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE);
-    
     while (written < orig_length) {
         UINT64 newlen = min(length, MAX_EXTENT_SIZE);
         BOOL done = FALSE;
@@ -6487,18 +6555,20 @@ NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT6
         // Rather than necessarily writing the whole extent at once, we deal with it in blocks of 128 MB.
         // First, see if we can write the extent part to an existing chunk.
         
+        ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE);
+        
         le = Vcb->chunks.Flink;
         while (le != &Vcb->chunks) {
             c = CONTAINING_RECORD(le, chunk, list_entry);
             
-            ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
             
             if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen) {
-                if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback)) {
+                if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen)) {
                     written += newlen;
                     
                     if (written == orig_length) {
-                        ExReleaseResourceLite(&c->nonpaged->lock);
+                        ExReleaseResourceLite(&c->lock);
                         ExReleaseResourceLite(&Vcb->chunk_lock);
                         return STATUS_SUCCESS;
                     } else {
@@ -6511,25 +6581,30 @@ NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT6
                 }
             }
             
-            ExReleaseResourceLite(&c->nonpaged->lock);
+            ExReleaseResourceLite(&c->lock);
 
             le = le->Flink;
         }
         
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+        
         if (done) continue;
         
         // Otherwise, see if we can put it in a new chunk.
         
-        if ((c = alloc_chunk(Vcb, flags, rollback))) {
-            ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+        ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+        
+        if ((c = alloc_chunk(Vcb, flags))) {
+            ExReleaseResourceLite(&Vcb->chunk_lock);
+            
+            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
             
             if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen) {
-                if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback)) {
+                if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen)) {
                     written += newlen;
                     
                     if (written == orig_length) {
-                        ExReleaseResourceLite(&c->nonpaged->lock);
-                        ExReleaseResourceLite(&Vcb->chunk_lock);
+                        ExReleaseResourceLite(&c->lock);
                         return STATUS_SUCCESS;
                     } else {
                         done = TRUE;
@@ -6540,8 +6615,9 @@ NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT6
                 }
             }
             
-            ExReleaseResourceLite(&c->nonpaged->lock);
-        }
+            ExReleaseResourceLite(&c->lock);
+        } else
+            ExReleaseResourceLite(&Vcb->chunk_lock);
         
         if (!done) {
             FIXME("FIXME - not enough room to write whole extent part, try to write bits and pieces\n"); // FIXME
@@ -6549,14 +6625,12 @@ NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT6
         }
     }
     
-    ExReleaseResourceLite(&Vcb->chunk_lock);
-    
     WARN("couldn't find any data chunks with %llx bytes free\n", length);
 
     return STATUS_DISK_FULL;
 }
 
-static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) {
+static void update_checksum_tree(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY* le = Vcb->sector_checksums.Flink;
     changed_sector* cs;
     traverse_ptr tp, next_tp;
@@ -6585,7 +6659,7 @@ static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) {
         
         // FIXME - create checksum_root if it doesn't exist at all
         
-        Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) { // tree is completely empty
             // FIXME - do proper check here that tree is empty
             if (!cs->deleted) {
@@ -6597,7 +6671,7 @@ static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) {
                 
                 RtlCopyMemory(checksums, cs->checksums, sizeof(UINT32) * cs->length);
                 
-                if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, cs->ol.key, checksums, sizeof(UINT32) * cs->length, NULL, rollback)) {
+                if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, cs->ol.key, checksums, sizeof(UINT32) * cs->length, NULL, Irp, rollback)) {
                     ERR("insert_tree_item failed\n");
                     ExFreePool(checksums);
                     goto exit;
@@ -6617,7 +6691,7 @@ static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) {
             searchkey.obj_type = TYPE_EXTENT_CSUM;
             searchkey.offset = cs->ol.key + (cs->length * Vcb->superblock.sector_size);
             
-            Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
+            Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("error - find_item returned %08x\n", Status);
                 goto exit;
@@ -6656,7 +6730,7 @@ static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) {
             searchkey.obj_type = TYPE_EXTENT_CSUM;
             searchkey.offset = cs->ol.key;
             
-            Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
+            Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("error - find_item returned %08x\n", Status);
                 goto exit;
@@ -6676,7 +6750,7 @@ static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) {
                     delete_tree_item(Vcb, &tp, rollback);
                 }
                 
-                if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+                if (find_next_item(Vcb, &tp, &next_tp, FALSE, Irp)) {
                     tp = next_tp;
                 } else
                     break;
@@ -6711,7 +6785,7 @@ static void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* rollback) {
                     
                     RtlCopyMemory(data, &checksums[index], sizeof(UINT32) * rl);
                     
-                    if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, startaddr + (index * Vcb->superblock.sector_size), data, sizeof(UINT32) * rl, NULL, rollback)) {
+                    if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, startaddr + (index * Vcb->superblock.sector_size), data, sizeof(UINT32) * rl, NULL, Irp, rollback)) {
                         ERR("insert_tree_item failed\n");
                         ExFreePool(data);
                         ExFreePool(bmparr);
@@ -6752,13 +6826,13 @@ void commit_checksum_changes(device_extension* Vcb, LIST_ENTRY* changed_sector_l
     }
 }
 
-NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback) {
+NTSTATUS truncate_file(fcb* fcb, UINT64 end, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     
     // FIXME - convert into inline extent if short enough
     
     Status = excise_extents(fcb->Vcb, fcb, sector_align(end, fcb->Vcb->superblock.sector_size),
-                            sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), rollback);
+                            sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), Irp, rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("error - excise_extents failed\n");
         return Status;
@@ -6815,7 +6889,7 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR
             oldalloc = ext->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) {
+            if (cur_inline && end > fcb->Vcb->options.max_inline) {
                 LIST_ENTRY changed_sector_list;
                 BOOL nocsum = fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
                 UINT64 origlength, length;
@@ -6846,13 +6920,22 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR
                 
                 fcb->inode_item.st_blocks -= origlength;
                 
-                remove_fcb_extent(ext, rollback);
+                remove_fcb_extent(fcb, ext, rollback);
                 
-                Status = insert_extent(fcb->Vcb, fcb, offset, length, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("insert_extent returned %08x\n", Status);
-                    ExFreePool(data);
-                    return Status;
+                if (write_fcb_compressed(fcb)) {
+                    Status = write_compressed(fcb, offset, offset + length, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("write_compressed returned %08x\n", Status);
+                        ExFreePool(data);
+                        return Status;
+                    }
+                } else {
+                    Status = insert_extent(fcb->Vcb, fcb, offset, length, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("insert_extent returned %08x\n", Status);
+                        ExFreePool(data);
+                        return Status;
+                    }
                 }
                 
                 oldalloc = ext->offset + length;
@@ -6883,7 +6966,7 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR
                     
                     ed->decoded_size = end - ext->offset;
                     
-                    remove_fcb_extent(ext, rollback);
+                    remove_fcb_extent(fcb, ext, rollback);
                     
                     if (!add_extent_to_fcb(fcb, ext->offset, ed, edsize, ext->unique, rollback)) {
                         ERR("add_extent_to_fcb failed\n");
@@ -6916,13 +6999,6 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR
                             ERR("insert_prealloc_extent returned %08x\n", Status);
                             return Status;
                         }
-                    } else {
-                        Status = insert_sparse_extent(fcb, oldalloc, newalloc - oldalloc, rollback);
-                        
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("insert_sparse_extent returned %08x\n", Status);
-                            return Status;
-                        }
                     }
                     
                     fcb->extents_changed = TRUE;
@@ -6938,7 +7014,7 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR
                 fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
             }
         } else {
-            if (end > fcb->Vcb->max_inline) {
+            if (end > fcb->Vcb->options.max_inline) {
                 newalloc = sector_align(end, fcb->Vcb->superblock.sector_size);
             
                 if (prealloc) {
@@ -6948,13 +7024,6 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR
                         ERR("insert_prealloc_extent returned %08x\n", Status);
                         return Status;
                     }
-                } else {
-                    Status = insert_sparse_extent(fcb, 0, newalloc, rollback);
-                    
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("insert_sparse_extent returned %08x\n", Status);
-                        return Status;
-                    }
                 }
                 
                 fcb->extents_changed = TRUE;
@@ -7010,883 +7079,767 @@ NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIR
     return STATUS_SUCCESS;
 }
 
-static BOOL is_file_prealloc(fcb* fcb, UINT64 start_data, UINT64 end_data) {
-    LIST_ENTRY* le;
-    extent* ext = NULL;
-    
-    le = fcb->extents.Flink;
-    
-    while (le != &fcb->extents) {
-        extent* nextext = CONTAINING_RECORD(le, extent, list_entry);
-        
-        if (!nextext->ignore) {
-            if (nextext->offset == start_data) {
-                ext = nextext;
-                break;
-            } else if (nextext->offset > start_data)
-                break;
-            
-            ext = nextext;
-        }
-        
-        le = le->Flink;
-    }
-    
-    if (!ext)
-        return FALSE;
-    
-    le = &ext->list_entry;
-    
-    while (le != &fcb->extents) {
-        ext = CONTAINING_RECORD(le, extent, list_entry);
-        
-        if (!ext->ignore) {
-            if (ext->datalen < sizeof(EXTENT_DATA)) {
-                ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
-                return FALSE;
-            }
-            
-            if (ext->offset < end_data && ext->data->type == EXTENT_TYPE_PREALLOC) {
-                EXTENT_DATA2* ed2;
-                
-                if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
-                    ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
-                    return FALSE;
-                }
-                
-                ed2 = (EXTENT_DATA2*)ext->data->data;
-                
-                if (ext->offset + ed2->num_bytes >= start_data)
-                    return TRUE;
-            }
-        }
-        
-        le = le->Flink;
-    }
-    
-    return FALSE;
-}
+// #ifdef DEBUG_PARANOID
+// static void print_loaded_trees(tree* t, int spaces) {
+//     char pref[10];
+//     int i;
+//     LIST_ENTRY* le;
+//     
+//     for (i = 0; i < spaces; i++) {
+//         pref[i] = ' ';
+//     }
+//     pref[spaces] = 0;
+//     
+//     if (!t) {
+//         ERR("%s(not loaded)\n", pref);
+//         return;
+//     }
+//     
+//     le = t->itemlist.Flink;
+//     while (le != &t->itemlist) {
+//         tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+//         
+//         ERR("%s%llx,%x,%llx ignore=%s\n", pref, td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
+//         
+//         if (t->header.level > 0) {
+//             print_loaded_trees(td->treeholder.tree, spaces+1);
+//         }
+//         
+//         le = le->Flink;
+//     }
+// }
 
-static NTSTATUS do_cow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
-    NTSTATUS Status;
-    
-    Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, rollback);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - excise_extents returned %08x\n", Status);
-        goto end;
-    }
-    
-    Status = insert_extent(fcb->Vcb, fcb, start_data, end_data - start_data, data, changed_sector_list, Irp, rollback);
-    
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - insert_extent returned %08x\n", Status);
-        goto end;
-    }
-    
-    Status = STATUS_SUCCESS;
-    
-end:
-    return Status;
-}
+// static void check_extents_consistent(device_extension* Vcb, fcb* fcb) {
+//     KEY searchkey;
+//     traverse_ptr tp, next_tp;
+//     UINT64 length, oldlength, lastoff, alloc;
+//     NTSTATUS Status;
+//     EXTENT_DATA* ed;
+//     EXTENT_DATA2* ed2;
+//     
+//     if (fcb->ads || fcb->inode_item.st_size == 0 || fcb->deleted)
+//         return;
+//     
+//     TRACE("inode = %llx, subvol = %llx\n", fcb->inode, fcb->subvol->id);
+//     
+//     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);
+//         goto failure;
+//     }
+//     
+//     if (keycmp(&searchkey, &tp.item->key)) {
+//         ERR("could not find EXTENT_DATA at offset 0\n");
+//         goto failure;
+//     }
+//     
+//     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));
+//         goto failure;
+//     }
+//     
+//     ed = (EXTENT_DATA*)tp.item->data;
+//     ed2 = (EXTENT_DATA2*)&ed->data[0];
+//     
+//     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);
+//     
+//     alloc = 0;
+//     if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) {
+//         alloc += length;
+//     }
+//     
+//     while (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
+//         if (next_tp.item->key.obj_id != searchkey.obj_id || next_tp.item->key.obj_type != searchkey.obj_type)
+//             break;
+//         
+//         tp = next_tp;
+//         
+//         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));
+//             goto failure;
+//         }
+//         
+//         ed = (EXTENT_DATA*)tp.item->data;
+//         ed2 = (EXTENT_DATA2*)&ed->data[0];
+//     
+//         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);
+//         
+//         if (tp.item->key.offset != lastoff + oldlength) {
+//             ERR("EXTENT_DATA in %llx,%llx was at %llx, expected %llx\n", fcb->subvol->id, fcb->inode, tp.item->key.offset, lastoff + oldlength);
+//             goto failure;
+//         }
+//         
+//         if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) {
+//             alloc += length;
+//         }
+//         
+//         oldlength = length;
+//         lastoff = tp.item->key.offset;
+//     }
+//     
+//     if (alloc != fcb->inode_item.st_blocks) {
+//         ERR("allocation size was %llx, expected %llx\n", alloc, fcb->inode_item.st_blocks);
+//         goto failure;
+//     }
+//     
+// //     if (fcb->inode_item.st_blocks != lastoff + oldlength) {
+// //         ERR("extents finished at %x, expected %x\n", (UINT32)(lastoff + oldlength), (UINT32)fcb->inode_item.st_blocks);
+// //         goto failure;
+// //     }
+//     
+//     return;
+//     
+// failure:
+//     if (fcb->subvol->treeholder.tree)
+//         print_loaded_trees(fcb->subvol->treeholder.tree, 0);
+// 
+//     int3;
+// }
 
-static NTSTATUS do_prealloc_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
-    NTSTATUS Status;
-    UINT64 last_written = start_data;
-    extent* ext = NULL;
-    LIST_ENTRY* le;
-    chunk* c;
-    
-    le = fcb->extents.Flink;
-    
-    while (le != &fcb->extents) {
-        extent* nextext = CONTAINING_RECORD(le, extent, list_entry);
-        
-        if (!nextext->ignore) {
-            if (nextext->offset == start_data) {
-                ext = nextext;
-                break;
-            } else if (nextext->offset > start_data)
-                break;
-            
-            ext = nextext;
-        }
-        
-        le = le->Flink;
-    }
-    
-    if (!ext)
-        return do_cow_write(Vcb, fcb, start_data, end_data, data, changed_sector_list, Irp, rollback);
-    
-    le = &ext->list_entry;
-    
-    while (le != &fcb->extents) {
-        EXTENT_DATA* ed;
-        EXTENT_DATA2* ed2;
-        LIST_ENTRY* le2 = le->Flink;
-        
-        ext = CONTAINING_RECORD(le, extent, list_entry);
-        ed = ext->data;
-        
-        if (!ext->ignore) {
-            if (ext->offset >= end_data)
-                break;
-            
-            if (ext->datalen < sizeof(EXTENT_DATA)) {
-                ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
-                return STATUS_INTERNAL_ERROR;
-            }
-            
-            if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
-                if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
-                    ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
-                    return STATUS_INTERNAL_ERROR;
-                }
-                
-                ed2 = (EXTENT_DATA2*)ed->data;
-            }
-            
-            if (ed->type == EXTENT_TYPE_PREALLOC) {
-                if (ext->offset > last_written) {
-                    Status = do_cow_write(Vcb, fcb, last_written, ext->offset, (UINT8*)data + last_written - start_data, changed_sector_list, Irp, rollback);
-                    
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("do_cow_write returned %08x\n", Status);                    
-                        return Status;
-                    }
-                    
-                    last_written = ext->offset;
-                }
-                
-                if (start_data <= ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace all
-                    EXTENT_DATA* ned;
-                    extent* newext;
-                    
-                    ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
-                    if (!ned) {
-                        ERR("out of memory\n");
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
-                    if (!newext) {
-                        ERR("out of memory\n");
-                        ExFreePool(ned);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    RtlCopyMemory(ned, ext->data, ext->datalen);
-                    
-                    ned->type = EXTENT_TYPE_REGULAR;
-                    
-                    Status = do_write_data(Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, ed2->num_bytes, changed_sector_list, Irp);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("do_write_data returned %08x\n", Status);
-                        return Status;
-                    }
-                    
-                    last_written = ext->offset + ed2->num_bytes;
-                    
-                    newext->offset = ext->offset;
-                    newext->data = ned;
-                    newext->datalen = ext->datalen;
-                    newext->unique = ext->unique;
-                    newext->ignore = FALSE;
-                    InsertHeadList(&ext->list_entry, &newext->list_entry);
-
-                    remove_fcb_extent(ext, rollback);
-                } else if (start_data <= ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace beginning
-                    EXTENT_DATA *ned, *nedb;
-                    EXTENT_DATA2* ned2;
-                    extent *newext1, *newext2;
-                    
-                    ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
-                    if (!ned) {
-                        ERR("out of memory\n");
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
-                    if (!nedb) {
-                        ERR("out of memory\n");
-                        ExFreePool(ned);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
-                    if (!newext1) {
-                        ERR("out of memory\n");
-                        ExFreePool(ned);
-                        ExFreePool(nedb);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
-                    if (!newext2) {
-                        ERR("out of memory\n");
-                        ExFreePool(ned);
-                        ExFreePool(nedb);
-                        ExFreePool(newext1);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    RtlCopyMemory(ned, ext->data, ext->datalen);
-                    ned->type = EXTENT_TYPE_REGULAR;
-                    ned2 = (EXTENT_DATA2*)ned->data;
-                    ned2->num_bytes = end_data - ext->offset;
-                    
-                    RtlCopyMemory(nedb, ext->data, ext->datalen);
-                    ned2 = (EXTENT_DATA2*)nedb->data;
-                    ned2->offset += end_data - ext->offset;
-                    ned2->num_bytes -= end_data - ext->offset;
-                    
-                    Status = do_write_data(Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, end_data - ext->offset, changed_sector_list, Irp);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("do_write_data returned %08x\n", Status);
-                        return Status;
-                    }
-                    
-                    last_written = end_data;
-                    
-                    newext1->offset = ext->offset;
-                    newext1->data = ned;
-                    newext1->datalen = ext->datalen;
-                    newext1->unique = FALSE;
-                    newext1->ignore = FALSE;
-                    InsertHeadList(&ext->list_entry, &newext1->list_entry);
-                    
-                    newext2->offset = end_data;
-                    newext2->data = nedb;
-                    newext2->datalen = ext->datalen;
-                    newext2->unique = FALSE;
-                    newext2->ignore = FALSE;
-                    InsertHeadList(&newext1->list_entry, &newext2->list_entry);
-                    
-                    c = get_chunk_from_address(Vcb, ed2->address);
-                    
-                    if (!c)
-                        ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
-                    else {
-                        Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
-                                                           fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
-                        
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("update_changed_extent_ref returned %08x\n", Status);
-                            return Status;
-                        }
-                    }
-
-                    remove_fcb_extent(ext, rollback);
-                } else if (start_data > ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace end
-                    EXTENT_DATA *ned, *nedb;
-                    EXTENT_DATA2* ned2;
-                    extent *newext1, *newext2;
-                    
-                    // FIXME - test this
-                    
-                    ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
-                    if (!ned) {
-                        ERR("out of memory\n");
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
-                    if (!nedb) {
-                        ERR("out of memory\n");
-                        ExFreePool(ned);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
-                    if (!newext1) {
-                        ERR("out of memory\n");
-                        ExFreePool(ned);
-                        ExFreePool(nedb);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
-                    if (!newext2) {
-                        ERR("out of memory\n");
-                        ExFreePool(ned);
-                        ExFreePool(nedb);
-                        ExFreePool(newext1);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    RtlCopyMemory(ned, ext->data, ext->datalen);
-                    
-                    ned2 = (EXTENT_DATA2*)ned->data;
-                    ned2->num_bytes = start_data - ext->offset;
-                    
-                    RtlCopyMemory(nedb, ext->data, ext->datalen);
-                    
-                    nedb->type = EXTENT_TYPE_REGULAR;
-                    ned2 = (EXTENT_DATA2*)nedb->data;
-                    ned2->offset += start_data - ext->offset;
-                    ned2->num_bytes = ext->offset + ed2->num_bytes - start_data;
-                    
-                    Status = do_write_data(Vcb, ed2->address + ned2->offset, data, ned2->num_bytes, changed_sector_list, Irp);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("do_write_data returned %08x\n", Status);
-                        
-                        return Status;
-                    }
-                    
-                    last_written = start_data + ned2->num_bytes;
-                    
-                    newext1->offset = ext->offset;
-                    newext1->data = ned;
-                    newext1->datalen = ext->datalen;
-                    newext1->unique = FALSE;
-                    newext1->ignore = FALSE;
-                    InsertHeadList(&ext->list_entry, &newext1->list_entry);
-                    
-                    newext2->offset = start_data;
-                    newext2->data = nedb;
-                    newext2->datalen = ext->datalen;
-                    newext2->unique = FALSE;
-                    newext2->ignore = FALSE;
-                    InsertHeadList(&newext1->list_entry, &newext2->list_entry);
-                    
-                    c = get_chunk_from_address(Vcb, ed2->address);
-                    
-                    if (!c)
-                        ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
-                    else {
-                        Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
-                                                           fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
-                        
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("update_changed_extent_ref returned %08x\n", Status);
-                            return Status;
-                        }
-                    }
-
-                    remove_fcb_extent(ext, rollback);
-                } else if (start_data > ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace middle
-                    EXTENT_DATA *ned, *nedb, *nedc;
-                    EXTENT_DATA2* ned2;
-                    extent *newext1, *newext2, *newext3;
-                    
-                    // FIXME - test this
-                    
-                    ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
-                    if (!ned) {
-                        ERR("out of memory\n");
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
-                    if (!nedb) {
-                        ERR("out of memory\n");
-                        ExFreePool(ned);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    nedc = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
-                    if (!nedb) {
-                        ERR("out of memory\n");
-                        ExFreePool(ned);
-                        ExFreePool(nedb);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
-                    if (!newext1) {
-                        ERR("out of memory\n");
-                        ExFreePool(ned);
-                        ExFreePool(nedb);
-                        ExFreePool(nedc);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
-                    if (!newext2) {
-                        ERR("out of memory\n");
-                        ExFreePool(ned);
-                        ExFreePool(nedb);
-                        ExFreePool(nedc);
-                        ExFreePool(newext1);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    newext3 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
-                    if (!newext2) {
-                        ERR("out of memory\n");
-                        ExFreePool(ned);
-                        ExFreePool(nedb);
-                        ExFreePool(nedc);
-                        ExFreePool(newext1);
-                        ExFreePool(newext2);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    RtlCopyMemory(ned, ext->data, ext->datalen);
-                    RtlCopyMemory(nedb, ext->data, ext->datalen);
-                    RtlCopyMemory(nedc, ext->data, ext->datalen);
-                    
-                    ned2 = (EXTENT_DATA2*)ned->data;
-                    ned2->num_bytes = start_data - ext->offset;
-                    
-                    nedb->type = EXTENT_TYPE_REGULAR;
-                    ned2 = (EXTENT_DATA2*)nedb->data;
-                    ned2->offset += start_data - ext->offset;
-                    ned2->num_bytes = end_data - start_data;
-                    
-                    ned2 = (EXTENT_DATA2*)nedc->data;
-                    ned2->offset += end_data - ext->offset;
-                    ned2->num_bytes -= end_data - ext->offset;
-                    
-                    ned2 = (EXTENT_DATA2*)nedb->data;
-                    Status = do_write_data(Vcb, ed2->address + ned2->offset, data, end_data - start_data, changed_sector_list, Irp);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("do_write_data returned %08x\n", Status);
-                        return Status;
-                    }
-                    
-                    last_written = end_data;
-                    
-                    newext1->offset = ext->offset;
-                    newext1->data = ned;
-                    newext1->datalen = ext->datalen;
-                    newext1->unique = FALSE;
-                    newext1->ignore = FALSE;
-                    InsertHeadList(&ext->list_entry, &newext1->list_entry);
-                    
-                    newext2->offset = start_data;
-                    newext2->data = nedb;
-                    newext2->datalen = ext->datalen;
-                    newext2->unique = FALSE;
-                    newext2->ignore = FALSE;
-                    InsertHeadList(&newext1->list_entry, &newext2->list_entry);
-                    
-                    newext3->offset = end_data;
-                    newext3->data = nedc;
-                    newext3->datalen = ext->datalen;
-                    newext3->unique = FALSE;
-                    newext3->ignore = FALSE;
-                    InsertHeadList(&newext2->list_entry, &newext3->list_entry);
-                    
-                    c = get_chunk_from_address(Vcb, ed2->address);
-                    
-                    if (!c)
-                        ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
-                    else {
-                        Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 2,
-                                                           fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
-                        
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("update_changed_extent_ref returned %08x\n", Status);
-                            return Status;
-                        }
-                    }
-
-                    remove_fcb_extent(ext, rollback);
-                }
-            }
-        }
-        
-        le = le2;
-    }
-    
-    if (last_written < end_data) {
-        Status = do_cow_write(Vcb, fcb, last_written, end_data, (UINT8*)data + last_written - start_data, changed_sector_list, Irp, rollback);
-                
-        if (!NT_SUCCESS(Status)) {
-            ERR("do_cow_write returned %08x\n", Status);
-            return Status;
-        }
-    }
-
-    fcb->extents_changed = TRUE;
-    mark_fcb_dirty(fcb);
-    
-    return STATUS_SUCCESS;
-}
-
-NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
-    NTSTATUS Status;
-    UINT64 size, new_start, new_end, last_written = start_data;
-    extent* ext = NULL;
-    LIST_ENTRY* le;
-    
-    TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, end_data, data, changed_sector_list);
-    
-    le = fcb->extents.Flink;
-    
-    while (le != &fcb->extents) {
-        extent* nextext = CONTAINING_RECORD(le, extent, list_entry);
-        
-        if (!nextext->ignore) {
-            if (nextext->offset == start_data) {
-                ext = nextext;
-                break;
-            } else if (nextext->offset > start_data)
-                break;
-            
-            ext = nextext;
-        }
-        
-        le = le->Flink;
-    }
-    
-    if (!ext)
-        return do_cow_write(Vcb, fcb, start_data, end_data, data, changed_sector_list, Irp, rollback);
-    
-    le = &ext->list_entry;
-    
-    while (le != &fcb->extents) {
-        EXTENT_DATA* ed;
-        EXTENT_DATA2* ed2;
-        BOOL do_cow;
-        LIST_ENTRY* le2 = le->Flink;
-        
-        ext = CONTAINING_RECORD(le, extent, list_entry);
-        
-        if (!ext->ignore) {
-            ed = ext->data;
-            
-            if (ext->offset >= end_data)
-                break;
-            
-            if (ext->datalen < sizeof(EXTENT_DATA)) {
-                ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
-                return STATUS_INTERNAL_ERROR;
-            }
-            
-            if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
-                if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
-                    ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
-                    return STATUS_INTERNAL_ERROR;
-                }
-                
-                ed2 = (EXTENT_DATA2*)ed->data;
-            }
-            
-            if (ed->type == EXTENT_TYPE_REGULAR) {
-                do_cow = !ext->unique;
-            } else {
-                do_cow = TRUE;
-            }
-            
-            if (ed->compression != BTRFS_COMPRESSION_NONE) {
-                FIXME("FIXME: compression not yet supported\n");
-                return STATUS_NOT_SUPPORTED;
-            }
-            
-            if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
-                WARN("encryption not supported\n");
-                return STATUS_NOT_SUPPORTED;
-            }
-            
-            if (ed->encoding != BTRFS_ENCODING_NONE) {
-                WARN("other encodings not supported\n");
-                return STATUS_NOT_SUPPORTED;
-            }
-            
-            size = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
-            
-            TRACE("extent: start = %llx, length = %llx\n", ext->offset, size);
-            
-            new_start = ext->offset < start_data ? start_data : ext->offset;
-            new_end = ext->offset + size > end_data ? end_data : (ext->offset + size);
-            
-            TRACE("new_start = %llx\n", new_start);
-            TRACE("new_end = %llx\n", new_end);
-            
-            if (ed->type == EXTENT_TYPE_PREALLOC) {
-                Status = do_prealloc_write(Vcb, fcb, new_start, new_end - new_start, (UINT8*)data + new_start - start_data, changed_sector_list, Irp, rollback);
-                
-                if (!NT_SUCCESS(Status)) {
-                    ERR("do_prealloc_write returned %08x\n", Status);
-                    return Status;
-                }
-            } else if (do_cow) {
-                TRACE("doing COW write\n");
-                
-                Status = excise_extents(Vcb, fcb, new_start, new_end, rollback);
-                
-                if (!NT_SUCCESS(Status)) {
-                    ERR("error - excise_extents returned %08x\n", Status);
-                    return Status;
-                }
-                
-                Status = insert_extent(Vcb, fcb, new_start, new_end - new_start, (UINT8*)data + new_start - start_data, changed_sector_list, Irp, rollback);
-                
-                if (!NT_SUCCESS(Status)) {
-                    ERR("error - insert_extent returned %08x\n", Status);
-                    return Status;
-                }
-            } else {
-                UINT64 writeaddr = ed2->address + ed2->offset + new_start - ext->offset;
-                
-                TRACE("doing non-COW write to %llx\n", writeaddr);
-                
-                Status = write_data_complete(Vcb, writeaddr, (UINT8*)data + new_start - start_data, new_end - new_start, Irp);
-                
-                if (!NT_SUCCESS(Status)) {
-                    ERR("error - write_data returned %08x\n", Status);
-                    return Status;
-                }
-                
-                if (changed_sector_list) {
-                    unsigned int i;
-                    changed_sector* sc;
-                    
-                    sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
-                    if (!sc) {
-                        ERR("out of memory\n");
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    sc->ol.key = writeaddr;
-                    sc->length = (new_end - new_start) / Vcb->superblock.sector_size;
-                    sc->deleted = FALSE;
-                    
-                    sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
-                    if (!sc->checksums) {
-                        ERR("out of memory\n");
-                        ExFreePool(sc);
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    for (i = 0; i < sc->length; i++) {
-                        sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + new_start - start_data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
-                    }
-
-                    insert_into_ordered_list(changed_sector_list, &sc->ol);
-                }
-            }
-            
-            last_written = new_end;
-        }
-        
-        le = le2;
-    }
-    
-    if (last_written < end_data) {
-        Status = do_cow_write(Vcb, fcb, last_written, end_data, (UINT8*)data + last_written - start_data, changed_sector_list, Irp, rollback);
-                
-        if (!NT_SUCCESS(Status)) {
-            ERR("do_cow_write returned %08x\n", Status);
-            return Status;
-        }
-    }
-
-    Status = STATUS_SUCCESS;
-    
-    fcb->extents_changed = TRUE;
-    mark_fcb_dirty(fcb);
-    
-    return Status;
-}
-
-// #ifdef DEBUG_PARANOID
-// static void print_loaded_trees(tree* t, int spaces) {
-//     char pref[10];
-//     int i;
-//     LIST_ENTRY* le;
-//     
-//     for (i = 0; i < spaces; i++) {
-//         pref[i] = ' ';
-//     }
-//     pref[spaces] = 0;
-//     
-//     if (!t) {
-//         ERR("%s(not loaded)\n", pref);
-//         return;
-//     }
-//     
-//     le = t->itemlist.Flink;
-//     while (le != &t->itemlist) {
-//         tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
-//         
-//         ERR("%s%llx,%x,%llx ignore=%s\n", pref, td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
-//         
-//         if (t->header.level > 0) {
-//             print_loaded_trees(td->treeholder.tree, spaces+1);
-//         }
-//         
-//         le = le->Flink;
-//     }
-// }
-
-// static void check_extents_consistent(device_extension* Vcb, fcb* fcb) {
+// static void check_extent_tree_consistent(device_extension* Vcb) {
 //     KEY searchkey;
 //     traverse_ptr tp, next_tp;
-//     UINT64 length, oldlength, lastoff, alloc;
-//     NTSTATUS Status;
-//     EXTENT_DATA* ed;
-//     EXTENT_DATA2* ed2;
-//     
-//     if (fcb->ads || fcb->inode_item.st_size == 0 || fcb->deleted)
-//         return;
-//     
-//     TRACE("inode = %llx, subvol = %llx\n", fcb->inode, fcb->subvol->id);
-//     
-//     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);
-//         goto failure;
-//     }
-//     
-//     if (keycmp(&searchkey, &tp.item->key)) {
-//         ERR("could not find EXTENT_DATA at offset 0\n");
-//         goto failure;
-//     }
+//     UINT64 lastaddr;
+//     BOOL b, inconsistency;
 //     
-//     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));
-//         goto failure;
+//     searchkey.obj_id = 0;
+//     searchkey.obj_type = 0;
+//     searchkey.offset = 0;
+//     
+//     if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+//         ERR("error - could not find any entries in extent_root\n");
+//         int3;
 //     }
 //     
-//     ed = (EXTENT_DATA*)tp.item->data;
-//     ed2 = (EXTENT_DATA2*)&ed->data[0];
+//     lastaddr = 0;
+//     inconsistency = FALSE;
 //     
-//     length = oldlength = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
-//     lastoff = tp.item->key.offset;
+//     do {
+//         if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
+// //             ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
+//             
+//             if (tp.item->key.obj_id < lastaddr) {
+// //                 ERR("inconsistency!\n");
+// //                 int3;
+//                 inconsistency = TRUE;
+//             }
+//             
+//             lastaddr = tp.item->key.obj_id + tp.item->key.offset;
+//         }
+//         
+//         b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
+//         if (b) {
+//             free_traverse_ptr(&tp);
+//             tp = next_tp;
+//         }
+//     } while (b);
 //     
-//     TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length);
+//     free_traverse_ptr(&tp);
 //     
-//     alloc = 0;
-//     if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) {
-//         alloc += length;
-//     }
+//     if (!inconsistency)
+//         return;
 //     
-//     while (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
-//         if (next_tp.item->key.obj_id != searchkey.obj_id || next_tp.item->key.obj_type != searchkey.obj_type)
-//             break;
-//         
-//         tp = next_tp;
-//         
-//         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));
-//             goto failure;
-//         }
-//         
-//         ed = (EXTENT_DATA*)tp.item->data;
-//         ed2 = (EXTENT_DATA2*)&ed->data[0];
+//     ERR("Inconsistency detected:\n");
 //     
-//         length = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
+//     if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
+//         ERR("error - could not find any entries in extent_root\n");
+//         int3;
+//     }
 //     
-//         TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length);
-//         
-//         if (tp.item->key.offset != lastoff + oldlength) {
-//             ERR("EXTENT_DATA in %llx,%llx was at %llx, expected %llx\n", fcb->subvol->id, fcb->inode, tp.item->key.offset, lastoff + oldlength);
-//             goto failure;
+//     do {
+//         if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
+//             ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
+//             
+//             if (tp.item->key.obj_id < lastaddr) {
+//                 ERR("inconsistency!\n");
+//             }
+//             
+//             lastaddr = tp.item->key.obj_id + tp.item->key.offset;
 //         }
 //         
-//         if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) {
-//             alloc += length;
+//         b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
+//         if (b) {
+//             free_traverse_ptr(&tp);
+//             tp = next_tp;
 //         }
-//         
-//         oldlength = length;
-//         lastoff = tp.item->key.offset;
-//     }
-//     
-//     if (alloc != fcb->inode_item.st_blocks) {
-//         ERR("allocation size was %llx, expected %llx\n", alloc, fcb->inode_item.st_blocks);
-//         goto failure;
-//     }
-//     
-// //     if (fcb->inode_item.st_blocks != lastoff + oldlength) {
-// //         ERR("extents finished at %x, expected %x\n", (UINT32)(lastoff + oldlength), (UINT32)fcb->inode_item.st_blocks);
-// //         goto failure;
-// //     }
+//     } while (b);
 //     
-//     return;
+//     free_traverse_ptr(&tp);
 //     
-// failure:
-//     if (fcb->subvol->treeholder.tree)
-//         print_loaded_trees(fcb->subvol->treeholder.tree, 0);
-// 
 //     int3;
 // }
+// #endif
+
+static NTSTATUS do_write_file_prealloc(fcb* fcb, extent* ext, UINT64 start_data, UINT64 end_data, void* data, UINT64* written,
+                                       LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+    EXTENT_DATA* ed = ext->data;
+    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+    NTSTATUS Status;
+    chunk* c;
+    
+    if (start_data <= ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace all
+        EXTENT_DATA* ned;
+        extent* newext;
+        
+        ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+        if (!ned) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+        if (!newext) {
+            ERR("out of memory\n");
+            ExFreePool(ned);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlCopyMemory(ned, ext->data, ext->datalen);
+        
+        ned->type = EXTENT_TYPE_REGULAR;
+        
+        Status = do_write_data(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, ed2->num_bytes, changed_sector_list, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("do_write_data returned %08x\n", Status);
+            return Status;
+        }
+        
+        *written = ed2->num_bytes;
+        
+        newext->offset = ext->offset;
+        newext->data = ned;
+        newext->datalen = ext->datalen;
+        newext->unique = ext->unique;
+        newext->ignore = FALSE;
+        InsertHeadList(&ext->list_entry, &newext->list_entry);
+
+        add_insert_extent_rollback(rollback, fcb, newext);
+        
+        remove_fcb_extent(fcb, ext, rollback);
+    } else if (start_data <= ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace beginning
+        EXTENT_DATA *ned, *nedb;
+        EXTENT_DATA2* ned2;
+        extent *newext1, *newext2;
+        
+        ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+        if (!ned) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+        if (!nedb) {
+            ERR("out of memory\n");
+            ExFreePool(ned);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+        if (!newext1) {
+            ERR("out of memory\n");
+            ExFreePool(ned);
+            ExFreePool(nedb);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+        if (!newext2) {
+            ERR("out of memory\n");
+            ExFreePool(ned);
+            ExFreePool(nedb);
+            ExFreePool(newext1);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlCopyMemory(ned, ext->data, ext->datalen);
+        ned->type = EXTENT_TYPE_REGULAR;
+        ned2 = (EXTENT_DATA2*)ned->data;
+        ned2->num_bytes = end_data - ext->offset;
+        
+        RtlCopyMemory(nedb, ext->data, ext->datalen);
+        ned2 = (EXTENT_DATA2*)nedb->data;
+        ned2->offset += end_data - ext->offset;
+        ned2->num_bytes -= end_data - ext->offset;
+        
+        Status = do_write_data(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, end_data - ext->offset, changed_sector_list, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("do_write_data returned %08x\n", Status);
+            return Status;
+        }
+        
+        *written = end_data - ext->offset;
+        
+        newext1->offset = ext->offset;
+        newext1->data = ned;
+        newext1->datalen = ext->datalen;
+        newext1->unique = FALSE;
+        newext1->ignore = FALSE;
+        InsertHeadList(&ext->list_entry, &newext1->list_entry);
+        
+        add_insert_extent_rollback(rollback, fcb, newext1);
+        
+        newext2->offset = end_data;
+        newext2->data = nedb;
+        newext2->datalen = ext->datalen;
+        newext2->unique = FALSE;
+        newext2->ignore = FALSE;
+        InsertHeadList(&newext1->list_entry, &newext2->list_entry);
+        
+        add_insert_extent_rollback(rollback, fcb, newext2);
+        
+        c = get_chunk_from_address(fcb->Vcb, ed2->address);
+        
+        if (!c)
+            ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
+        else {
+            Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
+                                                fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("update_changed_extent_ref returned %08x\n", Status);
+                return Status;
+            }
+        }
+
+        remove_fcb_extent(fcb, ext, rollback);
+    } else if (start_data > ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace end
+        EXTENT_DATA *ned, *nedb;
+        EXTENT_DATA2* ned2;
+        extent *newext1, *newext2;
+        
+        ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+        if (!ned) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+        if (!nedb) {
+            ERR("out of memory\n");
+            ExFreePool(ned);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+        if (!newext1) {
+            ERR("out of memory\n");
+            ExFreePool(ned);
+            ExFreePool(nedb);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+        if (!newext2) {
+            ERR("out of memory\n");
+            ExFreePool(ned);
+            ExFreePool(nedb);
+            ExFreePool(newext1);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlCopyMemory(ned, ext->data, ext->datalen);
+        
+        ned2 = (EXTENT_DATA2*)ned->data;
+        ned2->num_bytes = start_data - ext->offset;
+        
+        RtlCopyMemory(nedb, ext->data, ext->datalen);
+        
+        nedb->type = EXTENT_TYPE_REGULAR;
+        ned2 = (EXTENT_DATA2*)nedb->data;
+        ned2->offset += start_data - ext->offset;
+        ned2->num_bytes = ext->offset + ed2->num_bytes - start_data;
+        
+        Status = do_write_data(fcb->Vcb, ed2->address + ned2->offset, data, ned2->num_bytes, changed_sector_list, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("do_write_data returned %08x\n", Status);
+            return Status;
+        }
+        
+        *written = ned2->num_bytes;
+        
+        newext1->offset = ext->offset;
+        newext1->data = ned;
+        newext1->datalen = ext->datalen;
+        newext1->unique = FALSE;
+        newext1->ignore = FALSE;
+        InsertHeadList(&ext->list_entry, &newext1->list_entry);
+        
+        add_insert_extent_rollback(rollback, fcb, newext1);
+        
+        newext2->offset = start_data;
+        newext2->data = nedb;
+        newext2->datalen = ext->datalen;
+        newext2->unique = FALSE;
+        newext2->ignore = FALSE;
+        InsertHeadList(&newext1->list_entry, &newext2->list_entry);
+        
+        add_insert_extent_rollback(rollback, fcb, newext2);
+        
+        c = get_chunk_from_address(fcb->Vcb, ed2->address);
+        
+        if (!c)
+            ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
+        else {
+            Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
+                                               fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("update_changed_extent_ref returned %08x\n", Status);
+                return Status;
+            }
+        }
+
+        remove_fcb_extent(fcb, ext, rollback);
+    } else if (start_data > ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace middle
+        EXTENT_DATA *ned, *nedb, *nedc;
+        EXTENT_DATA2* ned2;
+        extent *newext1, *newext2, *newext3;
+        
+        ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+        if (!ned) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+        if (!nedb) {
+            ERR("out of memory\n");
+            ExFreePool(ned);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        nedc = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
+        if (!nedb) {
+            ERR("out of memory\n");
+            ExFreePool(ned);
+            ExFreePool(nedb);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+        if (!newext1) {
+            ERR("out of memory\n");
+            ExFreePool(ned);
+            ExFreePool(nedb);
+            ExFreePool(nedc);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+        if (!newext2) {
+            ERR("out of memory\n");
+            ExFreePool(ned);
+            ExFreePool(nedb);
+            ExFreePool(nedc);
+            ExFreePool(newext1);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        newext3 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+        if (!newext2) {
+            ERR("out of memory\n");
+            ExFreePool(ned);
+            ExFreePool(nedb);
+            ExFreePool(nedc);
+            ExFreePool(newext1);
+            ExFreePool(newext2);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlCopyMemory(ned, ext->data, ext->datalen);
+        RtlCopyMemory(nedb, ext->data, ext->datalen);
+        RtlCopyMemory(nedc, ext->data, ext->datalen);
+        
+        ned2 = (EXTENT_DATA2*)ned->data;
+        ned2->num_bytes = start_data - ext->offset;
+        
+        nedb->type = EXTENT_TYPE_REGULAR;
+        ned2 = (EXTENT_DATA2*)nedb->data;
+        ned2->offset += start_data - ext->offset;
+        ned2->num_bytes = end_data - start_data;
+        
+        ned2 = (EXTENT_DATA2*)nedc->data;
+        ned2->offset += end_data - ext->offset;
+        ned2->num_bytes -= end_data - ext->offset;
+        
+        ned2 = (EXTENT_DATA2*)nedb->data;
+        Status = do_write_data(fcb->Vcb, ed2->address + ned2->offset, data, end_data - start_data, changed_sector_list, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("do_write_data returned %08x\n", Status);
+            return Status;
+        }
+        
+        *written = end_data - start_data;
+        
+        newext1->offset = ext->offset;
+        newext1->data = ned;
+        newext1->datalen = ext->datalen;
+        newext1->unique = FALSE;
+        newext1->ignore = FALSE;
+        InsertHeadList(&ext->list_entry, &newext1->list_entry);
+        
+        add_insert_extent_rollback(rollback, fcb, newext1);
+        
+        newext2->offset = start_data;
+        newext2->data = nedb;
+        newext2->datalen = ext->datalen;
+        newext2->unique = FALSE;
+        newext2->ignore = FALSE;
+        InsertHeadList(&newext1->list_entry, &newext2->list_entry);
+        
+        add_insert_extent_rollback(rollback, fcb, newext2);
+        
+        newext3->offset = end_data;
+        newext3->data = nedc;
+        newext3->datalen = ext->datalen;
+        newext3->unique = FALSE;
+        newext3->ignore = FALSE;
+        InsertHeadList(&newext2->list_entry, &newext3->list_entry);
+        
+        add_insert_extent_rollback(rollback, fcb, newext3);
+        
+        c = get_chunk_from_address(fcb->Vcb, ed2->address);
+        
+        if (!c)
+            ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
+        else {
+            Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 2,
+                                               fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("update_changed_extent_ref returned %08x\n", Status);
+                return Status;
+            }
+        }
+
+        remove_fcb_extent(fcb, ext, rollback);
+    }
+    
+    return STATUS_SUCCESS;
+}
 
-// static void check_extent_tree_consistent(device_extension* Vcb) {
-//     KEY searchkey;
-//     traverse_ptr tp, next_tp;
-//     UINT64 lastaddr;
-//     BOOL b, inconsistency;
-//     
-//     searchkey.obj_id = 0;
-//     searchkey.obj_type = 0;
-//     searchkey.offset = 0;
-//     
-//     if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
-//         ERR("error - could not find any entries in extent_root\n");
-//         int3;
-//     }
-//     
-//     lastaddr = 0;
-//     inconsistency = FALSE;
-//     
-//     do {
-//         if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
-// //             ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
-//             
-//             if (tp.item->key.obj_id < lastaddr) {
-// //                 ERR("inconsistency!\n");
-// //                 int3;
-//                 inconsistency = TRUE;
-//             }
-//             
-//             lastaddr = tp.item->key.obj_id + tp.item->key.offset;
-//         }
-//         
-//         b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
-//         if (b) {
-//             free_traverse_ptr(&tp);
-//             tp = next_tp;
-//         }
-//     } while (b);
-//     
-//     free_traverse_ptr(&tp);
-//     
-//     if (!inconsistency)
-//         return;
-//     
-//     ERR("Inconsistency detected:\n");
-//     
-//     if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
-//         ERR("error - could not find any entries in extent_root\n");
-//         int3;
-//     }
-//     
-//     do {
-//         if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
-//             ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
-//             
-//             if (tp.item->key.obj_id < lastaddr) {
-//                 ERR("inconsistency!\n");
-//             }
-//             
-//             lastaddr = tp.item->key.obj_id + tp.item->key.offset;
-//         }
-//         
-//         b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
-//         if (b) {
-//             free_traverse_ptr(&tp);
-//             tp = next_tp;
-//         }
-//     } while (b);
-//     
-//     free_traverse_ptr(&tp);
-//     
-//     int3;
-// }
-// #endif
+NTSTATUS do_write_file(fcb* fcb, UINT64 start, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
+    LIST_ENTRY *le, *le2;
+    UINT64 written = 0, length = end_data - start;
+    UINT64 last_cow_start;
+#ifdef DEBUG_PARANOID
+    UINT64 last_off;
+#endif
+    
+    last_cow_start = 0;
+    
+    le = fcb->extents.Flink;
+    while (le != &fcb->extents) {
+        extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+        
+        le2 = le->Flink;
+        
+        if (!ext->ignore) {
+            EXTENT_DATA* ed = ext->data;
+            EXTENT_DATA2* ed2 = ed->type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ed->data;
+            UINT64 len;
+            BOOL nocow;
+            
+            len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
+            
+            if (ext->offset + len <= start)
+                goto nextitem;
+            
+            if (ext->offset > start + written + length)
+                break;
+            
+            nocow = (ext->unique && fcb->inode_item.flags & BTRFS_INODE_NODATACOW) || ed->type == EXTENT_TYPE_PREALLOC;
+           
+            if (nocow) {
+                if (max(last_cow_start, start + written) < ext->offset) {
+                    UINT64 start_write = max(last_cow_start, start + written);
+                    
+                    Status = excise_extents(fcb->Vcb, fcb, start_write, ext->offset, Irp, rollback);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("excise_extents returned %08x\n", Status);
+                        return Status;
+                    }
+                    
+                    Status = insert_extent(fcb->Vcb, fcb, start_write, ext->offset - start_write, data, changed_sector_list, Irp, rollback);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("insert_extent returned %08x\n", Status);
+                        return Status;
+                    }
+                    
+                    written += ext->offset - start_write;
+                    length -= ext->offset - start_write;
+                    
+                    if (length == 0)
+                        break;
+                }
+                
+                if (ed->type == EXTENT_TYPE_REGULAR) {
+                    UINT64 writeaddr = ed2->address + ed2->offset + start + written - ext->offset;
+                    UINT64 write_len = min(len, length);
+                                    
+                    TRACE("doing non-COW write to %llx\n", writeaddr);
+                    
+                    Status = write_data_complete(fcb->Vcb, writeaddr, (UINT8*)data + written, write_len, Irp, NULL);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("write_data_complete returned %08x\n", Status);
+                        return Status;
+                    }
+                    
+                    if (changed_sector_list) {
+                        unsigned int i;
+                        changed_sector* sc;
+                        
+                        sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
+                        if (!sc) {
+                            ERR("out of memory\n");
+                            return STATUS_INSUFFICIENT_RESOURCES;
+                        }
+                        
+                        sc->ol.key = writeaddr;
+                        sc->length = write_len / fcb->Vcb->superblock.sector_size;
+                        sc->deleted = FALSE;
+                        
+                        sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
+                        if (!sc->checksums) {
+                            ERR("out of memory\n");
+                            ExFreePool(sc);
+                            return STATUS_INSUFFICIENT_RESOURCES;
+                        }
+                        
+                        for (i = 0; i < sc->length; i++) {
+                            sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + written + (i * fcb->Vcb->superblock.sector_size), fcb->Vcb->superblock.sector_size);
+                        }
+    
+                        insert_into_ordered_list(changed_sector_list, &sc->ol);
+                    }
+                    
+                    written += write_len;
+                    length -= write_len;
+                    
+                    if (length == 0)
+                        break;
+                } else if (ed->type == EXTENT_TYPE_PREALLOC) {
+                    UINT64 write_len;
+                    
+                    Status = do_write_file_prealloc(fcb, ext, start + written, end_data, (UINT8*)data + written, &write_len,
+                                                    changed_sector_list, Irp, rollback);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("do_write_file_prealloc returned %08x\n", Status);
+                        return Status;
+                    }
+                    
+                    written += write_len;
+                    length -= write_len;
+                    
+                    if (length == 0)
+                        break;
+                }
+                
+                last_cow_start = ext->offset + len;
+            }
+        }
+        
+nextitem:
+        le = le2;
+    }
+    
+    if (length > 0) {
+        UINT64 start_write = max(last_cow_start, start + written);
+        
+        Status = excise_extents(fcb->Vcb, fcb, start_write, end_data, Irp, rollback);
+        if (!NT_SUCCESS(Status)) {
+            ERR("excise_extents returned %08x\n", Status);
+            return Status;
+        }
+        
+        Status = insert_extent(fcb->Vcb, fcb, start_write, end_data - start_write, data, changed_sector_list, Irp, rollback);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_extent returned %08x\n", Status);
+            return Status;
+        }
+    }
+    
+    // FIXME - make extending work again (here?)
+    // FIXME - make maximum extent size 128 MB again (here?)
+    
+#ifdef DEBUG_PARANOID
+    last_off = 0xffffffffffffffff;
+    
+    le = fcb->extents.Flink;
+    while (le != &fcb->extents) {
+        extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+        
+        if (!ext->ignore) {
+            if (ext->offset == last_off) {
+                ERR("offset %llx duplicated\n", ext->offset);
+                int3;
+            } else if (ext->offset < last_off && last_off != 0xffffffffffffffff) {
+                ERR("offsets out of order\n");
+                int3;
+            }
+            
+            last_off = ext->offset;
+        }
+        
+        le = le->Flink;
+    }
+#endif
+    
+    fcb->extents_changed = TRUE;
+    mark_fcb_dirty(fcb);
+    
+    return STATUS_SUCCESS;
+}
 
-static void STDCALL deferred_write_callback(void* context1, void* context2) {
-    PIRP Irp = context1;
-    device_extension* Vcb = context2;
+NTSTATUS write_compressed(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
+    UINT64 i;
+    
+    for (i = 0; i < sector_align(end_data - start_data, COMPRESSED_EXTENT_SIZE) / COMPRESSED_EXTENT_SIZE; i++) {
+        UINT64 s2, e2;
+        BOOL compressed;
+        
+        s2 = start_data + (i * COMPRESSED_EXTENT_SIZE);
+        e2 = min(s2 + COMPRESSED_EXTENT_SIZE, end_data);
+        
+        Status = write_compressed_bit(fcb, s2, e2, (UINT8*)data + (i * COMPRESSED_EXTENT_SIZE), &compressed, changed_sector_list, Irp, rollback);
+        
+        if (!NT_SUCCESS(Status)) {
+            ERR("write_compressed_bit returned %08x\n", Status);
+            return Status;
+        }
+        
+        // If the first 128 KB of a file is incompressible, we set the nocompress flag so we don't
+        // bother with the rest of it.
+        if (s2 == 0 && e2 == COMPRESSED_EXTENT_SIZE && !compressed && !fcb->Vcb->options.compress_force) {
+            fcb->inode_item.flags |= BTRFS_INODE_NOCOMPRESS;
+            mark_fcb_dirty(fcb);
+            
+            // write subsequent data non-compressed
+            if (e2 < end_data) {
+                Status = do_write_file(fcb, e2, end_data, (UINT8*)data + e2, changed_sector_list, Irp, rollback);
+                
+                if (!NT_SUCCESS(Status)) {
+                    ERR("do_write_file returned %08x\n", Status);
+                    return Status;
+                }
+            }
+            
+            return STATUS_SUCCESS;
+        }
+    }
     
-    if (!add_thread_job(Vcb, Irp))
-        do_write_job(Vcb, Irp);
+    return STATUS_SUCCESS;
 }
 
 NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache,
@@ -7900,14 +7853,14 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
     UINT8* data;
     LIST_ENTRY changed_sector_list;
     INODE_ITEM* origii;
-    BOOL changed_length = FALSE, nocsum, nocow/*, lazy_writer = FALSE, write_eof = FALSE*/;
+    BOOL changed_length = FALSE, nocsum/*, lazy_writer = FALSE, write_eof = FALSE*/;
     NTSTATUS Status;
     LARGE_INTEGER time;
     BTRFS_TIME now;
     fcb* fcb;
     ccb* ccb;
     file_ref* fileref;
-    BOOL paging_lock = FALSE, fcb_lock = FALSE, tree_lock = FALSE;
+    BOOL paging_lock = FALSE, fcb_lock = FALSE, tree_lock = FALSE, pagefile;
     ULONG filter = 0;
     
     TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb, FileObject, offset.QuadPart, buf, *length, paging_io, no_cache);
@@ -7938,11 +7891,8 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
     
     TRACE("fcb->Header.Flags = %x\n", fcb->Header.Flags);
     
-    if (!no_cache && !CcCanIWrite(FileObject, *length, wait, deferred_write)) {
-        CcDeferWrite(FileObject, (PCC_POST_DEFERRED_WRITE)deferred_write_callback, Irp, Vcb, *length, deferred_write);
-
+    if (!no_cache && !CcCanIWrite(FileObject, *length, wait, deferred_write))
         return STATUS_PENDING;
-    }
     
     if (!wait && no_cache)
         return STATUS_PENDING;
@@ -7973,7 +7923,9 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
             paging_lock = TRUE;
     }
     
-    if (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
+    pagefile = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE && paging_io;
+    
+    if (!pagefile && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
         if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) {
             Status = STATUS_PENDING;
             goto end;
@@ -7990,7 +7942,6 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
     }
     
     nocsum = fcb->ads ? TRUE : fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
-    nocow = fcb->ads ? TRUE : fcb->inode_item.flags & BTRFS_INODE_NODATACOW;
     
     newlength = fcb->ads ? fcb->adsdata.Length : fcb->inode_item.st_size;
     
@@ -8024,7 +7975,7 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
         }
     }
     
-    make_inline = fcb->ads ? FALSE : newlength <= fcb->Vcb->max_inline;
+    make_inline = fcb->ads ? FALSE : newlength <= fcb->Vcb->options.max_inline;
     
     if (changed_length) {
         if (newlength > fcb->Header.AllocationSize.QuadPart) {
@@ -8092,43 +8043,14 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
     }
     
     if (fcb->ads) {
-//         UINT32 maxlen;
-
         if (changed_length) {
             char* data2;
             
-//             // 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);
-//                 goto end;
-//             }
-//             
-//             if (keycmp(&tp.item->key, &searchkey)) {
-//                 ERR("error - could not find key for xattr\n");
-//                 Status = STATUS_INTERNAL_ERROR;
-//                 goto end;
-//             }
-//             
-//             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);
-//                 Status = STATUS_INTERNAL_ERROR;
-//                 goto end;
-//             }
-//             
-//             maxlen -= tp.item->size - datalen; // subtract XATTR_ITEM overhead
-//             
-//             if (newlength > maxlen) {
-//                 ERR("error - xattr too long (%llu > %u)\n", newlength, maxlen);
-//                 Status = STATUS_DISK_FULL;
-//                 goto end;
-//             }
+            if (newlength > fcb->adsmaxlen) {
+                ERR("error - xattr too long (%llu > %u)\n", newlength, fcb->adsmaxlen);
+                Status = STATUS_DISK_FULL;
+                goto end;
+            }
 
             data2 = ExAllocatePoolWithTag(PagedPool, newlength, ALLOC_TAG);
             if (!data2) {
@@ -8164,10 +8086,17 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
         if (fileref)
             mark_fileref_dirty(fileref);
     } else {
+        BOOL compress = write_fcb_compressed(fcb);
+        
         if (make_inline) {
             start_data = 0;
             end_data = sector_align(newlength, fcb->Vcb->superblock.sector_size);
             bufhead = sizeof(EXTENT_DATA) - 1;
+        } else if (compress) {
+            start_data = offset.QuadPart & ~(UINT64)(COMPRESSED_EXTENT_SIZE - 1);
+            end_data = min(sector_align(offset.QuadPart + *length, COMPRESSED_EXTENT_SIZE),
+                           sector_align(newlength, fcb->Vcb->superblock.sector_size));
+            bufhead = 0;
         } else {
             start_data = offset.QuadPart & ~(UINT64)(fcb->Vcb->superblock.sector_size - 1);
             end_data = sector_align(offset.QuadPart + *length, fcb->Vcb->superblock.sector_size);
@@ -8211,7 +8140,7 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
             InitializeListHead(&changed_sector_list);
 
         if (make_inline) {
-            Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, rollback);
+            Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("error - excise_extents returned %08x\n", Status);
                 ExFreePool(data);
@@ -8234,31 +8163,21 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
             }
             
             fcb->inode_item.st_blocks += newlength;
-        } else if (!nocow) {
-            if (is_file_prealloc(fcb, start_data, end_data)) {
-                Status = do_prealloc_write(fcb->Vcb, fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
-                
-                if (!NT_SUCCESS(Status)) {
-                    ERR("error - do_prealloc_write returned %08x\n", Status);
-                    ExFreePool(data);
-                    goto end;
-                }
-            } else {
-                Status = do_cow_write(fcb->Vcb, fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
-                
-                if (!NT_SUCCESS(Status)) {
-                    ERR("error - do_cow_write returned %08x\n", Status);
-                    ExFreePool(data);
-                    goto end;
-                }
+        } else if (compress) {
+            Status = write_compressed(fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("write_compressed returned %08x\n", Status);
+                ExFreePool(data);
+                goto end;
             }
             
             ExFreePool(data);
         } else {
-            Status = do_nocow_write(fcb->Vcb, fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
+            Status = do_write_file(fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
             
             if (!NT_SUCCESS(Status)) {
-                ERR("error - do_nocow_write returned %08x\n", Status);
+                ERR("do_write_file returned %08x\n", Status);
                 ExFreePool(data);
                 goto end;
             }
@@ -8267,54 +8186,56 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
         }
     }
     
-    KeQuerySystemTime(&time);
-    win_time_to_unix(time, &now);
-    
-//     ERR("no_cache = %s, FileObject->PrivateCacheMap = %p\n", no_cache ? "TRUE" : "FALSE", FileObject->PrivateCacheMap);
+    if (!pagefile) {
+        KeQuerySystemTime(&time);
+        win_time_to_unix(time, &now);
     
-//     if (!no_cache) {
-//         if (!FileObject->PrivateCacheMap) {
-//             CC_FILE_SIZES ccfs;
-//             
-//             ccfs.AllocationSize = fcb->Header.AllocationSize;
-//             ccfs.FileSize = fcb->Header.FileSize;
-//             ccfs.ValidDataLength = fcb->Header.ValidDataLength;
-//             
-//             TRACE("calling CcInitializeCacheMap...\n");
-//             CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
-//             
-//             changed_length = FALSE;
+//         ERR("no_cache = %s, FileObject->PrivateCacheMap = %p\n", no_cache ? "TRUE" : "FALSE", FileObject->PrivateCacheMap);
+//         
+//         if (!no_cache) {
+//             if (!FileObject->PrivateCacheMap) {
+//                 CC_FILE_SIZES ccfs;
+//                 
+//                 ccfs.AllocationSize = fcb->Header.AllocationSize;
+//                 ccfs.FileSize = fcb->Header.FileSize;
+//                 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+//                 
+//                 TRACE("calling CcInitializeCacheMap...\n");
+//                 CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
+//                 
+//                 changed_length = FALSE;
+//             }
 //         }
-//     }
-    
-    if (fcb->ads) {
-        if (fileref && fileref->parent)
-            origii = &fileref->parent->fcb->inode_item;
-        else {
-            ERR("no parent fcb found for stream\n");
-            Status = STATUS_INTERNAL_ERROR;
-            goto end;
-        }
-    } else
-        origii = &fcb->inode_item;
-    
-    origii->transid = Vcb->superblock.generation;
-    origii->sequence++;
-    origii->st_ctime = now;
-    
-    if (!fcb->ads) {
-        if (changed_length) {
-            TRACE("setting st_size to %llx\n", newlength);
-            origii->st_size = newlength;
-            filter |= FILE_NOTIFY_CHANGE_SIZE;
+        
+        if (fcb->ads) {
+            if (fileref && fileref->parent)
+                origii = &fileref->parent->fcb->inode_item;
+            else {
+                ERR("no parent fcb found for stream\n");
+                Status = STATUS_INTERNAL_ERROR;
+                goto end;
+            }
+        } else
+            origii = &fcb->inode_item;
+        
+        origii->transid = Vcb->superblock.generation;
+        origii->sequence++;
+        origii->st_ctime = now;
+        
+        if (!fcb->ads) {
+            if (changed_length) {
+                TRACE("setting st_size to %llx\n", newlength);
+                origii->st_size = newlength;
+                filter |= FILE_NOTIFY_CHANGE_SIZE;
+            }
+            
+            origii->st_mtime = now;
+            filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
         }
         
-        origii->st_mtime = now;
-        filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+        mark_fcb_dirty(fcb->ads ? fileref->parent->fcb : fcb);
     }
     
-    mark_fcb_dirty(fcb->ads ? fileref->parent->fcb : fcb);
-    
     if (!nocsum) {
         ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE);
         commit_checksum_changes(Vcb, &changed_sector_list);
@@ -8496,7 +8417,7 @@ NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
         goto end;
     }
     
-    if (!(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
+    if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
         WARN("insufficient permissions\n");
         Status = STATUS_ACCESS_DENIED;
         goto end;
diff --git a/reactos/drivers/filesystems/btrfs/zlib/CMakeLists.txt b/reactos/drivers/filesystems/btrfs/zlib/CMakeLists.txt
new file mode 100644 (file)
index 0000000..06aa88d
--- /dev/null
@@ -0,0 +1,17 @@
+
+include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/libs/zlib
+                    inc)
+
+list(APPEND SOURCE
+    ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/adler32.c
+    ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/crc32.c
+    ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/deflate.c
+    ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/inffast.c
+    ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/inflate.c
+    ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/inftrees.c
+    ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/trees.c
+    ${REACTOS_SOURCE_DIR}/sdk/lib/3rdparty/zlib/zutil.c)
+
+add_library(zlib_solo ${SOURCE})
+
+add_definitions(-DZ_SOLO)
index 42c9d7d..59d0a88 100644 (file)
@@ -3,7 +3,7 @@
 
 The following FSD are shared with: https://github.com/maharmstone/btrfs.
 
-reactos/drivers/filesystems/btrfs           # Synced to 0.5
+reactos/drivers/filesystems/btrfs           # Synced to 0.6
 
 The following FSD are shared with: http://www.ext2fsd.com/