Index: dll/shellext/shellbtrfs/contextmenu.cpp
===================================================================
--- dll/shellext/shellbtrfs/contextmenu.cpp	(révision 74430)
+++ dll/shellext/shellbtrfs/contextmenu.cpp	(copie de travail)
@@ -34,6 +34,7 @@
 #ifndef __REACTOS__
 #include "../btrfsioctl.h"
 #else
+#include "../../drivers/filesystems/btrfs/btrfs.h"
 #include "../../drivers/filesystems/btrfs/btrfsioctl.h"
 #endif
 
Index: dll/shellext/shellbtrfs/iconoverlay.cpp
===================================================================
--- dll/shellext/shellbtrfs/iconoverlay.cpp	(révision 74430)
+++ dll/shellext/shellbtrfs/iconoverlay.cpp	(copie de travail)
@@ -28,6 +28,7 @@
 #ifndef __REACTOS__
 #include "../btrfsioctl.h"
 #else
+#include "../../drivers/filesystems/btrfs/btrfs.h"
 #include "../../drivers/filesystems/btrfs/btrfsioctl.h"
 #endif
 
Index: dll/shellext/shellbtrfs/propsheet.h
===================================================================
--- dll/shellext/shellbtrfs/propsheet.h	(révision 74430)
+++ dll/shellext/shellbtrfs/propsheet.h	(copie de travail)
@@ -20,6 +20,7 @@
 #ifndef __REACTOS__
 #include "../btrfsioctl.h"
 #else
+#include "../../drivers/filesystems/btrfs/btrfs.h"
 #include "../../drivers/filesystems/btrfs/btrfsioctl.h"
 #endif
 
Index: drivers/filesystems/btrfs/CMakeLists.txt
===================================================================
--- drivers/filesystems/btrfs/CMakeLists.txt	(révision 74430)
+++ drivers/filesystems/btrfs/CMakeLists.txt	(copie de travail)
@@ -24,9 +24,11 @@
     read.c
     registry.c
     reparse.c
+    scrub.c
     search.c
     security.c
     treefuncs.c
+    volume.c
     worker-thread.c
     write.c
     btrfs_drv.h)
Index: drivers/filesystems/btrfs/balance.c
===================================================================
--- drivers/filesystems/btrfs/balance.c	(révision 74430)
+++ drivers/filesystems/btrfs/balance.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -17,6 +17,7 @@
 
 #include "btrfs_drv.h"
 #include "btrfsioctl.h"
+#include <ntddstor.h>
 
 typedef struct {
     UINT64 address;
@@ -64,10 +65,12 @@
     LIST_ENTRY list_entry;
 } data_reloc_ref;
 
-extern LIST_ENTRY volumes;
-extern ERESOURCE volumes_lock;
+#ifndef _MSC_VER // not in mingw yet
+#define DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED 0x80000000
+#endif
 
 static NTSTATUS add_metadata_reloc(device_extension* Vcb, LIST_ENTRY* items, traverse_ptr* tp, BOOL skinny, metadata_reloc** mr2, chunk* c, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
     metadata_reloc* mr;
     EXTENT_ITEM* ei;
     UINT16 len;
@@ -86,7 +89,12 @@
     mr->system = FALSE;
     InitializeListHead(&mr->refs);
     
-    delete_tree_item(Vcb, tp, rollback);
+    Status = delete_tree_item(Vcb, tp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("delete_tree_item returned %08x\n", Status);
+        ExFreePool(mr);
+        return Status;
+    }
     
     if (!c)
         c = get_chunk_from_address(Vcb, tp->item->key.obj_id);
@@ -94,7 +102,7 @@
     if (c) {
         ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
-        decrease_chunk_usage(c, Vcb->superblock.node_size);
+        c->used -= Vcb->superblock.node_size;
         
         space_list_add(Vcb, c, TRUE, tp->item->key.obj_id, Vcb->superblock.node_size, rollback);
         
@@ -176,7 +184,11 @@
                     ref->top = FALSE;
                     InsertTailList(&mr->refs, &ref->list_entry);
                     
-                    delete_tree_item(Vcb, &tp2, rollback);
+                    Status = delete_tree_item(Vcb, &tp2);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_tree_item returned %08x\n", Status);
+                        return Status;
+                    }
                 } else if (tp2.item->key.obj_type == TYPE_SHARED_BLOCK_REF && tp2.item->size >= sizeof(SHARED_BLOCK_REF)) {
                     metadata_reloc_ref* ref = ExAllocatePoolWithTag(PagedPool, sizeof(metadata_reloc_ref), ALLOC_TAG);
                     if (!ref) {
@@ -190,7 +202,11 @@
                     ref->top = FALSE;
                     InsertTailList(&mr->refs, &ref->list_entry);
                     
-                    delete_tree_item(Vcb, &tp2, rollback);
+                    Status = delete_tree_item(Vcb, &tp2);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_tree_item returned %08x\n", Status);
+                        return Status;
+                    }
                 }
             } else
                 break;
@@ -258,7 +274,8 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS add_metadata_reloc_extent_item(device_extension* Vcb, metadata_reloc* mr, LIST_ENTRY* rollback) {
+static NTSTATUS add_metadata_reloc_extent_item(device_extension* Vcb, metadata_reloc* mr) {
+    NTSTATUS Status;
     LIST_ENTRY* le;
     UINT64 rc = 0;
     UINT16 inline_len;
@@ -341,16 +358,14 @@
         le = le->Flink;
     }
     
-    if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
-        if (!insert_tree_item(Vcb, Vcb->extent_root, mr->new_address, TYPE_METADATA_ITEM, mr->data->level, ei, inline_len, NULL, NULL, rollback)) {
-            ERR("insert_tree_item failed\n");
-            return STATUS_INTERNAL_ERROR;
-        }
-    } else {
-        if (!insert_tree_item(Vcb, Vcb->extent_root, mr->new_address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, ei, inline_len, NULL, NULL, rollback)) {
-            ERR("insert_tree_item failed\n");
-            return STATUS_INTERNAL_ERROR;
-        }
+    if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
+        Status = insert_tree_item(Vcb, Vcb->extent_root, mr->new_address, TYPE_METADATA_ITEM, mr->data->level, ei, inline_len, NULL, NULL);
+    else
+        Status = insert_tree_item(Vcb, Vcb->extent_root, mr->new_address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, ei, inline_len, NULL, NULL);
+    
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
+        return Status;
     }
     
     if (!all_inline) {
@@ -370,9 +385,10 @@
                 
                 tbr->offset = ref->tbr.offset;
                 
-                if (!insert_tree_item(Vcb, Vcb->extent_root, mr->new_address, TYPE_TREE_BLOCK_REF, tbr->offset, tbr, sizeof(TREE_BLOCK_REF), NULL, NULL, rollback)) {
-                    ERR("insert_tree_item failed\n");
-                    return STATUS_INTERNAL_ERROR;
+                Status = insert_tree_item(Vcb, Vcb->extent_root, mr->new_address, TYPE_TREE_BLOCK_REF, tbr->offset, tbr, sizeof(TREE_BLOCK_REF), NULL, NULL);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("insert_tree_item returned %08x\n", Status);
+                    return Status;
                 }
             } else if (ref->type == TYPE_SHARED_BLOCK_REF) {
                 SHARED_BLOCK_REF* sbr;
@@ -385,9 +401,10 @@
                 
                 sbr->offset = ref->parent->new_address;
                 
-                if (!insert_tree_item(Vcb, Vcb->extent_root, mr->new_address, TYPE_SHARED_BLOCK_REF, sbr->offset, sbr, sizeof(SHARED_BLOCK_REF), NULL, NULL, rollback)) {
-                    ERR("insert_tree_item failed\n");
-                    return STATUS_INTERNAL_ERROR;
+                Status = insert_tree_item(Vcb, Vcb->extent_root, mr->new_address, TYPE_SHARED_BLOCK_REF, sbr->offset, sbr, sizeof(SHARED_BLOCK_REF), NULL, NULL);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("insert_tree_item returned %08x\n", Status);
+                    return Status;
                 }
             }
             
@@ -409,8 +426,7 @@
                     
                     sbr.offset = mr->new_address;
                     
-                    Status = increase_extent_refcount(Vcb, in[i].address, Vcb->superblock.node_size, TYPE_SHARED_BLOCK_REF, &sbr, NULL, 0,
-                                                      NULL, rollback);
+                    Status = increase_extent_refcount(Vcb, in[i].address, Vcb->superblock.node_size, TYPE_SHARED_BLOCK_REF, &sbr, NULL, 0, NULL);
                     if (!NT_SUCCESS(Status)) {
                         ERR("increase_extent_refcount returned %08x\n", Status);
                         return Status;
@@ -419,7 +435,7 @@
                     sbr.offset = mr->address;
                     
                     Status = decrease_extent_refcount(Vcb, in[i].address, Vcb->superblock.node_size, TYPE_SHARED_BLOCK_REF, &sbr, NULL, 0,
-                                                      sbr.offset, FALSE, NULL, rollback);
+                                                      sbr.offset, FALSE, NULL);
                     if (!NT_SUCCESS(Status)) {
                         ERR("decrease_extent_refcount returned %08x\n", Status);
                         return Status;
@@ -448,8 +464,7 @@
                                 sdr.offset = mr->new_address;
                                 sdr.count = sdrrc;
                                 
-                                Status = increase_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0,
-                                                                  NULL, rollback);
+                                Status = increase_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, NULL);
                                 if (!NT_SUCCESS(Status)) {
                                     ERR("increase_extent_refcount returned %08x\n", Status);
                                     return Status;
@@ -458,7 +473,7 @@
                                 sdr.offset = mr->address;
                                 
                                 Status = decrease_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0,
-                                                                  sdr.offset, FALSE, NULL, rollback);
+                                                                  sdr.offset, FALSE, NULL);
                                 if (!NT_SUCCESS(Status)) {
                                     ERR("decrease_extent_refcount returned %08x\n", Status);
                                     return Status;
@@ -546,7 +561,7 @@
         }
         
         Status = read_data(Vcb, mr->address, Vcb->superblock.node_size, NULL, TRUE, (UINT8*)mr->data,
-                           c && mr->address >= c->offset && mr->address < c->offset + c->chunk_item->size ? c : NULL, &pc, NULL, FALSE);
+                           c && mr->address >= c->offset && mr->address < c->offset + c->chunk_item->size ? c : NULL, &pc, NULL, 0, FALSE, 0);
         if (!NT_SUCCESS(Status)) {
             ERR("read_data returned %08x\n", Status);
             return Status;
@@ -705,7 +720,7 @@
                     ExAcquireResourceExclusiveLite(&newchunk->lock, TRUE);
                     
                     if (newchunk->chunk_item->type == flags && find_metadata_address_in_chunk(Vcb, newchunk, &mr->new_address)) {
-                        increase_chunk_usage(newchunk, Vcb->superblock.node_size);
+                        newchunk->used += Vcb->superblock.node_size;
                         space_list_subtract(Vcb, newchunk, FALSE, mr->new_address, Vcb->superblock.node_size, rollback);
                         done = TRUE;
                     }
@@ -725,7 +740,7 @@
                             
                             if ((c2->chunk_item->size - c2->used) >= Vcb->superblock.node_size) {
                                 if (find_metadata_address_in_chunk(Vcb, c2, &mr->new_address)) {
-                                    increase_chunk_usage(c2, Vcb->superblock.node_size);
+                                    c2->used += Vcb->superblock.node_size;
                                     space_list_subtract(Vcb, c2, FALSE, mr->new_address, Vcb->superblock.node_size, rollback);
                                     ExReleaseResourceLite(&c2->lock);
                                     newchunk = c2;
@@ -759,7 +774,7 @@
                             Status = STATUS_DISK_FULL;
                             goto end;
                         } else {
-                            increase_chunk_usage(newchunk, Vcb->superblock.node_size);
+                            newchunk->used += Vcb->superblock.node_size;
                             space_list_subtract(Vcb, newchunk, FALSE, mr->new_address, Vcb->superblock.node_size, rollback);
                         }
                         
@@ -854,11 +869,15 @@
                                 
                                 RtlCopyMemory(ri, &r->root_item, sizeof(ROOT_ITEM));
                                 
-                                delete_tree_item(Vcb, &tp, rollback);
+                                Status = delete_tree_item(Vcb, &tp);
+                                if (!NT_SUCCESS(Status)) {
+                                    ERR("delete_tree_item returned %08x\n", Status);
+                                    goto end;
+                                }
                                 
-                                if (!insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, NULL, rollback)) {
-                                    ERR("insert_tree_item failed\n");
-                                    Status = STATUS_INTERNAL_ERROR;
+                                Status = insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, NULL);
+                                if (!NT_SUCCESS(Status)) {
+                                    ERR("insert_tree_item returned %08x\n", Status);
                                     goto end;
                                 }
                             }
@@ -1021,7 +1040,7 @@
     while (le != items) {
         metadata_reloc* mr = CONTAINING_RECORD(le, metadata_reloc, list_entry);
         
-        Status = add_metadata_reloc_extent_item(Vcb, mr, rollback);
+        Status = add_metadata_reloc_extent_item(Vcb, mr);
         if (!NT_SUCCESS(Status)) {
             ERR("add_metadata_reloc_extent_item returned %08x\n", Status);
             goto end;
@@ -1132,7 +1151,7 @@
     
 end:
     if (NT_SUCCESS(Status))
-        clear_rollback(Vcb, &rollback);
+        clear_rollback(&rollback);
     else
         do_rollback(Vcb, &rollback);
     
@@ -1154,6 +1173,7 @@
 }
 
 static NTSTATUS add_data_reloc(device_extension* Vcb, LIST_ENTRY* items, LIST_ENTRY* metadata_items, traverse_ptr* tp, chunk* c, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
     data_reloc* dr;
     EXTENT_ITEM* ei;
     UINT16 len;
@@ -1171,7 +1191,11 @@
     dr->ei = (EXTENT_ITEM*)tp->item->data;
     InitializeListHead(&dr->refs);
     
-    delete_tree_item(Vcb, tp, rollback);
+    Status = delete_tree_item(Vcb, tp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("delete_tree_item returned %08x\n", Status);
+        return Status;
+    }
     
     if (!c)
         c = get_chunk_from_address(Vcb, tp->item->key.obj_id);
@@ -1179,7 +1203,7 @@
     if (c) {
         ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
-        decrease_chunk_usage(c, tp->item->key.offset);
+        c->used -= tp->item->key.offset;
         
         space_list_add(Vcb, c, TRUE, tp->item->key.obj_id, tp->item->key.offset, rollback);
         
@@ -1366,7 +1390,11 @@
                     ref->parent = mr;
                     InsertTailList(&dr->refs, &ref->list_entry);
                     
-                    delete_tree_item(Vcb, &tp2, rollback);
+                    Status = delete_tree_item(Vcb, &tp2);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_tree_item returned %08x\n", Status);
+                        return Status;
+                    }
                 } else if (tp2.item->key.obj_type == TYPE_SHARED_DATA_REF && tp2.item->size >= sizeof(SHARED_DATA_REF)) {
                     data_reloc_ref* ref = ExAllocatePoolWithTag(PagedPool, sizeof(data_reloc_ref), ALLOC_TAG);
                     if (!ref) {
@@ -1387,7 +1415,11 @@
                     ref->parent = mr;
                     InsertTailList(&dr->refs, &ref->list_entry);
                     
-                    delete_tree_item(Vcb, &tp2, rollback);
+                    Status = delete_tree_item(Vcb, &tp2);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_tree_item returned %08x\n", Status);
+                        return Status;
+                    }
                 }
             } else
                 break;
@@ -1399,7 +1431,8 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS add_data_reloc_extent_item(device_extension* Vcb, data_reloc* dr, LIST_ENTRY* rollback) {
+static NTSTATUS add_data_reloc_extent_item(device_extension* Vcb, data_reloc* dr) {
+    NTSTATUS Status;
     LIST_ENTRY* le;
     UINT64 rc = 0;
     UINT16 inline_len;
@@ -1472,9 +1505,10 @@
         le = le->Flink;
     }
     
-    if (!insert_tree_item(Vcb, Vcb->extent_root, dr->new_address, TYPE_EXTENT_ITEM, dr->size, ei, inline_len, NULL, NULL, rollback)) {
-        ERR("insert_tree_item failed\n");
-        return STATUS_INTERNAL_ERROR;
+    Status = insert_tree_item(Vcb, Vcb->extent_root, dr->new_address, TYPE_EXTENT_ITEM, dr->size, ei, inline_len, NULL, NULL);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
+        return Status;
     }
     
     if (!all_inline) {
@@ -1497,9 +1531,10 @@
                 
                 off = get_extent_data_ref_hash2(ref->edr.root, ref->edr.objid, ref->edr.offset);
                 
-                if (!insert_tree_item(Vcb, Vcb->extent_root, dr->new_address, TYPE_EXTENT_DATA_REF, off, edr, sizeof(EXTENT_DATA_REF), NULL, NULL, rollback)) {
-                    ERR("insert_tree_item failed\n");
-                    return STATUS_INTERNAL_ERROR;
+                Status = insert_tree_item(Vcb, Vcb->extent_root, dr->new_address, TYPE_EXTENT_DATA_REF, off, edr, sizeof(EXTENT_DATA_REF), NULL, NULL);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("insert_tree_item returned %08x\n", Status);
+                    return Status;
                 }
             } else if (ref->type == TYPE_SHARED_DATA_REF) {
                 SHARED_DATA_REF* sdr;
@@ -1513,9 +1548,10 @@
                 sdr->offset = ref->parent->new_address;
                 sdr->count = ref->sdr.count;
                 
-                if (!insert_tree_item(Vcb, Vcb->extent_root, dr->new_address, TYPE_SHARED_DATA_REF, sdr->offset, sdr, sizeof(SHARED_DATA_REF), NULL, NULL, rollback)) {
-                    ERR("insert_tree_item failed\n");
-                    return STATUS_INTERNAL_ERROR;
+                Status = insert_tree_item(Vcb, Vcb->extent_root, dr->new_address, TYPE_SHARED_DATA_REF, sdr->offset, sdr, sizeof(SHARED_DATA_REF), NULL, NULL);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("insert_tree_item returned %08x\n", Status);
+                    return Status;
                 }
             }
             
@@ -1618,7 +1654,7 @@
             ExAcquireResourceExclusiveLite(&newchunk->lock, TRUE);
             
             if (find_data_address_in_chunk(Vcb, newchunk, dr->size, &dr->new_address)) {
-                increase_chunk_usage(newchunk, dr->size);
+                newchunk->used += dr->size;
                 space_list_subtract(Vcb, newchunk, FALSE, dr->new_address, dr->size, &rollback);
                 done = TRUE;
             }
@@ -1638,7 +1674,7 @@
                     
                     if ((c2->chunk_item->size - c2->used) >= dr->size) {
                         if (find_data_address_in_chunk(Vcb, c2, dr->size, &dr->new_address)) {
-                            increase_chunk_usage(c2, dr->size);
+                            c2->used += dr->size;
                             space_list_subtract(Vcb, c2, FALSE, dr->new_address, dr->size, &rollback);
                             ExReleaseResourceLite(&c2->lock);
                             newchunk = c2;
@@ -1672,7 +1708,7 @@
                     Status = STATUS_DISK_FULL;
                     goto end;
                 } else {
-                    increase_chunk_usage(newchunk, dr->size);
+                    newchunk->used += dr->size;
                     space_list_subtract(Vcb, newchunk, FALSE, dr->new_address, dr->size, &rollback);
                 }
                 
@@ -1694,8 +1730,8 @@
         Status = load_csum(Vcb, csum, dr->address, dr->size / Vcb->superblock.sector_size, NULL);
 
         if (NT_SUCCESS(Status)) {
-            add_checksum_entry(Vcb, dr->new_address, dr->size / Vcb->superblock.sector_size, csum, NULL, &rollback);
-            add_checksum_entry(Vcb, dr->address, dr->size / Vcb->superblock.sector_size, NULL, NULL, &rollback);
+            add_checksum_entry(Vcb, dr->new_address, dr->size / Vcb->superblock.sector_size, csum, NULL);
+            add_checksum_entry(Vcb, dr->address, dr->size / Vcb->superblock.sector_size, NULL, NULL);
         }
 
         ExFreePool(csum);
@@ -1705,13 +1741,13 @@
         while (off < dr->size) {
             ULONG ds = min(dr->size - off, 0x100000);
             
-            Status = read_data(Vcb, dr->address + off, ds, NULL, FALSE, data, c, NULL, NULL, FALSE);
+            Status = read_data(Vcb, dr->address + off, ds, NULL, FALSE, data, c, NULL, NULL, 0, FALSE, 0);
             if (!NT_SUCCESS(Status)) {
                 ERR("read_data returned %08x\n", Status);
                 goto end;
             }
             
-            Status = write_data_complete(Vcb, dr->new_address + off, data, ds, NULL, newchunk);
+            Status = write_data_complete(Vcb, dr->new_address + off, data, ds, NULL, newchunk, FALSE, 0);
             if (!NT_SUCCESS(Status)) {
                 ERR("write_data_complete returned %08x\n", Status);
                 goto end;
@@ -1736,7 +1772,7 @@
     while (le != &items) {
         data_reloc* dr = CONTAINING_RECORD(le, data_reloc, list_entry);
         
-        Status = add_data_reloc_extent_item(Vcb, dr, &rollback);
+        Status = add_data_reloc_extent_item(Vcb, dr);
         if (!NT_SUCCESS(Status)) {
             ERR("add_data_reloc_extent_item returned %08x\n", Status);
             goto end;
@@ -1786,8 +1822,8 @@
             extent* ext = CONTAINING_RECORD(le2, extent, list_entry);
             
             if (!ext->ignore) {
-                if (ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC) {
-                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
+                if (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) {
+                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
                     
                     if (ed2->size > 0 && ed2->address >= c->offset && ed2->address < c->offset + c->chunk_item->size) {
                         LIST_ENTRY* le3 = items.Flink;
@@ -1821,7 +1857,7 @@
     
 end:
     if (NT_SUCCESS(Status))
-        clear_rollback(Vcb, &rollback);
+        clear_rollback(&rollback);
     else
         do_rollback(Vcb, &rollback);
     
@@ -2027,14 +2063,11 @@
 }
 
 static NTSTATUS add_balance_item(device_extension* Vcb) {
-    LIST_ENTRY rollback;
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
     BALANCE_ITEM* bi;
     
-    InitializeListHead(&rollback);
-    
     searchkey.obj_id = BALANCE_ITEM_ID;
     searchkey.obj_type = TYPE_TEMP_ITEM;
     searchkey.offset = 0;
@@ -2047,8 +2080,13 @@
         goto end;
     }
     
-    if (!keycmp(tp.item->key, searchkey))
-        delete_tree_item(Vcb, &tp, &rollback);
+    if (!keycmp(tp.item->key, searchkey)) {
+        Status = delete_tree_item(Vcb, &tp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            goto end;
+        }
+    }
     
     bi = ExAllocatePoolWithTag(PagedPool, sizeof(BALANCE_ITEM), ALLOC_TAG);
     if (!bi) {
@@ -2074,9 +2112,9 @@
         copy_balance_args(&Vcb->balance.opts[BALANCE_OPTS_SYSTEM], &bi->system);
     }
     
-    if (!insert_tree_item(Vcb, Vcb->root_root, BALANCE_ITEM_ID, TYPE_TEMP_ITEM, 0, bi, sizeof(BALANCE_ITEM), NULL, NULL, &rollback)) {
-        ERR("insert_tree_item failed\n");
-        Status = STATUS_INTERNAL_ERROR;
+    Status = insert_tree_item(Vcb, Vcb->root_root, BALANCE_ITEM_ID, TYPE_TEMP_ITEM, 0, bi, sizeof(BALANCE_ITEM), NULL, NULL);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
         goto end;
     }
     
@@ -2084,13 +2122,13 @@
     
 end:
     if (NT_SUCCESS(Status)) {
-        do_write(Vcb, NULL, &rollback);
-        free_trees(Vcb);
-        
-        clear_rollback(Vcb, &rollback);
-    } else
-        do_rollback(Vcb, &rollback);
+        Status = do_write(Vcb, NULL);
+        if (!NT_SUCCESS(Status))
+            ERR("do_write returned %08x\n", Status);
+    }
     
+    free_trees(Vcb);
+    
     ExReleaseResourceLite(&Vcb->tree_lock);
     
     return Status;
@@ -2097,13 +2135,10 @@
 }
 
 static NTSTATUS remove_balance_item(device_extension* Vcb) {
-    LIST_ENTRY rollback;
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
     
-    InitializeListHead(&rollback);
-    
     searchkey.obj_id = BALANCE_ITEM_ID;
     searchkey.obj_type = TYPE_TEMP_ITEM;
     searchkey.offset = 0;
@@ -2117,20 +2152,24 @@
     }
     
     if (!keycmp(tp.item->key, searchkey)) {
-        delete_tree_item(Vcb, &tp, &rollback);
+        Status = delete_tree_item(Vcb, &tp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            goto end;
+        }
         
-        do_write(Vcb, NULL, &rollback);
+        Status = do_write(Vcb, NULL);
+        if (!NT_SUCCESS(Status)) {
+            ERR("do_write returned %08x\n", Status);
+            goto end;
+        }
+        
         free_trees(Vcb);
     }
 
     Status = STATUS_SUCCESS;
     
-end:
-    if (NT_SUCCESS(Status))
-        clear_rollback(Vcb, &rollback);
-    else
-        do_rollback(Vcb, &rollback);
-    
+end:   
     ExReleaseResourceLite(&Vcb->tree_lock);
     
     return Status;
@@ -2214,7 +2253,7 @@
     
     RtlZeroMemory(sb, sizeof(superblock));
     
-    while (superblock_addrs[i] > 0 && dev->length >= superblock_addrs[i] + sizeof(superblock)) {
+    while (superblock_addrs[i] > 0 && dev->devitem.num_bytes >= superblock_addrs[i] + sizeof(superblock)) {
         Status = write_data_phys(dev->devobj, superblock_addrs[i], sb, sizeof(superblock));
         
         if (!NT_SUCCESS(Status)) {
@@ -2230,174 +2269,25 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS replace_mount_dev(device_extension* Vcb, device* dev, PDEVICE_OBJECT mountmgr, BOOL part0) {
-    NTSTATUS Status;
-    MOUNTDEV_NAME mdn, *mdn2 = NULL, *mdn3 = NULL;
-    ULONG mdnsize, mmpsize;
-    MOUNTMGR_MOUNT_POINT* mmp = NULL;
-    MOUNTMGR_MOUNT_POINTS mmps, *mmps2 = NULL;
-    ULONG i;
-    UNICODE_STRING us;
-
-    // get old device name
-    
-    Status = dev_ioctl(dev->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), TRUE, NULL);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
-        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
-        return Status;
-    }
-    
-    mdnsize = offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
-    
-    mdn2 = ExAllocatePoolWithTag(PagedPool, mdnsize, ALLOC_TAG);
-    if (!mdn2) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    Status = dev_ioctl(dev->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, TRUE, NULL);
-    if (!NT_SUCCESS(Status)) {
-        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
-        goto end;
-    }
-    
-    // get new device name
-    
-    Status = dev_ioctl(first_device(Vcb)->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), TRUE, NULL);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
-        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
-        goto end2;
-    }
-    
-    mdnsize = offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
-    
-    mdn3 = ExAllocatePoolWithTag(PagedPool, mdnsize, ALLOC_TAG);
-    if (!mdn3) {
-        ERR("out of memory\n");
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto end2;
-    }
-    
-    Status = dev_ioctl(first_device(Vcb)->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn3, mdnsize, TRUE, NULL);
-    if (!NT_SUCCESS(Status)) {
-        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
-        goto end2;
-    }
-    
-    // query and delete existing mount points
-    
-    mmpsize = sizeof(MOUNTMGR_MOUNT_POINT) + mdn2->NameLength;
-    
-    mmp = ExAllocatePoolWithTag(PagedPool, mmpsize, ALLOC_TAG);
-    if (!mmp) {
-        ERR("out of memory\n");
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto end2;
-    }
-    
-    RtlZeroMemory(mmp, sizeof(MOUNTMGR_MOUNT_POINT));
-    mmp->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
-    mmp->DeviceNameLength = mdn2->NameLength;
-    RtlCopyMemory(&mmp[1], mdn2->Name, mdn2->NameLength);
-    
-    Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_QUERY_POINTS, mmp, mmpsize, &mmps, mmpsize, TRUE, NULL);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
-        ERR("IOCTL_MOUNTMGR_QUERY_POINTS returned %08x\n", Status);
-        goto end2;
-    }
-    
-    mmps2 = ExAllocatePoolWithTag(PagedPool, mmps.Size, ALLOC_TAG);
-    if (!mmps2) {
-        ERR("out of memory\n");
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto end2;
-    }
-    
-    Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_DELETE_POINTS, mmp, mmpsize, mmps2, mmps.Size, TRUE, NULL);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
-        ERR("IOCTL_MOUNTMGR_DELETE_POINTS returned %08x\n", Status);
-        goto end2;
-    }
-    
-    // re-create mount points
-    
-    for (i = 0; i < mmps2->NumberOfMountPoints; i++) {
-        if (mmps2->MountPoints[i].SymbolicLinkNameOffset != 0) {
-            ULONG mcpilen;
-            MOUNTMGR_CREATE_POINT_INPUT* mcpi;
-            
-            mcpilen = sizeof(MOUNTMGR_CREATE_POINT_INPUT) + mmps2->MountPoints[i].SymbolicLinkNameLength + mdn3->NameLength;
-            
-            mcpi = ExAllocatePoolWithTag(PagedPool, mcpilen, ALLOC_TAG);
-            if (!mcpi) {
-                ERR("out of memory\n");
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto end2;
-            }
-            
-            mcpi->SymbolicLinkNameOffset = sizeof(MOUNTMGR_CREATE_POINT_INPUT);
-            mcpi->SymbolicLinkNameLength = mmps2->MountPoints[i].SymbolicLinkNameLength;
-            mcpi->DeviceNameOffset = mcpi->SymbolicLinkNameOffset + mcpi->SymbolicLinkNameLength;
-            mcpi->DeviceNameLength = mdn3->NameLength;
-            
-            RtlCopyMemory((UINT8*)mcpi + mcpi->SymbolicLinkNameOffset, (UINT8*)mmps2 + mmps2->MountPoints[i].SymbolicLinkNameOffset,
-                          mcpi->SymbolicLinkNameLength);
-            RtlCopyMemory((UINT8*)mcpi + mcpi->DeviceNameOffset, mdn3->Name, mdn3->NameLength);
-
-            Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_CREATE_POINT, mcpi, mcpilen, NULL, 0, TRUE, NULL);
-            if (!NT_SUCCESS(Status)) {
-                ERR("IOCTL_MOUNTMGR_CREATE_POINT returned %08x\n", Status);
-                ExFreePool(mcpi);
-                goto end2;
-            }
-            
-            ExFreePool(mcpi);
-        }
-    }
-    
-    Status = STATUS_SUCCESS;
-    
-end2:
-    // re-add old device back to mountmgr
-    
-    if (!part0) {
-        us.Buffer = mdn2->Name;
-        us.Length = us.MaximumLength = mdn2->NameLength;
-        
-        add_volume(mountmgr, &us);
-    }
-    
-end:
-    if (mdn2)
-        ExFreePool(mdn2);
-    
-    if (mdn3)
-        ExFreePool(mdn3);
-    
-    if (mmp)
-        ExFreePool(mmp);
-    
-    if (mmps2)
-        ExFreePool(mmps2);
-
-    return Status;
-}
-
 static NTSTATUS finish_removing_device(device_extension* Vcb, device* dev) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
-    LIST_ENTRY rollback, *le;
-    BOOL first_dev, part0 = FALSE;
+    LIST_ENTRY* le;
+    volume_device_extension* vde;
     
-    InitializeListHead(&rollback);
+    if (Vcb->need_write) {
+        Status = do_write(Vcb, NULL);
+        
+        if (!NT_SUCCESS(Status))
+            ERR("do_write returned %08x\n", Status);
+    } else
+        Status = STATUS_SUCCESS;
     
-    if (Vcb->need_write)
-        do_write(Vcb, NULL, &rollback);
-    
     free_trees(Vcb);
     
-    clear_rollback(Vcb, &rollback);
+    if (!NT_SUCCESS(Status))
+        return Status;
     
     // remove entry in chunk tree
 
@@ -2411,8 +2301,14 @@
         return Status;
     }
 
-    if (!keycmp(searchkey, tp.item->key))
-        delete_tree_item(Vcb, &tp, &rollback);
+    if (!keycmp(searchkey, tp.item->key)) {
+        Status = delete_tree_item(Vcb, &tp);
+        
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            return Status;
+        }
+    }
     
     // remove stats entry in device tree
     
@@ -2426,8 +2322,14 @@
         return Status;
     }
 
-    if (!keycmp(searchkey, tp.item->key))
-        delete_tree_item(Vcb, &tp, &rollback);
+    if (!keycmp(searchkey, tp.item->key)) {
+        Status = delete_tree_item(Vcb, &tp);
+        
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            return Status;
+        }
+    }
     
     // update superblock
     
@@ -2435,17 +2337,18 @@
     Vcb->superblock.total_bytes -= dev->devitem.num_bytes;
     Vcb->devices_loaded--;
     
-    first_dev = first_device(Vcb) == dev;
-    
     RemoveEntryList(&dev->list_entry);
     
     // flush
     
-    do_write(Vcb, NULL, &rollback);
+    Status = do_write(Vcb, NULL);
+    if (!NT_SUCCESS(Status))
+        ERR("do_write returned %08x\n", Status);
     
     free_trees(Vcb);
     
-    clear_rollback(Vcb, &rollback);
+    if (!NT_SUCCESS(Status))
+        return Status;
     
     if (!dev->readonly) {
         Status = remove_superblocks(dev);
@@ -2455,39 +2358,66 @@
     
     // remove entry in volume list
     
-    ExAcquireResourceExclusiveLite(&volumes_lock, TRUE);
+    vde = Vcb->vde;
     
-    le = volumes.Flink;
-    while (le != &volumes) {
-        volume* v = CONTAINING_RECORD(le, volume, list_entry);
+    ExAcquireResourceExclusiveLite(&vde->child_lock, TRUE);
+    
+    le = vde->children.Flink;
+    while (le != &vde->children) {
+        volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
         
-        if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
-            RtlCompareMemory(&dev->devitem.device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+        if (RtlCompareMemory(&dev->devitem.device_uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
             PFILE_OBJECT FileObject;
             PDEVICE_OBJECT mountmgr;
             UNICODE_STRING mmdevpath;
         
-            RemoveEntryList(&v->list_entry);
-        
-            // re-add entry to mountmgr
-
-            if (!first_dev && v->part_num != 0) {
+            vde->children_loaded--;
+            
+            if (vc->had_drive_letter) { // re-add entry to mountmgr
                 RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
                 Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
                 if (!NT_SUCCESS(Status))
                     ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
                 else {
-                    add_volume(mountmgr, &v->devpath);
+                    MOUNTDEV_NAME mdn;
+                    
+                    Status = dev_ioctl(dev->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), TRUE, NULL);
+                    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
+                        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
+                    else {
+                        MOUNTDEV_NAME* mdn2;
+                        ULONG mdnsize = offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
+                        
+                        mdn2 = ExAllocatePoolWithTag(PagedPool, mdnsize, ALLOC_TAG);
+                        if (!mdn2)
+                            ERR("out of memory\n");
+                        else {
+                            Status = dev_ioctl(dev->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, TRUE, NULL);
+                            if (!NT_SUCCESS(Status))
+                                ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
+                            else {
+                                UNICODE_STRING name;
+                                
+                                name.Buffer = mdn2->Name;
+                                name.Length = name.MaximumLength = mdn2->NameLength;
+                                
+                                Status = mountmgr_add_drive_letter(mountmgr, &name);
+                                if (!NT_SUCCESS(Status))
+                                    WARN("mountmgr_add_drive_letter returned %08x\n", Status);
+                            }
+                            
+                            ExFreePool(mdn2);
+                        }
+                    }
+                    
                     ObDereferenceObject(FileObject);
                 }
             }
             
-            part0 = v->part_num == 0 ? TRUE : FALSE;
-        
-            if (v->devpath.Buffer)
-                ExFreePool(v->devpath.Buffer);
+            ExFreePool(vc->pnp_name.Buffer);
+            RemoveEntryList(&vc->list_entry);
+            ExFreePool(vc);
             
-            ExFreePool(v);
             break;
         }
         
@@ -2494,40 +2424,31 @@
         le = le->Flink;
     }
     
-    ExReleaseResourceLite(&volumes_lock);
-    
-    if (first_dev) {
-        PDEVICE_OBJECT DeviceObject, olddev;
-        device* newfirstdev;
-        PFILE_OBJECT FileObject;
-        UNICODE_STRING mmdevpath;
-        PDEVICE_OBJECT mountmgr;
+    if (vde->children_loaded > 0 && vde->device->Characteristics & FILE_REMOVABLE_MEDIA) {
+        vde->device->Characteristics &= ~FILE_REMOVABLE_MEDIA;
         
-        DeviceObject = Vcb->Vpb->DeviceObject;
-        
-        olddev = DeviceObject->Vpb->RealDevice;
-        newfirstdev = first_device(Vcb);
-        
-        ObReferenceObject(newfirstdev->devobj);
-        DeviceObject->Vpb->RealDevice = newfirstdev->devobj;
-        ObDereferenceObject(olddev);
-        
-        RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
-        Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
-        if (!NT_SUCCESS(Status))
-            ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
-        else {
-            Status = replace_mount_dev(Vcb, dev, mountmgr, part0);
-            if (!NT_SUCCESS(Status))
-                ERR("replace_mount_dev returned %08x\n", Status);
+        le = vde->children.Flink;
+        while (le != &vde->children) {
+            volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
             
-            ObDereferenceObject(FileObject);
+            if (vc->devobj->Characteristics & FILE_REMOVABLE_MEDIA) {
+                vde->device->Characteristics |= FILE_REMOVABLE_MEDIA;
+                break;
+            }
+            
+            le = le->Flink;
         }
-        
     }
     
+    vde->num_children = Vcb->superblock.num_devices;
+    
+    ExReleaseResourceLite(&vde->child_lock);
+    
     // free dev
     
+    if (dev->trim && !dev->readonly && !Vcb->options.no_trim)
+        trim_whole_device(dev);
+    
     ObDereferenceObject(dev->devobj);
     
     while (!IsListEmpty(&dev->space)) {
@@ -2539,9 +2460,127 @@
     
     ExFreePool(dev);
     
+    if (Vcb->trim) {
+        Vcb->trim = FALSE;
+        
+        le = Vcb->devices.Flink;
+        while (le != &Vcb->devices) {
+            device* dev2 = CONTAINING_RECORD(le, device, list_entry);
+            
+            if (dev2->trim) {
+                Vcb->trim = TRUE;
+                break;
+            }
+            
+            le = le->Flink;
+        }
+    }
+    
+    FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_CHANGE_SIZE);
+    
     return STATUS_SUCCESS;
 }
 
+static void trim_unalloc_space(device_extension* Vcb, device* dev) {
+    DEVICE_MANAGE_DATA_SET_ATTRIBUTES* dmdsa;
+    DEVICE_DATA_SET_RANGE* ranges;
+    ULONG datalen, i;
+    KEY searchkey;
+    traverse_ptr tp;
+    NTSTATUS Status;
+    BOOL b;
+    UINT64 lastoff = 0x100000; // don't TRIM the first megabyte, in case someone has been daft enough to install GRUB there
+    LIST_ENTRY* le;
+    
+    dev->num_trim_entries = 0;
+    
+    searchkey.obj_id = dev->devitem.dev_id;
+    searchkey.obj_type = TYPE_DEV_EXTENT;
+    searchkey.offset = 0;
+    
+    Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, NULL);
+    if (!NT_SUCCESS(Status)) {
+        ERR("find_item returned %08x\n", Status);
+        return;
+    }
+    
+    do {
+        traverse_ptr next_tp;
+        
+        if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
+            if (tp.item->size >= sizeof(DEV_EXTENT)) {
+                DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
+                
+                if (tp.item->key.offset > lastoff)
+                    add_trim_entry_avoid_sb(Vcb, dev, lastoff, tp.item->key.offset - lastoff);
+
+                lastoff = tp.item->key.offset + de->length;
+            } else {
+                ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_EXTENT));
+                return;
+            }
+        }
+    
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, NULL);
+        
+        if (b) {
+            tp = next_tp;
+            if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type))
+                break;
+        }
+    } while (b);
+    
+    if (lastoff < dev->devitem.num_bytes)
+        add_trim_entry_avoid_sb(Vcb, dev, lastoff, dev->devitem.num_bytes - lastoff);
+    
+    if (dev->num_trim_entries == 0)
+        return;
+    
+    datalen = sector_align(sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES), sizeof(UINT64)) + (dev->num_trim_entries * sizeof(DEVICE_DATA_SET_RANGE));
+    
+    dmdsa = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG);
+    if (!dmdsa) {
+        ERR("out of memory\n");
+        goto end;
+    }
+        
+    dmdsa->Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
+    dmdsa->Action = DeviceDsmAction_Trim;
+    dmdsa->Flags = DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED;
+    dmdsa->ParameterBlockOffset = 0;
+    dmdsa->ParameterBlockLength = 0;
+    dmdsa->DataSetRangesOffset = sector_align(sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES), sizeof(UINT64));
+    dmdsa->DataSetRangesLength = dev->num_trim_entries * sizeof(DEVICE_DATA_SET_RANGE);
+    
+    ranges = (DEVICE_DATA_SET_RANGE*)((UINT8*)dmdsa + dmdsa->DataSetRangesOffset);
+    
+    i = 0;
+    le = dev->trim_list.Flink;
+    while (le != &dev->trim_list) {
+        space* s = CONTAINING_RECORD(le, space, list_entry);
+        
+        ranges[i].StartingOffset = s->address;
+        ranges[i].LengthInBytes = s->size;
+        i++;
+        
+        le = le->Flink;
+    }
+    
+    Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES, dmdsa, datalen, NULL, 0, TRUE, NULL);
+    if (!NT_SUCCESS(Status))
+        WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08x\n", Status);
+    
+    ExFreePool(dmdsa);
+    
+end:
+    while (!IsListEmpty(&dev->trim_list)) {
+        space* s = CONTAINING_RECORD(RemoveHeadList(&dev->trim_list), space, list_entry);
+        ExFreePool(s);
+    }
+    
+    dev->num_trim_entries = 0;
+}
+
 #ifndef __REACTOS__
 static void balance_thread(void* context) {
 #else
@@ -2554,11 +2593,13 @@
     NTSTATUS Status;
     
     Vcb->balance.stopping = FALSE;
-    Vcb->balance.cancelling = FALSE;
     KeInitializeEvent(&Vcb->balance.finished, NotificationEvent, FALSE);
     
-    if (Vcb->balance.opts[BALANCE_OPTS_DATA].flags & BTRFS_BALANCE_OPTS_ENABLED && Vcb->balance.opts[BALANCE_OPTS_DATA].flags & BTRFS_BALANCE_OPTS_CONVERT)
+    if (Vcb->balance.opts[BALANCE_OPTS_DATA].flags & BTRFS_BALANCE_OPTS_ENABLED && Vcb->balance.opts[BALANCE_OPTS_DATA].flags & BTRFS_BALANCE_OPTS_CONVERT) {
         Vcb->data_flags = BLOCK_FLAG_DATA | (Vcb->balance.opts[BALANCE_OPTS_DATA].convert == BLOCK_FLAG_SINGLE ? 0 : Vcb->balance.opts[BALANCE_OPTS_DATA].convert);
+        
+        FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_CHANGE_SIZE);
+    }
     
     if (Vcb->balance.opts[BALANCE_OPTS_METADATA].flags & BTRFS_BALANCE_OPTS_ENABLED && Vcb->balance.opts[BALANCE_OPTS_METADATA].flags & BTRFS_BALANCE_OPTS_CONVERT)
         Vcb->metadata_flags = BLOCK_FLAG_METADATA | (Vcb->balance.opts[BALANCE_OPTS_METADATA].convert == BLOCK_FLAG_SINGLE ? 0 : Vcb->balance.opts[BALANCE_OPTS_METADATA].convert);
@@ -2584,13 +2625,14 @@
             }
         } else {
             if (Vcb->need_write) {
-                LIST_ENTRY rollback;
+                Status = do_write(Vcb, NULL);
                 
-                InitializeListHead(&rollback);
-                do_write(Vcb, NULL, &rollback);
                 free_trees(Vcb);
                 
-                clear_rollback(Vcb, &rollback);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("do_write returned %08x\n", Status);
+                    goto end;
+                }
             }
         }
     }
@@ -2655,6 +2697,21 @@
             NTSTATUS Status;
             BOOL changed;
             
+            if (!c->cache_loaded) {
+                ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+                
+                Status = load_cache_chunk(Vcb, c, NULL);
+                
+                if (!NT_SUCCESS(Status)) {
+                    ERR("load_cache_chunk returned %08x\n", Status);
+                    Vcb->balance.status = Status;
+                    ExReleaseResourceLite(&c->lock);
+                    goto end;
+                }
+                
+                ExReleaseResourceLite(&c->lock);
+            }
+            
             do {
                 changed = FALSE;
                 
@@ -2711,6 +2768,21 @@
         c = CONTAINING_RECORD(le, chunk, list_entry_balance);
         
         if (c->chunk_item->type & BLOCK_FLAG_METADATA || c->chunk_item->type & BLOCK_FLAG_SYSTEM) {
+            if (!c->cache_loaded) {
+                ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+                
+                Status = load_cache_chunk(Vcb, c, NULL);
+                
+                if (!NT_SUCCESS(Status)) {
+                    ERR("load_cache_chunk returned %08x\n", Status);
+                    Vcb->balance.status = Status;
+                    ExReleaseResourceLite(&c->lock);
+                    goto end;
+                }
+                
+                ExReleaseResourceLite(&c->lock);
+            }
+            
             do {
                 FsRtlEnterFileSystem();
                 
@@ -2794,6 +2866,24 @@
             ExReleaseResourceLite(&Vcb->tree_lock);
             FsRtlExitFileSystem();
         }
+        
+        if (Vcb->trim && !Vcb->options.no_trim) {
+            FsRtlEnterFileSystem();
+            ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+            
+            le = Vcb->devices.Flink;
+            while (le != &Vcb->devices) {
+                device* dev2 = CONTAINING_RECORD(le, device, list_entry);
+                
+                if (dev2->devobj && !dev2->readonly && dev2->trim)
+                    trim_unalloc_space(Vcb, dev2);
+                
+                le = le->Flink;
+            }
+            
+            ExReleaseResourceLite(&Vcb->tree_lock);
+            FsRtlExitFileSystem();
+        }
     }
     
     ZwClose(Vcb->balance.thread);
@@ -2813,6 +2903,16 @@
     if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
         return STATUS_PRIVILEGE_NOT_HELD;
     
+    if (Vcb->locked) {
+        WARN("cannot start balance while locked\n");
+        return STATUS_DEVICE_NOT_READY;
+    }
+    
+    if (Vcb->scrub.thread) {
+        WARN("cannot start balance while scrub running\n");
+        return STATUS_DEVICE_NOT_READY;
+    }
+    
     if (Vcb->balance.thread) {
         WARN("balance already running\n");
         return STATUS_DEVICE_NOT_READY;
@@ -3064,7 +3164,6 @@
     
     Vcb->balance.paused = FALSE;
     Vcb->balance.stopping = TRUE;
-    Vcb->balance.cancelling = TRUE;
     Vcb->balance.status = STATUS_SUCCESS;
     KeSetEvent(&Vcb->balance.event, 0, FALSE);
     
Index: drivers/filesystems/btrfs/btrfs.c
===================================================================
--- drivers/filesystems/btrfs/btrfs.c	(révision 74430)
+++ drivers/filesystems/btrfs/btrfs.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -29,13 +29,23 @@
 #endif
 #include <ntddscsi.h>
 #include "btrfs.h"
-#ifndef __REACTOS__
-#include <winioctl.h>
-#else
-#include <rtlfuncs.h>
-#endif
 #include <ata.h>
 
+#ifndef _MSC_VER
+#include <initguid.h>
+#include <ntddstor.h>
+#undef INITGUID
+#endif
+
+#include <ntdddisk.h>
+#include <ntddvol.h>
+
+#ifdef _MSC_VER
+#include <initguid.h>
+#include <ntddstor.h>
+#undef INITGUID
+#endif
+
 #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_RAID56 | \
                             BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES)
@@ -51,9 +61,6 @@
 #endif
 UINT64 num_reads = 0;
 LIST_ENTRY uid_map_list;
-LIST_ENTRY volumes;
-ERESOURCE volumes_lock;
-LIST_ENTRY pnp_disks;
 LIST_ENTRY VcbList;
 ERESOURCE global_loading_lock;
 UINT32 debug_log_level = 0;
@@ -63,9 +70,10 @@
 UINT32 mount_zlib_level = 3;
 UINT32 mount_flush_interval = 30;
 UINT32 mount_max_inline = 2048;
-UINT32 mount_raid5_recalculation = 1;
-UINT32 mount_raid6_recalculation = 1;
 UINT32 mount_skip_balance = 0;
+UINT32 mount_no_barrier = 0;
+UINT32 mount_no_trim = 0;
+UINT32 mount_clear_cache = 0;
 BOOL log_started = FALSE;
 UNICODE_STRING log_device, log_file, registry_path;
 tPsUpdateDiskCounters PsUpdateDiskCounters;
@@ -72,8 +80,11 @@
 tCcCopyReadEx CcCopyReadEx;
 tCcCopyWriteEx CcCopyWriteEx;
 tCcSetAdditionalCacheAttributesEx CcSetAdditionalCacheAttributesEx;
+tFsRtlUpdateDiskCounters FsRtlUpdateDiskCounters;
 BOOL diskacc = FALSE;
-void* notification_entry = NULL;
+void *notification_entry = NULL, *notification_entry2 = NULL, *notification_entry3 = NULL;
+ERESOURCE volume_list_lock;
+LIST_ENTRY volume_list;
 
 #ifdef _DEBUG
 PFILE_OBJECT comfo = NULL;
@@ -92,6 +103,8 @@
 static NTSTATUS STDCALL dbg_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
     read_context* context = conptr;
     
+    UNUSED(DeviceObject);
+    
 //     DbgPrint("dbg_completion\n");
     
     context->iosb = Irp->IoStatus;
@@ -112,7 +125,7 @@
     PIRP Irp;
     va_list ap;
     char *buf2 = NULL, *buf;
-    read_context* context = NULL;
+    read_context context;
     UINT32 length;
     
     buf2 = ExAllocatePoolWithTag(NonPagedPool, 1024, ALLOC_TAG);
@@ -146,15 +159,9 @@
         offset.u.LowPart = 0;
         offset.u.HighPart = 0;
         
-        context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
-        if (!context) {
-            DbgPrint("Couldn't allocate context in debug_message\n");
-            return;
-        }
+        RtlZeroMemory(&context, sizeof(read_context));
         
-        RtlZeroMemory(context, sizeof(read_context));
-        
-        KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
+        KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
 
     //     status = ZwWriteFile(comh, NULL, NULL, NULL, &io, buf2, strlen(buf2), &offset, NULL);
         
@@ -178,8 +185,8 @@
                 DbgPrint("IoAllocateMdl failed\n");
                 goto exit;
             }
-            
-            MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
+
+            MmBuildMdlForNonPagedPool(Irp->MdlAddress);
         } else {
             Irp->UserBuffer = buf2;
         }
@@ -187,23 +194,21 @@
         IrpSp->Parameters.Write.Length = length;
         IrpSp->Parameters.Write.ByteOffset = offset;
         
-        Irp->UserIosb = &context->iosb;
+        Irp->UserIosb = &context.iosb;
 
-        Irp->UserEvent = &context->Event;
+        Irp->UserEvent = &context.Event;
 
-        IoSetCompletionRoutine(Irp, dbg_completion, context, TRUE, TRUE, TRUE);
+        IoSetCompletionRoutine(Irp, dbg_completion, &context, TRUE, TRUE, TRUE);
 
         Status = IoCallDriver(comdo, Irp);
 
         if (Status == STATUS_PENDING) {
-            KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
-            Status = context->iosb.Status;
+            KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+            Status = context.iosb.Status;
         }
         
-        if (comdo->Flags & DO_DIRECT_IO) {
-            MmUnlockPages(Irp->MdlAddress);
+        if (comdo->Flags & DO_DIRECT_IO)
             IoFreeMdl(Irp->MdlAddress);
-        }
         
         if (!NT_SUCCESS(Status)) {
             DbgPrint("failed to write to COM1 - error %08x\n", Status);
@@ -227,9 +232,6 @@
 exit2:
     va_end(ap);
     
-    if (context)
-        ExFreePool(context);
-    
     if (buf2)
         ExFreePool(buf2);
 }
@@ -271,6 +273,20 @@
     
     IoUnregisterFileSystem(DriverObject->DeviceObject);
     
+    if (notification_entry2)
+#ifdef __REACTOS__
+        IoUnregisterPlugPlayNotification(notification_entry2);
+#else
+        IoUnregisterPlugPlayNotificationEx(notification_entry2);
+#endif
+    
+    if (notification_entry3)
+#ifdef __REACTOS__
+        IoUnregisterPlugPlayNotification(notification_entry3);
+#else
+        IoUnregisterPlugPlayNotificationEx(notification_entry3);
+#endif
+    
     if (notification_entry)
 #ifdef __REACTOS__
         IoUnregisterPlugPlayNotification(notification_entry);
@@ -294,7 +310,6 @@
     }
     
     // FIXME - free volumes and their devpaths
-    // FIXME - free pnp_disks and their devpaths
     
 #ifdef _DEBUG
     if (comfo)
@@ -305,9 +320,8 @@
 #endif
     
     ExDeleteResourceLite(&global_loading_lock);
+    ExDeleteResourceLite(&volume_list_lock);
     
-    ExDeleteResourceLite(&volumes_lock);
-    
     if (log_device.Buffer)
         ExFreePool(log_device.Buffer);
     
@@ -340,7 +354,7 @@
         return TRUE;
     }
     
-    while (find_prev_item(Vcb, &tp, &prev_tp, FALSE, Irp)) {
+    while (find_prev_item(Vcb, &tp, &prev_tp, 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);
@@ -359,7 +373,7 @@
     return TRUE;
 }
 
-BOOL extract_xattr(void* item, USHORT size, char* name, UINT8** data, UINT16* datalen) {
+static BOOL extract_xattr(void* item, USHORT size, char* name, UINT8** data, UINT16* datalen) {
     DIR_ITEM* xa = (DIR_ITEM*)item;
     USHORT xasize;
     
@@ -444,10 +458,16 @@
 
     top_level = is_top_level(Irp);
     
-    if (DeviceObject == devobj || (Vcb && Vcb->type == VCB_TYPE_PARTITION0)) {
+    if (DeviceObject == devobj) {
         TRACE("Closing file system\n");
         Status = STATUS_SUCCESS;
-        goto exit;
+        goto end;
+    } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_close(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
     
     IrpSp = IoGetCurrentIrpStackLocation(Irp);
@@ -457,7 +477,7 @@
     
     Status = close_file(DeviceObject->DeviceExtension, IrpSp->FileObject);
 
-exit:
+end:
     Irp->IoStatus.Status = Status;
     Irp->IoStatus.Information = 0;
     
@@ -487,9 +507,12 @@
 
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_flush_buffers(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
     
     Status = STATUS_SUCCESS;
@@ -507,9 +530,9 @@
         Status = Irp->IoStatus.Status;
     }
     
+end:
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
     
-exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -542,6 +565,96 @@
     *freespace = sectors_used > *totalsize ? 0 : (*totalsize - sectors_used);
 }
 
+#ifndef __REACTOS__
+// This function exists because we have to lie about our FS type in certain situations. 
+// MPR!MprGetConnection queries the FS type, and compares it to a whitelist. If it doesn't match,
+// it will return ERROR_NO_NET_OR_BAD_PATH, which prevents UAC from working.
+// The command mklink refuses to create hard links on anything other than NTFS, so we have to
+// blacklist cmd.exe too.
+
+static BOOL lie_about_fs_type() {
+    NTSTATUS Status;
+    PROCESS_BASIC_INFORMATION pbi;
+    PPEB peb;
+    LIST_ENTRY* le;
+    ULONG retlen;
+    
+    static WCHAR mpr[] = L"MPR.DLL";
+    static WCHAR cmd[] = L"CMD.EXE";
+    UNICODE_STRING mprus, cmdus;
+    
+    mprus.Buffer = mpr;
+    mprus.Length = mprus.MaximumLength = wcslen(mpr) * sizeof(WCHAR);
+    cmdus.Buffer = cmd;
+    cmdus.Length = cmdus.MaximumLength = wcslen(cmd) * sizeof(WCHAR);
+    
+    if (!PsGetCurrentProcess())
+        return FALSE;
+    
+    Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &retlen);
+    
+    if (!NT_SUCCESS(Status)) {
+        ERR("ZwQueryInformationProcess returned %08x\n", Status);
+        return FALSE;
+    }
+
+    if (!pbi.PebBaseAddress)
+        return FALSE;
+    
+    peb = pbi.PebBaseAddress;
+    
+    if (!peb->Ldr)
+        return FALSE;
+    
+    le = peb->Ldr->InMemoryOrderModuleList.Flink;
+    while (le != &peb->Ldr->InMemoryOrderModuleList) {
+        LDR_DATA_TABLE_ENTRY* entry = CONTAINING_RECORD(le, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
+        BOOL blacklist = FALSE;
+        
+        if (entry->FullDllName.Length >= mprus.Length) {
+            UNICODE_STRING name;
+            
+            name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - mprus.Length) / sizeof(WCHAR)];
+            name.Length = name.MaximumLength = mprus.Length;
+            
+            blacklist = FsRtlAreNamesEqual(&name, &mprus, TRUE, NULL);
+        }
+        
+        if (!blacklist && entry->FullDllName.Length >= cmdus.Length) {
+            UNICODE_STRING name;
+            
+            name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - cmdus.Length) / sizeof(WCHAR)];
+            name.Length = name.MaximumLength = cmdus.Length;
+            
+            blacklist = FsRtlAreNamesEqual(&name, &cmdus, TRUE, NULL);
+        }
+        
+        if (blacklist) {
+            void** frames;
+            USHORT i, num_frames;
+            
+            frames = ExAllocatePoolWithTag(PagedPool, 256 * sizeof(void*), ALLOC_TAG);
+
+            num_frames = RtlWalkFrameChain(frames, 256, 1);
+            
+            for (i = 0; i < num_frames; i++) {
+                // entry->Reserved3[1] appears to be the image size
+                if (frames[i] >= entry->DllBase && (ULONG_PTR)frames[i] <= (ULONG_PTR)entry->DllBase + (ULONG_PTR)entry->Reserved3[1]) {
+                    ExFreePool(frames);
+                    return TRUE;
+                }
+            }
+            
+            ExFreePool(frames);
+        }
+        
+        le = le->Flink;
+    }
+
+    return FALSE;
+}
+#endif
+
 static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     PIO_STACK_LOCATION IrpSp;
     NTSTATUS Status;
@@ -549,28 +662,18 @@
     device_extension* Vcb = DeviceObject->DeviceExtension;
     BOOL top_level;
     
-#ifndef __REACTOS__
-    // An unfortunate necessity - we have to lie about our FS type. MPR!MprGetConnection polls for this,
-    // and compares it to a whitelist. If it doesn't match, it will return ERROR_NO_NET_OR_BAD_PATH,
-    // which prevents UAC from working.
-    // FIXME - only lie if we detect that we're being called by mpr.dll
-    
-    WCHAR* fs_name = L"NTFS";
-    ULONG fs_name_len = 4 * sizeof(WCHAR);
-#else
-    WCHAR* fs_name = L"Btrfs";
-    ULONG fs_name_len = 5 * sizeof(WCHAR);
-#endif
-
     TRACE("query volume information\n");
     
     FsRtlEnterFileSystem();
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
-    }    
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_query_volume_information(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
+    }
     
     IrpSp = IoGetCurrentIrpStackLocation(Irp);
     
@@ -581,6 +684,12 @@
         {
             FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
             BOOL overflow = FALSE;
+#ifndef __REACTOS__
+            WCHAR* fs_name = (Irp->RequestorMode == UserMode && lie_about_fs_type()) ? L"NTFS" : L"Btrfs";
+#else
+            WCHAR* fs_name = L"Btrfs";
+#endif
+            ULONG fs_name_len = wcslen(fs_name) * sizeof(WCHAR);
             ULONG orig_fs_name_len = fs_name_len;
             
             TRACE("FileFsAttributeInformation\n");
@@ -611,10 +720,6 @@
             break;
         }
 
-        case FileFsControlInformation:
-            FIXME("STUB: FileFsControlInformation\n");
-            break;
-
         case FileFsDeviceInformation:
         {
             FILE_FS_DEVICE_INFORMATION* ffdi = Irp->AssociatedIrp.SystemBuffer;
@@ -624,7 +729,7 @@
             ffdi->DeviceType = FILE_DEVICE_DISK;
             
             ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
-            ffdi->Characteristics = first_device(Vcb)->devobj->Characteristics;
+            ffdi->Characteristics = Vcb->Vpb->RealDevice->Characteristics;
             ExReleaseResourceLite(&Vcb->tree_lock);
             
             if (Vcb->readonly)
@@ -638,10 +743,6 @@
             break;
         }
 
-        case FileFsDriverPathInformation:
-            FIXME("STUB: FileFsDriverPathInformation\n");
-            break;
-
         case FileFsFullSizeInformation:
         {
             FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
@@ -754,7 +855,7 @@
             
             data->Flags = SSINFO_FLAGS_ALIGNED_DEVICE | SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE;
             
-            if (Vcb->trim)
+            if (Vcb->trim && !Vcb->options.no_trim)
                 data->Flags |= SSINFO_FLAGS_TRIM_ENABLED;
             
             BytesCopied = sizeof(FILE_FS_SECTOR_SIZE_INFORMATION);
@@ -776,16 +877,16 @@
 //         Status = STATUS_BUFFER_OVERFLOW;
 //     }
 
-    Irp->IoStatus.Status = Status;
-    
     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
         Irp->IoStatus.Information = 0;
     else
         Irp->IoStatus.Information = BytesCopied;
     
+end:
+    Irp->IoStatus.Status = Status;
+    
     IoCompleteRequest( Irp, IO_DISK_INCREMENT );
     
-exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -799,6 +900,8 @@
 static NTSTATUS STDCALL read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
     read_context* context = conptr;
     
+    UNUSED(DeviceObject);
+    
 //     DbgPrint("read_completion\n");
     
     context->iosb = Irp->IoStatus;
@@ -808,74 +911,8 @@
     return STATUS_MORE_PROCESSING_REQUIRED;
 }
 
-// static void test_tree_deletion(device_extension* Vcb) {
-//     KEY searchkey/*, endkey*/;
-//     traverse_ptr tp, next_tp;
-//     root* r;
-//     
-//     searchkey.obj_id = 0x100;
-//     searchkey.obj_type = 0x54;
-//     searchkey.offset = 0xca4ab2f5;
-//     
-// //     endkey.obj_id = 0x100;
-// //     endkey.obj_type = 0x60;
-// //     endkey.offset = 0x15a;
-//     
-//     r = Vcb->roots;
-//     while (r && r->id != 0x102)
-//         r = r->next;
-//     
-//     if (!r) {
-//         ERR("error - could not find root\n");
-//         return;
-//     }
-//     
-//     if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
-//         ERR("error - could not find key\n");
-//         return;
-//     }
-//     
-//     while (TRUE/*keycmp(tp.item->key, endkey) < 1*/) {
-//         tp.item->ignore = TRUE;
-//         add_to_tree_cache(tc, tp.tree);
-//         
-//         if (find_next_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
-//             free_traverse_ptr(&tp);
-//             tp = next_tp;
-//         } else
-//             break;
-//     }
-//     
-//     free_traverse_ptr(&tp);
-// }
-
-// static void test_tree_splitting(device_extension* Vcb) {
-//     int i;
-//     
-//     for (i = 0; i < 1000; i++) {
-//         char* data = ExAllocatePoolWithTag(PagedPool, 4, ALLOC_TAG);
-//         
-//         insert_tree_item(Vcb, Vcb->extent_root, 0, 0xfd, i, data, 4, NULL);
-//     }
-// }
-
-// static void test_dropping_tree(device_extension* Vcb) {
-//     LIST_ENTRY* le = Vcb->roots.Flink;
-//     
-//     while (le != &Vcb->roots) {
-//         root* r = CONTAINING_RECORD(le, root, list_entry);
-//         
-//         if (r->id == 0x101) {
-//             RemoveEntryList(&r->list_entry);
-//             InsertTailList(&Vcb->drop_roots, &r->list_entry);
-//             return;
-//         }
-//         
-//         le = le->Flink;
-//     }
-// }
-
-NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, PIRP Irp, LIST_ENTRY* rollback) {
+NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, PIRP Irp) {
+    NTSTATUS Status;
     root* r;
     tree* t;
     ROOT_ITEM* ri;
@@ -902,6 +939,10 @@
             ExFreePool(r);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
+        
+        t->is_unique = TRUE;
+        t->uniqueness_determined = TRUE;
+        t->buf = NULL;
     }
     
     ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
@@ -931,8 +972,9 @@
     // 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, Irp, rollback)) {
-        ERR("insert_tree_item failed\n");
+    Status = insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
         ExFreePool(ri);
         
         if (!no_tree)
@@ -940,7 +982,7 @@
         
         ExFreePool(r->nonpaged);
         ExFreePool(r);
-        return STATUS_INTERNAL_ERROR;
+        return Status;
     }
         
     ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
@@ -982,284 +1024,6 @@
     return STATUS_SUCCESS;
 }
 
-// static void test_creating_root(device_extension* Vcb) {
-//     NTSTATUS Status;
-//     LIST_ENTRY rollback;
-//     UINT64 id;
-//     root* r;
-//     
-//     InitializeListHead(&rollback);
-//     
-//     if (Vcb->root_root->lastinode == 0)
-//         get_last_inode(Vcb, Vcb->root_root);
-//     
-//     id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
-//     Status = create_root(Vcb, id, &r, &rollback);
-//     
-//     if (!NT_SUCCESS(Status)) {
-//         ERR("create_root returned %08x\n", Status);
-//         do_rollback(Vcb, &rollback);
-//     } else {
-//         Vcb->root_root->lastinode = id;
-//         clear_rollback(&rollback);
-//     }
-// }
-
-// static void test_alloc_chunk(device_extension* Vcb) {
-//     LIST_ENTRY rollback;
-//     chunk* c;
-//     
-//     InitializeListHead(&rollback);
-//     
-//     c = alloc_chunk(Vcb, BLOCK_FLAG_DATA | BLOCK_FLAG_RAID10, &rollback);
-//     if (!c) {
-//         ERR("alloc_chunk failed\n");
-//         do_rollback(Vcb, &rollback);
-//     } else {
-//         clear_rollback(&rollback);
-//     }
-// }
-
-// static void test_space_list(device_extension* Vcb) {
-//     chunk* c;
-//     int i, j;
-//     LIST_ENTRY* le;
-//     
-//     typedef struct {
-//         UINT64 address;
-//         UINT64 length;
-//         BOOL add;
-//     } space_test;
-//     
-//     static const space_test entries[] = {
-//         { 0x1000, 0x1000 },
-//         { 0x3000, 0x2000 },
-//         { 0x6000, 0x1000 },
-//         { 0, 0 }
-//     };
-//     
-//     static const space_test tests[] = {
-//         { 0x0, 0x800, TRUE }, 
-//         { 0x1800, 0x400, TRUE }, 
-//         { 0x800, 0x2000, TRUE }, 
-//         { 0x1000, 0x2000, TRUE }, 
-//         { 0x2000, 0x3800, TRUE }, 
-//         { 0x800, 0x1000, TRUE }, 
-//         { 0x1800, 0x1000, TRUE }, 
-//         { 0x5000, 0x800, TRUE }, 
-//         { 0x5000, 0x1000, TRUE }, 
-//         { 0x7000, 0x1000, TRUE }, 
-//         { 0x8000, 0x1000, TRUE },
-//         { 0x800, 0x800, TRUE }, 
-//         { 0x0, 0x3800, TRUE }, 
-//         { 0x1000, 0x2800, TRUE },
-//         { 0x1000, 0x1000, FALSE },
-//         { 0x800, 0x2000, FALSE },
-//         { 0x0, 0x3800, FALSE },
-//         { 0x2800, 0x1000, FALSE },
-//         { 0x1800, 0x2000, FALSE },
-//         { 0x3800, 0x1000, FALSE },
-//         { 0, 0, FALSE }
-//     };
-//     
-//     c = CONTAINING_RECORD(Vcb->chunks.Flink, chunk, list_entry);
-//     
-//     i = 0;
-//     while (tests[i].length > 0) {
-//         InitializeListHead(&c->space);
-//         InitializeListHead(&c->space_size);
-//         ERR("test %u\n", i);
-//         
-//         j = 0;
-//         while (entries[j].length > 0) {
-//             space* s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
-//             s->address = entries[j].address;
-//             s->size = entries[j].length;
-//             InsertTailList(&c->space, &s->list_entry);
-//             
-//             order_space_entry(s, &c->space_size);
-//             
-//             j++;
-//         }
-//         
-//         if (tests[i].add)
-//             space_list_add(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
-//         else
-//             space_list_subtract(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
-//         
-//         le = c->space.Flink;
-//         while (le != &c->space) {
-//             space* s = CONTAINING_RECORD(le, space, list_entry);
-//             
-//             ERR("(%llx,%llx)\n", s->address, s->size);
-//             
-//             le = le->Flink;
-//         }
-//         
-//         ERR("--\n");
-//         
-//         le = c->space_size.Flink;
-//         while (le != &c->space_size) {
-//             space* s = CONTAINING_RECORD(le, space, list_entry_size);
-//             
-//             ERR("(%llx,%llx)\n", s->address, s->size);
-//             
-//             le = le->Flink;
-//         }
-//         
-//         i++;
-//     }
-//     
-//     int3;
-// }
-
-#if 0
-void STDCALL tree_test(void* context) {
-    device_extension* Vcb = context;
-    NTSTATUS Status;
-    UINT64 id;
-    LARGE_INTEGER due_time, time;
-    KTIMER timer;
-    root* r;
-    LIST_ENTRY rollback;
-    ULONG seed;
-    
-    InitializeListHead(&rollback);
-    
-    KeInitializeTimer(&timer);
-    
-    id = InterlockedIncrement64(&Vcb->root_root->lastinode);
-    Status = create_root(Vcb, id, &r, FALSE, 0, NULL, &rollback);
-    if (!NT_SUCCESS(Status)) {
-        ERR("create_root returned %08x\n");
-        return;
-    }
-    
-    clear_rollback(Vcb, &rollback);
-    
-    due_time.QuadPart = (UINT64)1 * -10000000;
-    
-    KeQueryPerformanceCounter(&time);
-    seed = time.LowPart;
-    
-    while (TRUE) {
-        UINT32 i;
-        
-        FsRtlEnterFileSystem();
-        
-        ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
-        
-        for (i = 0; i < 100; i++) {
-            void* data;
-            ULONG datalen;
-            UINT64 objid, offset;
-            
-            objid = RtlRandomEx(&seed);
-            objid <<= 32;
-            objid |= RtlRandomEx(&seed);
-            
-            offset = RtlRandomEx(&seed);
-            offset <<= 32;
-            offset |= RtlRandomEx(&seed);
-            
-            datalen = 30;
-            data = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG);
-            
-            if (!insert_tree_item(Vcb, r, objid, 0xfd, offset, data, datalen, NULL, NULL, &rollback)) {
-                ERR("insert_tree_item failed\n");
-            }
-        }
-        
-        for (i = 0; i < 25; i++) {
-            KEY searchkey;
-            traverse_ptr tp;
-            
-            searchkey.obj_id = RtlRandomEx(&seed);
-            searchkey.obj_id <<= 32;
-            searchkey.obj_id |= RtlRandomEx(&seed);
-            
-            searchkey.obj_type = 0xfd;
-            
-            searchkey.offset = RtlRandomEx(&seed);
-            searchkey.offset <<= 32;
-            searchkey.offset |= RtlRandomEx(&seed);
-            
-            Status = find_item(Vcb, r, &tp, &searchkey, FALSE, NULL);
-            if (!NT_SUCCESS(Status)) {
-                ERR("error - find_item returned %08x\n", Status);
-            } else {
-                delete_tree_item(Vcb, &tp, &rollback);
-            }
-        }
-        
-        clear_rollback(Vcb, &rollback);
-        
-        ExReleaseResourceLite(&Vcb->tree_lock);
-        
-        FsRtlExitFileSystem();
-        
-        KeSetTimer(&timer, due_time, NULL);
-        
-        KeWaitForSingleObject(&timer, Executive, KernelMode, FALSE, NULL);
-    }
-}
-#endif
-
-// static void test_calc_thread(device_extension* Vcb) {
-//     UINT8* data;
-//     ULONG sectors, max_sectors, i, j;
-//     calc_job* cj;
-//     LARGE_INTEGER* sertimes;
-//     LARGE_INTEGER* partimes;
-//     LARGE_INTEGER time1, time2;
-//     
-//     max_sectors = 256;
-//     
-//     sertimes = ExAllocatePoolWithTag(PagedPool, sizeof(LARGE_INTEGER) * max_sectors, ALLOC_TAG);
-//     partimes = ExAllocatePoolWithTag(PagedPool, sizeof(LARGE_INTEGER) * max_sectors, ALLOC_TAG);
-//     RtlZeroMemory(sertimes, sizeof(LARGE_INTEGER) * max_sectors);
-//     RtlZeroMemory(partimes, sizeof(LARGE_INTEGER) * max_sectors);
-//     
-//     for (sectors = 1; sectors <= max_sectors; sectors++) {
-//         data = ExAllocatePoolWithTag(PagedPool, sectors * Vcb->superblock.sector_size, ALLOC_TAG);
-//         RtlZeroMemory(data, sectors * Vcb->superblock.sector_size);
-//         
-//         for (j = 0; j < 100; j++) {
-//             time1 = KeQueryPerformanceCounter(NULL);
-//             
-//             for (i = 0; i < sectors; i++) {
-//                 UINT32 tmp;
-//                 
-//                 tmp = ~calc_crc32c(0xffffffff, data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
-//             }
-//             
-//             time2 = KeQueryPerformanceCounter(NULL);
-//             
-//             sertimes[sectors - 1].QuadPart += time2.QuadPart - time1.QuadPart;
-//             
-//             time1 = KeQueryPerformanceCounter(NULL);
-//             
-//             add_calc_job(Vcb, data, sectors, &cj);
-//             KeWaitForSingleObject(&cj->event, Executive, KernelMode, FALSE, NULL);
-//             
-//             time2 = KeQueryPerformanceCounter(NULL);
-//             
-//             partimes[sectors - 1].QuadPart += time2.QuadPart - time1.QuadPart;
-//             
-//             free_calc_job(cj);
-//         }
-//         
-//         ExFreePool(data);
-//     }
-//     
-//     for (sectors = 1; sectors <= max_sectors; sectors++) {
-//         ERR("%u sectors: serial %llu, parallel %llu\n", sectors, sertimes[sectors - 1].QuadPart, partimes[sectors - 1].QuadPart);
-//     }
-//     
-//     ExFreePool(partimes);
-//     ExFreePool(sertimes);
-// }
-
 static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) {
     ULONG utf8len;
     NTSTATUS Status;
@@ -1338,9 +1102,12 @@
 
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_set_volume_information(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
     
     Status = STATUS_NOT_IMPLEMENTED;
@@ -1381,7 +1148,6 @@
 
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
     
-exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -1428,26 +1194,33 @@
 WCHAR* file_desc_fileref(file_ref* fileref) {
     NTSTATUS Status;
     UNICODE_STRING fn;
+    ULONG reqlen;
     
     if (fileref->debug_desc)
         return fileref->debug_desc;
     
-    Status = fileref_get_filename(fileref, &fn, NULL);
-    if (!NT_SUCCESS(Status)) {
+    fn.Length = fn.MaximumLength = 0;
+    Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
+    if (Status != STATUS_BUFFER_OVERFLOW)
         return L"ERROR";
-    }
     
-    fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, fn.Length + sizeof(WCHAR), ALLOC_TAG);
-    if (!fileref->debug_desc) {
-        ExFreePool(fn.Buffer);
+    fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, reqlen + sizeof(WCHAR), ALLOC_TAG);
+    if (!fileref->debug_desc)
         return L"(memory error)";
+    
+    fn.Buffer = fileref->debug_desc;
+    fn.Length = 0;
+    fn.MaximumLength = reqlen + sizeof(WCHAR);
+    
+    Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
+    if (!NT_SUCCESS(Status)) {
+        ExFreePool(fileref->debug_desc);
+        fileref->debug_desc = NULL;
+        return L"ERROR";
     }
     
-    RtlCopyMemory(fileref->debug_desc, fn.Buffer, fn.Length);
     fileref->debug_desc[fn.Length / sizeof(WCHAR)] = 0;
-    
-    ExFreePool(fn.Buffer);
-    
+
     return fileref->debug_desc;
 }
 
@@ -1465,12 +1238,30 @@
 void send_notification_fileref(file_ref* fileref, ULONG filter_match, ULONG action) {
     UNICODE_STRING fn;
     NTSTATUS Status;
+    ULONG reqlen;
     USHORT name_offset;
     fcb* fcb = fileref->fcb;
     
-    Status = fileref_get_filename(fileref, &fn, &name_offset);
+    fn.Length = fn.MaximumLength = 0;
+    Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
+    if (Status != STATUS_BUFFER_OVERFLOW) {
+        ERR("fileref_get_filename returned %08x\n", Status);
+        return;
+    }
+    
+    fn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
+    if (!fn.Buffer) {
+        ERR("out of memory\n");
+        return;
+    }
+    
+    fn.MaximumLength = reqlen;
+    fn.Length = 0;
+    
+    Status = fileref_get_filename(fileref, &fn, &name_offset, &reqlen);
     if (!NT_SUCCESS(Status)) {
         ERR("fileref_get_filename returned %08x\n", Status);
+        ExFreePool(fn.Buffer);
         return;
     }
     
@@ -1499,64 +1290,53 @@
         
         Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, NULL);
         
-        if (!NT_SUCCESS(Status)) {
+        if (!NT_SUCCESS(Status))
             ERR("open_fileref_by_inode returned %08x\n", Status);
-        } else if (!parfr->deleted) {
-            LIST_ENTRY* le2;
-            BOOL found = FALSE, deleted = FALSE;
-            UNICODE_STRING* fn;
+        else if (!parfr->deleted) {
+            UNICODE_STRING fn;
+            ULONG pathlen;
             
-            le2 = parfr->children.Flink;
-            while (le2 != &parfr->children) {
-                file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
-                
-                if (fr2->index == hl->index) {
-                    found = TRUE;
-                    deleted = fr2->deleted;
-                    
-                    if (!deleted)
-                        fn = &fr2->filepart;
-                    
-                    break;
-                }
-                
-                le2 = le2->Flink;
+            fn.Length = fn.MaximumLength = 0;
+            Status = fileref_get_filename(parfr, &fn, NULL, &pathlen);
+            if (Status != STATUS_BUFFER_OVERFLOW) {
+                ERR("fileref_get_filename returned %08x\n", Status);
+                free_fileref(fcb->Vcb, parfr);
+                break;
             }
             
-            if (!found)
-                fn = &hl->name;
+            if (parfr != fcb->Vcb->root_fileref)
+                pathlen += sizeof(WCHAR);
             
-            if (!deleted) {
-                UNICODE_STRING path;
-                
-                Status = fileref_get_filename(parfr, &path, NULL);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("fileref_get_filename returned %08x\n", Status);
-                } else {
-                    UNICODE_STRING fn2;
-                    ULONG name_offset;
-                    
-                    name_offset = path.Length;
-                    if (parfr != fileref->fcb->Vcb->root_fileref) name_offset += sizeof(WCHAR);
-                    
-                    fn2.Length = fn2.MaximumLength = fn->Length + name_offset;
-                    fn2.Buffer = ExAllocatePoolWithTag(PagedPool, fn2.MaximumLength, ALLOC_TAG);
-                    
-                    RtlCopyMemory(fn2.Buffer, path.Buffer, path.Length);
-                    if (parfr != fileref->fcb->Vcb->root_fileref) fn2.Buffer[path.Length / sizeof(WCHAR)] = '\\';
-                    RtlCopyMemory(&fn2.Buffer[name_offset / sizeof(WCHAR)], fn->Buffer, fn->Length);
-                    
-                    TRACE("%.*S\n", fn2.Length / sizeof(WCHAR), fn2.Buffer);
-                    
-                    FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn2, name_offset,
-                                                  NULL, NULL, filter_match, action, NULL, NULL);
-                    
-                    ExFreePool(fn2.Buffer);
-                    ExFreePool(path.Buffer);
-                }
+            fn.MaximumLength = pathlen + hl->name.Length;
+            fn.Buffer = ExAllocatePoolWithTag(PagedPool, fn.MaximumLength, ALLOC_TAG);
+            if (!fn.Buffer) {
+                ERR("out of memory\n");
+                free_fileref(fcb->Vcb, parfr);
+                break;
             }
             
-            free_fileref(parfr);
+            Status = fileref_get_filename(parfr, &fn, NULL, NULL);
+            if (!NT_SUCCESS(Status)) {
+                ERR("fileref_get_filename returned %08x\n", Status);
+                free_fileref(fcb->Vcb, parfr);
+                ExFreePool(fn.Buffer);
+                break;
+            }
+            
+            if (parfr != fcb->Vcb->root_fileref) {
+                fn.Buffer[(pathlen / sizeof(WCHAR)) - 1] = '\\';
+                fn.Length += sizeof(WCHAR);
+            }
+            
+            RtlCopyMemory(&fn.Buffer[pathlen / sizeof(WCHAR)], hl->name.Buffer, hl->name.Length);
+            fn.Length += hl->name.Length;
+            
+            FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, pathlen,
+                                          NULL, NULL, filter_match, action, NULL, NULL);
+            
+            ExFreePool(fn.Buffer);
+            
+            free_fileref(fcb->Vcb, parfr);
         }
         
         le = le->Flink;
@@ -1588,7 +1368,9 @@
         
         dirt->fcb = fcb;
         
-        ExInterlockedInsertTailList(&fcb->Vcb->dirty_fcbs, &dirt->list_entry, &fcb->Vcb->dirty_fcbs_lock);
+        ExAcquireResourceExclusiveLite(&fcb->Vcb->dirty_fcbs_lock, TRUE);
+        InsertTailList(&fcb->Vcb->dirty_fcbs, &dirt->list_entry);
+        ExReleaseResourceLite(&fcb->Vcb->dirty_fcbs_lock);
     }
     
     fcb->Vcb->need_write = TRUE;
@@ -1608,13 +1390,15 @@
         
         dirt->fileref = fileref;
         
-        ExInterlockedInsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &dirt->list_entry, &fileref->fcb->Vcb->dirty_filerefs_lock);
+        ExAcquireResourceExclusiveLite(&fileref->fcb->Vcb->dirty_filerefs_lock, TRUE);
+        InsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &dirt->list_entry);
+        ExReleaseResourceLite(&fileref->fcb->Vcb->dirty_filerefs_lock);
     }
     
     fileref->fcb->Vcb->need_write = TRUE;
 }
 
-void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) {
+void free_fcb(fcb* fcb) {
     LONG rc;
 
 // #ifdef DEBUG    
@@ -1627,11 +1411,10 @@
     rc = InterlockedDecrement(&fcb->refcount);
     
 #ifdef DEBUG_FCB_REFCOUNTS
-//     WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
 #ifdef DEBUG_LONG_MESSAGES
-    _debug_message(func, file, line, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
+    ERR("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
 #else
-    _debug_message(func, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
+    ERR("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
 #endif
 #endif
     
@@ -1651,8 +1434,9 @@
     ExDeleteResourceLite(&fcb->nonpaged->resource);
     ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
     ExDeleteResourceLite(&fcb->nonpaged->dir_children_lock);
-    ExFreePool(fcb->nonpaged);
     
+    ExFreeToNPagedLookasideList(&fcb->Vcb->fcb_np_lookaside, fcb->nonpaged);
+    
     if (fcb->sd)
         ExFreePool(fcb->sd);
     
@@ -1678,7 +1462,6 @@
         if (ext->csum)
             ExFreePool(ext->csum);
         
-        ExFreePool(ext->data);
         ExFreePool(ext);
     }
     
@@ -1713,7 +1496,11 @@
     
     FsRtlUninitializeFileLock(&fcb->lock);
     
-    ExFreePool(fcb);
+    if (fcb->pool_type == NonPagedPool)
+        ExFreePool(fcb);
+    else
+        ExFreeToPagedLookasideList(&fcb->Vcb->fcb_lookaside, fcb);
+    
 #ifdef DEBUG_FCB_REFCOUNTS
 #ifdef DEBUG_LONG_MESSAGES
     _debug_message(func, file, line, "freeing fcb %p\n", fcb);
@@ -1723,7 +1510,7 @@
 #endif
 }
 
-void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line) {
+void free_fileref(device_extension* Vcb, file_ref* fr) {
     LONG rc;
 
 // #ifdef DEBUG    
@@ -1736,12 +1523,8 @@
     rc = InterlockedDecrement(&fr->refcount);
     
 #ifdef DEBUG_FCB_REFCOUNTS
-#ifdef DEBUG_LONG_MESSAGES
-    _debug_message(func, file, line, "fileref %p: refcount now %i\n", fr, rc);
-#else
-    _debug_message(func, "fileref %p: refcount now %i\n", fr, rc);
+    ERR("fileref %p: refcount now %i\n", fr, rc);
 #endif
-#endif
     
 #ifdef _DEBUG
     if (rc < 0) {
@@ -1760,21 +1543,13 @@
     
     // FIXME - do delete if needed
     
-    if (fr->filepart.Buffer)
-        ExFreePool(fr->filepart.Buffer);
-    
-    if (fr->filepart_uc.Buffer)
-        ExFreePool(fr->filepart_uc.Buffer);
-    
-    if (fr->utf8.Buffer)
-        ExFreePool(fr->utf8.Buffer);
-    
     if (fr->debug_desc)
         ExFreePool(fr->debug_desc);
     
     ExDeleteResourceLite(&fr->nonpaged->children_lock);
+    ExDeleteResourceLite(&fr->nonpaged->fileref_lock);
     
-    ExFreePool(fr->nonpaged);
+    ExFreeToNPagedLookasideList(&Vcb->fileref_np_lookaside, fr->nonpaged);
     
     // FIXME - throw error if children not empty
     
@@ -1781,8 +1556,12 @@
     if (fr->fcb->fileref == fr)
         fr->fcb->fileref = NULL;
     
-    if (fr->dc)
+    if (fr->dc) {
+        if (fr->fcb->ads)
+            fr->dc->size = fr->fcb->adsdata.Length;
+        
         fr->dc->fileref = NULL;
+    }
 
     if (fr->list_entry.Flink)
         RemoveEntryList(&fr->list_entry);
@@ -1789,11 +1568,12 @@
     
     if (fr->parent) {
         ExReleaseResourceLite(&fr->parent->nonpaged->children_lock);
-        free_fileref(fr->parent);
+        free_fileref(Vcb, fr->parent);
     }
     
     free_fcb(fr->fcb);
-    ExFreePool(fr);
+    
+    ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, fr);
 }
 
 static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject) {
@@ -1848,7 +1628,7 @@
     ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
     
     if (fileref)
-        free_fileref(fileref);
+        free_fileref(Vcb, fileref);
     else
         free_fcb(fcb);
     
@@ -1860,7 +1640,6 @@
 void STDCALL uninit(device_extension* Vcb, BOOL flush) {
     space* s;
     UINT64 i;
-    LIST_ENTRY rollback;
     NTSTATUS Status;
     LIST_ENTRY* le;
     LARGE_INTEGER time;
@@ -1876,22 +1655,28 @@
         KeWaitForSingleObject(&Vcb->balance.finished, Executive, KernelMode, FALSE, NULL);
     }
     
+    if (Vcb->scrub.thread) {
+        Vcb->scrub.paused = FALSE;
+        Vcb->scrub.stopping = TRUE;
+        KeSetEvent(&Vcb->scrub.event, 0, FALSE);
+        KeWaitForSingleObject(&Vcb->scrub.finished, Executive, KernelMode, FALSE, NULL);
+    }
+    
     Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
     if (!NT_SUCCESS(Status) && Status != STATUS_TOO_LATE)
         WARN("registry_mark_volume_unmounted returned %08x\n", Status);
     
     if (flush) {
-        InitializeListHead(&rollback);
-        
         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
 
-        if (Vcb->need_write && !Vcb->readonly)
-            do_write(Vcb, NULL, &rollback);
+        if (Vcb->need_write && !Vcb->readonly) {
+            Status = do_write(Vcb, NULL);
+            if (!NT_SUCCESS(Status))
+                ERR("do_write returned %08x\n", Status);
+        }
         
         free_trees(Vcb);
         
-        clear_rollback(Vcb, &rollback);
-
         ExReleaseResourceLite(&Vcb->tree_lock);
     }
     
@@ -1966,6 +1751,7 @@
         if (c->cache)
             free_fcb(c->cache);
         
+        ExDeleteResourceLite(&c->range_locks_lock);
         ExDeleteResourceLite(&c->lock);
         ExDeleteResourceLite(&c->changed_extents_lock);
         
@@ -1989,16 +1775,31 @@
         ExFreePool(dev);
     }
     
+    ExAcquireResourceExclusiveLite(&Vcb->scrub.stats_lock, TRUE);
+    while (!IsListEmpty(&Vcb->scrub.errors)) {
+        scrub_error* err = CONTAINING_RECORD(RemoveHeadList(&Vcb->scrub.errors), scrub_error, list_entry);
+        
+        ExFreePool(err);
+    }
+    ExReleaseResourceLite(&Vcb->scrub.stats_lock);
+    
     ExDeleteResourceLite(&Vcb->fcb_lock);
     ExDeleteResourceLite(&Vcb->load_lock);
     ExDeleteResourceLite(&Vcb->tree_lock);
     ExDeleteResourceLite(&Vcb->chunk_lock);
+    ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
+    ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
+    ExDeleteResourceLite(&Vcb->scrub.stats_lock);
     
     ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
     ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
-    ExDeletePagedLookasideList(&Vcb->rollback_item_lookaside);
     ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
+    ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
+    ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
+    ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
     ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
+    ExDeleteNPagedLookasideList(&Vcb->fileref_np_lookaside);
+    ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
     
     ZwClose(Vcb->flush_thread_handle);
 }
@@ -2007,6 +1808,7 @@
     LARGE_INTEGER newlength, time;
     BTRFS_TIME now;
     NTSTATUS Status;
+    ULONG utf8len = 0;
 
     KeQuerySystemTime(&time);
     win_time_to_unix(time, &now);
@@ -2066,25 +1868,27 @@
                     CcSetFileSizes(FileObject, &ccfs);
                 }
             }
-                
-            le = fileref->fcb->hardlinks.Flink;
-            while (le != &fileref->fcb->hardlinks) {
-                hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
-                
-                if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->index) {
-                    RemoveEntryList(&hl->list_entry);
+            
+            if (fileref->dc) {
+                le = fileref->fcb->hardlinks.Flink;
+                while (le != &fileref->fcb->hardlinks) {
+                    hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
+
+                    if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->dc->index) {
+                        RemoveEntryList(&hl->list_entry);
+                        
+                        if (hl->name.Buffer)
+                            ExFreePool(hl->name.Buffer);
+                        
+                        if (hl->utf8.Buffer)
+                            ExFreePool(hl->utf8.Buffer);
+                        
+                        ExFreePool(hl);
+                        break;
+                    }
                     
-                    if (hl->name.Buffer)
-                        ExFreePool(hl->name.Buffer);
-                    
-                    if (hl->utf8.Buffer)
-                        ExFreePool(hl->utf8.Buffer);
-                    
-                    ExFreePool(hl);
-                    break;
+                    le = le->Flink;
                 }
-                
-                le = le->Flink;
             }
         } else { // subvolume
             if (fileref->fcb->subvol->root_item.num_references > 1) {
@@ -2107,12 +1911,25 @@
     // remove dir_child from parent
     
     if (fileref->dc) {
+        TRACE("delete file %.*S\n", fileref->dc->name.Length / sizeof(WCHAR), fileref->dc->name.Buffer);
+        
         ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
         RemoveEntryList(&fileref->dc->list_entry_index);
-        remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
+
+        if (!fileref->fcb->ads)
+            remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
+
         ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
         
-        ExFreePool(fileref->dc->utf8.Buffer);
+        if (!fileref->oldutf8.Buffer)
+            fileref->oldutf8 = fileref->dc->utf8;
+        else
+            ExFreePool(fileref->dc->utf8.Buffer);
+        
+        utf8len = fileref->dc->utf8.Length;
+        
+        fileref->oldindex = fileref->dc->index;
+        
         ExFreePool(fileref->dc->name.Buffer);
         ExFreePool(fileref->dc->name_uc.Buffer);
         ExFreePool(fileref->dc);
@@ -2122,22 +1939,27 @@
     
     // update INODE_ITEM of parent
     
-    TRACE("delete file %.*S\n", fileref->filepart.Length / sizeof(WCHAR), fileref->filepart.Buffer);
     ExAcquireResourceExclusiveLite(fileref->parent->fcb->Header.Resource, TRUE);
-    TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
-    fileref->parent->fcb->inode_item.st_size -= fileref->utf8.Length * 2;
-    TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
+    
     fileref->parent->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
     fileref->parent->fcb->inode_item.sequence++;
     fileref->parent->fcb->inode_item.st_ctime = now;
-    fileref->parent->fcb->inode_item.st_mtime = now;
+    
+    if (!fileref->fcb->ads) {
+        TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
+        fileref->parent->fcb->inode_item.st_size -= utf8len * 2;
+        TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
+        fileref->parent->fcb->inode_item.st_mtime = now;
+    }
+    
+    fileref->parent->fcb->inode_item_changed = TRUE;
     ExReleaseResourceLite(fileref->parent->fcb->Header.Resource);
-
-    fileref->parent->fcb->inode_item_changed = TRUE;
+    
+    if (!fileref->fcb->ads && fileref->parent->dc)
+        send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
+    
     mark_fcb_dirty(fileref->parent->fcb);
     
-    send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
-    
     fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
     fileref->fcb->subvol->root_item.ctime = now;
     
@@ -2165,15 +1987,16 @@
 
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit2;
-    }
-    
-    if (DeviceObject == devobj) {
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_cleanup(DeviceObject, Irp);
+        goto exit;
+    } else if (DeviceObject == devobj) {
         TRACE("closing file system\n");
         Status = STATUS_SUCCESS;
         goto exit;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto exit;
     }
     
     if (FileObject && FileObject->FsContext) {
@@ -2188,6 +2011,10 @@
         TRACE("cleanup called for FileObject %p\n", FileObject);
         TRACE("fileref %p (%S), refcount = %u, open_count = %u\n", fileref, file_desc(FileObject), fileref ? fileref->refcount : 0, fileref ? fileref->open_count : 0);
         
+        ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
+        
+        ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
+        
         IoRemoveShareAccess(FileObject, &fcb->share_access);
         
         FsRtlNotifyCleanup(Vcb->NotifySync, &Vcb->DirNotifyList, ccb);    
@@ -2220,8 +2047,6 @@
                 if (fileref && fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
                     send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED);
                     
-                    ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
-                    
                     ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
                     
                     Status = delete_fileref(fileref, FileObject, Irp, &rollback);
@@ -2229,6 +2054,7 @@
                         ERR("delete_fileref returned %08x\n", Status);
                         do_rollback(Vcb, &rollback);
                         ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
+                        ExReleaseResourceLite(fcb->Header.Resource);
                         ExReleaseResourceLite(&fcb->Vcb->tree_lock);
                         goto exit;
                     }
@@ -2235,8 +2061,7 @@
                     
                     ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
                     
-                    ExReleaseResourceLite(&fcb->Vcb->tree_lock);
-                    clear_rollback(Vcb, &rollback);
+                    clear_rollback(&rollback);
                 } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) {
                     IO_STATUS_BLOCK iosb;
                     CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
@@ -2261,6 +2086,9 @@
                 CcUninitializeCacheMap(FileObject, NULL, NULL);
         }
         
+        ExReleaseResourceLite(fcb->Header.Resource);
+        ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+        
         FileObject->Flags |= FO_CLEANUP_COMPLETE;
     }
     
@@ -2272,7 +2100,6 @@
     
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
     
-exit2:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -2307,13 +2134,11 @@
     return FALSE;
 }
 
-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 STDCALL get_file_attributes(device_extension* Vcb, 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, Irp)) {
         ULONG dosnum = 0;
         
@@ -2375,34 +2200,20 @@
 }
 
 NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer, BOOL override) {
-    IO_STATUS_BLOCK* IoStatus;
+    IO_STATUS_BLOCK IoStatus;
     LARGE_INTEGER Offset;
     PIRP Irp;
     PIO_STACK_LOCATION IrpSp;
     NTSTATUS Status;
-    read_context* context;
+    read_context context;
     
     num_reads++;
     
-    context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
-    if (!context) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    RtlZeroMemory(context, sizeof(read_context));
-    KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
-    
-    IoStatus = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_STATUS_BLOCK), ALLOC_TAG);
-    if (!IoStatus) {
-        ERR("out of memory\n");
-        ExFreePool(context);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
+    RtlZeroMemory(&context, sizeof(read_context));
+    KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
 
     Offset.QuadPart = StartingOffset;
 
-//     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
     Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
     
     if (!Irp) {
@@ -2419,43 +2230,42 @@
         IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
     
     if (DeviceObject->Flags & DO_BUFFERED_IO) {
-        FIXME("FIXME - buffered IO\n");
+        Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, Length, ALLOC_TAG);
+        if (!Irp->AssociatedIrp.SystemBuffer) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
+
+        Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
+
+        Irp->UserBuffer = Buffer;
     } else if (DeviceObject->Flags & DO_DIRECT_IO) {
-//         TRACE("direct IO\n");
-        
         Irp->MdlAddress = IoAllocateMdl(Buffer, Length, FALSE, FALSE, NULL);
         if (!Irp->MdlAddress) {
             ERR("IoAllocateMdl failed\n");
             Status = STATUS_INSUFFICIENT_RESOURCES;
-    //         IoFreeIrp(Irp);
             goto exit;
-//         } else {
-//             TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
         }
         
         MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
-    } else {
-//         TRACE("neither buffered nor direct IO\n");
+    } else
         Irp->UserBuffer = Buffer;
-    }
 
     IrpSp->Parameters.Read.Length = Length;
     IrpSp->Parameters.Read.ByteOffset = Offset;
     
-    Irp->UserIosb = IoStatus;
-//     Irp->Tail.Overlay.Thread = PsGetCurrentThread();
+    Irp->UserIosb = &IoStatus;
     
-    Irp->UserEvent = &context->Event;
-
-//     IoQueueThreadIrp(Irp);
+    Irp->UserEvent = &context.Event;
     
-    IoSetCompletionRoutine(Irp, read_completion, context, TRUE, TRUE, TRUE);
+    IoSetCompletionRoutine(Irp, read_completion, &context, TRUE, TRUE, TRUE);
 
     Status = IoCallDriver(DeviceObject, Irp);
 
     if (Status == STATUS_PENDING) {
-        KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
-        Status = context->iosb.Status;
+        KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+        Status = context.iosb.Status;
     }
     
     if (DeviceObject->Flags & DO_DIRECT_IO) {
@@ -2465,9 +2275,6 @@
     
 exit:
     IoFreeIrp(Irp);
-
-    ExFreePool(IoStatus);
-    ExFreePool(context);
     
     return Status;
 }
@@ -2486,6 +2293,12 @@
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
+    if (superblock_addrs[0] + to_read > length) {
+        WARN("device was too short to have any superblock\n");
+        ExFreePool(sb);
+        return STATUS_UNRECOGNIZED_VOLUME;
+    }
+    
     i = 0;
     valid_superblocks = 0;
     
@@ -2492,7 +2305,7 @@
     while (superblock_addrs[i] > 0) {
         UINT32 crc32;
         
-        if (i > 0 && superblock_addrs[i] + sizeof(superblock) > length)
+        if (i > 0 && superblock_addrs[i] + to_read > length)
             break;
         
         Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb, FALSE);
@@ -2505,6 +2318,7 @@
         if (sb->magic != BTRFS_MAGIC) {
             if (i == 0) {
                 TRACE("not a BTRFS volume\n");
+                ExFreePool(sb);
                 return STATUS_UNRECOGNIZED_VOLUME;
             }
         } else {
@@ -2576,7 +2390,7 @@
     return Status;
 }
 
-static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, traverse_ptr* tp) {
+static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, UINT64 generation, traverse_ptr* tp) {
     root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
     if (!r) {
         ERR("out of memory\n");
@@ -2587,6 +2401,7 @@
     r->path.Buffer = NULL;
     r->treeholder.address = addr;
     r->treeholder.tree = NULL;
+    r->treeholder.generation = generation;
     InitializeListHead(&r->fcbs);
 
     r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
@@ -2660,7 +2475,7 @@
     
     Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("error - find_tree returned %08x\n", Status);
+        ERR("error - find_item returned %08x\n", Status);
         return Status;
     }
     
@@ -2675,7 +2490,7 @@
             } else {
                 TRACE("root %llx - address %llx\n", tp.item->key.obj_id, ri->block_number);
                 
-                Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, &tp);
+                Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, ri->generation, &tp);
                 if (!NT_SUCCESS(Status)) {
                     ERR("add_root returned %08x\n", Status);
                     return Status;
@@ -2696,17 +2511,13 @@
         INODE_REF* ir;
         LARGE_INTEGER time;
         BTRFS_TIME now;
-        LIST_ENTRY rollback;
         
-        InitializeListHead(&rollback);
-        
         WARN("data reloc root doesn't exist, creating it\n");
         
-        Status = create_root(Vcb, BTRFS_ROOT_DATA_RELOC, &reloc_root, FALSE, 0, Irp, &rollback);
+        Status = create_root(Vcb, BTRFS_ROOT_DATA_RELOC, &reloc_root, FALSE, 0, Irp);
         
         if (!NT_SUCCESS(Status)) {
             ERR("create_root returned %08x\n", Status);
-            do_rollback(Vcb, &rollback);
             goto end;
         }
         
@@ -2722,7 +2533,7 @@
         ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
         if (!ii) {
             ERR("out of memory\n");
-            do_rollback(Vcb, &rollback);
+            Status = STATUS_INSUFFICIENT_RESOURCES;
             goto end;
         }
         
@@ -2738,13 +2549,17 @@
         ii->st_ctime = now;
         ii->st_mtime = now;
         
-        insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp, &rollback);
+        Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
+            goto end;
+        }
 
         irlen = offsetof(INODE_REF, name[0]) + 2;
         ir = ExAllocatePoolWithTag(PagedPool, irlen, ALLOC_TAG);
         if (!ir) {
             ERR("out of memory\n");
-            do_rollback(Vcb, &rollback);
+            Status = STATUS_INSUFFICIENT_RESOURCES;
             goto end;
         }
         
@@ -2753,16 +2568,20 @@
         ir->name[0] = '.';
         ir->name[1] = '.';
         
-        insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_REF, SUBVOL_ROOT_INODE, ir, irlen, NULL, Irp, &rollback);
+        Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_REF, SUBVOL_ROOT_INODE, ir, irlen, NULL, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
+            goto end;
+        }
         
-        clear_rollback(Vcb, &rollback);
-        
         Vcb->data_reloc_root = reloc_root;
         Vcb->need_write = TRUE;
     }
     
+    Status = STATUS_SUCCESS;
+    
 end:
-    return STATUS_SUCCESS;
+    return Status;
 }
 
 static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev, PIRP Irp) {
@@ -2773,7 +2592,15 @@
     NTSTATUS Status;
     
     InitializeListHead(&dev->space);
+
+    searchkey.obj_id = 0;
+    searchkey.obj_type = TYPE_DEV_STATS;
+    searchkey.offset = dev->devitem.dev_id;
     
+    Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
+    if (NT_SUCCESS(Status) && !keycmp(tp.item->key, searchkey))
+        RtlCopyMemory(dev->stats, tp.item->data, min(sizeof(UINT64) * 5, tp.item->size));
+    
     searchkey.obj_id = dev->devitem.dev_id;
     searchkey.obj_type = TYPE_DEV_EXTENT;
     searchkey.offset = 0;
@@ -2780,7 +2607,7 @@
     
     Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("error - find_tree returned %08x\n", Status);
+        ERR("error - find_item returned %08x\n", Status);
         return Status;
     }
     
@@ -2824,7 +2651,7 @@
     
     // The Linux driver doesn't like to allocate chunks within the first megabyte of a device.
     
-    space_list_subtract2(Vcb, &dev->space, NULL, 0, 0x100000, NULL);
+    space_list_subtract2(&dev->space, NULL, 0, 0x100000, NULL, NULL);
     
     return STATUS_SUCCESS;
 }
@@ -2849,6 +2676,7 @@
 }
 
 device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) {
+    volume_device_extension* vde;
     LIST_ENTRY* le;
     
     le = Vcb->devices.Flink;
@@ -2867,56 +2695,44 @@
         le = le->Flink;
     }
     
-    ExAcquireResourceSharedLite(&volumes_lock, TRUE);
+    vde = Vcb->vde;
     
-    if (Vcb->devices_loaded < Vcb->superblock.num_devices && !IsListEmpty(&volumes)) {
-        LIST_ENTRY* le = volumes.Flink;
+    ExAcquireResourceSharedLite(&vde->child_lock, TRUE);
+    
+    if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
+        LIST_ENTRY* le = vde->children.Flink;
         
-        while (le != &volumes) {
-            volume* v = CONTAINING_RECORD(le, volume, list_entry);
+        while (le != &vde->children) {
+            volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
             
-            if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
-                RtlCompareMemory(uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
-            ) {
-                NTSTATUS Status;
-                PFILE_OBJECT FileObject;
-                PDEVICE_OBJECT DeviceObject;
+            if (RtlCompareMemory(uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
                 device* dev;
                 
-                Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
-                if (!NT_SUCCESS(Status)) {
-                    ExReleaseResourceLite(&volumes_lock);
-                    ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
-                    return NULL;
-                }
-                
-                DeviceObject = FileObject->DeviceObject;
-                
-                ObReferenceObject(DeviceObject);
-                ObDereferenceObject(FileObject);
-                
                 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
                 if (!dev) {
-                    ExReleaseResourceLite(&volumes_lock);
+                    ExReleaseResourceLite(&vde->child_lock);
                     ERR("out of memory\n");
-                    ObDereferenceObject(DeviceObject);
                     return NULL;
                 }
                 
                 RtlZeroMemory(dev, sizeof(device));
-                dev->devobj = DeviceObject;
+                dev->devobj = vc->devobj;
                 dev->devitem.device_uuid = *uuid;
-                dev->devitem.dev_id = v->devnum;
-                dev->seeding = v->seeding;
+                dev->devitem.dev_id = vc->devid;
+                dev->devitem.num_bytes = vc->size;
+                dev->seeding = vc->seeding;
                 dev->readonly = dev->seeding;
                 dev->reloc = FALSE;
                 dev->removable = FALSE;
-                dev->disk_num = v->disk_num;
-                dev->part_num = v->part_num;
+                dev->disk_num = vc->disk_num;
+                dev->part_num = vc->part_num;
+                dev->num_trim_entries = 0;
+                InitializeListHead(&dev->trim_list);
+                
                 add_device_to_list(Vcb, dev);
                 Vcb->devices_loaded++;
                 
-                ExReleaseResourceLite(&volumes_lock);
+                ExReleaseResourceLite(&vde->child_lock);
                 
                 return dev;
             }
@@ -2925,7 +2741,7 @@
         }
     }
     
-    ExReleaseResourceLite(&volumes_lock);
+    ExReleaseResourceLite(&vde->child_lock);
     
     WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
          uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
@@ -2968,27 +2784,17 @@
     return cc;
 }
 
-void init_device(device_extension* Vcb, device* dev, BOOL get_length, BOOL get_nums) {
+void init_device(device_extension* Vcb, device* dev, BOOL get_nums) {
     NTSTATUS Status;
     ULONG aptelen;
     ATA_PASS_THROUGH_EX* apte;
     IDENTIFY_DEVICE_DATA* idd;
+    STORAGE_PROPERTY_QUERY spq;
+    DEVICE_TRIM_DESCRIPTOR dtd;
     
     dev->removable = is_device_removable(dev->devobj);
     dev->change_count = dev->removable ? get_device_change_count(dev->devobj) : 0;
     
-    if (get_length) {
-        GET_LENGTH_INFORMATION gli;
-        
-        Status = dev_ioctl(dev->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
-                           &gli, sizeof(GET_LENGTH_INFORMATION), TRUE, NULL);
-        
-        if (!NT_SUCCESS(Status))
-            ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08x\n", Status);
-        
-        dev->length = gli.Length.QuadPart;
-    }
-    
     if (get_nums) {
         STORAGE_DEVICE_NUMBER sdn;
         
@@ -2997,8 +2803,8 @@
         
         if (!NT_SUCCESS(Status)) {
             WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status);
-            dev->disk_num = 0;
-            dev->part_num = 0;
+            dev->disk_num = 0xffffffff;
+            dev->part_num = 0xffffffff;
         } else {
             dev->disk_num = sdn.DeviceNumber;
             dev->part_num = sdn.PartitionNumber;
@@ -3005,10 +2811,12 @@
         }
     }
     
-    dev->ssd = FALSE;
     dev->trim = FALSE;
     dev->readonly = dev->seeding;
     dev->reloc = FALSE;
+    dev->num_trim_entries = 0;
+    dev->stats_changed = FALSE;
+    InitializeListHead(&dev->trim_list);
     
     if (!dev->readonly) {
         Status = dev_ioctl(dev->devobj, IOCTL_DISK_IS_WRITABLE, NULL, 0,
@@ -3031,7 +2839,7 @@
     apte->DataTransferLength = aptelen - sizeof(ATA_PASS_THROUGH_EX);
     apte->TimeOutValue = 3;
     apte->DataBufferOffset = apte->Length;
-    apte->CurrentTaskFile[6] = 0xec; // IDENTIFY DEVICE
+    apte->CurrentTaskFile[6] = IDE_COMMAND_IDENTIFY;
     
     Status = dev_ioctl(dev->devobj, IOCTL_ATA_PASS_THROUGH, apte, aptelen,
                        apte, aptelen, TRUE, NULL);
@@ -3041,15 +2849,24 @@
     else {
         idd = (IDENTIFY_DEVICE_DATA*)((UINT8*)apte + sizeof(ATA_PASS_THROUGH_EX));
         
-        if (idd->NominalMediaRotationRate == 1) {
-            dev->ssd = TRUE;
-            TRACE("device identified as SSD\n");
-        } else if (idd->NominalMediaRotationRate == 0)
-            TRACE("no rotational speed returned, assuming not SSD\n");
-        else
-            TRACE("rotational speed of %u RPM\n", idd->NominalMediaRotationRate);
-        
-        if (idd->DataSetManagementFeature.SupportsTrim) {
+        if (idd->CommandSetSupport.FlushCache) {
+            dev->can_flush = TRUE;
+            TRACE("FLUSH CACHE supported\n");
+        } else
+            TRACE("FLUSH CACHE not supported\n");
+    }
+    
+    ExFreePool(apte);
+    
+    spq.PropertyId = StorageDeviceTrimProperty;
+    spq.QueryType = PropertyStandardQuery;
+    spq.AdditionalParameters[0] = 0;
+    
+    Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(STORAGE_PROPERTY_QUERY),
+                       &dtd, sizeof(DEVICE_TRIM_DESCRIPTOR), TRUE, NULL);
+    
+    if (NT_SUCCESS(Status)) {
+        if (dtd.TrimEnabled) {
             dev->trim = TRUE;
             Vcb->trim = TRUE;
             TRACE("TRIM supported\n");
@@ -3057,7 +2874,7 @@
             TRACE("TRIM not supported\n");
     }
     
-    ExFreePool(apte);
+    RtlZeroMemory(dev->stats, sizeof(UINT64) * 5);
 }
 
 static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb, PIRP Irp) {
@@ -3100,7 +2917,7 @@
                         RtlCopyMemory(&dev->devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
                         
                         if (le != Vcb->devices.Flink)
-                            init_device(Vcb, dev, TRUE, TRUE);
+                            init_device(Vcb, dev, TRUE);
                         
                         done = TRUE;
                         break;
@@ -3110,49 +2927,42 @@
                 }
                 
                 if (!done) {
-                    ExAcquireResourceSharedLite(&volumes_lock, TRUE);
+                    volume_device_extension* vde = Vcb->vde;
                     
-                    if (!IsListEmpty(&volumes) && Vcb->devices_loaded < Vcb->superblock.num_devices) {
-                        LIST_ENTRY* le = volumes.Flink;
+                    ExAcquireResourceSharedLite(&vde->child_lock, TRUE);
+                    
+                    if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
+                        LIST_ENTRY* le = vde->children.Flink;
                         
-                        while (le != &volumes) {
-                            volume* v = CONTAINING_RECORD(le, volume, list_entry);
+                        while (le != &vde->children) {
+                            volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
             
-                            if (RtlCompareMemory(&di->device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
-                                PFILE_OBJECT FileObject;
-                                PDEVICE_OBJECT DeviceObject;
+                            if (RtlCompareMemory(&di->device_uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
                                 device* dev;
-                                
-                                Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_DATA | FILE_WRITE_DATA, &FileObject, &DeviceObject);
-                                if (!NT_SUCCESS(Status)) {
-                                    ExReleaseResourceLite(&volumes_lock);
-                                    ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
-                                    return Status;
-                                }
-                                
-                                DeviceObject = FileObject->DeviceObject;
-                                
-                                ObReferenceObject(DeviceObject);
-                                ObDereferenceObject(FileObject);
-                                
+
                                 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
                                 if (!dev) {
-                                    ExReleaseResourceLite(&volumes_lock);
+                                    ExReleaseResourceLite(&vde->child_lock);
                                     ERR("out of memory\n");
-                                    ObDereferenceObject(DeviceObject);
                                     return STATUS_INSUFFICIENT_RESOURCES;
                                 }
                                 
                                 RtlZeroMemory(dev, sizeof(device));
                                
-                                dev->devobj = DeviceObject;
+                                dev->devobj = vc->devobj;
                                 RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
-                                dev->seeding = v->seeding;
-                                init_device(Vcb, dev, FALSE, FALSE);
+                                dev->seeding = vc->seeding;
+                                init_device(Vcb, dev, FALSE);
 
-                                dev->length = v->length;
-                                dev->disk_num = v->disk_num;
-                                dev->part_num = v->part_num;
+                                if (dev->devitem.num_bytes > vc->size) {
+                                    WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", tp.item->key.offset,
+                                         dev->devitem.num_bytes, vc->size);
+
+                                    dev->devitem.num_bytes = vc->size;
+                                }
+
+                                dev->disk_num = vc->disk_num;
+                                dev->part_num = vc->part_num;
                                 add_device_to_list(Vcb, dev);
                                 Vcb->devices_loaded++;
 
@@ -3171,7 +2981,7 @@
                     } else
                         ERR("unexpected device %llx found\n", tp.item->key.offset);
                     
-                    ExReleaseResourceLite(&volumes_lock);
+                    ExReleaseResourceLite(&vde->child_lock);
                 }
             }
         } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
@@ -3188,10 +2998,11 @@
                 c->size = tp.item->size;
                 c->offset = tp.item->key.offset;
                 c->used = c->oldused = 0;
-                c->cache = NULL;
+                c->cache = c->old_cache = NULL;
                 c->created = FALSE;
                 c->readonly = FALSE;
                 c->reloc = FALSE;
+                c->cache_loaded = FALSE;
                 
                 c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, tp.item->size, ALLOC_TAG);
                 
@@ -3212,6 +3023,9 @@
                 if (c->chunk_item->type & BLOCK_FLAG_SYSTEM && c->chunk_item->type > Vcb->system_flags)
                     Vcb->system_flags = c->chunk_item->type;
                 
+                if (c->chunk_item->type & BLOCK_FLAG_RAID10 && c->chunk_item->sub_stripes == 0)
+                    c->chunk_item->sub_stripes = 1; // avoid potential division by zero
+                
                 if (c->chunk_item->num_stripes > 0) {
                     CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
                     UINT16 i;
@@ -3251,10 +3065,12 @@
                 InitializeListHead(&c->changed_extents);
                 
                 InitializeListHead(&c->range_locks);
-                KeInitializeSpinLock(&c->range_locks_spinlock);
+                ExInitializeResourceLite(&c->range_locks_lock);
                 KeInitializeEvent(&c->range_locks_event, NotificationEvent, FALSE);
                 
                 c->last_alloc_set = FALSE;
+                
+                c->last_stripe = 0;
 
                 InsertTailList(&Vcb->chunks, &c->list_entry);
                 
@@ -3331,17 +3147,18 @@
                 }
             }
         } else if (ci->type & BLOCK_FLAG_RAID5) {
+            UINT64 stripe_size = ci->size / (ci->num_stripes - 1);
+            
             for (j = 0; j < ci->num_stripes; j++) {
-                UINT64 stripe_size = ci->size / (ci->num_stripes - 1);
-                
                 if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
                     TRACE("cut out superblock in chunk %llx\n", c->offset);
                     
                     off_start = superblock_addrs[i] - cis[j].offset;
-                    off_start -= off_start % (ci->stripe_length * (ci->num_stripes - 1));
+                    off_start -= off_start % ci->stripe_length;
                     off_start *= ci->num_stripes - 1;
 
-                    off_end = off_start + (ci->stripe_length * (ci->num_stripes - 1));
+                    off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
+                    off_end *= ci->num_stripes - 1;
                     
                     TRACE("cutting out %llx, size %llx\n", c->offset + off_start, off_end - off_start);
 
@@ -3349,17 +3166,18 @@
                 }
             }
         } else if (ci->type & BLOCK_FLAG_RAID6) {
+            UINT64 stripe_size = ci->size / (ci->num_stripes - 2);
+            
             for (j = 0; j < ci->num_stripes; j++) {
-                UINT64 stripe_size = ci->size / (ci->num_stripes - 2);
-                
                 if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
                     TRACE("cut out superblock in chunk %llx\n", c->offset);
                     
                     off_start = superblock_addrs[i] - cis[j].offset;
-                    off_start -= off_start % (ci->stripe_length * (ci->num_stripes - 2));
+                    off_start -= off_start % ci->stripe_length;
                     off_start *= ci->num_stripes - 2;
 
-                    off_end = off_start + (ci->stripe_length * (ci->num_stripes - 2));
+                    off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
+                    off_end *= ci->num_stripes - 2;
                     
                     TRACE("cutting out %llx, size %llx\n", c->offset + off_start, off_end - off_start);
 
@@ -3420,23 +3238,6 @@
             }
         }
 
-        if (!Vcb->readonly) {
-            // It doesn't make a great deal of sense to load the free space cache of a
-            // readonly seeding chunk, as we'll never write to it. But btrfs check will
-            // complain if we don't write a valid cache, so we have to do it anyway...
-                
-            // 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, Irp);
-            if (!NT_SUCCESS(Status)) {
-                ERR("load_free_space_cache returned %08x\n", Status);
-                return Status;
-            }
-            
-            protect_superblocks(Vcb, c);
-        }
-
         le = le->Flink;
     }
     
@@ -3683,41 +3484,213 @@
     return STATUS_SUCCESS;
 }
 
-static BOOL raid_generations_okay(device_extension* Vcb) {
-    LIST_ENTRY* le2;
+static BOOL is_btrfs_volume(PDEVICE_OBJECT DeviceObject) {
+    NTSTATUS Status;
+    MOUNTDEV_NAME mdn, *mdn2;
+    ULONG mdnsize;
     
-    // FIXME - if the difference between superblocks is small, we should try to recover
+    Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), TRUE, NULL);
+    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
+        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
+        return FALSE;
+    }
     
-    le2 = Vcb->devices.Flink;
-    while (le2 != &Vcb->devices) {
-        LIST_ENTRY* le;
-        device* dev = CONTAINING_RECORD(le2, device, list_entry);
+    mdnsize = offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
+    
+    mdn2 = ExAllocatePoolWithTag(PagedPool, mdnsize, ALLOC_TAG);
+    if (!mdn2) {
+        ERR("out of memory\n");
+        return FALSE;
+    }
+    
+    Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, TRUE, NULL);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
+        ExFreePool(mdn2);
+        return FALSE;
+    }
+    
+    if (mdn2->NameLength > wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR) &&
+        RtlCompareMemory(mdn2->Name, BTRFS_VOLUME_PREFIX, wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR)) == wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR)) {
+        ExFreePool(mdn2);
+        return TRUE;
+    }
+    
+    ExFreePool(mdn2);
+    
+    return FALSE;
+}
+
+static NTSTATUS get_device_pnp_name_guid(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING pnp_name, const GUID* guid) {
+    NTSTATUS Status;
+    WCHAR *list = NULL, *s;
+    
+    Status = IoGetDeviceInterfaces((PVOID)guid, NULL, 0, &list);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IoGetDeviceInterfaces returned %08x\n", Status);
+        return Status;
+    }
+    
+    s = list;
+    while (s[0] != 0) {
+        PFILE_OBJECT FileObject;
+        PDEVICE_OBJECT devobj;
+        UNICODE_STRING name;
         
-        ExAcquireResourceSharedLite(&volumes_lock, TRUE);
+        name.Length = name.MaximumLength = wcslen(s) * sizeof(WCHAR);
+        name.Buffer = s;
         
-        le = volumes.Flink;
-        
-        while (le != &volumes) {
-            volume* v = CONTAINING_RECORD(le, volume, list_entry);
+        if (NT_SUCCESS(IoGetDeviceObjectPointer(&name, FILE_READ_ATTRIBUTES, &FileObject, &devobj))) {
+            if (DeviceObject == devobj || DeviceObject == FileObject->DeviceObject) {
+                ObDereferenceObject(FileObject);
+                
+                pnp_name->Buffer = ExAllocatePoolWithTag(PagedPool, name.Length, ALLOC_TAG);
+                if (!pnp_name->Buffer) {
+                    ERR("out of memory\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto end;
+                }
+                
+                RtlCopyMemory(pnp_name->Buffer, name.Buffer, name.Length);
+                pnp_name->Length = pnp_name->MaximumLength = name.Length;
+                
+                Status = STATUS_SUCCESS;
+                goto end;
+            }
             
-            if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
-                RtlCompareMemory(&dev->devitem.device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
-            ) {
-                if (v->gen1 != Vcb->superblock.generation - 1) {
-                    WARN("device %llu had generation %llx, expected %llx\n", dev->devitem.dev_id, v->gen1, Vcb->superblock.generation - 1);
-                    ExReleaseResourceLite(&volumes_lock);
-                    return FALSE;
-                } else
-                    break;
-            }
-            le = le->Flink;
+            ObDereferenceObject(FileObject);
         }
         
-        ExReleaseResourceLite(&volumes_lock);
+        s = &s[wcslen(s) + 1];
+    }
+    
+    pnp_name->Length = pnp_name->MaximumLength = 0;
+    pnp_name->Buffer = 0;
+    
+    Status = STATUS_NOT_FOUND;
+    
+end:
+    if (list)
+        ExFreePool(list);
+    
+    return Status;
+}
+
+NTSTATUS get_device_pnp_name(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING pnp_name, const GUID** guid) {
+    NTSTATUS Status;
+    
+    Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_VOLUME);
+    if (NT_SUCCESS(Status)) {
+        *guid = &GUID_DEVINTERFACE_VOLUME;
+        return Status;
+    }
+    
+    Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_HIDDEN_VOLUME);
+    if (NT_SUCCESS(Status)) {
+        *guid = &GUID_DEVINTERFACE_HIDDEN_VOLUME;
+        return Status;
+    }
+    
+    Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_DISK);
+    if (NT_SUCCESS(Status)) {
+        *guid = &GUID_DEVINTERFACE_DISK;
+        return Status;
+    }
+    
+    return STATUS_NOT_FOUND;
+}
+
+static NTSTATUS check_mount_device(PDEVICE_OBJECT DeviceObject) {
+    NTSTATUS Status;
+    ULONG to_read;
+    superblock* sb;
+    UINT32 crc32;
+    UNICODE_STRING pnp_name;
+    const GUID* guid;
+    
+    to_read = DeviceObject->SectorSize == 0 ? sizeof(superblock) : sector_align(sizeof(superblock), DeviceObject->SectorSize);
+    
+    sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+    if (!sb) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    Status = sync_read_phys(DeviceObject, superblock_addrs[0], to_read, (PUCHAR)sb, FALSE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("sync_read_phys returned %08x\n", Status);
+        goto end;
+    }
+
+    if (sb->magic != BTRFS_MAGIC) {
+        Status = STATUS_SUCCESS;
+        goto end;
+    }
+    
+    crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
         
-        le2 = le2->Flink;
+    if (crc32 != *((UINT32*)sb->checksum)) {
+        WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
+        Status = STATUS_SUCCESS;
+        goto end;
     }
     
+    Status = get_device_pnp_name(DeviceObject, &pnp_name, &guid);
+    if (!NT_SUCCESS(Status)) {
+        ERR("get_device_pnp_name returned %08x\n", Status);
+        goto end;
+    }
+    
+    if (pnp_name.Length == 0)
+        goto end;
+    
+    volume_arrival(drvobj, &pnp_name);
+    
+    ExFreePool(pnp_name.Buffer);
+
+end:
+    ExFreePool(sb);
+    
+    return Status;
+}
+
+static BOOL still_has_superblock(PDEVICE_OBJECT device) {
+    NTSTATUS Status;
+    ULONG to_read;
+    superblock* sb;
+    
+    to_read = device->SectorSize == 0 ? sizeof(superblock) : sector_align(sizeof(superblock), device->SectorSize);
+    
+    sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+    if (!sb) {
+        ERR("out of memory\n");
+        return FALSE;
+    }
+    
+    Status = sync_read_phys(device, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("Failed to read superblock: %08x\n", Status);
+        ExFreePool(sb);
+        return FALSE;
+    }
+    
+    if (sb->magic != BTRFS_MAGIC) {
+        TRACE("not a BTRFS volume\n");
+        ExFreePool(sb);
+        return FALSE;
+    } else {
+        UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+        
+        if (crc32 != *((UINT32*)sb->checksum)) {
+            WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
+            ExFreePool(sb);
+            return FALSE;
+        }
+    }
+    
+    device->Flags &= ~DO_VERIFY_VOLUME;
+    
+    ExFreePool(sb);
     return TRUE;
 }
 
@@ -3727,7 +3700,6 @@
     PDEVICE_OBJECT DeviceToMount;
     NTSTATUS Status;
     device_extension* Vcb = NULL;
-    GET_LENGTH_INFORMATION gli;
     LIST_ENTRY *le, batchlist;
     KEY searchkey;
     traverse_ptr tp;
@@ -3735,6 +3707,8 @@
     ccb* root_ccb = NULL;
     BOOL init_lookaside = FALSE;
     device* dev;
+    volume_device_extension* vde;
+    volume_child* vc;
     
     TRACE("(%p, %p)\n", DeviceObject, Irp);
     
@@ -3745,13 +3719,63 @@
 
     IrpSp = IoGetCurrentIrpStackLocation(Irp);
     DeviceToMount = IrpSp->Parameters.MountVolume.DeviceObject;
+    
+    if (!is_btrfs_volume(DeviceToMount)) {
+        Status = check_mount_device(DeviceToMount);
+        if (!NT_SUCCESS(Status))
+            WARN("check_mount_device returned %08x\n", Status);
 
-    Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), TRUE, NULL);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error reading length information: %08x\n", Status);
         Status = STATUS_UNRECOGNIZED_VOLUME;
+        goto exit2;
+    }
+    
+    vde = DeviceToMount->DeviceExtension;
+    
+    if (!vde || vde->type != VCB_TYPE_VOLUME) {
+        Status = STATUS_UNRECOGNIZED_VOLUME;
+        goto exit2;
+    }
+    
+    ExAcquireResourceExclusiveLite(&vde->child_lock, TRUE);
+    
+    le = vde->children.Flink;
+    while (le != &vde->children) {
+        LIST_ENTRY* le2 = le->Flink;
+        
+        vc = CONTAINING_RECORD(vde->children.Flink, volume_child, list_entry);
+        
+        if (!still_has_superblock(vc->devobj)) {
+            remove_volume_child(vde, vc, TRUE);
+            
+            if (vde->num_children == 0) {
+                ERR("error - number of devices is zero\n");
+                Status = STATUS_INTERNAL_ERROR;
+                goto exit2;
+            }
+            
+            if (vde->children_loaded == 0) { // just removed last device
+                Status = STATUS_DEVICE_NOT_READY;
+                goto exit2;
+            }
+        }
+        
+        le = le2;
+    }
+    
+    if (vde->children_loaded < vde->num_children) { // devices missing
+        Status = STATUS_DEVICE_NOT_READY;
         goto exit;
     }
+    
+    if (vde->num_children == 0) {
+        ERR("error - number of devices is zero\n");
+        Status = STATUS_INTERNAL_ERROR;
+        goto exit;
+    }
+    
+    ExConvertExclusiveToSharedLite(&vde->child_lock);
+    
+    vc = CONTAINING_RECORD(vde->children.Flink, volume_child, list_entry);
 
     Status = IoCreateDevice(drvobj, sizeof(device_extension), NULL, FILE_DEVICE_DISK_FILE_SYSTEM, 0, FALSE, &NewDeviceObject);
     if (!NT_SUCCESS(Status)) {
@@ -3761,9 +3785,15 @@
     }
     
     NewDeviceObject->Flags |= DO_DIRECT_IO;
+    
+    // Some programs seem to expect that the sector size will be 512, for
+    // FILE_NO_INTERMEDIATE_BUFFERING and the like.
+    NewDeviceObject->SectorSize = min(DeviceToMount->SectorSize, 512);
+    
     Vcb = (PVOID)NewDeviceObject->DeviceExtension;
     RtlZeroMemory(Vcb, sizeof(device_extension));
-    Vcb->type = VCB_TYPE_VOLUME;
+    Vcb->type = VCB_TYPE_FS;
+    Vcb->vde = vde;
     
     ExInitializeResourceLite(&Vcb->tree_lock);
     Vcb->open_trees = 0;
@@ -3771,15 +3801,16 @@
 
     ExInitializeResourceLite(&Vcb->fcb_lock);
     ExInitializeResourceLite(&Vcb->chunk_lock);
+    ExInitializeResourceLite(&Vcb->dirty_fcbs_lock);
+    ExInitializeResourceLite(&Vcb->dirty_filerefs_lock);
+    ExInitializeResourceLite(&Vcb->scrub.stats_lock);
 
     ExInitializeResourceLite(&Vcb->load_lock);
     ExAcquireResourceExclusiveLite(&Vcb->load_lock, TRUE);
 
     DeviceToMount->Flags |= DO_DIRECT_IO;
-    
-    TRACE("partition length = %llx\n", gli.Length.QuadPart);
 
-    Status = read_superblock(Vcb, DeviceToMount, gli.Length.QuadPart);
+    Status = read_superblock(Vcb, vc->devobj, vc->size);
     if (!NT_SUCCESS(Status)) {
         Status = STATUS_UNRECOGNIZED_VOLUME;
         goto exit;
@@ -3803,24 +3834,6 @@
         goto exit;
     }
     
-    ExAcquireResourceSharedLite(&volumes_lock, TRUE);
-    
-    le = volumes.Flink;
-    while (le != &volumes) {
-        volume* v = CONTAINING_RECORD(le, volume, list_entry);
-        
-        if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && v->devnum < Vcb->superblock.dev_item.dev_id) {
-            // skipping over device in RAID which isn't the first one
-            ExReleaseResourceLite(&volumes_lock);
-            Status = STATUS_UNRECOGNIZED_VOLUME;
-            goto exit;
-        }
-        
-        le = le->Flink;
-    }
-    
-    ExReleaseResourceLite(&volumes_lock);
-    
     Vcb->readonly = FALSE;
     if (Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED) {
         WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED);
@@ -3841,13 +3854,19 @@
         goto exit;
     }
     
-    dev->devobj = DeviceToMount;
+    dev->devobj = vc->devobj;
     RtlCopyMemory(&dev->devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
     
+    if (dev->devitem.num_bytes > vc->size) {
+        WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", dev->devitem.dev_id,
+                dev->devitem.num_bytes, vc->size);
+
+        dev->devitem.num_bytes = vc->size;
+    }
+    
     dev->seeding = Vcb->superblock.flags & BTRFS_SUPERBLOCK_FLAGS_SEEDING ? TRUE : FALSE;
     
-    init_device(Vcb, dev, FALSE, TRUE);
-    dev->length = gli.Length.QuadPart;
+    init_device(Vcb, dev, TRUE);
     
     InsertTailList(&Vcb->devices, &dev->list_entry);
     Vcb->devices_loaded = 1;
@@ -3866,7 +3885,7 @@
     
     Vcb->log_to_phys_loaded = FALSE;
     
-    add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, NULL);
+    add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, Vcb->superblock.chunk_root_generation, NULL);
     
     if (!Vcb->chunk_root) {
         ERR("Could not load chunk root.\n");
@@ -3889,20 +3908,24 @@
     InitializeListHead(&Vcb->dirty_fcbs);
     InitializeListHead(&Vcb->dirty_filerefs);
     
-    KeInitializeSpinLock(&Vcb->dirty_fcbs_lock);
-    KeInitializeSpinLock(&Vcb->dirty_filerefs_lock);
-    
     InitializeListHead(&Vcb->DirNotifyList);
+    InitializeListHead(&Vcb->scrub.errors);
 
     FsRtlNotifyInitializeSync(&Vcb->NotifySync);
     
     ExInitializePagedLookasideList(&Vcb->tree_data_lookaside, NULL, NULL, 0, sizeof(tree_data), ALLOC_TAG, 0);
     ExInitializePagedLookasideList(&Vcb->traverse_ptr_lookaside, NULL, NULL, 0, sizeof(traverse_ptr), ALLOC_TAG, 0);
-    ExInitializePagedLookasideList(&Vcb->rollback_item_lookaside, NULL, NULL, 0, sizeof(rollback_item), ALLOC_TAG, 0);
     ExInitializePagedLookasideList(&Vcb->batch_item_lookaside, NULL, NULL, 0, sizeof(batch_item), ALLOC_TAG, 0);
+    ExInitializePagedLookasideList(&Vcb->fileref_lookaside, NULL, NULL, 0, sizeof(file_ref), ALLOC_TAG, 0);
+    ExInitializePagedLookasideList(&Vcb->fcb_lookaside, NULL, NULL, 0, sizeof(fcb), ALLOC_TAG, 0);
+    ExInitializePagedLookasideList(&Vcb->name_bit_lookaside, NULL, NULL, 0, sizeof(name_bit), ALLOC_TAG, 0);
     ExInitializeNPagedLookasideList(&Vcb->range_lock_lookaside, NULL, NULL, 0, sizeof(range_lock), ALLOC_TAG, 0);
+    ExInitializeNPagedLookasideList(&Vcb->fileref_np_lookaside, NULL, NULL, 0, sizeof(file_ref_nonpaged), ALLOC_TAG, 0);
+    ExInitializeNPagedLookasideList(&Vcb->fcb_np_lookaside, NULL, NULL, 0, sizeof(fcb_nonpaged), ALLOC_TAG, 0);
     init_lookaside = TRUE;
     
+    Vcb->Vpb = IrpSp->Parameters.MountVolume.Vpb;
+    
     Status = load_chunk_root(Vcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("load_chunk_root returned %08x\n", Status);
@@ -3940,15 +3963,6 @@
             if (Vcb->readonly)
                 WARN("setting volume to readonly\n");
         }
-        
-        if (!raid_generations_okay(Vcb)) {
-            ERR("could not mount as generation mismatch\n");
-            
-            IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL);
-
-            Status = STATUS_INTERNAL_ERROR;
-            goto exit;
-        }
     } else {
         if (dev->readonly) {
             WARN("setting volume to readonly as device is readonly\n");
@@ -3956,7 +3970,7 @@
         }
     }
     
-    add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, NULL);
+    add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, Vcb->superblock.generation - 1, NULL);
     
     if (!Vcb->root_root) {
         ERR("Could not load root of roots.\n");
@@ -3979,8 +3993,12 @@
     InitializeListHead(&batchlist);
     
     // 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);
+    if (!Vcb->readonly && (Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation || Vcb->options.clear_cache)) {
+        if (Vcb->options.clear_cache)
+            WARN("ClearCache option was set, clearing cache...\n");
+        else
+            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, &batchlist, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("clear_free_space_cache returned %08x\n", Status);
@@ -3989,9 +4007,9 @@
         }
     }
     
-    commit_batch_list(Vcb, &batchlist, Irp, NULL);
+    commit_batch_list(Vcb, &batchlist, Irp);
     
-    Vcb->volume_fcb = create_fcb(NonPagedPool);
+    Vcb->volume_fcb = create_fcb(Vcb, NonPagedPool);
     if (!Vcb->volume_fcb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -4001,7 +4019,7 @@
     Vcb->volume_fcb->Vcb = Vcb;
     Vcb->volume_fcb->sd = NULL;
     
-    root_fcb = create_fcb(NonPagedPool);
+    root_fcb = create_fcb(Vcb, NonPagedPool);
     if (!root_fcb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -4052,9 +4070,9 @@
     
     fcb_get_sd(root_fcb, NULL, TRUE, Irp);
     
-    root_fcb->atts = get_file_attributes(Vcb, &root_fcb->inode_item, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE, Irp);
+    root_fcb->atts = get_file_attributes(Vcb, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE, Irp);
     
-    Vcb->root_fileref = create_fileref();
+    Vcb->root_fileref = create_fileref(Vcb);
     if (!Vcb->root_fileref) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -4112,7 +4130,6 @@
     NewDeviceObject->Vpb->VolumeLabel[0] = '?';
     NewDeviceObject->Vpb->VolumeLabel[1] = 0;
     NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point?
-    Vcb->Vpb = NewDeviceObject->Vpb;
     
     KeInitializeEvent(&Vcb->flush_thread_finished, NotificationEvent, FALSE);
     
@@ -4137,8 +4154,13 @@
         WARN("look_for_balance_item returned %08x\n", Status);
     
     Status = STATUS_SUCCESS;
+    
+    vde->mounted_device = NewDeviceObject;
+    
+exit:
+    ExReleaseResourceLite(&vde->child_lock);
 
-exit:
+exit2:
     if (Vcb) {
         ExReleaseResourceLite(&Vcb->load_lock);
     }
@@ -4148,15 +4170,19 @@
             if (init_lookaside) {
                 ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
                 ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
-                ExDeletePagedLookasideList(&Vcb->rollback_item_lookaside);
                 ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
+                ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
+                ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
+                ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
                 ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
+                ExDeleteNPagedLookasideList(&Vcb->fileref_np_lookaside);
+                ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
             }
                 
             if (Vcb->root_file)
                 ObDereferenceObject(Vcb->root_file);
             else if (Vcb->root_fileref)
-                free_fileref(Vcb->root_fileref);
+                free_fileref(Vcb, Vcb->root_fileref);
             else if (root_fcb)
                 free_fcb(root_fcb);
 
@@ -4167,6 +4193,9 @@
             ExDeleteResourceLite(&Vcb->load_lock);
             ExDeleteResourceLite(&Vcb->fcb_lock);
             ExDeleteResourceLite(&Vcb->chunk_lock);
+            ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
+            ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
+            ExDeleteResourceLite(&Vcb->scrub.stats_lock);
 
             if (Vcb->devices.Flink) {
                 while (!IsListEmpty(&Vcb->devices)) {
@@ -4195,17 +4224,14 @@
 
 static NTSTATUS verify_volume(PDEVICE_OBJECT devobj) {
     device_extension* Vcb = devobj->DeviceExtension;
-    ULONG cc, to_read;
-    IO_STATUS_BLOCK iosb;
+    ULONG cc;
     NTSTATUS Status;
-    superblock* sb;
-    UINT32 crc32;
     LIST_ENTRY* le;
     
     if (Vcb->removing)
         return STATUS_WRONG_VOLUME;
     
-    Status = dev_ioctl(Vcb->Vpb->RealDevice, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+    Status = dev_ioctl(Vcb->Vpb->RealDevice, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, NULL);
     
     if (!NT_SUCCESS(Status)) {
         ERR("dev_ioctl returned %08x\n", Status);
@@ -4212,53 +4238,16 @@
         return Status;
     }
     
-    to_read = devobj->SectorSize == 0 ? sizeof(superblock) : sector_align(sizeof(superblock), devobj->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);
-    
     ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
     
     le = Vcb->devices.Flink;
     while (le != &Vcb->devices) {
         device* dev = CONTAINING_RECORD(le, device, list_entry);
+        superblock* sb;
+        UINT32 crc32;
+        ULONG to_read;
         
         if (dev->removable) {
-            NTSTATUS Status;
-            ULONG cc;
             IO_STATUS_BLOCK iosb;
             
             Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
@@ -4278,6 +4267,44 @@
             dev->change_count = cc;
         }
         
+        to_read = dev->devobj->SectorSize == 0 ? sizeof(superblock) : sector_align(sizeof(superblock), dev->devobj->SectorSize);
+        
+        sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+        if (!sb) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        Status = sync_read_phys(dev->devobj, 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;
+        }
+        
+        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("checksum error\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;
+        }
+        
+        ExFreePool(sb);
+        
         dev->devobj->Flags &= ~DO_VERIFY_VOLUME;
         
         le = le->Flink;
@@ -4302,9 +4329,12 @@
 
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_file_system_control(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || (Vcb->type != VCB_TYPE_FS && Vcb->type != VCB_TYPE_CONTROL)) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
     
     Status = STATUS_NOT_IMPLEMENTED;
@@ -4323,13 +4353,13 @@
         case IRP_MN_KERNEL_CALL:
             TRACE("IRP_MN_KERNEL_CALL\n");
             
-            Status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, FALSE);
+            Status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode);
             break;
             
         case IRP_MN_USER_FS_REQUEST:
             TRACE("IRP_MN_USER_FS_REQUEST\n");
             
-            Status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, TRUE);
+            Status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode);
             break;
             
         case IRP_MN_VERIFY_VOLUME:
@@ -4351,11 +4381,11 @@
             break;
     }
 
+end:
     Irp->IoStatus.Status = Status;
 
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
     
-exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -4375,8 +4405,12 @@
 
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_lock_control(DeviceObject, Irp);
+        
+        Irp->IoStatus.Status = Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        
         goto exit;
     }
     
@@ -4395,17 +4429,6 @@
     return Status;
 }
 
-NTSTATUS part0_passthrough(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
-    NTSTATUS Status;
-    part0_device_extension* p0de = DeviceObject->DeviceExtension;
-    
-    IoSkipCurrentIrpStackLocation(Irp);
-    
-    Status = IoCallDriver(p0de->devobj, Irp);
-    
-    return Status;
-}
-
 static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     NTSTATUS Status;
     BOOL top_level;
@@ -4417,9 +4440,9 @@
 
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_shutdown(DeviceObject, Irp);
+        goto end;
     }    
     
     Status = STATUS_SUCCESS;
@@ -4432,17 +4455,103 @@
         uninit(Vcb, TRUE);
     }
     
+#ifdef _DEBUG
+    if (comfo) {
+        ObDereferenceObject(comfo);
+        comdo = NULL;
+        comfo = NULL;
+    }
+#endif
+    
+end:
     Irp->IoStatus.Status = Status;
     Irp->IoStatus.Information = 0;
 
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
 
+    if (top_level) 
+        IoSetTopLevelIrp(NULL);
+    
+    FsRtlExitFileSystem();
+
+    return Status;
+}
+
+static NTSTATUS STDCALL drv_power(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    NTSTATUS Status;
+    device_extension* Vcb = DeviceObject->DeviceExtension;
+    BOOL top_level;
+
+    FsRtlEnterFileSystem();
+
+    top_level = is_top_level(Irp);
+    
+    Irp->IoStatus.Information = 0;
+    
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_power(DeviceObject, Irp);
+        
+        Irp->IoStatus.Status = Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        
+        goto exit;
+    } else if (Vcb && Vcb->type == VCB_TYPE_FS) {
+        IoSkipCurrentIrpStackLocation(Irp);
+    
+        Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
+    
+        goto exit;
+    }
+
+    Status = STATUS_INVALID_DEVICE_REQUEST;
+    Irp->IoStatus.Status = Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    
 exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
     FsRtlExitFileSystem();
+    
+    return Status;
+}
 
+static NTSTATUS STDCALL drv_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    NTSTATUS Status;
+    device_extension* Vcb = DeviceObject->DeviceExtension;
+    BOOL top_level;
+
+    FsRtlEnterFileSystem();
+
+    top_level = is_top_level(Irp);
+    
+    Irp->IoStatus.Information = 0;
+    
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        volume_device_extension* vde = DeviceObject->DeviceExtension;
+
+        IoSkipCurrentIrpStackLocation(Irp);
+    
+        Status = IoCallDriver(vde->pdo, Irp);
+        
+        goto exit;
+    } else if (Vcb && Vcb->type == VCB_TYPE_FS) {
+        IoSkipCurrentIrpStackLocation(Irp);
+    
+        Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
+    
+        goto exit;
+    }
+
+    Status = Irp->IoStatus.Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    
+exit:
+    if (top_level) 
+        IoSetTopLevelIrp(NULL);
+    
+    FsRtlExitFileSystem();
+    
     return Status;
 }
 
@@ -4483,11 +4592,9 @@
     rl->thread = PsGetCurrentThread();
     
     while (TRUE) {
-        KIRQL irql;
-        
         locked = FALSE;
         
-        KeAcquireSpinLock(&c->range_locks_spinlock, &irql);
+        ExAcquireResourceExclusiveLite(&c->range_locks_lock, TRUE);
         
         le = c->range_locks.Flink;
         while (le != &c->range_locks) {
@@ -4504,13 +4611,13 @@
         if (!locked) {
             InsertTailList(&c->range_locks, &rl->list_entry);
             
-            KeReleaseSpinLock(&c->range_locks_spinlock, irql);
+            ExReleaseResourceLite(&c->range_locks_lock);
             return;
         }
         
         KeClearEvent(&c->range_locks_event);
         
-        KeReleaseSpinLock(&c->range_locks_spinlock, irql);
+        ExReleaseResourceLite(&c->range_locks_lock);
         
         KeWaitForSingleObject(&c->range_locks_event, UserRequest, KernelMode, FALSE, NULL);
     }
@@ -4517,10 +4624,9 @@
 }
 
 void chunk_unlock_range(device_extension* Vcb, chunk* c, UINT64 start, UINT64 length) {
-    KIRQL irql;
     LIST_ENTRY* le;
     
-    KeAcquireSpinLock(&c->range_locks_spinlock, &irql);
+    ExAcquireResourceExclusiveLite(&c->range_locks_lock, TRUE);
     
     le = c->range_locks.Flink;
     while (le != &c->range_locks) {
@@ -4537,9 +4643,15 @@
     
     KeSetEvent(&c->range_locks_event, 0, FALSE);
     
-    KeReleaseSpinLock(&c->range_locks_spinlock, irql);
+    ExReleaseResourceLite(&c->range_locks_lock);
 }
 
+void log_device_error(device_extension* Vcb, device* dev, int error) {
+    dev->stats[error]++;
+    dev->stats_changed = TRUE;
+    Vcb->stats_changed = TRUE;
+}
+
 #ifdef _DEBUG
 static void STDCALL init_serial() {
     NTSTATUS Status;
@@ -4711,6 +4823,9 @@
             
             if (!PsUpdateDiskCounters)
                 diskacc = FALSE;
+            
+            RtlInitUnicodeString(&name, L"FsRtlUpdateDiskCounters");
+            FsRtlUpdateDiskCounters = (tFsRtlUpdateDiskCounters)MmGetSystemRoutineAddress(&name);
         }
         
         RtlInitUnicodeString(&name, L"CcCopyReadEx");
@@ -4726,6 +4841,7 @@
         CcCopyReadEx = NULL;
         CcCopyWriteEx = NULL;
         CcSetAdditionalCacheAttributesEx = NULL;
+        FsRtlUpdateDiskCounters = NULL;
     }
    
     drvobj = DriverObject;
@@ -4743,15 +4859,17 @@
     DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS]            = (PDRIVER_DISPATCH)drv_flush_buffers;
     DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)drv_query_volume_information;
     DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION]   = (PDRIVER_DISPATCH)drv_set_volume_information;
-    DriverObject->MajorFunction[IRP_MJ_CLEANUP]                  = (PDRIVER_DISPATCH)drv_cleanup;
     DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL]        = (PDRIVER_DISPATCH)drv_directory_control;
     DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL]      = (PDRIVER_DISPATCH)drv_file_system_control;
-    DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL]             = (PDRIVER_DISPATCH)drv_lock_control;
     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]           = (PDRIVER_DISPATCH)drv_device_control;
     DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]                 = (PDRIVER_DISPATCH)drv_shutdown;
-    DriverObject->MajorFunction[IRP_MJ_PNP]                      = (PDRIVER_DISPATCH)drv_pnp;
+    DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL]             = (PDRIVER_DISPATCH)drv_lock_control;
+    DriverObject->MajorFunction[IRP_MJ_CLEANUP]                  = (PDRIVER_DISPATCH)drv_cleanup;
     DriverObject->MajorFunction[IRP_MJ_QUERY_SECURITY]           = (PDRIVER_DISPATCH)drv_query_security;
     DriverObject->MajorFunction[IRP_MJ_SET_SECURITY]             = (PDRIVER_DISPATCH)drv_set_security;
+    DriverObject->MajorFunction[IRP_MJ_POWER]                    = (PDRIVER_DISPATCH)drv_power;
+    DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL]           = (PDRIVER_DISPATCH)drv_system_control;
+    DriverObject->MajorFunction[IRP_MJ_PNP]                      = (PDRIVER_DISPATCH)drv_pnp;
 
     init_fast_io_dispatch(&DriverObject->FastIoDispatch);
 
@@ -4786,14 +4904,23 @@
         return Status;
     }
 
-    InitializeListHead(&volumes);
-    InitializeListHead(&pnp_disks);
-    
     InitializeListHead(&VcbList);
     ExInitializeResourceLite(&global_loading_lock);
-    ExInitializeResourceLite(&volumes_lock);
+    ExInitializeResourceLite(&volume_list_lock);
     
+    InitializeListHead(&volume_list);
+    
     Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
+                                            (PVOID)&GUID_DEVINTERFACE_VOLUME, DriverObject, volume_notification, DriverObject, &notification_entry2);
+    if (!NT_SUCCESS(Status))
+        ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
+    
+    Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
+                                            (PVOID)&GUID_DEVINTERFACE_HIDDEN_VOLUME, DriverObject, volume_notification, DriverObject, &notification_entry3);
+    if (!NT_SUCCESS(Status))
+        ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
+    
+    Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
                                             (PVOID)&GUID_DEVINTERFACE_DISK, DriverObject, pnp_notification, DriverObject, &notification_entry);
     if (!NT_SUCCESS(Status))
         ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
Index: drivers/filesystems/btrfs/btrfs.h
===================================================================
--- drivers/filesystems/btrfs/btrfs.h	(révision 74430)
+++ drivers/filesystems/btrfs/btrfs.h	(copie de travail)
@@ -495,6 +495,12 @@
     UINT8 reserved[32];
 } BALANCE_ITEM;
 
+#define BTRFS_DEV_STAT_WRITE_ERRORS          0
+#define BTRFS_DEV_STAT_READ_ERRORS           1
+#define BTRFS_DEV_STAT_FLUSH_ERRORS          2
+#define BTRFS_DEV_STAT_CORRUPTION_ERRORS     3
+#define BTRFS_DEV_STAT_GENERATION_ERRORS     4
+
 #pragma pack(pop)
 
 #endif
Index: drivers/filesystems/btrfs/btrfs.rc
===================================================================
--- drivers/filesystems/btrfs/btrfs.rc	(révision 74430)
+++ drivers/filesystems/btrfs/btrfs.rc	(copie de travail)
@@ -70,12 +70,12 @@
         BLOCK "080904b0"
         BEGIN
             VALUE "FileDescription", "WinBtrfs"
-            VALUE "FileVersion", "0.8"
+            VALUE "FileVersion", "0.9"
             VALUE "InternalName", "btrfs"
-            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016"
+            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-17"
             VALUE "OriginalFilename", "btrfs.sys"
             VALUE "ProductName", "WinBtrfs"
-            VALUE "ProductVersion", "0.8"
+            VALUE "ProductVersion", "0.9"
         END
     END
     BLOCK "VarFileInfo"
Index: drivers/filesystems/btrfs/btrfs_drv.h
===================================================================
--- drivers/filesystems/btrfs/btrfs_drv.h	(révision 74430)
+++ drivers/filesystems/btrfs/btrfs_drv.h	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -22,7 +22,7 @@
 #undef _WIN32_WINNT
 #undef NTDDI_VERSION
 
-#define _WIN32_WINNT 0x0600
+#define _WIN32_WINNT 0x0601
 #define NTDDI_VERSION 0x06010000 // Win 7
 #define _CRT_SECURE_NO_WARNINGS
 #endif /* __REACTOS__ */
@@ -38,7 +38,6 @@
 #include <iotypes.h>
 #include <pseh/pseh2.h>
 #endif /* __REACTOS__ */
-//#include <windows.h>
 #include <windef.h>
 #include <wdm.h>
 #include <stdio.h>
@@ -56,6 +55,8 @@
 #define DEBUG_PARANOID
 #endif
 
+#define UNUSED(x) (void)(x)
+
 #define BTRFS_NODE_TYPE_CCB 0x2295
 #define BTRFS_NODE_TYPE_FCB 0x2296
 
@@ -79,6 +80,9 @@
 #define EA_EA "user.EA"
 #define EA_EA_HASH 0x8270dd43
 
+#define EA_PROP_COMPRESSION "btrfs.compression"
+#define EA_PROP_COMPRESSION_HASH 0x20ccdf69
+
 #define MAX_EXTENT_SIZE 0x8000000 // 128 MB
 #define COMPRESSED_EXTENT_SIZE 0x20000 // 128 KB
 
@@ -86,6 +90,8 @@
 
 #define IO_REPARSE_TAG_LXSS_SYMLINK 0xa000001d // undocumented?
 
+#define BTRFS_VOLUME_PREFIX L"\\Device\\Btrfs{"
+
 #ifdef _MSC_VER
 #define try __try
 #define except __except
@@ -96,30 +102,8 @@
 #define finally if (1)
 #endif
 
-// #pragma pack(push, 1)
-
 struct _device_extension;
 
-typedef struct {
-    BTRFS_UUID fsuuid;
-    BTRFS_UUID devuuid;
-    UINT64 devnum;
-    UNICODE_STRING devpath;
-    UINT64 length;
-    UINT64 gen1, gen2;
-    BOOL seeding;
-    BOOL processed;
-    DWORD disk_num;
-    DWORD part_num;
-    LIST_ENTRY list_entry;
-} volume;
-
-typedef struct {
-    UNICODE_STRING devpath;
-    ULONG disk_num;
-    LIST_ENTRY list_entry;
-} pnp_disk;
-
 typedef struct _fcb_nonpaged {
     FAST_MUTEX HeaderMutex;
     SECTION_OBJECT_POINTERS segment_object;
@@ -132,7 +116,6 @@
 
 typedef struct {
     UINT64 offset;
-    EXTENT_DATA* data;
     ULONG datalen;
     BOOL unique;
     BOOL ignore;
@@ -140,6 +123,8 @@
     UINT32* csum;
     
     LIST_ENTRY list_entry;
+    
+    EXTENT_DATA extent_data;
 } extent;
 
 typedef struct {
@@ -161,6 +146,7 @@
     UNICODE_STRING name;
     UINT32 hash_uc;
     UNICODE_STRING name_uc;
+    ULONG size;
     struct _file_ref* fileref;
     LIST_ENTRY list_entry_index;
     LIST_ENTRY list_entry_hash;
@@ -167,10 +153,17 @@
     LIST_ENTRY list_entry_hash_uc;
 } dir_child;
 
+enum prop_compression_type {
+    PropCompression_None,
+    PropCompression_Zlib,
+    PropCompression_LZO
+};
+
 typedef struct _fcb {
     FSRTL_ADVANCED_FCB_HEADER Header;
     struct _fcb_nonpaged* nonpaged;
     LONG refcount;
+    POOL_TYPE pool_type;
     struct _device_extension* Vcb;
     struct _root* subvol;
     UINT64 inode;
@@ -183,8 +176,8 @@
     ULONG atts;
     SHARE_ACCESS share_access;
     WCHAR* debug_desc;
+    BOOL csum_loaded;
     LIST_ENTRY extents;
-    UINT64 last_dir_index;
     ANSI_STRING reparse_xattr;
     ANSI_STRING ea_xattr;
     ULONG ealen;
@@ -191,6 +184,7 @@
     LIST_ENTRY hardlinks;
     struct _file_ref* fileref;
     BOOL inode_item_changed;
+    enum prop_compression_type prop_compression;
     
     LIST_ENTRY dir_children_index;
     LIST_ENTRY dir_children_hash;
@@ -204,6 +198,7 @@
     BOOL extents_changed;
     BOOL reparse_xattr_changed;
     BOOL ea_changed;
+    BOOL prop_compression_changed;
     BOOL created;
     
     BOOL ads;
@@ -222,16 +217,14 @@
 } dirty_fcb;
 
 typedef struct {
+    ERESOURCE fileref_lock;
     ERESOURCE children_lock;
 } file_ref_nonpaged;
 
 typedef struct _file_ref {
     fcb* fcb;
-    UNICODE_STRING filepart;
-    UNICODE_STRING filepart_uc;
-    ANSI_STRING utf8;
     ANSI_STRING oldutf8;
-    UINT64 index;
+    UINT64 oldindex;
     BOOL delete_on_close;
     BOOL deleted;
     BOOL created;
@@ -259,7 +252,6 @@
     ULONG disposition;
     ULONG options;
     UINT64 query_dir_offset;
-//     char* query_string;
     UNICODE_STRING query_string;
     BOOL has_wildcard;
     BOOL specific_file;
@@ -276,34 +268,12 @@
     BOOL user_set_change_time;
 } ccb;
 
-// typedef struct _log_to_phys {
-//     UINT64 address;
-//     UINT64 size;
-//     UINT64 physaddr;
-//     UINT32 sector_size;
-//     struct _log_to_phys* next;
-// } log_to_phys;
-
 struct _device_extension;
 
-// enum tree_holder_status {
-//     tree_holder_unloaded,
-//     tree_holder_loading,
-//     tree_holder_loaded,
-//     tree_holder_unloading
-// };
-
-// typedef struct {
-//     enum tree_holder_status status;
-//     KSPIN_LOCK spin_lock;
-//     ERESOURCE lock;
-// } tree_holder_nonpaged;
-
 typedef struct {
     UINT64 address;
     UINT64 generation;
     struct _tree* tree;
-//     tree_holder_nonpaged* nonpaged;
 } tree_holder;
 
 typedef struct _tree_data {
@@ -322,13 +292,7 @@
     };
 } tree_data;
 
-// typedef struct _tree_nonpaged {
-//     ERESOURCE load_tree_lock;
-// } tree_nonpaged;
-
 typedef struct _tree {
-//     UINT64 address;
-//     UINT8 level;
     tree_header header;
     UINT32 hash;
     BOOL has_address;
@@ -337,7 +301,6 @@
     struct _tree* parent;
     tree_data* paritem;
     struct _root* root;
-//     tree_nonpaged* nonpaged;
     LIST_ENTRY itemlist;
     LIST_ENTRY list_entry;
     LIST_ENTRY list_entry_hash;
@@ -345,10 +308,12 @@
     BOOL has_new_address;
     BOOL updated_extents;
     BOOL write;
+    BOOL is_unique;
+    BOOL uniqueness_determined;
+    UINT8* buf;
 } tree;
 
 typedef struct {
-//     KSPIN_LOCK load_tree_lock;
     ERESOURCE load_tree_lock;
 } root_nonpaged;
 
@@ -374,6 +339,8 @@
     Batch_DeleteDirItem,
     Batch_DeleteInodeRef,
     Batch_DeleteInodeExtRef,
+    Batch_DeleteXattr,
+    Batch_DeleteExtentData,
 };
 
 typedef struct {
@@ -414,14 +381,17 @@
     BOOL seeding;
     BOOL readonly;
     BOOL reloc;
-    BOOL ssd;
     BOOL trim;
+    BOOL can_flush;
     ULONG change_count;
-    UINT64 length;
     ULONG disk_num;
     ULONG part_num;
+    UINT64 stats[5];
+    BOOL stats_changed;
     LIST_ENTRY space;
     LIST_ENTRY list_entry;
+    ULONG num_trim_entries;
+    LIST_ENTRY trim_list;
 } device;
 
 typedef struct {
@@ -439,12 +409,13 @@
     UINT32 oldused;
     device** devices;
     fcb* cache;
+    fcb* old_cache;
     LIST_ENTRY space;
     LIST_ENTRY space_size;
     LIST_ENTRY deleting;
     LIST_ENTRY changed_extents;
     LIST_ENTRY range_locks;
-    KSPIN_LOCK range_locks_spinlock;
+    ERESOURCE range_locks_lock;
     KEVENT range_locks_event;
     ERESOURCE lock;
     ERESOURCE changed_extents_lock;
@@ -452,7 +423,9 @@
     BOOL readonly;
     BOOL reloc;
     BOOL last_alloc_set;
+    BOOL cache_loaded;
     UINT64 last_alloc;
+    UINT16 last_stripe;
     
     LIST_ENTRY list_entry;
     LIST_ENTRY list_entry_changed;
@@ -525,14 +498,15 @@
     UINT32 flush_interval;
     UINT32 max_inline;
     UINT64 subvol_id;
-    UINT32 raid5_recalculation;
-    UINT32 raid6_recalculation;
     BOOL skip_balance;
+    BOOL no_barrier;
+    BOOL no_trim;
+    BOOL clear_cache;
 } mount_options;
 
-#define VCB_TYPE_VOLUME     1
-#define VCB_TYPE_PARTITION0 2
-#define VCB_TYPE_CONTROL    3
+#define VCB_TYPE_FS         1
+#define VCB_TYPE_CONTROL    2
+#define VCB_TYPE_VOLUME     3
 
 #ifdef DEBUG_STATS
 typedef struct {
@@ -548,6 +522,8 @@
     UINT64 overwrite_total_time;
     UINT64 num_creates;
     UINT64 create_total_time;
+    UINT64 open_fcb_calls;
+    UINT64 open_fcb_time;
 } debug_stats;
 #endif
 
@@ -562,7 +538,6 @@
     btrfs_balance_opts opts[3];
     BOOL paused;
     BOOL stopping;
-    BOOL cancelling;
     BOOL removing;
     BOOL dev_readonly;
     NTSTATUS status;
@@ -570,18 +545,62 @@
     KEVENT finished;
 } balance_info;
 
+typedef struct {
+    UINT64 address;
+    UINT64 device;
+    BOOL recovered;
+    BOOL is_metadata;
+    BOOL parity;
+    LIST_ENTRY list_entry;
+    
+    union {
+        struct {
+            UINT64 subvol;
+            UINT64 offset;
+            UINT16 filename_length;
+            WCHAR filename[1];
+        } data;
+        
+        struct {
+            UINT64 root;
+            UINT8 level;
+            KEY firstitem;
+        } metadata;
+    };
+} scrub_error;
+
+typedef struct {
+    HANDLE thread;
+    ERESOURCE stats_lock;
+    KEVENT event;
+    KEVENT finished;
+    BOOL stopping;
+    BOOL paused;
+    LARGE_INTEGER start_time;
+    LARGE_INTEGER finish_time;
+    LARGE_INTEGER resume_time;
+    LARGE_INTEGER duration;
+    UINT64 total_chunks;
+    UINT64 chunks_left;
+    UINT64 data_scrubbed;
+    NTSTATUS error;
+    ULONG num_errors;
+    LIST_ENTRY errors;
+} scrub_info;
+
+struct _volume_device_extension;
+
 typedef struct _device_extension {
     UINT32 type;
     mount_options options;
     PVPB Vpb;
+    struct _volume_device_extension* vde;
     LIST_ENTRY devices;
 #ifdef DEBUG_STATS
     debug_stats stats;
 #endif
     UINT64 devices_loaded;
-//     DISK_GEOMETRY geometry;
     superblock superblock;
-//     WCHAR label[MAX_LABEL_SIZE];
     BOOL readonly;
     BOOL removing;
     BOOL locked;
@@ -599,12 +618,10 @@
     LIST_ENTRY DirNotifyList;
     LONG open_trees;
     BOOL need_write;
-//     ERESOURCE LogToPhysLock;
-//     UINT64 chunk_root_phys_addr;
+    BOOL stats_changed;
     UINT64 data_flags;
     UINT64 metadata_flags;
     UINT64 system_flags;
-//     log_to_phys* log_to_phys;
     LIST_ENTRY roots;
     LIST_ENTRY drop_roots;
     root* chunk_root;
@@ -623,9 +640,9 @@
     LIST_ENTRY* trees_ptrs[256];
     LIST_ENTRY all_fcbs;
     LIST_ENTRY dirty_fcbs;
-    KSPIN_LOCK dirty_fcbs_lock;
+    ERESOURCE dirty_fcbs_lock;
     LIST_ENTRY dirty_filerefs;
-    KSPIN_LOCK dirty_filerefs_lock;
+    ERESOURCE dirty_filerefs_lock;
     ERESOURCE chunk_lock;
     HANDLE flush_thread_handle;
     KTIMER flush_thread_timer;
@@ -632,27 +649,58 @@
     KEVENT flush_thread_finished;
     drv_calc_threads calcthreads;
     balance_info balance;
+    scrub_info scrub;
     PFILE_OBJECT root_file;
     PAGED_LOOKASIDE_LIST tree_data_lookaside;
     PAGED_LOOKASIDE_LIST traverse_ptr_lookaside;
-    PAGED_LOOKASIDE_LIST rollback_item_lookaside;
     PAGED_LOOKASIDE_LIST batch_item_lookaside;
+    PAGED_LOOKASIDE_LIST fileref_lookaside;
+    PAGED_LOOKASIDE_LIST fcb_lookaside;
+    PAGED_LOOKASIDE_LIST name_bit_lookaside;
     NPAGED_LOOKASIDE_LIST range_lock_lookaside;
+    NPAGED_LOOKASIDE_LIST fileref_np_lookaside;
+    NPAGED_LOOKASIDE_LIST fcb_np_lookaside;
     LIST_ENTRY list_entry;
 } device_extension;
 
 typedef struct {
     UINT32 type;
+} control_device_extension;
+
+typedef struct {
+    BTRFS_UUID uuid;
+    UINT64 devid;
+    UINT64 generation;
     PDEVICE_OBJECT devobj;
+    UNICODE_STRING pnp_name;
+    UINT64 size;
+    BOOL seeding;
+    BOOL had_drive_letter;
+    void* notification_entry;
+    ULONG disk_num;
+    ULONG part_num;
+    LIST_ENTRY list_entry;
+} volume_child;
+
+typedef struct _volume_device_extension {
+    UINT32 type;
     BTRFS_UUID uuid;
     UNICODE_STRING name;
-} part0_device_extension;
+    PDEVICE_OBJECT device;
+    PDEVICE_OBJECT mounted_device;
+    PDEVICE_OBJECT pdo;
+    UNICODE_STRING bus_name;
+    PDEVICE_OBJECT attached_device;
+    
+    UINT64 num_children;
+    UINT64 children_loaded;
+    ERESOURCE child_lock;
+    LIST_ENTRY children;
+    
+    LIST_ENTRY list_entry;
+} volume_device_extension;
 
 typedef struct {
-    UINT32 type;
-} control_device_extension;
-
-typedef struct {
     LIST_ENTRY listentry;
     PSID sid;
     UINT32 uid;
@@ -672,7 +720,7 @@
 typedef struct {
     struct _write_data_context* context;
     UINT8* buf;
-    BOOL need_free;
+    PMDL mdl;
     device* device;
     PIRP Irp;
     IO_STATUS_BLOCK iosb;
@@ -685,6 +733,8 @@
     LIST_ENTRY stripes;
     LONG stripes_left;
     BOOL tree;
+    UINT8 *parity1, *parity2, *scratch;
+    PMDL mdl, parity1_mdl, parity2_mdl;
 } write_data_context;
 
 typedef struct {
@@ -695,7 +745,10 @@
     LIST_ENTRY list_entry;
 } tree_write;
 
-// #pragma pack(pop)
+typedef struct {
+    UNICODE_STRING us;
+    LIST_ENTRY list_entry;
+} name_bit;
 
 static __inline void* map_user_buffer(PIRP Irp) {
     if (!Irp->MdlAddress) {
@@ -752,16 +805,14 @@
 device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid);
 UINT64 sector_align( UINT64 NumberToBeAligned, UINT64 Alignment );
 BOOL get_file_attributes_from_xattr(char* val, UINT16 len, ULONG* atts);
-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 extract_xattr(void* item, USHORT size, char* name, UINT8** data, UINT16* datalen);
+ULONG STDCALL get_file_attributes(device_extension* Vcb, 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);
-fcb* create_fcb(POOL_TYPE pool_type);
-file_ref* create_fileref();
+void free_fcb(fcb* fcb);
+void free_fileref(device_extension* Vcb, file_ref* fr);
+file_ref* create_fileref(device_extension* Vcb);
 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, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, PIRP Irp);
 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);
@@ -771,15 +822,17 @@
 WCHAR* file_desc(PFILE_OBJECT FileObject);
 WCHAR* file_desc_fileref(file_ref* fileref);
 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, PIRP Irp, LIST_ENTRY* rollback);
 void chunk_lock_range(device_extension* Vcb, chunk* c, UINT64 start, UINT64 length);
 void chunk_unlock_range(device_extension* Vcb, chunk* c, UINT64 start, UINT64 length);
-void init_device(device_extension* Vcb, device* dev, BOOL get_length, BOOL get_nums);
+void init_device(device_extension* Vcb, device* dev, BOOL get_nums);
 void init_file_cache(PFILE_OBJECT FileObject, CC_FILE_SIZES* ccfs);
 NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer, BOOL override);
+NTSTATUS get_device_pnp_name(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING pnp_name, const GUID** guid);
+NTSTATUS load_cache_chunk(device_extension* Vcb, chunk* c, PIRP Irp);
+void log_device_error(device_extension* Vcb, device* dev, int error);
 
 #ifdef _MSC_VER
 #define funcname __FUNCTION__
@@ -787,10 +840,6 @@
 #define funcname __func__
 #endif
 
-// FIXME - we probably shouldn't be moving funcname etc. around if we're not printing debug messages
-#define free_fcb(fcb) _free_fcb(fcb, funcname, __FILE__, __LINE__)
-#define free_fileref(fileref) _free_fileref(fileref, funcname, __FILE__, __LINE__)
-
 extern BOOL have_sse2;
 
 extern UINT32 mount_compress;
@@ -799,9 +848,10 @@
 extern UINT32 mount_zlib_level;
 extern UINT32 mount_flush_interval;
 extern UINT32 mount_max_inline;
-extern UINT32 mount_raid5_recalculation;
-extern UINT32 mount_raid6_recalculation;
 extern UINT32 mount_skip_balance;
+extern UINT32 mount_no_barrier;
+extern UINT32 mount_no_trim;
+extern UINT32 mount_clear_cache;
 
 #ifdef _DEBUG
 
@@ -846,12 +896,6 @@
 
 #endif
 
-static __inline void increase_chunk_usage(chunk* c, UINT64 delta) {
-    c->used += delta;
-    
-    TRACE("increasing size of chunk %llx by %llx\n", c->offset, delta);
-}
-
 // in fastio.c
 void STDCALL init_fast_io_dispatch(FAST_IO_DISPATCH** fiod);
 
@@ -872,8 +916,6 @@
 } rollback_extent;
 
 enum rollback_type {
-    ROLLBACK_INSERT_ITEM,
-    ROLLBACK_DELETE_ITEM,
     ROLLBACK_INSERT_EXTENT,
     ROLLBACK_DELETE_EXTENT,
     ROLLBACK_ADD_SPACE,
@@ -887,41 +929,39 @@
 } rollback_item;
 
 // in treefuncs.c
-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 STDCALL _find_item_to_level(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, UINT8 level,
-                                     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);
+NTSTATUS STDCALL find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, PIRP Irp);
+NTSTATUS STDCALL find_item_to_level(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, UINT8 level, PIRP Irp);
+BOOL STDCALL find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, PIRP Irp);
+BOOL STDCALL find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, PIRP Irp);
 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, 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, 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(device_extension* Vcb, LIST_ENTRY* rollback);
+NTSTATUS 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);
+NTSTATUS STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp);
+tree* STDCALL free_tree(tree* t);
+NTSTATUS STDCALL load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, UINT64 generation, PIRP Irp);
+NTSTATUS STDCALL do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, PIRP Irp);
+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(device_extension* Vcb, LIST_ENTRY* rollback, enum rollback_type type, void* ptr);
-void commit_batch_list(device_extension* Vcb, LIST_ENTRY* batchlist, PIRP Irp, LIST_ENTRY* rollback);
+void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr);
+void commit_batch_list(device_extension* Vcb, LIST_ENTRY* batchlist, PIRP Irp);
 void clear_batch_list(device_extension* Vcb, LIST_ENTRY* batchlist);
 
-#define find_item(Vcb, r, tp, searchkey, ignore, Irp) _find_item(Vcb, r, tp, searchkey, ignore, Irp, funcname, __FILE__, __LINE__)
-#define find_item_to_level(Vcb, r, tp, searchkey, ignore, level, Irp) _find_item_to_level(Vcb, r, tp, searchkey, ignore, level, 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, 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 remove_drive_letter(PDEVICE_OBJECT mountmgr, volume* v);
-void add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us);
+NTSTATUS remove_drive_letter(PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath);
 #ifdef __REACTOS__
 NTSTATUS NTAPI pnp_notification(PVOID NotificationStructure, PVOID Context);
 #else
 NTSTATUS pnp_notification(PVOID NotificationStructure, PVOID Context);
 #endif
+void disk_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath);
+void volume_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath);
+void volume_removal(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath);
+#ifndef __REACTOS__
+NTSTATUS volume_notification(PVOID NotificationStructure, PVOID Context);
+#else
+NTSTATUS NTAPI volume_notification(PVOID NotificationStructure, PVOID Context);
+#endif
+void remove_volume_child(volume_device_extension* vde, volume_child* vc, BOOL no_release_lock);
 
 // in cache.c
 NTSTATUS STDCALL init_cache();
@@ -931,23 +971,24 @@
 // in write.c
 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);
+                     BOOL wait, BOOL deferred_write, BOOL write_irp, 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, PIRP Irp, LIST_ENTRY* rollback);
 chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address);
 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);
+NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, UINT32 length, write_data_context* wtc, PIRP Irp,
+                            chunk* c, BOOL file_write, UINT32 irp_offset);
+NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp, chunk* c, BOOL file_write, UINT32 irp_offset);
 void free_write_data_stripes(write_data_context* wtc);
 NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, BOOL prealloc, void* data,
-                         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, PIRP Irp, LIST_ENTRY* rollback);
-NTSTATUS do_write_file(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, PIRP Irp, LIST_ENTRY* rollback);
+                         PIRP Irp, LIST_ENTRY* rollback, UINT8 compression, UINT64 decoded_size, BOOL file_write, UINT32 irp_offset);
+NTSTATUS do_write_file(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, PIRP Irp, BOOL file_write, UINT32 irp_offset, LIST_ENTRY* rollback);
 NTSTATUS write_compressed(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, PIRP Irp, LIST_ENTRY* rollback);
 BOOL find_data_address_in_chunk(device_extension* Vcb, chunk* c, UINT64 length, UINT64* address);
 void get_raid56_lock_range(chunk* c, UINT64 address, UINT64 length, UINT64* lockaddr, UINT64* locklen);
+NTSTATUS calc_csum(device_extension* Vcb, UINT8* data, UINT32 sectors, UINT32* csum);
 
 // in dirctrl.c
 NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
@@ -958,7 +999,6 @@
 NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 BOOL get_sd_from_xattr(fcb* fcb, ULONG buflen);
 void fcb_get_sd(fcb* fcb, struct _fcb* parent, BOOL look_for_xattr, 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);
@@ -968,8 +1008,8 @@
 NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 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 STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT64 end, fcb* fcb, file_ref* fileref, PFILE_OBJECT FileObject, BOOL advance_only);
+NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset, ULONG* preqlen);
 NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp);
 NTSTATUS STDCALL drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 NTSTATUS STDCALL drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
@@ -986,65 +1026,63 @@
 NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* parsed, ULONG* fn_offset,
                       POOL_TYPE pooltype, BOOL case_sensitive, PIRP Irp);
 NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, POOL_TYPE pooltype, 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, PIRP Irp);
 NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp);
 NTSTATUS load_csum(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length, PIRP Irp);
 NTSTATUS load_dir_children(fcb* fcb, BOOL ignore_size, PIRP Irp);
-NTSTATUS add_dir_child(fcb* fcb, UINT64 inode, BOOL subvol, UINT64 index, PANSI_STRING utf8, PUNICODE_STRING name, PUNICODE_STRING name_uc, UINT8 type, dir_child** pdc);
+NTSTATUS add_dir_child(fcb* fcb, UINT64 inode, BOOL subvol, PANSI_STRING utf8, PUNICODE_STRING name, UINT8 type, dir_child** pdc);
+NTSTATUS open_fileref_child(device_extension* Vcb, file_ref* sf, PUNICODE_STRING name, BOOL case_sensitive, BOOL lastpart, BOOL streampart,
+                            POOL_TYPE pooltype, file_ref** psf2, PIRP Irp);
+fcb* create_fcb(device_extension* Vcb, POOL_TYPE pool_type);
 
 // in fsctl.c
-NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user);
+NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type);
 void do_unlock_volume(device_extension* Vcb);
+void trim_whole_device(device* dev);
 
 // in flushthread.c
 void STDCALL flush_thread(void* context);
-NTSTATUS STDCALL do_write(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS STDCALL do_write(device_extension* Vcb, PIRP Irp);
 NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback);
-void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* batchlist, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* batchlist, PIRP Irp);
 NTSTATUS STDCALL write_data_phys(PDEVICE_OBJECT device, UINT64 address, void* data, UINT32 length);
 BOOL is_tree_unique(device_extension* Vcb, tree* t, PIRP Irp);
 NTSTATUS do_tree_writes(device_extension* Vcb, LIST_ENTRY* tree_writes, PIRP Irp);
-void add_checksum_entry(device_extension* Vcb, UINT64 address, ULONG length, UINT32* csum, PIRP Irp, LIST_ENTRY* rollback);
+void add_checksum_entry(device_extension* Vcb, UINT64 address, ULONG length, UINT32* csum, PIRP Irp);
 BOOL find_metadata_address_in_chunk(device_extension* Vcb, chunk* c, UINT64* address);
+void add_trim_entry_avoid_sb(device_extension* Vcb, device* dev, UINT64 address, UINT64 size);
 
 // in read.c
 NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp);
 NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UINT32* csum, BOOL is_tree, UINT8* buf, chunk* c, chunk** pc,
-                           PIRP Irp, BOOL check_nocsum_parity);
-NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr, PIRP Irp, BOOL check_nocsum_parity);
+                           PIRP Irp, UINT64 generation, BOOL file_read, UINT32 irp_offset);
+NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr, PIRP Irp);
 NTSTATUS do_read(PIRP Irp, BOOL wait, ULONG* bytes_read);
+NTSTATUS check_csum(device_extension* Vcb, UINT8* data, UINT32 sectors, UINT32* csum);
 
 // in pnp.c
 NTSTATUS STDCALL drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp);
+NTSTATUS pnp_surprise_removal(PDEVICE_OBJECT DeviceObject, PIRP Irp);
+NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp);
 
 // in free-space.c
-NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp);
+NTSTATUS load_cache_chunk(device_extension* Vcb, chunk* c, PIRP Irp);
 NTSTATUS clear_free_space_cache(device_extension* Vcb, LIST_ENTRY* batchlist, 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(device_extension* Vcb, 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(device_extension* Vcb, LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func);
+void space_list_add(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback);
+void space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback);
+void space_list_subtract(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback);
+void space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback);
 
-#define space_list_add(Vcb, c, deleting, address, length, rollback) _space_list_add(Vcb, c, deleting, address, length, rollback, funcname)
-#define space_list_add2(Vcb, list, list_size, address, length, rollback) _space_list_add2(Vcb, 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(Vcb, list, list_size, address, length, rollback) _space_list_subtract2(Vcb, 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, PIRP Irp, 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);
 NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset,
-                                       UINT32 refcount, BOOL superseded, PIRP Irp, LIST_ENTRY* rollback);
-NTSTATUS decrease_extent_refcount_tree(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT8 level, 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, PIRP Irp, LIST_ENTRY* rollback);
+                                       UINT32 refcount, BOOL superseded, PIRP Irp);
+NTSTATUS decrease_extent_refcount_tree(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT8 level, PIRP Irp);
 UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp);
 BOOL is_extent_unique(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp);
-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 increase_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem, UINT8 level, PIRP Irp);
 UINT64 get_extent_flags(device_extension* Vcb, UINT64 address, PIRP Irp);
 void update_extent_flags(device_extension* Vcb, UINT64 address, UINT64 flags, PIRP Irp);
 NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset,
@@ -1053,7 +1091,7 @@
 UINT64 find_extent_shared_tree_refcount(device_extension* Vcb, UINT64 address, UINT64 parent, PIRP Irp);
 UINT64 find_extent_shared_data_refcount(device_extension* Vcb, UINT64 address, UINT64 parent, PIRP Irp);
 NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem,
-                                  UINT8 level, UINT64 parent, BOOL superseded, PIRP Irp, LIST_ENTRY* rollback);
+                                  UINT8 level, UINT64 parent, BOOL superseded, PIRP Irp);
 UINT64 get_extent_data_ref_hash2(UINT64 root, UINT64 objid, UINT64 offset);
 
 // in worker-thread.c
@@ -1067,7 +1105,8 @@
 NTSTATUS registry_load_volume_options(device_extension* Vcb);
 
 // in compress.c
-NTSTATUS decompress(UINT8 type, UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen);
+NTSTATUS zlib_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen);
+NTSTATUS lzo_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen, UINT32 inpageoff);
 NTSTATUS write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, PIRP Irp, LIST_ENTRY* rollback);
 
 // in galois.c
@@ -1098,6 +1137,42 @@
 NTSTATUS look_for_balance_item(device_extension* Vcb);
 NTSTATUS remove_device(device_extension* Vcb, void* data, ULONG length, KPROCESSOR_MODE processor_mode);
 
+// in volume.c
+NTSTATUS STDCALL vol_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_read(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_query_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_set_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+NTSTATUS STDCALL vol_power(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+void add_volume_device(superblock* sb, PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath, UINT64 length, ULONG disk_num, ULONG part_num, PUNICODE_STRING partname);
+NTSTATUS mountmgr_add_drive_letter(PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath);
+#ifndef __REACTOS__
+NTSTATUS pnp_removal(PVOID NotificationStructure, PVOID Context);
+#else
+NTSTATUS NTAPI pnp_removal(PVOID NotificationStructure, PVOID Context);
+#endif
+
+// in scrub.c
+NTSTATUS start_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode);
+NTSTATUS query_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode, void* data, ULONG length);
+NTSTATUS pause_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode);
+NTSTATUS resume_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode);
+NTSTATUS stop_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode);
+
 #define fast_io_possible(fcb) (!FsRtlAreThereCurrentFileLocks(&fcb->lock) && !fcb->Vcb->readonly ? FastIoIsPossible : FastIoIsQuestionable)
 
 static __inline void print_open_trees(device_extension* Vcb) {
@@ -1176,15 +1251,11 @@
 #endif
 
 #ifdef _MSC_VER
-// #define int3 __asm { int 3 }
 #define int3 __debugbreak()
 #else
 #define int3 asm("int3;")
 #endif
 
-// if (Vcb->open_trees > 0) { ERR("open tree count = %i\n", Vcb->open_trees); print_open_trees(Vcb); int3; }
-// else TRACE("open tree count = %i\n", Vcb->open_trees);
-
 // FIXME - find a way to catch unfreed trees again
 
 // from sys/stat.h
@@ -1270,6 +1341,8 @@
 
 typedef VOID (*tCcSetAdditionalCacheAttributesEx)(PFILE_OBJECT FileObject, ULONG Flags);
 
+typedef VOID (*tFsRtlUpdateDiskCounters)(ULONG64 BytesRead, ULONG64 BytesWritten);
+
 #ifndef __REACTOS__
 #undef RtlIsNtDdiVersionAvailable
 
@@ -1276,6 +1349,53 @@
 BOOLEAN RtlIsNtDdiVersionAvailable(ULONG Version);
 
 PEPROCESS PsGetThreadProcess(PETHREAD Thread); // not in mingw
+
+// not in DDK headers - taken from winternl.h
+typedef struct _LDR_DATA_TABLE_ENTRY {
+    PVOID Reserved1[2];
+    LIST_ENTRY InMemoryOrderLinks;
+    PVOID Reserved2[2];
+    PVOID DllBase;
+    PVOID Reserved3[2];
+    UNICODE_STRING FullDllName;
+    BYTE Reserved4[8];
+    PVOID Reserved5[3];
+    union {
+        ULONG CheckSum;
+        PVOID Reserved6;
+    };
+    ULONG TimeDateStamp;
+} LDR_DATA_TABLE_ENTRY,*PLDR_DATA_TABLE_ENTRY;
+
+typedef struct _PEB_LDR_DATA {
+    BYTE Reserved1[8];
+    PVOID Reserved2[3];
+    LIST_ENTRY InMemoryOrderModuleList;
+} PEB_LDR_DATA,*PPEB_LDR_DATA;
+
+typedef struct _RTL_USER_PROCESS_PARAMETERS {
+    BYTE Reserved1[16];
+    PVOID Reserved2[10];
+    UNICODE_STRING ImagePathName;
+    UNICODE_STRING CommandLine;
+} RTL_USER_PROCESS_PARAMETERS,*PRTL_USER_PROCESS_PARAMETERS;
+
+typedef VOID (NTAPI *PPS_POST_PROCESS_INIT_ROUTINE)(VOID);
+
+typedef struct _PEB {
+    BYTE Reserved1[2];
+    BYTE BeingDebugged;
+    BYTE Reserved2[1];
+    PVOID Reserved3[2];
+    PPEB_LDR_DATA Ldr;
+    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
+    BYTE Reserved4[104];
+    PVOID Reserved5[52];
+    PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
+    BYTE Reserved6[128];
+    PVOID Reserved7[1];
+    ULONG SessionId;
+} PEB,*PPEB;
 #endif
 
 #if defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7)
Index: drivers/filesystems/btrfs/btrfsioctl.h
===================================================================
--- drivers/filesystems/btrfs/btrfsioctl.h	(révision 74430)
+++ drivers/filesystems/btrfs/btrfsioctl.h	(copie de travail)
@@ -19,6 +19,13 @@
 #define FSCTL_BTRFS_REMOVE_DEVICE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x836, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
 #define IOCTL_BTRFS_QUERY_FILESYSTEMS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x837, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
 #define FSCTL_BTRFS_GET_UUID CTL_CODE(FILE_DEVICE_UNKNOWN, 0x838, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+#define FSCTL_BTRFS_START_SCRUB CTL_CODE(FILE_DEVICE_UNKNOWN, 0x839, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+#define FSCTL_BTRFS_QUERY_SCRUB CTL_CODE(FILE_DEVICE_UNKNOWN, 0x83a, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+#define FSCTL_BTRFS_PAUSE_SCRUB CTL_CODE(FILE_DEVICE_UNKNOWN, 0x83b, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+#define FSCTL_BTRFS_RESUME_SCRUB CTL_CODE(FILE_DEVICE_UNKNOWN, 0x83c, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+#define FSCTL_BTRFS_STOP_SCRUB CTL_CODE(FILE_DEVICE_UNKNOWN, 0x83d, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+#define IOCTL_BTRFS_PROBE_VOLUME CTL_CODE(FILE_DEVICE_UNKNOWN, 0x83e, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+#define FSCTL_BTRFS_RESET_STATS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x83f, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
 
 typedef struct {
     UINT64 subvol;
@@ -32,6 +39,10 @@
     WCHAR name[1];
 } btrfs_create_snapshot;
 
+#define BTRFS_COMPRESSION_ANY   0
+#define BTRFS_COMPRESSION_ZLIB  1
+#define BTRFS_COMPRESSION_LZO   2
+
 typedef struct {
     UINT64 subvol;
     UINT64 inode;
@@ -44,6 +55,7 @@
     UINT64 flags;
     UINT32 inline_length;
     UINT64 disk_size[3];
+    UINT8 compression_type;
 } btrfs_inode_info;
 
 typedef struct {
@@ -55,6 +67,8 @@
     BOOL gid_changed;
     UINT32 st_mode;
     BOOL mode_changed;
+    UINT8 compression_type;
+    BOOL compression_type_changed;
 } btrfs_set_inode_info;
 
 typedef struct {
@@ -64,6 +78,7 @@
     BOOL readonly;
     ULONG device_number;
     ULONG partition_number;
+    UINT64 stats[5];
     USHORT namelen;
     WCHAR name[1];
 } btrfs_device;
@@ -145,4 +160,45 @@
     btrfs_filesystem_device device;
 } btrfs_filesystem;
 
+#define BTRFS_SCRUB_STOPPED     0
+#define BTRFS_SCRUB_RUNNING     1
+#define BTRFS_SCRUB_PAUSED      2
+
+typedef struct {
+    UINT32 next_entry;
+    UINT64 address;
+    UINT64 device;
+    BOOL recovered;
+    BOOL is_metadata;
+    BOOL parity;
+    
+    union {
+        struct {
+            UINT64 subvol;
+            UINT64 offset;
+            UINT16 filename_length;
+            WCHAR filename[1];
+        } data;
+        
+        struct {
+            UINT64 root;
+            UINT8 level;
+            KEY firstitem;
+        } metadata;
+    };
+} btrfs_scrub_error;
+
+typedef struct {
+    UINT32 status;
+    LARGE_INTEGER start_time;
+    LARGE_INTEGER finish_time;
+    UINT64 chunks_left;
+    UINT64 total_chunks;
+    UINT64 data_scrubbed;
+    UINT64 duration;
+    NTSTATUS error;
+    UINT32 num_errors;
+    btrfs_scrub_error errors;
+} btrfs_query_scrub;
+
 #endif
Index: drivers/filesystems/btrfs/cache.c
===================================================================
--- drivers/filesystems/btrfs/cache.c	(révision 74430)
+++ drivers/filesystems/btrfs/cache.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -39,6 +39,8 @@
     
     fcb->lazy_writer_thread = KeGetCurrentThread();
     
+    IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP);
+    
     return TRUE;
 }
 
@@ -56,16 +58,35 @@
     ExReleaseResourceLite(fcb->Header.Resource);
     
     ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+    
+    if (IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP)
+        IoSetTopLevelIrp(NULL);
 }
 
 static BOOLEAN STDCALL acquire_for_read_ahead(PVOID Context, BOOLEAN Wait) {
+    PFILE_OBJECT FileObject = Context;
+    fcb* fcb = FileObject->FsContext;
+    
     TRACE("(%p, %u)\n", Context, Wait);
     
+    if (!ExAcquireResourceSharedLite(fcb->Header.Resource, Wait))
+        return FALSE;
+    
+    IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP);
+    
     return TRUE;
 }
 
 static void STDCALL release_from_read_ahead(PVOID Context) {
+    PFILE_OBJECT FileObject = Context;
+    fcb* fcb = FileObject->FsContext;
+    
     TRACE("(%p)\n", Context);
+    
+    ExReleaseResourceLite(fcb->Header.Resource);
+    
+    if (IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP)
+        IoSetTopLevelIrp(NULL);
 }
 
 NTSTATUS STDCALL init_cache() {
Index: drivers/filesystems/btrfs/calcthread.c
===================================================================
--- drivers/filesystems/btrfs/calcthread.c	(révision 74430)
+++ drivers/filesystems/btrfs/calcthread.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
Index: drivers/filesystems/btrfs/compress.c
===================================================================
--- drivers/filesystems/btrfs/compress.c	(révision 74430)
+++ drivers/filesystems/btrfs/compress.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * Copyright (c) Reimar Doeffinger 2006
  * Copyright (c) Markus Oberhumer 1996
  * 
@@ -160,9 +160,12 @@
     if (stream->error) return STATUS_INTERNAL_ERROR;
     
     if (byte > 17) {
-        lzo_copy(stream, byte - 17);
+        lzo_copy(stream, min(byte - 17, stream->outlen - stream->outpos));
         if (stream->error) return STATUS_INTERNAL_ERROR;
         
+        if (stream->outlen == stream->outpos)
+            return STATUS_SUCCESS;
+        
         byte = lzo_nextbyte(stream);
         if (stream->error) return STATUS_INTERNAL_ERROR;
         
@@ -211,9 +214,12 @@
             len = lzo_len(stream, byte, 15);
             if (stream->error) return STATUS_INTERNAL_ERROR;
             
-            lzo_copy(stream, len + 3);
+            lzo_copy(stream, min(len + 3, stream->outlen - stream->outpos));
             if (stream->error) return STATUS_INTERNAL_ERROR;
             
+            if (stream->outlen == stream->outpos)
+                return STATUS_SUCCESS;
+            
             byte = lzo_nextbyte(stream);
             if (stream->error) return STATUS_INTERNAL_ERROR;
             
@@ -227,14 +233,20 @@
             break;
         }
         
-        lzo_copyback(stream, back, len + 2);
+        lzo_copyback(stream, back, min(len + 2, stream->outlen - stream->outpos));
         if (stream->error) return STATUS_INTERNAL_ERROR;
         
+        if (stream->outlen == stream->outpos)
+            return STATUS_SUCCESS;
+        
         len = byte & 3;
         
         if (len) {
-            lzo_copy(stream, len);
+            lzo_copy(stream, min(len, stream->outlen - stream->outpos));
             if (stream->error) return STATUS_INTERNAL_ERROR;
+            
+            if (stream->outlen == stream->outpos)
+                return STATUS_SUCCESS;
         } else
             backcopy = !backcopy;
         
@@ -245,18 +257,12 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS lzo_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) {
+NTSTATUS lzo_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen, UINT32 inpageoff) {
     NTSTATUS Status;
-    UINT32 extlen, partlen, inoff, outoff;
+    UINT32 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);
+    inoff = 0;
     outoff = 0;
     
     do {
@@ -273,7 +279,7 @@
         stream.inlen = partlen;
         stream.inpos = 0;
         stream.out = &outbuf[outoff];
-        stream.outlen = LINUX_PAGE_SIZE;
+        stream.outlen = min(outlen, LINUX_PAGE_SIZE);
         stream.outpos = 0;
         
         Status = do_lzo_decompress(&stream);
@@ -288,22 +294,28 @@
         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);
+        if (LINUX_PAGE_SIZE - ((inpageoff + inoff) % LINUX_PAGE_SIZE) < sizeof(UINT32))
+            inoff = ((((inpageoff + inoff) / LINUX_PAGE_SIZE) + 1) * LINUX_PAGE_SIZE) - inpageoff;
+        
+        outlen -= stream.outlen;
+    } while (inoff < inlen && outlen > 0);
     
     return STATUS_SUCCESS;
 }
 
 static void* zlib_alloc(void* opaque, unsigned int items, unsigned int size) {
+    UNUSED(opaque);
+
     return ExAllocatePoolWithTag(PagedPool, items * size, ALLOC_TAG_ZLIB);
 }
 
 static void zlib_free(void* opaque, void* ptr) {
+    UNUSED(opaque);
+
     ExFreePool(ptr);
 }
 
-static NTSTATUS zlib_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) {
+NTSTATUS zlib_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) {
     z_stream c_stream;
     int ret;
 
@@ -332,6 +344,9 @@
             inflateEnd(&c_stream);
             return STATUS_INTERNAL_ERROR;
         }
+        
+        if (c_stream.avail_out == 0)
+            break;
     } while (ret != Z_STREAM_END);
 
     ret = inflateEnd(&c_stream);
@@ -346,17 +361,6 @@
     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, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     UINT8 compression;
@@ -448,7 +452,7 @@
             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, Irp, rollback, compression, end_data - start_data)) {
+                if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, Irp, rollback, compression, end_data - start_data, FALSE, 0)) {
                     ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
                     
                     if (compression != BTRFS_COMPRESSION_NONE)
@@ -474,7 +478,7 @@
         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, Irp, rollback, compression, end_data - start_data)) {
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, Irp, rollback, compression, end_data - start_data, FALSE, 0)) {
                 if (compression != BTRFS_COMPRESSION_NONE)
                     ExFreePool(comp_data);
                 
@@ -833,7 +837,7 @@
             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, Irp, rollback, compression, end_data - start_data)) {
+                if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, Irp, rollback, compression, end_data - start_data, FALSE, 0)) {
                     ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
                     
                     if (compression != BTRFS_COMPRESSION_NONE)
@@ -859,7 +863,7 @@
         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, Irp, rollback, compression, end_data - start_data)) {
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, Irp, rollback, compression, end_data - start_data, FALSE, 0)) {
                 if (compression != BTRFS_COMPRESSION_NONE)
                     ExFreePool(comp_data);
                 
@@ -879,11 +883,14 @@
 NTSTATUS write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, PIRP Irp, LIST_ENTRY* rollback) {
     UINT8 type;
 
-    if (fcb->Vcb->options.compress_type != 0)
+    if (fcb->Vcb->options.compress_type != 0 && fcb->prop_compression == PropCompression_None)
         type = fcb->Vcb->options.compress_type;
     else {
-        if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO)
+        if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO) && fcb->prop_compression == PropCompression_LZO) {
+            fcb->Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO;
             type = BTRFS_COMPRESSION_LZO;
+        } else if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO && fcb->prop_compression != PropCompression_Zlib)
+            type = BTRFS_COMPRESSION_LZO;
         else
             type = BTRFS_COMPRESSION_ZLIB;
     }
Index: drivers/filesystems/btrfs/crc32c.c
===================================================================
--- drivers/filesystems/btrfs/crc32c.c	(révision 74430)
+++ drivers/filesystems/btrfs/crc32c.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
Index: drivers/filesystems/btrfs/create.c
===================================================================
--- drivers/filesystems/btrfs/create.c	(révision 74430)
+++ drivers/filesystems/btrfs/create.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -20,7 +20,7 @@
 #endif /* __REACTOS__ */
 #include "btrfs_drv.h"
 #ifndef __REACTOS__
-#include <winioctl.h>
+#include <ntddstor.h>
 #endif
 
 extern PDEVICE_OBJECT devobj;
@@ -27,13 +27,21 @@
 
 static WCHAR datastring[] = L"::$DATA";
 
-fcb* create_fcb(POOL_TYPE pool_type) {
+fcb* create_fcb(device_extension* Vcb, POOL_TYPE pool_type) {
     fcb* fcb;
     
-    fcb = ExAllocatePoolWithTag(PagedPool, sizeof(struct _fcb), ALLOC_TAG);
-    if (!fcb) {
-        ERR("out of memory\n");
-        return NULL;
+    if (pool_type == NonPagedPool) {
+        fcb = ExAllocatePoolWithTag(pool_type, sizeof(struct _fcb), ALLOC_TAG);
+        if (!fcb) {
+            ERR("out of memory\n");
+            return NULL;
+        }
+    } else {
+        fcb = ExAllocateFromPagedLookasideList(&Vcb->fcb_lookaside);
+        if (!fcb) {
+            ERR("out of memory\n");
+            return NULL;
+        }
     }
     
 #ifdef DEBUG_FCB_REFCOUNTS
@@ -40,14 +48,20 @@
     WARN("allocating fcb %p\n", fcb);
 #endif
     RtlZeroMemory(fcb, sizeof(struct _fcb));
+    fcb->pool_type = pool_type;
     
     fcb->Header.NodeTypeCode = BTRFS_NODE_TYPE_FCB;
     fcb->Header.NodeByteSize = sizeof(struct _fcb);
     
-    fcb->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _fcb_nonpaged), ALLOC_TAG);
+    fcb->nonpaged = ExAllocateFromNPagedLookasideList(&Vcb->fcb_np_lookaside);
     if (!fcb->nonpaged) {
         ERR("out of memory\n");
-        ExFreePool(fcb);
+        
+        if (pool_type == NonPagedPool)
+            ExFreePool(fcb);
+        else
+            ExFreeToPagedLookasideList(&Vcb->fcb_lookaside, fcb);
+        
         return NULL;
     }
     RtlZeroMemory(fcb->nonpaged, sizeof(struct _fcb_nonpaged));
@@ -80,10 +94,10 @@
     return fcb;
 }
 
-file_ref* create_fileref() {
+file_ref* create_fileref(device_extension* Vcb) {
     file_ref* fr;
     
-    fr = ExAllocatePoolWithTag(PagedPool, sizeof(file_ref), ALLOC_TAG);
+    fr = ExAllocateFromPagedLookasideList(&Vcb->fileref_lookaside);
     if (!fr) {
         ERR("out of memory\n");
         return NULL;
@@ -91,10 +105,10 @@
     
     RtlZeroMemory(fr, sizeof(file_ref));
     
-    fr->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(file_ref_nonpaged), ALLOC_TAG);
+    fr->nonpaged = ExAllocateFromNPagedLookasideList(&Vcb->fileref_np_lookaside);
     if (!fr->nonpaged) {
         ERR("out of memory\n");
-        ExFreePool(fr);
+        ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, fr);
         return NULL;
     }
     
@@ -106,18 +120,19 @@
     
     InitializeListHead(&fr->children);
     
+    ExInitializeResourceLite(&fr->nonpaged->fileref_lock);
     ExInitializeResourceLite(&fr->nonpaged->children_lock);
     
     return fr;
 }
 
-static NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, fcb* fcb,
-                                         root** subvol, UINT64* inode, dir_child** pdc, BOOL case_sensitive, PIRP Irp) {
+static NTSTATUS STDCALL find_file_in_dir(PUNICODE_STRING filename, fcb* fcb, root** subvol, UINT64* inode, dir_child** pdc, BOOL case_sensitive) {
     NTSTATUS Status;
     UNICODE_STRING fnus;
     UINT32 hash;
     LIST_ENTRY* le;
     UINT8 c;
+    BOOL locked = FALSE;
     
     if (!case_sensitive) {
         Status = RtlUpcaseUnicodeString(&fnus, filename, TRUE);
@@ -133,7 +148,10 @@
     
     c = hash >> 24;
     
-    ExAcquireResourceSharedLite(&fcb->nonpaged->dir_children_lock, TRUE);
+    if (!ExIsResourceAcquiredSharedLite(&fcb->nonpaged->dir_children_lock)) {
+        ExAcquireResourceSharedLite(&fcb->nonpaged->dir_children_lock, TRUE);
+        locked = TRUE;
+    }
     
     if (case_sensitive) {
         if (!fcb->hash_ptrs[c]) {
@@ -234,7 +252,8 @@
     Status = STATUS_OBJECT_NAME_NOT_FOUND;
 
 end:
-    ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
+    if (locked)
+        ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
     
     if (!case_sensitive)
         ExFreePool(fnus.Buffer);
@@ -242,218 +261,12 @@
     return Status;
 }
 
-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;
-    UINT32 crc32;
-    KEY searchkey;
-    traverse_ptr tp, next_tp;
-    BOOL success = FALSE, b;
-    
-    static char xapref[] = "user.";
-    ULONG xapreflen = strlen(xapref);
-    
-    TRACE("(%p, %p, %.*S)\n", Vcb, fcb, stream->Length / sizeof(WCHAR), stream->Buffer);
-    
-    Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream->Buffer, stream->Length);
-    if (!NT_SUCCESS(Status)) {
-        ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
-        return FALSE;
-    }
-    
-    TRACE("utf8len = %u\n", utf8len);
-    
-    utf8 = ExAllocatePoolWithTag(PagedPool, xapreflen + utf8len + 1, ALLOC_TAG);
-    if (!utf8) {
-        ERR("out of memory\n");
-        goto end;
-    }
-    
-    RtlCopyMemory(utf8, xapref, xapreflen);
-    
-    Status = RtlUnicodeToUTF8N(&utf8[xapreflen], utf8len, &utf8len, stream->Buffer, stream->Length);
-    if (!NT_SUCCESS(Status)) {
-        ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
-        goto end;
-    }
-    
-    utf8len += xapreflen;
-    utf8[utf8len] = 0;
-    
-    TRACE("utf8 = %s\n", utf8);
-    
-    crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, utf8len);
-    TRACE("crc32 = %08x\n", crc32);
-    
-    if ((crc32 == EA_DOSATTRIB_HASH && utf8len == strlen(EA_DOSATTRIB) && RtlCompareMemory(utf8, EA_DOSATTRIB, utf8len) == utf8len) || 
-        (crc32 == EA_EA_HASH && utf8len == strlen(EA_EA) && RtlCompareMemory(utf8, EA_EA, utf8len) == utf8len)) {
-        return FALSE;
-    }
-    
-    searchkey.obj_id = fcb->inode;
-    searchkey.obj_type = TYPE_XATTR_ITEM;
-    searchkey.offset = crc32;
-    
-    Status = find_item(Vcb, 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)) {
-        if (tp.item->size < sizeof(DIR_ITEM)) {
-            ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
-        } else {
-            ULONG len = tp.item->size, xasize;
-            DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
-            
-            TRACE("found match on hash\n");
-            
-            while (len > 0) {
-                if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
-                    ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                    break;
-                }
-                
-                if (RtlCompareMemory(di->name, utf8, utf8len) == utf8len) {
-                    TRACE("found exact match for %s\n", utf8);
-                    
-                    *hash = tp.item->key.offset;
-                    
-                    xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
-                    if (!xattr->Buffer) {
-                        ERR("out of memory\n");
-                        goto end;
-                    }
-                    
-                    xattr->Length = xattr->MaximumLength = di->n;
-                    RtlCopyMemory(xattr->Buffer, di->name, di->n);
-                    xattr->Buffer[di->n] = 0;
-                    
-                    success = TRUE;
-                    goto end;
-                }
-                
-                xasize = sizeof(DIR_ITEM) - 1 + di->m + di->n;
-                
-                if (len > xasize) {
-                    len -= xasize;
-                    di = (DIR_ITEM*)&di->name[di->m + di->n];
-                } else
-                    break;
-            }
-        }
-    }
-    
-    searchkey.offset = 0;
-    
-    Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        goto end;
-    }
-    
-    do {
-        if (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM && tp.item->key.offset != crc32) {
-            if (tp.item->size < sizeof(DIR_ITEM)) {
-                ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
-            } else {
-                ULONG len = tp.item->size, xasize;
-                DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
-                ULONG utf16len;
-                
-                TRACE("found xattr with hash %08x\n", (UINT32)tp.item->key.offset);
-                
-                while (len > 0) {
-                    if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
-                        ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                        break;
-                    }
-                    
-                    if (di->n > xapreflen && RtlCompareMemory(di->name, xapref, xapreflen) == xapreflen) {
-                        TRACE("found potential xattr %.*s\n", di->n, di->name);
-                    }
-                    
-                    Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, &di->name[xapreflen], di->n - xapreflen);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-                    } else {
-                        WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, utf16len, ALLOC_TAG);
-                        if (!utf16) {
-                            ERR("out of memory\n");
-                            goto end;
-                        }
-                        
-                        Status = RtlUTF8ToUnicodeN(utf16, utf16len, &utf16len, &di->name[xapreflen], di->n - xapreflen);
-
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                        } else {
-                            UNICODE_STRING us;
-                            
-                            us.Buffer = utf16;
-                            us.Length = us.MaximumLength = (USHORT)utf16len;
-                            
-                            if (FsRtlAreNamesEqual(stream, &us, TRUE, NULL)) {
-                                TRACE("found case-insensitive match for %s\n", utf8);
-                                
-                                *newstreamname = us;
-                                *hash = tp.item->key.offset;
-                                
-                                xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
-                                if (!xattr->Buffer) {
-                                    ERR("out of memory\n");
-                                    ExFreePool(utf16);
-                                    goto end;
-                                }
-                                
-                                xattr->Length = xattr->MaximumLength = di->n;
-                                RtlCopyMemory(xattr->Buffer, di->name, di->n);
-                                xattr->Buffer[di->n] = 0;
-                                
-                                success = TRUE;
-                                goto end;
-                            }
-                        }
-                        
-                        ExFreePool(utf16);
-                    }
-                    
-                    xasize = sizeof(DIR_ITEM) - 1 + di->m + di->n;
-                    
-                    if (len > xasize) {
-                        len -= xasize;
-                        di = (DIR_ITEM*)&di->name[di->m + di->n];
-                    } else
-                        break;
-                }
-            }
-        }
-        
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
-        if (b) {
-            tp = next_tp;
-            
-            if (next_tp.item->key.obj_id > fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
-                break;
-        }
-    } while (b);
-    
-end:
-    ExFreePool(utf8);
-    
-    return success;
-}
-
-static NTSTATUS split_path(PUNICODE_STRING path, UNICODE_STRING** parts, ULONG* num_parts, BOOL* stream) {
-    ULONG len, i, j, np;
+static NTSTATUS split_path(device_extension* Vcb, PUNICODE_STRING path, LIST_ENTRY* parts, BOOL* stream) {
+    ULONG len, i;
     BOOL has_stream;
-    UNICODE_STRING* ps;
     WCHAR* buf;
+    name_bit* nb;
     
-    np = 1;
-    
     len = path->Length / sizeof(WCHAR);
     if (len > 0 && (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\'))
         len--;
@@ -461,7 +274,6 @@
     has_stream = FALSE;
     for (i = 0; i < len; i++) {
         if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
-            np++;
             has_stream = FALSE;
         } else if (path->Buffer[i] == ':') {
             has_stream = TRUE;
@@ -468,34 +280,33 @@
         }
     }
     
-    if (has_stream)
-        np++;
-    
-    ps = ExAllocatePoolWithTag(PagedPool, np * sizeof(UNICODE_STRING), ALLOC_TAG);
-    if (!ps) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    RtlZeroMemory(ps, np * sizeof(UNICODE_STRING));
-    
     buf = path->Buffer;
     
-    j = 0;
     for (i = 0; i < len; i++) {
         if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
-            ps[j].Buffer = buf;
-            ps[j].Length = (&path->Buffer[i] - buf) * sizeof(WCHAR);
-            ps[j].MaximumLength = ps[j].Length;
+            nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
+            if (!nb) {
+                ERR("out of memory\n");
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
             
+            nb->us.Buffer = buf;
+            nb->us.Length = nb->us.MaximumLength = (&path->Buffer[i] - buf) * sizeof(WCHAR);
+            InsertTailList(parts, &nb->list_entry);
+            
             buf = &path->Buffer[i+1];
-            j++;
         }
     }
+
+    nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
+    if (!nb) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
     
-    ps[j].Buffer = buf;
-    ps[j].Length = (&path->Buffer[i] - buf) * sizeof(WCHAR);
-    ps[j].MaximumLength = ps[j].Length;
+    nb->us.Buffer = buf;
+    nb->us.Length = nb->us.MaximumLength = (&path->Buffer[i] - buf) * sizeof(WCHAR);
+    InsertTailList(parts, &nb->list_entry);
     
     if (has_stream) {
         static WCHAR datasuf[] = {':','$','D','A','T','A',0};
@@ -504,16 +315,25 @@
         dsus.Buffer = datasuf;
         dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
         
-        for (i = 0; i < ps[j].Length / sizeof(WCHAR); i++) {
-            if (ps[j].Buffer[i] == ':') {
-                ps[j+1].Buffer = &ps[j].Buffer[i+1];
-                ps[j+1].Length = ps[j].Length - (i * sizeof(WCHAR)) - sizeof(WCHAR);
+        for (i = 0; i < nb->us.Length / sizeof(WCHAR); i++) {
+            if (nb->us.Buffer[i] == ':') {
+                name_bit* nb2;
                 
-                ps[j].Length = i * sizeof(WCHAR);
-                ps[j].MaximumLength = ps[j].Length;
+                nb2 = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
+                if (!nb2) {
+                    ERR("out of memory\n");
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
                 
-                j++;
+                nb2->us.Buffer = &nb->us.Buffer[i+1];
+                nb2->us.Length = nb2->us.MaximumLength = nb->us.Length - (i * sizeof(WCHAR)) - sizeof(WCHAR);
+                InsertTailList(parts, &nb2->list_entry);
                 
+                nb->us.Length = i * sizeof(WCHAR);
+                nb->us.MaximumLength = nb->us.Length;
+                
+                nb = nb2;
+                
                 break;
             }
         }
@@ -520,11 +340,13 @@
         
         // FIXME - should comparison be case-insensitive?
         // remove :$DATA suffix
-        if (ps[j].Length >= dsus.Length && RtlCompareMemory(&ps[j].Buffer[(ps[j].Length - dsus.Length)/sizeof(WCHAR)], dsus.Buffer, dsus.Length) == dsus.Length)
-            ps[j].Length -= dsus.Length;
+        if (nb->us.Length >= dsus.Length && RtlCompareMemory(&nb->us.Buffer[(nb->us.Length - dsus.Length)/sizeof(WCHAR)], dsus.Buffer, dsus.Length) == dsus.Length)
+            nb->us.Length -= dsus.Length;
         
-        if (ps[j].Length == 0) {
-            np--;
+        if (nb->us.Length == 0) {
+            RemoveTailList(parts);
+            ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
+            
             has_stream = FALSE;
         }
     }
@@ -531,16 +353,11 @@
     
     // if path is just stream name, remove first empty item
     if (has_stream && path->Length >= sizeof(WCHAR) && path->Buffer[0] == ':') {
-        ps[0] = ps[1];
-        np--;
+        name_bit *nb1 = CONTAINING_RECORD(RemoveHeadList(parts), name_bit, list_entry);
+        
+        ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb1);
     }
 
-//     for (i = 0; i < np; i++) {
-//         ERR("part %u: %u, (%.*S)\n", i, ps[i].Length, ps[i].Length / sizeof(WCHAR), ps[i].Buffer);
-//     }
-    
-    *num_parts = np;
-    *parts = ps;
     *stream = has_stream;
     
     return STATUS_SUCCESS;
@@ -558,80 +375,6 @@
 // }
 // #endif
 
-static file_ref* search_fileref_children(file_ref* dir, PUNICODE_STRING name, BOOL case_sensitive) {
-    LIST_ENTRY* le;
-    file_ref *c, *deleted = NULL;
-    NTSTATUS Status;
-    UNICODE_STRING ucus;
-#ifdef DEBUG_FCB_REFCOUNTS
-    ULONG rc;
-#endif
-    
-    if (case_sensitive) {
-        le = dir->children.Flink;
-        while (le != &dir->children) {
-            c = CONTAINING_RECORD(le, file_ref, list_entry);
-            
-            if (c->refcount > 0 && c->filepart.Length == name->Length &&
-                RtlCompareMemory(c->filepart.Buffer, name->Buffer, name->Length) == name->Length) {
-                if (c->deleted) {
-                    deleted = c;
-                } else {
-#ifdef DEBUG_FCB_REFCOUNTS
-                    rc = InterlockedIncrement(&c->refcount);
-                    WARN("fileref %p: refcount now %i (%S)\n", c, rc, file_desc_fileref(c));
-#else
-                    InterlockedIncrement(&c->refcount);
-#endif
-                    return c;
-                }
-            }
-            
-            le = le->Flink;
-        }
-        
-        goto end;
-    }
-
-    Status = RtlUpcaseUnicodeString(&ucus, name, TRUE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-        return NULL;
-    }
-        
-    le = dir->children.Flink;
-    while (le != &dir->children) {
-        c = CONTAINING_RECORD(le, file_ref, list_entry);
-        
-        if (c->refcount > 0 && c->filepart_uc.Length == ucus.Length &&
-            RtlCompareMemory(c->filepart_uc.Buffer, ucus.Buffer, ucus.Length) == ucus.Length) {
-            if (c->deleted) {
-                deleted = c;
-            } else {
-#ifdef DEBUG_FCB_REFCOUNTS
-                rc = InterlockedIncrement(&c->refcount);
-                WARN("fileref %p: refcount now %i (%S)\n", c, rc, file_desc_fileref(c));
-#else
-                InterlockedIncrement(&c->refcount);
-#endif
-                ExFreePool(ucus.Buffer);
-                
-                return c;
-            }
-        }
-        
-        le = le->Flink;
-    }
-    
-    ExFreePool(ucus.Buffer);
-    
-end:
-    if (deleted)
-        increase_fileref_refcount(deleted);
-    
-    return deleted;
-}
-
 NTSTATUS load_csum(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length, PIRP Irp) {
     NTSTATUS Status;
     KEY searchkey;
@@ -690,6 +433,7 @@
     KEY searchkey;
     traverse_ptr tp, next_tp;
     NTSTATUS Status;
+    ULONG num_children = 0;
     
     fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
     if (!fcb->hash_ptrs) {
@@ -803,6 +547,8 @@
         
         insert_dir_child_into_hash_lists(fcb, dc);
         
+        num_children++;
+        
 cont:
         if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp))
             tp = next_tp;
@@ -810,6 +556,11 @@
             break;
     }
     
+    // If a directory has a lot of files, force it to stick around until the next flush
+    // so we aren't constantly re-reading.
+    if (num_children >= 100)
+        mark_fcb_dirty(fcb);
+    
     return STATUS_SUCCESS;
 }
 
@@ -850,7 +601,7 @@
         }
     }
     
-    fcb = create_fcb(pooltype);
+    fcb = create_fcb(Vcb, pooltype);
     if (!fcb) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
@@ -883,17 +634,17 @@
         RtlCopyMemory(&fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
     
     if (fcb->type == 0) { // guess the type from the inode mode, if the caller doesn't know already
-        if (fcb->inode_item.st_mode & __S_IFDIR)
+        if ((fcb->inode_item.st_mode & __S_IFDIR) == __S_IFDIR)
             fcb->type = BTRFS_TYPE_DIRECTORY;
-        else if (fcb->inode_item.st_mode & __S_IFCHR)
+        else if ((fcb->inode_item.st_mode & __S_IFCHR) == __S_IFCHR)
             fcb->type = BTRFS_TYPE_CHARDEV;
-        else if (fcb->inode_item.st_mode & __S_IFBLK)
+        else if ((fcb->inode_item.st_mode & __S_IFBLK) == __S_IFBLK)
             fcb->type = BTRFS_TYPE_BLOCKDEV;
-        else if (fcb->inode_item.st_mode & __S_IFIFO)
+        else if ((fcb->inode_item.st_mode & __S_IFIFO) == __S_IFIFO)
             fcb->type = BTRFS_TYPE_FIFO;
-        else if (fcb->inode_item.st_mode & __S_IFLNK)
+        else if ((fcb->inode_item.st_mode & __S_IFLNK) == __S_IFLNK)
             fcb->type = BTRFS_TYPE_SYMLINK;
-        else if (fcb->inode_item.st_mode & __S_IFSOCK)
+        else if ((fcb->inode_item.st_mode & __S_IFSOCK) == __S_IFSOCK)
             fcb->type = BTRFS_TYPE_SOCKET;
         else
             fcb->type = BTRFS_TYPE_FILE;
@@ -1041,76 +792,187 @@
                 ier = (INODE_EXTREF*)&ier->name[ier->n];
             }
         } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
-            if (tp.item->size < sizeof(DIR_ITEM)) {
-                ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
+            ULONG len;
+            DIR_ITEM* di;
+            
+            static char xapref[] = "user.";
+            
+            if (tp.item->size < offsetof(DIR_ITEM, name[0])) {
+                ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(DIR_ITEM, name[0]));
                 continue;
             }
             
-            if (tp.item->key.offset == EA_REPARSE_HASH) {
-                UINT8* xattrdata;
-                UINT16 xattrlen;
+            len = tp.item->size;
+            di = (DIR_ITEM*)tp.item->data;
+            
+            do {
+                if (len < offsetof(DIR_ITEM, name[0]) + di->m + di->n)
+                    break;
                 
-                if (extract_xattr(tp.item->data, tp.item->size, EA_REPARSE, &xattrdata, &xattrlen)) {
-                    fcb->reparse_xattr.Buffer = (char*)xattrdata;
-                    fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = xattrlen;
-                }
-            } else if (tp.item->key.offset == EA_EA_HASH) {
-                UINT8* eadata;
-                UINT16 ealen;
-                
-                if (extract_xattr(tp.item->data, tp.item->size, EA_EA, &eadata, &ealen)) {
-                    ULONG offset;
+                if (tp.item->key.offset == EA_REPARSE_HASH && di->n == strlen(EA_REPARSE) && RtlCompareMemory(EA_REPARSE, di->name, di->n) == di->n) {
+                    if (di->m > 0) {
+                        fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
+                        if (!fcb->reparse_xattr.Buffer) {
+                            ERR("out of memory\n");
+                            free_fcb(fcb);
+                            return STATUS_INSUFFICIENT_RESOURCES;
+                        }
+                        
+                        RtlCopyMemory(fcb->reparse_xattr.Buffer, &di->name[di->n], di->m);
+                    } else
+                        fcb->reparse_xattr.Buffer = NULL;
                     
-                    Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)eadata, ealen, &offset);
-                    
-                    if (!NT_SUCCESS(Status)) {
-                        WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
-                        ExFreePool(eadata);
-                    } else {
-                        FILE_FULL_EA_INFORMATION* eainfo;
-                        fcb->ea_xattr.Buffer = (char*)eadata;
-                        fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = ealen;
+                    fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = di->m;
+                } else if (tp.item->key.offset == EA_EA_HASH && di->n == strlen(EA_EA) && RtlCompareMemory(EA_EA, di->name, di->n) == di->n) {
+                    if (di->m > 0) {
+                        ULONG offset;
                         
-                        fcb->ealen = 4;
+                        Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)&di->name[di->n], di->m, &offset);
                         
-                        // calculate ealen
-                        eainfo = (FILE_FULL_EA_INFORMATION*)eadata;
-                        do {
-                            fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
+                        if (!NT_SUCCESS(Status))
+                            WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
+                        else {
+                            FILE_FULL_EA_INFORMATION* eainfo;
                             
-                            if (eainfo->NextEntryOffset == 0)
-                                break;
+                            fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
+                            if (!fcb->ea_xattr.Buffer) {
+                                ERR("out of memory\n");
+                                free_fcb(fcb);
+                                return STATUS_INSUFFICIENT_RESOURCES;
+                            }
                             
-                            eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
-                        } while (TRUE);
+                            RtlCopyMemory(fcb->ea_xattr.Buffer, &di->name[di->n], di->m);
+                            
+                            fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = di->m;
+                             
+                            fcb->ealen = 4;
+                            
+                            // calculate ealen
+                            eainfo = (FILE_FULL_EA_INFORMATION*)&di->name[di->n];
+                            do {
+                                fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
+                                
+                                if (eainfo->NextEntryOffset == 0)
+                                    break;
+                                
+                                eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
+                            } while (TRUE);
+                        }
                     }
-                }
-            } else if (tp.item->key.offset == EA_DOSATTRIB_HASH) {
-                UINT8* xattrdata;
-                UINT16 xattrlen;
-                
-                if (extract_xattr(tp.item->data, tp.item->size, EA_DOSATTRIB, &xattrdata, &xattrlen)) {
-                    if (get_file_attributes_from_xattr((char*)xattrdata, xattrlen, &fcb->atts)) {
-                        atts_set = TRUE;
+                } else if (tp.item->key.offset == EA_DOSATTRIB_HASH && di->n == strlen(EA_DOSATTRIB) && RtlCompareMemory(EA_DOSATTRIB, di->name, di->n) == di->n) {
+                    if (di->m > 0) {
+                        if (get_file_attributes_from_xattr(&di->name[di->n], di->m, &fcb->atts)) {
+                            atts_set = TRUE;
+                            
+                            if (fcb->type == BTRFS_TYPE_DIRECTORY)
+                                fcb->atts |= FILE_ATTRIBUTE_DIRECTORY;
+                            else if (fcb->type == BTRFS_TYPE_SYMLINK)
+                                fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
+                        }
+                    }
+                } else if (tp.item->key.offset == EA_NTACL_HASH && di->n == strlen(EA_NTACL) && RtlCompareMemory(EA_NTACL, di->name, di->n) == di->n) {
+                    if (di->m > 0) {
+                        fcb->sd = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
+                        if (!fcb->sd) {
+                            ERR("out of memory\n");
+                            free_fcb(fcb);
+                            return STATUS_INSUFFICIENT_RESOURCES;
+                        }
                         
-                        if (fcb->type == BTRFS_TYPE_DIRECTORY)
-                            fcb->atts |= FILE_ATTRIBUTE_DIRECTORY;
-                        else if (fcb->type == BTRFS_TYPE_SYMLINK)
-                            fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
+                        RtlCopyMemory(fcb->sd, &di->name[di->n], di->m);
+                        
+                        if (get_sd_from_xattr(fcb, di->m))
+                            sd_set = TRUE;
+                        else {
+                            ExFreePool(fcb->sd);
+                            fcb->sd = NULL;
+                        }
                     }
+                } else if (tp.item->key.offset == EA_PROP_COMPRESSION_HASH && di->n == strlen(EA_PROP_COMPRESSION) && RtlCompareMemory(EA_PROP_COMPRESSION, di->name, di->n) == di->n) {
+                    if (di->m > 0) {
+                        const char lzo[] = "lzo";
+                        const char zlib[] = "zlib";
+                        
+                        if (di->m == strlen(lzo) && RtlCompareMemory(&di->name[di->n], lzo, di->m) == di->m)
+                            fcb->prop_compression = PropCompression_LZO;
+                        else if (di->m == strlen(zlib) && RtlCompareMemory(&di->name[di->n], zlib, di->m) == di->m)
+                            fcb->prop_compression = PropCompression_Zlib;
+                        else
+                            fcb->prop_compression = PropCompression_None;
+                    }
+                } else if (di->n > strlen(xapref) && RtlCompareMemory(xapref, di->name, strlen(xapref)) == strlen(xapref)) {
+                    dir_child* dc;
+                    ULONG utf16len;
                     
-                    ExFreePool(xattrdata);
+                    Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, &di->name[strlen(xapref)], di->n - strlen(xapref));
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+                        free_fcb(fcb);
+                        return Status;
+                    }
+                    
+                    dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
+                    if (!dc) {
+                        ERR("out of memory\n");
+                        free_fcb(fcb);
+                        return STATUS_INSUFFICIENT_RESOURCES;
+                    }
+                    
+                    RtlZeroMemory(dc, sizeof(dir_child));
+                    
+                    dc->utf8.MaximumLength = dc->utf8.Length = di->n - strlen(xapref);
+                    dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.MaximumLength, ALLOC_TAG);
+                    if (!dc->utf8.Buffer) {
+                        ERR("out of memory\n");
+                        ExFreePool(dc);
+                        free_fcb(fcb);
+                        return STATUS_INSUFFICIENT_RESOURCES;
+                    }
+                    
+                    RtlCopyMemory(dc->utf8.Buffer, &di->name[strlen(xapref)], dc->utf8.Length);
+                    
+                    dc->name.MaximumLength = dc->name.Length = utf16len;
+                    dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.MaximumLength, ALLOC_TAG);
+                    if (!dc->name.Buffer) {
+                        ERR("out of memory\n");
+                        ExFreePool(dc->utf8.Buffer);
+                        ExFreePool(dc);
+                        free_fcb(fcb);
+                        return STATUS_INSUFFICIENT_RESOURCES;
+                    }
+                    
+                    Status = RtlUTF8ToUnicodeN(dc->name.Buffer, utf16len, &utf16len, dc->utf8.Buffer, dc->utf8.Length);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+                        ExFreePool(dc->utf8.Buffer);
+                        ExFreePool(dc->name.Buffer);
+                        ExFreePool(dc);
+                        free_fcb(fcb);
+                        return Status;
+                    }
+                    
+                    Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, TRUE);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+                        ExFreePool(dc->utf8.Buffer);
+                        ExFreePool(dc->name.Buffer);
+                        ExFreePool(dc);
+                        free_fcb(fcb);
+                        return Status;
+                    }
+                    
+                    dc->size = di->m;
+                    
+                    InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
                 }
-            } else if (tp.item->key.offset == EA_NTACL_HASH) {
-                UINT16 buflen;
                 
-                if (extract_xattr(tp.item->data, tp.item->size, EA_NTACL, (UINT8**)&fcb->sd, &buflen)) {
-                    if (get_sd_from_xattr(fcb, buflen)) {
-                        sd_set = TRUE;
-                    } else
-                        ExFreePool(fcb->sd);
-                }
-            }
+                len -= offsetof(DIR_ITEM, name[0]) + di->m + di->n;
+                
+                if (len < offsetof(DIR_ITEM, name[0]))
+                    break;
+                
+                di = (DIR_ITEM*)&di->name[di->m + di->n];
+            } while (TRUE);
         } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) {
             extent* ext;
             BOOL unique = FALSE;
@@ -1118,7 +980,7 @@
             ed = (EXTENT_DATA*)tp.item->data;
             
             if (tp.item->size < sizeof(EXTENT_DATA)) {
-                ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+                ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
                     tp.item->size, sizeof(EXTENT_DATA));
                 
                 free_fcb(fcb);
@@ -1129,7 +991,7 @@
                 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
                 
                 if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
-                    ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+                    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) - 1 + sizeof(EXTENT_DATA2));
                 
                     free_fcb(fcb);
@@ -1136,7 +998,7 @@
                     return STATUS_INTERNAL_ERROR;
                 }
                 
-                if (ed2->address == 0 && ed2->size == 0) // sparse
+                if (ed2->address == 0 || ed2->size == 0) // sparse
                     continue;
                 
                 if (ed2->size != 0 && is_tree_unique(Vcb, tp.tree, Irp))
@@ -1143,7 +1005,7 @@
                     unique = is_extent_unique(Vcb, ed2->address, ed2->size, Irp);
             }
             
-            ext = ExAllocatePoolWithTag(pooltype, sizeof(extent), ALLOC_TAG);
+            ext = ExAllocatePoolWithTag(pooltype, offsetof(extent, extent_data) + tp.item->size, ALLOC_TAG);
             if (!ext) {
                 ERR("out of memory\n");
                 free_fcb(fcb);
@@ -1150,46 +1012,14 @@
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
             
-            ext->data = ExAllocatePoolWithTag(pooltype, tp.item->size, ALLOC_TAG);
-            if (!ext->data) {
-                ERR("out of memory\n");
-                ExFreePool(ext);
-                free_fcb(fcb);
-                return STATUS_INSUFFICIENT_RESOURCES;
-            }
-            
             ext->offset = tp.item->key.offset;
-            RtlCopyMemory(ext->data, tp.item->data, tp.item->size);
+            RtlCopyMemory(&ext->extent_data, tp.item->data, tp.item->size);
             ext->datalen = tp.item->size;
             ext->unique = unique;
             ext->ignore = FALSE;
             ext->inserted = FALSE;
+            ext->csum = NULL;
             
-            if (ed->type == EXTENT_TYPE_REGULAR && !(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
-                EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
-                UINT64 len;
-                
-                len = (ed->compression == BTRFS_COMPRESSION_NONE ? ed2->num_bytes : ed2->size) / Vcb->superblock.sector_size;
-                
-                ext->csum = ExAllocatePoolWithTag(NonPagedPool, len * sizeof(UINT32), ALLOC_TAG);
-                if (!ext->csum) {
-                    ERR("out of memory\n");
-                    ExFreePool(ext);
-                    free_fcb(fcb);
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-                
-                Status = load_csum(Vcb, ext->csum, ed2->address + (ed->compression == BTRFS_COMPRESSION_NONE ? ed2->offset : 0), len, Irp);
-                
-                if (!NT_SUCCESS(Status)) {
-                    ERR("load_csum returned %08x\n", Status);
-                    ExFreePool(ext);
-                    free_fcb(fcb);
-                    return Status;
-                }
-            } else
-                ext->csum = NULL;
-            
             InsertTailList(&fcb->extents, &ext->list_entry);
         }
     }
@@ -1218,7 +1048,7 @@
     }
     
     if (!atts_set)
-        fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', TRUE, Irp);
+        fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', TRUE, Irp);
     
     if (!sd_set)
         fcb_get_sd(fcb, parent, FALSE, Irp);
@@ -1245,8 +1075,7 @@
     return STATUS_SUCCESS;
 }
 
-NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr,
-                         UINT32 streamhash, fcb* parent, fcb** pfcb, PIRP Irp) {
+static NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, dir_child* dc, fcb* parent, fcb** pfcb, PIRP Irp) {
     fcb* fcb;
     UINT8* xattrdata;
     UINT16 xattrlen, overhead;
@@ -1254,7 +1083,22 @@
     KEY searchkey;
     traverse_ptr tp;
     LIST_ENTRY* lastle = NULL;
+    static char xapref[] = "user.";
+    ANSI_STRING xattr;
+    UINT32 crc32;
     
+    xattr.Length = strlen(xapref) + dc->utf8.Length;
+    xattr.MaximumLength = xattr.Length + 1;
+    xattr.Buffer = ExAllocatePoolWithTag(PagedPool, xattr.MaximumLength, ALLOC_TAG);
+    if (!xattr.Buffer) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlCopyMemory(xattr.Buffer, xapref, strlen(xapref));
+    RtlCopyMemory(&xattr.Buffer[strlen(xapref)], dc->utf8.Buffer, dc->utf8.Length);
+    xattr.Buffer[xattr.Length] = 0;
+    
     if (!IsListEmpty(&subvol->fcbs)) {
         LIST_ENTRY* le = subvol->fcbs.Flink;
                                 
@@ -1262,8 +1106,8 @@
             fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
             
             if (fcb->inode == inode) {
-                if (fcb->ads && fcb->adsxattr.Length == xattr->Length &&
-                    RtlCompareMemory(fcb->adsxattr.Buffer, xattr->Buffer, fcb->adsxattr.Length) == fcb->adsxattr.Length) {
+                if (fcb->ads && fcb->adsxattr.Length == xattr.Length &&
+                    RtlCompareMemory(fcb->adsxattr.Buffer, xattr.Buffer, fcb->adsxattr.Length) == fcb->adsxattr.Length) {
 #ifdef DEBUG_FCB_REFCOUNTS
                     LONG rc = InterlockedIncrement(&fcb->refcount);
 
@@ -1271,6 +1115,7 @@
 #else
                     InterlockedIncrement(&fcb->refcount);
 #endif
+                    ExFreePool(xattr.Buffer);
 
                     *pfcb = fcb;
                     return STATUS_SUCCESS;
@@ -1284,32 +1129,36 @@
         }
     }
     
-    fcb = create_fcb(PagedPool);
+    fcb = create_fcb(Vcb, PagedPool);
     if (!fcb) {
         ERR("out of memory\n");
+        ExFreePool(xattr.Buffer);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-      
-    if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr->Buffer, streamhash, &xattrdata, &xattrlen, Irp)) {
+    
+    fcb->Vcb = Vcb;
+    
+    crc32 = calc_crc32c(0xfffffffe, (UINT8*)xattr.Buffer, xattr.Length);
+
+    if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr.Buffer, crc32, &xattrdata, &xattrlen, Irp)) {
         ERR("get_xattr failed\n");
         free_fcb(fcb);
+        ExFreePool(xattr.Buffer);
         return STATUS_INTERNAL_ERROR;
     }
 
-    fcb->Vcb = Vcb;
-    
     fcb->subvol = parent->subvol;
     fcb->inode = parent->inode;
     fcb->type = parent->type;
     fcb->ads = TRUE;
-    fcb->adshash = streamhash;
-    fcb->adsxattr = *xattr;
+    fcb->adshash = crc32;
+    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;
+    searchkey.offset = crc32;
 
     Status = find_item(Vcb, parent->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
@@ -1356,131 +1205,94 @@
     return STATUS_SUCCESS;
 }
 
-void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock) {
-    if (do_lock)
-        ExAcquireResourceExclusiveLite(&parent->nonpaged->children_lock, TRUE);
-    
-    if (IsListEmpty(&parent->children))
-        InsertTailList(&parent->children, &child->list_entry);
-    else {
-        LIST_ENTRY* le = parent->children.Flink;
-        file_ref* fr1 = CONTAINING_RECORD(le, file_ref, list_entry);
-        
-        if (child->index < fr1->index)
-            InsertHeadList(&parent->children, &child->list_entry);
-        else {
-            while (le != &parent->children) {
-                file_ref* fr2 = (le->Flink == &parent->children) ? NULL : CONTAINING_RECORD(le->Flink, file_ref, list_entry);
-                
-                if (child->index >= fr1->index && (!fr2 || fr2->index > child->index)) {
-                    InsertHeadList(&fr1->list_entry, &child->list_entry);
-                    break;
-                }
-                
-                fr1 = fr2;
-                le = le->Flink;
-            }
-        }
-    }
-    
-    if (do_lock)
-        ExReleaseResourceLite(&parent->nonpaged->children_lock);
-}
-
-static NTSTATUS open_fileref_child(device_extension* Vcb, file_ref* sf, PUNICODE_STRING name, BOOL case_sensitive, BOOL lastpart, BOOL streampart,
-                                   POOL_TYPE pooltype, file_ref** psf2, PIRP Irp) {
+NTSTATUS open_fileref_child(device_extension* Vcb, file_ref* sf, PUNICODE_STRING name, BOOL case_sensitive, BOOL lastpart, BOOL streampart,
+                            POOL_TYPE pooltype, file_ref** psf2, PIRP Irp) {
     NTSTATUS Status;
     file_ref* sf2;
         
     if (streampart) {
-        UNICODE_STRING streamname;
-        ANSI_STRING xattr;
-        UINT32 streamhash;
+        BOOL locked = FALSE;
+        LIST_ENTRY* le;
+        UNICODE_STRING name_uc;
+        dir_child* dc = NULL;
+        fcb* fcb;
         
-        sf2 = search_fileref_children(sf, name, case_sensitive);
+        if (!case_sensitive) {
+            Status = RtlUpcaseUnicodeString(&name_uc, name, TRUE);
+            if (!NT_SUCCESS(Status)) {
+                ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+                return Status;
+            }
+        }
         
-        if (sf2) {
-            if (sf2->deleted) {
-                TRACE("element in path has been deleted\n");
-                free_fileref(sf2);
-                return lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
-            }
+        if (!ExIsResourceAcquiredSharedLite(&sf->fcb->nonpaged->dir_children_lock)) {
+            ExAcquireResourceSharedLite(&sf->fcb->nonpaged->dir_children_lock, TRUE);
+            locked = TRUE;
+        }
+        
+        le = sf->fcb->dir_children_index.Flink;
+        while (le != &sf->fcb->dir_children_index) {
+            dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_index);
             
-            *psf2 = sf2;
-    
-            return STATUS_SUCCESS;
+            if (dc2->index == 0) {
+                if ((case_sensitive && dc2->name.Length == name->Length && RtlCompareMemory(dc2->name.Buffer, name->Buffer, dc2->name.Length) == dc2->name.Length) ||
+                    (!case_sensitive && dc2->name_uc.Length == name_uc.Length && RtlCompareMemory(dc2->name_uc.Buffer, name_uc.Buffer, dc2->name_uc.Length) == dc2->name_uc.Length)
+                ) {
+                    dc = dc2;
+                    break;
+                }
+            } else
+                break;
+            
+            le = le->Flink;
         }
         
-        streamname.Buffer = NULL;
-        streamname.Length = streamname.MaximumLength = 0;
-        xattr.Buffer = NULL;
-        xattr.Length = xattr.MaximumLength = 0;
+        if (!case_sensitive)
+            ExFreePool(name_uc.Buffer);
         
-        // FIXME - check if already opened
+        if (locked)
+            ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
         
-        if (!find_stream(Vcb, sf->fcb, name, &streamname, &streamhash, &xattr, Irp)) {
-            TRACE("could not find stream %.*S\n", name->Length / sizeof(WCHAR), name->Buffer);
-            
+        if (!dc)
             return STATUS_OBJECT_NAME_NOT_FOUND;
-        } else {
-            fcb* fcb;
+        
+        if (dc->fileref) {
+            increase_fileref_refcount(dc->fileref);
+            *psf2 = dc->fileref;
+            return STATUS_SUCCESS;
+        }
 
-            if (streamhash == EA_DOSATTRIB_HASH && xattr.Length == strlen(EA_DOSATTRIB) &&
-                RtlCompareMemory(xattr.Buffer, EA_DOSATTRIB, xattr.Length) == xattr.Length) {
-                WARN("not allowing user.DOSATTRIB to be opened as stream\n");
-            
-                return STATUS_OBJECT_NAME_NOT_FOUND;
-            }
-            
-            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);
-                return Status;
-            }
-            
-            sf2 = create_fileref();
-            if (!sf2) {
-                ERR("out of memory\n");
-                free_fcb(fcb);
-                return STATUS_INSUFFICIENT_RESOURCES;
-            }
-            
-            sf2->fcb = fcb;
+        Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, dc, sf->fcb, &fcb, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("open_fcb_stream returned %08x\n", Status);
+            return Status;
+        }
+        
+        sf2 = create_fileref(Vcb);
+        if (!sf2) {
+            ERR("out of memory\n");
+            free_fcb(fcb);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        sf2->fcb = fcb;
 
-            if (streamname.Buffer) // case has changed
-                sf2->filepart = streamname;
-            else {
-                sf2->filepart.MaximumLength = sf2->filepart.Length = name->Length;
-                sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
-                if (!sf2->filepart.Buffer) {
-                    ERR("out of memory\n");
-                    free_fileref(sf2);
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-                
-                RtlCopyMemory(sf2->filepart.Buffer, name->Buffer, name->Length);
-            }
-            
-            Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE);
-            if (!NT_SUCCESS(Status)) {
-                ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-                free_fileref(sf2);
-                return Status;
-            }
-            
-            // FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM
+        sf2->parent = (struct _file_ref*)sf;
+        
+        sf2->dc = dc;
+        dc->fileref = sf2;
 
-            sf2->parent = (struct _file_ref*)sf;
-            insert_fileref_child(sf, sf2, TRUE);
-            
-            increase_fileref_refcount(sf);
-        }
+        ExAcquireResourceExclusiveLite(&sf->nonpaged->children_lock, TRUE);
+        InsertTailList(&sf->children, &sf2->list_entry);
+        ExReleaseResourceLite(&sf->nonpaged->children_lock);
+
+        increase_fileref_refcount(sf);
     } else {
         root* subvol;
         UINT64 inode;
         dir_child* dc;
         
-        Status = find_file_in_dir(Vcb, name, sf->fcb, &subvol, &inode, &dc, case_sensitive, Irp);
+        Status = find_file_in_dir(name, sf->fcb, &subvol, &inode, &dc, case_sensitive);
         if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
             TRACE("could not find %.*S\n", name->Length / sizeof(WCHAR), name->Buffer);
 
@@ -1490,6 +1302,9 @@
             return Status;
         } else {
             fcb* fcb;
+#ifdef DEBUG_STATS
+            LARGE_INTEGER time1, time2;
+#endif
             
             if (dc->fileref) {
                 if (!lastpart && dc->type != BTRFS_TYPE_DIRECTORY) {
@@ -1502,7 +1317,16 @@
                 return STATUS_SUCCESS;
             }
             
+#ifdef DEBUG_STATS
+            time1 = KeQueryPerformanceCounter(NULL);
+#endif
             Status = open_fcb(Vcb, subvol, inode, dc->type, &dc->utf8, sf->fcb, &fcb, pooltype, Irp);
+#ifdef DEBUG_STATS
+            time2 = KeQueryPerformanceCounter(NULL);
+            Vcb->stats.open_fcb_calls++;
+            Vcb->stats.open_fcb_time += time2.QuadPart - time1.QuadPart;
+#endif            
+            
             if (!NT_SUCCESS(Status)) {
                 ERR("open_fcb returned %08x\n", Status);
                 return Status;
@@ -1514,7 +1338,7 @@
                 return STATUS_OBJECT_PATH_NOT_FOUND;
             }
 
-            sf2 = create_fileref();
+            sf2 = create_fileref(Vcb);
             if (!sf2) {
                 ERR("out of memory\n");
                 free_fcb(fcb);
@@ -1526,43 +1350,14 @@
             if (dc->type == BTRFS_TYPE_DIRECTORY)
                 fcb->fileref = sf2;
             
-            sf2->index = dc->index;
             sf2->dc = dc;
             dc->fileref = sf2;
             
-            sf2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.Length, ALLOC_TAG);
-            if (!sf2->utf8.Buffer) {
-                ERR("out of memory\n");
-                free_fileref(sf2);
-                return STATUS_INSUFFICIENT_RESOURCES;
-            }
-            
-            sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.Length, ALLOC_TAG);
-            if (!sf2->filepart.Buffer) {
-                ERR("out of memory\n");
-                free_fileref(sf2);
-                return STATUS_INSUFFICIENT_RESOURCES;
-            }
-            
-            sf2->filepart_uc.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name_uc.Length, ALLOC_TAG);
-            if (!sf2->filepart_uc.Buffer) {
-                ERR("out of memory\n");
-                free_fileref(sf2);
-                return STATUS_INSUFFICIENT_RESOURCES;
-            }
-            
-            sf2->utf8.Length = sf2->utf8.MaximumLength = dc->utf8.Length;
-            RtlCopyMemory(sf2->utf8.Buffer, dc->utf8.Buffer, dc->utf8.Length);
-            
-            sf2->filepart.Length = sf2->filepart.MaximumLength = dc->name.Length;
-            RtlCopyMemory(sf2->filepart.Buffer, dc->name.Buffer, dc->name.Length);
-            
-            sf2->filepart_uc.Length = sf2->filepart_uc.MaximumLength = dc->name_uc.Length;
-            RtlCopyMemory(sf2->filepart_uc.Buffer, dc->name_uc.Buffer, dc->name_uc.Length);
-            
             sf2->parent = (struct _file_ref*)sf;
             
-            insert_fileref_child(sf, sf2, TRUE);
+            ExAcquireResourceExclusiveLite(&sf->nonpaged->children_lock, TRUE);
+            InsertTailList(&sf->children, &sf2->list_entry);
+            ExReleaseResourceLite(&sf->nonpaged->children_lock);
             
             increase_fileref_refcount(sf);
         }
@@ -1577,10 +1372,10 @@
                       POOL_TYPE pooltype, BOOL case_sensitive, PIRP Irp) {
     UNICODE_STRING fnus2;
     file_ref *dir, *sf, *sf2;
-    ULONG i, num_parts;
-    UNICODE_STRING* parts = NULL;
+    LIST_ENTRY parts;
     BOOL has_stream;
     NTSTATUS Status;
+    LIST_ENTRY* le;
     
     TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, parsed);
 
@@ -1617,15 +1412,8 @@
         }
         
         if (fnus2.Length == sizeof(WCHAR)) {
-            if (Vcb->root_fileref->open_count == 0) { // don't allow root to be opened on unmounted FS
-                ULONG cc;
-                IO_STATUS_BLOCK iosb;
-                
-                Status = dev_ioctl(first_device(Vcb)->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
-                
-                if (!NT_SUCCESS(Status))
-                    return Status;
-            }
+            if (Vcb->root_fileref->open_count == 0 && !(Vcb->Vpb->Flags & VPB_MOUNTED)) // don't allow root to be opened on unmounted FS
+                return STATUS_DEVICE_NOT_READY;
             
             increase_fileref_refcount(Vcb->root_fileref);
             *pfr = Vcb->root_fileref;
@@ -1649,13 +1437,11 @@
         return STATUS_OBJECT_PATH_NOT_FOUND;
     }
     
-    if (fnus->Length == 0) {
-        num_parts = 0;
-    } else if (fnus->Length == wcslen(datastring) * sizeof(WCHAR) &&
-               RtlCompareMemory(fnus->Buffer, datastring, wcslen(datastring) * sizeof(WCHAR)) == wcslen(datastring) * sizeof(WCHAR)) {
-        num_parts = 0;
-    } else {
-        Status = split_path(&fnus2, &parts, &num_parts, &has_stream);
+    InitializeListHead(&parts);
+    
+    if (fnus->Length != 0 &&
+        (fnus->Length != wcslen(datastring) * sizeof(WCHAR) || RtlCompareMemory(fnus->Buffer, datastring, wcslen(datastring) * sizeof(WCHAR)) != wcslen(datastring) * sizeof(WCHAR))) {
+        Status = split_path(Vcb, &fnus2, &parts, &has_stream);
         if (!NT_SUCCESS(Status)) {
             ERR("split_path returned %08x\n", Status);
             return Status;
@@ -1665,16 +1451,21 @@
     sf = dir;
     increase_fileref_refcount(dir);
     
-    if (parent) {
-        num_parts--;
+    if (parent && !IsListEmpty(&parts)) {
+        name_bit* nb;
         
-        if (has_stream && num_parts > 0) {
-            num_parts--;
+        nb = CONTAINING_RECORD(RemoveTailList(&parts), name_bit, list_entry);
+        ExFreePool(nb);
+        
+        if (has_stream && !IsListEmpty(&parts)) {
+            nb = CONTAINING_RECORD(RemoveTailList(&parts), name_bit, list_entry);
+            ExFreePool(nb);
+            
             has_stream = FALSE;
         }
     }
     
-    if (num_parts == 0) {
+    if (IsListEmpty(&parts)) {
         Status = STATUS_SUCCESS;
         *pfr = dir;
         
@@ -1684,10 +1475,13 @@
         goto end2;
     }
     
-    for (i = 0; i < num_parts; i++) {
-        BOOL lastpart = (i == num_parts-1) || (i == num_parts-2 && has_stream);
+    le = parts.Flink;
+    while (le != &parts) {
+        name_bit* nb = CONTAINING_RECORD(le, name_bit, list_entry);
+        BOOL lastpart = le->Flink == &parts || (has_stream && le->Flink->Flink == &parts);
+        BOOL streampart = has_stream && le->Flink == &parts;
         
-        Status = open_fileref_child(Vcb, sf, &parts[i], case_sensitive, lastpart, has_stream && i == num_parts - 1, pooltype, &sf2, Irp);
+        Status = open_fileref_child(Vcb, sf, &nb->us, case_sensitive, lastpart, streampart, pooltype, &sf2, Irp);
         if (!NT_SUCCESS(Status)) {
             if (Status == STATUS_OBJECT_PATH_NOT_FOUND || Status == STATUS_OBJECT_NAME_NOT_FOUND)
                 TRACE("open_fileref_child returned %08x\n", Status);
@@ -1697,9 +1491,13 @@
             goto end;
         }
         
-        if (i == num_parts - 1) {
-            if (fn_offset)
-                *fn_offset = parts[has_stream ? (num_parts - 2) : (num_parts - 1)].Buffer - fnus->Buffer;
+        if (le->Flink == &parts) { // last entry
+            if (fn_offset) {
+                if (has_stream)
+                    nb = CONTAINING_RECORD(le->Blink, name_bit, list_entry);
+                
+                *fn_offset = nb->us.Buffer - fnus->Buffer;
+            }
             
             break;
         }
@@ -1707,14 +1505,19 @@
         if (sf2->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
             Status = STATUS_REPARSE;
             
-            if (parsed)
-                *parsed = (parts[i+1].Buffer - fnus->Buffer - 1) * sizeof(WCHAR);
+            if (parsed) {
+                name_bit* nb2 = CONTAINING_RECORD(le->Flink, name_bit, list_entry);
+                
+                *parsed = (nb2->us.Buffer - fnus->Buffer - 1) * sizeof(WCHAR);
+            }
             
             break;
         }
         
-        free_fileref(sf);
+        free_fileref(Vcb, sf);
         sf = sf2;
+        
+        le = le->Flink;
     }
     
     if (Status != STATUS_REPARSE)
@@ -1722,63 +1525,21 @@
     *pfr = sf2;
     
 end:
-    free_fileref(sf);
+    free_fileref(Vcb, sf);
     
+    while (!IsListEmpty(&parts)) {
+        name_bit* nb = CONTAINING_RECORD(RemoveHeadList(&parts), name_bit, list_entry);
+        ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
+    }
+    
 end2:
-    if (parts)
-        ExFreePool(parts);
-    
     TRACE("returning %08x\n", Status);
     
     return Status;
 }
 
-NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index, PIRP Irp) {
-    KEY searchkey;
-    traverse_ptr tp, prev_tp;
+NTSTATUS add_dir_child(fcb* fcb, UINT64 inode, BOOL subvol, PANSI_STRING utf8, PUNICODE_STRING name, UINT8 type, dir_child** pdc) {
     NTSTATUS Status;
-    
-    ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
-    
-    if (fcb->last_dir_index != 0) {
-        *index = fcb->last_dir_index;
-        fcb->last_dir_index++;
-        Status = STATUS_SUCCESS;
-        goto end;
-    }
-    
-    searchkey.obj_id = fcb->inode;
-    searchkey.obj_type = TYPE_DIR_INDEX + 1;
-    searchkey.offset = 0;
-    
-    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, Irp))
-            tp = prev_tp;
-    }
-    
-    if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) {
-        fcb->last_dir_index = tp.item->key.offset + 1;
-    } else
-        fcb->last_dir_index = 2;
-    
-    *index = fcb->last_dir_index;
-    fcb->last_dir_index++;
-    
-    Status = STATUS_SUCCESS;
-    
-end:
-    ExReleaseResourceLite(fcb->Header.Resource);
-    
-    return Status;
-}
-
-NTSTATUS add_dir_child(fcb* fcb, UINT64 inode, BOOL subvol, UINT64 index, PANSI_STRING utf8, PUNICODE_STRING name, PUNICODE_STRING name_uc, UINT8 type, dir_child** pdc) {
     dir_child* dc;
     
     dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
@@ -1802,19 +1563,9 @@
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    dc->name_uc.Buffer = ExAllocatePoolWithTag(PagedPool, name_uc->Length, ALLOC_TAG);
-    if (!dc->name_uc.Buffer) {
-        ERR("out of memory\n");
-        ExFreePool(dc->utf8.Buffer);
-        ExFreePool(dc->name.Buffer);
-        ExFreePool(dc);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
     dc->key.obj_id = inode;
     dc->key.obj_type = subvol ? TYPE_ROOT_ITEM : TYPE_INODE_ITEM;
     dc->key.offset = 0;
-    dc->index = index;
     dc->type = type;
     dc->fileref = NULL;
     
@@ -1823,15 +1574,29 @@
     
     dc->name.Length = dc->name.MaximumLength = name->Length;
     RtlCopyMemory(dc->name.Buffer, name->Buffer, name->Length);
+        
+    Status = RtlUpcaseUnicodeString(&dc->name_uc, name, TRUE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+        ExFreePool(dc->utf8.Buffer);
+        ExFreePool(dc->name.Buffer);
+        ExFreePool(dc);
+        return Status;
+    }
     
-    dc->name_uc.Length = dc->name_uc.MaximumLength = name_uc->Length;
-    RtlCopyMemory(dc->name_uc.Buffer, name_uc->Buffer, name_uc->Length);
-    
     dc->hash = calc_crc32c(0xffffffff, (UINT8*)dc->name.Buffer, dc->name.Length);
     dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)dc->name_uc.Buffer, dc->name_uc.Length);
     
     ExAcquireResourceExclusiveLite(&fcb->nonpaged->dir_children_lock, TRUE);
     
+    if (IsListEmpty(&fcb->dir_children_index))
+        dc->index = 2;
+    else {
+        dir_child* dc2 = CONTAINING_RECORD(fcb->dir_children_index.Blink, dir_child, list_entry_index);
+        
+        dc->index = max(2, dc2->index + 1);
+    }
+    
     InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
     
     insert_dir_child_into_hash_lists(fcb, dc);
@@ -1849,7 +1614,7 @@
     fcb* fcb;
     ULONG utf8len;
     char* utf8 = NULL;
-    UINT64 dirpos, inode;
+    UINT64 inode;
     UINT8 type;
     LARGE_INTEGER time;
     BTRFS_TIME now;
@@ -1858,6 +1623,7 @@
     ULONG defda;
     file_ref* fileref;
     dir_child* dc;
+    ANSI_STRING utf8as;
 #ifdef DEBUG_FCB_REFCOUNTS
     LONG rc;
 #endif
@@ -1880,13 +1646,6 @@
     
     utf8[utf8len] = 0;
     
-    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);
-        return Status;
-    }
-    
     KeQuerySystemTime(&time);
     win_time_to_unix(time, &now);
     
@@ -1929,7 +1688,7 @@
     if (IrpSp->Parameters.Create.FileAttributes == FILE_ATTRIBUTE_NORMAL)
         IrpSp->Parameters.Create.FileAttributes = defda;
     
-    fcb = create_fcb(pool_type);
+    fcb = create_fcb(Vcb, pool_type);
     if (!fcb) {
         ERR("out of memory\n");
         ExFreePool(utf8);
@@ -1982,6 +1741,9 @@
             fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
     }
     
+    fcb->prop_compression = parfileref->fcb->prop_compression;
+    fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None;
+    
     fcb->inode_item_changed = TRUE;
     
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
@@ -2048,7 +1810,7 @@
         fcb->ea_changed = TRUE;
     }
     
-    fileref = create_fileref();
+    fileref = create_fileref(Vcb);
     if (!fileref) {
         ERR("out of memory\n");
         free_fcb(fcb);
@@ -2056,40 +1818,13 @@
     }
     
     fileref->fcb = fcb;
-    fileref->index = dirpos;
-
-    fileref->utf8.MaximumLength = fileref->utf8.Length = utf8len;
-    fileref->utf8.Buffer = utf8;
     
-    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");
-            free_fcb(fcb);
-            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);
-        free_fileref(fileref);
-        return Status;
-    }
-
     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);
-            free_fileref(fileref);
+            free_fileref(Vcb, fileref);
             return Status;
         }
     }
@@ -2105,15 +1840,22 @@
     
     fileref->parent = parfileref;
 
-    insert_fileref_child(parfileref, fileref, TRUE);
+    utf8as.Buffer = utf8;
+    utf8as.Length = utf8as.MaximumLength = utf8len;
     
-    Status = add_dir_child(fileref->parent->fcb, fcb->inode, FALSE, fileref->index, &fileref->utf8, &fileref->filepart, &fileref->filepart_uc, fcb->type, &dc);
+    Status = add_dir_child(fileref->parent->fcb, fcb->inode, FALSE, &utf8as, fpus, fcb->type, &dc);
     if (!NT_SUCCESS(Status))
         WARN("add_dir_child returned %08x\n", Status);
     
+    ExFreePool(utf8);
+    
     fileref->dc = dc;
     dc->fileref = fileref;
     
+    ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
+    InsertTailList(&parfileref->children, &fileref->list_entry);
+    ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
+    
     increase_fileref_refcount(parfileref);
     
     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
@@ -2120,7 +1862,7 @@
         fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
         if (!fcb->hash_ptrs) {
             ERR("out of memory\n");
-            free_fileref(fileref);
+            free_fileref(Vcb, fileref);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
@@ -2129,7 +1871,7 @@
         fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
         if (!fcb->hash_ptrs_uc) {
             ERR("out of memory\n");
-            free_fileref(fileref);
+            free_fileref(Vcb, fileref);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
@@ -2163,6 +1905,7 @@
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
+    dir_child* dc;
 #ifdef DEBUG_FCB_REFCOUNTS
     LONG rc;
 #endif
@@ -2205,7 +1948,7 @@
         return Status;
     }
     
-    free_fileref(parfileref);
+    free_fileref(Vcb, parfileref);
     
     parfileref = newpar;
     *pparfileref = parfileref;
@@ -2225,7 +1968,7 @@
         return STATUS_OBJECT_NAME_INVALID;
     }
         
-    fcb = create_fcb(pool_type);
+    fcb = create_fcb(Vcb, pool_type);
     if (!fcb) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
@@ -2307,7 +2050,7 @@
     } else
         fcb->adsmaxlen -= overhead + utf8len + xapreflen;
     
-    fileref = create_fileref();
+    fileref = create_fileref(Vcb);
     if (!fileref) {
         ERR("out of memory\n");
         free_fcb(fcb);
@@ -2315,24 +2058,54 @@
     }
     
     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) {
+    
+    dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
+    if (!dc) {
         ERR("out of memory\n");
-        free_fileref(fileref);
+        free_fileref(Vcb, fileref);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    RtlCopyMemory(fileref->filepart.Buffer, stream->Buffer, stream->Length);
+    RtlZeroMemory(dc, sizeof(dir_child));
     
-    Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
+    dc->utf8.MaximumLength = dc->utf8.Length = fcb->adsxattr.Length - xapreflen;
+    dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.MaximumLength, ALLOC_TAG);
+    if (!dc->utf8.Buffer) {
+        ERR("out of memory\n");
+        ExFreePool(dc);
+        free_fileref(Vcb, fileref);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlCopyMemory(dc->utf8.Buffer, &fcb->adsxattr.Buffer[xapreflen], fcb->adsxattr.Length - xapreflen);
+    
+    dc->name.MaximumLength = dc->name.Length = stream->Length;
+    dc->name.Buffer = ExAllocatePoolWithTag(pool_type, dc->name.MaximumLength, ALLOC_TAG);
+    if (!dc->name.Buffer) {
+        ERR("out of memory\n");
+        ExFreePool(dc->utf8.Buffer);
+        ExFreePool(dc);
+        free_fileref(Vcb, fileref);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlCopyMemory(dc->name.Buffer, stream->Buffer, stream->Length);
+    
+    Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, TRUE);
     if (!NT_SUCCESS(Status)) {
         ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-        free_fileref(fileref);
+        ExFreePool(dc->utf8.Buffer);
+        ExFreePool(dc->name.Buffer);
+        ExFreePool(dc);
+        free_fileref(Vcb, fileref);
         return Status;
     }
     
+    dc->fileref = fileref;
+    fileref->dc = dc;
+    
+    InsertHeadList(&parfileref->fcb->dir_children_index, &dc->list_entry_index);
+    
     mark_fcb_dirty(fcb);
     mark_fileref_dirty(fileref);
     
@@ -2354,7 +2127,9 @@
     
     fileref->parent = (struct _file_ref*)parfileref;
     
-    insert_fileref_child(parfileref, fileref, TRUE);
+    ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
+    InsertTailList(&parfileref->children, &fileref->list_entry);
+    ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
     
     increase_fileref_refcount(parfileref);
     
@@ -2368,7 +2143,7 @@
     NTSTATUS Status;
 //     fcb *fcb, *parfcb = NULL;
     file_ref *fileref, *parfileref = NULL;
-    ULONG i, j, fn_offset;
+    ULONG i, j;
 //     ULONG utf8len;
     ccb* ccb;
     static WCHAR datasuf[] = {':','$','D','A','T','A',0};
@@ -2427,8 +2202,6 @@
     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;
                 
@@ -2501,7 +2274,7 @@
     if (!ccb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
-        free_fileref(fileref);
+        free_fileref(Vcb, fileref);
         goto end;
     }
     
@@ -2529,11 +2302,6 @@
     InterlockedIncrement(&Vcb->open_files);
     
     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;
     
@@ -2571,7 +2339,7 @@
     
 end2:
     if (parfileref && !loaded_related)
-        free_fileref(parfileref);
+        free_fileref(Vcb, parfileref);
     
     return Status;
 }
@@ -2721,7 +2489,7 @@
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
-        Status = read_file(fcb, *data, 0, size, &bytes_read, NULL, TRUE);
+        Status = read_file(fcb, *data, 0, size, &bytes_read, NULL);
         if (!NT_SUCCESS(Status)) {
             ERR("read_file_fcb returned %08x\n", Status);
             ExFreePool(*data);
@@ -2819,6 +2587,47 @@
     return STATUS_SUCCESS;
 }
 
+static void fcb_load_csums(fcb* fcb, PIRP Irp) {
+    LIST_ENTRY* le;
+    NTSTATUS Status;
+    
+    if (fcb->csum_loaded)
+        return;
+    
+    if (IsListEmpty(&fcb->extents) || fcb->inode_item.flags & BTRFS_INODE_NODATASUM)
+        goto end;
+    
+    le = fcb->extents.Flink;
+    while (le != &fcb->extents) {
+        extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+        
+        if (ext->extent_data.type == EXTENT_TYPE_REGULAR) {
+            EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ext->extent_data.data[0];
+            UINT64 len;
+            
+            len = (ext->extent_data.compression == BTRFS_COMPRESSION_NONE ? ed2->num_bytes : ed2->size) / fcb->Vcb->superblock.sector_size;
+            
+            ext->csum = ExAllocatePoolWithTag(NonPagedPool, len * sizeof(UINT32), ALLOC_TAG);
+            if (!ext->csum) {
+                ERR("out of memory\n");
+                goto end;
+            }
+            
+            Status = load_csum(fcb->Vcb, ext->csum, ed2->address + (ext->extent_data.compression == BTRFS_COMPRESSION_NONE ? ed2->offset : 0), len, Irp);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("load_csum returned %08x\n", Status);
+                goto end;
+            }
+        }
+        
+        le = le->Flink;
+    }
+    
+end:
+    fcb->csum_loaded = TRUE;
+}
+
 static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_ENTRY* rollback) {
     PFILE_OBJECT FileObject;
     ULONG RequestedDisposition;
@@ -2852,7 +2661,7 @@
     if (options & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) {
         WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
         Status = STATUS_INVALID_PARAMETER;
-        goto exit2;
+        goto exit;
     }
 
     FileObject = Stack->FileObject;
@@ -2904,13 +2713,13 @@
     
     if (Vcb->readonly && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE)) {
         Status = STATUS_MEDIA_WRITE_PROTECTED;
-        goto exit2;
+        goto exit;
     }
     
     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 exit2;
+        goto exit;
     }
     
     ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
@@ -2934,11 +2743,13 @@
         } else {
             WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
             Status = STATUS_NOT_IMPLEMENTED;
+            ExReleaseResourceLite(&Vcb->fcb_lock);
             goto exit;
         }
     } else {
         if (related && fn.Length != 0 && fn.Buffer[0] == '\\') {
             Status = STATUS_OBJECT_NAME_INVALID;
+            ExReleaseResourceLite(&Vcb->fcb_lock);
             goto exit;
         }
         
@@ -2954,7 +2765,7 @@
                 fileref = related;
             else if (NT_SUCCESS(Status)) {
                 fnoff *= sizeof(WCHAR);
-                fnoff += related->filepart.Length + sizeof(WCHAR);
+                fnoff += (related->dc ? related->dc->name.Length : 0) + sizeof(WCHAR);
                 
                 if (related->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
                     Status = STATUS_REPARSE;
@@ -2965,7 +2776,7 @@
                     fn.Length -= fnoff;
 
                     Status = open_fileref(Vcb, &fileref, &fn, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
-                                        pool_type, Stack->Flags & SL_CASE_SENSITIVE, Irp);
+                                          pool_type, Stack->Flags & SL_CASE_SENSITIVE, Irp);
                     
                     loaded_related = TRUE;
                 }
@@ -2987,7 +2798,9 @@
         if (!NT_SUCCESS(Status)) {
             ERR("get_reparse_block returned %08x\n", Status);
             
-            free_fileref(fileref);
+            free_fileref(Vcb, fileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             goto exit;
         }
         
@@ -2998,12 +2811,15 @@
         
         Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
         
-        free_fileref(fileref);
+        free_fileref(Vcb, fileref);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+        
         goto exit;
     }
     
     if (NT_SUCCESS(Status) && fileref->deleted) {
-        free_fileref(fileref);
+        free_fileref(Vcb, fileref);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
         
         Status = STATUS_OBJECT_NAME_NOT_FOUND;
     }
@@ -3012,19 +2828,25 @@
         if (RequestedDisposition == FILE_CREATE) {
             TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref));
             Status = STATUS_OBJECT_NAME_COLLISION;
-            free_fileref(fileref);
+            
+            free_fileref(Vcb, fileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             goto exit;
         }
     } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
         if (RequestedDisposition == FILE_OPEN || RequestedDisposition == FILE_OVERWRITE) {
             TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
+            ExReleaseResourceLite(&Vcb->fcb_lock);
             goto exit;
         }
     } else if (Status == STATUS_OBJECT_PATH_NOT_FOUND) {
         TRACE("open_fileref returned %08x\n", Status);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
         goto exit;
     } else {
         ERR("open_fileref returned %08x\n", Status);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
         goto exit;
     }
     
@@ -3031,6 +2853,8 @@
     if (NT_SUCCESS(Status)) { // file already exists
         file_ref* sf;
         
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+        
         if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
             LARGE_INTEGER zero;
             
@@ -3039,13 +2863,21 @@
 #endif
             if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
                 Status = STATUS_ACCESS_DENIED;
-                free_fileref(fileref);
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(Vcb, fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 goto exit;
             }
             
             if (Vcb->readonly) {
                 Status = STATUS_MEDIA_WRITE_PROTECTED;
-                free_fileref(fileref);
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(Vcb, fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 goto exit;
             }
             
@@ -3052,7 +2884,11 @@
             zero.QuadPart = 0;
             if (!MmCanFileBeTruncated(&fileref->fcb->nonpaged->segment_object, &zero)) {
                 Status = STATUS_USER_MAPPED_FILE;
-                free_fileref(fileref);
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(Vcb, fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 goto exit;
             }
         }
@@ -3065,7 +2901,12 @@
                            IoGetFileObjectGenericMapping(), Stack->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
                            &granted_access, &Status)) {
             SeUnlockSubjectContext(&Stack->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
-            WARN("SeAccessCheck failed, returning %08x\n", Status);
+            TRACE("SeAccessCheck failed, returning %08x\n", Status);
+        
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fileref(Vcb, fileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+        
             goto exit;
         }
         
@@ -3076,7 +2917,11 @@
             (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC) &&
             fileref->fcb->inode != SUBVOL_ROOT_INODE) {
             Status = STATUS_ACCESS_DENIED;
-            free_fileref(fileref);
+        
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fileref(Vcb, fileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+        
             goto exit;
         }
         
@@ -3088,7 +2933,10 @@
                 WARN("could not open as deletion pending\n");
                 Status = STATUS_DELETE_PENDING;
                 
-                free_fileref(fileref);
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(Vcb, fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 goto exit;
             }
             sf = sf->parent;
@@ -3106,7 +2954,11 @@
             
             if (granted_access & ~allowed) {
                 Status = STATUS_ACCESS_DENIED;
-                free_fileref(fileref);
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(Vcb, fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 goto exit;
             }
         }
@@ -3114,7 +2966,11 @@
         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;
-            free_fileref(fileref);
+        
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fileref(Vcb, fileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+        
             goto exit;
         }
         
@@ -3131,7 +2987,11 @@
             Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
             if (!NT_SUCCESS(Status)) {
                 ERR("get_reparse_block returned %08x\n", Status);
-                free_fileref(fileref);
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(Vcb, fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 goto exit;
             }
             
@@ -3143,20 +3003,31 @@
             
             Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
             
-            free_fileref(fileref);
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fileref(Vcb, fileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             goto exit;
         }
         
         if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY && !fileref->fcb->ads) {
             if (options & FILE_NON_DIRECTORY_FILE && !(fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
-                free_fileref(fileref);
                 Status = STATUS_FILE_IS_A_DIRECTORY;
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(Vcb, fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 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));
-            free_fileref(fileref);
             Status = STATUS_NOT_A_DIRECTORY;
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fileref(Vcb, fileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             goto exit;
         }
         
@@ -3166,9 +3037,14 @@
             if (!NT_SUCCESS(Status)) {
                 WARN("IoCheckShareAccess failed, returning %08x\n", Status);
                 
-                free_fileref(fileref);
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(Vcb, fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 goto exit;
             }
+            
+            IoUpdateShareAccess(FileObject, &fileref->fcb->share_access);
         } else {
             IoSetShareAccess(granted_access, Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
         }
@@ -3177,7 +3053,10 @@
             if (!MmFlushImageSection(&fileref->fcb->nonpaged->segment_object, MmFlushForWrite)) {
                 Status = (options & FILE_DELETE_ON_CLOSE) ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION;
                 
-                free_fileref(fileref);
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(Vcb, fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 goto exit;
             }
         }
@@ -3190,7 +3069,11 @@
             if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
                 WARN("cannot overwrite readonly file\n");
                 Status = STATUS_ACCESS_DENIED;
-                free_fileref(fileref);
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(Vcb, fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 goto exit;
             }
     
@@ -3206,7 +3089,11 @@
             Status = truncate_file(fileref->fcb, 0, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("truncate_file returned %08x\n", Status);
-                free_fileref(fileref);
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(Vcb, fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 goto exit;
             }
             
@@ -3215,7 +3102,11 @@
                 
                 if (!NT_SUCCESS(Status)) {
                     ERR("extend_file returned %08x\n", Status);
-                    free_fileref(fileref);
+                    
+                    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                    free_fileref(Vcb, fileref);
+                    ExReleaseResourceLite(&Vcb->fcb_lock);
+                    
                     goto exit;
                 }
             }
@@ -3227,7 +3118,11 @@
                 Status = IoCheckEaBufferValidity(Irp->AssociatedIrp.SystemBuffer, Stack->Parameters.Create.EaLength, &offset);
                 if (!NT_SUCCESS(Status)) {
                     ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
-                    free_fileref(fileref);
+                    
+                    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                    free_fileref(Vcb, fileref);
+                    ExReleaseResourceLite(&Vcb->fcb_lock);
+                    
                     goto exit;
                 }
                 
@@ -3259,7 +3154,10 @@
                     ERR("out of memory\n");
                     Status = STATUS_INSUFFICIENT_RESOURCES;
                     
-                    free_fileref(fileref);
+                    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                    free_fileref(Vcb, fileref);
+                    ExReleaseResourceLite(&Vcb->fcb_lock);
+                    
                     goto exit;
                 }
                 
@@ -3282,8 +3180,8 @@
             
             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, Irp);
+            defda = get_file_attributes(Vcb, fileref->fcb->subvol, fileref->fcb->inode, fileref->fcb->type,
+                                        fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', TRUE, Irp);
             
             if (RequestedDisposition == FILE_SUPERSEDE)
                 fileref->fcb->atts = Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
@@ -3316,8 +3214,12 @@
                 do {
                     if (ffei->Flags & FILE_NEED_EA) {
                         WARN("returning STATUS_ACCESS_DENIED as no EA knowledge\n");
-                        free_fileref(fileref);
                         Status = STATUS_ACCESS_DENIED;
+                        
+                        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                        free_fileref(Vcb, fileref);
+                        ExReleaseResourceLite(&Vcb->fcb_lock);
+                        
                         goto exit;
                     }
                     
@@ -3334,8 +3236,12 @@
         ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
         if (!ccb) {
             ERR("out of memory\n");
-            free_fileref(fileref);
             Status = STATUS_INSUFFICIENT_RESOURCES;
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fileref(Vcb, fileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             goto exit;
         }
         
@@ -3355,12 +3261,6 @@
         ccb->fileref = fileref;
         
         FileObject->FsContext2 = ccb;
-        
-        if (fn_offset > 0) {
-            fn.Length -= fn_offset * sizeof(WCHAR);
-            RtlMoveMemory(&fn.Buffer[0], &fn.Buffer[fn_offset], fn.Length);
-        }
-        
         FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
         
         if (NT_SUCCESS(Status)) {
@@ -3394,8 +3294,8 @@
             while (le != &fileref->fcb->extents) {
                 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
                 
-                if (ext->data->type == EXTENT_TYPE_PREALLOC) {
-                    ext->data->type = EXTENT_TYPE_REGULAR;
+                if (ext->extent_data.type == EXTENT_TYPE_PREALLOC) {
+                    ext->extent_data.type = EXTENT_TYPE_REGULAR;
                     changed = TRUE;
                 }
                 
@@ -3425,6 +3325,8 @@
         open_type = 2;
 #endif
         Status = file_create(Irp, DeviceObject->DeviceExtension, FileObject, related, loaded_related, &fn, RequestedDisposition, options, rollback);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+        
         Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
     }
     
@@ -3432,19 +3334,31 @@
         FileObject->Flags |= FO_CACHE_SUPPORTED;
     
 exit:
-    ExReleaseResourceLite(&Vcb->fcb_lock);
+    if (loaded_related) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fileref(Vcb, related);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
     
-exit2:
-    if (loaded_related)
-        free_fileref(related);
-    
-    if (NT_SUCCESS(Status)) {
+    if (Status == STATUS_SUCCESS) {
+        fcb* fcb2;
+        
         if (!FileObject->Vpb)
             FileObject->Vpb = DeviceObject->Vpb;
-    } else {
-        if (Status != STATUS_OBJECT_NAME_NOT_FOUND && Status != STATUS_OBJECT_PATH_NOT_FOUND)
-            TRACE("returning %08x\n", Status);
-    }
+        
+        fcb2 = FileObject->FsContext;
+        
+        if (fcb2->ads) {
+            struct _ccb* ccb2 = FileObject->FsContext2;
+            
+            fcb2 = ccb2->fileref->parent->fcb;
+        }
+        
+        ExAcquireResourceExclusiveLite(fcb2->Header.Resource, TRUE);
+        fcb_load_csums(fcb2, Irp);
+        ExReleaseResourceLite(fcb2->Header.Resource);
+    } else if (Status != STATUS_REPARSE && Status != STATUS_OBJECT_NAME_NOT_FOUND && Status != STATUS_OBJECT_PATH_NOT_FOUND)
+        TRACE("returning %08x\n", Status);
     
 #ifdef DEBUG_STATS
     time2 = KeQueryPerformanceCounter(NULL);
@@ -3552,7 +3466,7 @@
     top_level = is_top_level(Irp);
     
     /* return success if just called for FS device object */
-    if (DeviceObject == devobj || (Vcb && Vcb->type == VCB_TYPE_PARTITION0))  {
+    if (DeviceObject == devobj)  {
         TRACE("create called for FS device object\n");
         
         Irp->IoStatus.Information = FILE_OPENED;
@@ -3559,8 +3473,24 @@
         Status = STATUS_SUCCESS;
 
         goto exit;
+    } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_create(DeviceObject, Irp);
+        goto exit;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto exit;
     }
     
+    if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
+        Status = STATUS_DEVICE_NOT_READY;
+        goto exit;
+    }
+    
+    if (Vcb->removing) {
+        Status = STATUS_ACCESS_DENIED;
+        goto exit;
+    }
+    
     Status = verify_vcb(Vcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("verify_vcb returned %08x\n", Status);
@@ -3634,11 +3564,6 @@
             goto exit;
         }
         
-        if (Vcb->removing) {
-            Status = STATUS_ACCESS_DENIED;
-            goto exit;
-        }
-        
         ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
         if (!ccb) {
             ERR("out of memory\n");
@@ -3693,7 +3618,7 @@
         if (!NT_SUCCESS(Status))
             do_rollback(Vcb, &rollback);
         else
-            clear_rollback(Vcb, &rollback);
+            clear_rollback(&rollback);
         
         if (!skip_lock)
             ExReleaseResourceLite(&Vcb->tree_lock);
Index: drivers/filesystems/btrfs/devctrl.c
===================================================================
--- drivers/filesystems/btrfs/devctrl.c	(révision 74430)
+++ drivers/filesystems/btrfs/devctrl.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -16,100 +16,14 @@
  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include "btrfs_drv.h"
-#ifndef __REACTOS__
-#include <winioctl.h>
-#endif
+#include <ntdddisk.h>
 #include <mountdev.h>
-#include <initguid.h>
 #include <diskguid.h>
 
+extern PDRIVER_OBJECT drvobj;
 extern LIST_ENTRY VcbList;
 extern ERESOURCE global_loading_lock;
 
-static NTSTATUS part0_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
-    NTSTATUS Status;
-    part0_device_extension* p0de = DeviceObject->DeviceExtension;
-    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    
-    TRACE("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
-    
-    switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
-        case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
-        {
-            MOUNTDEV_UNIQUE_ID* mduid;
-
-            if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) {
-                Status = STATUS_BUFFER_TOO_SMALL;
-                Irp->IoStatus.Status = Status;
-                Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
-                IoCompleteRequest(Irp, IO_NO_INCREMENT);
-                return Status;
-            }
-
-            mduid = Irp->AssociatedIrp.SystemBuffer;
-            mduid->UniqueIdLength = sizeof(BTRFS_UUID);
-
-            if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID) - 1 + mduid->UniqueIdLength) {
-                Status = STATUS_BUFFER_OVERFLOW;
-                Irp->IoStatus.Status = Status;
-                Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
-                IoCompleteRequest(Irp, IO_NO_INCREMENT);
-                return Status;
-            }
-
-            RtlCopyMemory(mduid->UniqueId, &p0de->uuid, sizeof(BTRFS_UUID));
-
-            Status = STATUS_SUCCESS;
-            Irp->IoStatus.Status = Status;
-            Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID) - 1 + mduid->UniqueIdLength;
-            IoCompleteRequest(Irp, IO_NO_INCREMENT);
-            
-            return Status;
-        }
-        
-        case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
-        {
-            PMOUNTDEV_NAME name;
-
-            if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) {
-                Status = STATUS_BUFFER_TOO_SMALL;
-                Irp->IoStatus.Status = Status;
-                Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
-                IoCompleteRequest(Irp, IO_NO_INCREMENT);
-                return Status;
-            }
-
-            name = Irp->AssociatedIrp.SystemBuffer;
-            name->NameLength = p0de->name.Length;
-
-            if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength) {
-                Status = STATUS_BUFFER_OVERFLOW;
-                Irp->IoStatus.Status = Status;
-                Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
-                IoCompleteRequest(Irp, IO_NO_INCREMENT);
-                return Status;
-            }
-            
-            RtlCopyMemory(name->Name, p0de->name.Buffer, p0de->name.Length);
-
-            Status = STATUS_SUCCESS;
-            Irp->IoStatus.Status = Status;
-            Irp->IoStatus.Information = offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength;
-            IoCompleteRequest(Irp, IO_NO_INCREMENT);
-            
-            return Status;
-        }
-    }
-    
-    IoSkipCurrentIrpStackLocation(Irp);
-    
-    Status = IoCallDriver(p0de->devobj, Irp);
-    
-    TRACE("returning %08x\n", Status);
-    
-    return Status;
-}
-
 static NTSTATUS mountdev_query_stable_guid(device_extension* Vcb, PIRP Irp) {
     MOUNTDEV_STABLE_GUID* msg = Irp->UserBuffer;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
@@ -126,31 +40,7 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS get_partition_info_ex(device_extension* Vcb, PIRP Irp) {
-    NTSTATUS Status;
-    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    PARTITION_INFORMATION_EX* piex;
-    
-    TRACE("IOCTL_DISK_GET_PARTITION_INFO_EX\n");
-    
-    Status = dev_ioctl(Vcb->Vpb->RealDevice, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0,
-                       Irp->UserBuffer, IrpSp->Parameters.DeviceIoControl.OutputBufferLength, TRUE, &Irp->IoStatus);
-    if (!NT_SUCCESS(Status))
-        return Status;
-    
-    piex = (PARTITION_INFORMATION_EX*)Irp->UserBuffer;
-    
-    if (piex->PartitionStyle == PARTITION_STYLE_MBR) {
-        piex->Mbr.PartitionType = PARTITION_IFS;
-        piex->Mbr.RecognizedPartition = TRUE;
-    } else if (piex->PartitionStyle == PARTITION_STYLE_GPT) {
-        piex->Gpt.PartitionType = PARTITION_BASIC_DATA_GUID;
-    }
-    
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS is_writable(device_extension* Vcb, PIRP Irp) {
+static NTSTATUS is_writable(device_extension* Vcb) {
     TRACE("IOCTL_DISK_IS_WRITABLE\n");
     
     return Vcb->readonly ? STATUS_MEDIA_WRITE_PROTECTED : STATUS_SUCCESS;
@@ -264,6 +154,59 @@
     return Status;
 }
 
+static NTSTATUS probe_volume(void* data, ULONG length, KPROCESSOR_MODE processor_mode) {
+    MOUNTDEV_NAME* mdn = (MOUNTDEV_NAME*)data;
+    UNICODE_STRING path, pnp_name;
+    NTSTATUS Status;
+    PDEVICE_OBJECT DeviceObject;
+    PFILE_OBJECT FileObject;
+    const GUID* guid;
+    
+    if (length < sizeof(MOUNTDEV_NAME))
+        return STATUS_INVALID_PARAMETER;
+    
+    if (length < offsetof(MOUNTDEV_NAME, Name[0]) + mdn->NameLength)
+        return STATUS_INVALID_PARAMETER;
+    
+    TRACE("%.*S\n", mdn->NameLength / sizeof(WCHAR), mdn->Name);
+    
+    if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
+        return STATUS_PRIVILEGE_NOT_HELD;
+    
+    path.Buffer = mdn->Name;
+    path.Length = path.MaximumLength = mdn->NameLength;
+    
+    Status = IoGetDeviceObjectPointer(&path, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+        return Status;
+    }
+    
+    Status = get_device_pnp_name(DeviceObject, &pnp_name, &guid);
+    if (!NT_SUCCESS(Status)) {
+        ERR("get_device_pnp_name returned %08x\n", Status);
+        ObDereferenceObject(FileObject);
+        return Status;
+    }
+    
+    if (RtlCompareMemory(guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID)) {
+        Status = dev_ioctl(DeviceObject, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, TRUE, NULL);
+        if (!NT_SUCCESS(Status))
+            WARN("IOCTL_DISK_UPDATE_PROPERTIES returned %08x\n", Status);
+    }
+
+    ObDereferenceObject(FileObject);
+    
+    volume_removal(drvobj, &pnp_name);
+
+    if (RtlCompareMemory(guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID))
+        disk_arrival(drvobj, &pnp_name);
+    else
+        volume_arrival(drvobj, &pnp_name);
+    
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS control_ioctl(PIRP Irp) {
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     NTSTATUS Status;
@@ -272,6 +215,10 @@
         case IOCTL_BTRFS_QUERY_FILESYSTEMS:
             Status = query_filesystems(map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
             break;
+
+        case IOCTL_BTRFS_PROBE_VOLUME:
+            Status = probe_volume(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
+            break;
             
         default:
             TRACE("unhandled ioctl %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
@@ -295,12 +242,15 @@
     Irp->IoStatus.Information = 0;
     
     if (Vcb) {
-        if (Vcb->type == VCB_TYPE_PARTITION0) {
-            Status = part0_device_control(DeviceObject, Irp);
-            goto end2;
-        } else if (Vcb->type == VCB_TYPE_CONTROL) {
+        if (Vcb->type == VCB_TYPE_CONTROL) {
             Status = control_ioctl(Irp);
             goto end;
+        } else if (Vcb->type == VCB_TYPE_VOLUME) {
+            Status = vol_device_control(DeviceObject, Irp);
+            goto end;
+        } else if (Vcb->type != VCB_TYPE_FS) {
+            Status = STATUS_INVALID_PARAMETER;
+            goto end;
         }
     } else {
         Status = STATUS_INVALID_PARAMETER;
@@ -316,13 +266,9 @@
         case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
             Status = mountdev_query_stable_guid(Vcb, Irp);
             goto end;
-            
-        case IOCTL_DISK_GET_PARTITION_INFO_EX:
-            Status = get_partition_info_ex(Vcb, Irp);
-            goto end;
-            
+
         case IOCTL_DISK_IS_WRITABLE:
-            Status = is_writable(Vcb, Irp);
+            Status = is_writable(Vcb);
             goto end;
             
         default:
Index: drivers/filesystems/btrfs/dirctrl.c
===================================================================
--- drivers/filesystems/btrfs/dirctrl.c	(révision 74430)
+++ drivers/filesystems/btrfs/dirctrl.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -28,6 +28,7 @@
     UNICODE_STRING name;
     UINT8 type;
     enum DirEntryType dir_entry_type;
+    dir_child* dc;
 } dir_entry;
 
 ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts, PIRP Irp) {
@@ -62,7 +63,7 @@
         
         RtlCopyMemory(&tag, fcb->reparse_xattr.Buffer, sizeof(ULONG));
     } else {
-        Status = read_file(fcb, (UINT8*)&tag, 0, sizeof(ULONG), &br, NULL, TRUE);
+        Status = read_file(fcb, (UINT8*)&tag, 0, sizeof(ULONG), &br, NULL);
         if (!NT_SUCCESS(Status)) {
             ERR("read_file returned %08x\n", Status);
             goto end;
@@ -158,25 +159,13 @@
         switch (de->dir_entry_type) {
             case DirEntryType_File:
             {
-                LIST_ENTRY* le;
                 BOOL found = FALSE;
                 
-                if (!IsListEmpty(&r->fcbs)) {
-                    le = r->fcbs.Flink;
-                    while (le != &r->fcbs) {
-                        struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
-                        
-                        if (fcb2->inode == inode && !fcb2->ads) {
-                            ii = fcb2->inode_item;
-                            atts = fcb2->atts;
-                            ealen = fcb2->ealen;
-                            found = TRUE;
-                            break;
-                        } else if (fcb2->inode > inode)
-                            break;
-                        
-                        le = le->Flink;
-                    }
+                if (de->dc && de->dc->fileref && de->dc->fileref->fcb) {
+                    ii = de->dc->fileref->fcb->inode_item;
+                    atts = de->dc->fileref->fcb->atts;
+                    ealen = de->dc->fileref->fcb->ealen;
+                    found = TRUE;
                 }
                 
                 if (!found) {
@@ -211,7 +200,7 @@
                         
                         BOOL dotfile = de->name.Length > sizeof(WCHAR) && de->name.Buffer[0] == '.';
 
-                        atts = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE, Irp);
+                        atts = get_file_attributes(fcb->Vcb, r, inode, de->type, dotfile, FALSE, Irp);
                     }
                     
                     if (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation || 
@@ -464,7 +453,7 @@
     return STATUS_NO_MORE_FILES;
 }
 
-static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_entry* de, dir_child** pdc, PIRP Irp) {
+static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_entry* de, dir_child** pdc) {
     LIST_ENTRY* le;
     dir_child* dc;
     
@@ -535,6 +524,7 @@
     de->name = dc->name;
     de->type = dc->type;
     de->dir_entry_type = DirEntryType_File;
+    de->dc = dc;
     
     *offset = dc->index + 1;
     *pdc = dc;
@@ -542,7 +532,7 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+static NTSTATUS STDCALL query_directory(device_extension* Vcb, PIRP Irp) {
     PIO_STACK_LOCATION IrpSp;
     NTSTATUS Status, status2;
     fcb* fcb;
@@ -585,8 +575,8 @@
         return STATUS_ACCESS_DENIED;
     }
     
-    ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
-    ExAcquireResourceSharedLite(&fcb->Vcb->fcb_lock, TRUE);
+    ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
+    ExAcquireResourceSharedLite(&Vcb->fcb_lock, TRUE);
     
     TRACE("%S\n", file_desc(IrpSp->FileObject));
     
@@ -632,7 +622,8 @@
         
         if (IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != '*') {
             specific_file = TRUE;
-            if (!ccb->case_sensitive || FsRtlDoesNameContainWildCards(IrpSp->Parameters.QueryDirectory.FileName)) {
+            
+            if (FsRtlDoesNameContainWildCards(IrpSp->Parameters.QueryDirectory.FileName)) {
                 has_wildcard = TRUE;
                 specific_file = FALSE;
             }
@@ -679,7 +670,7 @@
     
     ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
     
-    Status = next_dir_entry(fileref, &newoffset, &de, &dc, Irp);
+    Status = next_dir_entry(fileref, &newoffset, &de, &dc);
     
     if (!NT_SUCCESS(Status)) {
         if (Status == STATUS_NO_MORE_FILES && initial)
@@ -735,6 +726,7 @@
                             de.name = dc2->name;
                             de.type = dc2->type;
                             de.dir_entry_type = DirEntryType_File;
+                            de.dc = dc2;
                             
                             break;
                         }
@@ -758,6 +750,7 @@
                             de.name = dc2->name;
                             de.type = dc2->type;
                             de.dir_entry_type = DirEntryType_File;
+                            de.dc = dc2;
                             
                             break;
                         }
@@ -779,7 +772,7 @@
     } else if (has_wildcard) {
         while (!FsRtlIsNameInExpression(&ccb->query_string, &de.name, !ccb->case_sensitive, NULL)) {
             newoffset = ccb->query_dir_offset;
-            Status = next_dir_entry(fileref, &newoffset, &de, &dc, Irp);
+            Status = next_dir_entry(fileref, &newoffset, &de, &dc);
             
             if (NT_SUCCESS(Status))
                 ccb->query_dir_offset = newoffset;
@@ -822,7 +815,7 @@
             
             if (length > 0) {
                 newoffset = ccb->query_dir_offset;
-                Status = next_dir_entry(fileref, &newoffset, &de, &dc, Irp);
+                Status = next_dir_entry(fileref, &newoffset, &de, &dc);
                 if (NT_SUCCESS(Status)) {
                     if (!has_wildcard || FsRtlIsNameInExpression(&ccb->query_string, &de.name, !ccb->case_sensitive, NULL)) {
                         curitem = (UINT8*)buf + IrpSp->Parameters.QueryDirectory.Length - length;
@@ -861,8 +854,8 @@
     ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
     
 end2:
-    ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
-    ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+    ExReleaseResourceLite(&Vcb->fcb_lock);
+    ExReleaseResourceLite(&Vcb->tree_lock);
     
     TRACE("returning %08x\n", Status);
     
@@ -910,8 +903,27 @@
     TRACE("%S\n", file_desc(FileObject));
 
     if (ccb->filename.Length == 0) {
-        Status = fileref_get_filename(fileref, &ccb->filename, NULL);
-        if (!NT_SUCCESS(Status)) {
+        ULONG reqlen;
+        
+        ccb->filename.MaximumLength = ccb->filename.Length = 0;
+        
+        Status = fileref_get_filename(fileref, &ccb->filename, NULL, &reqlen);
+        if (Status == STATUS_BUFFER_OVERFLOW) {
+            ccb->filename.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
+            if (!ccb->filename.Buffer) {
+                ERR("out of memory\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto end;
+            }
+            
+            ccb->filename.MaximumLength = reqlen;
+            
+            Status = fileref_get_filename(fileref, &ccb->filename, NULL, &reqlen);
+            if (!NT_SUCCESS(Status)) {
+                ERR("fileref_get_filename returned %08x\n", Status);
+                goto end;
+            }
+        } else {
             ERR("fileref_get_filename returned %08x\n", Status);
             goto end;
         }
@@ -943,9 +955,12 @@
 
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_directory_control(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
     
     IrpSp = IoGetCurrentIrpStackLocation(Irp);
@@ -960,7 +975,7 @@
             break;
             
         case IRP_MN_QUERY_DIRECTORY:
-            Status = query_directory(DeviceObject, Irp);
+            Status = query_directory(Vcb, Irp);
             break;
             
         default:
@@ -973,6 +988,7 @@
     if (Status == STATUS_PENDING)
         goto exit;
     
+end:
     Irp->IoStatus.Status = Status;
 
 //     if (Irp->UserIosb)
Index: drivers/filesystems/btrfs/extent-tree.c
===================================================================
--- drivers/filesystems/btrfs/extent-tree.c	(révision 74430)
+++ drivers/filesystems/btrfs/extent-tree.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -222,7 +222,8 @@
 }
 
 static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 flags, LIST_ENTRY* extent_refs,
-                                      KEY* firstitem, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) {
+                                      KEY* firstitem, UINT8 level, PIRP Irp) {
+    NTSTATUS Status;
     LIST_ENTRY *le, *next_le;
     UINT64 refcount;
     ULONG inline_len;
@@ -326,10 +327,11 @@
         le = le->Flink;
     }
     
-    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");
+    Status = insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, inline_len, NULL, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
         ExFreePool(ei);
-        return STATUS_INTERNAL_ERROR;
+        return Status;
     }
     
     if (!all_inline) {
@@ -352,9 +354,10 @@
             } else
                 data = NULL;
             
-            if (!insert_tree_item(Vcb, Vcb->extent_root, address, er->type, er->hash, data, len, NULL, Irp, rollback)) {
-                ERR("error - failed to insert item\n");
-                return STATUS_INTERNAL_ERROR;
+            Status = insert_tree_item(Vcb, Vcb->extent_root, address, er->type, er->hash, data, len, NULL, Irp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item returned %08x\n", Status);
+                return Status;
             }
             
             le = le->Flink;
@@ -364,7 +367,7 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS convert_old_extent(device_extension* Vcb, UINT64 address, BOOL tree, KEY* firstitem, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS convert_old_extent(device_extension* Vcb, UINT64 address, BOOL tree, KEY* firstitem, UINT8 level, PIRP Irp) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp, next_tp;
@@ -390,7 +393,11 @@
     
     size = tp.item->key.offset;
     
-    delete_tree_item(Vcb, &tp, rollback);
+    Status = delete_tree_item(Vcb, &tp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("delete_tree_item returned %08x\n", Status);
+        return Status;
+    }
     
     while (find_next_item(Vcb, &tp, &next_tp, FALSE, Irp)) {
         tp = next_tp;
@@ -420,7 +427,11 @@
                 }
             }
             
-            delete_tree_item(Vcb, &tp, rollback);
+            Status = delete_tree_item(Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                goto end;
+            }
         }
 
         if (tp.item->key.obj_id > address || tp.item->key.obj_type > TYPE_EXTENT_REF_V0)
@@ -428,7 +439,7 @@
     }
 
     Status = construct_extent_item(Vcb, address, size, tree ? (EXTENT_ITEM_TREE_BLOCK | EXTENT_ITEM_SHARED_BACKREFS) : EXTENT_ITEM_DATA,
-                                   &extent_refs, firstitem, level, Irp, rollback);
+                                   &extent_refs, firstitem, level, Irp);
     if (!NT_SUCCESS(Status))
         ERR("construct_extent_item returned %08x\n", Status);
 
@@ -438,7 +449,7 @@
     return Status;
 }
 
-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 increase_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem, UINT8 level, PIRP Irp) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
@@ -499,16 +510,14 @@
         *ptr = type;
         RtlCopyMemory(ptr + 1, data, datalen);
         
-        if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA && is_tree) {
-            if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, ei, eisize, NULL, Irp, rollback)) {
-                ERR("insert_tree_item failed\n");
-                return STATUS_INTERNAL_ERROR;
-            }
-        } else {
-            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;
-            }
+        if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA && is_tree)
+            Status = insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, ei, eisize, NULL, Irp);
+        else
+            Status = insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL, Irp);
+        
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
+            return Status;
         }
 
         return STATUS_SUCCESS;
@@ -520,7 +529,7 @@
     skinny = tp.item->key.obj_type == TYPE_METADATA_ITEM;
 
     if (tp.item->size == sizeof(EXTENT_ITEM_V0) && !skinny) {
-        Status = convert_old_extent(Vcb, address, is_tree, firstitem, level, Irp, rollback);
+        Status = convert_old_extent(Vcb, address, is_tree, firstitem, level, Irp);
         
         if (!NT_SUCCESS(Status)) {
             ERR("convert_old_extent returned %08x\n", Status);
@@ -527,7 +536,7 @@
             return Status;
         }
 
-        return increase_extent_refcount(Vcb, address, size, type, data, firstitem, level, Irp, rollback);
+        return increase_extent_refcount(Vcb, address, size, type, data, firstitem, level, Irp);
     }
         
     if (tp.item->size < sizeof(EXTENT_ITEM)) {
@@ -595,11 +604,16 @@
                     sectedr2 = (EXTENT_DATA_REF*)((UINT8*)newei + ((UINT8*)sectedr - tp.item->data));
                     sectedr2->count += rc;
                     
-                    delete_tree_item(Vcb, &tp, rollback);
+                    Status = delete_tree_item(Vcb, &tp);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_tree_item returned %08x\n", Status);
+                        return Status;
+                    }
                     
-                    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;
+                    Status = 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);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("insert_tree_item returned %08x\n", Status);
+                        return Status;
                     }
                     
                     return STATUS_SUCCESS;
@@ -639,11 +653,16 @@
                     sectsdr2 = (SHARED_DATA_REF*)((UINT8*)newei + ((UINT8*)sectsdr - tp.item->data));
                     sectsdr2->count += rc;
                     
-                    delete_tree_item(Vcb, &tp, rollback);
+                    Status = delete_tree_item(Vcb, &tp);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_tree_item returned %08x\n", Status);
+                        return Status;
+                    }
                     
-                    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;
+                    Status = 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);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("insert_tree_item returned %08x\n", Status);
+                        return Status;
                     }
                     
                     return STATUS_SUCCESS;
@@ -705,11 +724,16 @@
         *ptr = type;
         RtlCopyMemory(ptr + 1, data, datalen);
         
-        delete_tree_item(Vcb, &tp, rollback);
+        Status = delete_tree_item(Vcb, &tp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            return Status;
+        }
         
-        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;
+        Status = 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);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
+            return Status;
         }
         
         return STATUS_SUCCESS;
@@ -757,11 +781,16 @@
                 return STATUS_INTERNAL_ERROR;
             }
             
-            delete_tree_item(Vcb, &tp2, rollback);
+            Status = delete_tree_item(Vcb, &tp2);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
             
-            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;
+            Status = 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);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item returned %08x\n", Status);
+                return Status;
             }
             
             newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
@@ -769,11 +798,16 @@
             
             newei->refcount += get_extent_data_refcount(type, data);
             
-            delete_tree_item(Vcb, &tp, rollback);
+            Status = delete_tree_item(Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
             
-            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;
+            Status = 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);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item returned %08x\n", Status);
+                return Status;
             }
             
             return STATUS_SUCCESS;
@@ -785,9 +819,10 @@
     data2 = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG);
     RtlCopyMemory(data2, data, datalen);
     
-    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;
+    Status = insert_tree_item(Vcb, Vcb->extent_root, address, type, offset, data2, datalen, NULL, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
+        return Status;
     }
     
     newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
@@ -795,17 +830,22 @@
     
     newei->refcount += get_extent_data_refcount(type, data);
     
-    delete_tree_item(Vcb, &tp, rollback);
+    Status = delete_tree_item(Vcb, &tp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("delete_tree_item returned %08x\n", Status);
+        return Status;
+    }
     
-    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;
+    Status = 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);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
+        return Status;
     }
     
     return STATUS_SUCCESS;
 }
 
-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 increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, PIRP Irp) {
     EXTENT_DATA_REF edr;
     
     edr.root = root;
@@ -813,17 +853,11 @@
     edr.offset = offset;
     edr.count = refcount;
     
-    return increase_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, Irp, rollback);
+    return increase_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, Irp);
 }
 
-void decrease_chunk_usage(chunk* c, UINT64 delta) {
-    c->used -= delta;
-    
-    TRACE("decreasing size of chunk %llx by %llx\n", c->offset, delta);
-}
-
 NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem,
-                                  UINT8 level, UINT64 parent, BOOL superseded, PIRP Irp, LIST_ENTRY* rollback) {
+                                  UINT8 level, UINT64 parent, BOOL superseded, PIRP Irp) {
     KEY searchkey;
     NTSTATUS Status;
     traverse_ptr tp, tp2;
@@ -872,7 +906,7 @@
         }
         
         if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
-            Status = convert_old_extent(Vcb, address, is_tree, firstitem, level, Irp, rollback);
+            Status = convert_old_extent(Vcb, address, is_tree, firstitem, level, Irp);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("convert_old_extent returned %08x\n", Status);
@@ -879,7 +913,7 @@
                 return Status;
             }
 
-            return decrease_extent_refcount(Vcb, address, size, type, data, firstitem, level, parent, superseded, Irp, rollback);
+            return decrease_extent_refcount(Vcb, address, size, type, data, firstitem, level, parent, superseded, Irp);
         }
     }
     
@@ -938,10 +972,14 @@
                 
                 if (sectedr->root == edr->root && sectedr->objid == edr->objid && sectedr->offset == edr->offset) {
                     if (ei->refcount == edr->count) {
-                        delete_tree_item(Vcb, &tp, rollback);
+                        Status = delete_tree_item(Vcb, &tp);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("delete_tree_item returned %08x\n", Status);
+                            return Status;
+                        }
                         
                         if (!superseded)
-                            add_checksum_entry(Vcb, address, size / Vcb->superblock.sector_size, NULL, Irp, rollback);
+                            add_checksum_entry(Vcb, address, size / Vcb->superblock.sector_size, NULL, Irp);
                         
                         return STATUS_SUCCESS;
                     }
@@ -977,11 +1015,16 @@
                     
                     newei->refcount -= rc;
                     
-                    delete_tree_item(Vcb, &tp, rollback);
+                    Status = delete_tree_item(Vcb, &tp);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_tree_item returned %08x\n", Status);
+                        return Status;
+                    }
                     
-                    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;
+                    Status = 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);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("insert_tree_item returned %08x\n", Status);
+                        return Status;
                     }
                     
                     return STATUS_SUCCESS;
@@ -994,10 +1037,14 @@
                 
                 if (sectsdr->offset == sdr->offset) {
                     if (ei->refcount == sectsdr->count) {
-                        delete_tree_item(Vcb, &tp, rollback);
+                        Status = delete_tree_item(Vcb, &tp);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("delete_tree_item returned %08x\n", Status);
+                            return Status;
+                        }
                         
                         if (!superseded)
-                            add_checksum_entry(Vcb, address, size / Vcb->superblock.sector_size, NULL, Irp, rollback);
+                            add_checksum_entry(Vcb, address, size / Vcb->superblock.sector_size, NULL, Irp);
                         
                         return STATUS_SUCCESS;
                     }
@@ -1033,11 +1080,16 @@
 
                     newei->refcount -= rc;
                     
-                    delete_tree_item(Vcb, &tp, rollback);
+                    Status = delete_tree_item(Vcb, &tp);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_tree_item returned %08x\n", Status);
+                        return Status;
+                    }
                     
-                    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;
+                    Status = 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);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("insert_tree_item returned %08x\n", Status);
+                        return Status;
                     }
                     
                     return STATUS_SUCCESS;
@@ -1050,7 +1102,12 @@
                 
                 if (secttbr->offset == tbr->offset) {
                     if (ei->refcount == 1) {
-                        delete_tree_item(Vcb, &tp, rollback);
+                        Status = delete_tree_item(Vcb, &tp);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("delete_tree_item returned %08x\n", Status);
+                            return Status;
+                        }
+                        
                         return STATUS_SUCCESS;
                     }
 
@@ -1069,11 +1126,16 @@
                     
                     newei->refcount--;
                     
-                    delete_tree_item(Vcb, &tp, rollback);
+                    Status = delete_tree_item(Vcb, &tp);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_tree_item returned %08x\n", Status);
+                        return Status;
+                    }
                     
-                    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;
+                    Status = 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);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("insert_tree_item returned %08x\n", Status);
+                        return Status;
                     }
                     
                     return STATUS_SUCCESS;
@@ -1086,7 +1148,12 @@
                 
                 if (sectsbr->offset == sbr->offset) {
                     if (ei->refcount == 1) {
-                        delete_tree_item(Vcb, &tp, rollback);
+                        Status = delete_tree_item(Vcb, &tp);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("delete_tree_item returned %08x\n", Status);
+                            return Status;
+                        }
+                        
                         return STATUS_SUCCESS;
                     }
                     
@@ -1105,11 +1172,16 @@
                     
                     newei->refcount--;
                     
-                    delete_tree_item(Vcb, &tp, rollback);
+                    Status = delete_tree_item(Vcb, &tp);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_tree_item returned %08x\n", Status);
+                        return Status;
+                    }
                     
-                    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;
+                    Status = 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);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("insert_tree_item returned %08x\n", Status);
+                        return Status;
                     }
                     
                     return STATUS_SUCCESS;
@@ -1157,11 +1229,20 @@
         
         if (sectedr->root == edr->root && sectedr->objid == edr->objid && sectedr->offset == edr->offset) {
             if (ei->refcount == edr->count) {
-                delete_tree_item(Vcb, &tp, rollback);
-                delete_tree_item(Vcb, &tp2, rollback);
+                Status = delete_tree_item(Vcb, &tp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_tree_item returned %08x\n", Status);
+                    return Status;
+                }
                 
+                Status = delete_tree_item(Vcb, &tp2);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_tree_item returned %08x\n", Status);
+                    return Status;
+                }
+                
                 if (!superseded)
-                    add_checksum_entry(Vcb, address, size / Vcb->superblock.sector_size, NULL, Irp, rollback);
+                    add_checksum_entry(Vcb, address, size / Vcb->superblock.sector_size, NULL, Irp);
                 
                 return STATUS_SUCCESS;
             }
@@ -1171,7 +1252,11 @@
                 return STATUS_INTERNAL_ERROR;
             }
             
-            delete_tree_item(Vcb, &tp2, rollback);
+            Status = delete_tree_item(Vcb, &tp2);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
             
             if (sectedr->count > edr->count) {
                 EXTENT_DATA_REF* newedr = ExAllocatePoolWithTag(PagedPool, tp2.item->size, ALLOC_TAG);
@@ -1185,9 +1270,10 @@
                 
                 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, Irp, rollback)) {
-                    ERR("insert_tree_item failed\n");
-                    return STATUS_INTERNAL_ERROR;
+                Status = 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);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("insert_tree_item returned %08x\n", Status);
+                    return Status;
                 }
             }
             
@@ -1201,11 +1287,16 @@
 
             newei->refcount -= rc;
             
-            delete_tree_item(Vcb, &tp, rollback);
+            Status = delete_tree_item(Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
             
-            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;
+            Status = 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);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item returned %08x\n", Status);
+                return Status;
             }
             
             return STATUS_SUCCESS;
@@ -1220,11 +1311,20 @@
         
         if (sectsdr->offset == sdr->offset) {
             if (ei->refcount == sdr->count) {
-                delete_tree_item(Vcb, &tp, rollback);
-                delete_tree_item(Vcb, &tp2, rollback);
+                Status = delete_tree_item(Vcb, &tp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_tree_item returned %08x\n", Status);
+                    return Status;
+                }
                 
+                Status = delete_tree_item(Vcb, &tp2);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_tree_item returned %08x\n", Status);
+                    return Status;
+                }
+                
                 if (!superseded)
-                    add_checksum_entry(Vcb, address, size / Vcb->superblock.sector_size, NULL, Irp, rollback);
+                    add_checksum_entry(Vcb, address, size / Vcb->superblock.sector_size, NULL, Irp);
                 
                 return STATUS_SUCCESS;
             }
@@ -1234,7 +1334,11 @@
                 return STATUS_INTERNAL_ERROR;
             }
             
-            delete_tree_item(Vcb, &tp2, rollback);
+            Status = delete_tree_item(Vcb, &tp2);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
             
             if (sectsdr->count > sdr->count) {
                 SHARED_DATA_REF* newsdr = ExAllocatePoolWithTag(PagedPool, tp2.item->size, ALLOC_TAG);
@@ -1248,9 +1352,10 @@
                 
                 newsdr->count -= sdr->count;
                 
-                if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, newsdr, tp2.item->size, NULL, Irp, rollback)) {
-                    ERR("insert_tree_item failed\n");
-                    return STATUS_INTERNAL_ERROR;
+                Status = insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, newsdr, tp2.item->size, NULL, Irp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("insert_tree_item returned %08x\n", Status);
+                    return Status;
                 }
             }
             
@@ -1264,11 +1369,16 @@
 
             newei->refcount -= rc;
             
-            delete_tree_item(Vcb, &tp, rollback);
+            Status = delete_tree_item(Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
             
-            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;
+            Status = 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);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item returned %08x\n", Status);
+                return Status;
             }
             
             return STATUS_SUCCESS;
@@ -1283,12 +1393,26 @@
         
         if (sectsbr->offset == sbr->offset) {
             if (ei->refcount == 1) {
-                delete_tree_item(Vcb, &tp, rollback);
-                delete_tree_item(Vcb, &tp2, rollback);
+                Status = delete_tree_item(Vcb, &tp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_tree_item returned %08x\n", Status);
+                    return Status;
+                }
+                
+                Status = delete_tree_item(Vcb, &tp2);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_tree_item returned %08x\n", Status);
+                    return Status;
+                }
+                
                 return STATUS_SUCCESS;
             }
             
-            delete_tree_item(Vcb, &tp2, rollback);
+            Status = delete_tree_item(Vcb, &tp2);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
             
             newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
             if (!newei) {
@@ -1300,11 +1424,16 @@
 
             newei->refcount -= rc;
             
-            delete_tree_item(Vcb, &tp, rollback);
+            Status = delete_tree_item(Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
             
-            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;
+            Status = 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);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item returned %08x\n", Status);
+                return Status;
             }
             
             return STATUS_SUCCESS;
@@ -1319,12 +1448,26 @@
         
         if (secttbr->offset == tbr->offset) {
             if (ei->refcount == 1) {
-                delete_tree_item(Vcb, &tp, rollback);
-                delete_tree_item(Vcb, &tp2, rollback);
+                Status = delete_tree_item(Vcb, &tp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_tree_item returned %08x\n", Status);
+                    return Status;
+                }
+                
+                Status = delete_tree_item(Vcb, &tp2);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_tree_item returned %08x\n", Status);
+                    return Status;
+                }
+                
                 return STATUS_SUCCESS;
             }
             
-            delete_tree_item(Vcb, &tp2, rollback);
+            Status = delete_tree_item(Vcb, &tp2);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
             
             newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
             if (!newei) {
@@ -1336,11 +1479,16 @@
 
             newei->refcount -= rc;
             
-            delete_tree_item(Vcb, &tp, rollback);
+            Status = delete_tree_item(Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
             
-            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;
+            Status = 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);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item returned %08x\n", Status);
+                return Status;
             }
             
             return STATUS_SUCCESS;
@@ -1353,16 +1501,29 @@
         EXTENT_ITEM* newei;
         
         if (ei->refcount == erv0->count) {
-            delete_tree_item(Vcb, &tp, rollback);
-            delete_tree_item(Vcb, &tp2, rollback);
+            Status = delete_tree_item(Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
             
+            Status = delete_tree_item(Vcb, &tp2);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
+            
             if (!superseded)
-                add_checksum_entry(Vcb, address, size / Vcb->superblock.sector_size, NULL, Irp, rollback);
+                add_checksum_entry(Vcb, address, size / Vcb->superblock.sector_size, NULL, Irp);
             
             return STATUS_SUCCESS;
         }
         
-        delete_tree_item(Vcb, &tp2, rollback);
+        Status = delete_tree_item(Vcb, &tp2);
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            return Status;
+        }
         
         newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
         if (!newei) {
@@ -1374,11 +1535,16 @@
 
         newei->refcount -= rc;
         
-        delete_tree_item(Vcb, &tp, rollback);
+        Status = delete_tree_item(Vcb, &tp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            return Status;
+        }
         
-        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;
+        Status = 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);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
+            return Status;
         }
         
         return STATUS_SUCCESS;
@@ -1389,7 +1555,7 @@
 }
 
 NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode,
-                                       UINT64 offset, UINT32 refcount, BOOL superseded, PIRP Irp, LIST_ENTRY* rollback) {
+                                       UINT64 offset, UINT32 refcount, BOOL superseded, PIRP Irp) {
     EXTENT_DATA_REF edr;
     
     edr.root = root;
@@ -1397,16 +1563,16 @@
     edr.offset = offset;
     edr.count = refcount;
     
-    return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, 0, superseded, Irp, rollback);
+    return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, 0, superseded, Irp);
 }
 
 NTSTATUS decrease_extent_refcount_tree(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root,
-                                       UINT8 level, PIRP Irp, LIST_ENTRY* rollback) {
+                                       UINT8 level, PIRP Irp) {
     TREE_BLOCK_REF tbr;
     
     tbr.offset = root;
     
-    return decrease_extent_refcount(Vcb, address, size, TYPE_TREE_BLOCK_REF, &tbr, NULL/*FIXME*/, level, 0, FALSE, Irp, rollback);
+    return decrease_extent_refcount(Vcb, address, size, TYPE_TREE_BLOCK_REF, &tbr, NULL/*FIXME*/, level, 0, FALSE, Irp);
 }
 
 static UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, PIRP Irp) {
@@ -1529,7 +1695,7 @@
         return eiv0->refcount;
     } else if (tp.item->size < sizeof(EXTENT_ITEM)) {
         ERR("(%llx,%x,%llx) was %x bytes, expected at least %x\n", tp.item->key.obj_id, tp.item->key.obj_type,
-                                                                       tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
+                                                                       tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
         return 0;
     }
     
@@ -1706,7 +1872,7 @@
         return 0;
     else if (tp.item->size < sizeof(EXTENT_ITEM)) {
         ERR("(%llx,%x,%llx) was %x bytes, expected at least %x\n", tp.item->key.obj_id, tp.item->key.obj_type,
-                                                                   tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
+                                                                   tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
         return 0;
     }
     
@@ -1747,7 +1913,7 @@
         return;
     else if (tp.item->size < sizeof(EXTENT_ITEM)) {
         ERR("(%llx,%x,%llx) was %x bytes, expected at least %x\n", tp.item->key.obj_id, tp.item->key.obj_type,
-                                                                   tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
+                                                                   tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
         return;
     }
     
Index: drivers/filesystems/btrfs/fastio.c
===================================================================
--- drivers/filesystems/btrfs/fastio.c	(révision 74430)
+++ drivers/filesystems/btrfs/fastio.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -19,14 +19,6 @@
 
 FAST_IO_DISPATCH FastIoDispatch;
 
-static void STDCALL acquire_file_for_create_section(PFILE_OBJECT FileObject) {
-    TRACE("STUB: acquire_file_for_create_section\n");
-}
-
-static void STDCALL release_file_for_create_section(PFILE_OBJECT FileObject) {
-    TRACE("STUB: release_file_for_create_section\n");
-}
-
 static BOOLEAN STDCALL fast_query_basic_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_BASIC_INFORMATION fbi,
                                              PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
     fcb* fcb;
@@ -131,7 +123,7 @@
 
         fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = adssize;
         fsi->NumberOfLinks = fcb->inode_item.st_nlink;
-        fsi->Directory = S_ISDIR(fcb->inode_item.st_mode);
+        fsi->Directory = FALSE;
     } else {
         fsi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
         fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
@@ -151,12 +143,6 @@
     return TRUE;
 }
 
-static BOOLEAN STDCALL fast_io_query_open(PIRP Irp, PFILE_NETWORK_OPEN_INFORMATION  NetworkInformation, PDEVICE_OBJECT DeviceObject) {
-    TRACE("STUB: fast_io_query_open\n");
-    
-    return FALSE;
-}
-
 static BOOLEAN STDCALL fast_io_check_if_possible(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait,
                                          ULONG LockKey, BOOLEAN CheckForReadOperation, PIO_STATUS_BLOCK IoStatus,
                                          PDEVICE_OBJECT DeviceObject) {
@@ -178,40 +164,56 @@
     return FALSE;
 }
 
-static BOOLEAN STDCALL fast_io_lock(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PLARGE_INTEGER Length, PEPROCESS ProcessId, ULONG Key, BOOLEAN FailImmediately, BOOLEAN ExclusiveLock, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
-    TRACE("STUB: fast_io_lock\n");
-    return FALSE;
+static BOOLEAN STDCALL fast_io_query_network_open_info(PFILE_OBJECT FileObject, BOOLEAN Wait, FILE_NETWORK_OPEN_INFORMATION* fnoi,
+                                                       PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) {
+    INODE_ITEM* ii;
+    fcb* fcb;
+    ccb* ccb;
+    file_ref* fileref;
+    
+    TRACE("(%p, %u, %p, %p, %p)\n", FileObject, Wait, fnoi, IoStatus, DeviceObject);
+    
+    RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION));
+    
+    fcb = FileObject->FsContext;
+    
+    if (!fcb || fcb == fcb->Vcb->volume_fcb)
+        return FALSE;
+    
+    ccb = FileObject->FsContext2;
+    
+    if (!ccb)
+        return FALSE;
+    
+    fileref = ccb->fileref;
+    
+    if (fcb->ads) {
+        if (!fileref || !fileref->parent) {
+            ERR("no fileref for stream\n");
+            return FALSE;
+        }
+        
+        ii = &fileref->parent->fcb->inode_item;
+    } else
+        ii = &fcb->inode_item;
+    
+    fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
+    fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
+    fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
+    fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
+    
+    if (fcb->ads) {
+        fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length;
+        fnoi->FileAttributes = fileref->parent->fcb->atts;
+    } else {
+        fnoi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+        fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
+        fnoi->FileAttributes = fcb->atts;
+    }
+    
+    return TRUE;
 }
 
-static BOOLEAN STDCALL fast_io_unlock_single(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PLARGE_INTEGER Length, PEPROCESS ProcessId, ULONG Key, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
-    TRACE("STUB: fast_io_unlock_single\n");
-    return FALSE;
-}
-
-static BOOLEAN STDCALL fast_io_unlock_all(PFILE_OBJECT FileObject, PEPROCESS ProcessId, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
-    TRACE("STUB: fast_io_unlock_all\n");
-    return FALSE;
-}
-
-static BOOLEAN STDCALL fast_io_unlock_all_by_key(PFILE_OBJECT FileObject, PVOID ProcessId, ULONG Key, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
-    TRACE("STUB: fast_io_unlock_all_by_key\n");
-    return FALSE;
-}
-
-static BOOLEAN STDCALL fast_io_device_control(PFILE_OBJECT FileObject, BOOLEAN Wait, PVOID InputBuffer OPTIONAL, ULONG InputBufferLength, PVOID OutputBuffer OPTIONAL, ULONG OutputBufferLength, ULONG IoControlCode, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject){
-    TRACE("STUB: fast_io_device_control\n");
-    return FALSE;
-}
-
-static VOID STDCALL fast_io_detach_device(PDEVICE_OBJECT SourceDevice, PDEVICE_OBJECT TargetDevice){
-    TRACE("STUB: fast_io_detach_device\n");
-}
-
-static BOOLEAN STDCALL fast_io_query_network_open_info(PFILE_OBJECT FileObject, BOOLEAN Wait, struct _FILE_NETWORK_OPEN_INFORMATION *Buffer, struct _IO_STATUS_BLOCK *IoStatus, PDEVICE_OBJECT DeviceObject){
-    TRACE("STUB: fast_io_query_network_open_info\n");
-    return FALSE;
-}
-
 static NTSTATUS STDCALL fast_io_acquire_for_mod_write(PFILE_OBJECT FileObject, PLARGE_INTEGER EndingOffset, struct _ERESOURCE **ResourceToRelease, PDEVICE_OBJECT DeviceObject) {
     fcb* fcb;
     
@@ -230,38 +232,34 @@
     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){
-    TRACE("STUB: fast_io_read_compressed\n");
-    return FALSE;
+static NTSTATUS STDCALL fast_io_release_for_mod_write(PFILE_OBJECT FileObject, struct _ERESOURCE *ResourceToRelease, PDEVICE_OBJECT DeviceObject) {
+    TRACE("(%p, %p, %p)\n", FileObject, ResourceToRelease, DeviceObject);
+    
+    ExReleaseResourceLite(ResourceToRelease);
+    
+    return STATUS_SUCCESS;
 }
 
-static BOOLEAN STDCALL fast_io_write_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){
-    TRACE("STUB: fast_io_write_compressed\n");
-    return FALSE;
-}
-
-static BOOLEAN STDCALL fast_io_mdl_read_complete_compressed(PFILE_OBJECT FileObject, PMDL MdlChain, PDEVICE_OBJECT DeviceObject){
-    TRACE("STUB: fast_io_mdl_read_complete_compressed\n");
-    return FALSE;
-}
-
-static BOOLEAN STDCALL fast_io_mdl_write_complete_compressed(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PMDL MdlChain, PDEVICE_OBJECT DeviceObject){
-    TRACE("STUB: fast_io_mdl_write_complete_compressed\n");
-    return FALSE;
-}
-
-static NTSTATUS STDCALL fast_io_release_for_mod_write(PFILE_OBJECT FileObject, struct _ERESOURCE *ResourceToRelease, PDEVICE_OBJECT DeviceObject){
-    TRACE("STUB: fast_io_release_for_mod_write\n");
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-static NTSTATUS STDCALL fast_io_acquire_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject){
+static NTSTATUS STDCALL fast_io_acquire_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject) {
     TRACE("STUB: fast_io_acquire_for_ccflush\n");
+    
+    UNUSED(FileObject);
+    UNUSED(DeviceObject);
+    
+    IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP);
+    
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS STDCALL fast_io_release_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject){
+static NTSTATUS STDCALL fast_io_release_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject) {
     TRACE("STUB: fast_io_release_for_ccflush\n");
+    
+    UNUSED(FileObject);
+    UNUSED(DeviceObject);
+    
+    if (IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP)
+        IoSetTopLevelIrp(NULL);
+
     return STATUS_SUCCESS;
 }
 
@@ -320,21 +318,15 @@
     FastIoDispatch.FastIoCheckIfPossible = fast_io_check_if_possible;
     FastIoDispatch.FastIoQueryBasicInfo = fast_query_basic_info;
     FastIoDispatch.FastIoQueryStandardInfo = fast_query_standard_info;
-    FastIoDispatch.FastIoLock = fast_io_lock;
-    FastIoDispatch.FastIoUnlockSingle = fast_io_unlock_single;
-    FastIoDispatch.FastIoUnlockAll = fast_io_unlock_all;
-    FastIoDispatch.FastIoUnlockAllByKey = fast_io_unlock_all_by_key;
-    FastIoDispatch.FastIoDeviceControl = fast_io_device_control;
-    FastIoDispatch.AcquireFileForNtCreateSection = acquire_file_for_create_section;
-    FastIoDispatch.ReleaseFileForNtCreateSection = release_file_for_create_section;
-    FastIoDispatch.FastIoDetachDevice = fast_io_detach_device;
+//     FastIoDispatch.FastIoLock = fast_io_lock;
+//     FastIoDispatch.FastIoUnlockSingle = fast_io_unlock_single;
+//     FastIoDispatch.FastIoUnlockAll = fast_io_unlock_all;
+//     FastIoDispatch.FastIoUnlockAllByKey = fast_io_unlock_all_by_key;
+//     FastIoDispatch.FastIoDeviceControl = fast_io_device_control;
+//     FastIoDispatch.AcquireFileForNtCreateSection = acquire_file_for_create_section;
+//     FastIoDispatch.ReleaseFileForNtCreateSection = release_file_for_create_section;
     FastIoDispatch.FastIoQueryNetworkOpenInfo = fast_io_query_network_open_info;
     FastIoDispatch.AcquireForModWrite = fast_io_acquire_for_mod_write;
-    FastIoDispatch.FastIoReadCompressed = fast_io_read_compressed;
-    FastIoDispatch.FastIoWriteCompressed = fast_io_write_compressed;
-    FastIoDispatch.MdlReadCompleteCompressed = fast_io_mdl_read_complete_compressed;
-    FastIoDispatch.MdlWriteCompleteCompressed = fast_io_mdl_write_complete_compressed;
-    FastIoDispatch.FastIoQueryOpen = fast_io_query_open;
     FastIoDispatch.ReleaseForModWrite = fast_io_release_for_mod_write;
     FastIoDispatch.AcquireForCcFlush = fast_io_acquire_for_ccflush;
     FastIoDispatch.ReleaseForCcFlush = fast_io_release_for_ccflush;
Index: drivers/filesystems/btrfs/fileinfo.c
===================================================================
--- drivers/filesystems/btrfs/fileinfo.c	(révision 74430)
+++ drivers/filesystems/btrfs/fileinfo.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -24,8 +24,6 @@
 #endif
 #endif
 
-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;
     fcb* fcb = FileObject->FsContext;
@@ -105,7 +103,8 @@
         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, Irp);
+        defda = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.',
+                                    TRUE, Irp);
         
         if (fcb->type == BTRFS_TYPE_DIRECTORY)
             fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
@@ -219,6 +218,12 @@
     ExReleaseResourceLite(fcb->Header.Resource);
     
     ExReleaseResourceLite(&Vcb->fcb_lock);
+    
+    // send notification that directory is about to be deleted
+    if (NT_SUCCESS(Status) && fdi->DeleteFile && fcb->type == BTRFS_TYPE_DIRECTORY) {
+        FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext,
+                                       NULL, FALSE, FALSE, 0, NULL, NULL, NULL);
+    }
 
     return Status;
 }
@@ -251,7 +256,7 @@
     
     // FIXME - we can skip a lot of this if the inode is about to be deleted
     
-    fcb = create_fcb(PagedPool); // FIXME - what if we duplicate the paging file?
+    fcb = create_fcb(Vcb, PagedPool); // FIXME - what if we duplicate the paging file?
     if (!fcb) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
@@ -323,7 +328,7 @@
         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
         
         if (!ext->ignore) {
-            extent* ext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+            extent* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
             
             if (!ext2) {
                 ERR("out of memory\n");
@@ -334,18 +339,8 @@
             ext2->offset = ext->offset;
             ext2->datalen = ext->datalen;
             
-            if (ext2->datalen > 0) {
-                ext2->data = ExAllocatePoolWithTag(PagedPool, ext2->datalen, ALLOC_TAG);
-                
-                if (!ext2->data) {
-                    ERR("out of memory\n");
-                    free_fcb(fcb);
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-                
-                RtlCopyMemory(ext2->data, ext->data, ext2->datalen);
-            } else
-                ext2->data = NULL;
+            if (ext2->datalen > 0)
+                RtlCopyMemory(&ext2->extent_data, &ext->extent_data, ext2->datalen);
             
             ext2->unique = FALSE;
             ext2->ignore = FALSE;
@@ -353,9 +348,9 @@
             
             if (ext->csum) {
                 ULONG len;
-                EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
+                EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
                 
-                if (ext->data->compression == BTRFS_COMPRESSION_NONE)
+                if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE)
                     len = ed2->num_bytes;
                 else
                     len = ed2->size;
@@ -424,8 +419,6 @@
         le = le->Flink;
     }
     
-    fcb->last_dir_index = oldfcb->last_dir_index;
-    
     if (oldfcb->reparse_xattr.Buffer && oldfcb->reparse_xattr.Length > 0) {
         fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = oldfcb->reparse_xattr.Length;
         
@@ -451,6 +444,8 @@
         
         RtlCopyMemory(fcb->ea_xattr.Buffer, oldfcb->ea_xattr.Buffer, fcb->ea_xattr.Length);
     }
+    
+    fcb->prop_compression = oldfcb->prop_compression;
 
 end:
     *pfcb = fcb;
@@ -466,417 +461,47 @@
     LIST_ENTRY list_entry;
 } move_entry;
 
-static NTSTATUS add_children_to_move_list(move_entry* me, PIRP Irp) {
+static NTSTATUS add_children_to_move_list(device_extension* Vcb, move_entry* me, PIRP Irp) {
     NTSTATUS Status;
-    KEY searchkey;
-    traverse_ptr tp;
-    BOOL b;
     LIST_ENTRY* le;
-    move_entry* me2;
-    
-    static char xapref[] = "user.";
-    ULONG xapreflen = strlen(xapref);
-    
-    ExAcquireResourceSharedLite(&me->fileref->nonpaged->children_lock, TRUE);
-    
-    le = me->fileref->children.Flink;
-    while (le != &me->fileref->children) {
-        file_ref* fr = CONTAINING_RECORD(le, file_ref, list_entry);
-        
-        if (!fr->deleted) {
-            me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
-            if (!me2) {
-                ERR("out of memory\n");
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto end;
-            }
-            
-            me2->fileref = fr;
-            
-            increase_fileref_refcount(fr);
 
-            me2->dummyfcb = NULL;
-            me2->dummyfileref = NULL;
-            me2->parent = me;
-            
-            InsertHeadList(&me->list_entry, &me2->list_entry);
-        }
-        
-        le = le->Flink;
-    }
+    ExAcquireResourceSharedLite(&me->fileref->fcb->nonpaged->dir_children_lock, TRUE);
     
-    searchkey.obj_id = me->fileref->fcb->inode;
-    searchkey.obj_type = TYPE_XATTR_ITEM;
-    searchkey.offset = 0;
+    le = me->fileref->fcb->dir_children_index.Flink;
     
-    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;
-    }
-    
-    do {
-        traverse_ptr next_tp;
+    while (le != &me->fileref->fcb->dir_children_index) {
+        dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
+        file_ref* fr;
+        move_entry* me2;
         
-        if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
-            DIR_ITEM* xa = (DIR_ITEM*)tp.item->data;
-            ULONG len;
-            
-            if (tp.item->size < sizeof(DIR_ITEM)) {
-                ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
-                Status = STATUS_INTERNAL_ERROR;
-                goto end;
-            }
-            
-            len = tp.item->size;
-            
-            do {
-                if (len < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) {
-                    ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                    Status = STATUS_INTERNAL_ERROR;
-                    goto end;
-                }
-                
-                if (xa->n > xapreflen && RtlCompareMemory(xa->name, xapref, xapreflen) == xapreflen &&
-                    (tp.item->key.offset != EA_DOSATTRIB_HASH || xa->n != strlen(EA_DOSATTRIB) || RtlCompareMemory(xa->name, EA_DOSATTRIB, xa->n) != xa->n) &&
-                    (tp.item->key.offset != EA_EA_HASH || xa->n != strlen(EA_EA) || RtlCompareMemory(xa->name, EA_EA, xa->n) != xa->n)
-                ) {
-                    BOOL found = FALSE;
-                
-                    le = me->fileref->children.Flink;
-                    
-                    while (le != &me->fileref->children) {
-                        file_ref* fr = CONTAINING_RECORD(le, file_ref, list_entry);
-
-                        if (fr->fcb->ads && fr->fcb->adshash == tp.item->key.offset && fr->fcb->adsxattr.Length == xa->n &&
-                            RtlCompareMemory(fr->fcb->adsxattr.Buffer, xa->name, xa->n) == xa->n) {
-                            found = TRUE;
-                            break;
-                        }
-                         
-                        le = le->Flink;
-                    }
-                    
-                    if (!found) {
-                        fcb* fcb;
-                        file_ref* fr;
-                        ANSI_STRING xattr;
-                        ULONG stringlen;
-                        
-                        xattr.Length = xa->n;
-                        xattr.MaximumLength = xattr.Length + 1;
-                        xattr.Buffer = ExAllocatePoolWithTag(PagedPool, xattr.MaximumLength, ALLOC_TAG);
-                        
-                        if (!xattr.Buffer) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            goto end;
-                        }
-                        
-                        RtlCopyMemory(xattr.Buffer, xa->name, xa->n);
-                        xattr.Buffer[xa->n] = 0;
-                        
-                        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, Irp);
-                        
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("open_fcb_stream returned %08x\n", Status);
-                            ExFreePool(xattr.Buffer);
-                            goto end;
-                        }
-                        
-                        fr = create_fileref();
-                        if (!fr) {
-                            ERR("out of memory\n");
-                            free_fcb(fcb);
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            goto end;
-                        }
-                        
-                        fr->fcb = fcb;
-                        
-                        Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, &xa->name[xapreflen], xa->n - xapreflen);
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-                            free_fileref(fr);
-                            goto end;
-                        }
-                        
-                        fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
-                        if (!fr->filepart.Buffer) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            free_fileref(fr);
-                            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);
-                            free_fileref(fr);
-                            goto end;
-                        }
-                        
-                        fr->filepart.Length = fr->filepart.MaximumLength = stringlen;
-                        
-                        Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE);
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-                            free_fileref(fr);
-                            goto end;
-                        }
-
-                        fr->parent = (struct _file_ref*)me->fileref;
-                        increase_fileref_refcount(fr->parent);
-                        
-                        insert_fileref_child(me->fileref, fr, FALSE);
-
-                        me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
-                        if (!me2) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            free_fileref(fr);
-                            goto end;
-                        }
-                        
-                        me2->fileref = fr;
-                        me2->dummyfcb = NULL;
-                        me2->dummyfileref = NULL;
-                        me2->parent = me;
-                        
-                        InsertHeadList(&me->list_entry, &me2->list_entry);
-                    }
-                }
-                
-                len -= sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
-                
-                if (len > 0)
-                    xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
-            } while (len > 0);
-        }
+        Status = open_fileref_child(Vcb, me->fileref, &dc->name, TRUE, TRUE, dc->index == 0 ? TRUE : FALSE, PagedPool, &fr, Irp);
         
-        b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp);
-        if (b) {
-            tp = next_tp;
-            
-            if (next_tp.item->key.obj_id > searchkey.obj_id || (next_tp.item->key.obj_id == searchkey.obj_id && next_tp.item->key.obj_type > searchkey.obj_type))
-                break;
+        if (!NT_SUCCESS(Status)) {
+            ERR("open_fileref_child returned %08x\n", Status);
+            ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
+            return Status;
         }
-    } while (b);
-    
-    if (me->fileref->fcb->type == BTRFS_TYPE_DIRECTORY && me->fileref->fcb->inode_item.st_size != 0) {
-        searchkey.obj_id = me->fileref->fcb->inode;
-        searchkey.obj_type = TYPE_DIR_INDEX;
-        searchkey.offset = 2;
         
-        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;
+        me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
+        if (!me2) {
+            ERR("out of memory\n");
+            ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
+            return STATUS_INSUFFICIENT_RESOURCES;
         }
         
-        do {
-            traverse_ptr next_tp;
-            
-            // FIXME - both lists are ordered; we can make this more efficient
-            
-            if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
-                BOOL found = FALSE;
-                
-                le = me->fileref->children.Flink;
-                
-                while (le != &me->fileref->children) {
-                    file_ref* fr = CONTAINING_RECORD(le, file_ref, list_entry);
-                    
-                    if (!fr->fcb->ads) {
-                        if (fr->index == tp.item->key.offset) {
-                            found = TRUE;
-                            break;
-                        } else if (fr->index > tp.item->key.offset)
-                            break;
-                    }
-                    
-                    le = le->Flink;
-                }
-                
-                if (!found) {
-                    DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
-                    
-                    if (tp.item->size < sizeof(DIR_ITEM)) {
-                        ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
-                        Status = STATUS_INTERNAL_ERROR;
-                        goto end;
-                    }
-                    
-                    if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->m + di->n) {
-                        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(DIR_ITEM) - 1 + di->m + di->n);
-                        Status = STATUS_INTERNAL_ERROR;
-                        goto end;
-                    }
-                    
-                    if (di->n == 0) {
-                        ERR("(%llx,%x,%llx): filename length was 0\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                        Status = STATUS_INTERNAL_ERROR;
-                        goto end;
-                    }
-                    
-                    if (di->key.obj_type == TYPE_INODE_ITEM || di->key.obj_type == TYPE_ROOT_ITEM) {
-                        ANSI_STRING utf8;
-                        fcb* fcb;
-                        file_ref* fr;
-                        ULONG stringlen;
-                        root* subvol;
-                        UINT64 inode;
-                        dir_child* dc = NULL;
-                        
-                        utf8.Length = utf8.MaximumLength = di->n;
-                        utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG);
-                        if (!utf8.Buffer) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            goto end;
-                        }
-                        
-                        RtlCopyMemory(utf8.Buffer, di->name, di->n);
-                        
-                        if (di->key.obj_type == TYPE_ROOT_ITEM) {
-                            LIST_ENTRY* le2;
-                            
-                            subvol = NULL;
-                            
-                            le2 = me->fileref->fcb->Vcb->roots.Flink;
-                            while (le2 != &me->fileref->fcb->Vcb->roots) {
-                                root* r2 = CONTAINING_RECORD(le2, root, list_entry);
-                                
-                                if (r2->id == di->key.obj_id) {
-                                    subvol = r2;
-                                    break;
-                                }
-                                
-                                le2 = le2->Flink;
-                            }
-                            
-                            if (!subvol) {
-                                ERR("could not find subvol %llx\n", di->key.obj_id);
-                                Status = STATUS_INTERNAL_ERROR;
-                                goto end;
-                            }
+        me2->fileref = fr;
+        me2->dummyfcb = NULL;
+        me2->dummyfileref = NULL;
+        me2->parent = me;
 
-                            inode = SUBVOL_ROOT_INODE;
-                        } else {
-                            subvol = me->fileref->fcb->subvol;
-                            inode = di->key.obj_id;
-                        }
-                        
-                        Status = open_fcb(me->fileref->fcb->Vcb, subvol, inode, di->type, &utf8, me->fileref->fcb, &fcb, PagedPool, Irp);
-                        
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("open_fcb returned %08x\n", Status);
-                            ExFreePool(utf8.Buffer);
-                            goto end;
-                        }
-                        
-                        fr = create_fileref();
-                        if (!fr) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            ExFreePool(utf8.Buffer);
-                            free_fcb(fcb);
-                            goto end;
-                        }
-                        
-                        fr->fcb = fcb;
-                        fr->utf8 = utf8;
-                        
-                        Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, utf8.Buffer, utf8.Length);
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-                            free_fileref(fr);
-                            goto end;
-                        }
-                        
-                        fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
-                        if (!fr->filepart.Buffer) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            free_fileref(fr);
-                            goto end;
-                        }
-                        
-                        Status = RtlUTF8ToUnicodeN(fr->filepart.Buffer, stringlen, &stringlen, utf8.Buffer, utf8.Length);
-                        
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                            free_fileref(fr);
-                            goto end;
-                        }
-                        
-                        fr->filepart.Length = fr->filepart.MaximumLength = stringlen;
-                        
-                        Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE);
-                        
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-                            free_fileref(fr);
-                            goto end;
-                        }
-                        
-                        fr->parent = me->fileref;
-
-                        fr->index = tp.item->key.offset;
-                        increase_fileref_refcount(me->fileref);
-                        
-                        Status = add_dir_child(me->fileref->fcb, di->key.obj_type == TYPE_ROOT_ITEM ? subvol->id : fr->fcb->inode,
-                                               di->key.obj_type == TYPE_ROOT_ITEM ? TRUE : FALSE, fr->index, &utf8, &fr->filepart, &fr->filepart_uc, BTRFS_TYPE_DIRECTORY, &dc);
-                        if (!NT_SUCCESS(Status))
-                            WARN("add_dir_child returned %08x\n", Status);
-                        
-                        fr->dc = dc;
-                        dc->fileref = fr;
-                        
-                        insert_fileref_child(fr->parent, fr, FALSE);
-                        
-                        if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
-                            fr->fcb->fileref = fr;
-                        
-                        me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
-                        if (!me2) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            free_fileref(fr);
-                            goto end;
-                        }
-                        
-                        me2->fileref = fr;
-                        me2->dummyfcb = NULL;
-                        me2->dummyfileref = NULL;
-                        me2->parent = me;
-                        
-                        InsertHeadList(&me->list_entry, &me2->list_entry);
-                    } else {
-                        ERR("unrecognized key (%llx,%x,%llx)\n", di->key.obj_id, di->key.obj_type, di->key.offset);
-                        Status = STATUS_INTERNAL_ERROR;
-                        goto end;
-                    }
-                }
-            }
-            
-            b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp);
-            if (b) {
-                tp = next_tp;
-                
-                if (next_tp.item->key.obj_id > searchkey.obj_id || (next_tp.item->key.obj_id == searchkey.obj_id && next_tp.item->key.obj_type > searchkey.obj_type))
-                    break;
-            }
-        } while (b);
+        InsertHeadList(&me->list_entry, &me2->list_entry);
+        
+        le = le->Flink;
     }
     
-    Status = STATUS_SUCCESS;
+    ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock);
     
-end:
-    ExReleaseResourceLite(&me->fileref->nonpaged->children_lock);
-    
-    return Status;
+    return STATUS_SUCCESS;
 }
 
 void remove_dir_child_from_hash_lists(fcb* fcb, dir_child* dc) {
@@ -917,7 +542,7 @@
     RemoveEntryList(&dc->list_entry_hash_uc);
 }
 
-static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     LIST_ENTRY move_list, *le;
     move_entry* me;
@@ -927,6 +552,9 @@
     
     InitializeListHead(&move_list);
     
+    KeQuerySystemTime(&time);
+    win_time_to_unix(time, &now);
+    
     me = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG);
     
     if (!me) {
@@ -952,7 +580,7 @@
         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, Irp);
+            Status = add_children_to_move_list(fileref->fcb->Vcb, me, Irp);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("add_children_to_move_list returned %08x\n", Status);
@@ -965,6 +593,8 @@
         le = le->Flink;
     }
     
+    send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED);
+    
     // loop through list and create new inodes
     
     le = move_list.Flink;
@@ -975,7 +605,7 @@
             if (!me->dummyfcb) {
                 ULONG defda;
                 BOOL inserted = FALSE;
-                LIST_ENTRY* le;
+                LIST_ENTRY* le3;
                 
                 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, TRUE);
                 
@@ -1009,8 +639,9 @@
                     me->fileref->fcb->inode = InterlockedIncrement64(&destdir->fcb->subvol->lastinode);
                     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, Irp);
+                    defda = get_file_attributes(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, me->fileref->fcb->inode,
+                                                me->fileref->fcb->type, me->fileref->dc && me->fileref->dc->name.Length >= sizeof(WCHAR) && me->fileref->dc->name.Buffer[0] == '.',
+                                                TRUE, Irp);
                     
                     me->fileref->fcb->sd_dirty = !!me->fileref->fcb->sd;
                     me->fileref->fcb->atts_changed = defda != me->fileref->fcb->atts;
@@ -1019,13 +650,20 @@
                     me->fileref->fcb->ea_changed = !!me->fileref->fcb->ea_xattr.Buffer;
                     me->fileref->fcb->inode_item_changed = TRUE;
                     
+                    if (le == move_list.Flink) { // first entry
+                        me->fileref->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
+                        me->fileref->fcb->inode_item.sequence++;
+
+                        if (!ccb->user_set_change_time)
+                            me->fileref->fcb->inode_item.st_ctime = now;
+                    }
+                    
                     le2 = me->fileref->fcb->extents.Flink;
                     while (le2 != &me->fileref->fcb->extents) {
                         extent* ext = CONTAINING_RECORD(le2, extent, list_entry);
                         
-                        if (!ext->ignore && ext->datalen >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2) &&
-                            (ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC)) {
-                            EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
+                        if (!ext->ignore && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
+                            EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
                                         
                             if (ed2->size != 0) {
                                 chunk* c = get_chunk_from_address(me->fileref->fcb->Vcb, ed2->address);
@@ -1058,17 +696,17 @@
                 InsertHeadList(&me->fileref->fcb->list_entry, &me->dummyfcb->list_entry);
                 RemoveEntryList(&me->fileref->fcb->list_entry);
                 
-                le = destdir->fcb->subvol->fcbs.Flink;
-                while (le != &destdir->fcb->subvol->fcbs) {
-                    fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
+                le3 = destdir->fcb->subvol->fcbs.Flink;
+                while (le3 != &destdir->fcb->subvol->fcbs) {
+                    fcb* fcb = CONTAINING_RECORD(le3, struct _fcb, list_entry);
                     
                     if (fcb->inode > me->fileref->fcb->inode) {
-                        InsertHeadList(le->Blink, &me->fileref->fcb->list_entry);
+                        InsertHeadList(le3->Blink, &me->fileref->fcb->list_entry);
                         inserted = TRUE;
                         break;
                     }
                     
-                    le = le->Flink;
+                    le3 = le3->Flink;
                 }
                 
                 if (!inserted)
@@ -1077,8 +715,7 @@
                 InsertTailList(&me->fileref->fcb->Vcb->all_fcbs, &me->dummyfcb->list_entry_all);
                 
                 while (!IsListEmpty(&me->fileref->fcb->hardlinks)) {
-                    LIST_ENTRY* le = RemoveHeadList(&me->fileref->fcb->hardlinks);
-                    hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
+                    hardlink* hl = CONTAINING_RECORD(RemoveHeadList(&me->fileref->fcb->hardlinks), hardlink, list_entry);
                     
                     if (hl->name.Buffer)
                         ExFreePool(hl->name.Buffer);
@@ -1119,9 +756,6 @@
         le = le->Flink;
     }
     
-    KeQuerySystemTime(&time);
-    win_time_to_unix(time, &now);
-    
     fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
     fileref->fcb->subvol->root_item.ctime = now;
     
@@ -1134,7 +768,7 @@
         
         me = CONTAINING_RECORD(le, move_entry, list_entry);
         
-        me->dummyfileref = create_fileref();
+        me->dummyfileref = create_fileref(fileref->fcb->Vcb);
         if (!me->dummyfileref) {
             ERR("out of memory\n");
             Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -1147,51 +781,26 @@
             me->dummyfileref->fcb = me->dummyfcb;
         
         InterlockedIncrement(&me->dummyfileref->fcb->refcount);
-
-        me->dummyfileref->filepart = me->fileref->filepart;
         
-        if (le == move_list.Flink) // first item
-            me->fileref->filepart.Length = me->fileref->filepart.MaximumLength = fnus->Length;
-        else
-            me->fileref->filepart.MaximumLength = me->fileref->filepart.Length;
-        
-        me->fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->filepart.MaximumLength, ALLOC_TAG);
-        
-        if (!me->fileref->filepart.Buffer) {
-            ERR("out of memory\n");
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto end;
-        }
-        
-        RtlCopyMemory(me->fileref->filepart.Buffer, le == move_list.Flink ? fnus->Buffer : me->dummyfileref->filepart.Buffer, me->fileref->filepart.Length);
-        
-        Status = RtlUpcaseUnicodeString(&me->fileref->filepart_uc, &me->fileref->filepart, TRUE);
-        if (!NT_SUCCESS(Status)) {
-            ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-            goto end;
-        }
-        
-        me->dummyfileref->utf8 = me->fileref->utf8;
         me->dummyfileref->oldutf8 = me->fileref->oldutf8;
+        me->dummyfileref->oldindex = me->fileref->dc->index;
         
         if (le == move_list.Flink) {
-            if (me->fileref->utf8.Length != utf8->Length || RtlCompareMemory(me->fileref->utf8.Buffer, utf8->Buffer, utf8->Length) != utf8->Length)
+            if (me->fileref->dc->utf8.Length != utf8->Length || RtlCompareMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length) != utf8->Length)
                 name_changed = TRUE;
             
-            me->fileref->utf8.Length = me->fileref->utf8.MaximumLength = utf8->Length;
-        } else
-            me->fileref->utf8.MaximumLength = me->fileref->utf8.Length;
-        
-        if (me->fileref->utf8.MaximumLength > 0) {
-            me->fileref->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->utf8.MaximumLength, ALLOC_TAG);
-            
-            if (!me->fileref->utf8.Buffer) {
-                ERR("out of memory\n");
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto end;
+            if (!me->dummyfileref->oldutf8.Buffer) {
+                me->dummyfileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->dc->utf8.Length, ALLOC_TAG);
+                if (!me->dummyfileref->oldutf8.Buffer) {
+                    ERR("out of memory\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto end;
+                }
+                
+                RtlCopyMemory(me->dummyfileref->oldutf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length);
+                
+                me->dummyfileref->oldutf8.Length = me->dummyfileref->oldutf8.MaximumLength = me->fileref->dc->utf8.Length;
             }
-            
-            RtlCopyMemory(me->fileref->utf8.Buffer, le == move_list.Flink ? utf8->Buffer : me->dummyfileref->utf8.Buffer, me->fileref->utf8.Length);
         }
         
         me->dummyfileref->delete_on_close = me->fileref->delete_on_close;
@@ -1202,10 +811,10 @@
         
         me->dummyfileref->parent = me->parent ? me->parent->dummyfileref : origparent;
         increase_fileref_refcount(me->dummyfileref->parent);
-        
-        me->dummyfileref->index = me->fileref->index;
 
-        insert_fileref_child(me->dummyfileref->parent, me->dummyfileref, TRUE);
+        ExAcquireResourceExclusiveLite(&me->dummyfileref->parent->nonpaged->children_lock, TRUE);
+        InsertTailList(&me->dummyfileref->parent->children, &me->dummyfileref->list_entry);
+        ExReleaseResourceLite(&me->dummyfileref->parent->nonpaged->children_lock);
        
         me->dummyfileref->debug_desc = me->fileref->debug_desc;
         
@@ -1215,16 +824,8 @@
         if (!me->parent) {
             RemoveEntryList(&me->fileref->list_entry);
             
-            free_fileref(me->fileref->parent);
-            
             increase_fileref_refcount(destdir);
             
-            Status = fcb_get_last_dir_index(destdir->fcb, &me->fileref->index, Irp);
-            if (!NT_SUCCESS(Status)) {
-                ERR("fcb_get_last_dir_index returned %08x\n", Status);
-                goto end;
-            }
-            
             if (me->fileref->dc) {
                 // remove from old parent
                 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
@@ -1232,6 +833,14 @@
                 remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc);
                 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
                 
+                me->fileref->parent->fcb->inode_item.st_size -= me->fileref->dc->utf8.Length * 2;
+                me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
+                me->fileref->parent->fcb->inode_item.sequence++;
+                me->fileref->parent->fcb->inode_item.st_ctime = now;
+                me->fileref->parent->fcb->inode_item.st_mtime = now;
+                me->fileref->parent->fcb->inode_item_changed = TRUE;
+                mark_fcb_dirty(me->fileref->parent->fcb);
+                
                 if (name_changed) {
                     ExFreePool(me->fileref->dc->utf8.Buffer);
                     ExFreePool(me->fileref->dc->name.Buffer);
@@ -1244,7 +853,10 @@
                         goto end;
                     }
                     
-                    me->fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->filepart.Length, ALLOC_TAG);
+                    me->fileref->dc->utf8.Length = me->fileref->dc->utf8.MaximumLength = utf8->Length;
+                    RtlCopyMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length);
+                    
+                    me->fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus->Length, ALLOC_TAG);
                     if (!me->fileref->dc->name.Buffer) {
                         ERR("out of memory\n");
                         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -1251,39 +863,48 @@
                         goto end;
                     }
                     
-                    me->fileref->dc->name_uc.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->filepart_uc.Length, ALLOC_TAG);
-                    if (!me->fileref->dc->name_uc.Buffer) {
-                        ERR("out of memory\n");
-                        Status = STATUS_INSUFFICIENT_RESOURCES;
+                    me->fileref->dc->name.Length = me->fileref->dc->name.MaximumLength = fnus->Length;
+                    RtlCopyMemory(me->fileref->dc->name.Buffer, fnus->Buffer, fnus->Length);
+                    
+                    Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
                         goto end;
                     }
                     
-                    me->fileref->dc->utf8.Length = me->fileref->dc->utf8.MaximumLength = utf8->Length;
-                    RtlCopyMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length);
-                    
-                    me->fileref->dc->name.Length = me->fileref->dc->name.MaximumLength = me->fileref->filepart.Length;
-                    RtlCopyMemory(me->fileref->dc->name.Buffer, me->fileref->filepart.Buffer, me->fileref->filepart.Length);
-                    
-                    me->fileref->dc->name_uc.Length = me->fileref->dc->name_uc.MaximumLength = me->fileref->filepart_uc.Length;
-                    RtlCopyMemory(me->fileref->dc->name_uc.Buffer, me->fileref->filepart_uc.Buffer, me->fileref->filepart_uc.Length);
-                    
                     me->fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)me->fileref->dc->name.Buffer, me->fileref->dc->name.Length);
                     me->fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)me->fileref->dc->name_uc.Buffer, me->fileref->dc->name_uc.Length);
                 }
                 
+                if (me->fileref->dc->key.obj_type == TYPE_INODE_ITEM)
+                    me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
+                
                 // add to new parent
+                
                 ExAcquireResourceExclusiveLite(&destdir->fcb->nonpaged->dir_children_lock, TRUE);
+                
+                if (IsListEmpty(&destdir->fcb->dir_children_index))
+                    me->fileref->dc->index = 2;
+                else {
+                    dir_child* dc2 = CONTAINING_RECORD(destdir->fcb->dir_children_index.Blink, dir_child, list_entry_index);
+                    
+                    me->fileref->dc->index = max(2, dc2->index + 1);
+                }
+                
                 InsertTailList(&destdir->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
                 insert_dir_child_into_hash_lists(destdir->fcb, me->fileref->dc);
                 ExReleaseResourceLite(&destdir->fcb->nonpaged->dir_children_lock);
             }
             
+            free_fileref(fileref->fcb->Vcb, me->fileref->parent);
             me->fileref->parent = destdir;
             
-            insert_fileref_child(me->fileref->parent, me->fileref, TRUE);
+            ExAcquireResourceExclusiveLite(&me->fileref->parent->nonpaged->children_lock, TRUE);
+            InsertTailList(&me->fileref->parent->children, &me->fileref->list_entry);
+            ExReleaseResourceLite(&me->fileref->parent->nonpaged->children_lock);
             
             TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
-            me->fileref->parent->fcb->inode_item.st_size += me->fileref->utf8.Length * 2;
+            me->fileref->parent->fcb->inode_item.st_size += me->fileref->dc->utf8.Length * 2;
             TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size);
             me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation;
             me->fileref->parent->fcb->inode_item.sequence++;
@@ -1291,6 +912,31 @@
             me->fileref->parent->fcb->inode_item.st_mtime = now;
             me->fileref->parent->fcb->inode_item_changed = TRUE;
             mark_fcb_dirty(me->fileref->parent->fcb);
+        } else {
+            if (me->fileref->dc) {
+                ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
+                RemoveEntryList(&me->fileref->dc->list_entry_index);
+                
+                if (!me->fileref->fcb->ads)
+                    remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc);
+                
+                ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock);
+                
+                if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE) {
+                    ExAcquireResourceExclusiveLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock, TRUE);
+                    
+                    if (me->fileref->fcb->ads)
+                        InsertHeadList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
+                    else {
+                        me->fileref->dc->key.obj_id = me->fileref->fcb->inode;
+                        
+                        InsertTailList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index);
+                        insert_dir_child_into_hash_lists(me->parent->fileref->fcb, me->fileref->dc);
+                    }
+                    
+                    ExReleaseResourceLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock);
+                }
+            }
         }
 
         if (me->fileref->fcb->inode == SUBVOL_ROOT_INODE)
@@ -1313,9 +959,9 @@
             }
             
             hl->parent = me->fileref->parent->fcb->inode;
-            hl->index = me->fileref->index;
+            hl->index = me->fileref->dc->index;
             
-            hl->utf8.Length = hl->utf8.MaximumLength = me->fileref->utf8.Length;
+            hl->utf8.Length = hl->utf8.MaximumLength = me->fileref->dc->utf8.Length;
             hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
             if (!hl->utf8.Buffer) {
                 ERR("out of memory\n");
@@ -1324,9 +970,9 @@
                 goto end;
             }
             
-            RtlCopyMemory(hl->utf8.Buffer, me->fileref->utf8.Buffer, me->fileref->utf8.Length);
+            RtlCopyMemory(hl->utf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length);
             
-            hl->name.Length = hl->name.MaximumLength = me->fileref->filepart.Length;
+            hl->name.Length = hl->name.MaximumLength = me->fileref->dc->name.Length;
             hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
             if (!hl->name.Buffer) {
                 ERR("out of memory\n");
@@ -1336,7 +982,7 @@
                 goto end;
             }
             
-            RtlCopyMemory(hl->name.Buffer, me->fileref->filepart.Buffer, me->fileref->filepart.Length);
+            RtlCopyMemory(hl->name.Buffer, me->fileref->dc->name.Buffer, me->fileref->dc->name.Length);
             
             InsertTailList(&me->fileref->fcb->hardlinks, &hl->list_entry);
         }
@@ -1367,7 +1013,6 @@
     destdir->fcb->subvol->root_item.ctime = now;
     
     me = CONTAINING_RECORD(move_list.Flink, move_entry, list_entry);
-    send_notification_fileref(me->dummyfileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED);
     send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
     send_notification_fileref(me->dummyfileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
     send_notification_fileref(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
@@ -1383,9 +1028,9 @@
             free_fcb(me->dummyfcb);
         
         if (me->dummyfileref)
-            free_fileref(me->dummyfileref);
+            free_fileref(fileref->fcb->Vcb, me->dummyfileref);
         
-        free_fileref(me->fileref);
+        free_fileref(fileref->fcb->Vcb, me->fileref);
         
         ExFreePool(me);
     }
@@ -1487,9 +1132,8 @@
     fcb *fcb = FileObject->FsContext;
     ccb* ccb = FileObject->FsContext2;
     file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
-    UINT64 index;
     WCHAR* fn;
-    ULONG fnlen, utf8len;
+    ULONG fnlen, utf8len, origutf8len;
     UNICODE_STRING fnus;
     ANSI_STRING utf8;
     NTSTATUS Status;
@@ -1536,8 +1180,10 @@
     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
     
     if (fcb->ads) {
-        FIXME("FIXME - renaming streams\n"); // FIXME
-        Status = STATUS_NOT_IMPLEMENTED;
+        // MSDN says that NTFS data streams can be renamed (https://msdn.microsoft.com/en-us/library/windows/hardware/ff540344.aspx),
+        // but if you try it always seems to return STATUS_INVALID_PARAMETER. There is a function in ntfs.sys called NtfsStreamRename,
+        // but it never seems to get invoked... If you know what's going on here, I'd appreciate it if you let me know.
+        Status = STATUS_INVALID_PARAMETER;
         goto end;
     }
     
@@ -1546,6 +1192,8 @@
     
     TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer);
     
+    origutf8len = fileref->dc->utf8.Length;
+    
     Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR));
     if (!NT_SUCCESS(Status))
         goto end;
@@ -1595,7 +1243,7 @@
         }
         
         if (fileref == oldfileref || oldfileref->deleted) {
-            free_fileref(oldfileref);
+            free_fileref(Vcb, oldfileref);
             oldfileref = NULL;
         }
     }
@@ -1624,7 +1272,7 @@
         if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL,
                            IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
             SeReleaseSubjectContext(&subjcont);
-            WARN("SeAccessCheck failed, returning %08x\n", Status);
+            TRACE("SeAccessCheck failed, returning %08x\n", Status);
             goto end;
         }
 
@@ -1638,7 +1286,7 @@
     }
     
     if (fileref->parent->fcb->subvol != related->fcb->subvol && fileref->fcb->subvol == fileref->parent->fcb->subvol) {
-        Status = move_across_subvols(fileref, related, &utf8, &fnus, Irp, &rollback);
+        Status = move_across_subvols(fileref, ccb, related, &utf8, &fnus, Irp, &rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("move_across_subvols returned %08x\n", Status);
         }
@@ -1646,51 +1294,72 @@
     }
     
     if (related == fileref->parent) { // keeping file in same directory
-        UNICODE_STRING fnus2, oldfn, newfn;
+        UNICODE_STRING oldfn, newfn;
         USHORT name_offset;
-        ULONG oldutf8len;
+        ULONG reqlen, oldutf8len;
         
-        fnus2.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
-        if (!fnus2.Buffer) {
+        oldfn.Length = oldfn.MaximumLength = 0;
+        
+        Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen);
+        if (Status != STATUS_BUFFER_OVERFLOW) {
+            ERR("fileref_get_filename returned %08x\n", Status);
+            goto end;
+        }
+        
+        oldfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
+        if (!oldfn.Buffer) {
             ERR("out of memory\n");
             Status = STATUS_INSUFFICIENT_RESOURCES;
             goto end;
         }
         
-        Status = fileref_get_filename(fileref, &oldfn, &name_offset);
+        oldfn.MaximumLength = reqlen;
+        
+        Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen);
         if (!NT_SUCCESS(Status)) {
             ERR("fileref_get_filename returned %08x\n", Status);
+            ExFreePool(oldfn.Buffer);
             goto end;
         }
         
-        fnus2.Length = fnus2.MaximumLength = fnus.Length;
-        RtlCopyMemory(fnus2.Buffer, fnus.Buffer, fnus.Length);
+        oldutf8len = fileref->dc->utf8.Length;
         
-        oldutf8len = fileref->utf8.Length;
+        if (!fileref->created && !fileref->oldutf8.Buffer) {
+            fileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG);
+            if (!fileref->oldutf8.Buffer) {
+                ERR("out of memory\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto end;
+            }
+            
+            fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = fileref->dc->utf8.Length;
+            RtlCopyMemory(fileref->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
+        }
         
-        if (!fileref->created && !fileref->oldutf8.Buffer)
-            fileref->oldutf8 = fileref->utf8;
-        else
-            ExFreePool(fileref->utf8.Buffer);
+        TRACE("renaming %.*S to %.*S\n", fileref->dc->name.Length / sizeof(WCHAR), fileref->dc->name.Buffer, fnus.Length / sizeof(WCHAR), fnus.Buffer);
         
-        TRACE("renaming %.*S to %.*S\n", fileref->filepart.Length / sizeof(WCHAR), fileref->filepart.Buffer, fnus2.Length / sizeof(WCHAR), fnus.Buffer);
+        newfn.Length = newfn.MaximumLength = 0;
         
-        fileref->utf8 = utf8;
-        fileref->filepart = fnus2;
-        
-        Status = fileref_get_filename(fileref, &newfn, &name_offset);
-        if (!NT_SUCCESS(Status)) {
+        Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen);
+        if (Status != STATUS_BUFFER_OVERFLOW) {
             ERR("fileref_get_filename returned %08x\n", Status);
             ExFreePool(oldfn.Buffer);
             goto end;
         }
         
-        if (fileref->filepart_uc.Buffer)
-            ExFreePool(fileref->filepart_uc.Buffer);
+        newfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
+        if (!newfn.Buffer) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            ExFreePool(oldfn.Buffer);
+            goto end;
+        }
         
-        Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
+        newfn.MaximumLength = reqlen;
+        
+        Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen);
         if (!NT_SUCCESS(Status)) {
-            ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+            ERR("fileref_get_filename returned %08x\n", Status);
             ExFreePool(oldfn.Buffer);
             ExFreePool(newfn.Buffer);
             goto end;
@@ -1710,34 +1379,36 @@
                 ERR("out of memory\n");
                 Status = STATUS_INSUFFICIENT_RESOURCES;
                 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
+                ExFreePool(oldfn.Buffer);
+                ExFreePool(newfn.Buffer);
                 goto end;
             }
             
-            fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.Length, ALLOC_TAG);
+            fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
+            RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
+            
+            fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
             if (!fileref->dc->name.Buffer) {
                 ERR("out of memory\n");
                 Status = STATUS_INSUFFICIENT_RESOURCES;
                 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
+                ExFreePool(oldfn.Buffer);
+                ExFreePool(newfn.Buffer);
                 goto end;
             }
             
-            fileref->dc->name_uc.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart_uc.Length, ALLOC_TAG);
-            if (!fileref->dc->name_uc.Buffer) {
-                ERR("out of memory\n");
-                Status = STATUS_INSUFFICIENT_RESOURCES;
+            fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length;
+            RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length);
+
+            Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE);
+            if (!NT_SUCCESS(Status)) {
+                ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
                 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
+                ExFreePool(oldfn.Buffer);
+                ExFreePool(newfn.Buffer);
                 goto end;
             }
             
-            fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
-            RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
-            
-            fileref->dc->name.Length = fileref->dc->name.MaximumLength = fileref->filepart.Length;
-            RtlCopyMemory(fileref->dc->name.Buffer, fileref->filepart.Buffer, fileref->filepart.Length);
-            
-            fileref->dc->name_uc.Length = fileref->dc->name_uc.MaximumLength = fileref->filepart_uc.Length;
-            RtlCopyMemory(fileref->dc->name_uc.Buffer, fileref->filepart_uc.Buffer, fileref->filepart_uc.Length);
-            
             remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
             
             fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name.Buffer, fileref->dc->name.Length);
@@ -1751,15 +1422,17 @@
         KeQuerySystemTime(&time);
         win_time_to_unix(time, &now);
         
-        fcb->inode_item.transid = Vcb->superblock.generation;
-        fcb->inode_item.sequence++;
+        if (fileref->parent->fcb->subvol == fcb->subvol || !(fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY)) {
+            fcb->inode_item.transid = Vcb->superblock.generation;
+            fcb->inode_item.sequence++;
+            
+            if (!ccb->user_set_change_time)
+                fcb->inode_item.st_ctime = now;
+            
+            fcb->inode_item_changed = TRUE;
+            mark_fcb_dirty(fcb);
+        }
         
-        if (!ccb->user_set_change_time)
-            fcb->inode_item.st_ctime = now;
-        
-        fcb->inode_item_changed = TRUE;
-        mark_fcb_dirty(fcb);
-        
         // update parent's INODE_ITEM
         
         related->fcb->inode_item.transid = Vcb->superblock.generation;
@@ -1789,16 +1462,15 @@
     // We move files by moving the existing fileref to the new directory, and
     // replacing it with a dummy fileref with the same original values, but marked as deleted.
     
-    fr2 = create_fileref();
+    send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED);
     
+    fr2 = create_fileref(Vcb);
+    
     fr2->fcb = fileref->fcb;
     fr2->fcb->refcount++;
     
-    fr2->filepart = fileref->filepart;
-    fr2->filepart_uc = fileref->filepart_uc;
-    fr2->utf8 = fileref->utf8;
     fr2->oldutf8 = fileref->oldutf8;
-    fr2->index = fileref->index;
+    fr2->oldindex = fileref->dc->index;
     fr2->delete_on_close = fileref->delete_on_close;
     fr2->deleted = TRUE;
     fr2->created = fileref->created;
@@ -1805,34 +1477,24 @@
     fr2->parent = fileref->parent;
     fr2->dc = NULL;
     
+    if (!fr2->oldutf8.Buffer) {
+        fr2->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG);
+        if (!fr2->oldutf8.Buffer) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
+        }
+        
+        RtlCopyMemory(fr2->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
+        
+        fr2->oldutf8.Length = fr2->oldutf8.MaximumLength = fileref->dc->utf8.Length;
+    }
+    
     if (fr2->fcb->type == BTRFS_TYPE_DIRECTORY)
         fr2->fcb->fileref = fr2;
-
-    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;
-    }
     
-    fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
-    if (!fileref->filepart.Buffer) {
-        ERR("out of memory\n");
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto end;
-    }
-    
-    fileref->filepart.Length = fileref->filepart.MaximumLength = fnus.Length;
-    RtlCopyMemory(fileref->filepart.Buffer, fnus.Buffer, fnus.Length);
-    
-    Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-        goto end;
-    }
-    
-    fileref->utf8 = utf8;
+    fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = 0;
     fileref->oldutf8.Buffer = NULL;
-    fileref->index = index;
     fileref->deleted = FALSE;
     fileref->created = TRUE;
     fileref->parent = related;
@@ -1842,8 +1504,6 @@
     RemoveEntryList(&fileref->list_entry);
     ExReleaseResourceLite(&fileref->parent->nonpaged->children_lock);
     
-    insert_fileref_child(related, fileref, TRUE);
-    
     mark_fileref_dirty(fr2);
     mark_fileref_dirty(fileref);
     
@@ -1854,7 +1514,7 @@
         remove_dir_child_from_hash_lists(fr2->parent->fcb, fileref->dc);
         ExReleaseResourceLite(&fr2->parent->fcb->nonpaged->dir_children_lock);
         
-        if (fileref->utf8.Length != fr2->utf8.Length || RtlCompareMemory(fileref->utf8.Buffer, fr2->utf8.Buffer, fr2->utf8.Length) != fr2->utf8.Length) {
+        if (fileref->dc->utf8.Length != utf8.Length || RtlCompareMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length) != utf8.Length) {
             // handle changed name
             
             ExFreePool(fileref->dc->utf8.Buffer);
@@ -1868,7 +1528,10 @@
                 goto end;
             }
             
-            fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.Length, ALLOC_TAG);
+            fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
+            RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
+            
+            fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
             if (!fileref->dc->name.Buffer) {
                 ERR("out of memory\n");
                 Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -1875,22 +1538,15 @@
                 goto end;
             }
             
-            fileref->dc->name_uc.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart_uc.Length, ALLOC_TAG);
-            if (!fileref->dc->name_uc.Buffer) {
-                ERR("out of memory\n");
-                Status = STATUS_INSUFFICIENT_RESOURCES;
+            fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length;
+            RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length);
+            
+            Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE);
+            if (!NT_SUCCESS(Status)) {
+                ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
                 goto end;
             }
             
-            fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length;
-            RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length);
-            
-            fileref->dc->name.Length = fileref->dc->name.MaximumLength = fileref->filepart.Length;
-            RtlCopyMemory(fileref->dc->name.Buffer, fileref->filepart.Buffer, fileref->filepart.Length);
-            
-            fileref->dc->name_uc.Length = fileref->dc->name_uc.MaximumLength = fileref->filepart_uc.Length;
-            RtlCopyMemory(fileref->dc->name_uc.Buffer, fileref->filepart_uc.Buffer, fileref->filepart_uc.Length);
-            
             fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name.Buffer, fileref->dc->name.Length);
             fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length);
         }
@@ -1897,11 +1553,24 @@
         
         // add to new parent
         ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, TRUE);
+        
+        if (IsListEmpty(&related->fcb->dir_children_index))
+            fileref->dc->index = 2;
+        else {
+            dir_child* dc2 = CONTAINING_RECORD(related->fcb->dir_children_index.Blink, dir_child, list_entry_index);
+            
+            fileref->dc->index = max(2, dc2->index + 1);
+        }
+        
         InsertTailList(&related->fcb->dir_children_index, &fileref->dc->list_entry_index);
         insert_dir_child_into_hash_lists(related->fcb, fileref->dc);
         ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock);
     }
     
+    ExAcquireResourceExclusiveLite(&related->nonpaged->children_lock, TRUE);
+    InsertTailList(&related->children, &fileref->list_entry);
+    ExReleaseResourceLite(&related->nonpaged->children_lock);
+    
     if (fcb->inode_item.st_nlink > 1) {
         // add new hardlink entry to fcb
         
@@ -1913,9 +1582,9 @@
         }
         
         hl->parent = related->fcb->inode;
-        hl->index = index;
+        hl->index = fileref->dc->index;
         
-        hl->name.Length = hl->name.MaximumLength = fileref->filepart.Length;
+        hl->name.Length = hl->name.MaximumLength = fnus.Length;
         hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
         
         if (!hl->name.Buffer) {
@@ -1925,9 +1594,9 @@
             goto end;
         }
         
-        RtlCopyMemory(hl->name.Buffer, fileref->filepart.Buffer, fileref->filepart.Length);
+        RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
         
-        hl->utf8.Length = hl->utf8.MaximumLength = fileref->utf8.Length;
+        hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
         hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
         
         if (!hl->utf8.Buffer) {
@@ -1938,7 +1607,7 @@
             goto end;
         }
         
-        RtlCopyMemory(hl->utf8.Buffer, fileref->utf8.Buffer, fileref->utf8.Length);
+        RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
         
         InsertTailList(&fcb->hardlinks, &hl->list_entry);
     }
@@ -1949,7 +1618,7 @@
     while (le != &fcb->hardlinks) {
         hl = CONTAINING_RECORD(le, hardlink, list_entry);
         
-        if (hl->parent == fr2->parent->fcb->inode && hl->index == fr2->index) {
+        if (hl->parent == fr2->parent->fcb->inode && hl->index == fr2->oldindex) {
             RemoveEntryList(&hl->list_entry);
             
             if (hl->utf8.Buffer)
@@ -1970,15 +1639,17 @@
     KeQuerySystemTime(&time);
     win_time_to_unix(time, &now);
     
-    fcb->inode_item.transid = Vcb->superblock.generation;
-    fcb->inode_item.sequence++;
+    if (fileref->parent->fcb->subvol == fcb->subvol || !(fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY)) {
+        fcb->inode_item.transid = Vcb->superblock.generation;
+        fcb->inode_item.sequence++;
+        
+        if (!ccb->user_set_change_time)
+            fcb->inode_item.st_ctime = now;
+        
+        fcb->inode_item_changed = TRUE;
+        mark_fcb_dirty(fcb);
+    }
     
-    if (!ccb->user_set_change_time)
-        fcb->inode_item.st_ctime = now;
-    
-    fcb->inode_item_changed = TRUE;
-    mark_fcb_dirty(fcb);
-    
     // update new parent's INODE_ITEM
     
     related->fcb->inode_item.transid = Vcb->superblock.generation;
@@ -1996,18 +1667,17 @@
     
     fr2->parent->fcb->inode_item.transid = Vcb->superblock.generation;
     TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size);
-    fr2->parent->fcb->inode_item.st_size -= 2 * fr2->utf8.Length;
+    fr2->parent->fcb->inode_item.st_size -= 2 * origutf8len;
     TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size);
     fr2->parent->fcb->inode_item.sequence++;
     fr2->parent->fcb->inode_item.st_ctime = now;
     fr2->parent->fcb->inode_item.st_mtime = now;
     
-    free_fileref(fr2);
+    free_fileref(Vcb, fr2);
     
     fr2->parent->fcb->inode_item_changed = TRUE;
     mark_fcb_dirty(fr2->parent->fcb);
     
-    send_notification_fileref(fr2, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED);
     send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
     send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
     send_notification_fileref(fr2->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
@@ -2016,16 +1686,16 @@
 
 end:
     if (oldfileref)
-        free_fileref(oldfileref);
+        free_fileref(Vcb, oldfileref);
     
     if (!NT_SUCCESS(Status) && related)
-        free_fileref(related);
+        free_fileref(Vcb, related);
     
     if (!NT_SUCCESS(Status) && fr2)
-        free_fileref(fr2);
+        free_fileref(Vcb, fr2);
     
     if (NT_SUCCESS(Status))
-        clear_rollback(Vcb, &rollback);
+        clear_rollback(&rollback);
     else
         do_rollback(Vcb, &rollback);
 
@@ -2036,7 +1706,7 @@
     return Status;
 }
 
-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 STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT64 end, fcb* fcb, file_ref* fileref, PFILE_OBJECT FileObject, BOOL advance_only) {
     LARGE_INTEGER time;
     BTRFS_TIME now;
     CC_FILE_SIZES ccfs;
@@ -2143,7 +1813,7 @@
     }
     
     if (fcb->ads) {
-        Status = stream_set_end_of_file_information(Vcb, feofi->EndOfFile.QuadPart, fcb, fileref, FileObject, advance_only, &rollback);
+        Status = stream_set_end_of_file_information(Vcb, feofi->EndOfFile.QuadPart, fcb, fileref, FileObject, advance_only);
         goto end;
     }
     
@@ -2210,7 +1880,7 @@
 
 end:
     if (NT_SUCCESS(Status))
-        clear_rollback(Vcb, &rollback);
+        clear_rollback(&rollback);
     else
         do_rollback(Vcb, &rollback);
 
@@ -2224,18 +1894,7 @@
     return Status;
 }
 
-// static NTSTATUS STDCALL set_allocation_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
-//     FILE_ALLOCATION_INFORMATION* fai = (FILE_ALLOCATION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
-//     fcb* fcb = FileObject->FsContext;
-//     
-//     FIXME("FIXME\n");
-//     ERR("fcb = %p (%.*S)\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
-//     ERR("AllocationSize = %llx\n", fai->AllocationSize.QuadPart);
-//     
-//     return STATUS_NOT_IMPLEMENTED;
-// }
-
-static NTSTATUS STDCALL set_position_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
+static NTSTATUS STDCALL set_position_information(PFILE_OBJECT FileObject, PIRP Irp) {
     FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
     
     TRACE("setting the position on %S to %llx\n", file_desc(FileObject), fpi->CurrentByteOffset.QuadPart);
@@ -2252,7 +1911,6 @@
     fcb *fcb = FileObject->FsContext, *tfofcb, *parfcb;
     ccb* ccb = FileObject->FsContext2;
     file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
-    UINT64 index;
     WCHAR* fn;
     ULONG fnlen, utf8len;
     UNICODE_STRING fnus;
@@ -2376,7 +2034,7 @@
                 goto end;
             }
         } else {
-            free_fileref(oldfileref);
+            free_fileref(Vcb, oldfileref);
             oldfileref = NULL;
         }
     }
@@ -2395,7 +2053,7 @@
     if (!SeAccessCheck(related->fcb->sd, &subjcont, FALSE, FILE_ADD_FILE, 0, NULL,
                        IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
         SeReleaseSubjectContext(&subjcont);
-        WARN("SeAccessCheck failed, returning %08x\n", Status);
+        TRACE("SeAccessCheck failed, returning %08x\n", Status);
         goto end;
     }
 
@@ -2413,7 +2071,7 @@
         if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL,
                            IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
             SeReleaseSubjectContext(&subjcont);
-            WARN("SeAccessCheck failed, returning %08x\n", Status);
+            TRACE("SeAccessCheck failed, returning %08x\n", Status);
             goto end;
         }
 
@@ -2426,46 +2084,24 @@
         }
     }
     
-    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;
-    }
+    fr2 = create_fileref(Vcb);
     
-    fr2 = create_fileref();
-    
     fr2->fcb = fcb;
     fcb->refcount++;
     
-    fr2->utf8 = utf8;
-    fr2->index = index;
     fr2->created = TRUE;
     fr2->parent = related;
-    
-    fr2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
-    if (!fr2->filepart.Buffer) {
-        ERR("out of memory\n");
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto end;
-    }
-    
-    fr2->filepart.Length = fr2->filepart.MaximumLength = fnus.Length;
-    RtlCopyMemory(fr2->filepart.Buffer, fnus.Buffer, fnus.Length);
-    
-    Status = RtlUpcaseUnicodeString(&fr2->filepart_uc, &fr2->filepart, TRUE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-        goto end;
-    }
-      
-    insert_fileref_child(related, fr2, TRUE);
-    
-    Status = add_dir_child(related->fcb, fcb->inode, FALSE, index, &utf8, &fr2->filepart, &fr2->filepart_uc, fcb->type, &dc);
+
+    Status = add_dir_child(related->fcb, fcb->inode, FALSE, &utf8, &fnus, fcb->type, &dc);
     if (!NT_SUCCESS(Status))
         WARN("add_dir_child returned %08x\n", Status);
     
     fr2->dc = dc;
     dc->fileref = fr2;
+    
+    ExAcquireResourceExclusiveLite(&related->nonpaged->children_lock, TRUE);
+    InsertTailList(&related->children, &fr2->list_entry);
+    ExReleaseResourceLite(&related->nonpaged->children_lock);
 
     // add hardlink for existing fileref, if it's not there already
     if (IsListEmpty(&fcb->hardlinks)) {
@@ -2477,10 +2113,10 @@
         }
         
         hl->parent = fileref->parent->fcb->inode;
-        hl->index = fileref->index;
+        hl->index = fileref->dc->index;
         
-        hl->name.Length = hl->name.MaximumLength = fileref->filepart.Length;
-        hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
+        hl->name.Length = hl->name.MaximumLength = fnus.Length;
+        hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG);
         
         if (!hl->name.Buffer) {
             ERR("out of memory\n");
@@ -2489,9 +2125,9 @@
             goto end;
         }
         
-        RtlCopyMemory(hl->name.Buffer, fileref->filepart.Buffer, fileref->filepart.Length);
+        RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length);
         
-        hl->utf8.Length = hl->utf8.MaximumLength = fileref->utf8.Length;
+        hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length;
         hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
         
         if (!hl->utf8.Buffer) {
@@ -2502,7 +2138,7 @@
             goto end;
         }
         
-        RtlCopyMemory(hl->utf8.Buffer, fileref->utf8.Buffer, fileref->utf8.Length);
+        RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
         
         InsertTailList(&fcb->hardlinks, &hl->list_entry);
     }
@@ -2515,7 +2151,7 @@
     }
     
     hl->parent = related->fcb->inode;
-    hl->index = index;
+    hl->index = dc->index;
     
     hl->name.Length = hl->name.MaximumLength = fnus.Length;
     hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
@@ -2541,11 +2177,12 @@
     }
     
     RtlCopyMemory(hl->utf8.Buffer, utf8.Buffer, utf8.Length);
+    ExFreePool(utf8.Buffer);
     
     InsertTailList(&fcb->hardlinks, &hl->list_entry);
     
     mark_fileref_dirty(fr2);
-    free_fileref(fr2);
+    free_fileref(Vcb, fr2);
     
     // update inode's INODE_ITEM
     
@@ -2580,16 +2217,16 @@
     
 end:
     if (oldfileref)
-        free_fileref(oldfileref);
+        free_fileref(Vcb, oldfileref);
     
     if (!NT_SUCCESS(Status) && related)
-        free_fileref(related);
+        free_fileref(Vcb, related);
     
     if (!NT_SUCCESS(Status) && fr2)
-        free_fileref(fr2);
+        free_fileref(Vcb, fr2);
     
     if (NT_SUCCESS(Status))
-        clear_rollback(Vcb, &rollback);
+        clear_rollback(&rollback);
     else
         do_rollback(Vcb, &rollback);
 
@@ -2612,9 +2249,12 @@
     
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_set_information(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
     
     if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
@@ -2640,7 +2280,7 @@
     }
     
     if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation &&
-        (fcb->inode != SUBVOL_ROOT_INODE || IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation)) {
+        (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation))) {
         Status = STATUS_ACCESS_DENIED;
         goto end;
     }
@@ -2720,7 +2360,7 @@
         {
             TRACE("FilePositionInformation\n");
             
-            Status = set_position_information(Vcb, Irp, IrpSp->FileObject);
+            Status = set_position_information(IrpSp->FileObject, Irp);
             
             break;
         }
@@ -2760,7 +2400,6 @@
 
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
     
-exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -2843,7 +2482,7 @@
         
         fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length;
         fsi->NumberOfLinks = fileref->parent->fcb->inode_item.st_nlink;
-        fsi->Directory = S_ISDIR(fileref->parent->fcb->inode_item.st_mode);
+        fsi->Directory = FALSE;
     } else {
         fsi->AllocationSize.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
         fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
@@ -2884,7 +2523,7 @@
     
     fai->AccessFlags = GENERIC_READ;
     
-    return STATUS_NOT_IMPLEMENTED;
+    return STATUS_NOT_IMPLEMENTED; // FIXME
 }
 
 static NTSTATUS STDCALL fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) {
@@ -2933,100 +2572,85 @@
     return STATUS_SUCCESS;
 }
 
-typedef struct {
-    file_ref* fileref;
-    LIST_ENTRY list_entry;
-} fileref_list;
-
-NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset) {
-    LIST_ENTRY fr_list, *le;
+NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset, ULONG* preqlen) {
     file_ref* fr;
     NTSTATUS Status;
-    ULONG len, i;
+    ULONG reqlen = 0;
+    USHORT offset;
+    BOOL overflow = FALSE;
     
     // FIXME - we need a lock on filerefs' filepart
     
     if (fileref == fileref->fcb->Vcb->root_fileref) {
-        fn->Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(WCHAR), ALLOC_TAG);
-        if (!fn->Buffer) {
-            ERR("out of memory\n");
-            return STATUS_INSUFFICIENT_RESOURCES;
+        if (fn->MaximumLength >= sizeof(WCHAR)) {
+            fn->Buffer[0] = '\\';
+            fn->Length = sizeof(WCHAR);
+            
+            if (name_offset)
+                *name_offset = 0;
+            
+            return STATUS_SUCCESS;
+        } else {
+            if (preqlen)
+                *preqlen = sizeof(WCHAR);
+            fn->Length = 0;
+            
+            return STATUS_BUFFER_OVERFLOW;
         }
-        
-        fn->Length = fn->MaximumLength = sizeof(WCHAR);
-        fn->Buffer[0] = '\\';
-
-        if (name_offset)
-            *name_offset = 0;
-
-        return STATUS_SUCCESS;
     }
     
-    InitializeListHead(&fr_list);
-    
-    len = 0;
     fr = fileref;
+    offset = 0;
     
-    do {
-        fileref_list* frl;
+    while (fr->parent) {
+        USHORT movelen;
         
-        frl = ExAllocatePoolWithTag(PagedPool, sizeof(fileref_list), ALLOC_TAG);
-        if (!frl) {
-            ERR("out of memory\n");
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto end;
+        if (!overflow) {
+            if (fr->dc->name.Length + sizeof(WCHAR) > fn->MaximumLength - fn->Length)
+                overflow = TRUE;
         }
         
-        frl->fileref = fr;
-        InsertTailList(&fr_list, &frl->list_entry);
+        if (overflow)
+            movelen = fn->MaximumLength - fr->dc->name.Length - sizeof(WCHAR);
+        else
+            movelen = fn->Length;
         
-        len += fr->filepart.Length;
+        if ((!overflow || fn->MaximumLength > fr->dc->name.Length + sizeof(WCHAR)) && movelen > 0) {
+            RtlMoveMemory(&fn->Buffer[(fr->dc->name.Length / sizeof(WCHAR)) + 1], fn->Buffer, movelen);
+            offset += fr->dc->name.Length + sizeof(WCHAR);
+        }
         
-        if (fr != fileref->fcb->Vcb->root_fileref)
-            len += sizeof(WCHAR);
-        
-        fr = fr->parent;
-    } while (fr);
-    
-    fn->Buffer = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
-    if (!fn->Buffer) {
-        ERR("out of memory\n");
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto end;
-    }
-    
-    fn->Length = fn->MaximumLength = len;
-    
-    i = 0;
-    
-    le = fr_list.Blink;
-    while (le != &fr_list) {
-        fileref_list* frl = CONTAINING_RECORD(le, fileref_list, list_entry);
-        
-        if (frl->fileref != fileref->fcb->Vcb->root_fileref) {
-            fn->Buffer[i] = frl->fileref->fcb->ads ? ':' : '\\';
-            i++;
+        if (fn->MaximumLength >= sizeof(WCHAR)) {
+            fn->Buffer[0] = fr->fcb->ads ? ':' : '\\';
+            fn->Length += sizeof(WCHAR);
             
-            if (name_offset && frl->fileref == fileref)
-                *name_offset = i * sizeof(WCHAR);
+            if (fn->MaximumLength > sizeof(WCHAR)) {
+                RtlCopyMemory(&fn->Buffer[1], fr->dc->name.Buffer, min(fr->dc->name.Length, fn->MaximumLength - sizeof(WCHAR)));
+                fn->Length += fr->dc->name.Length;
+            }
             
-            RtlCopyMemory(&fn->Buffer[i], frl->fileref->filepart.Buffer, frl->fileref->filepart.Length);
-            i += frl->fileref->filepart.Length / sizeof(WCHAR);
+            if (fn->Length > fn->MaximumLength) {
+                fn->Length = fn->MaximumLength;
+                overflow = TRUE;
+            }
         }
+         
+        reqlen += sizeof(WCHAR) + fr->dc->name.Length;
         
-        le = le->Blink;
+        fr = fr->parent;
     }
     
-    Status = STATUS_SUCCESS;
-    
-end:
-    while (!IsListEmpty(&fr_list)) {
-        fileref_list* frl;
+    offset += sizeof(WCHAR);
+
+    if (overflow) {
+        if (preqlen)
+            *preqlen = reqlen;
+        Status = STATUS_BUFFER_OVERFLOW;
+    } else {
+        if (name_offset)
+            *name_offset = offset;
         
-        le = RemoveHeadList(&fr_list);
-        frl = CONTAINING_RECORD(le, fileref_list, list_entry);
-        
-        ExFreePool(frl);
+        Status = STATUS_SUCCESS;
     }
     
     return Status;
@@ -3033,9 +2657,7 @@
 }
 
 static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, file_ref* fileref, LONG* length) {
-#ifdef _DEBUG
-    ULONG retlen = 0;
-#endif
+    ULONG reqlen;
     UNICODE_STRING fn;
     NTSTATUS Status;
     static WCHAR datasuf[] = {':','$','D','A','T','A',0};
@@ -3046,8 +2668,6 @@
         return STATUS_INVALID_PARAMETER;
     }
    
-    RtlZeroMemory(fni, sizeof(FILE_NAME_INFORMATION));
-    
     *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]);
     
     TRACE("maximum length is %u\n", *length);
@@ -3055,53 +2675,42 @@
     
     fni->FileName[0] = 0;
     
-    Status = fileref_get_filename(fileref, &fn, NULL);
-    if (!NT_SUCCESS(Status)) {
+    fn.Buffer = fni->FileName;
+    fn.Length = 0;
+    fn.MaximumLength = *length;
+    
+    Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
+    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
         ERR("fileref_get_filename returned %08x\n", Status);
         return Status;
     }
     
-    if (*length >= (LONG)fn.Length) {
-        RtlCopyMemory(fni->FileName, fn.Buffer, fn.Length);
-#ifdef _DEBUG
-        retlen = fn.Length;
-#endif
-        *length -= fn.Length;
-    } else {
-        if (*length > 0) {
-            RtlCopyMemory(fni->FileName, fn.Buffer, *length);
-#ifdef _DEBUG
-            retlen = *length;
-#endif
-        }
-        *length = -1;
-    }
-    
-    fni->FileNameLength = fn.Length;
-    
     if (fcb->ads) {
-        if (*length >= (LONG)datasuflen) {
-            RtlCopyMemory(&fni->FileName[fn.Length / sizeof(WCHAR)], datasuf, datasuflen);
-#ifdef _DEBUG
-            retlen += datasuflen;
-#endif
-            *length -= datasuflen;
-        } else {
-            if (*length > 0) {
-                RtlCopyMemory(&fni->FileName[fn.Length / sizeof(WCHAR)], datasuf, *length);
-#ifdef _DEBUG
-                retlen += *length;
-#endif
+        if (Status == STATUS_BUFFER_OVERFLOW)
+            reqlen += datasuflen;
+        else {
+            if (fn.Length + datasuflen > fn.MaximumLength) {
+                RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, fn.MaximumLength - fn.Length);
+                reqlen += datasuflen;
+                Status = STATUS_BUFFER_OVERFLOW;
+            } else {
+                RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, datasuflen);
+                fn.Length += datasuflen;
             }
-            *length = -1;
         }
     }
     
-    ExFreePool(fn.Buffer);
-    
-    TRACE("%.*S\n", retlen / sizeof(WCHAR), fni->FileName);
+    if (Status == STATUS_BUFFER_OVERFLOW) {
+        *length = -1;
+        fni->FileNameLength = reqlen;
+        TRACE("%.*S (truncated)\n", fn.Length / sizeof(WCHAR), fn.Buffer);
+    } else {
+        *length -= fn.Length;
+        fni->FileNameLength = fn.Length;
+        TRACE("%.*S\n", fn.Length / sizeof(WCHAR), fn.Buffer);
+    }
 
-    return STATUS_SUCCESS;
+    return Status;
 }
 
 static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, file_ref* fileref, PIRP Irp, LONG* length) {
@@ -3125,25 +2734,13 @@
     return STATUS_SUCCESS;
 }
 
-typedef struct {
-    UNICODE_STRING name;
-    UINT64 size;
-    BOOL ignore;
-    LIST_ENTRY list_entry;
-} stream_info;
-
-static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, PIRP Irp, LONG* length) {
+static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) {
     ULONG reqsize;
-    LIST_ENTRY streamlist, *le;
+    LIST_ENTRY* le;
     FILE_STREAM_INFORMATION *entry, *lastentry;
     NTSTATUS Status;
-    KEY searchkey;
-    traverse_ptr tp, next_tp;
-    BOOL b;
-    stream_info* si;
     
-    static WCHAR datasuf[] = {':','$','D','A','T','A',0};
-    static char xapref[] = "user.";
+    static WCHAR datasuf[] = L":$DATA";
     UNICODE_STRING suf;
     
     if (!fileref) {
@@ -3150,187 +2747,29 @@
         ERR("fileref was NULL\n");
         return STATUS_INVALID_PARAMETER;
     }
-    
-    InitializeListHead(&streamlist);
-    
-    ExAcquireResourceSharedLite(&fileref->fcb->Vcb->tree_lock, TRUE);
-    ExAcquireResourceSharedLite(fileref->fcb->Header.Resource, TRUE);
-    
+
     suf.Buffer = datasuf;
     suf.Length = suf.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
     
-    searchkey.obj_id = fileref->fcb->inode;
-    searchkey.obj_type = TYPE_XATTR_ITEM;
-    searchkey.offset = 0;
-
-    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 (fileref->fcb->type != BTRFS_TYPE_DIRECTORY)
+        reqsize = sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR);
+    else
+        reqsize = 0;
     
-    if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
-        si = ExAllocatePoolWithTag(PagedPool, sizeof(stream_info), ALLOC_TAG);
-        if (!si) {
-            ERR("out of memory\n");
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto end;
-        }
-        
-        si->name.Length = si->name.MaximumLength = 0;
-        si->name.Buffer = NULL;
-        si->size = fileref->fcb->inode_item.st_size;
-        si->ignore = FALSE;
-        
-        InsertTailList(&streamlist, &si->list_entry);
-    }
+    ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
     
-    do {
-        if (tp.item->key.obj_id == fileref->fcb->inode && tp.item->key.obj_type == TYPE_XATTR_ITEM) {
-            if (tp.item->size < sizeof(DIR_ITEM)) {
-                ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
-            } else {
-                ULONG len = tp.item->size;
-                DIR_ITEM* xa = (DIR_ITEM*)tp.item->data;
-                ULONG stringlen;
-                
-                do {
-                    if (len < sizeof(DIR_ITEM) || len < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) {
-                        ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                        break;
-                    }
-                    
-                    if (xa->n > strlen(xapref) && RtlCompareMemory(xa->name, xapref, strlen(xapref)) == strlen(xapref) &&
-                        (tp.item->key.offset != EA_DOSATTRIB_HASH || xa->n != strlen(EA_DOSATTRIB) || RtlCompareMemory(xa->name, EA_DOSATTRIB, xa->n) != xa->n) &&
-                        (tp.item->key.offset != EA_EA_HASH || xa->n != strlen(EA_EA) || RtlCompareMemory(xa->name, EA_EA, xa->n) != xa->n)
-                    ) {
-                        Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, &xa->name[strlen(xapref)], xa->n - strlen(xapref));
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-                            goto end;
-                        }
-                        
-                        si = ExAllocatePoolWithTag(PagedPool, sizeof(stream_info), ALLOC_TAG);
-                        if (!si) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            goto end;
-                        }
-                        
-                        si->name.Buffer = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
-                        if (!si->name.Buffer) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            ExFreePool(si);
-                            goto end;
-                        }
-                            
-                        Status = RtlUTF8ToUnicodeN(si->name.Buffer, stringlen, &stringlen, &xa->name[strlen(xapref)], xa->n - strlen(xapref));
-                        
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                            ExFreePool(si->name.Buffer);
-                            ExFreePool(si);
-                            goto end;
-                        }
-                        
-                        si->name.Length = si->name.MaximumLength = stringlen;
-                        
-                        si->size = xa->m;
-                        
-                        si->ignore = FALSE;
-                        
-                        TRACE("stream name = %.*S (length = %u)\n", si->name.Length / sizeof(WCHAR), si->name.Buffer, si->name.Length / sizeof(WCHAR));
-                        
-                        InsertTailList(&streamlist, &si->list_entry);
-                    }
-                    
-                    len -= sizeof(DIR_ITEM) - sizeof(char) + xa->n + xa->m;
-                    xa = (DIR_ITEM*)&xa->name[xa->n + xa->m]; // FIXME - test xattr hash collisions work
-                } while (len > 0);
-            }
-        }
+    le = fileref->fcb->dir_children_index.Flink;
+    while (le != &fileref->fcb->dir_children_index) {
+        dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
         
-        b = find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp);
-        if (b) {
-            tp = next_tp;
-            
-            if (next_tp.item->key.obj_id > fileref->fcb->inode || next_tp.item->key.obj_type > TYPE_XATTR_ITEM)
-                break;
-        }
-    } while (b);
-    
-    ExAcquireResourceSharedLite(&fileref->nonpaged->children_lock, TRUE);
-    
-    le = fileref->children.Flink;
-    while (le != &fileref->children) {
-        file_ref* fr = CONTAINING_RECORD(le, file_ref, list_entry);
-        
-        if (fr->fcb && fr->fcb->ads) {
-            LIST_ENTRY* le2 = streamlist.Flink;
-            BOOL found = FALSE;
-            
-            while (le2 != &streamlist) {
-                si = CONTAINING_RECORD(le2, stream_info, list_entry);
-                
-                if (si && si->name.Buffer && si->name.Length == fr->filepart.Length &&
-                    RtlCompareMemory(si->name.Buffer, fr->filepart.Buffer, si->name.Length) == si->name.Length) {
-                    
-                    si->size = fr->fcb->adsdata.Length;
-                    si->ignore = fr->fcb->deleted;
-                    
-                    found = TRUE;
-                    break;
-                }
-                
-                le2 = le2->Flink;
-            }
-            
-            if (!found && !fr->fcb->deleted) {
-                si = ExAllocatePoolWithTag(PagedPool, sizeof(stream_info), ALLOC_TAG);
-                if (!si) {
-                    ERR("out of memory\n");
-                    Status = STATUS_INSUFFICIENT_RESOURCES;
-                    goto end;
-                }
-                
-                si->name.Length = si->name.MaximumLength = fr->filepart.Length;
-                
-                si->name.Buffer = ExAllocatePoolWithTag(PagedPool, si->name.MaximumLength, ALLOC_TAG);
-                if (!si->name.Buffer) {
-                    ERR("out of memory\n");
-                    Status = STATUS_INSUFFICIENT_RESOURCES;
-                    ExFreePool(si);
-                    goto end;
-                }
-                
-                RtlCopyMemory(si->name.Buffer, fr->filepart.Buffer, fr->filepart.Length);
-                
-                si->size = fr->fcb->adsdata.Length;
-                si->ignore = FALSE;
-                
-                InsertTailList(&streamlist, &si->list_entry);
-            }
-        }
-        
-        le = le->Flink;
-    }
-    
-    ExReleaseResourceLite(&fileref->nonpaged->children_lock);
-    
-    reqsize = 0;
-    
-    le = streamlist.Flink;
-    while (le != &streamlist) {
-        si = CONTAINING_RECORD(le, stream_info, list_entry);
-        
-        if (!si->ignore) {
+        if (dc->index == 0) {
             reqsize = sector_align(reqsize, sizeof(LONGLONG));
-            reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + si->name.Length;
-        }
+            reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length;
+        } else
+            break;
 
         le = le->Flink;
-    }        
+    }
     
     TRACE("length = %i, reqsize = %u\n", *length, reqsize);
     
@@ -3342,37 +2781,54 @@
     entry = fsi;
     lastentry = NULL;
     
-    le = streamlist.Flink;
-    while (le != &streamlist) {
-        si = CONTAINING_RECORD(le, stream_info, list_entry);
+    if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
+        ULONG off;
         
-        if (!si->ignore) {
+        entry->NextEntryOffset = 0;
+        entry->StreamNameLength = suf.Length + sizeof(WCHAR);
+        entry->StreamSize.QuadPart = fileref->fcb->inode_item.st_size;
+        entry->StreamAllocationSize.QuadPart = sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size);
+
+        entry->StreamName[0] = ':';
+        RtlCopyMemory(&entry->StreamName[1], suf.Buffer, suf.Length);
+        
+        off = sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR), sizeof(LONGLONG));
+
+        lastentry = entry;
+        entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off);
+    }
+    
+    le = fileref->fcb->dir_children_index.Flink;
+    while (le != &fileref->fcb->dir_children_index) {
+        dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
+        
+        if (dc->index == 0) {
             ULONG off;
             
             entry->NextEntryOffset = 0;
-            entry->StreamNameLength = si->name.Length + suf.Length + sizeof(WCHAR);
-            entry->StreamSize.QuadPart = si->size;
+            entry->StreamNameLength = dc->name.Length + suf.Length + sizeof(WCHAR);
             
-            if (le == streamlist.Flink)
-                entry->StreamAllocationSize.QuadPart = sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size);
+            if (dc->fileref)
+                entry->StreamSize.QuadPart = dc->fileref->fcb->adsdata.Length;
             else
-                entry->StreamAllocationSize.QuadPart = si->size;
+                entry->StreamSize.QuadPart = dc->size;
             
+            entry->StreamAllocationSize.QuadPart = entry->StreamSize.QuadPart;
+            
             entry->StreamName[0] = ':';
             
-            if (si->name.Length > 0)
-                RtlCopyMemory(&entry->StreamName[1], si->name.Buffer, si->name.Length);
+            RtlCopyMemory(&entry->StreamName[1], dc->name.Buffer, dc->name.Length);
+            RtlCopyMemory(&entry->StreamName[1 + (dc->name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length);
             
-            RtlCopyMemory(&entry->StreamName[1 + (si->name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length);
-            
             if (lastentry)
                 lastentry->NextEntryOffset = (UINT8*)entry - (UINT8*)lastentry;
             
-            off = sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + si->name.Length, sizeof(LONGLONG));
+            off = sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length, sizeof(LONGLONG));
 
             lastentry = entry;
             entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off);
-        }
+        } else
+            break;
         
         le = le->Flink;
     }
@@ -3382,19 +2838,8 @@
     Status = STATUS_SUCCESS;
     
 end:
-    while (!IsListEmpty(&streamlist)) {
-        le = RemoveHeadList(&streamlist);
-        si = CONTAINING_RECORD(le, stream_info, list_entry);
-        
-        if (si->name.Buffer)
-            ExFreePool(si->name.Buffer);
-        
-        ExFreePool(si);
-    }
+    ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
     
-    ExReleaseResourceLite(fileref->fcb->Header.Resource);
-    ExReleaseResourceLite(&fileref->fcb->Vcb->tree_lock);
-    
     return Status;
 }
 
@@ -3407,7 +2852,7 @@
     fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink;
     fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink;
     fsli->DeletePending = fileref ? fileref->delete_on_close : FALSE;
-    fsli->Directory = fcb->type == BTRFS_TYPE_DIRECTORY ? TRUE : FALSE;
+    fsli->Directory = (!fcb->ads && fcb->type == BTRFS_TYPE_DIRECTORY) ? TRUE : FALSE;
     
     *length -= sizeof(FILE_STANDARD_LINK_INFORMATION);
     
@@ -3415,291 +2860,13 @@
 }
 #endif /* __REACTOS__ */
 
-typedef struct {
-    UNICODE_STRING name;
-    UINT64 inode;
-    LIST_ENTRY list_entry;
-} name_bit;
-
-static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol, PIRP Irp) {
-    KEY searchkey;
-    traverse_ptr tp;
-    NTSTATUS Status;
-    LIST_ENTRY* le;
-    root* parsubvol;
-    UNICODE_STRING dirpath;
-    ROOT_REF* rr;
-    ULONG namelen;
-    
-    // FIXME - add subvol->parent field
-    
-    if (subvol == Vcb->root_fileref->fcb->subvol) {
-        subvol->path.Length = subvol->path.MaximumLength = sizeof(WCHAR);
-        subvol->path.Buffer = ExAllocatePoolWithTag(PagedPool, subvol->path.Length, ALLOC_TAG);
-        subvol->path.Buffer[0] = '\\';
-        return STATUS_SUCCESS;
-    }
-    
-    searchkey.obj_id = subvol->id;
-    searchkey.obj_type = TYPE_ROOT_BACKREF;
-    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) { // top subvol
-        subvol->path.Length = subvol->path.MaximumLength = sizeof(WCHAR);
-        subvol->path.Buffer = ExAllocatePoolWithTag(PagedPool, subvol->path.Length, ALLOC_TAG);
-        subvol->path.Buffer[0] = '\\';
-        return STATUS_SUCCESS;
-    }
-    
-    if (tp.item->size < sizeof(ROOT_REF)) {
-        ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF));
-        return STATUS_INTERNAL_ERROR;
-    }
-    
-    rr = (ROOT_REF*)tp.item->data;
-    
-    if (tp.item->size < sizeof(ROOT_REF) - 1 + rr->n) {
-        ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF) - 1 + rr->n);
-        return STATUS_INTERNAL_ERROR;
-    }
-    
-    le = Vcb->roots.Flink;
-
-    parsubvol = NULL;
-
-    while (le != &Vcb->roots) {
-        root* r2 = CONTAINING_RECORD(le, root, list_entry);
-        
-        if (r2->id == tp.item->key.offset) {
-            parsubvol = r2;
-            break;
-        }
-        
-        le = le->Flink;
-    }
-    
-    if (!parsubvol) {
-        ERR("unable to find subvol %llx\n", tp.item->key.offset);
-        return STATUS_INTERNAL_ERROR;
-    }
-    
-    // FIXME - recursion
-
-    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;
-    }
-    
-    Status = RtlUTF8ToUnicodeN(NULL, 0, &namelen, rr->name, rr->n);
-    if (!NT_SUCCESS(Status)) {
-        ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
-        goto end;
-    }
-    
-    if (namelen == 0) {
-        ERR("length was 0\n");
-        Status = STATUS_INTERNAL_ERROR;
-        goto end;
-    }
-    
-    subvol->path.Length = subvol->path.MaximumLength = dirpath.Length + namelen;
-    subvol->path.Buffer = ExAllocatePoolWithTag(PagedPool, subvol->path.Length, ALLOC_TAG);
-    
-    if (!subvol->path.Buffer) {  
-        ERR("out of memory\n");
-        
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto end;
-    }
-    
-    RtlCopyMemory(subvol->path.Buffer, dirpath.Buffer, dirpath.Length);
-        
-    Status = RtlUTF8ToUnicodeN(&subvol->path.Buffer[dirpath.Length / sizeof(WCHAR)], namelen, &namelen, rr->name, rr->n);
-    if (!NT_SUCCESS(Status)) {
-        ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
-        goto end;
-    }
-    
-    Status = STATUS_SUCCESS;
-    
-end:
-    if (dirpath.Buffer)
-        ExFreePool(dirpath.Buffer);
-    
-    if (!NT_SUCCESS(Status) && subvol->path.Buffer) {
-        ExFreePool(subvol->path.Buffer);
-        subvol->path.Buffer = NULL;
-    }
-    
-    return Status;
-}
-
-static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us, PIRP Irp) {
-    KEY searchkey;
-    NTSTATUS Status;
-    UINT64 in;
-    traverse_ptr tp;
-    LIST_ENTRY name_trail, *le;
-    UINT16 levels = 0;
-    UINT32 namelen = 0;
-    WCHAR* usbuf;
-    
-    InitializeListHead(&name_trail);
-    
-    in = inode;
-    
-    // FIXME - start with subvol prefix
-    if (!subvol->path.Buffer) {
-        Status = get_subvol_path(Vcb, subvol, Irp);
-        if (!NT_SUCCESS(Status)) {
-            ERR("get_subvol_path returned %08x\n", Status);
-            return Status;
-        }
-    }
-    
-    while (in != subvol->root_item.objid) {
-        searchkey.obj_id = in;
-        searchkey.obj_type = TYPE_INODE_EXTREF;
-        searchkey.offset = 0xffffffffffffffff;
-        
-        Status = find_item(Vcb, 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) {
-            ERR("could not find INODE_REF for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id);
-            Status = STATUS_INTERNAL_ERROR;
-            goto end;
-        }
-        
-        if (tp.item->key.obj_type == TYPE_INODE_REF) {
-            INODE_REF* ir = (INODE_REF*)tp.item->data;
-            name_bit* nb;
-            ULONG len;
-            
-            if (tp.item->size < sizeof(INODE_REF)) {
-                ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_REF));
-                Status = STATUS_INTERNAL_ERROR;
-                goto end;
-            }
-            
-            if (tp.item->size < sizeof(INODE_REF) - 1 + ir->n) {
-                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(INODE_REF) - 1 + ir->n);
-                Status = STATUS_INTERNAL_ERROR;
-                goto end;
-            }
-            
-            nb = ExAllocatePoolWithTag(PagedPool, sizeof(name_bit), ALLOC_TAG);
-            if (!nb) {
-                ERR("out of memory\n");
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto end;
-            }
-            
-            nb->name.Buffer = NULL;
-            
-            InsertTailList(&name_trail, &nb->list_entry);
-            levels++;
-            
-            Status = RtlUTF8ToUnicodeN(NULL, 0, &len, ir->name, ir->n);
-            if (!NT_SUCCESS(Status)) {
-                ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
-                goto end;
-            }
-            
-            if (len == 0) {
-                ERR("length was 0\n");
-                Status = STATUS_INTERNAL_ERROR;
-                goto end;
-            }
-            
-            nb->name.Length = nb->name.MaximumLength = len;
-            
-            nb->name.Buffer = ExAllocatePoolWithTag(PagedPool, nb->name.Length, ALLOC_TAG);
-            if (!nb->name.Buffer) {  
-                ERR("out of memory\n");
-                
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto end;
-            }
-                
-            Status = RtlUTF8ToUnicodeN(nb->name.Buffer, len, &len, ir->name, ir->n);
-            if (!NT_SUCCESS(Status)) {
-                ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
-                goto end;
-            }
-            
-            in = tp.item->key.offset;
-            namelen += nb->name.Length;
-            
-//         } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
-//             // FIXME
-        } else {
-            ERR("could not find INODE_REF for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id);
-            Status = STATUS_INTERNAL_ERROR;
-            goto end;
-        }
-    }
-    
-    namelen += (levels + 1) * sizeof(WCHAR);
-    
-    us->Length = us->MaximumLength = namelen;
-    us->Buffer = ExAllocatePoolWithTag(PagedPool, us->Length, ALLOC_TAG);
-    
-    if (!us->Buffer) {
-        ERR("out of memory\n");
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto end;
-    }
-    
-    us->Buffer[0] = '\\';
-    usbuf = &us->Buffer[1];
-    
-    le = name_trail.Blink;
-    while (le != &name_trail) {
-        name_bit* nb = CONTAINING_RECORD(le, name_bit, list_entry);
-        
-        RtlCopyMemory(usbuf, nb->name.Buffer, nb->name.Length);
-        usbuf += nb->name.Length / sizeof(WCHAR);
-        
-        usbuf[0] = '\\';
-        usbuf++;
-        
-        le = le->Blink;
-    }
-    
-    Status = STATUS_SUCCESS;
-    
-end:
-    while (!IsListEmpty(&name_trail)) {
-        name_bit* nb = CONTAINING_RECORD(name_trail.Flink, name_bit, list_entry);
-        
-        if (nb->name.Buffer)
-            ExFreePool(nb->name.Buffer);
-        
-        RemoveEntryList(&nb->list_entry);
-        
-        ExFreePool(nb);
-    }
-
-    return Status;
-}
-
 NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp) {
     NTSTATUS Status;
     fcb* fcb;
-    hardlink* hl;
+    UINT64 parent = 0;
+    UNICODE_STRING name;
+    BOOL hl_alloc = FALSE;
     file_ref *parfr, *fr;
-    dir_child* dc = NULL;
     
     Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, PagedPool, Irp);
     if (!NT_SUCCESS(Status)) {
@@ -3736,196 +2903,124 @@
 
                 ir = (INODE_REF*)tp.item->data;
 
-                hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
-                if (!hl) {
-                    ERR("out of memory\n");
-                    free_fcb(fcb);
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
+                parent = tp.item->key.offset;
                 
-                hl->parent = tp.item->key.offset;
-                hl->index = ir->index;
-                
-                hl->utf8.Length = hl->utf8.MaximumLength = ir->n;
-                
-                if (hl->utf8.Length > 0) {
-                    hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
-                    RtlCopyMemory(hl->utf8.Buffer, ir->name, ir->n);
-                }
-                
                 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n);
                 if (!NT_SUCCESS(Status)) {
                     ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-                    ExFreePool(hl);
                     free_fcb(fcb);
                     return Status;
                 }
                 
-                hl->name.Length = hl->name.MaximumLength = stringlen;
+                name.Length = name.MaximumLength = stringlen;
                 
                 if (stringlen == 0)
-                    hl->name.Buffer = NULL;
+                    name.Buffer = NULL;
                 else {
-                    hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
+                    name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
                     
-                    if (!hl->name.Buffer) {
+                    if (!name.Buffer) {
                         ERR("out of memory\n");
-                        ExFreePool(hl);
                         free_fcb(fcb);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
                     
-                    Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ir->name, ir->n);
+                    Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ir->name, ir->n);
                     if (!NT_SUCCESS(Status)) {
                         ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                        ExFreePool(hl->name.Buffer);
-                        ExFreePool(hl);
+                        ExFreePool(name.Buffer);
                         free_fcb(fcb);
                         return Status;
                     }
+                    
+                    hl_alloc = TRUE;
                 }
-                    
-                InsertTailList(&fcb->hardlinks, &hl->list_entry);
             } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
                 INODE_EXTREF* ier;
-                hardlink* hl;
                 ULONG stringlen;
 
                 ier = (INODE_EXTREF*)tp.item->data;
+
+                parent = ier->dir;
                 
-                hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
-                if (!hl) {
-                    ERR("out of memory\n");
-                    free_fcb(fcb);
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-                
-                hl->parent = ier->dir;
-                hl->index = ier->index;
-                
-                hl->utf8.Length = hl->utf8.MaximumLength = ier->n;
-                
-                if (hl->utf8.Length > 0) {
-                    hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG);
-                    RtlCopyMemory(hl->utf8.Buffer, ier->name, ier->n);
-                }
-                
                 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n);
                 if (!NT_SUCCESS(Status)) {
                     ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-                    ExFreePool(hl);
                     free_fcb(fcb);
                     return Status;
                 }
                 
-                hl->name.Length = hl->name.MaximumLength = stringlen;
+                name.Length = name.MaximumLength = stringlen;
                 
                 if (stringlen == 0)
-                    hl->name.Buffer = NULL;
+                    name.Buffer = NULL;
                 else {
-                    hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
+                    name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
                     
-                    if (!hl->name.Buffer) {
+                    if (!name.Buffer) {
                         ERR("out of memory\n");
-                        ExFreePool(hl);
                         free_fcb(fcb);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
-                    
-                    Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ier->name, ier->n);
+
+                    Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ier->name, ier->n);
                     if (!NT_SUCCESS(Status)) {
                         ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                        ExFreePool(hl->name.Buffer);
-                        ExFreePool(hl);
+                        ExFreePool(name.Buffer);
                         free_fcb(fcb);
                         return Status;
                     }
+
+                    hl_alloc = TRUE;
                 }
-                
-                InsertTailList(&fcb->hardlinks, &hl->list_entry);
+
             }
         }
+    } else {
+        hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
+        
+        name = hl->name;
+        parent = hl->parent;
     }
     
-    if (IsListEmpty(&fcb->hardlinks)) {
+    if (parent == 0) {
         ERR("subvol %llx, inode %llx has no hardlinks\n", subvol->id, inode);
         free_fcb(fcb);
-        return STATUS_INTERNAL_ERROR;
+        return STATUS_INVALID_PARAMETER;
     }
-    
-    hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
-    
-    // FIXME - does this work with subvols?
-    
-    if (hl->parent == inode) // root of subvol
-        parfr = NULL;
-    else {
-        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);
-            return Status;
-        }
-    }
-    
-    fr = create_fileref();
-    if (!fr) {
-        ERR("out of memory\n");
+
+    Status = open_fileref_by_inode(Vcb, subvol, parent, &parfr, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("open_fileref_by_inode returned %08x\n", Status);
         free_fcb(fcb);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    fr->fcb = fcb;
-    fcb->fileref = fr;
-    
-    fr->index = hl->index;
-    
-    fr->utf8.Length = fr->utf8.MaximumLength = hl->utf8.Length;
-    if (fr->utf8.Length > 0) {
-        fr->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, fr->utf8.Length, ALLOC_TAG);
         
-        if (!fr->utf8.Buffer) {
-            ERR("out of memory\n");
-            free_fileref(fr);
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
+        if (hl_alloc)
+            ExFreePool(name.Buffer);
         
-        RtlCopyMemory(fr->utf8.Buffer, hl->utf8.Buffer, hl->utf8.Length);
+        return Status;
     }
+
+    Status = open_fileref_child(Vcb, parfr, &name, TRUE, TRUE, FALSE, PagedPool, &fr, Irp);
     
-    fr->filepart.MaximumLength = fr->filepart.Length = hl->name.Length;
-    
-    if (fr->filepart.Length > 0) {
-        fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fr->filepart.MaximumLength, ALLOC_TAG);
-        if (!fr->filepart.Buffer) {
-            ERR("out of memory\n");
-            free_fileref(fr);
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
+    if (!NT_SUCCESS(Status)) {
+        ERR("open_fileref_child returned %08x\n", Status);
         
-        RtlCopyMemory(fr->filepart.Buffer, hl->name.Buffer, hl->name.Length);
-    }
-    
-    Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-        free_fileref(fr);
+        if (hl_alloc)
+            ExFreePool(name.Buffer);
+        
+        free_fcb(fcb);
+        free_fileref(Vcb, parfr);
+        
         return Status;
     }
     
-    fr->parent = parfr;
-    
-    Status = add_dir_child(parfr->fcb, fr->fcb->inode == SUBVOL_ROOT_INODE ? fr->fcb->subvol->id : fr->fcb->inode, fr->fcb->inode == SUBVOL_ROOT_INODE,
-                           fr->index, &fr->utf8, &fr->filepart, &fr->filepart_uc, fr->fcb->type, &dc);
-    if (!NT_SUCCESS(Status))
-        WARN("add_dir_child returned %08x\n", Status);
-    
-    fr->dc = dc;
-    dc->fileref = fr;
-    
-    insert_fileref_child(parfr, fr, TRUE);
+    *pfr = fr;
 
-    *pfr = fr;
+    if (hl_alloc)
+        ExFreePool(name.Buffer);
+
+    free_fcb(fcb);
+    free_fileref(Vcb, parfr);
     
     return STATUS_SUCCESS;
 }
@@ -3960,7 +3055,7 @@
         if (fcb == fcb->Vcb->root_fileref->fcb)
             namelen = sizeof(WCHAR);
         else
-            namelen = fileref->filepart.Length;
+            namelen = fileref->dc->name.Length;
                     
         bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + namelen;
         
@@ -3977,8 +3072,8 @@
                 feli->FileNameLength = 1;
                 feli->FileName[0] = '.';
             } else {
-                feli->FileNameLength = fileref->filepart.Length / sizeof(WCHAR);
-                RtlCopyMemory(feli->FileName, fileref->filepart.Buffer, fileref->filepart.Length);
+                feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
+                RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
             }
             
             fli->EntriesReturned++;
@@ -3989,7 +3084,7 @@
         ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
     
         if (IsListEmpty(&fcb->hardlinks)) {
-            bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->filepart.Length - sizeof(WCHAR);
+            bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->dc->name.Length - sizeof(WCHAR);
             
             if (bytes_needed > *length)
                 overflow = TRUE;
@@ -3999,8 +3094,8 @@
 
                 feli->NextEntryOffset = 0;
                 feli->ParentFileId = fileref->parent->fcb->inode;
-                feli->FileNameLength = fileref->filepart.Length / sizeof(WCHAR);
-                RtlCopyMemory(feli->FileName, fileref->filepart.Buffer, fileref->filepart.Length);
+                feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
+                RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
                 
                 fli->EntriesReturned++;
                 
@@ -4027,12 +3122,12 @@
                     while (le2 != &parfr->children) {
                         file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
                         
-                        if (fr2->index == hl->index) {
+                        if (fr2->dc->index == hl->index) {
                             found = TRUE;
                             deleted = fr2->deleted;
                             
                             if (!deleted)
-                                fn = &fr2->filepart;
+                                fn = &fr2->dc->name;
                             
                             break;
                         }
@@ -4072,7 +3167,7 @@
                         }
                     }
                     
-                    free_fileref(parfr);
+                    free_fileref(fcb->Vcb, parfr);
                 }
                 
                 le = le->Flink;
@@ -4205,7 +3300,9 @@
                 goto exit;
             }
             
+            ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
             Status = fill_in_file_attribute_information(ati, fcb, fileref, Irp, &length);
+            ExReleaseResourceLite(&Vcb->tree_lock);
             
             break;
         }
@@ -4333,7 +3430,7 @@
             
             TRACE("FileStreamInformation\n");
             
-            Status = fill_in_file_stream_information(fsi, fileref, Irp, &length);
+            Status = fill_in_file_stream_information(fsi, fileref, &length);
 
             break;
         }
@@ -4345,7 +3442,9 @@
             
             TRACE("FileHardLinkInformation\n");
             
+            ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
             Status = fill_in_hard_link_information(fli, fileref, Irp, &length);
+            ExReleaseResourceLite(&Vcb->tree_lock);
             
             break;
         }
@@ -4432,9 +3531,12 @@
     
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_query_information(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
     
     Irp->IoStatus.Information = 0;
@@ -4443,8 +3545,6 @@
     
     IrpSp = IoGetCurrentIrpStackLocation(Irp);
     
-    ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
-    
     fcb = IrpSp->FileObject->FsContext;
     TRACE("fcb = %p\n", fcb);
     TRACE("fcb->subvol = %p\n", fcb->subvol);
@@ -4451,6 +3551,7 @@
     
     Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp);
     
+end:
     TRACE("returning %08x\n", Status);
     
     Irp->IoStatus.Status = Status;
@@ -4457,9 +3558,6 @@
     
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
     
-    ExReleaseResourceLite(&Vcb->tree_lock);
-    
-exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -4485,9 +3583,12 @@
 
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_query_ea(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
     
     ffei = map_user_buffer(Irp);
@@ -4679,7 +3780,6 @@
 
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
 
-exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -4718,9 +3818,12 @@
 
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_set_ea(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
     
     if (Vcb->readonly) {
@@ -4974,7 +4077,6 @@
 
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
     
-exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
Index: drivers/filesystems/btrfs/flushthread.c
===================================================================
--- drivers/filesystems/btrfs/flushthread.c	(révision 74430)
+++ drivers/filesystems/btrfs/flushthread.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -16,6 +16,9 @@
  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include "btrfs_drv.h"
+#include <ata.h>
+#include <ntddscsi.h>
+#include <ntddstor.h>
 
 #define MAX_CSUM_SIZE (4096 - sizeof(tree_header) - sizeof(leaf_node))
 
@@ -38,14 +41,20 @@
     TREE_BLOCK_REF tbr;
 } EXTENT_ITEM_SKINNY_METADATA;
 
-static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, PIRP Irp, LIST_ENTRY* rollback);
+static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, PIRP Irp);
 static NTSTATUS update_tree_extents(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback);
-static BOOL insert_tree_item_batch(LIST_ENTRY* batchlist, device_extension* Vcb, root* r, UINT64 objid, UINT64 objtype, UINT64 offset,
-                                   void* data, UINT16 datalen, enum batch_operation operation, PIRP Irp, LIST_ENTRY* rollback);
+static NTSTATUS insert_tree_item_batch(LIST_ENTRY* batchlist, device_extension* Vcb, root* r, UINT64 objid, UINT64 objtype, UINT64 offset,
+                                       void* data, UINT16 datalen, enum batch_operation operation);
 
+#ifndef _MSC_VER // not in mingw yet
+#define DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED 0x80000000
+#endif
+
 static NTSTATUS STDCALL write_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
     write_context* context = conptr;
     
+    UNUSED(DeviceObject);
+    
     context->iosb = Irp->IoStatus;
     KeSetEvent(&context->Event, 0, FALSE);
     
@@ -58,30 +67,21 @@
     LARGE_INTEGER offset;
     PIRP Irp;
     PIO_STACK_LOCATION IrpSp;
-    write_context* context = NULL;
+    write_context context;
     
     TRACE("(%p, %llx, %p, %x)\n", device, address, data, length);
     
-    context = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_context), ALLOC_TAG);
-    if (!context) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
+    RtlZeroMemory(&context, sizeof(write_context));
     
-    RtlZeroMemory(context, sizeof(write_context));
+    KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
     
-    KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
-    
     offset.QuadPart = address;
     
-//     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, Vcb->device, data, length, &offset, NULL, &context->iosb);
-    
     Irp = IoAllocateIrp(device->StackSize, FALSE);
     
     if (!Irp) {
         ERR("IoAllocateIrp failed\n");
-        Status = STATUS_INTERNAL_ERROR;
-        goto exit2;
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
     
     IrpSp = IoGetNextIrpStackLocation(Irp);
@@ -98,7 +98,7 @@
             goto exit;
         }
         
-        MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
+        MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoReadAccess);
     } else {
         Irp->UserBuffer = data;
     }
@@ -106,17 +106,17 @@
     IrpSp->Parameters.Write.Length = length;
     IrpSp->Parameters.Write.ByteOffset = offset;
     
-    Irp->UserIosb = &context->iosb;
+    Irp->UserIosb = &context.iosb;
 
-    Irp->UserEvent = &context->Event;
+    Irp->UserEvent = &context.Event;
 
-    IoSetCompletionRoutine(Irp, write_completion, context, TRUE, TRUE, TRUE);
+    IoSetCompletionRoutine(Irp, write_completion, &context, TRUE, TRUE, TRUE);
 
     Status = IoCallDriver(device, Irp);
     
     if (Status == STATUS_PENDING) {
-        KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
-        Status = context->iosb.Status;
+        KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+        Status = context.iosb.Status;
     }
     
     if (!NT_SUCCESS(Status)) {
@@ -131,27 +131,160 @@
 exit:
     IoFreeIrp(Irp);
     
-exit2:
-    if (context)
-        ExFreePool(context);
-    
     return Status;
 }
 
+static void add_trim_entry(device* dev, UINT64 address, UINT64 size) {
+    space* s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
+    if (!s) {
+        ERR("out of memory\n");
+        return;
+    }
+    
+    s->address = address;
+    s->size = size;
+    dev->num_trim_entries++;
+    
+    InsertTailList(&dev->trim_list, &s->list_entry);
+}
+
 static void clean_space_cache_chunk(device_extension* Vcb, chunk* c) {
-    // FIXME - loop through c->deleting and do TRIM if device supports it
-    // FIXME - also find way of doing TRIM of dropped chunks
+    ULONG type;
     
+    if (Vcb->trim && !Vcb->options.no_trim) {
+        if (c->chunk_item->type & BLOCK_FLAG_DUPLICATE)
+            type = BLOCK_FLAG_DUPLICATE;
+        else if (c->chunk_item->type & BLOCK_FLAG_RAID0)
+            type = BLOCK_FLAG_RAID0;
+        else if (c->chunk_item->type & BLOCK_FLAG_RAID1)
+            type = BLOCK_FLAG_DUPLICATE;
+        else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
+            type = BLOCK_FLAG_RAID10;
+        else if (c->chunk_item->type & BLOCK_FLAG_RAID5)
+            type = BLOCK_FLAG_RAID5;
+        else if (c->chunk_item->type & BLOCK_FLAG_RAID6)
+            type = BLOCK_FLAG_RAID6;
+        else // SINGLE
+            type = BLOCK_FLAG_DUPLICATE;
+    }
+    
     while (!IsListEmpty(&c->deleting)) {
         space* s = CONTAINING_RECORD(c->deleting.Flink, space, list_entry);
         
+        if (Vcb->trim && !Vcb->options.no_trim && (!Vcb->options.no_barrier || !(c->chunk_item->type & BLOCK_FLAG_METADATA))) {
+            CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+            
+            if (type == BLOCK_FLAG_DUPLICATE) {
+                UINT16 i;
+                
+                for (i = 0; i < c->chunk_item->num_stripes; i++) {
+                    if (c->devices[i] && c->devices[i]->devobj && !c->devices[i]->readonly && c->devices[i]->trim)
+                        add_trim_entry(c->devices[i], s->address - c->offset + cis[i].offset, s->size);
+                }
+            } else if (type == BLOCK_FLAG_RAID0) {
+                UINT64 startoff, endoff;
+                UINT16 startoffstripe, endoffstripe, i;
+                
+                get_raid0_offset(s->address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &startoff, &startoffstripe);
+                get_raid0_offset(s->address - c->offset + s->size - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &endoff, &endoffstripe);
+                
+                for (i = 0; i < c->chunk_item->num_stripes; i++) {
+                    if (c->devices[i] && c->devices[i]->devobj && !c->devices[i]->readonly && c->devices[i]->trim) {
+                        UINT64 stripestart, stripeend;
+                        
+                        if (startoffstripe > i)
+                            stripestart = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
+                        else if (startoffstripe == i)
+                            stripestart = startoff;
+                        else
+                            stripestart = startoff - (startoff % c->chunk_item->stripe_length);
+                        
+                        if (endoffstripe > i)
+                            stripeend = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
+                        else if (endoffstripe == i)
+                            stripeend = endoff + 1;
+                        else
+                            stripeend = endoff - (endoff % c->chunk_item->stripe_length);
+                        
+                        if (stripestart != stripeend)
+                            add_trim_entry(c->devices[i], stripestart + cis[i].offset, stripeend - stripestart);
+                    }
+                }
+            } else if (type == BLOCK_FLAG_RAID10) {
+                UINT64 startoff, endoff;
+                UINT16 sub_stripes, startoffstripe, endoffstripe, i;
+                
+                sub_stripes = max(1, c->chunk_item->sub_stripes);
+                
+                get_raid0_offset(s->address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes / sub_stripes, &startoff, &startoffstripe);
+                get_raid0_offset(s->address - c->offset + s->size - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes / sub_stripes, &endoff, &endoffstripe);
+                
+                startoffstripe *= sub_stripes;
+                endoffstripe *= sub_stripes;
+                
+                for (i = 0; i < c->chunk_item->num_stripes; i += sub_stripes) {
+                    ULONG j;
+                    UINT64 stripestart, stripeend;
+                    
+                    if (startoffstripe > i)
+                        stripestart = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
+                    else if (startoffstripe == i)
+                        stripestart = startoff;
+                    else
+                        stripestart = startoff - (startoff % c->chunk_item->stripe_length);
+                    
+                    if (endoffstripe > i)
+                        stripeend = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
+                    else if (endoffstripe == i)
+                        stripeend = endoff + 1;
+                    else
+                        stripeend = endoff - (endoff % c->chunk_item->stripe_length);
+
+                    if (stripestart != stripeend) {
+                        for (j = 0; j < sub_stripes; j++) {
+                            if (c->devices[i+j] && c->devices[i+j]->devobj && !c->devices[i+j]->readonly && c->devices[i+j]->trim)
+                                add_trim_entry(c->devices[i+j], stripestart + cis[i+j].offset, stripeend - stripestart);
+                        }
+                    }
+                }
+            }
+            // FIXME - RAID5(?), RAID6(?)
+        }
+        
         RemoveEntryList(&s->list_entry);
         ExFreePool(s);
     }
 }
 
+typedef struct {
+    DEVICE_MANAGE_DATA_SET_ATTRIBUTES* dmdsa;
+    ATA_PASS_THROUGH_EX apte;
+    PIRP Irp;
+    IO_STATUS_BLOCK iosb;
+} ioctl_context_stripe;
+
+typedef struct {
+    KEVENT Event;
+    LONG left;
+    ioctl_context_stripe* stripes;
+} ioctl_context;
+
+static NTSTATUS STDCALL ioctl_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+    ioctl_context* context = (ioctl_context*)conptr;
+    LONG left2 = InterlockedDecrement(&context->left);
+    
+    UNUSED(DeviceObject);
+    UNUSED(Irp);
+    
+    if (left2 == 0)
+        KeSetEvent(&context->Event, 0, FALSE);
+    
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
 static void clean_space_cache(device_extension* Vcb) {
     chunk* c;
+    ULONG num;
     
     TRACE("(%p)\n", Vcb);
     
@@ -166,9 +299,129 @@
         
         ExReleaseResourceLite(&c->lock);
     }
+    
+    if (Vcb->trim && !Vcb->options.no_trim) {
+        ioctl_context context;
+        LIST_ENTRY* le;
+        ULONG total_num;
+        
+        context.left = 0;
+        
+        le = Vcb->devices.Flink;
+        while (le != &Vcb->devices) {
+            device* dev = CONTAINING_RECORD(le, device, list_entry);
+        
+            if (dev->devobj && !dev->readonly && dev->trim && dev->num_trim_entries > 0)
+                context.left++;
+            
+            le = le->Flink;
+        }
+        
+        if (context.left == 0)
+            return;
+        
+        total_num = context.left;
+        num = 0;
+        
+        KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
+        
+        context.stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(ioctl_context_stripe) * context.left, ALLOC_TAG);
+        if (!context.stripes) {
+            ERR("out of memory\n");
+            return;
+        }
+        
+        RtlZeroMemory(context.stripes, sizeof(ioctl_context_stripe) * context.left);
+        
+        le = Vcb->devices.Flink;
+        while (le != &Vcb->devices) {
+            device* dev = CONTAINING_RECORD(le, device, list_entry);
+        
+            if (dev->devobj && !dev->readonly && dev->trim && dev->num_trim_entries > 0) {
+                LIST_ENTRY* le2;
+                ioctl_context_stripe* stripe = &context.stripes[num];
+                DEVICE_DATA_SET_RANGE* ranges;
+                ULONG datalen = sector_align(sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES), sizeof(UINT64)) + (dev->num_trim_entries * sizeof(DEVICE_DATA_SET_RANGE)), i;
+                PIO_STACK_LOCATION IrpSp;
+                
+                stripe->dmdsa = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG);
+                if (!stripe->dmdsa) {
+                    ERR("out of memory\n");
+                    goto nextdev;
+                }
+                
+                stripe->dmdsa->Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
+                stripe->dmdsa->Action = DeviceDsmAction_Trim;
+                stripe->dmdsa->Flags = DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED;
+                stripe->dmdsa->ParameterBlockOffset = 0;
+                stripe->dmdsa->ParameterBlockLength = 0;
+                stripe->dmdsa->DataSetRangesOffset = sector_align(sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES), sizeof(UINT64));
+                stripe->dmdsa->DataSetRangesLength = dev->num_trim_entries * sizeof(DEVICE_DATA_SET_RANGE);
+                
+                ranges = (DEVICE_DATA_SET_RANGE*)((UINT8*)stripe->dmdsa + stripe->dmdsa->DataSetRangesOffset);
+                
+                i = 0;
+                
+                le2 = dev->trim_list.Flink;
+                while (le2 != &dev->trim_list) {
+                    space* s = CONTAINING_RECORD(le2, space, list_entry);
+                    
+                    ranges[i].StartingOffset = s->address;
+                    ranges[i].LengthInBytes = s->size;
+                    i++;
+                    
+                    le2 = le2->Flink;
+                }
+                
+                stripe->Irp = IoAllocateIrp(dev->devobj->StackSize, FALSE);
+                
+                if (!stripe->Irp) {
+                    ERR("IoAllocateIrp failed\n");
+                    goto nextdev;
+                }
+                
+                IrpSp = IoGetNextIrpStackLocation(stripe->Irp);
+                IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
+                
+                IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES;
+                IrpSp->Parameters.DeviceIoControl.InputBufferLength = datalen;
+                IrpSp->Parameters.DeviceIoControl.OutputBufferLength = 0;
+                
+                stripe->Irp->AssociatedIrp.SystemBuffer = stripe->dmdsa;
+                stripe->Irp->Flags |= IRP_BUFFERED_IO;
+                stripe->Irp->UserBuffer = NULL;
+                stripe->Irp->UserIosb = &stripe->iosb;
+                
+                IoSetCompletionRoutine(stripe->Irp, ioctl_completion, &context, TRUE, TRUE, TRUE);
+                
+                IoCallDriver(dev->devobj, stripe->Irp);
+                
+nextdev:
+                while (!IsListEmpty(&dev->trim_list)) {
+                    space* s = CONTAINING_RECORD(RemoveHeadList(&dev->trim_list), space, list_entry);
+                    ExFreePool(s);
+                }
+                
+                dev->num_trim_entries = 0;
+                
+                num++;
+            }
+            
+            le = le->Flink;
+        }
+        
+        KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+        
+        for (num = 0; num < total_num; num++) {
+            if (context.stripes[num].dmdsa)
+                ExFreePool(context.stripes[num].dmdsa);
+        }
+        
+        ExFreePool(context.stripes);
+    }
 }
 
-static BOOL trees_consistent(device_extension* Vcb, LIST_ENTRY* rollback) {
+static BOOL trees_consistent(device_extension* Vcb) {
     ULONG maxsize = Vcb->superblock.node_size - sizeof(tree_header);
     LIST_ENTRY* le;
     
@@ -205,7 +458,7 @@
     return TRUE;
 }
 
-static NTSTATUS add_parents(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS add_parents(device_extension* Vcb, PIRP Irp) {
     UINT8 level;
     LIST_ENTRY* le;
     
@@ -258,11 +511,16 @@
                         
                         RtlCopyMemory(ri, &t->root->root_item, sizeof(ROOT_ITEM));
                         
-                        delete_tree_item(Vcb, &tp, rollback);
+                        Status = delete_tree_item(Vcb, &tp);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("delete_tree_item returned %08x\n", Status);
+                            return Status;
+                        }
                         
-                        if (!insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, Irp, rollback)) {
-                            ERR("insert_tree_item failed\n");
-                            return STATUS_INTERNAL_ERROR;
+                        Status = insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, Irp);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("insert_tree_item returned %08x\n", Status);
+                            return Status;
                         }
                     }
                 }
@@ -278,7 +536,7 @@
     return STATUS_SUCCESS;
 }
 
-static void add_parents_to_cache(device_extension* Vcb, tree* t) {
+static void add_parents_to_cache(tree* t) {
     while (t->parent) {
         t = t->parent;
         t->write = TRUE;
@@ -286,6 +544,7 @@
 }
 
 static BOOL insert_tree_extent_skinny(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64 address, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
     EXTENT_ITEM_SKINNY_METADATA* eism;
     traverse_ptr insert_tp;
     
@@ -301,8 +560,9 @@
     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, Irp, rollback)) {
-        ERR("insert_tree_item failed\n");
+    Status = insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
         ExFreePool(eism);
         return FALSE;
     }
@@ -313,7 +573,7 @@
 
     ExReleaseResourceLite(&c->lock);
     
-    add_parents_to_cache(Vcb, insert_tp.tree);
+    add_parents_to_cache(insert_tp.tree);
     
     return TRUE;
 }
@@ -324,6 +584,18 @@
     
     TRACE("(%p, %llx, %p)\n", Vcb, c->offset, address);
     
+    if (Vcb->superblock.node_size > c->chunk_item->size - c->used)
+        return FALSE;
+    
+    if (!c->cache_loaded) {
+        NTSTATUS Status = load_cache_chunk(Vcb, c, NULL);
+        
+        if (!NT_SUCCESS(Status)) {
+            ERR("load_cache_chunk returned %08x\n", Status);
+            return FALSE;
+        }
+    }
+    
     if (IsListEmpty(&c->space_size))
         return FALSE;
     
@@ -388,6 +660,7 @@
 }
 
 static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64* new_address, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
     UINT64 address;
     EXTENT_ITEM_TREE2* eit2;
     traverse_ptr insert_tp;
@@ -428,8 +701,9 @@
 //     }
 // #endif
     
-    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");
+    Status = insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
         ExFreePool(eit2);
         return FALSE;
     }
@@ -440,7 +714,7 @@
     
     ExReleaseResourceLite(&c->lock);
 
-    add_parents_to_cache(Vcb, insert_tp.tree);
+    add_parents_to_cache(insert_tp.tree);
     
     *new_address = address;
     
@@ -580,7 +854,7 @@
     else
         root = t->header.tree_id;
     
-    Status = decrease_extent_refcount_tree(Vcb, address, Vcb->superblock.node_size, root, level, Irp, rollback);
+    Status = decrease_extent_refcount_tree(Vcb, address, Vcb->superblock.node_size, root, level, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("decrease_extent_refcount_tree returned %08x\n", Status);
         return Status;
@@ -592,8 +866,18 @@
         if (c) {
             ExAcquireResourceExclusiveLite(&c->lock, TRUE);
             
-            decrease_chunk_usage(c, Vcb->superblock.node_size);
+            if (!c->cache_loaded) {
+                Status = load_cache_chunk(Vcb, c, NULL);
+
+                if (!NT_SUCCESS(Status)) {
+                    ERR("load_cache_chunk returned %08x\n", Status);
+                    ExReleaseResourceLite(&c->lock);
+                    return Status;
+                }
+            }
             
+            c->used -= Vcb->superblock.node_size;
+            
             space_list_add(Vcb, c, TRUE, address, Vcb->superblock.node_size, rollback);
             
             ExReleaseResourceLite(&c->lock);
@@ -774,7 +1058,7 @@
                                 }
                             }
                             
-                            Status = increase_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, Irp, rollback);
+                            Status = increase_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, Irp);
                             if (!NT_SUCCESS(Status)) {
                                 ERR("increase_extent_refcount returned %08x\n", Status);
                                 return Status;
@@ -790,7 +1074,7 @@
                                     sdr.count = 1;
                                     
                                     Status = decrease_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0,
-                                                                      t->header.address, ce->superseded, Irp, rollback);
+                                                                      t->header.address, ce ? ce->superseded : FALSE, Irp);
                                     if (!NT_SUCCESS(Status)) {
                                         ERR("decrease_extent_refcount returned %08x\n", Status);
                                         return Status;
@@ -855,7 +1139,7 @@
                     tbr.offset = t->root->id;
                     
                     Status = increase_extent_refcount(Vcb, td->treeholder.address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF,
-                                                      &tbr, &td->key, t->header.level - 1, Irp, rollback);
+                                                      &tbr, &td->key, t->header.level - 1, Irp);
                     if (!NT_SUCCESS(Status)) {
                         ERR("increase_extent_refcount returned %08x\n", Status);
                         return Status;
@@ -870,7 +1154,7 @@
                             sbr.offset = t->header.address;
                             
                             Status = decrease_extent_refcount(Vcb, td->treeholder.address, Vcb->superblock.node_size, TYPE_SHARED_BLOCK_REF, &sbr, NULL, 0,
-                                                              t->header.address, FALSE, Irp, rollback);
+                                                              t->header.address, FALSE, Irp);
                             if (!NT_SUCCESS(Status)) {
                                 ERR("decrease_extent_refcount returned %08x\n", Status);
                                 return Status;
@@ -894,7 +1178,7 @@
                 sbr.offset = t->parent->header.address;
                 
                 Status = decrease_extent_refcount(Vcb, t->header.address, Vcb->superblock.node_size, TYPE_SHARED_BLOCK_REF, &sbr, NULL, 0,
-                                                  t->parent->header.address, FALSE, Irp, rollback);
+                                                  t->parent->header.address, FALSE, Irp);
                 if (!NT_SUCCESS(Status)) {
                     ERR("decrease_extent_refcount returned %08x\n", Status);
                     return Status;
@@ -908,7 +1192,7 @@
             tbr.offset = t->header.tree_id;
         
         Status = increase_extent_refcount(Vcb, t->header.address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF, &tbr,
-                                          t->parent ? &t->paritem->key : NULL, t->header.level, Irp, rollback);
+                                          t->parent ? &t->paritem->key : NULL, t->header.level, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("increase_extent_refcount returned %08x\n", Status);
             return Status;
@@ -947,13 +1231,13 @@
                         
                         sbr.offset = t->header.address;
                         
-                        Status = increase_extent_refcount(Vcb, td->treeholder.address, Vcb->superblock.node_size, TYPE_SHARED_BLOCK_REF, &sbr, &td->key, t->header.level - 1, Irp, rollback);
+                        Status = increase_extent_refcount(Vcb, td->treeholder.address, Vcb->superblock.node_size, TYPE_SHARED_BLOCK_REF, &sbr, &td->key, t->header.level - 1, Irp);
                     } else {
                         TREE_BLOCK_REF tbr;
                         
                         tbr.offset = t->root->id;
                         
-                        Status = increase_extent_refcount(Vcb, td->treeholder.address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF, &tbr, &td->key, t->header.level - 1, Irp, rollback);
+                        Status = increase_extent_refcount(Vcb, td->treeholder.address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF, &tbr, &td->key, t->header.level - 1, Irp);
                     }
                     
                     if (!NT_SUCCESS(Status)) {
@@ -1017,7 +1301,7 @@
                                     }
                                 }
                                 
-                                Status = increase_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, Irp, rollback);
+                                Status = increase_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, Irp);
                             } else {
                                 EXTENT_DATA_REF edr;
                                 
@@ -1040,7 +1324,7 @@
                                     }
                                 }
                                 
-                                Status = increase_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, Irp, rollback);
+                                Status = increase_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, Irp);
                             }
                             
                             if (!NT_SUCCESS(Status)) {
@@ -1087,9 +1371,9 @@
             
             c = get_chunk_from_address(Vcb, t->new_address);
             
-            if (c) {
-                increase_chunk_usage(c, Vcb->superblock.node_size);
-            } else {
+            if (c)
+                c->used += Vcb->superblock.node_size;
+            else {
                 ERR("could not find chunk for address %llx\n", t->new_address);
                 return STATUS_INTERNAL_ERROR;
             }
@@ -1159,7 +1443,6 @@
                 
                 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;
                 }
                 
@@ -1176,6 +1459,7 @@
             }
             
             t->root->treeholder.address = t->new_address;
+            t->root->treeholder.generation = Vcb->superblock.generation;
         }
         
         le = le->Flink;
@@ -1195,19 +1479,8 @@
     LIST_ENTRY* le;
     tree_write* tw;
     NTSTATUS Status;
-    write_data_context* wtc;
+    ULONG num_bits;
     
-    wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_context), ALLOC_TAG);
-    if (!wtc) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE);
-    InitializeListHead(&wtc->stripes);
-    wtc->tree = TRUE;
-    wtc->stripes_left = 0;
-    
     // merge together runs
     c = NULL;
     le = tree_writes->Flink;
@@ -1224,7 +1497,6 @@
                 
                 if (!data) {
                     ERR("out of memory\n");
-                    ExFreePool(wtc);
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
                 
@@ -1278,51 +1550,101 @@
         le = le->Flink;
     }
     
+    num_bits = 0;
+    
     le = tree_writes->Flink;
     while (le != tree_writes) {
         tw = CONTAINING_RECORD(le, tree_write, list_entry);
         
-        if (!tw->overlap) {
-            TRACE("address: %llx, size: %x, overlap = %u\n", tw->address, tw->length, tw->overlap);
-            
-            Status = write_data(Vcb, tw->address, tw->data, TRUE, tw->length, wtc, NULL, NULL);
-            if (!NT_SUCCESS(Status)) {
-                ERR("write_data returned %08x\n", Status);
-                ExFreePool(wtc);
-                return Status;
-            }
-        }
+        if (!tw->overlap)
+            num_bits++;
         
         le = le->Flink;
     }
     
-    if (wtc->stripes.Flink != &wtc->stripes) {
-        // launch writes and wait
-        le = wtc->stripes.Flink;
-        while (le != &wtc->stripes) {
-            write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
-            
-            if (stripe->status != WriteDataStatus_Ignore)
-                IoCallDriver(stripe->device->devobj, stripe->Irp);
-            
-            le = le->Flink;
+    if (num_bits > 0) {
+        write_data_context* wtc;
+        int i;
+        ULONG bit_num = 0;
+        
+        wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_context) * num_bits, ALLOC_TAG);
+        if (!wtc) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
         }
         
-        KeWaitForSingleObject(&wtc->Event, Executive, KernelMode, FALSE, NULL);
+        le = tree_writes->Flink;
         
-        le = wtc->stripes.Flink;
-        while (le != &wtc->stripes) {
-            write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
+        while (le != tree_writes) {
+            tw = CONTAINING_RECORD(le, tree_write, list_entry);
             
-            if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) {
-                Status = stripe->iosb.Status;
-                break;
+            if (!tw->overlap) {
+                TRACE("address: %llx, size: %x, overlap = %u\n", tw->address, tw->length, tw->overlap);
+                
+                KeInitializeEvent(&wtc[bit_num].Event, NotificationEvent, FALSE);
+                InitializeListHead(&wtc[bit_num].stripes);
+                wtc[bit_num].tree = TRUE;
+                wtc[bit_num].stripes_left = 0;
+                wtc[bit_num].parity1 = wtc[bit_num].parity2 = wtc[bit_num].scratch = NULL;
+                wtc[bit_num].mdl = wtc[bit_num].parity1_mdl = wtc[bit_num].parity2_mdl = NULL;
+                
+                Status = write_data(Vcb, tw->address, tw->data, tw->length, &wtc[bit_num], NULL, NULL, FALSE, 0);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("write_data returned %08x\n", Status);
+                    
+                    for (i = 0; i < num_bits; i++) {
+                        free_write_data_stripes(&wtc[i]);
+                    }
+                    ExFreePool(wtc);
+                    
+                    return Status;
+                }
+                
+                bit_num++;
             }
             
             le = le->Flink;
         }
         
-        free_write_data_stripes(wtc);
+        for (i = 0; i < num_bits; i++) {
+            if (wtc[i].stripes.Flink != &wtc[i].stripes) {
+                // launch writes and wait
+                le = wtc[i].stripes.Flink;
+                while (le != &wtc[i].stripes) {
+                    write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
+                    
+                    if (stripe->status != WriteDataStatus_Ignore)
+                        IoCallDriver(stripe->device->devobj, stripe->Irp);
+                    
+                    le = le->Flink;
+                }
+            }
+        }
+
+        for (i = 0; i < num_bits; i++) {
+            if (wtc[i].stripes.Flink != &wtc[i].stripes) {
+                KeWaitForSingleObject(&wtc[i].Event, Executive, KernelMode, FALSE, NULL);
+            }
+        }
+
+        for (i = 0; i < num_bits; i++) {
+            le = wtc[i].stripes.Flink;
+            while (le != &wtc[i].stripes) {
+                write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
+                
+                if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) {
+                    Status = stripe->iosb.Status;
+                    log_device_error(Vcb, stripe->device, BTRFS_DEV_STAT_WRITE_ERRORS);
+                    break;
+                }
+                
+                le = le->Flink;
+            }
+                
+            free_write_data_stripes(&wtc[i]);
+        }
+        
+        ExFreePool(wtc);
     }
     
     le = tree_writes->Flink;
@@ -1332,10 +1654,9 @@
         if (tw->overlap) {
             TRACE("address: %llx, size: %x, overlap = %u\n", tw->address, tw->length, tw->overlap);
             
-            Status = write_data_complete(Vcb, tw->address, tw->data, tw->length, Irp, NULL);
+            Status = write_data_complete(Vcb, tw->address, tw->data, tw->length, Irp, NULL, FALSE, 0);
             if (!NT_SUCCESS(Status)) {
                 ERR("write_data_complete returned %08x\n", Status);
-                ExFreePool(wtc);
                 return Status;
             }
         }
@@ -1376,7 +1697,7 @@
                 
                 if (!t->has_new_address) {
                     ERR("error - tried to write tree with no new address\n");
-                    int3;
+                    return STATUS_INTERNAL_ERROR;
                 }
                 
                 le2 = t->itemlist.Flink;
@@ -1472,6 +1793,9 @@
 
         if (t->write) {
 #ifdef DEBUG_PARANOID
+            BOOL first = TRUE;
+            KEY lastkey;
+            
             le2 = t->itemlist.Flink;
             while (le2 != &t->itemlist) {
                 tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
@@ -1478,6 +1802,19 @@
                 if (!td->ignore) {
                     num_items++;
                     
+                    if (!first) {
+                        if (keycmp(td->key, lastkey) == 0) {
+                            ERR("(%llx,%x,%llx): duplicate key\n", td->key.obj_id, td->key.obj_type, td->key.offset);
+                            crash = TRUE;
+                        } else if (keycmp(td->key, lastkey) == -1) {
+                            ERR("(%llx,%x,%llx): key out of order\n", td->key.obj_id, td->key.obj_type, td->key.offset);
+                            crash = TRUE;
+                        }
+                    } else
+                        first = FALSE;
+                    
+                    lastkey = td->key;
+                    
                     if (t->header.level == 0)
                         size += td->size;
                 }
@@ -1509,6 +1846,11 @@
                 crash = TRUE;
             }
             
+            if (t->header.level > 0 && t->header.num_items == 1) {
+                ERR("tree %llx, level %x: internal tree has only one item\n", t->root->id, t->header.level);
+                crash = TRUE;
+            }
+            
             if (crash) {
                 ERR("tree %p\n", t);
                 le2 = t->itemlist.Flink;
@@ -1639,6 +1981,9 @@
         le = RemoveHeadList(&tree_writes);
         tw = CONTAINING_RECORD(le, tree_write, list_entry);
         
+        if (tw->data)
+            ExFreePool(tw->data);
+        
         ExFreePool(tw);
     }
     
@@ -1714,42 +2059,130 @@
     sb->num_devices = Vcb->superblock.num_devices;
 }
 
-static NTSTATUS STDCALL write_superblock(device_extension* Vcb, device* device) {
+typedef struct {
+    void* context;
+    UINT8* buf;
+    PMDL mdl;
+    device* device;
     NTSTATUS Status;
+    PIRP Irp;
+    LIST_ENTRY list_entry;
+} write_superblocks_stripe;
+
+typedef struct _write_superblocks_context {
+    KEVENT Event;
+    LIST_ENTRY stripes;
+    LONG left;
+} write_superblocks_context;
+
+static NTSTATUS STDCALL write_superblock_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+    write_superblocks_stripe* stripe = conptr;
+    write_superblocks_context* context = stripe->context;
+    
+    UNUSED(DeviceObject);
+    
+    stripe->Status = Irp->IoStatus.Status;
+    
+    if (InterlockedDecrement(&context->left) == 0)
+        KeSetEvent(&context->Event, 0, FALSE);
+    
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS STDCALL write_superblock(device_extension* Vcb, device* device, write_superblocks_context* context) {
     unsigned int i = 0;
-    UINT32 crc32;
-#ifdef __REACTOS__
-    Status = STATUS_INTERNAL_ERROR;
-#endif
     
-    RtlCopyMemory(&Vcb->superblock.dev_item, &device->devitem, sizeof(DEV_ITEM));
-    
     // All the documentation says that the Linux driver only writes one superblock
     // if it thinks a disk is an SSD, but this doesn't seem to be the case!
     
-    while (superblock_addrs[i] > 0 && device->length >= superblock_addrs[i] + sizeof(superblock)) {
-        TRACE("writing superblock %u\n", i);
+    while (superblock_addrs[i] > 0 && device->devitem.num_bytes >= superblock_addrs[i] + sizeof(superblock)) {
+        ULONG sblen = sector_align(sizeof(superblock), Vcb->superblock.sector_size);
+        superblock* sb;
+        UINT32 crc32;
+        write_superblocks_stripe* stripe;
+        PIO_STACK_LOCATION IrpSp;
         
-        Vcb->superblock.sb_phys_addr = superblock_addrs[i];
+        sb = ExAllocatePoolWithTag(NonPagedPool, sblen, ALLOC_TAG);
+        if (!sb) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
         
-        crc32 = calc_crc32c(0xffffffff, (UINT8*)&Vcb->superblock.uuid, (ULONG)sizeof(superblock) - sizeof(Vcb->superblock.checksum));
-        crc32 = ~crc32;
-        TRACE("crc32 is %08x\n", crc32);
-        RtlCopyMemory(&Vcb->superblock.checksum, &crc32, sizeof(UINT32));
+        RtlCopyMemory(sb, &Vcb->superblock, sizeof(superblock));
         
-        Status = write_data_phys(device->devobj, superblock_addrs[i], &Vcb->superblock, sizeof(superblock));
+        if (sblen > sizeof(superblock))
+            RtlZeroMemory((UINT8*)sb + sizeof(superblock), sblen - sizeof(superblock));
         
-        if (!NT_SUCCESS(Status))
-            break;
+        RtlCopyMemory(&sb->dev_item, &device->devitem, sizeof(DEV_ITEM));
+        sb->sb_phys_addr = superblock_addrs[i];
         
+        crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+        RtlCopyMemory(&sb->checksum, &crc32, sizeof(UINT32));
+        
+        stripe = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_superblocks_stripe), ALLOC_TAG);
+        if (!sb) {
+            ERR("out of memory\n");
+            ExFreePool(sb);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        stripe->buf = (UINT8*)sb;
+        
+        stripe->Irp = IoAllocateIrp(device->devobj->StackSize, FALSE);
+        if (!stripe->Irp) {
+            ERR("IoAllocateIrp failed\n");
+            ExFreePool(stripe);
+            ExFreePool(sb);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        IrpSp = IoGetNextIrpStackLocation(stripe->Irp);
+        IrpSp->MajorFunction = IRP_MJ_WRITE;
+        
+        if (i == 0)
+            IrpSp->Flags |= SL_WRITE_THROUGH;
+        
+        if (device->devobj->Flags & DO_BUFFERED_IO) {
+            stripe->Irp->AssociatedIrp.SystemBuffer = sb;
+            stripe->mdl = NULL;
+
+            stripe->Irp->Flags = IRP_BUFFERED_IO;
+        } else if (device->devobj->Flags & DO_DIRECT_IO) {
+            stripe->mdl = IoAllocateMdl(sb, sblen, FALSE, FALSE, NULL);
+            if (!stripe->mdl) {
+                ERR("IoAllocateMdl failed\n");
+                IoFreeIrp(stripe->Irp);
+                ExFreePool(stripe);
+                ExFreePool(sb);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+            
+            stripe->Irp->MdlAddress = stripe->mdl;
+            
+            MmBuildMdlForNonPagedPool(stripe->mdl);
+        } else {
+            stripe->Irp->UserBuffer = sb;
+            stripe->mdl = NULL;
+        }
+
+        IrpSp->Parameters.Write.Length = sblen;
+        IrpSp->Parameters.Write.ByteOffset.QuadPart = superblock_addrs[i];
+
+        IoSetCompletionRoutine(stripe->Irp, write_superblock_completion, stripe, TRUE, TRUE, TRUE);
+        
+        stripe->context = context;
+        stripe->device = device;
+        InsertTailList(&context->stripes, &stripe->list_entry);
+        
+        context->left++;
+        
         i++;
     }
     
-    if (i == 0) {
+    if (i == 0)
         ERR("no superblocks written!\n");
-    }
 
-    return Status;
+    return STATUS_SUCCESS;
 }
 
 static NTSTATUS write_superblocks(device_extension* Vcb, PIRP Irp) {
@@ -1756,6 +2189,7 @@
     UINT64 i;
     NTSTATUS Status;
     LIST_ENTRY* le;
+    write_superblocks_context context;
     
     TRACE("(%p)\n", Vcb);
     
@@ -1783,15 +2217,19 @@
     
     update_backup_superblock(Vcb, &Vcb->superblock.backup[BTRFS_NUM_BACKUP_ROOTS - 1], Irp);
     
+    KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
+    InitializeListHead(&context.stripes);
+    context.left = 0;
+    
     le = Vcb->devices.Flink;
     while (le != &Vcb->devices) {
         device* dev = CONTAINING_RECORD(le, device, list_entry);
         
         if (dev->devobj && !dev->readonly) {
-            Status = write_superblock(Vcb, dev);
+            Status = write_superblock(Vcb, dev, &context);
             if (!NT_SUCCESS(Status)) {
                 ERR("write_superblock returned %08x\n", Status);
-                return Status;
+                goto end;
             }
         }
         
@@ -1798,7 +2236,60 @@
         le = le->Flink;
     }
     
-    return STATUS_SUCCESS;
+    if (IsListEmpty(&context.stripes)) {
+        ERR("error - not writing any superblocks\n");
+        Status = STATUS_INTERNAL_ERROR;
+        goto end;
+    }
+    
+    le = context.stripes.Flink;
+    while (le != &context.stripes) {
+        write_superblocks_stripe* stripe = CONTAINING_RECORD(le, write_superblocks_stripe, list_entry);
+        
+        IoCallDriver(stripe->device->devobj, stripe->Irp);
+        
+        le = le->Flink;
+    }
+    
+    KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+    
+    le = context.stripes.Flink;
+    while (le != &context.stripes) {
+        write_superblocks_stripe* stripe = CONTAINING_RECORD(le, write_superblocks_stripe, list_entry);
+        
+        if (!NT_SUCCESS(stripe->Status)) {
+            ERR("device %llx returned %08x\n", stripe->device->devitem.dev_id, stripe->Status);
+            log_device_error(Vcb, stripe->device, BTRFS_DEV_STAT_WRITE_ERRORS);
+            Status = stripe->Status;
+            goto end;
+        }
+        
+        le = le->Flink;
+    }
+    
+    Status = STATUS_SUCCESS;
+    
+end:
+    while (!IsListEmpty(&context.stripes)) {
+        write_superblocks_stripe* stripe = CONTAINING_RECORD(RemoveHeadList(&context.stripes), write_superblocks_stripe, list_entry);
+
+        if (stripe->mdl) {
+            if (stripe->mdl->MdlFlags & MDL_PAGES_LOCKED)
+                MmUnlockPages(stripe->mdl);
+
+            IoFreeMdl(stripe->mdl);
+        }
+        
+        if (stripe->Irp)
+            IoFreeIrp(stripe->Irp);
+        
+        if (stripe->buf)
+            ExFreePool(stripe->buf);
+
+        ExFreePool(stripe);
+    }
+
+    return Status;
 }
 
 static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_extent* ce, PIRP Irp, LIST_ENTRY* rollback) {
@@ -1823,7 +2314,6 @@
     le = ce->refs.Flink;
     while (le != &ce->refs) {
         changed_extent_ref* cer = CONTAINING_RECORD(le, changed_extent_ref, list_entry);
-        LIST_ENTRY* le3 = le->Flink;
         UINT64 old_count = 0;
         
         if (cer->type == TYPE_EXTENT_DATA_REF) {
@@ -1833,9 +2323,6 @@
                 
                 if (cer2->type == TYPE_EXTENT_DATA_REF && cer2->edr.root == cer->edr.root && cer2->edr.objid == cer->edr.objid && cer2->edr.offset == cer->edr.offset) {
                     old_count = cer2->edr.count;
-                    
-                    RemoveEntryList(&cer2->list_entry);
-                    ExFreePool(cer2);
                     break;
                 }
                 
@@ -1845,15 +2332,60 @@
             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, Irp, 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);
                             
                 if (!NT_SUCCESS(Status)) {
                     ERR("increase_extent_refcount_data returned %08x\n", Status);
                     return Status;
                 }
-            } else if (cer->edr.count < old_count) {
+            }
+        } else if (cer->type == TYPE_SHARED_DATA_REF) {
+            le2 = ce->old_refs.Flink;
+            while (le2 != &ce->old_refs) {
+                changed_extent_ref* cer2 = CONTAINING_RECORD(le2, changed_extent_ref, list_entry);
+                
+                if (cer2->type == TYPE_SHARED_DATA_REF && cer2->sdr.offset == cer->sdr.offset) {
+//                     old_count = cer2->edr.count;
+                    
+                    RemoveEntryList(&cer2->list_entry);
+                    ExFreePool(cer2);
+                    break;
+                }
+                
+                le2 = le2->Flink;
+            }            
+        }
+        
+        le = le->Flink;
+    }
+    
+    le = ce->refs.Flink;
+    while (le != &ce->refs) {
+        changed_extent_ref* cer = CONTAINING_RECORD(le, changed_extent_ref, list_entry);
+        LIST_ENTRY* le3 = le->Flink;
+        UINT64 old_count = 0;
+        
+        if (cer->type == TYPE_EXTENT_DATA_REF) {
+            le2 = ce->old_refs.Flink;
+            while (le2 != &ce->old_refs) {
+                changed_extent_ref* cer2 = CONTAINING_RECORD(le2, changed_extent_ref, list_entry);
+                
+                if (cer2->type == TYPE_EXTENT_DATA_REF && cer2->edr.root == cer->edr.root && cer2->edr.objid == cer->edr.objid && cer2->edr.offset == cer->edr.offset) {
+                    old_count = cer2->edr.count;
+                    
+                    RemoveEntryList(&cer2->list_entry);
+                    ExFreePool(cer2);
+                    break;
+                }
+                
+                le2 = le2->Flink;
+            }
+            
+            old_size = ce->old_count > 0 ? ce->old_size : ce->size;
+            
+            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, ce->superseded, Irp, rollback);
+                                                       old_count - cer->edr.count, ce->superseded, Irp);
                 
                 if (!NT_SUCCESS(Status)) {
                     ERR("decrease_extent_refcount_data returned %08x\n", Status);
@@ -1893,28 +2425,18 @@
                 } else
                     data = NULL;
                 
-                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;
+                Status = insert_tree_item(Vcb, Vcb->extent_root, ce->address, TYPE_EXTENT_ITEM, ce->size, data, tp.item->size, NULL, Irp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("insert_tree_item returned %08x\n", Status);
+                    return Status;
                 }
                 
-                delete_tree_item(Vcb, &tp, rollback);
+                Status = delete_tree_item(Vcb, &tp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_tree_item returned %08x\n", Status);
+                    return Status;
+                }
             }
-        } else if (cer->type == TYPE_SHARED_DATA_REF) {
-            le2 = ce->old_refs.Flink;
-            while (le2 != &ce->old_refs) {
-                changed_extent_ref* cer2 = CONTAINING_RECORD(le2, changed_extent_ref, list_entry);
-                
-                if (cer2->type == TYPE_SHARED_DATA_REF && cer2->sdr.offset == cer->sdr.offset) {
-//                     old_count = cer2->edr.count;
-                    
-                    RemoveEntryList(&cer2->list_entry);
-                    ExFreePool(cer2);
-                    break;
-                }
-                
-                le2 = le2->Flink;
-            }            
         }
        
         RemoveEntryList(&cer->list_entry);
@@ -1930,7 +2452,7 @@
     
 end:
     if (ce->count == 0 && !ce->superseded) {
-        decrease_chunk_usage(c, ce->size);
+        c->used -= ce->size;
         space_list_add(Vcb, c, TRUE, ce->address, ce->size, rollback);
     }
 
@@ -1940,7 +2462,7 @@
     return STATUS_SUCCESS;
 }
 
-void add_checksum_entry(device_extension* Vcb, UINT64 address, ULONG length, UINT32* csum, PIRP Irp, LIST_ENTRY* rollback) {
+void add_checksum_entry(device_extension* Vcb, UINT64 address, ULONG length, UINT32* csum, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
     UINT32* data;
@@ -1976,9 +2498,10 @@
                 
                 RtlCopyMemory(checksums, data, il * sizeof(UINT32));
                 
-                if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, off, checksums,
-                                        il * sizeof(UINT32), NULL, Irp, rollback)) {
-                    ERR("insert_tree_item failed\n");
+                Status = insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, off, checksums,
+                                          il * sizeof(UINT32), NULL, Irp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("insert_tree_item returned %08x\n", Status);
                     ExFreePool(checksums);
                     return;
                 }
@@ -2064,7 +2587,11 @@
                     RtlClearBits(&bmp, (tp.item->key.offset - startaddr) / Vcb->superblock.sector_size, itemlen / sizeof(UINT32));
                 }
                 
-                delete_tree_item(Vcb, &tp, rollback);
+                Status = delete_tree_item(Vcb, &tp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_tree_item returned %08x\n", Status);
+                    return;
+                }
             }
             
             if (find_next_item(Vcb, &tp, &next_tp, FALSE, Irp)) {
@@ -2104,8 +2631,9 @@
                 
                 off = startaddr + UInt32x32To64(index, Vcb->superblock.sector_size);
                 
-                if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, off, data, sizeof(UINT32) * rl, NULL, Irp, rollback)) {
-                    ERR("insert_tree_item failed\n");
+                Status = insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, off, data, sizeof(UINT32) * rl, NULL, Irp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("insert_tree_item returned %08x\n", Status);
                     ExFreePool(data);
                     ExFreePool(bmparr);
                     ExFreePool(checksums);
@@ -2141,6 +2669,16 @@
         
         ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
+        if (!c->cache_loaded && (!IsListEmpty(&c->changed_extents) || c->used != c->oldused)) {
+            Status = load_cache_chunk(Vcb, c, NULL);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("load_cache_chunk returned %08x\n", Status);
+                ExReleaseResourceLite(&c->lock);
+                goto end;
+            }
+        }
+        
         le2 = c->changed_extents.Flink;
         while (le2 != &c->changed_extents) {
             LIST_ENTRY* le3 = le2->Flink;
@@ -2159,7 +2697,7 @@
         // This is usually done by update_chunks, but we have to check again in case any new chunks
         // have been allocated since.
         if (c->created) {
-            Status = create_chunk(Vcb, c, Irp, rollback);
+            Status = create_chunk(Vcb, c, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("create_chunk returned %08x\n", Status);
                 ExReleaseResourceLite(&c->lock);
@@ -2166,6 +2704,26 @@
                 goto end;
             }
         }
+        
+        if (c->old_cache) {
+            if (c->old_cache->dirty) {
+                LIST_ENTRY batchlist;
+                
+                InitializeListHead(&batchlist);
+                
+                Status = flush_fcb(c->old_cache, FALSE, &batchlist, Irp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("flush_fcb returned %08x\n", Status);
+                    ExReleaseResourceLite(&c->lock);
+                    goto end;
+                }
+                
+                commit_batch_list(Vcb, &batchlist, Irp);
+            }
+            
+            free_fcb(c->old_cache);
+            c->old_cache = NULL;
+        }
 
         if (c->used != c->oldused) {
             searchkey.obj_id = c->offset;
@@ -2181,7 +2739,6 @@
             
             if (keycmp(searchkey, tp.item->key)) {
                 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->lock);
                 goto end;
@@ -2207,12 +2764,18 @@
             
             TRACE("adjusting usage of chunk %llx to %llx\n", c->offset, c->used);
             
-            delete_tree_item(Vcb, &tp, rollback);
+            Status = delete_tree_item(Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                ExFreePool(bgi);
+                ExReleaseResourceLite(&c->lock);
+                goto end;
+            }
             
-            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");
+            Status = insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, bgi, tp.item->size, NULL, Irp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item returned %08x\n", Status);
                 ExFreePool(bgi);
-                Status = STATUS_INTERNAL_ERROR;
                 ExReleaseResourceLite(&c->lock);
                 goto end;
             }
@@ -2310,7 +2873,10 @@
     nt->new_address = 0;
     nt->has_new_address = FALSE;
     nt->updated_extents = FALSE;
+    nt->uniqueness_determined = TRUE;
+    nt->is_unique = TRUE;
     nt->list_entry_hash.Flink = NULL;
+    nt->buf = NULL;
     InitializeListHead(&nt->itemlist);
     
 //     ExInitializeResourceLite(&nt->nonpaged->load_tree_lock);
@@ -2390,6 +2956,27 @@
             
             le = le->Flink;
         }
+    } else {
+        LIST_ENTRY* le = nt->itemlist.Flink;
+        
+        while (le != &nt->itemlist) {
+            tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry);
+            
+            if (!td2->inserted && td2->data) {
+                UINT8* data = ExAllocatePoolWithTag(PagedPool, td2->size, ALLOC_TAG);
+                
+                if (!data) {
+                    ERR("out of memory\n");
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+                
+                RtlCopyMemory(data, td2->data, td2->size);
+                td2->data = data;
+                td2->inserted = TRUE;
+            }
+            
+            le = le->Flink;
+        }
     }
     
     if (nt->parent) {
@@ -2444,7 +3031,10 @@
     pt->updated_extents = FALSE;
 //     pt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
     pt->size = pt->header.num_items * sizeof(internal_node);
+    pt->uniqueness_determined = TRUE;
+    pt->is_unique = TRUE;
     pt->list_entry_hash.Flink = NULL;
+    pt->buf = NULL;
     InitializeListHead(&pt->itemlist);
     
 //     ExInitializeResourceLite(&pt->nonpaged->load_tree_lock);
@@ -2550,7 +3140,7 @@
                 ERR("(%llx,%x,%llx) in tree %llx is too large (%x > %x)\n",
                     td->key.obj_id, td->key.obj_type, td->key.offset, t->root->id,
                     ds, Vcb->superblock.node_size - sizeof(tree_header));
-                int3;
+                return STATUS_INTERNAL_ERROR;
             }
             
             // FIXME - move back if previous item was deleted item with same key
@@ -2571,58 +3161,66 @@
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
+    BOOL ret = FALSE;
+    EXTENT_ITEM* ei;
+    UINT8* type;
     
-    do {
-        EXTENT_ITEM* ei;
-        UINT8* type;
+    if (t->uniqueness_determined)
+        return t->is_unique;
+    
+    if (t->parent && !is_tree_unique(Vcb, t->parent, Irp))
+        goto end;
+    
+    if (t->has_address) {
+        searchkey.obj_id = t->header.address;
+        searchkey.obj_type = Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA ? TYPE_METADATA_ITEM : TYPE_EXTENT_ITEM;
+        searchkey.offset = 0xffffffffffffffff;
         
-        if (t->has_address) {
-            searchkey.obj_id = t->header.address;
-            searchkey.obj_type = Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA ? TYPE_METADATA_ITEM : TYPE_EXTENT_ITEM;
-            searchkey.offset = 0xffffffffffffffff;
+        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;
+        }
+        
+        if (tp.item->key.obj_id != t->header.address || (tp.item->key.obj_type != TYPE_METADATA_ITEM && tp.item->key.obj_type != TYPE_EXTENT_ITEM))
+            goto end;
+        
+        if (tp.item->key.obj_type == TYPE_EXTENT_ITEM && tp.item->size == sizeof(EXTENT_ITEM_V0))
+            goto end;
+        
+        if (tp.item->size < sizeof(EXTENT_ITEM))
+            goto end;
+        
+        ei = (EXTENT_ITEM*)tp.item->data;
+        
+        if (ei->refcount > 1)
+            goto end;
+        
+        if (tp.item->key.obj_type == TYPE_EXTENT_ITEM && ei->flags & EXTENT_ITEM_TREE_BLOCK) {
+            EXTENT_ITEM2* ei2;
             
-            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;
-            }
+            if (tp.item->size < sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2))
+                goto end;
             
-            if (tp.item->key.obj_id != t->header.address || (tp.item->key.obj_type != TYPE_METADATA_ITEM && tp.item->key.obj_type != TYPE_EXTENT_ITEM))
-                return FALSE;
-            
-            if (tp.item->key.obj_type == TYPE_EXTENT_ITEM && tp.item->size == sizeof(EXTENT_ITEM_V0))
-                return FALSE;
-            
-            if (tp.item->size < sizeof(EXTENT_ITEM))
-                return FALSE;
-            
-            ei = (EXTENT_ITEM*)tp.item->data;
-            
-            if (ei->refcount > 1)
-                return FALSE;
-            
-            if (tp.item->key.obj_type == TYPE_EXTENT_ITEM && ei->flags & EXTENT_ITEM_TREE_BLOCK) {
-                EXTENT_ITEM2* ei2;
-                
-                if (tp.item->size < sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2))
-                    return FALSE;
-                
-                ei2 = (EXTENT_ITEM2*)&ei[1];
-                type = (UINT8*)&ei2[1];
-            } else
-                type = (UINT8*)&ei[1];
-            
-            if (type >= tp.item->data + tp.item->size || *type != TYPE_TREE_BLOCK_REF)
-                return FALSE;
-        }
+            ei2 = (EXTENT_ITEM2*)&ei[1];
+            type = (UINT8*)&ei2[1];
+        } else
+            type = (UINT8*)&ei[1];
         
-        t = t->parent;
-    } while (t);
+        if (type >= tp.item->data + tp.item->size || *type != TYPE_TREE_BLOCK_REF)
+            goto end;
+    }
     
-    return TRUE;
+    ret = TRUE;
+    
+end:
+    t->is_unique = ret;
+    t->uniqueness_determined = TRUE;
+
+    return ret;
 }
 
-static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, BOOL* done, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, BOOL* done, BOOL* done_deletions, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY* le;
     tree_data* nextparitem = NULL;
     NTSTATUS Status;
@@ -2694,17 +3292,30 @@
 #endif
                 }
                 
+                td2->inserted = TRUE;
                 le = le->Flink;
             }
-        }
-        
-        le = next_tree->itemlist.Flink;
-        while (le != &next_tree->itemlist) {
-            tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
+        } else {
+            le = next_tree->itemlist.Flink;
             
-            td->inserted = TRUE;
-            
-            le = le->Flink;
+            while (le != &next_tree->itemlist) {
+                tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry);
+                
+                if (!td2->inserted && td2->data) {
+                    UINT8* data = ExAllocatePoolWithTag(PagedPool, td2->size, ALLOC_TAG);
+                    
+                    if (!data) {
+                        ERR("out of memory\n");
+                        return STATUS_INSUFFICIENT_RESOURCES;
+                    }
+                    
+                    RtlCopyMemory(data, td2->data, td2->size);
+                    td2->data = data;
+                    td2->inserted = TRUE;
+                }
+                
+                le = le->Flink;
+            }
         }
         
         t->itemlist.Blink->Flink = next_tree->itemlist.Flink;
@@ -2737,6 +3348,8 @@
             nextparitem->ignore = TRUE;
             next_tree->parent->header.num_items--;
             next_tree->parent->size -= sizeof(internal_node);
+            
+            *done_deletions = TRUE;
         }
         
         par = next_tree->parent;
@@ -2778,7 +3391,6 @@
             if (t->size + size < Vcb->superblock.node_size - sizeof(tree_header)) {
                 RemoveEntryList(&td->list_entry);
                 InsertTailList(&t->itemlist, &td->list_entry);
-                td->inserted = TRUE;
                 
                 if (next_tree->header.level > 0 && td->treeholder.tree) {
                     td->treeholder.tree->parent = t;
@@ -2785,8 +3397,20 @@
 #ifdef DEBUG_PARANOID
                     if (td->treeholder.tree->parent && td->treeholder.tree->parent->header.level <= td->treeholder.tree->header.level) int3;
 #endif
+                } else if (next_tree->header.level == 0 && !td->inserted && td->size > 0) {
+                    UINT8* data = ExAllocatePoolWithTag(PagedPool, td->size, ALLOC_TAG);
+                    
+                    if (!data) {
+                        ERR("out of memory\n");
+                        return STATUS_INSUFFICIENT_RESOURCES;
+                    }
+                    
+                    RtlCopyMemory(data, td->data, td->size);
+                    td->data = data;
                 }
                 
+                td->inserted = TRUE;
+                
                 if (!td->ignore) {
                     next_tree->size -= size;
                     t->size += size;
@@ -2832,7 +3456,7 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* t, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* t, UINT8 level, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -2863,12 +3487,18 @@
             } else
                 eism = NULL;
             
-            delete_tree_item(Vcb, &tp, rollback);
+            Status = delete_tree_item(Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                ExFreePool(eism);
+                return Status;
+            }
             
-            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");
+            Status = insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, tp.item->size, NULL, Irp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item returned %08x\n", Status);
                 ExFreePool(eism);
-                return STATUS_INTERNAL_ERROR;
+                return Status;
             }
             
             return STATUS_SUCCESS;
@@ -2902,14 +3532,20 @@
         
         RtlCopyMemory(eit, tp.item->data, tp.item->size);
         
-        delete_tree_item(Vcb, &tp, rollback);
+        Status = delete_tree_item(Vcb, &tp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            ExFreePool(eit);
+            return Status;
+        }
         
         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, Irp, rollback)) {
-            ERR("insert_tree_item failed\n");
+        Status = 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);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
             ExFreePool(eit);
-            return STATUS_INTERNAL_ERROR;
+            return Status;
         }
     
         return STATUS_SUCCESS;
@@ -3018,7 +3654,7 @@
                         free_tree(t);
                     } else if (t->header.level != 0) {
                         if (t->has_new_address) {
-                            Status = update_extent_level(Vcb, t->new_address, t, 0, Irp, rollback);
+                            Status = update_extent_level(Vcb, t->new_address, t, 0, Irp);
                             
                             if (!NT_SUCCESS(Status)) {
                                 ERR("update_extent_level returned %08x\n", Status);
@@ -3073,7 +3709,7 @@
                 BOOL done;
                 
                 do {
-                    Status = try_tree_amalgamate(Vcb, t, &done, Irp, rollback);
+                    Status = try_tree_amalgamate(Vcb, t, &done, &done_deletions, Irp, rollback);
                     if (!NT_SUCCESS(Status)) {
                         ERR("try_tree_amalgamate returned %08x\n", Status);
                         return Status;
@@ -3170,7 +3806,7 @@
     NTSTATUS Status;
     
     if (!th->tree) {
-        Status = load_tree(Vcb, th->address, r, &th->tree, NULL, NULL);
+        Status = load_tree(Vcb, th->address, r, &th->tree, th->generation, NULL);
         
         if (!NT_SUCCESS(Status)) {
             ERR("load_tree(%llx) returned %08x\n", th->address, Status);
@@ -3239,9 +3875,13 @@
             if (!NT_SUCCESS(Status)) {
                 WARN("find_item returned %08x\n", Status);
             } else {
-                if (!keycmp(tp.item->key, searchkey))
-                    delete_tree_item(Vcb, &tp, rollback);
-                else
+                if (!keycmp(tp.item->key, searchkey)) {
+                    Status = delete_tree_item(Vcb, &tp);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_tree_item returned %08x\n", Status);
+                        return Status;
+                    }
+                } else
                     WARN("could not find (%llx,%x,%llx) in uuid tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
             }
         }
@@ -3259,9 +3899,14 @@
         return Status;
     }
     
-    if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
-        delete_tree_item(Vcb, &tp, rollback);
-    else
+    if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+        Status = delete_tree_item(Vcb, &tp);
+        
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            return Status;
+        }
+    } else
         WARN("could not find (%llx,%x,%llx) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
     
     // delete items in tree cache
@@ -3292,7 +3937,7 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS update_dev_item(device_extension* Vcb, device* device, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS update_dev_item(device_extension* Vcb, device* device, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     DEV_ITEM* di;
@@ -3313,7 +3958,11 @@
         return STATUS_INTERNAL_ERROR;
     }
     
-    delete_tree_item(Vcb, &tp, rollback);
+    Status = delete_tree_item(Vcb, &tp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("delete_tree_item returned %08x\n", Status);
+        return Status;
+    }
     
     di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG);
     if (!di) {
@@ -3323,9 +3972,10 @@
     
     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, Irp, rollback)) {
-        ERR("insert_tree_item failed\n");
-        return STATUS_INTERNAL_ERROR;
+    Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, device->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
+        return Status;
     }
     
     return STATUS_SUCCESS;
@@ -3399,7 +4049,7 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, PIRP Irp) {
     CHUNK_ITEM* ci;
     CHUNK_ITEM_STRIPE* cis;
     BLOCK_GROUP_ITEM* bgi;
@@ -3414,10 +4064,11 @@
     
     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, Irp, rollback)) {
+    Status = insert_tree_item(Vcb, Vcb->chunk_root, 0x100, TYPE_CHUNK_ITEM, c->offset, ci, c->size, NULL, Irp);
+    if (!NT_SUCCESS(Status)) {
         ERR("insert_tree_item failed\n");
         ExFreePool(ci);
-        return STATUS_INTERNAL_ERROR;
+        return Status;
     }
 
     if (c->chunk_item->type & BLOCK_FLAG_SYSTEM) {
@@ -3440,10 +4091,11 @@
     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, Irp, rollback)) {
+    Status = insert_tree_item(Vcb, Vcb->extent_root, c->offset, TYPE_BLOCK_GROUP_ITEM, c->chunk_item->size, bgi, sizeof(BLOCK_GROUP_ITEM), NULL, Irp);
+    if (!NT_SUCCESS(Status)) {
         ERR("insert_tree_item failed\n");
         ExFreePool(bgi);
-        return STATUS_INSUFFICIENT_RESOURCES;
+        return Status;
     }
     
     if (c->chunk_item->type & BLOCK_FLAG_RAID0)
@@ -3476,14 +4128,15 @@
         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, Irp, rollback)) {
-            ERR("insert_tree_item failed\n");
+        Status = 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);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
             ExFreePool(de);
-            return STATUS_INTERNAL_ERROR;
+            return Status;
         }
         
         // FIXME - no point in calling this twice for the same device
-        Status = update_dev_item(Vcb, c->devices[i], Irp, rollback);
+        Status = update_dev_item(Vcb, c->devices[i], Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("update_dev_item returned %08x\n", Status);
             return Status;
@@ -3519,7 +4172,8 @@
 }
 
 static NTSTATUS STDCALL set_xattr(device_extension* Vcb, LIST_ENTRY* batchlist, root* subvol, UINT64 inode, char* name, UINT32 crc32,
-                                  UINT8* data, UINT16 datalen, PIRP Irp, LIST_ENTRY* rollback) {
+                                  UINT8* data, UINT16 datalen) {
+    NTSTATUS Status;
     ULONG xasize;
     DIR_ITEM* xa;
     
@@ -3543,106 +4197,50 @@
     RtlCopyMemory(xa->name, name, strlen(name));
     RtlCopyMemory(xa->name + strlen(name), data, datalen);
     
-    if (!insert_tree_item_batch(batchlist, Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, Batch_SetXattr, Irp, rollback))
-        return STATUS_INTERNAL_ERROR;
+    Status = insert_tree_item_batch(batchlist, Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, Batch_SetXattr);
+    if (!NT_SUCCESS(Status)) { 
+        ERR("insert_tree_item_batch returned %08x\n", Status);
+        return Status;
+    }
     
     return STATUS_SUCCESS;
 }
 
-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;
+static BOOL STDCALL delete_xattr(device_extension* Vcb, LIST_ENTRY* batchlist, root* subvol, UINT64 inode, char* name, UINT32 crc32) {
+    NTSTATUS Status;
+    ULONG xasize;
     DIR_ITEM* xa;
-    NTSTATUS Status;
     
     TRACE("(%p, %llx, %llx, %s, %08x)\n", Vcb, subvol->id, inode, name, crc32);
     
-    searchkey.obj_id = inode;
-    searchkey.obj_type = TYPE_XATTR_ITEM;
-    searchkey.offset = crc32;
+    xasize = sizeof(DIR_ITEM) - 1 + (ULONG)strlen(name);
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return FALSE;
+    xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG);
+    if (!xa) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    if (!keycmp(tp.item->key, searchkey)) { // key exists
-        ULONG size = tp.item->size;
-        
-        if (tp.item->size < sizeof(DIR_ITEM)) {
-            ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
-            
-            return FALSE;
-        } else {
-            xa = (DIR_ITEM*)tp.item->data;
-            
-            while (TRUE) {
-                ULONG oldxasize;
-                
-                if (size < sizeof(DIR_ITEM) || size < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) {
-                    ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                        
-                    return FALSE;
-                }
-                
-                oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
-                
-                if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
-                    ULONG newsize;
-                    UINT8 *newdata, *dioff;
-                    
-                    newsize = tp.item->size - (sizeof(DIR_ITEM) - 1 + xa->n + xa->m);
-                    
-                    delete_tree_item(Vcb, &tp, rollback);
-                    
-                    if (newsize == 0) {
-                        TRACE("xattr %s deleted\n", name);
-                        
-                        return TRUE;
-                    }
-
-                    // FIXME - deleting collisions almost certainly works, but we should test it properly anyway
-                    newdata = ExAllocatePoolWithTag(PagedPool, newsize, ALLOC_TAG);
-                    if (!newdata) {
-                        ERR("out of memory\n");
-                        return FALSE;
-                    }
-
-                    if ((UINT8*)xa > tp.item->data) {
-                        RtlCopyMemory(newdata, tp.item->data, (UINT8*)xa - tp.item->data);
-                        dioff = newdata + ((UINT8*)xa - tp.item->data);
-                    } else {
-                        dioff = newdata;
-                    }
-                    
-                    if ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data < tp.item->size)
-                        RtlCopyMemory(dioff, &xa->name[xa->n+xa->m], tp.item->size - ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data));
-                    
-                    insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, newsize, NULL, Irp, rollback);
-                    
-                        
-                    return TRUE;
-                }
-                
-                if (xa->m + xa->n >= size) { // FIXME - test this works
-                    WARN("xattr %s not found\n", name);
-
-                    return FALSE;
-                } else {
-                    xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
-                    size -= oldxasize;
-                }
-            }
-        }
-    } else {
-        WARN("xattr %s not found\n", name);
-        
-        return FALSE;
+    xa->key.obj_id = 0;
+    xa->key.obj_type = 0;
+    xa->key.offset = 0;
+    xa->transid = Vcb->superblock.generation;
+    xa->m = 0;
+    xa->n = (UINT16)strlen(name);
+    xa->type = BTRFS_TYPE_EA;
+    RtlCopyMemory(xa->name, name, strlen(name));
+    
+    Status = insert_tree_item_batch(batchlist, Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, Batch_DeleteXattr);
+    if (!NT_SUCCESS(Status)) { 
+        ERR("insert_tree_item_batch returned %08x\n", Status);
+        return Status;
     }
+    
+    return STATUS_SUCCESS;
 }
 
-static NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS insert_sparse_extent(fcb* fcb, LIST_ENTRY* batchlist, UINT64 start, UINT64 length) {
+    NTSTATUS Status;
     EXTENT_DATA* ed;
     EXTENT_DATA2* ed2;
     
@@ -3667,16 +4265,17 @@
     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;
+    Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), Batch_Insert);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
+        return Status;
     }
 
     return STATUS_SUCCESS;
 }
 
-static BOOL insert_tree_item_batch(LIST_ENTRY* batchlist, device_extension* Vcb, root* r, UINT64 objid, UINT64 objtype, UINT64 offset,
-                                   void* data, UINT16 datalen, enum batch_operation operation, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS insert_tree_item_batch(LIST_ENTRY* batchlist, device_extension* Vcb, root* r, UINT64 objid, UINT64 objtype, UINT64 offset,
+                                       void* data, UINT16 datalen, enum batch_operation operation) {
     LIST_ENTRY* le;
     batch_root* br = NULL;
     batch_item* bi;
@@ -3697,7 +4296,7 @@
         br = ExAllocatePoolWithTag(PagedPool, sizeof(batch_root), ALLOC_TAG);
         if (!br) {
             ERR("out of memory\n");
-            return FALSE;
+            return STATUS_INSUFFICIENT_RESOURCES;
         }
         
         br->r = r;
@@ -3708,7 +4307,7 @@
     bi = ExAllocateFromPagedLookasideList(&Vcb->batch_item_lookaside);
     if (!bi) {
         ERR("out of memory\n");
-        return FALSE;
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
     
     bi->key.obj_id = objid;
@@ -3724,7 +4323,7 @@
         
         if (keycmp(bi2->key, bi->key) != 1) {
             InsertHeadList(&bi2->list_entry, &bi->list_entry);
-            return TRUE;
+            return STATUS_SUCCESS;
         }
         
         le = le->Blink;
@@ -3732,7 +4331,7 @@
     
     InsertHeadList(&br->items, &bi->list_entry);
     
-    return TRUE;
+    return STATUS_SUCCESS;
 }
 
 typedef struct {
@@ -3746,7 +4345,7 @@
     LIST_ENTRY list_entry;
 } extent_range;
 
-static void rationalize_extents(fcb* fcb, PIRP Irp, LIST_ENTRY* rollback) {
+static void rationalize_extents(fcb* fcb, PIRP Irp) {
     LIST_ENTRY* le;
     LIST_ENTRY extent_ranges;
     extent_range* er;
@@ -3759,8 +4358,8 @@
     while (le != &fcb->extents) {
         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
         
-        if ((ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC) && ext->data->compression == BTRFS_COMPRESSION_NONE && ext->unique) {
-            EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
+        if ((ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) && ext->extent_data.compression == BTRFS_COMPRESSION_NONE && ext->unique) {
+            EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
             
             if (ed2->size != 0) {
                 LIST_ENTRY* le2;
@@ -3848,8 +4447,8 @@
                 while (le2 != &fcb->extents) {
                     extent* ext = CONTAINING_RECORD(le2, extent, list_entry);
                     
-                    if ((ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC) && ext->data->compression == BTRFS_COMPRESSION_NONE && ext->unique) {
-                        EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
+                    if ((ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) && ext->extent_data.compression == BTRFS_COMPRESSION_NONE && ext->unique) {
+                        EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
                 
                         if (ed2->size != 0 && ed2->address == er->address) {
                             NTSTATUS Status;
@@ -3861,7 +4460,7 @@
                                 goto end;
                             }
                             
-                            ext->data->decoded_size -= er->skip_start;
+                            ext->extent_data.decoded_size -= er->skip_start;
                             ed2->size -= er->skip_start;
                             ed2->address += er->skip_start;
                             ed2->offset -= er->skip_start;
@@ -3875,12 +4474,26 @@
                 }
                 
                 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM))
-                    add_checksum_entry(fcb->Vcb, er->address, er->skip_start / fcb->Vcb->superblock.sector_size, NULL, NULL, rollback);
+                    add_checksum_entry(fcb->Vcb, er->address, er->skip_start / fcb->Vcb->superblock.sector_size, NULL, NULL);
                 
-                decrease_chunk_usage(er->chunk, er->skip_start);
+                ExAcquireResourceExclusiveLite(&er->chunk->lock, TRUE);
                 
+                if (!er->chunk->cache_loaded) {
+                    NTSTATUS Status = load_cache_chunk(fcb->Vcb, er->chunk, NULL);
+                    
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("load_cache_chunk returned %08x\n", Status);
+                        ExReleaseResourceLite(&er->chunk->lock);
+                        goto end;
+                    }
+                }
+                
+                er->chunk->used -= er->skip_start;
+                
                 space_list_add(fcb->Vcb, er->chunk, TRUE, er->address, er->skip_start, NULL);
                 
+                ExReleaseResourceLite(&er->chunk->lock);
+                
                 er->address += er->skip_start;
                 er->length -= er->skip_start;
             }
@@ -3890,8 +4503,8 @@
                 while (le2 != &fcb->extents) {
                     extent* ext = CONTAINING_RECORD(le2, extent, list_entry);
                     
-                    if ((ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC) && ext->data->compression == BTRFS_COMPRESSION_NONE && ext->unique) {
-                        EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
+                    if ((ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) && ext->extent_data.compression == BTRFS_COMPRESSION_NONE && ext->unique) {
+                        EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
                 
                         if (ed2->size != 0 && ed2->address == er->address) {
                             NTSTATUS Status;
@@ -3903,7 +4516,7 @@
                                 goto end;
                             }
                             
-                            ext->data->decoded_size -= er->skip_end;
+                            ext->extent_data.decoded_size -= er->skip_end;
                             ed2->size -= er->skip_end;
                             
                             add_changed_extent_ref(er->chunk, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset,
@@ -3915,12 +4528,26 @@
                 }
                 
                 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM))
-                    add_checksum_entry(fcb->Vcb, er->address + er->length - er->skip_end, er->skip_end / fcb->Vcb->superblock.sector_size, NULL, NULL, rollback);
+                    add_checksum_entry(fcb->Vcb, er->address + er->length - er->skip_end, er->skip_end / fcb->Vcb->superblock.sector_size, NULL, NULL);
                 
-                decrease_chunk_usage(er->chunk, er->skip_end);
+                ExAcquireResourceExclusiveLite(&er->chunk->lock, TRUE);
                 
+                if (!er->chunk->cache_loaded) {
+                    NTSTATUS Status = load_cache_chunk(fcb->Vcb, er->chunk, NULL);
+                    
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("load_cache_chunk returned %08x\n", Status);
+                        ExReleaseResourceLite(&er->chunk->lock);
+                        goto end;
+                    }
+                }
+                
+                er->chunk->used -= er->skip_end;
+                
                 space_list_add(fcb->Vcb, er->chunk, TRUE, er->address + er->length - er->skip_end, er->skip_end, NULL);
                 
+                ExReleaseResourceLite(&er->chunk->lock);
+                
                 er->length -= er->skip_end;
             }
             
@@ -3969,8 +4596,8 @@
     while (le != &fcb->extents) {
         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
         
-        if ((ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC) && ext->data->compression == BTRFS_COMPRESSION_NONE && ext->unique) {
-            EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
+        if ((ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) && ext->extent_data.compression == BTRFS_COMPRESSION_NONE && ext->unique) {
+            EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
             
             if (ed2->size != 0) {
                 LIST_ENTRY* le2;
@@ -3992,7 +4619,7 @@
                         ed2->offset += ed2->address - er2->address;
                         ed2->address = er2->address;
                         ed2->size = er2->length;
-                        ext->data->decoded_size = ed2->size;
+                        ext->extent_data.decoded_size = ed2->size;
                         
                         add_changed_extent_ref(er2->chunk, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset,
                                                1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM);
@@ -4017,7 +4644,7 @@
     }
 }
 
-void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* batchlist, PIRP Irp, LIST_ENTRY* rollback) {
+NTSTATUS flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* batchlist, PIRP Irp) {
     traverse_ptr tp;
     KEY searchkey;
     NTSTATUS Status;
@@ -4030,21 +4657,27 @@
     
     if (fcb->ads) {
         if (fcb->deleted)
-            delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, Irp, rollback);
+            delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash);
         else {
-            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)fcb->adsdata.Buffer, fcb->adsdata.Length, Irp, rollback);
+            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)fcb->adsdata.Buffer, fcb->adsdata.Length);
             if (!NT_SUCCESS(Status)) {
                 ERR("set_xattr returned %08x\n", Status);
                 goto end;
             }
         }
+        
+        Status = STATUS_SUCCESS;
         goto end;
     }
     
     if (fcb->deleted) {
-        if (!insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0xffffffffffffffff, NULL, 0, Batch_DeleteInode, Irp, rollback))
-            ERR("insert_tree_item_batch failed\n");
+        Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0xffffffffffffffff, NULL, 0, Batch_DeleteInode);
+        if (!NT_SUCCESS(Status)) { 
+            ERR("insert_tree_item_batch returned %08x\n", Status);
+            goto end;
+        }
         
+        Status = STATUS_SUCCESS;
         goto end;
     }
     
@@ -4053,8 +4686,6 @@
 #endif
     
     if (fcb->extents_changed) {
-        BOOL b;
-        traverse_ptr next_tp;
         LIST_ENTRY* le;
         BOOL prealloc = FALSE, extents_inline = FALSE;
         UINT64 last_end;
@@ -4071,7 +4702,6 @@
                 if (ext->csum)
                     ExFreePool(ext->csum);
                 
-                ExFreePool(ext->data);
                 ExFreePool(ext);
             }
             
@@ -4082,14 +4712,14 @@
         while (le != &fcb->extents) {
             extent* ext = CONTAINING_RECORD(le, extent, list_entry);
             
-            if (ext->inserted && ext->csum && ext->data->type == EXTENT_TYPE_REGULAR) {
-                EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
+            if (ext->inserted && ext->csum && ext->extent_data.type == EXTENT_TYPE_REGULAR) {
+                EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
                 
                 if (ed2->size > 0) { // not sparse
-                    if (ext->data->compression == BTRFS_COMPRESSION_NONE)
-                        add_checksum_entry(fcb->Vcb, ed2->address + ed2->offset, ed2->num_bytes / fcb->Vcb->superblock.sector_size, ext->csum, Irp, rollback);
+                    if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE)
+                        add_checksum_entry(fcb->Vcb, ed2->address + ed2->offset, ed2->num_bytes / fcb->Vcb->superblock.sector_size, ext->csum, Irp);
                     else
-                        add_checksum_entry(fcb->Vcb, ed2->address, ed2->size / fcb->Vcb->superblock.sector_size, ext->csum, Irp, rollback);
+                        add_checksum_entry(fcb->Vcb, ed2->address, ed2->size / fcb->Vcb->superblock.sector_size, ext->csum, Irp);
                 }
             }
             
@@ -4097,7 +4727,7 @@
         }
         
         if (!IsListEmpty(&fcb->extents)) {
-            rationalize_extents(fcb, Irp, rollback);
+            rationalize_extents(fcb, Irp);
             
             // merge together adjacent EXTENT_DATAs pointing to same extent
             
@@ -4106,18 +4736,18 @@
                 LIST_ENTRY* le2 = le->Flink;
                 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
                 
-                if ((ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC) && le->Flink != &fcb->extents) {
+                if ((ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) && le->Flink != &fcb->extents) {
                     extent* nextext = CONTAINING_RECORD(le->Flink, extent, list_entry);
                         
-                    if (ext->data->type == nextext->data->type) {
-                        EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
-                        EXTENT_DATA2* ned2 = (EXTENT_DATA2*)nextext->data->data;
+                    if (ext->extent_data.type == nextext->extent_data.type) {
+                        EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
+                        EXTENT_DATA2* ned2 = (EXTENT_DATA2*)nextext->extent_data.data;
                         
                         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;
                         
-                            if (ext->data->compression == BTRFS_COMPRESSION_NONE && ext->csum) {
+                            if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE && ext->csum) {
                                 ULONG len = (ed2->num_bytes + ned2->num_bytes) / fcb->Vcb->superblock.sector_size;
                                 UINT32* csum;
                                 
@@ -4124,6 +4754,7 @@
                                 csum = ExAllocatePoolWithTag(NonPagedPool, len * sizeof(UINT32), ALLOC_TAG);
                                 if (!csum) {
                                     ERR("out of memory\n");
+                                    Status = STATUS_INSUFFICIENT_RESOURCES;
                                     goto end;
                                 }
                                 
@@ -4135,7 +4766,7 @@
                                 ext->csum = csum;
                             }
                             
-                            ext->data->generation = fcb->Vcb->superblock.generation;
+                            ext->extent_data.generation = fcb->Vcb->superblock.generation;
                             ed2->num_bytes += ned2->num_bytes;
                         
                             RemoveEntryList(&nextext->list_entry);
@@ -4143,7 +4774,6 @@
                             if (nextext->csum)
                                 ExFreePool(nextext->csum);
                         
-                            ExFreePool(nextext->data);
                             ExFreePool(nextext);
                         
                             c = get_chunk_from_address(fcb->Vcb, ed2->address);
@@ -4171,29 +4801,11 @@
         if (!fcb->created) {
             // delete existing EXTENT_DATA items
             
-            searchkey.obj_id = fcb->inode;
-            searchkey.obj_type = TYPE_EXTENT_DATA;
-            searchkey.offset = 0;
-            
-            Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
-            if (!NT_SUCCESS(Status)) {
-                ERR("error - find_item returned %08x\n", Status);
+            Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, 0, NULL, 0, Batch_DeleteExtentData);
+            if (!NT_SUCCESS(Status)) { 
+                ERR("insert_tree_item_batch returned %08x\n", Status);
                 goto end;
             }
-            
-            do {
-                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, Irp);
-                
-                if (b) {
-                    tp = next_tp;
-                    
-                    if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type))
-                        break;
-                }
-            } while (b);
         }
         
         // add new EXTENT_DATAs
@@ -4208,7 +4820,7 @@
             ext->inserted = FALSE;
             
             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);
+                Status = insert_sparse_extent(fcb, batchlist, last_end, ext->offset - last_end);
                 if (!NT_SUCCESS(Status)) {
                     ERR("insert_sparse_extent returned %08x\n", Status);
                     goto end;
@@ -4222,19 +4834,19 @@
                 goto end;
             }
             
-            RtlCopyMemory(ed, ext->data, ext->datalen);
+            RtlCopyMemory(ed, &ext->extent_data, ext->datalen);
             
-            if (!insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, ext->offset,
-                                ed, ext->datalen, Batch_Insert, Irp, rollback)) {
-                ERR("insert_tree_item_batch failed\n");
-                Status = STATUS_INTERNAL_ERROR;
+            Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, ext->offset,
+                                            ed, ext->datalen, Batch_Insert);
+            if (!NT_SUCCESS(Status)) { 
+                ERR("insert_tree_item_batch returned %08x\n", Status);
                 goto end;
             }
             
-            if (ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_PREALLOC)
+            if (ed->type == EXTENT_TYPE_PREALLOC)
                 prealloc = TRUE;
             
-            if (ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_INLINE)
+            if (ed->type == EXTENT_TYPE_INLINE)
                 extents_inline = TRUE;
             
             if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES)) {
@@ -4252,7 +4864,7 @@
         
         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);
+            Status = insert_sparse_extent(fcb, batchlist, last_end, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) - last_end);
             if (!NT_SUCCESS(Status)) {
                 ERR("insert_sparse_extent returned %08x\n", Status);
                 goto end;
@@ -4287,13 +4899,15 @@
                 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
                 if (!ii) {
                     ERR("out of memory\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
                     goto end;
                 }
                 
                 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, Irp, rollback)) {
-                    ERR("insert_tree_item failed\n");
+                Status = insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("insert_tree_item returned %08x\n", Status);
                     goto end;
                 }
                 
@@ -4300,7 +4914,7 @@
                 ii_offset = 0;
             } else {
                 ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
-                int3;
+                Status = STATUS_INTERNAL_ERROR;
                 goto end;
             }
         } else {
@@ -4313,9 +4927,13 @@
             ii_offset = tp.item->key.offset;
         }
         
-        if (!cache)
-            delete_tree_item(fcb->Vcb, &tp, rollback);
-        else {
+        if (!cache) {
+            Status = delete_tree_item(fcb->Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                goto end;
+            }
+        } else {
             searchkey.obj_id = fcb->inode;
             searchkey.obj_type = TYPE_INODE_ITEM;
             searchkey.offset = ii_offset;
@@ -4328,21 +4946,21 @@
             
             if (keycmp(tp.item->key, searchkey)) {
                 ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
-                int3;
+                Status = STATUS_INTERNAL_ERROR;
                 goto end;
             } else
                 RtlCopyMemory(tp.item->data, &fcb->inode_item, min(tp.item->size, sizeof(INODE_ITEM)));
         }
+
+#ifdef DEBUG_PARANOID
+        if (!extents_changed && fcb->type != BTRFS_TYPE_DIRECTORY && old_size != fcb->inode_item.st_size) {
+            ERR("error - size has changed but extents not marked as changed\n");
+            int3;
+        }
+#endif
     } else
         ii_offset = 0;
     
-#ifdef DEBUG_PARANOID
-    if (!extents_changed && fcb->type != BTRFS_TYPE_DIRECTORY && old_size != fcb->inode_item.st_size) {
-        ERR("error - size has changed but extents not marked as changed\n");
-        int3;
-    }
-#endif
-    
     fcb->created = FALSE;
         
     if (!cache && fcb->inode_item_changed) {
@@ -4349,14 +4967,16 @@
         ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
         if (!ii) {
             ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
             goto end;
         }
         
         RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
         
-        if (!insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, ii_offset, ii, sizeof(INODE_ITEM),
-                                    Batch_Insert, Irp, rollback)) {
-            ERR("insert_tree_item_batch failed\n");
+        Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, ii_offset, ii, sizeof(INODE_ITEM),
+                                        Batch_Insert);
+        if (!NT_SUCCESS(Status)) { 
+            ERR("insert_tree_item_batch returned %08x\n", Status);
             goto end;
         }
         
@@ -4364,9 +4984,10 @@
     }
     
     if (fcb->sd_dirty) {
-        Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), Irp, rollback);
+        Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd));
         if (!NT_SUCCESS(Status)) {
             ERR("set_xattr returned %08x\n", Status);
+            goto end;
         }
         
         fcb->sd_dirty = FALSE;
@@ -4393,13 +5014,13 @@
             val2--;
             *val2 = '0';
             
-            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, val2, val + sizeof(val) - val2, Irp, rollback);
+            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, val2, val + sizeof(val) - val2);
             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, Irp, rollback);
+            delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH);
         
         fcb->atts_changed = FALSE;
         fcb->atts_deleted = FALSE;
@@ -4407,13 +5028,13 @@
     
     if (fcb->reparse_xattr_changed) {
         if (fcb->reparse_xattr.Buffer && fcb->reparse_xattr.Length > 0) {
-            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, (UINT8*)fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length, Irp, rollback);
+            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, (UINT8*)fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
             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, Irp, rollback);
+            delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH);
         
         fcb->reparse_xattr_changed = FALSE;
     }
@@ -4420,30 +5041,103 @@
     
     if (fcb->ea_changed) {
         if (fcb->ea_xattr.Buffer && fcb->ea_xattr.Length > 0) {
-            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_EA, EA_EA_HASH, (UINT8*)fcb->ea_xattr.Buffer, fcb->ea_xattr.Length, Irp, rollback);
+            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_EA, EA_EA_HASH, (UINT8*)fcb->ea_xattr.Buffer, fcb->ea_xattr.Length);
             if (!NT_SUCCESS(Status)) {
                 ERR("set_xattr returned %08x\n", Status);
                 goto end;
             }
         } else
-            delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_EA, EA_EA_HASH, Irp, rollback);
+            delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_EA, EA_EA_HASH);
         
         fcb->ea_changed = FALSE;
     }
     
+    if (fcb->prop_compression_changed) {
+        if (fcb->prop_compression == PropCompression_None)
+            delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_PROP_COMPRESSION, EA_PROP_COMPRESSION_HASH);
+        else if (fcb->prop_compression == PropCompression_Zlib) {
+            const char zlib[] = "zlib";
+            
+            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_PROP_COMPRESSION, EA_PROP_COMPRESSION_HASH, (UINT8*)zlib, strlen(zlib));
+            if (!NT_SUCCESS(Status)) {
+                ERR("set_xattr returned %08x\n", Status);
+                goto end;
+            }
+        } else if (fcb->prop_compression == PropCompression_LZO) {
+            const char lzo[] = "lzo";
+            
+            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_PROP_COMPRESSION, EA_PROP_COMPRESSION_HASH, (UINT8*)lzo, strlen(lzo));
+            if (!NT_SUCCESS(Status)) {
+                ERR("set_xattr returned %08x\n", Status);
+                goto end;
+            }
+        }
+
+        fcb->prop_compression_changed = FALSE;
+    }
+    
+    Status = STATUS_SUCCESS;
+    
 end:
     fcb->dirty = FALSE;
+    
+    return Status;
 }
 
+void add_trim_entry_avoid_sb(device_extension* Vcb, device* dev, UINT64 address, UINT64 size) {
+    int i;
+    ULONG sblen = sector_align(sizeof(superblock), Vcb->superblock.sector_size);
+    
+    i = 0;
+    while (superblock_addrs[i] != 0) {
+        if (superblock_addrs[i] + sblen >= address && superblock_addrs[i] < address + size) {
+            if (superblock_addrs[i] > address)
+                add_trim_entry(dev, address, superblock_addrs[i] - address);
+
+            if (size <= superblock_addrs[i] + sblen - address)
+                return;
+                
+            size -= superblock_addrs[i] + sblen - address;
+            address = superblock_addrs[i] + sblen;
+        } else if (superblock_addrs[i] > address + size)
+            break;
+        
+        i++;
+    }
+    
+    add_trim_entry(dev, address, size);
+}
+
 static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* batchlist, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
     UINT64 i, factor;
-    CHUNK_ITEM_STRIPE* cis;
+    CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];;
     
     TRACE("dropping chunk %llx\n", c->offset);
     
+    if (c->chunk_item->type & BLOCK_FLAG_RAID0)
+        factor = c->chunk_item->num_stripes;
+    else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
+        factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes;
+    else if (c->chunk_item->type & BLOCK_FLAG_RAID5)
+        factor = c->chunk_item->num_stripes - 1;
+    else if (c->chunk_item->type & BLOCK_FLAG_RAID6)
+        factor = c->chunk_item->num_stripes - 2;
+    else // SINGLE, DUPLICATE, RAID1
+        factor = 1;
+    
+    // do TRIM
+    if (Vcb->trim && !Vcb->options.no_trim) {
+        UINT64 len = c->chunk_item->size / factor;
+        
+        for (i = 0; i < c->chunk_item->num_stripes; i++) {
+            if (c->devices[i] && c->devices[i]->devobj && !c->devices[i]->readonly && c->devices[i]->trim)
+                add_trim_entry_avoid_sb(Vcb, c->devices[i], cis[i].offset, len);
+        }
+    }
+    
     // remove free space cache
     if (c->cache) {
         c->cache->deleted = TRUE;
@@ -4454,10 +5148,15 @@
             return Status;
         }
         
-        flush_fcb(c->cache, TRUE, batchlist, Irp, rollback);
+        Status = flush_fcb(c->cache, TRUE, batchlist, Irp);
         
         free_fcb(c->cache);
         
+        if (!NT_SUCCESS(Status)) {
+            ERR("flush_fcb returned %08x\n", Status);
+            return Status;
+        }
+        
         searchkey.obj_id = FREE_SPACE_CACHE_ID;
         searchkey.obj_type = 0;
         searchkey.offset = c->offset;
@@ -4469,18 +5168,14 @@
         }
 
         if (!keycmp(tp.item->key, searchkey)) {
-            delete_tree_item(Vcb, &tp, rollback);
+            Status = delete_tree_item(Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
         }
     }
     
-    if (c->chunk_item->type & BLOCK_FLAG_RAID0)
-        factor = c->chunk_item->num_stripes;
-    else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
-        factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes;
-    else // SINGLE, DUPLICATE, RAID1
-        factor = 1;
-    
-    cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
     for (i = 0; i < c->chunk_item->num_stripes; i++) {
         if (!c->created) {
             // remove DEV_EXTENTs from tree 4
@@ -4495,7 +5190,11 @@
             }
             
             if (!keycmp(tp.item->key, searchkey)) {
-                delete_tree_item(Vcb, &tp, rollback);
+                Status = delete_tree_item(Vcb, &tp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_tree_item returned %08x\n", Status);
+                    return Status;
+                }
                 
                 if (tp.item->size >= sizeof(DEV_EXTENT)) {
                     DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
@@ -4502,7 +5201,7 @@
                     
                     c->devices[i]->devitem.bytes_used -= de->length;
                     
-                    space_list_add2(Vcb, &c->devices[i]->space, NULL, cis[i].offset, de->length, rollback);
+                    space_list_add2(&c->devices[i]->space, NULL, cis[i].offset, de->length, NULL, rollback);
                 }
             } else
                 WARN("could not find (%llx,%x,%llx) in dev tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
@@ -4510,7 +5209,7 @@
             UINT64 len = c->chunk_item->size / factor;
             
             c->devices[i]->devitem.bytes_used -= len;
-            space_list_add2(Vcb, &c->devices[i]->space, NULL, cis[i].offset, len, rollback);
+            space_list_add2(&c->devices[i]->space, NULL, cis[i].offset, len, NULL, rollback);
         }
     }
     
@@ -4535,7 +5234,11 @@
                 return STATUS_INTERNAL_ERROR;
             }
             
-            delete_tree_item(Vcb, &tp, rollback);
+            Status = delete_tree_item(Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
             
             di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG);
             if (!di) {
@@ -4545,9 +5248,10 @@
             
             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, Irp, rollback)) {
-                ERR("insert_tree_item failed\n");
-                return STATUS_INTERNAL_ERROR;
+            Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, c->devices[i]->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item returned %08x\n", Status);
+                return Status;
             }
             
             for (j = i + 1; j < c->chunk_item->num_stripes; j++) {
@@ -4569,9 +5273,14 @@
             return Status;
         }
         
-        if (!keycmp(tp.item->key, searchkey))
-            delete_tree_item(Vcb, &tp, rollback);
-        else
+        if (!keycmp(tp.item->key, searchkey)) {
+            Status = delete_tree_item(Vcb, &tp);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
+        } else
             WARN("could not find CHUNK_ITEM for chunk %llx\n", c->offset);
         
         // remove BLOCK_GROUP_ITEM from extent tree
@@ -4585,9 +5294,14 @@
             return Status;
         }
         
-        if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
-            delete_tree_item(Vcb, &tp, rollback);
-        else
+        if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+            Status = delete_tree_item(Vcb, &tp);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
+        } else
             WARN("could not find BLOCK_GROUP_ITEM for chunk %llx\n", c->offset);
     }
     
@@ -4640,6 +5354,7 @@
         ExFreePool(s);
     }
     
+    ExDeleteResourceLite(&c->range_locks_lock);
     ExDeleteResourceLite(&c->lock);
     ExDeleteResourceLite(&c->changed_extents_lock);
 
@@ -4679,23 +5394,12 @@
             le3 = c->cache->extents.Flink;
             while (le3 != &c->cache->extents) {
                 extent* ext = CONTAINING_RECORD(le3, extent, list_entry);
-                EXTENT_DATA* ed = ext->data;
+                EXTENT_DATA* ed = &ext->extent_data;
                 
                 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));
-                        break;
-                    }
-                    
                     if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
                         EXTENT_DATA2* 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));
-                            break;
-                        }
-                        
+
                         if (ed2->size != 0 && ed2->address >= c->offset && ed2->address + ed2->size <= c->offset + c->chunk_item->size)
                             used_minus_cache -= ed2->size;
                     }
@@ -4714,7 +5418,7 @@
                 return Status;
             }
         } else if (c->created) {
-            Status = create_chunk(Vcb, c, Irp, rollback);
+            Status = create_chunk(Vcb, c, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("create_chunk returned %08x\n", Status);
                 ExReleaseResourceLite(&c->lock);
@@ -4734,7 +5438,7 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -4773,7 +5477,11 @@
                 if (rr->dir == parinode && rr->n == utf8->Length && RtlCompareMemory(rr->name, utf8->Buffer, rr->n) == rr->n) {
                     ULONG newlen = tp.item->size - itemlen;
                     
-                    delete_tree_item(Vcb, &tp, rollback);
+                    Status = delete_tree_item(Vcb, &tp);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_tree_item returned %08x\n", Status);
+                        return Status;
+                    }
                     
                     if (newlen == 0) {
                         TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
@@ -4797,7 +5505,11 @@
                         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, Irp, rollback);
+                        Status = 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);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("insert_tree_item returned %08x\n", Status);
+                            return Status;
+                        }
                     }
                     
                     break;
@@ -4818,7 +5530,7 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -4849,18 +5561,25 @@
         RtlCopyMemory(rr2 + tp.item->size, rr, sizeof(ROOT_REF) - 1 + rr->n);
         ExFreePool(rr);
         
-        delete_tree_item(Vcb, &tp, rollback);
+        Status = delete_tree_item(Vcb, &tp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            ExFreePool(rr2);
+            return Status;
+        }
         
-        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");
+        Status = insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
             ExFreePool(rr2);
-            return STATUS_INTERNAL_ERROR;
+            return Status;
         }
     } 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, Irp, rollback)) {
-            ERR("error - failed to insert item\n");
+        Status = insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr, sizeof(ROOT_REF) - 1 + rr->n, NULL, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
             ExFreePool(rr);
-            return STATUS_INTERNAL_ERROR;
+            return Status;
         }
     }
     
@@ -4867,7 +5586,7 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     UINT8* data;
@@ -4912,14 +5631,24 @@
         return Status;
     }
     
-    if (!keycmp(tp.item->key, searchkey))
-        delete_tree_item(Vcb, &tp, rollback);
+    if (!keycmp(tp.item->key, searchkey)) {
+        Status = delete_tree_item(Vcb, &tp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            
+            if (datalen > 0)
+                ExFreePool(data);
+            
+            return Status;
+        }
+    }
     
     if (datalen > 0) {
-        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");
+        Status = insert_tree_item(Vcb, Vcb->root_root, subvolid, TYPE_ROOT_BACKREF, parsubvolid, data, datalen, NULL, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
             ExFreePool(data);
-            return STATUS_INTERNAL_ERROR;
+            return Status;
         }
     }
     
@@ -4926,7 +5655,7 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS add_root_item_to_cache(device_extension* Vcb, UINT64 root, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS add_root_item_to_cache(device_extension* Vcb, UINT64 root, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -4943,7 +5672,6 @@
     
     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;
     }
     
@@ -4959,11 +5687,16 @@
         
         RtlZeroMemory(((UINT8*)ri) + tp.item->size, sizeof(ROOT_ITEM) - tp.item->size);
         
-        delete_tree_item(Vcb, &tp, rollback);
+        Status = delete_tree_item(Vcb, &tp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            return Status;
+        }
         
-        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;
+        Status = insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
+            return Status;
         }
     } else {
         tp.tree->write = TRUE;
@@ -4972,7 +5705,7 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* batchlist, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* batchlist, PIRP Irp) {
     NTSTATUS Status;
     
     // if fileref created and then immediately deleted, do nothing
@@ -4991,9 +5724,9 @@
         DIR_ITEM *di, *di2;
         UINT32 crc32;
         
-        crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length);
+        crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
         
-        disize = sizeof(DIR_ITEM) - 1 + fileref->utf8.Length;
+        disize = sizeof(DIR_ITEM) - 1 + fileref->dc->utf8.Length;
         di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
         if (!di) {
             ERR("out of memory\n");
@@ -5012,9 +5745,9 @@
 
         di->transid = fileref->fcb->Vcb->superblock.generation;
         di->m = 0;
-        di->n = (UINT16)fileref->utf8.Length;
+        di->n = (UINT16)fileref->dc->utf8.Length;
         di->type = fileref->fcb->type;
-        RtlCopyMemory(di->name, fileref->utf8.Buffer, fileref->utf8.Length);
+        RtlCopyMemory(di->name, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
         
         di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
         if (!di2) {
@@ -5024,41 +5757,44 @@
         
         RtlCopyMemory(di2, di, disize);
 
-        if (!insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index,
-                                    di, disize, Batch_Insert, Irp, rollback)) {
-            ERR("insert_tree_item_batch failed\n");
-            return STATUS_INTERNAL_ERROR;
+        Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX,
+                                        fileref->dc->index, di, disize, Batch_Insert);
+        if (!NT_SUCCESS(Status)) { 
+            ERR("insert_tree_item_batch returned %08x\n", Status);
+            return Status;
         }
         
-        if (!insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_ITEM, crc32,
-                                    di2, disize, Batch_DirItem, Irp, rollback)) {
-            ERR("insert_tree_item_batch failed\n");
-            return STATUS_INTERNAL_ERROR;
+        Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_ITEM, crc32,
+                                        di2, disize, Batch_DirItem);
+        if (!NT_SUCCESS(Status)) { 
+            ERR("insert_tree_item_batch returned %08x\n", Status);
+            return Status;
         }
         
         if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
             INODE_REF* ir;
             
-            ir = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_REF) - 1 + fileref->utf8.Length, ALLOC_TAG);
+            ir = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_REF) - 1 + fileref->dc->utf8.Length, ALLOC_TAG);
             if (!ir) {
                 ERR("out of memory\n");
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
 
-            ir->index = fileref->index;
-            ir->n = fileref->utf8.Length;
-            RtlCopyMemory(ir->name, fileref->utf8.Buffer, ir->n);
+            ir->index = fileref->dc->index;
+            ir->n = fileref->dc->utf8.Length;
+            RtlCopyMemory(ir->name, fileref->dc->utf8.Buffer, ir->n);
         
-            if (!insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->fcb->subvol, fileref->fcb->inode, TYPE_INODE_REF, fileref->parent->fcb->inode,
-                                        ir, sizeof(INODE_REF) - 1 + ir->n, Batch_InodeRef, Irp, rollback)) {
-                ERR("insert_tree_item_batch failed\n");
-                return STATUS_INTERNAL_ERROR;
+            Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->fcb->subvol, fileref->fcb->inode, TYPE_INODE_REF, fileref->parent->fcb->inode,
+                                            ir, sizeof(INODE_REF) - 1 + ir->n, Batch_InodeRef);
+            if (!NT_SUCCESS(Status)) { 
+                ERR("insert_tree_item_batch returned %08x\n", Status);
+                return Status;
             }
         } else {
             ULONG rrlen;
             ROOT_REF* rr;
 
-            rrlen = sizeof(ROOT_REF) - 1 + fileref->utf8.Length;
+            rrlen = sizeof(ROOT_REF) - 1 + fileref->dc->utf8.Length;
                 
             rr = ExAllocatePoolWithTag(PagedPool, rrlen, ALLOC_TAG);
             if (!rr) {
@@ -5067,17 +5803,17 @@
             }
             
             rr->dir = fileref->parent->fcb->inode;
-            rr->index = fileref->index;
-            rr->n = fileref->utf8.Length;
-            RtlCopyMemory(rr->name, fileref->utf8.Buffer, fileref->utf8.Length);
+            rr->index = fileref->dc->index;
+            rr->n = fileref->dc->utf8.Length;
+            RtlCopyMemory(rr->name, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
             
-            Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp, rollback);
+            Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp);
             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, Irp, rollback);
+            Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("update_root_backref returned %08x\n", Status);
                 return Status;
@@ -5090,10 +5826,7 @@
         ANSI_STRING* name;
         DIR_ITEM* di;
         
-        if (fileref->oldutf8.Buffer)
-            name = &fileref->oldutf8;
-        else
-            name = &fileref->utf8;
+        name = &fileref->oldutf8;
 
         crc32 = calc_crc32c(0xfffffffe, (UINT8*)name->Buffer, name->Length);
 
@@ -5111,10 +5844,11 @@
         
         // delete DIR_ITEM (0x54)
         
-        if (!insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_ITEM,
-                                    crc32, di, sizeof(DIR_ITEM) - 1 + name->Length, Batch_DeleteDirItem, Irp, rollback)) {
-            ERR("insert_tree_item_batch failed\n");
-            return STATUS_INTERNAL_ERROR;
+        Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_ITEM,
+                                        crc32, di, sizeof(DIR_ITEM) - 1 + name->Length, Batch_DeleteDirItem);
+        if (!NT_SUCCESS(Status)) { 
+            ERR("insert_tree_item_batch returned %08x\n", Status);
+            return Status;
         }
         
         if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
@@ -5128,23 +5862,24 @@
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
             
-            ir->index = fileref->index;
+            ir->index = fileref->oldindex;
             ir->n = name->Length;
             RtlCopyMemory(ir->name, name->Buffer, name->Length);
 
-            if (!insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, TYPE_INODE_REF,
-                                        fileref->parent->fcb->inode, ir, sizeof(INODE_REF) - 1 + name->Length, Batch_DeleteInodeRef, Irp, rollback)) {
-                ERR("insert_tree_item_batch failed\n");
-                return STATUS_INTERNAL_ERROR;
+            Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, TYPE_INODE_REF,
+                                            fileref->parent->fcb->inode, ir, sizeof(INODE_REF) - 1 + name->Length, Batch_DeleteInodeRef);
+            if (!NT_SUCCESS(Status)) { 
+                ERR("insert_tree_item_batch 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, Irp, rollback);
+            Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, name, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_root_ref returned %08x\n", Status);
                 return Status;
             }
             
-            Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp, rollback);
+            Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("update_root_backref returned %08x\n", Status);
                 return Status;
@@ -5153,10 +5888,11 @@
         
         // delete DIR_INDEX (0x60)
         
-        if (!insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX,
-                                    fileref->index, NULL, 0, Batch_Delete, Irp, rollback)) {
-            ERR("insert_tree_item_batch failed\n");
-            return STATUS_INTERNAL_ERROR;
+        Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX,
+                                        fileref->oldindex, NULL, 0, Batch_Delete);
+        if (!NT_SUCCESS(Status)) { 
+            ERR("insert_tree_item_batch returned %08x\n", Status);
+            return Status;
         }
         
         if (fileref->oldutf8.Buffer) {
@@ -5164,12 +5900,12 @@
             fileref->oldutf8.Buffer = NULL;
         }
     } else { // rename or change type
-        PANSI_STRING oldutf8 = fileref->oldutf8.Buffer ? &fileref->oldutf8 : &fileref->utf8;
+        PANSI_STRING oldutf8 = fileref->oldutf8.Buffer ? &fileref->oldutf8 : &fileref->dc->utf8;
         UINT32 crc32, oldcrc32;
         ULONG disize;
         DIR_ITEM *olddi, *di, *di2;
         
-        crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length);
+        crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
         
         if (!fileref->oldutf8.Buffer)
             oldcrc32 = crc32;
@@ -5188,15 +5924,16 @@
         
         // delete DIR_ITEM (0x54)
         
-        if (!insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_ITEM,
-                                    oldcrc32, olddi, sizeof(DIR_ITEM) - 1 + oldutf8->Length, Batch_DeleteDirItem, Irp, rollback)) {
-            ERR("insert_tree_item_batch failed\n");
-            return STATUS_INTERNAL_ERROR;
+        Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_ITEM,
+                                        oldcrc32, olddi, sizeof(DIR_ITEM) - 1 + oldutf8->Length, Batch_DeleteDirItem);
+        if (!NT_SUCCESS(Status)) { 
+            ERR("insert_tree_item_batch returned %08x\n", Status);
+            return Status;
         }
 
         // add DIR_ITEM (0x54)
         
-        disize = sizeof(DIR_ITEM) - 1 + fileref->utf8.Length;
+        disize = sizeof(DIR_ITEM) - 1 + fileref->dc->utf8.Length;
         di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
         if (!di) {
             ERR("out of memory\n");
@@ -5222,16 +5959,17 @@
         
         di->transid = fileref->fcb->Vcb->superblock.generation;
         di->m = 0;
-        di->n = (UINT16)fileref->utf8.Length;
+        di->n = (UINT16)fileref->dc->utf8.Length;
         di->type = fileref->fcb->type;
-        RtlCopyMemory(di->name, fileref->utf8.Buffer, fileref->utf8.Length);
+        RtlCopyMemory(di->name, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
         
         RtlCopyMemory(di2, di, disize);
         
-        if (!insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_ITEM, crc32,
-                                    di, disize, Batch_DirItem, Irp, rollback)) {
-            ERR("insert_tree_item_batch failed\n");
-            return STATUS_INTERNAL_ERROR;
+        Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_ITEM, crc32,
+                                        di, disize, Batch_DirItem);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item_batch returned %08x\n", Status);
+            return Status;
         }
         
         if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
@@ -5245,32 +5983,34 @@
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
 
-            ir->index = fileref->index;
+            ir->index = fileref->dc->index;
             ir->n = oldutf8->Length;
             RtlCopyMemory(ir->name, oldutf8->Buffer, ir->n);
         
-            if (!insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->fcb->subvol, fileref->fcb->inode, TYPE_INODE_REF, fileref->parent->fcb->inode,
-                                        ir, sizeof(INODE_REF) - 1 + ir->n, Batch_DeleteInodeRef, Irp, rollback)) {
-                ERR("insert_tree_item_batch failed\n");
-                return STATUS_INTERNAL_ERROR;
+            Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->fcb->subvol, fileref->fcb->inode, TYPE_INODE_REF, fileref->parent->fcb->inode,
+                                            ir, sizeof(INODE_REF) - 1 + ir->n, Batch_DeleteInodeRef);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item_batch returned %08x\n", Status);
+                return Status;
             }
             
             // add INODE_REF (0xc)
             
-            ir2 = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_REF) - 1 + fileref->utf8.Length, ALLOC_TAG);
+            ir2 = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_REF) - 1 + fileref->dc->utf8.Length, ALLOC_TAG);
             if (!ir2) {
                 ERR("out of memory\n");
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
 
-            ir2->index = fileref->index;
-            ir2->n = fileref->utf8.Length;
-            RtlCopyMemory(ir2->name, fileref->utf8.Buffer, ir2->n);
+            ir2->index = fileref->dc->index;
+            ir2->n = fileref->dc->utf8.Length;
+            RtlCopyMemory(ir2->name, fileref->dc->utf8.Buffer, ir2->n);
         
-            if (!insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->fcb->subvol, fileref->fcb->inode, TYPE_INODE_REF, fileref->parent->fcb->inode,
-                                        ir2, sizeof(INODE_REF) - 1 + ir2->n, Batch_InodeRef, Irp, rollback)) {
-                ERR("insert_tree_item_batch failed\n");
-                return STATUS_INTERNAL_ERROR;
+            Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->fcb->subvol, fileref->fcb->inode, TYPE_INODE_REF, fileref->parent->fcb->inode,
+                                            ir2, sizeof(INODE_REF) - 1 + ir2->n, Batch_InodeRef);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item_batch returned %08x\n", Status);
+                return Status;
             }
         } else { // subvolume
             ULONG rrlen;
@@ -5278,12 +6018,12 @@
             
             // 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, oldutf8, Irp, rollback);
+            Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, oldutf8, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_root_ref returned %08x\n", Status);
             }
             
-            rrlen = sizeof(ROOT_REF) - 1 + fileref->utf8.Length;
+            rrlen = sizeof(ROOT_REF) - 1 + fileref->dc->utf8.Length;
             
             rr = ExAllocatePoolWithTag(PagedPool, rrlen, ALLOC_TAG);
             if (!rr) {
@@ -5292,17 +6032,17 @@
             }
             
             rr->dir = fileref->parent->fcb->inode;
-            rr->index = fileref->index;
-            rr->n = fileref->utf8.Length;
-            RtlCopyMemory(rr->name, fileref->utf8.Buffer, fileref->utf8.Length);
+            rr->index = fileref->dc->index;
+            rr->n = fileref->dc->utf8.Length;
+            RtlCopyMemory(rr->name, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length);
             
-            Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp, rollback);
+            Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp);
             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, Irp, rollback);
+            Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("update_root_backref returned %08x\n", Status);
                 return Status;
@@ -5311,18 +6051,20 @@
         
         // delete DIR_INDEX (0x60)
         
-        if (!insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX,
-                                    fileref->index, NULL, 0, Batch_Delete, Irp, rollback)) {
-            ERR("insert_tree_item_batch failed\n");
-            return STATUS_INTERNAL_ERROR;
+        Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX,
+                                        fileref->dc->index, NULL, 0, Batch_Delete);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item_batch returned %08x\n", Status);
+            return Status;
         }
         
         // add DIR_INDEX (0x60)
         
-        if (!insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX,
-                                    fileref->index, di2, disize, Batch_Insert, Irp, rollback)) {
-            ERR("insert_tree_item_batch failed\n");
-            return STATUS_INTERNAL_ERROR;
+       Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX,
+                                       fileref->dc->index, di2, disize, Batch_Insert);
+       if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item_batch returned %08x\n", Status);
+            return Status;
         }
 
         if (fileref->oldutf8.Buffer) {
@@ -5336,10 +6078,137 @@
     return STATUS_SUCCESS;
 }
 
-NTSTATUS STDCALL do_write(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
+static void flush_disk_caches(device_extension* Vcb) {
+    LIST_ENTRY* le;
+    ioctl_context context;
+    ULONG num;
+    
+    context.left = 0;
+    
+    le = Vcb->devices.Flink;
+    
+    while (le != &Vcb->devices) {
+        device* dev = CONTAINING_RECORD(le, device, list_entry);
+        
+        if (dev->devobj && !dev->readonly && dev->can_flush)
+            context.left++;
+        
+        le = le->Flink;
+    }
+    
+    if (context.left == 0)
+        return;
+    
+    num = 0;
+    
+    KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
+    
+    context.stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(ioctl_context_stripe) * context.left, ALLOC_TAG);
+    if (!context.stripes) {
+        ERR("out of memory\n");
+        return;
+    }
+    
+    RtlZeroMemory(context.stripes, sizeof(ioctl_context_stripe) * context.left);
+    
+    le = Vcb->devices.Flink;
+    
+    while (le != &Vcb->devices) {
+        device* dev = CONTAINING_RECORD(le, device, list_entry);
+        
+        if (dev->devobj && !dev->readonly && dev->can_flush) {
+            PIO_STACK_LOCATION IrpSp;
+            ioctl_context_stripe* stripe = &context.stripes[num];
+            
+            RtlZeroMemory(&stripe->apte, sizeof(ATA_PASS_THROUGH_EX));
+            
+            stripe->apte.Length = sizeof(ATA_PASS_THROUGH_EX);
+            stripe->apte.TimeOutValue = 5;
+            stripe->apte.CurrentTaskFile[6] = IDE_COMMAND_FLUSH_CACHE;
+
+            stripe->Irp = IoAllocateIrp(dev->devobj->StackSize, FALSE);
+            
+            if (!stripe->Irp) {
+                ERR("IoAllocateIrp failed\n");
+                goto nextdev;
+            }
+            
+            IrpSp = IoGetNextIrpStackLocation(stripe->Irp);
+            IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
+            
+            IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_ATA_PASS_THROUGH;
+            IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ATA_PASS_THROUGH_EX);
+            IrpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ATA_PASS_THROUGH_EX);
+            
+            stripe->Irp->AssociatedIrp.SystemBuffer = &stripe->apte;
+            stripe->Irp->Flags |= IRP_BUFFERED_IO | IRP_INPUT_OPERATION;
+            stripe->Irp->UserBuffer = &stripe->apte;
+            stripe->Irp->UserIosb = &stripe->iosb;
+            
+            IoSetCompletionRoutine(stripe->Irp, ioctl_completion, &context, TRUE, TRUE, TRUE);
+            
+            IoCallDriver(dev->devobj, stripe->Irp);
+            
+nextdev:
+            num++;
+        }
+        
+        le = le->Flink;
+    }
+    
+    KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+    
+    ExFreePool(context.stripes);
+}
+
+static NTSTATUS flush_changed_dev_stats(device_extension* Vcb, device* dev, PIRP Irp) {
     NTSTATUS Status;
+    KEY searchkey;
+    traverse_ptr tp;
+    ULONG statslen;
+    UINT64* stats;
+    
+    searchkey.obj_id = 0;
+    searchkey.obj_type = TYPE_DEV_STATS;
+    searchkey.offset = dev->devitem.dev_id;
+    
+    Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("find_item returned %08x\n", Status);
+        return Status;
+    }
+    
+    if (!keycmp(tp.item->key, searchkey)) {
+        Status = delete_tree_item(Vcb, &tp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            return Status;
+        }
+    }
+    
+    statslen = sizeof(UINT64) * 5;
+    stats = ExAllocatePoolWithTag(PagedPool, statslen, ALLOC_TAG);
+    if (!stats) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlCopyMemory(stats, dev->stats, statslen);
+    
+    Status = insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, dev->devitem.dev_id, stats, statslen, NULL, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
+        return Status;
+    }
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS STDCALL do_write2(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
     LIST_ENTRY *le, batchlist;
     BOOL cache_changed = FALSE;
+    volume_device_extension* vde;
 #ifdef DEBUG_FLUSH_TIMES
     UINT64 filerefs = 0, fcbs = 0;
     LARGE_INTEGER freq, time1, time2;
@@ -5356,6 +6225,8 @@
     time1 = KeQueryPerformanceCounter(&freq);
 #endif
     
+    ExAcquireResourceExclusiveLite(&Vcb->dirty_filerefs_lock, TRUE);
+    
     while (!IsListEmpty(&Vcb->dirty_filerefs)) {
         dirty_fileref* dirt;
         
@@ -5363,8 +6234,8 @@
         
         dirt = CONTAINING_RECORD(le, dirty_fileref, list_entry);
         
-        flush_fileref(dirt->fileref, &batchlist, Irp, rollback);
-        free_fileref(dirt->fileref);
+        flush_fileref(dirt->fileref, &batchlist, Irp);
+        free_fileref(Vcb, dirt->fileref);
         ExFreePool(dirt);
 
 #ifdef DEBUG_FLUSH_TIMES
@@ -5372,8 +6243,10 @@
 #endif
     }
     
-    commit_batch_list(Vcb, &batchlist, Irp, rollback);
+    ExReleaseResourceLite(&Vcb->dirty_filerefs_lock);
     
+    commit_batch_list(Vcb, &batchlist, Irp);
+    
 #ifdef DEBUG_FLUSH_TIMES
     time2 = KeQueryPerformanceCounter(NULL);
 
@@ -5385,6 +6258,8 @@
     // We process deleted streams first, so we don't run over our xattr
     // limit unless we absolutely have to.
     
+    ExAcquireResourceExclusiveLite(&Vcb->dirty_fcbs_lock, TRUE);
+    
     le = Vcb->dirty_fcbs.Flink;
     while (le != &Vcb->dirty_fcbs) {
         dirty_fcb* dirt;
@@ -5396,11 +6271,16 @@
             RemoveEntryList(le);
             
             ExAcquireResourceExclusiveLite(dirt->fcb->Header.Resource, TRUE);
-            flush_fcb(dirt->fcb, FALSE, &batchlist, Irp, rollback);
+            Status = flush_fcb(dirt->fcb, FALSE, &batchlist, Irp);
             ExReleaseResourceLite(dirt->fcb->Header.Resource);
             
             free_fcb(dirt->fcb);
             ExFreePool(dirt);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("flush_fcb returned %08x\n", Status);
+                return Status;
+            }
 
 #ifdef DEBUG_FLUSH_TIMES
             fcbs++;
@@ -5421,10 +6301,15 @@
             RemoveEntryList(le);
             
             ExAcquireResourceExclusiveLite(dirt->fcb->Header.Resource, TRUE);
-            flush_fcb(dirt->fcb, FALSE, &batchlist, Irp, rollback);
+            Status = flush_fcb(dirt->fcb, FALSE, &batchlist, Irp);
             ExReleaseResourceLite(dirt->fcb->Header.Resource);
             free_fcb(dirt->fcb);
             ExFreePool(dirt);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("flush_fcb returned %08x\n", Status);
+                return Status;
+            }
 
 #ifdef DEBUG_FLUSH_TIMES
             fcbs++;
@@ -5434,8 +6319,10 @@
         le = le2;
     }
     
-    commit_batch_list(Vcb, &batchlist, Irp, rollback);
+    ExReleaseResourceLite(&Vcb->dirty_fcbs_lock);
     
+    commit_batch_list(Vcb, &batchlist, Irp);
+    
 #ifdef DEBUG_FLUSH_TIMES
     time2 = KeQueryPerformanceCounter(NULL);
 
@@ -5460,7 +6347,7 @@
         }
     }
     
-    commit_batch_list(Vcb, &batchlist, Irp, rollback);
+    commit_batch_list(Vcb, &batchlist, Irp);
     
     // If only changing superblock, e.g. changing label, we still need to rewrite
     // the root tree so the generations match, otherwise you won't be able to mount on Linux.
@@ -5483,14 +6370,34 @@
     }
     
     // make sure we always update the extent tree
-    Status = add_root_item_to_cache(Vcb, BTRFS_ROOT_EXTENT, Irp, rollback);
+    Status = add_root_item_to_cache(Vcb, BTRFS_ROOT_EXTENT, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("add_root_item_to_cache returned %08x\n", Status);
         return Status;
     }
     
+    if (Vcb->stats_changed) {
+        le = Vcb->devices.Flink;
+        while (le != &Vcb->devices) {
+            device* dev = CONTAINING_RECORD(le, device, list_entry);
+            
+            if (dev->stats_changed) {
+                Status = flush_changed_dev_stats(Vcb, dev, Irp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("flush_changed_dev_stats returned %08x\n", Status);
+                    return Status;
+                }
+                dev->stats_changed = FALSE;
+            }
+            
+            le = le->Flink;
+        }
+        
+        Vcb->stats_changed = FALSE;
+    }
+    
     do {
-        Status = add_parents(Vcb, Irp, rollback);
+        Status = add_parents(Vcb, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("add_parents returned %08x\n", Status);
             goto end;
@@ -5526,7 +6433,7 @@
         if (cache_changed)
             ERR("cache has changed, looping again\n");
 #endif
-    } while (cache_changed || !trees_consistent(Vcb, rollback));
+    } while (cache_changed || !trees_consistent(Vcb));
     
 #ifdef DEBUG_WRITE_LOOPS
     ERR("%u loops\n", loops);
@@ -5560,7 +6467,7 @@
         Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
-            int3;
+            goto end;
         }
         
         if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
@@ -5571,12 +6478,13 @@
             Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("error - find_item returned %08x\n", Status);
-                int3;
+                goto end;
             }
             
             if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
                 ERR("error - could not find entry in extent tree for tree at %llx\n", t->header.address);
-                int3;
+                Status = STATUS_INTERNAL_ERROR;
+                goto end;
             }
         }
         
@@ -5586,6 +6494,9 @@
     
     Vcb->superblock.cache_generation = Vcb->superblock.generation;
     
+    if (!Vcb->options.no_barrier)
+        flush_disk_caches(Vcb);
+    
     Status = write_superblocks(Vcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("write_superblocks returned %08x\n", Status);
@@ -5592,6 +6503,20 @@
         goto end;
     }
     
+    vde = Vcb->vde;
+    ExAcquireResourceSharedLite(&vde->child_lock, TRUE);
+        
+    le = vde->children.Flink;
+    
+    while (le != &vde->children) {
+        volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+        
+        vc->generation = Vcb->superblock.generation;
+        le = le->Flink;
+    }
+    
+    ExReleaseResourceLite(&vde->child_lock);
+    
     clean_space_cache(Vcb);
     
     Vcb->superblock.generation++;
@@ -5624,6 +6549,25 @@
     return Status;
 }
 
+NTSTATUS STDCALL do_write(device_extension* Vcb, PIRP Irp) {
+    LIST_ENTRY rollback;
+    NTSTATUS Status;
+    
+    InitializeListHead(&rollback);
+    
+    Status = do_write2(Vcb, Irp, &rollback);
+    
+    if (!NT_SUCCESS(Status)) {
+        ERR("do_write2 returned %08x, dropping into readonly mode\n", Status);
+        Vcb->readonly = TRUE;
+        FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_FORCED_CLOSED);
+        do_rollback(Vcb, &rollback);
+    } else
+        clear_rollback(&rollback);
+    
+    return Status;
+}
+
 #ifdef DEBUG_STATS
 static void print_stats(device_extension* Vcb) {
     ERR("READ STATS:\n");
@@ -5640,6 +6584,8 @@
     ERR("number of overwrites: %llu\n", Vcb->stats.num_overwrites);
     ERR("total time taken: %llu\n", Vcb->stats.overwrite_total_time);
     ERR("number of creates: %llu\n", Vcb->stats.num_creates);
+    ERR("calls to open_fcb: %llu\n", Vcb->stats.open_fcb_calls);
+    ERR("time spent in open_fcb: %llu\n", Vcb->stats.open_fcb_time);
     ERR("total time taken: %llu\n", Vcb->stats.create_total_time);
     
     RtlZeroMemory(&Vcb->stats, sizeof(debug_stats));
@@ -5647,10 +6593,8 @@
 #endif
 
 static void do_flush(device_extension* Vcb) {
-    LIST_ENTRY rollback;
+    NTSTATUS Status;
     
-    InitializeListHead(&rollback);
-    
     FsRtlEnterFileSystem();
 
     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
@@ -5660,11 +6604,14 @@
 #endif
 
     if (Vcb->need_write && !Vcb->readonly)
-        do_write(Vcb, NULL, &rollback);
+        Status = do_write(Vcb, NULL);
+    else
+        Status = STATUS_SUCCESS;
     
     free_trees(Vcb);
     
-    clear_rollback(Vcb, &rollback);
+    if (!NT_SUCCESS(Status))
+        ERR("do_write returned %08x\n", Status);
 
     ExReleaseResourceLite(&Vcb->tree_lock);
 
Index: drivers/filesystems/btrfs/free-space.c
===================================================================
--- drivers/filesystems/btrfs/free-space.c	(révision 74430)
+++ drivers/filesystems/btrfs/free-space.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -21,8 +21,6 @@
 // this be a constant number of sectors, a constant 256 KB, or what?
 #define CACHE_INCREMENTS    64
 
-// #define DEBUG_SPACE_LISTS
-
 static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIST_ENTRY* batchlist, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     fcb* fcb;
@@ -45,7 +43,12 @@
     
     fcb->deleted = TRUE;
     
-    flush_fcb(fcb, FALSE, batchlist, Irp, rollback);
+    Status = flush_fcb(fcb, FALSE, batchlist, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("flush_fcb returned %08x\n", Status);
+        free_fcb(fcb);
+        return Status;
+    }
     
     free_fcb(fcb);
 
@@ -76,7 +79,11 @@
             break;
         
         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
-            delete_tree_item(Vcb, &tp, &rollback);
+            Status = delete_tree_item(Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                return Status;
+            }
             
             if (tp.item->size >= sizeof(FREE_SPACE_ITEM)) {
                 FREE_SPACE_ITEM* fsi = (FREE_SPACE_ITEM*)tp.item->data;
@@ -115,7 +122,7 @@
     Status = STATUS_SUCCESS;
     
     if (NT_SUCCESS(Status))
-        clear_rollback(Vcb, &rollback);
+        clear_rollback(&rollback);
     else
         do_rollback(Vcb, &rollback);
     
@@ -464,7 +471,7 @@
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    Status = read_file(c->cache, data, 0, c->cache->inode_item.st_size, NULL, NULL, FALSE);
+    Status = read_file(c->cache, data, 0, c->cache->inode_item.st_size, NULL, NULL);
     if (!NT_SUCCESS(Status)) {
         ERR("read_file returned %08x\n", Status);
         ExFreePool(data);
@@ -607,7 +614,11 @@
     
     InitializeListHead(&rollback);
     
-    delete_tree_item(Vcb, &tp, &rollback);
+    Status = delete_tree_item(Vcb, &tp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("delete_tree_item returned %08x\n", Status);
+        return Status;
+    }
     
     Status = excise_extents(Vcb, c->cache, 0, c->cache->inode_item.st_size, Irp, &rollback);
     if (!NT_SUCCESS(Status)) {
@@ -616,17 +627,30 @@
         return Status;
     }
     
-    clear_rollback(Vcb, &rollback);
+    clear_rollback(&rollback);
     
     c->cache->deleted = TRUE;
     mark_fcb_dirty(c->cache);
     
-    free_fcb(c->cache);
+    c->old_cache = c->cache;
     c->cache = NULL;
+    
+    le = c->space.Flink;
+    while (le != &c->space) {
+        space* s = CONTAINING_RECORD(le, space, list_entry);
+        LIST_ENTRY* le2 = le->Flink;
+        
+        RemoveEntryList(&s->list_entry);
+        RemoveEntryList(&s->list_entry_size);
+        ExFreePool(s);
+        
+        le = le2;
+    }
+    
     return STATUS_NOT_FOUND;
 }
 
-NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp) {
+static NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp) {
     traverse_ptr tp, next_tp;
     KEY searchkey;
     UINT64 lastaddr;
@@ -724,6 +748,25 @@
     return STATUS_SUCCESS;
 }
 
+NTSTATUS load_cache_chunk(device_extension* Vcb, chunk* c, PIRP Irp) {
+    NTSTATUS Status;
+    
+    if (c->cache_loaded)
+        return STATUS_SUCCESS;
+    
+    Status = load_free_space_cache(Vcb, c, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("load_free_space_cache returned %08x\n", Status);
+        return Status;
+    }
+    
+    protect_superblocks(Vcb, c);
+    
+    c->cache_loaded = TRUE;
+
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS insert_cache_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback) {
     LIST_ENTRY* le = fcb->Vcb->chunks.Flink;
     chunk* c;
@@ -740,7 +783,7 @@
             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, rollback, BTRFS_COMPRESSION_NONE, length)) {
+                if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, length, FALSE, 0)) {
                     ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
                     return STATUS_SUCCESS;
                 }
@@ -762,7 +805,7 @@
         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, rollback, BTRFS_COMPRESSION_NONE, length))
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, length, FALSE, 0))
                 return STATUS_SUCCESS;
         }
         
@@ -838,8 +881,8 @@
             while (le != &c->cache->extents) {
                 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
                 
-                if (!ext->ignore && (ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC)) {
-                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ext->data->data[0];
+                if (!ext->ignore && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
+                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ext->extent_data.data[0];
                     
                     if (ed2->size != 0) {
                         chunk* c2 = get_chunk_from_address(Vcb, ed2->address);
@@ -863,7 +906,7 @@
         
         // create new inode
         
-        c->cache = create_fcb(PagedPool);
+        c->cache = create_fcb(Vcb, PagedPool);
         if (!c->cache) {
             ERR("out of memory\n");
             return STATUS_INSUFFICIENT_RESOURCES;
@@ -912,18 +955,27 @@
             return Status;
         }
         
-        if (!keycmp(searchkey, tp.item->key))
-            delete_tree_item(Vcb, &tp, rollback);
+        if (!keycmp(searchkey, tp.item->key)) {
+            Status = delete_tree_item(Vcb, &tp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("delete_tree_item returned %08x\n", Status);
+                ExFreePool(fsi);
+                free_fcb(c->cache);
+                c->cache = NULL;
+                return Status;
+            }
+        }
         
         fsi->key.obj_id = c->cache->inode;
         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, Irp, rollback)) {
-            ERR("insert_tree_item failed\n");
+        Status = insert_tree_item(Vcb, Vcb->root_root, FREE_SPACE_CACHE_ID, 0, c->offset, fsi, sizeof(FREE_SPACE_ITEM), NULL, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item returned %08x\n", Status);
             free_fcb(c->cache);
             c->cache = NULL;
-            return STATUS_INTERNAL_ERROR;
+            return Status;
         }
         
         // allocate space
@@ -939,7 +991,13 @@
         c->cache->extents_changed = TRUE;
         InsertTailList(&Vcb->all_fcbs, &c->cache->list_entry_all);
         
-        flush_fcb(c->cache, TRUE, batchlist, Irp, rollback);
+        Status = flush_fcb(c->cache, TRUE, batchlist, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("flush_fcb returned %08x\n", Status);
+            free_fcb(c->cache);
+            c->cache = NULL;
+            return Status;
+        }
         
         *changed = TRUE;
     } else if (realloc_extents) {
@@ -980,8 +1038,8 @@
             while (le != &c->cache->extents) {
                 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
                 
-                if (!ext->ignore && (ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC)) {
-                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ext->data->data[0];
+                if (!ext->ignore && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) {
+                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ext->extent_data.data[0];
                     
                     if (ed2->size != 0) {
                         chunk* c2 = get_chunk_from_address(Vcb, ed2->address);
@@ -1014,7 +1072,11 @@
         c->cache->inode_item.st_size = new_cache_size;
         c->cache->inode_item.st_blocks = new_cache_size;
         
-        flush_fcb(c->cache, TRUE, batchlist, Irp, rollback);
+        Status = flush_fcb(c->cache, TRUE, batchlist, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("flush_fcb returned %08x\n", Status);
+            return Status;
+        }
     
         *changed = TRUE;
     } else {
@@ -1039,9 +1101,10 @@
             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, Irp, rollback)) {
-                ERR("insert_tree_item failed\n");
-                return STATUS_INTERNAL_ERROR;
+            Status = insert_tree_item(Vcb, Vcb->root_root, c->cache->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item returned %08x\n", Status);
+                return Status;
             }
             
             *changed = TRUE;
@@ -1066,7 +1129,6 @@
         
         if (keycmp(searchkey, tp.item->key)) {
             ERR("could not find (%llx,%x,%llx) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
-            int3;
             return STATUS_INTERNAL_ERROR;
         }
         
@@ -1111,12 +1173,12 @@
         le = le->Flink;
     }
     
-    commit_batch_list(Vcb, &batchlist, Irp, rollback);
+    commit_batch_list(Vcb, &batchlist, Irp);
     
     return STATUS_SUCCESS;
 }
 
-static void add_rollback_space(device_extension* Vcb, LIST_ENTRY* rollback, BOOL add, LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c) {
+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);
@@ -1131,17 +1193,13 @@
     rs->length = length;
     rs->chunk = c;
     
-    add_rollback(Vcb, rollback, add ? ROLLBACK_ADD_SPACE : ROLLBACK_SUBTRACT_SPACE, rs);
+    add_rollback(rollback, add ? ROLLBACK_ADD_SPACE : ROLLBACK_SUBTRACT_SPACE, rs);
 }
 
-void _space_list_add2(device_extension* Vcb, LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, 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) {
     LIST_ENTRY* le;
     space *s, *s2;
     
-#ifdef DEBUG_SPACE_LISTS
-    _debug_message(func, "called space_list_add (%p, %llx, %llx, %p)\n", list, address, length, rollback);
-#endif
-    
     if (IsListEmpty(list)) {
         s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
 
@@ -1158,7 +1216,7 @@
             InsertTailList(list_size, &s->list_entry_size);
         
         if (rollback)
-            add_rollback_space(Vcb, rollback, TRUE, list, list_size, address, length, c);
+            add_rollback_space(rollback, TRUE, list, list_size, address, length, c);
         
         return;
     }
@@ -1175,7 +1233,7 @@
         if (address <= s2->address && address + length >= s2->address + s2->size) {
             if (address < s2->address) {
                 if (rollback)
-                    add_rollback_space(Vcb, rollback, TRUE, list, list_size, address, s2->address - address, c);
+                    add_rollback_space(rollback, TRUE, list, list_size, address, s2->address - address, c);
                 
                 s2->size += s2->address - address;
                 s2->address = address;
@@ -1200,7 +1258,7 @@
             
             if (length > s2->size) {
                 if (rollback)
-                    add_rollback_space(Vcb, rollback, TRUE, list, list_size, s2->address + s2->size, address + length - s2->address - s2->size, c);
+                    add_rollback_space(rollback, TRUE, list, list_size, s2->address + s2->size, address + length - s2->address - s2->size, c);
                 
                 s2->size = length;
                 
@@ -1232,7 +1290,7 @@
         // new entry overlaps start of old one
         if (address < s2->address && address + length >= s2->address) {
             if (rollback)
-                add_rollback_space(Vcb, rollback, TRUE, list, list_size, address, s2->address - address, c);
+                add_rollback_space(rollback, TRUE, list, list_size, address, s2->address - address, c);
             
             s2->size += s2->address - address;
             s2->address = address;
@@ -1265,7 +1323,7 @@
         // new entry overlaps end of old one
         if (address <= s2->address + s2->size && address + length > s2->address + s2->size) {
             if (rollback)
-                add_rollback_space(Vcb, rollback, TRUE, list, list_size, address, s2->address + s2->size - address, c);
+                add_rollback_space(rollback, TRUE, list, list_size, address, s2->address + s2->size - address, c);
             
             s2->size = address + length - s2->address;
             
@@ -1303,7 +1361,7 @@
             }
             
             if (rollback)
-                add_rollback_space(Vcb, rollback, TRUE, list, list_size, address, length, c);
+                add_rollback_space(rollback, TRUE, list, list_size, address, length, c);
             
             s->address = address;
             s->size = length;
@@ -1346,10 +1404,10 @@
         order_space_entry(s, list_size);
     
     if (rollback)
-        add_rollback_space(Vcb, rollback, TRUE, list, list_size, address, length, c);
+        add_rollback_space(rollback, TRUE, list, list_size, address, length, c);
 }
 
-static void space_list_merge(device_extension* Vcb, LIST_ENTRY* spacelist, LIST_ENTRY* spacelist_size, LIST_ENTRY* deleting) {
+static void space_list_merge(LIST_ENTRY* spacelist, LIST_ENTRY* spacelist_size, LIST_ENTRY* deleting) {
     LIST_ENTRY* le;
     
     if (!IsListEmpty(deleting)) {
@@ -1357,7 +1415,7 @@
         while (le != deleting) {
             space* s = CONTAINING_RECORD(le, space, list_entry);
             
-            space_list_add2(Vcb, spacelist, spacelist_size, s->address, s->size, NULL);
+            space_list_add2(spacelist, spacelist_size, s->address, s->size, NULL, NULL);
             
             le = le->Flink;
         }
@@ -1375,7 +1433,7 @@
     UINT32* checksums;
     LIST_ENTRY* le;
     
-    space_list_merge(Vcb, &c->space, &c->space_size, &c->deleting);
+    space_list_merge(&c->space, &c->space_size, &c->deleting);
     
     data = ExAllocatePoolWithTag(NonPagedPool, c->cache->inode_item.st_size, ALLOC_TAG);
     if (!data) {
@@ -1415,7 +1473,11 @@
     c->cache->inode_item.sequence++;
     c->cache->inode_item.st_ctime = *now;
     
-    flush_fcb(c->cache, TRUE, batchlist, Irp, rollback);
+    Status = flush_fcb(c->cache, TRUE, batchlist, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("flush_fcb returned %08x\n", Status);
+        return Status;
+    }
     
     // update free_space item
     
@@ -1467,7 +1529,7 @@
     
     // write cache
     
-    Status = do_write_file(c->cache, 0, c->cache->inode_item.st_size, data, NULL, rollback);
+    Status = do_write_file(c->cache, 0, c->cache->inode_item.st_size, data, NULL, FALSE, 0, rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("do_write_file returned %08x\n", Status);
         return Status;
@@ -1506,12 +1568,12 @@
         le = le->Flink;
     }
     
-    commit_batch_list(Vcb, &batchlist, Irp, rollback);
+    commit_batch_list(Vcb, &batchlist, Irp);
     
     return STATUS_SUCCESS;
 }
 
-void _space_list_add(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) {
+void space_list_add(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback) {
     LIST_ENTRY* list;
     
     TRACE("(%p, %p, %u, %llx, %llx, %p)\n", Vcb, c, deleting, address, length, rollback);
@@ -1521,17 +1583,13 @@
     if (!c->list_entry_changed.Flink)
         InsertTailList(&Vcb->chunks_changed, &c->list_entry_changed);
     
-    _space_list_add2(Vcb, list, deleting ? NULL : &c->space_size, address, length, c, rollback, func);
+    space_list_add2(list, deleting ? NULL : &c->space_size, address, length, c, rollback);
 }
 
-void _space_list_subtract2(device_extension* Vcb, LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, 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) {
     LIST_ENTRY *le, *le2;
     space *s, *s2;
     
-#ifdef DEBUG_SPACE_LISTS
-    _debug_message(func, "called space_list_subtract (%p, %llx, %llx, %p)\n", list, address, length, rollback);
-#endif
-    
     if (IsListEmpty(list))
         return;
     
@@ -1545,7 +1603,7 @@
         
         if (s2->address >= address && s2->address + s2->size <= address + length) { // remove entry entirely
             if (rollback)
-                add_rollback_space(Vcb, rollback, FALSE, list, list_size, s2->address, s2->size, c);
+                add_rollback_space(rollback, FALSE, list, list_size, s2->address, s2->size, c);
             
             RemoveEntryList(&s2->list_entry);
             
@@ -1556,7 +1614,7 @@
         } else if (address + length > s2->address && address + length < s2->address + s2->size) {
             if (address > s2->address) { // cut out hole
                 if (rollback)
-                    add_rollback_space(Vcb, rollback, FALSE, list, list_size, address, length, c);
+                    add_rollback_space(rollback, FALSE, list, list_size, address, length, c);
                 
                 s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
 
@@ -1581,7 +1639,7 @@
                 return;
             } else { // remove start of entry
                 if (rollback)
-                    add_rollback_space(Vcb, rollback, FALSE, list, list_size, s2->address, address + length - s2->address, c);
+                    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;
@@ -1593,7 +1651,7 @@
             }
         } else if (address > s2->address && address < s2->address + s2->size) { // remove end of entry
             if (rollback)
-                add_rollback_space(Vcb, rollback, FALSE, list, list_size, address, s2->address + s2->size - address, c);
+                add_rollback_space(rollback, FALSE, list, list_size, address, s2->address + s2->size - address, c);
             
             s2->size = address - s2->address;
             
@@ -1607,7 +1665,7 @@
     }
 }
 
-void _space_list_subtract(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) {
+void space_list_subtract(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback) {
     LIST_ENTRY* list;
     
     list = deleting ? &c->deleting : &c->space;
@@ -1615,5 +1673,5 @@
     if (!c->list_entry_changed.Flink)
         InsertTailList(&Vcb->chunks_changed, &c->list_entry_changed);
     
-    _space_list_subtract2(Vcb, list, deleting ? NULL : &c->space_size, address, length, c, rollback, func);
+    space_list_subtract2(list, deleting ? NULL : &c->space_size, address, length, c, rollback);
 }
Index: drivers/filesystems/btrfs/fsctl.c
===================================================================
--- drivers/filesystems/btrfs/fsctl.c	(révision 74430)
+++ drivers/filesystems/btrfs/fsctl.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -17,9 +17,8 @@
 
 #include "btrfs_drv.h"
 #include "btrfsioctl.h"
-#ifndef __REACTOS__
-#include <winioctl.h>
-#endif
+#include <ntddstor.h>
+#include <ntdddisk.h>
 
 #ifndef FSCTL_CSV_CONTROL
 #define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS)
@@ -29,10 +28,13 @@
 
 #define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?)
 
+#ifndef _MSC_VER // not in mingw yet
+#define DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED 0x80000000
+#endif
+
 extern LIST_ENTRY VcbList;
 extern ERESOURCE global_loading_lock;
-extern LIST_ENTRY volumes;
-extern ERESOURCE volumes_lock;
+extern PDRIVER_OBJECT drvobj;
 
 static NTSTATUS get_file_ids(PFILE_OBJECT FileObject, void* data, ULONG length) {
     btrfs_get_file_ids* bgfi;
@@ -75,7 +77,7 @@
 static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* subvol, UINT64* newaddr, PIRP Irp, LIST_ENTRY* rollback) {
     UINT8* buf;
     NTSTATUS Status;
-    write_data_context* wtc;
+    write_data_context wtc;
     LIST_ENTRY* le;
     tree t;
     tree_header* th;
@@ -87,14 +89,10 @@
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_context), ALLOC_TAG);
-    if (!wtc) {
-        ERR("out of memory\n");
-        ExFreePool(buf);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
+    wtc.parity1 = wtc.parity2 = wtc.scratch = NULL;
+    wtc.mdl = wtc.parity1_mdl = wtc.parity2_mdl = NULL;
     
-    Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, NULL, Irp, FALSE);
+    Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, NULL, Irp, 0, FALSE, 0);
     if (!NT_SUCCESS(Status)) {
         ERR("read_data returned %08x\n", Status);
         goto end;
@@ -121,9 +119,9 @@
     
     c = get_chunk_from_address(Vcb, t.new_address);
             
-    if (c) {
-        increase_chunk_usage(c, Vcb->superblock.node_size);
-    } else {
+    if (c)
+        c->used += Vcb->superblock.node_size;
+    else {
         ERR("could not find chunk for address %llx\n", t.new_address);
         Status = STATUS_INTERNAL_ERROR;
         goto end;
@@ -146,7 +144,7 @@
                     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, Irp, 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);
                         
                         if (!NT_SUCCESS(Status)) {
                             ERR("increase_extent_refcount_data returned %08x\n", Status);
@@ -165,7 +163,7 @@
             
             tbr.offset = subvol->id;
             
-            Status = increase_extent_refcount(Vcb, in[i].address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF, &tbr, NULL, th->level - 1, Irp, rollback);
+            Status = increase_extent_refcount(Vcb, in[i].address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF, &tbr, NULL, th->level - 1, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("increase_extent_refcount returned %08x\n", Status);
                 goto end;
@@ -175,21 +173,21 @@
     
     *((UINT32*)buf) = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
     
-    KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE);
-    InitializeListHead(&wtc->stripes);
-    wtc->tree = TRUE;
-    wtc->stripes_left = 0;
+    KeInitializeEvent(&wtc.Event, NotificationEvent, FALSE);
+    InitializeListHead(&wtc.stripes);
+    wtc.tree = TRUE;
+    wtc.stripes_left = 0;
     
-    Status = write_data(Vcb, t.new_address, buf, FALSE, Vcb->superblock.node_size, wtc, NULL, NULL);
+    Status = write_data(Vcb, t.new_address, buf, Vcb->superblock.node_size, &wtc, NULL, NULL, FALSE, 0);
     if (!NT_SUCCESS(Status)) {
         ERR("write_data returned %08x\n", Status);
         goto end;
     }
     
-    if (wtc->stripes.Flink != &wtc->stripes) {
+    if (wtc.stripes.Flink != &wtc.stripes) {
         // launch writes and wait
-        le = wtc->stripes.Flink;
-        while (le != &wtc->stripes) {
+        le = wtc.stripes.Flink;
+        while (le != &wtc.stripes) {
             write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
             
             if (stripe->status != WriteDataStatus_Ignore)
@@ -198,14 +196,15 @@
             le = le->Flink;
         }
         
-        KeWaitForSingleObject(&wtc->Event, Executive, KernelMode, FALSE, NULL);
+        KeWaitForSingleObject(&wtc.Event, Executive, KernelMode, FALSE, NULL);
         
-        le = wtc->stripes.Flink;
-        while (le != &wtc->stripes) {
+        le = wtc.stripes.Flink;
+        while (le != &wtc.stripes) {
             write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
             
             if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) {
                 Status = stripe->iosb.Status;
+                log_device_error(Vcb, stripe->device, BTRFS_DEV_STAT_WRITE_ERRORS);
                 break;
             }
             
@@ -212,7 +211,7 @@
             le = le->Flink;
         }
         
-        free_write_data_stripes(wtc);
+        free_write_data_stripes(&wtc);
         buf = NULL;
     }
     
@@ -220,7 +219,6 @@
         *newaddr = t.new_address;
     
 end:
-    ExFreePool(wtc);
     
     if (buf)
         ExFreePool(buf);
@@ -228,7 +226,7 @@
     return Status;
 }
 
-static void flush_subvol_fcbs(root* subvol, LIST_ENTRY* rollback) {
+static void flush_subvol_fcbs(root* subvol) {
     LIST_ENTRY* le = subvol->fcbs.Flink;
     
     if (IsListEmpty(&subvol->fcbs))
@@ -252,7 +250,7 @@
     root *r, *subvol = subvol_fcb->subvol;
     KEY searchkey;
     traverse_ptr tp;
-    UINT64 address, dirpos, *root_num;
+    UINT64 address, *root_num;
     LARGE_INTEGER time;
     BTRFS_TIME now;
     fcb* fcb = parent->FsContext;
@@ -273,20 +271,23 @@
     
     fileref = ccb->fileref;
     
-    InitializeListHead(&rollback);
-    
     // flush open files on this subvol
     
-    flush_subvol_fcbs(subvol, &rollback);
+    flush_subvol_fcbs(subvol);
 
     // flush metadata
     
     if (Vcb->need_write)
-        do_write(Vcb, Irp, &rollback);
+        Status = do_write(Vcb, Irp);
+    else
+        Status = STATUS_SUCCESS;
     
     free_trees(Vcb);
     
-    clear_rollback(Vcb, &rollback);
+    if (!NT_SUCCESS(Status)) {
+        ERR("do_write returned %08x\n", Status);
+        return Status;
+    }
     
     InitializeListHead(&rollback);
     
@@ -293,7 +294,7 @@
     // create new root
     
     id = InterlockedIncrement64(&Vcb->root_root->lastinode);
-    Status = create_root(Vcb, id, &r, TRUE, Vcb->superblock.generation, Irp, &rollback);
+    Status = create_root(Vcb, id, &r, TRUE, Vcb->superblock.generation, Irp);
     
     if (!NT_SUCCESS(Status)) {
         ERR("create_root returned %08x\n", Status);
@@ -307,7 +308,7 @@
         
         TRACE("uuid root doesn't exist, creating it\n");
         
-        Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, Irp, &rollback);
+        Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, Irp);
         
         if (!NT_SUCCESS(Status)) {
             ERR("create_root returned %08x\n", Status);
@@ -338,10 +339,10 @@
     
     *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, Irp, &rollback)) {
-        ERR("insert_tree_item failed\n");
+    Status = insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
         ExFreePool(root_num);
-        Status = STATUS_INTERNAL_ERROR;
         goto end;
     }
     
@@ -414,7 +415,7 @@
     
     // create fileref for entry in other subvolume
     
-    fr = create_fileref();
+    fr = create_fileref(Vcb);
     if (!fr) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -421,55 +422,16 @@
         goto end;
     }
     
-    fr->utf8.Length = fr->utf8.MaximumLength = utf8->Length;
-    fr->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, fr->utf8.MaximumLength, ALLOC_TAG);
-    if (!fr->utf8.Buffer) {
-        ERR("out of memory\n");
-        free_fileref(fr);
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto end;
-    }
-    
-    RtlCopyMemory(fr->utf8.Buffer, utf8->Buffer, utf8->Length);
-    
     Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, fcb, &fr->fcb, PagedPool, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
-        free_fileref(fr);
+        free_fileref(Vcb, fr);
         goto end;
     }
     
-    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);
-        goto end;
-    }
-    
-    fr->index = dirpos;
-    
-    fr->filepart.MaximumLength = fr->filepart.Length = name->Length;
-    
-    fr->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fr->filepart.MaximumLength, ALLOC_TAG);
-    if (!fr->filepart.Buffer) {
-        ERR("out of memory\n");
-        free_fileref(fr);
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto end;
-    }
-    
-    RtlCopyMemory(fr->filepart.Buffer, name->Buffer, name->Length);
-    
-    Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-        free_fileref(fr);
-        goto end;
-    }
-    
     fr->parent = fileref;
     
-    Status = add_dir_child(fileref->fcb, r->id, TRUE, dirpos, utf8, &fr->filepart, &fr->filepart_uc, BTRFS_TYPE_DIRECTORY, &dc);
+    Status = add_dir_child(fileref->fcb, r->id, TRUE, utf8, name, BTRFS_TYPE_DIRECTORY, &dc);
     if (!NT_SUCCESS(Status))
         WARN("add_dir_child returned %08x\n", Status);
     
@@ -476,7 +438,10 @@
     fr->dc = dc;
     dc->fileref = fr;
     
-    insert_fileref_child(fileref, fr, TRUE);
+    ExAcquireResourceExclusiveLite(&fileref->nonpaged->children_lock, TRUE);
+    InsertTailList(&fileref->children, &fr->list_entry);
+    ExReleaseResourceLite(&fileref->nonpaged->children_lock);
+    
     increase_fileref_refcount(fileref);
     
     fr->created = TRUE;
@@ -485,7 +450,7 @@
     if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
         fr->fcb->fileref = fr;
     
-    free_fileref(fr);
+    free_fileref(Vcb, fr);
 
     // change fcb's INODE_ITEM
     
@@ -525,15 +490,16 @@
         le = le->Flink;
     }
     
-    do_write(Vcb, Irp, &rollback);
+    Status = do_write(Vcb, Irp);
     
     free_trees(Vcb);
     
-    Status = STATUS_SUCCESS;
+    if (!NT_SUCCESS(Status))
+        ERR("do_write returned %08x\n", Status);
     
 end:
     if (NT_SUCCESS(Status))
-        clear_rollback(Vcb, &rollback);
+        clear_rollback(&rollback);
     else
         do_rollback(Vcb, &rollback);
 
@@ -626,11 +592,11 @@
     if (NT_SUCCESS(Status)) {
         if (!fr2->deleted) {
             WARN("file already exists\n");
-            free_fileref(fr2);
+            free_fileref(Vcb, fr2);
             Status = STATUS_OBJECT_NAME_COLLISION;
             goto end3;
         } else
-            free_fileref(fr2);
+            free_fileref(Vcb, fr2);
     } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
         ERR("open_fileref returned %08x\n", Status);
         goto end3;
@@ -701,7 +667,7 @@
             Status = STATUS_SUCCESS;
         } else {
             send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED);
-            free_fileref(fr);
+            free_fileref(Vcb, fr);
         }
     }
     
@@ -718,11 +684,10 @@
 }
 
 static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, WCHAR* name, ULONG length, PIRP Irp) {
-    fcb *fcb, *rootfcb;
+    fcb *fcb, *rootfcb = NULL;
     ccb* ccb;
     file_ref* fileref;
     NTSTATUS Status;
-    LIST_ENTRY rollback;
     UINT64 id;
     root* r;
     LARGE_INTEGER time;
@@ -730,7 +695,6 @@
     ULONG len, irsize;
     UNICODE_STRING nameus;
     ANSI_STRING utf8;
-    UINT64 dirpos;
     INODE_REF* ir;
     KEY searchkey;
     traverse_ptr tp;
@@ -816,8 +780,6 @@
     KeQuerySystemTime(&time);
     win_time_to_unix(time, &now);
     
-    InitializeListHead(&rollback);
-    
     // no need for fcb_lock as we have tree_lock exclusively
     Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, FALSE, NULL, NULL, PagedPool, FALSE, Irp);
     
@@ -824,20 +786,18 @@
     if (NT_SUCCESS(Status)) {
         if (!fr2->deleted) {
             WARN("file already exists\n");
-            free_fileref(fr2);
+            free_fileref(Vcb, fr2);
             Status = STATUS_OBJECT_NAME_COLLISION;
             goto end;
         } else
-            free_fileref(fr2);
+            free_fileref(Vcb, fr2);
     } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
         ERR("open_fileref returned %08x\n", Status);
         goto end;
     }
     
-    // FIXME - make sure rollback removes new roots from internal structures
-    
     id = InterlockedIncrement64(&Vcb->root_root->lastinode);
-    Status = create_root(Vcb, id, &r, FALSE, 0, Irp, &rollback);
+    Status = create_root(Vcb, id, &r, FALSE, 0, Irp);
     
     if (!NT_SUCCESS(Status)) {
         ERR("create_root returned %08x\n", Status);
@@ -851,7 +811,7 @@
         
         TRACE("uuid root doesn't exist, creating it\n");
         
-        Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, Irp, &rollback);
+        Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, Irp);
         
         if (!NT_SUCCESS(Status)) {
             ERR("create_root returned %08x\n", Status);
@@ -882,9 +842,9 @@
     
     *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, Irp, &rollback)) {
-        ERR("insert_tree_item failed\n");
-        Status = STATUS_INTERNAL_ERROR;
+    Status = insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
         goto end;
     }
     
@@ -904,7 +864,7 @@
     
     // add .. inode to new subvol
     
-    rootfcb = create_fcb(PagedPool);
+    rootfcb = create_fcb(Vcb, PagedPool);
     if (!rootfcb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -924,7 +884,7 @@
     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, Irp);
+    rootfcb->atts = get_file_attributes(Vcb, rootfcb->subvol, rootfcb->inode, rootfcb->type, FALSE, TRUE, Irp);
     
     SeCaptureSubjectContext(&subjcont);
     
@@ -964,6 +924,12 @@
     
     rootfcb->created = TRUE;
     
+    if (fileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
+        rootfcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
+    
+    rootfcb->prop_compression = fileref->fcb->prop_compression;
+    rootfcb->prop_compression_changed = rootfcb->prop_compression != PropCompression_None;
+    
     r->lastinode = rootfcb->inode;
     
     // add INODE_REF
@@ -980,15 +946,15 @@
     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, Irp, &rollback)) {
-        ERR("insert_tree_item failed\n");
-        Status = STATUS_INTERNAL_ERROR;
+    Status = insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
         goto end;
     }
     
     // create fileref for entry in other subvolume
     
-    fr = create_fileref();
+    fr = create_fileref(Vcb);
     if (!fr) {
         ERR("out of memory\n");
         
@@ -1003,44 +969,10 @@
     fr->fcb = rootfcb;
     
     mark_fcb_dirty(rootfcb);
-    
-    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;
-    }
-    
-    fr->index = dirpos;
-    fr->utf8 = utf8;
-    
-    fr->filepart.MaximumLength = fr->filepart.Length = nameus.Length;
-    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;
-    }
-    
-    RtlCopyMemory(fr->filepart.Buffer, nameus.Buffer, nameus.Length);
-    
-    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;
-    }
-    
+
     fr->parent = fileref;
     
-    Status = add_dir_child(fileref->fcb, r->id, TRUE, dirpos, &utf8, &fr->filepart, &fr->filepart_uc, BTRFS_TYPE_DIRECTORY, &dc);
+    Status = add_dir_child(fileref->fcb, r->id, TRUE, &utf8, &nameus, BTRFS_TYPE_DIRECTORY, &dc);
     if (!NT_SUCCESS(Status))
         WARN("add_dir_child returned %08x\n", Status);
     
@@ -1051,7 +983,7 @@
     if (!fr->fcb->hash_ptrs) {
         ERR("out of memory\n");
         ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-        free_fileref(fr);
+        free_fileref(Vcb, fr);
         ExReleaseResourceLite(&Vcb->fcb_lock);
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end;
@@ -1063,7 +995,7 @@
     if (!fcb->hash_ptrs_uc) {
         ERR("out of memory\n");
         ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-        free_fileref(fr);
+        free_fileref(Vcb, fr);
         ExReleaseResourceLite(&Vcb->fcb_lock);
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end;
@@ -1071,7 +1003,10 @@
     
     RtlZeroMemory(fr->fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
     
-    insert_fileref_child(fileref, fr, TRUE);
+    ExAcquireResourceExclusiveLite(&fileref->nonpaged->children_lock, TRUE);
+    InsertTailList(&fileref->children, &fr->list_entry);
+    ExReleaseResourceLite(&fileref->nonpaged->children_lock);
+        
     increase_fileref_refcount(fileref);
     
     if (fr->fcb->type == BTRFS_TYPE_DIRECTORY)
@@ -1103,10 +1038,20 @@
     Status = STATUS_SUCCESS;    
     
 end:
-    if (!NT_SUCCESS(Status))
-        do_rollback(Vcb, &rollback);
-    else
-        clear_rollback(Vcb, &rollback);
+    if (!NT_SUCCESS(Status)) {
+        if (fr) {
+            fr->deleted = TRUE;
+            mark_fileref_dirty(fr);
+        } else if (rootfcb) {
+            rootfcb->deleted = TRUE;
+            mark_fcb_dirty(rootfcb);
+        }
+
+        if (r) {
+            RemoveEntryList(&r->list_entry);
+            InsertTailList(&Vcb->drop_roots, &r->list_entry);
+        }
+    }
     
     ExReleaseResourceLite(&Vcb->tree_lock);
     
@@ -1118,7 +1063,7 @@
 end2:
     if (fr) {
         ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-        free_fileref(fr);
+        free_fileref(Vcb, fr);
         ExReleaseResourceLite(&Vcb->fcb_lock);
     }
         
@@ -1176,18 +1121,18 @@
             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;
+                if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
+                    bii->inline_length += ext->extent_data.decoded_size;
                 } else {
-                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
+                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_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) {
+                        if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) {
                             bii->disk_size[0] += ed2->num_bytes;
-                        } else if (ext->data->compression == BTRFS_COMPRESSION_ZLIB) {
+                        } else if (ext->extent_data.compression == BTRFS_COMPRESSION_ZLIB) {
                             bii->disk_size[1] += ed2->size;
-                        } else if (ext->data->compression == BTRFS_COMPRESSION_LZO) {
+                        } else if (ext->extent_data.compression == BTRFS_COMPRESSION_LZO) {
                             bii->disk_size[2] += ed2->size;
                         }
                     }
@@ -1198,6 +1143,20 @@
         }
     }
     
+    switch (fcb->prop_compression) {
+        case PropCompression_Zlib:
+            bii->compression_type = BTRFS_COMPRESSION_ZLIB;
+        break;
+        
+        case PropCompression_LZO:
+            bii->compression_type = BTRFS_COMPRESSION_LZO;
+        break;
+        
+        default:
+            bii->compression_type = BTRFS_COMPRESSION_ANY;
+        break;
+    }
+    
     ExReleaseResourceLite(fcb->Header.Resource);
     
     return STATUS_SUCCESS;
@@ -1240,6 +1199,14 @@
         return STATUS_ACCESS_DENIED;
     }
     
+    if (bsii->compression_type_changed && bsii->compression_type > BTRFS_COMPRESSION_LZO)
+        return STATUS_INVALID_PARAMETER;
+    
+    if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+        WARN("trying to change inode on readonly subvolume\n");
+        return STATUS_ACCESS_DENIED;
+    }
+    
     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
     
     if (bsii->flags_changed) {
@@ -1307,7 +1274,25 @@
     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) {
+    if (bsii->compression_type_changed) {
+        switch (bsii->compression_type) {
+            case BTRFS_COMPRESSION_ANY:
+                fcb->prop_compression = PropCompression_None;
+            break;
+            
+            case BTRFS_COMPRESSION_ZLIB:
+                fcb->prop_compression = PropCompression_Zlib;
+            break;
+            
+            case BTRFS_COMPRESSION_LZO:
+                fcb->prop_compression = PropCompression_LZO;
+            break;
+        }
+        
+        fcb->prop_compression_changed = TRUE;
+    }
+    
+    if (bsii->flags_changed || bsii->mode_changed || bsii->uid_changed || bsii->gid_changed || bsii->compression_type_changed) {
         fcb->inode_item_changed = TRUE;
         mark_fcb_dirty(fcb);
     }
@@ -1352,10 +1337,11 @@
         
         dev->next_entry = 0;
         dev->dev_id = dev2->devitem.dev_id;
-        dev->size = dev2->length;
         dev->readonly = (Vcb->readonly || dev2->readonly) ? TRUE : FALSE;
         dev->device_number = dev2->disk_num;
         dev->partition_number = dev2->part_num;
+        dev->size = dev2->devitem.num_bytes;
+        RtlCopyMemory(dev->stats, dev2->stats, sizeof(UINT64) * 5);
         
         length -= sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen;
         
@@ -1545,7 +1531,7 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS fs_get_statistics(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, ULONG_PTR* retlen) {
+static NTSTATUS fs_get_statistics(void* buffer, DWORD buflen, ULONG_PTR* retlen) {
     FILESYSTEM_STATISTICS* fss;
     
     WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n");
@@ -1628,8 +1614,8 @@
         fcb->atts &= ~FILE_ATTRIBUTE_SPARSE_FILE;
         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, Irp);
+        defda = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type,
+                                    fileref && fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', TRUE, Irp);
         
         fcb->atts_deleted = defda == fcb->atts;
     }
@@ -1655,10 +1641,10 @@
     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));
+                       sector_align(fcb->inode_item.st_size, 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);
+        start_data = start & ~(UINT64)(Vcb->superblock.sector_size - 1);
+        end_data = sector_align(start + length, Vcb->superblock.sector_size);
     }
 
     data = ExAllocatePoolWithTag(PagedPool, end_data - start_data, ALLOC_TAG);
@@ -1670,7 +1656,7 @@
     RtlZeroMemory(data, end_data - start_data);
     
     if (start > start_data || start + length < end_data) {
-        Status = read_file(fcb, data, start_data, end_data - start_data, NULL, Irp, TRUE);
+        Status = read_file(fcb, data, start_data, end_data - start_data, NULL, Irp);
         
         if (!NT_SUCCESS(Status)) {
             ERR("read_file returned %08x\n", Status);
@@ -1691,7 +1677,7 @@
             return Status;
         }
     } else {
-        Status = do_write_file(fcb, start_data, end_data, data, Irp, rollback);
+        Status = do_write_file(fcb, start_data, end_data, data, Irp, FALSE, 0, rollback);
         
         ExFreePool(data);
         
@@ -1798,7 +1784,7 @@
         goto end;
     }
     
-    if (ext->datalen >= sizeof(EXTENT_DATA) && ext->data->type == EXTENT_TYPE_INLINE) {
+    if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
         Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("zero_data returned %08x\n", Status);
@@ -1874,7 +1860,7 @@
     if (!NT_SUCCESS(Status))
         do_rollback(Vcb, &rollback);
     else
-        clear_rollback(Vcb, &rollback);
+        clear_rollback(&rollback);
     
     ExReleaseResourceLite(fcb->Header.Resource);
     ExReleaseResourceLite(&Vcb->tree_lock);
@@ -1882,7 +1868,7 @@
     return Status;
 }
 
-static NTSTATUS query_ranges(device_extension* Vcb, PFILE_OBJECT FileObject, FILE_ALLOCATED_RANGE_BUFFER* inbuf, ULONG inbuflen, void* outbuf, ULONG outbuflen, ULONG_PTR* retlen) {
+static NTSTATUS query_ranges(PFILE_OBJECT FileObject, FILE_ALLOCATED_RANGE_BUFFER* inbuf, ULONG inbuflen, void* outbuf, ULONG outbuflen, ULONG_PTR* retlen) {
     NTSTATUS Status;
     fcb* fcb;
     LIST_ENTRY* le;
@@ -1936,8 +1922,8 @@
         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
         
         if (!ext->ignore) {
-            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;
+            EXTENT_DATA2* ed2 = (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ext->extent_data.data : NULL;
+            UINT64 len = ed2 ? ed2->num_bytes : ext->extent_data.decoded_size;
             
             if (ext->offset > last_end) { // first extent after a hole
                 if (last_end > last_start) {
@@ -2033,12 +2019,21 @@
 static NTSTATUS lock_volume(device_extension* Vcb, PIRP Irp) {
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     NTSTATUS Status;
-    LIST_ENTRY rollback;
     KIRQL irql;
     BOOL lock_paused_balance = FALSE;
     
     TRACE("FSCTL_LOCK_VOLUME\n");
     
+    if (Vcb->scrub.thread) {
+        WARN("cannot lock while scrub running\n");
+        return STATUS_DEVICE_NOT_READY;
+    }
+    
+    if (Vcb->balance.thread) {
+        WARN("cannot lock while balance running\n");
+        return STATUS_DEVICE_NOT_READY;
+    }
+    
     TRACE("locking volume\n");
     
     FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_LOCK);
@@ -2056,8 +2051,6 @@
     
     ExReleaseResourceLite(&Vcb->fcb_lock);
     
-    InitializeListHead(&rollback);
-    
     if (Vcb->balance.thread && KeReadStateEvent(&Vcb->balance.event)) {
         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
         KeClearEvent(&Vcb->balance.event);
@@ -2071,14 +2064,19 @@
     flush_fcb_caches(Vcb);
     
     if (Vcb->need_write && !Vcb->readonly)
-        do_write(Vcb, Irp, &rollback);
+        Status = do_write(Vcb, Irp);
+    else
+        Status = STATUS_SUCCESS;
     
     free_trees(Vcb);
     
-    clear_rollback(Vcb, &rollback);
-    
     ExReleaseResourceLite(&Vcb->tree_lock);
     
+    if (!NT_SUCCESS(Status)) {
+        ERR("do_write returned %08x\n", Status);
+        goto end;
+    }
+    
     IoAcquireVpbSpinLock(&irql);
 
     if (!(Vcb->Vpb->Flags & VPB_LOCKED)) { 
@@ -2191,7 +2189,6 @@
                 KIRQL irql;
                 PVPB newvpb;
                 BOOL free_newvpb = FALSE;
-                LIST_ENTRY rollback;
                 
                 newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG);
                 if (!newvpb) {
@@ -2206,8 +2203,6 @@
                 devobj->Vpb->Flags &= ~VPB_MOUNTED;
                 IoReleaseVpbSpinLock(irql);
                 
-                InitializeListHead(&rollback);
-                
                 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
                 
                 Vcb->removing = TRUE;
@@ -2221,11 +2216,17 @@
                 flush_fcb_caches(Vcb);
                 
                 if (Vcb->need_write && !Vcb->readonly)
-                    do_write(Vcb, Irp, &rollback);
+                    Status = do_write(Vcb, Irp);
+                else
+                    Status = STATUS_SUCCESS;
                 
                 free_trees(Vcb);
                 
-                clear_rollback(Vcb, &rollback);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("do_write returned %08x\n", Status);
+                    ExReleaseResourceLite(&Vcb->tree_lock);
+                    goto end;
+                }
                 
                 flush_fcb_caches(Vcb);
                 
@@ -2292,7 +2293,7 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS get_compression(device_extension* Vcb, PIRP Irp) {
+static NTSTATUS get_compression(PIRP Irp) {
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     USHORT* compression;
     
@@ -2320,35 +2321,26 @@
 
 static void update_volumes(device_extension* Vcb) {
     LIST_ENTRY* le;
+    volume_device_extension* vde;
     
+    vde = Vcb->vde;
+    
     ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
-    ExAcquireResourceExclusiveLite(&volumes_lock, TRUE);
     
-    le = volumes.Flink;
+    ExAcquireResourceExclusiveLite(&vde->child_lock, TRUE);
+    
+    le = vde->children.Flink;
+
+    while (le != &vde->children) {
+        volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
         
-    while (le != &volumes) {
-        volume* v = CONTAINING_RECORD(le, volume, list_entry);
-        
-        if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
-            LIST_ENTRY* le;
-            
-            le = Vcb->devices.Flink;
-            while (le != &Vcb->devices) {
-                device* dev = CONTAINING_RECORD(le, device, list_entry);
-                
-                if (RtlCompareMemory(&dev->devitem.device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
-                    v->gen1 = v->gen2 = Vcb->superblock.generation - 1;
-                    break;
-                }
-                
-                le = le->Flink;
-            }
-        }
-        
+        vc->generation = Vcb->superblock.generation - 1;
+
         le = le->Flink;
     }
     
-    ExReleaseResourceLite(&volumes_lock);
+    ExReleaseResourceLite(&vde->child_lock);
+    
     ExReleaseResourceLite(&Vcb->tree_lock);
 }
 
@@ -2355,7 +2347,7 @@
 static NTSTATUS dismount_volume(device_extension* Vcb, PIRP Irp) {
     NTSTATUS Status;
     KIRQL irql;
-    LIST_ENTRY rollback;
+    volume_device_extension* vde;
     
     TRACE("FSCTL_DISMOUNT_VOLUME\n");
     
@@ -2367,8 +2359,6 @@
         return STATUS_ACCESS_DENIED;
     }
     
-    InitializeListHead(&rollback);
-    
     Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT);
     if (!NT_SUCCESS(Status)) {
         WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status);
@@ -2376,18 +2366,25 @@
     
     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
     
-    flush_fcb_caches(Vcb);
+    if (!Vcb->locked) {
+        flush_fcb_caches(Vcb);
+        
+        if (Vcb->need_write && !Vcb->readonly) {
+            Status = do_write(Vcb, Irp);
+            
+            if (!NT_SUCCESS(Status))
+                ERR("do_write returned %08x\n", Status);
+        }
+    }
     
-    if (Vcb->need_write && !Vcb->readonly)
-        do_write(Vcb, Irp, &rollback);
-    
     free_trees(Vcb);
     
-    clear_rollback(Vcb, &rollback);
-    
     Vcb->removing = TRUE;
     update_volumes(Vcb);
     
+    vde = Vcb->vde;
+    vde->mounted_device = NULL;
+    
     ExReleaseResourceLite(&Vcb->tree_lock);
     
     IoAcquireVpbSpinLock(&irql);
@@ -2480,27 +2477,48 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS add_device(device_extension* Vcb, PIRP Irp, void* data, ULONG length, KPROCESSOR_MODE processor_mode) {
+void trim_whole_device(device* dev) {
+    DEVICE_MANAGE_DATA_SET_ATTRIBUTES dmdsa;
+    NTSTATUS Status;
+    
+    // FIXME - avoid "bootloader area"??
+    
+    dmdsa.Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
+    dmdsa.Action = DeviceDsmAction_Trim;
+    dmdsa.Flags = DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE | DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED;
+    dmdsa.ParameterBlockOffset = 0;
+    dmdsa.ParameterBlockLength = 0;
+    dmdsa.DataSetRangesOffset = 0;
+    dmdsa.DataSetRangesLength = 0;
+    
+    Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES, &dmdsa, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES), NULL, 0, TRUE, NULL);
+    if (!NT_SUCCESS(Status))
+        WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08x\n", Status);
+}
+
+static NTSTATUS add_device(device_extension* Vcb, PIRP Irp, KPROCESSOR_MODE processor_mode) {
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     NTSTATUS Status;
     PFILE_OBJECT fileobj, mountmgrfo;
+    PDEVICE_OBJECT DeviceObject;
     HANDLE h;
-    LIST_ENTRY rollback, *le;
-    GET_LENGTH_INFORMATION gli;
+    LIST_ENTRY* le;
     device* dev;
     DEV_ITEM* di;
-    UINT64 dev_id;
+    UINT64 dev_id, size;
     UINT8* mb;
     UINT64* stats;
-    MOUNTDEV_NAME mdn1, *mdn2;
-    UNICODE_STRING volname, mmdevpath;
-    volume* v;
+    UNICODE_STRING mmdevpath, pnp_name, pnp_name2;
+    volume_child* vc;
     PDEVICE_OBJECT mountmgr;
     KEY searchkey;
     traverse_ptr tp;
     STORAGE_DEVICE_NUMBER sdn;
+    volume_device_extension* vde;
+    const GUID* pnp_guid;
+    GET_LENGTH_INFORMATION gli;
     
-    volname.Buffer = NULL;
+    pnp_name.Buffer = NULL;
     
     if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
         return STATUS_PRIVILEGE_NOT_HELD;
@@ -2530,15 +2548,21 @@
         ERR("ObReferenceObjectByHandle returned %08x\n", Status);
         return Status;
     }
+
+    DeviceObject = fileobj->DeviceObject;
     
-    Status = is_device_part_of_mounted_btrfs_raid(fileobj->DeviceObject);
+    Status = get_device_pnp_name(DeviceObject, &pnp_name, &pnp_guid);
     if (!NT_SUCCESS(Status)) {
-        ERR("is_device_part_of_mounted_btrfs_raid returned %08x\n", Status);
+        ERR("get_device_pnp_name returned %08x\n", Status);
         ObDereferenceObject(fileobj);
         return Status;
     }
     
-    Status = dev_ioctl(fileobj->DeviceObject, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0, TRUE, NULL);
+    // If this is a disk, we have been handed the PDO, so need to go up to find something we can use
+    if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID) && DeviceObject->AttachedDevice)
+        DeviceObject = DeviceObject->AttachedDevice;
+    
+    Status = dev_ioctl(DeviceObject, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0, TRUE, NULL);
     if (!NT_SUCCESS(Status)) {
         ERR("IOCTL_DISK_IS_WRITABLE returned %08x\n", Status);
         ObDereferenceObject(fileobj);
@@ -2545,88 +2569,90 @@
         return Status;
     }
     
-    Status = dev_ioctl(fileobj->DeviceObject, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
-                       &gli, sizeof(gli), TRUE, NULL);
+    Status = is_device_part_of_mounted_btrfs_raid(DeviceObject);
     if (!NT_SUCCESS(Status)) {
-        ERR("error reading length information: %08x\n", Status);
+        ERR("is_device_part_of_mounted_btrfs_raid returned %08x\n", Status);
         ObDereferenceObject(fileobj);
         return Status;
     }
-    
-    if (gli.Length.QuadPart < 0x100000) {
-        ERR("device was not large enough to hold FS (%llx bytes, need at least 1 MB)\n", gli.Length.QuadPart);
-        ObDereferenceObject(fileobj);
-        return STATUS_INTERNAL_ERROR;
-    }
-    
-    Status = dev_ioctl(fileobj->DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0,
-                       &mdn1, sizeof(MOUNTDEV_NAME), TRUE, NULL);
-    if (Status == STATUS_BUFFER_OVERFLOW) {
-        mdn2 = ExAllocatePoolWithTag(PagedPool, offsetof(MOUNTDEV_NAME, Name[0]) + mdn1.NameLength, ALLOC_TAG);
-        if (!mdn2) {
-            ERR("out of memory\n");
-            ObDereferenceObject(fileobj);
-            return STATUS_INTERNAL_ERROR;
-        }
+
+    // if disk, check it has no partitions
+    if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID)) {
+        ULONG dlisize;
+        DRIVE_LAYOUT_INFORMATION_EX* dli = NULL;
         
-        Status = dev_ioctl(fileobj->DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0,
-                           mdn2, offsetof(MOUNTDEV_NAME, Name[0]) + mdn1.NameLength, TRUE, NULL);
+        dlisize = 0;
         
-        if (!NT_SUCCESS(Status)) {
-            ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
-            ObDereferenceObject(fileobj);
-            return Status;
+        do {
+            dlisize += 1024;
+            
+            if (dli)
+                ExFreePool(dli);
+            
+            dli = ExAllocatePoolWithTag(PagedPool, dlisize, ALLOC_TAG);
+            if (!dli) {
+                ERR("out of memory\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto end2;
+            }
+        
+            Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, dli, dlisize, TRUE, NULL);
+        } while (Status == STATUS_BUFFER_TOO_SMALL);
+        
+        if (NT_SUCCESS(Status) && dli->PartitionCount > 0) {
+            ExFreePool(dli);
+            ERR("not adding disk which has partitions\n");
+            Status = STATUS_DEVICE_NOT_READY;
+            goto end2;
         }
-    } else if (NT_SUCCESS(Status)) {
-        mdn2 = ExAllocatePoolWithTag(PagedPool, sizeof(MOUNTDEV_NAME), ALLOC_TAG);
-        if (!mdn2) {
-            ERR("out of memory\n");
+        
+        ExFreePool(dli);
+    }
+    
+    Status = dev_ioctl(DeviceObject, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
+                       &sdn, sizeof(STORAGE_DEVICE_NUMBER), TRUE, NULL);
+    if (NT_SUCCESS(Status)) {
+        if (sdn.DeviceType != FILE_DEVICE_DISK) { // FIXME - accept floppies and CDs?
+            WARN("device was not disk\n"); 
             ObDereferenceObject(fileobj);
-            return STATUS_INTERNAL_ERROR;
+            return STATUS_INVALID_PARAMETER;
         }
-        
-        RtlCopyMemory(mdn2, &mdn1, sizeof(MOUNTDEV_NAME));
     } else {
-        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
+        sdn.DeviceNumber = 0xffffffff;
+        sdn.PartitionNumber = 0xffffffff;
+    }
+    
+    Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
+                        &gli, sizeof(gli), TRUE, NULL);
+    if (!NT_SUCCESS(Status)) {
+        ERR("error reading length information: %08x\n", Status);
         ObDereferenceObject(fileobj);
         return Status;
     }
     
-    if (mdn2->NameLength == 0) {
-        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned zero-length name\n");
+    size = gli.Length.QuadPart;
+    
+    if (size < 0x100000) {
+        ERR("device was not large enough to hold FS (%llx bytes, need at least 1 MB)\n", size);
         ObDereferenceObject(fileobj);
-        ExFreePool(mdn2);
         return STATUS_INTERNAL_ERROR;
     }
     
-    volname.Length = volname.MaximumLength = mdn2->NameLength;
-    volname.Buffer = ExAllocatePoolWithTag(PagedPool, volname.MaximumLength, ALLOC_TAG);
-    if (!volname.Buffer) {
-        ERR("out of memory\n");
-        ObDereferenceObject(fileobj);
-        ExFreePool(mdn2);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
+    volume_removal(drvobj, &pnp_name);
     
-    RtlCopyMemory(volname.Buffer, mdn2->Name, volname.Length);
-    ExFreePool(mdn2);
-    
-    InitializeListHead(&rollback);
-    
     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
         
-    if (Vcb->need_write) {
-        Status = do_write(Vcb, Irp, &rollback);
-        if (!NT_SUCCESS(Status)) {
-            ERR("do_write returned %08x\n", Status);
-            do_rollback(Vcb, &rollback);
-            goto end;
-        }
-    }
+    if (Vcb->need_write)
+        Status = do_write(Vcb, Irp);
+    else
+        Status = STATUS_SUCCESS;
     
     free_trees(Vcb);
     
-    clear_rollback(Vcb, &rollback);
+    if (!NT_SUCCESS(Status)) {
+        ERR("do_write returned %08x\n", Status);
+        goto end;
+    }
     
     dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
     if (!dev) {
@@ -2637,18 +2663,16 @@
     
     RtlZeroMemory(dev, sizeof(device));
 
-    dev->devobj = fileobj->DeviceObject;
+    dev->devobj = DeviceObject;
     dev->seeding = FALSE;
-    dev->length = gli.Length.QuadPart;
-    init_device(Vcb, dev, FALSE, TRUE);
+    init_device(Vcb, dev, TRUE);
     
     InitializeListHead(&dev->space);
     
-    if (gli.Length.QuadPart > 0x100000) { // add disk hole - the first MB is marked as used
-        Status = add_space_entry(&dev->space, NULL, 0x100000, gli.Length.QuadPart - 0x100000);
+    if (size > 0x100000) { // add disk hole - the first MB is marked as used
+        Status = add_space_entry(&dev->space, NULL, 0x100000, size - 0x100000);
         if (!NT_SUCCESS(Status)) {
             ERR("add_space_entry returned %08x\n", Status);
-            Status = STATUS_INTERNAL_ERROR;
             goto end;
         }
     }
@@ -2668,7 +2692,7 @@
     dev_id++;
     
     dev->devitem.dev_id = dev_id;
-    dev->devitem.num_bytes = gli.Length.QuadPart;
+    dev->devitem.num_bytes = size;
     dev->devitem.bytes_used = 0;
     dev->devitem.optimal_io_align = Vcb->superblock.sector_size;
     dev->devitem.optimal_io_width = Vcb->superblock.sector_size;
@@ -2690,10 +2714,10 @@
     
     RtlCopyMemory(di, &dev->devitem, sizeof(DEV_ITEM));
     
-    if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, di->dev_id, di, sizeof(DEV_ITEM), NULL, Irp, &rollback)) {
-        ERR("insert_tree_item failed\n");
+    Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, di->dev_id, di, sizeof(DEV_ITEM), NULL, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
         ExFreePool(di);
-        Status = STATUS_INTERNAL_ERROR;
         goto end;
     }
     
@@ -2701,7 +2725,7 @@
     stats = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * 5, ALLOC_TAG);
     if (!stats) {
         ERR("out of memory\n");
-        Status = STATUS_INTERNAL_ERROR;
+        Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end;
     }
     
@@ -2714,19 +2738,29 @@
     Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
+        ExFreePool(stats);
         goto end;
     }
     
-    if (!keycmp(tp.item->key, searchkey))
-        delete_tree_item(Vcb, &tp, &rollback);
+    if (!keycmp(tp.item->key, searchkey)) {
+        Status = delete_tree_item(Vcb, &tp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_tree_item returned %08x\n", Status);
+            ExFreePool(stats);
+            goto end;
+        }
+    }
     
-    if (!insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, di->dev_id, stats, sizeof(UINT64) * 5, NULL, Irp, &rollback)) {
-        ERR("insert_tree_item failed\n");
+    Status = insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, di->dev_id, stats, sizeof(UINT64) * 5, NULL, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("insert_tree_item returned %08x\n", Status);
         ExFreePool(stats);
-        Status = STATUS_INTERNAL_ERROR;
         goto end;
     }
     
+    if (dev->trim && !dev->readonly && !Vcb->options.no_trim)
+        trim_whole_device(dev);
+    
     // We clear the first megabyte of the device, so Windows doesn't identify it as another FS
     mb = ExAllocatePoolWithTag(PagedPool, 0x100000, ALLOC_TAG);
     if (!mb) {
@@ -2737,7 +2771,7 @@
     
     RtlZeroMemory(mb, 0x100000);
     
-    Status = write_data_phys(fileobj->DeviceObject, 0, mb, 0x100000);
+    Status = write_data_phys(DeviceObject, 0, mb, 0x100000);
     if (!NT_SUCCESS(Status)) {
         ERR("write_data_phys returned %08x\n", Status);
         goto end;
@@ -2745,71 +2779,104 @@
     
     ExFreePool(mb);
     
-    v = ExAllocatePoolWithTag(PagedPool, sizeof(volume), ALLOC_TAG);
-    if (!v) {
+    vde = Vcb->vde;
+    
+    vc = ExAllocatePoolWithTag(NonPagedPool, sizeof(volume_child), ALLOC_TAG);
+    if (!vc) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end;
     }
     
-    v->fsuuid = Vcb->superblock.uuid;
-    v->devuuid = dev->devitem.device_uuid;
-    v->devnum = dev_id;
-    v->devpath = volname;
-    v->length = gli.Length.QuadPart;
-    v->gen1 = v->gen2 = Vcb->superblock.generation;
-    v->seeding = FALSE;
-    v->processed = TRUE;
+    vc->uuid = dev->devitem.device_uuid;
+    vc->devid = dev_id;
+    vc->generation = Vcb->superblock.generation;
+    vc->devobj = DeviceObject;
+    vc->notification_entry = NULL;
     
-    ExAcquireResourceExclusiveLite(&volumes_lock, TRUE);
-    InsertTailList(&volumes, &v->list_entry);
-    ExReleaseResourceLite(&volumes_lock);
+    Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, fileobj,
+                                            drvobj, pnp_removal, vde, &vc->notification_entry);
+    if (!NT_SUCCESS(Status))
+        WARN("IoRegisterPlugPlayNotification returned %08x\n", Status);
     
-    volname.Buffer = NULL;
+    pnp_name2 = pnp_name;
     
-    Status = dev_ioctl(fileobj->DeviceObject, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
-                       &sdn, sizeof(STORAGE_DEVICE_NUMBER), TRUE, NULL);
-    if (!NT_SUCCESS(Status)) {
-        WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status);
-        v->disk_num = 0;
-        v->part_num = 0;
-    } else {
-        v->disk_num = sdn.DeviceNumber;
-        v->part_num = sdn.PartitionNumber;
+    if (pnp_name.Length > 4 * sizeof(WCHAR) && pnp_name.Buffer[0] == '\\' && (pnp_name.Buffer[1] == '\\' || pnp_name.Buffer[1] == '?') &&
+        pnp_name.Buffer[2] == '?' && pnp_name.Buffer[3] == '\\') {
+        pnp_name2.Buffer = &pnp_name2.Buffer[3];
+        pnp_name2.Length -= 3 * sizeof(WCHAR);
+        pnp_name2.MaximumLength -= 3 * sizeof(WCHAR);
     }
     
+    vc->pnp_name.Length = vc->pnp_name.MaximumLength = pnp_name2.Length;
+    
+    if (pnp_name2.Length == 0)
+        vc->pnp_name.Buffer = NULL;
+    else {
+        vc->pnp_name.Buffer = ExAllocatePoolWithTag(PagedPool, pnp_name2.Length, ALLOC_TAG);
+        if (!vc->pnp_name.Buffer) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
+        }
+        
+        RtlCopyMemory(vc->pnp_name.Buffer, pnp_name2.Buffer, pnp_name2.Length);
+    }
+    
+    vc->size = size;
+    vc->seeding = FALSE;
+    vc->disk_num = sdn.DeviceNumber;
+    vc->part_num = sdn.PartitionNumber;
+    vc->had_drive_letter = FALSE;
+    
+    ExAcquireResourceExclusiveLite(&vde->child_lock, TRUE);
+    InsertTailList(&vde->children, &vc->list_entry);
+    vde->num_children++;
+    vde->children_loaded++;
+    ExReleaseResourceLite(&vde->child_lock);
+    
     RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
     Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
     if (!NT_SUCCESS(Status))
         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
     else {
-        remove_drive_letter(mountmgr, v);
+        Status = remove_drive_letter(mountmgr, &pnp_name);
+        if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND)
+            WARN("remove_drive_letter returned %08x\n", Status);
         
+        vc->had_drive_letter = NT_SUCCESS(Status);
+        
         ObDereferenceObject(mountmgrfo);
     }
     
     Vcb->superblock.num_devices++;
-    Vcb->superblock.total_bytes += gli.Length.QuadPart;
+    Vcb->superblock.total_bytes += size;
     Vcb->devices_loaded++;
     InsertTailList(&Vcb->devices, &dev->list_entry);
     
-    ObReferenceObject(fileobj->DeviceObject);
+    // FIXME - send notification that volume size has increased
     
-    do_write(Vcb, Irp, &rollback);
-
-    free_trees(Vcb);
+    ObReferenceObject(DeviceObject); // once for vde
+    ObReferenceObject(DeviceObject); // and again for Vcb
     
-    clear_rollback(Vcb, &rollback);
+    Status = do_write(Vcb, Irp);
+    if (!NT_SUCCESS(Status))
+        ERR("do_write returned %08x\n", Status);
     
-    Status = STATUS_SUCCESS;
+end:
+    free_trees(Vcb);
     
-end:
     ExReleaseResourceLite(&Vcb->tree_lock);
+    
+end2:
     ObDereferenceObject(fileobj);
     
-    if (volname.Buffer)
-        ExFreePool(volname.Buffer);
+    if (pnp_name.Buffer)
+        ExFreePool(pnp_name.Buffer);
     
+    if (NT_SUCCESS(Status))
+        FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_CHANGE_SIZE);
+    
     return Status;
 }
 
@@ -2848,7 +2915,51 @@
     return STATUS_SUCCESS;
 }
 
-NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user) {
+static NTSTATUS reset_stats(device_extension* Vcb, void* data, ULONG length, KPROCESSOR_MODE processor_mode) {
+    UINT64 devid;
+    NTSTATUS Status;
+    LIST_ENTRY* le;
+    
+    if (length < sizeof(UINT64))
+        return STATUS_INVALID_PARAMETER;
+    
+    if (Vcb->readonly)
+        return STATUS_MEDIA_WRITE_PROTECTED;
+    
+    if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
+        return STATUS_PRIVILEGE_NOT_HELD;
+    
+    devid = *((UINT64*)data);
+    
+    ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
+    
+    le = Vcb->devices.Flink;
+
+    while (le != &Vcb->devices) {
+        device* dev = CONTAINING_RECORD(le, device, list_entry);
+        
+        if (dev->devitem.dev_id == devid) {
+            RtlZeroMemory(dev->stats, sizeof(UINT64) * 5);
+            dev->stats_changed = TRUE;
+            Vcb->stats_changed = TRUE;
+            Vcb->need_write = TRUE;
+            Status = STATUS_SUCCESS;
+            goto end;
+        }
+        
+        le = le->Flink;
+    }
+    
+    WARN("device %llx not found\n", devid);
+    Status = STATUS_INVALID_PARAMETER;
+    
+end:
+    ExReleaseResourceLite(&Vcb->tree_lock);
+    
+    return Status;
+}
+
+NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type) {
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     NTSTATUS Status;
     
@@ -2915,7 +3026,7 @@
             break;
 
         case FSCTL_GET_COMPRESSION:
-            Status = get_compression(DeviceObject->DeviceExtension, Irp);
+            Status = get_compression(Irp);
             break;
 
         case FSCTL_SET_COMPRESSION:
@@ -2948,8 +3059,7 @@
             break;
 
         case FSCTL_FILESYSTEM_GET_STATISTICS:
-            Status = fs_get_statistics(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer,
-                                       IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
+            Status = fs_get_statistics(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
             break;
 
         case FSCTL_GET_NTFS_VOLUME_DATA:
@@ -3054,7 +3164,7 @@
             break;
 
         case FSCTL_QUERY_ALLOCATED_RANGES:
-            Status = query_ranges(DeviceObject->DeviceExtension, IrpSp->FileObject, IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
+            Status = query_ranges(IrpSp->FileObject, IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
                                   IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->UserBuffer,
                                   IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information);
             break;
@@ -3364,15 +3474,39 @@
             break;
             
         case FSCTL_BTRFS_ADD_DEVICE:
-            Status = add_device(DeviceObject->DeviceExtension, Irp, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
+            Status = add_device(DeviceObject->DeviceExtension, Irp, Irp->RequestorMode);
             break;
             
         case FSCTL_BTRFS_REMOVE_DEVICE:
             Status = remove_device(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
-        break;
+            break;
         
         case FSCTL_BTRFS_GET_UUID:
             Status = query_uuid(DeviceObject->DeviceExtension, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
+            break;
+        
+        case FSCTL_BTRFS_START_SCRUB:
+            Status = start_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode);
+            break;
+        
+        case FSCTL_BTRFS_QUERY_SCRUB:
+            Status = query_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode, map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
+            break;
+        
+        case FSCTL_BTRFS_PAUSE_SCRUB:
+            Status = pause_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode);
+            break;
+
+        case FSCTL_BTRFS_RESUME_SCRUB:
+            Status = resume_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode);
+            break;
+
+        case FSCTL_BTRFS_STOP_SCRUB:
+            Status = stop_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode);
+            break;
+            
+        case FSCTL_BTRFS_RESET_STATS:
+            Status = reset_stats(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
         break;
 
         default:
Index: drivers/filesystems/btrfs/galois.c
===================================================================
--- drivers/filesystems/btrfs/galois.c	(révision 74430)
+++ drivers/filesystems/btrfs/galois.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
Index: drivers/filesystems/btrfs/pnp.c
===================================================================
--- drivers/filesystems/btrfs/pnp.c	(révision 74430)
+++ drivers/filesystems/btrfs/pnp.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -38,6 +38,8 @@
     pnp_stripe* stripe = conptr;
     pnp_context* context = (pnp_context*)stripe->context;
     
+    UNUSED(DeviceObject);
+    
     stripe->Status = Irp->IoStatus.Status;
     
     InterlockedDecrement(&context->left);
@@ -49,33 +51,26 @@
 }
 
 static NTSTATUS send_disks_pnp_message(device_extension* Vcb, UCHAR minor) {
-    pnp_context* context;
+    pnp_context context;
     UINT64 num_devices, i;
     NTSTATUS Status;
     LIST_ENTRY* le;
     
-    context = ExAllocatePoolWithTag(NonPagedPool, sizeof(pnp_context), ALLOC_TAG);
-    if (!context) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
     ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
     
-    RtlZeroMemory(context, sizeof(pnp_context));
-    KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
+    RtlZeroMemory(&context, sizeof(pnp_context));
+    KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
     
     num_devices = Vcb->superblock.num_devices;
     
-    context->stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(pnp_stripe) * num_devices, ALLOC_TAG);
-    if (!context->stripes) {
+    context.stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(pnp_stripe) * num_devices, ALLOC_TAG);
+    if (!context.stripes) {
         ERR("out of memory\n");
-        ExFreePool(context);
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end2;
     }
     
-    RtlZeroMemory(context->stripes, sizeof(pnp_stripe) * num_devices);
+    RtlZeroMemory(context.stripes, sizeof(pnp_stripe) * num_devices);
     
     i = 0;
     le = Vcb->devices.Flink;
@@ -85,75 +80,73 @@
         device* dev = CONTAINING_RECORD(le, device, list_entry);
         
         if (dev->devobj) {
-            context->stripes[i].context = (struct pnp_context*)context;
+            context.stripes[i].context = (struct pnp_context*)&context;
 
-            context->stripes[i].Irp = IoAllocateIrp(dev->devobj->StackSize, FALSE);
+            context.stripes[i].Irp = IoAllocateIrp(dev->devobj->StackSize, FALSE);
             
-            if (!context->stripes[i].Irp) {
+            if (!context.stripes[i].Irp) {
                 UINT64 j;
                 
                 ERR("IoAllocateIrp failed\n");
                 
                 for (j = 0; j < i; j++) {
-                    if (context->stripes[j].dev->devobj) {
-                        IoFreeIrp(context->stripes[j].Irp);
+                    if (context.stripes[j].dev->devobj) {
+                        IoFreeIrp(context.stripes[j].Irp);
                     }
                 }
-                ExFreePool(context->stripes);
-                ExFreePool(context);
+                ExFreePool(context.stripes);
                 
                 Status = STATUS_INSUFFICIENT_RESOURCES;
                 goto end2;
             }
             
-            IrpSp = IoGetNextIrpStackLocation(context->stripes[i].Irp);
+            IrpSp = IoGetNextIrpStackLocation(context.stripes[i].Irp);
             IrpSp->MajorFunction = IRP_MJ_PNP;
             IrpSp->MinorFunction = minor;
 
-            context->stripes[i].Irp->UserIosb = &context->stripes[i].iosb;
+            context.stripes[i].Irp->UserIosb = &context.stripes[i].iosb;
             
-            IoSetCompletionRoutine(context->stripes[i].Irp, pnp_completion, &context->stripes[i], TRUE, TRUE, TRUE);
+            IoSetCompletionRoutine(context.stripes[i].Irp, pnp_completion, &context.stripes[i], TRUE, TRUE, TRUE);
             
-            context->stripes[i].Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
-            context->stripes[i].dev = dev;
+            context.stripes[i].Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+            context.stripes[i].dev = dev;
             
-            context->left++;
+            context.left++;
         }
         
         le = le->Flink;
     }
     
-    if (context->left == 0) {
+    if (context.left == 0) {
         Status = STATUS_SUCCESS;
         goto end;
     }
     
     for (i = 0; i < num_devices; i++) {
-        if (context->stripes[i].Irp) {
-            IoCallDriver(context->stripes[i].dev->devobj, context->stripes[i].Irp);
+        if (context.stripes[i].Irp) {
+            IoCallDriver(context.stripes[i].dev->devobj, context.stripes[i].Irp);
         }
     }
     
-    KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
+    KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
     
     Status = STATUS_SUCCESS;
     
     for (i = 0; i < num_devices; i++) {
-        if (context->stripes[i].Irp) {
-            if (context->stripes[i].Status != STATUS_SUCCESS)
-                Status = context->stripes[i].Status;
+        if (context.stripes[i].Irp) {
+            if (context.stripes[i].Status != STATUS_SUCCESS)
+                Status = context.stripes[i].Status;
         }
     }
     
 end:
     for (i = 0; i < num_devices; i++) {
-        if (context->stripes[i].Irp) {
-            IoFreeIrp(context->stripes[i].Irp);
+        if (context.stripes[i].Irp) {
+            IoFreeIrp(context.stripes[i].Irp);
         }
     }
 
-    ExFreePool(context->stripes);
-    ExFreePool(context);
+    ExFreePool(context.stripes);
     
 end2:
     ExReleaseResourceLite(&Vcb->tree_lock);
@@ -161,7 +154,7 @@
     return Status;
 }
 
-static NTSTATUS pnp_cancel_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+static NTSTATUS pnp_cancel_remove_device(PDEVICE_OBJECT DeviceObject) {
 device_extension* Vcb = DeviceObject->DeviceExtension;
     NTSTATUS Status;
     
@@ -185,10 +178,9 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+NTSTATUS pnp_query_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     device_extension* Vcb = DeviceObject->DeviceExtension;
     NTSTATUS Status;
-    LIST_ENTRY rollback;
     
     ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
 
@@ -205,14 +197,19 @@
 
     Vcb->removing = TRUE;
     
-    InitializeListHead(&rollback);
-    
     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
 
-    if (Vcb->need_write && !Vcb->readonly)
-        do_write(Vcb, Irp, &rollback);
-    
-    clear_rollback(Vcb, &rollback);
+    if (Vcb->need_write && !Vcb->readonly) {
+        Status = do_write(Vcb, Irp);
+        
+        free_trees(Vcb);
+        
+        if (!NT_SUCCESS(Status)) {
+            ERR("do_write returned %08x\n", Status);
+            ExReleaseResourceLite(&Vcb->tree_lock);
+            goto end;
+        }
+    }
 
     ExReleaseResourceLite(&Vcb->tree_lock);
 
@@ -223,7 +220,7 @@
     return Status;
 }
 
-static NTSTATUS pnp_remove_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+static NTSTATUS pnp_remove_device(PDEVICE_OBJECT DeviceObject) {
     device_extension* Vcb = DeviceObject->DeviceExtension;
     NTSTATUS Status;
     
@@ -233,15 +230,20 @@
     }
     
     if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
+        volume_device_extension* vde;
+        
         Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT);
         if (!NT_SUCCESS(Status)) {
             WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status);
         }
         
-        if (Vcb->open_files > 0) {
-            Vcb->removing = TRUE;
-            Vcb->Vpb->Flags &= ~VPB_MOUNTED;
-        } else
+        vde = Vcb->vde;
+        vde->mounted_device = NULL;
+        Vcb->removing = TRUE;
+        Vcb->Vpb->Flags &= ~VPB_MOUNTED;
+        Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
+        
+        if (Vcb->open_files == 0)
             uninit(Vcb, FALSE);
     }
 
@@ -248,22 +250,21 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS pnp_start_device(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
-    FIXME("STUB\n");
-
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-static NTSTATUS pnp_surprise_removal(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+NTSTATUS pnp_surprise_removal(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     device_extension* Vcb = DeviceObject->DeviceExtension;
     
     TRACE("(%p, %p)\n", DeviceObject, Irp);
     
     if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
-        if (Vcb->open_files > 0) {
-            Vcb->removing = TRUE;
-            Vcb->Vpb->Flags &= ~VPB_MOUNTED;
-        } else
+        volume_device_extension* vde;
+        
+        vde = Vcb->vde;
+        vde->mounted_device = NULL;
+        Vcb->removing = TRUE;
+        Vcb->Vpb->Flags &= ~VPB_MOUNTED;
+        Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
+        
+        if (Vcb->open_files == 0)
             uninit(Vcb, FALSE);
     }
 
@@ -280,8 +281,13 @@
 
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        volume_device_extension* vde = DeviceObject->DeviceExtension;
+        IoSkipCurrentIrpStackLocation(Irp);
+        Status = IoCallDriver(vde->pdo, Irp);
+        goto exit;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
         goto end;
     }
     
@@ -289,7 +295,7 @@
     
     switch (IrpSp->MinorFunction) {
         case IRP_MN_CANCEL_REMOVE_DEVICE:
-            Status = pnp_cancel_remove_device(DeviceObject, Irp);
+            Status = pnp_cancel_remove_device(DeviceObject);
             break;
 
         case IRP_MN_QUERY_REMOVE_DEVICE:
@@ -297,13 +303,9 @@
             break;
 
         case IRP_MN_REMOVE_DEVICE:
-            Status = pnp_remove_device(DeviceObject, Irp);
+            Status = pnp_remove_device(DeviceObject);
             break;
 
-        case IRP_MN_START_DEVICE:
-            Status = pnp_start_device(DeviceObject, Irp);
-            break;
-
         case IRP_MN_SURPRISE_REMOVAL:
             Status = pnp_surprise_removal(DeviceObject, Irp);
             break;
@@ -313,7 +315,7 @@
             
             IoSkipCurrentIrpStackLocation(Irp);
             Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
-            goto end;
+            goto exit;
     }
 
 // //     Irp->IoStatus.Status = Status;
@@ -325,11 +327,12 @@
 // 
 // //     IoCompleteRequest(Irp, IO_NO_INCREMENT);
 
+end:
     Irp->IoStatus.Status = Status;
 
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
     
-end:
+exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
Index: drivers/filesystems/btrfs/read.c
===================================================================
--- drivers/filesystems/btrfs/read.c	(révision 74430)
+++ drivers/filesystems/btrfs/read.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -20,11 +20,9 @@
 enum read_data_status {
     ReadDataStatus_Pending,
     ReadDataStatus_Success,
-    ReadDataStatus_Cancelling,
-    ReadDataStatus_Cancelled,
     ReadDataStatus_Error,
-    ReadDataStatus_CRCError,
-    ReadDataStatus_MissingDevice
+    ReadDataStatus_MissingDevice,
+    ReadDataStatus_Skip
 };
 
 struct read_data_context;
@@ -37,6 +35,10 @@
     PIRP Irp;
     IO_STATUS_BLOCK iosb;
     enum read_data_status status;
+    BOOL not_alloc;
+    PMDL mdl;
+    UINT64 stripestart;
+    UINT64 stripeend;
 } read_data_stripe;
 
 typedef struct {
@@ -49,1068 +51,40 @@
     LONG stripes_left;
     UINT64 type;
     UINT32 sector_size;
-    UINT16 firstoff, startoffstripe, sectors_per_stripe, stripes_cancel;
+    UINT16 firstoff, startoffstripe, sectors_per_stripe;
     UINT32* csum;
     BOOL tree;
-    BOOL check_nocsum_parity;
     read_data_stripe* stripes;
-    KSPIN_LOCK spin_lock;
+    UINT8* va;
 } read_data_context;
 
 extern BOOL diskacc;
 extern tPsUpdateDiskCounters PsUpdateDiskCounters;
 extern tCcCopyReadEx CcCopyReadEx;
+extern tFsRtlUpdateDiskCounters FsRtlUpdateDiskCounters;
 
+#define LINUX_PAGE_SIZE 4096
+
 static NTSTATUS STDCALL read_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
     read_data_stripe* stripe = conptr;
     read_data_context* context = (read_data_context*)stripe->context;
-    UINT64 i;
-    LONG stripes_left;
-    KIRQL irql;
+    
+    UNUSED(DeviceObject);
 
-    KeAcquireSpinLock(&context->spin_lock, &irql);
-    
-    stripes_left = InterlockedDecrement(&context->stripes_left);
-    
-    if (stripe->status == ReadDataStatus_Cancelling) {
-        stripe->status = ReadDataStatus_Cancelled;
-        goto end;
-    }
-    
     stripe->iosb = Irp->IoStatus;
     
-    if (NT_SUCCESS(Irp->IoStatus.Status)) {
-        if (context->type == BLOCK_FLAG_DUPLICATE) {
-            stripe->status = ReadDataStatus_Success;
-
-            if (stripes_left > 0 && stripes_left == context->stripes_cancel) {
-                for (i = 0; i < context->num_stripes; i++) {
-                    if (context->stripes[i].status == ReadDataStatus_Pending) {
-                        context->stripes[i].status = ReadDataStatus_Cancelling;
-                        IoCancelIrp(context->stripes[i].Irp);
-                    }
-                }
-            }
-        } else if (context->type == BLOCK_FLAG_RAID0) {
-            stripe->status = ReadDataStatus_Success;
-        } else if (context->type == BLOCK_FLAG_RAID10) {
-            stripe->status = ReadDataStatus_Success;
-            
-            if (stripes_left > 0 && context->stripes_cancel != 0) {
-                for (i = 0; i < context->num_stripes; i++) {
-                    if (context->stripes[i].status == ReadDataStatus_Pending && context->stripes[i].stripenum == stripe->stripenum) {
-                        context->stripes[i].status = ReadDataStatus_Cancelling;
-                        IoCancelIrp(context->stripes[i].Irp);
-                        break;
-                    }
-                }
-            }
-        } else if (context->type == BLOCK_FLAG_RAID5) {
-            stripe->status = ReadDataStatus_Success;
-            
-            if (stripes_left > 0 && stripes_left == context->stripes_cancel && (context->csum || context->tree || !context->check_nocsum_parity)) {
-                for (i = 0; i < context->num_stripes; i++) {
-                    if (context->stripes[i].status == ReadDataStatus_Pending) {
-                        context->stripes[i].status = ReadDataStatus_Cancelling;
-                        IoCancelIrp(context->stripes[i].Irp);
-                        break;
-                    }
-                }
-            }
-        } else if (context->type == BLOCK_FLAG_RAID6) {
-            stripe->status = ReadDataStatus_Success;
-
-            if (stripes_left > 0 && stripes_left == context->stripes_cancel && (context->csum || context->tree || !context->check_nocsum_parity)) {
-                for (i = 0; i < context->num_stripes; i++) {
-                    if (context->stripes[i].status == ReadDataStatus_Pending) {
-                        context->stripes[i].status = ReadDataStatus_Cancelling;
-                        IoCancelIrp(context->stripes[i].Irp);
-                    }
-                }
-            }
-        }
-            
-        goto end;
-    } else {
+    if (NT_SUCCESS(Irp->IoStatus.Status))
+        stripe->status = ReadDataStatus_Success;
+    else
         stripe->status = ReadDataStatus_Error;
-    }
     
-end:
-    KeReleaseSpinLock(&context->spin_lock, irql);
-    
-    if (stripes_left == 0)
+    if (InterlockedDecrement(&context->stripes_left) == 0)
         KeSetEvent(&context->Event, 0, FALSE);
 
     return STATUS_MORE_PROCESSING_REQUIRED;
 }
 
-static void raid5_reconstruct(UINT64 off, UINT32 skip, read_data_context* context, CHUNK_ITEM* ci, UINT64* stripeoff, UINT64 maxsize,
-                              BOOL first, UINT32 firststripesize, UINT16 missing) {
-    UINT16 parity, stripe;
-    UINT32 stripelen = first ? firststripesize : ci->stripe_length;
-    UINT32 readlen;
-    
-    TRACE("(%llx, %x, %p, %p, %llx, %llx, %u, %x, %x)\n", off, skip, context, ci, *stripeoff, maxsize, first, firststripesize, missing);
-    
-    parity = ((off / ((ci->num_stripes - 1) * ci->stripe_length)) + ci->num_stripes - 1) % ci->num_stripes;
-    
-    readlen = min(min(ci->stripe_length - (skip % ci->stripe_length), stripelen), maxsize - *stripeoff);
-    
-    if (missing != parity) {
-        UINT16 firststripe = missing == 0 ? 1 : 0;
-        
-        RtlCopyMemory(&context->stripes[missing].buf[*stripeoff], &context->stripes[firststripe].buf[*stripeoff], readlen);
-        
-        for (stripe = firststripe + 1; stripe < context->num_stripes; stripe++) {
-            if (stripe != missing)
-                do_xor(&context->stripes[missing].buf[*stripeoff], &context->stripes[stripe].buf[*stripeoff], readlen);
-        }
-    } else
-        TRACE("parity == missing == %x, skipping\n", parity);
-    
-    *stripeoff += stripelen;
-}
-
-static void raid5_decode(UINT64 off, UINT32 skip, read_data_context* context, CHUNK_ITEM* ci, UINT64* stripeoff, UINT8* buf,
-                         UINT32* pos, UINT32 length, UINT32 firststripesize) {
-    UINT16 parity, stripe;
-    BOOL first = *pos == 0;
-    UINT32 stripelen = first ? firststripesize : ci->stripe_length;
-    
-    parity = ((off / ((ci->num_stripes - 1) * ci->stripe_length)) + ci->num_stripes - 1) % ci->num_stripes;
-    
-    stripe = (parity + 1) % ci->num_stripes;
-    
-    while (TRUE) {
-        if (stripe == parity) {
-            *stripeoff += stripelen;
-            return;
-        }
-        
-        if (skip >= ci->stripe_length) {
-            skip -= ci->stripe_length;
-        } else {
-            UINT32 copylen = min(ci->stripe_length - skip, length - *pos);
-            
-            RtlCopyMemory(buf + *pos, &context->stripes[stripe].buf[*stripeoff + skip - ci->stripe_length + stripelen], copylen);
-            
-            *pos += copylen;
-            
-            if (*pos == length)
-                return;
-            
-            skip = 0;
-        }
-        
-        stripe = (stripe + 1) % ci->num_stripes;
-    }
-}
-
-static BOOL raid5_decode_with_checksum(UINT64 off, UINT32 skip, read_data_context* context, CHUNK_ITEM* ci, UINT64* stripeoff, UINT8* buf,
-                                       UINT32* pos, UINT32 length, UINT32 firststripesize, UINT32* csum, UINT32 sector_size) {
-    UINT16 parity, stripe;
-    BOOL first = *pos == 0;
-    UINT32 stripelen = first ? firststripesize : ci->stripe_length;
-    
-    parity = ((off / ((ci->num_stripes - 1) * ci->stripe_length)) + ci->num_stripes - 1) % ci->num_stripes;
-    
-    stripe = (parity + 1) % ci->num_stripes;
-    
-    while (TRUE) {
-        if (stripe == parity) {
-            *stripeoff += stripelen;
-            return TRUE;
-        }
-        
-        if (skip >= ci->stripe_length) {
-            skip -= ci->stripe_length;
-        } else {
-            UINT32 i;
-            UINT32 copylen = min(ci->stripe_length - skip, length - *pos);
-            
-            RtlCopyMemory(buf + *pos, &context->stripes[stripe].buf[*stripeoff + skip - ci->stripe_length + stripelen], copylen);
-            
-            for (i = 0; i < copylen / sector_size; i ++) {
-                UINT32 crc32 = ~calc_crc32c(0xffffffff, buf + *pos + (i * sector_size), sector_size);
-                
-                if (crc32 != csum[i]) {
-                    UINT16 j, firststripe = stripe == 0 ? 1 : 0;
-                    
-                    RtlCopyMemory(buf + *pos + (i * sector_size),
-                                  &context->stripes[firststripe].buf[*stripeoff + skip - ci->stripe_length + stripelen + (i * sector_size)], sector_size);
-                    
-                    for (j = firststripe + 1; j < ci->num_stripes; j++) {
-                        if (j != stripe) {
-                            do_xor(buf + *pos + (i * sector_size), &context->stripes[j].buf[*stripeoff + skip - ci->stripe_length + stripelen + (i * sector_size)], sector_size);
-                        }
-                    }
-                    
-                    crc32 = ~calc_crc32c(0xffffffff, buf + *pos + (i * sector_size), sector_size);
-                    
-                    if (crc32 != csum[i]) {
-                        ERR("unrecoverable checksum error\n");
-                        return FALSE;
-                    }
-                    
-                    RtlCopyMemory(&context->stripes[stripe].buf[*stripeoff + skip - ci->stripe_length + stripelen + (i * sector_size)], buf + *pos + (i * sector_size), sector_size);
-                    context->stripes[stripe].rewrite = TRUE;
-                }
-            }
-            
-            *pos += copylen;
-            
-            if (*pos == length)
-                return TRUE;
-            
-            skip = 0;
-        }
-        
-        stripe = (stripe + 1) % ci->num_stripes;
-    }
-    
-    return FALSE;
-}
-
-static BOOL raid5_decode_with_checksum_metadata(UINT64 addr, UINT64 off, UINT32 skip, read_data_context* context, CHUNK_ITEM* ci, UINT64* stripeoff, UINT8* buf,
-                                                UINT32* pos, UINT32 length, UINT32 firststripesize, UINT32 node_size) {
-    UINT16 parity, stripe;
-    BOOL first = *pos == 0;
-    UINT32 stripelen = first ? firststripesize : ci->stripe_length;
-    
-    parity = ((off / ((ci->num_stripes - 1) * ci->stripe_length)) + ci->num_stripes - 1) % ci->num_stripes;
-    
-    stripe = (parity + 1) % ci->num_stripes;
-    
-    while (TRUE) {
-        if (stripe == parity) {
-            *stripeoff += stripelen;
-            return TRUE;
-        }
-        
-        if (skip >= ci->stripe_length) {
-            skip -= ci->stripe_length;
-        } else {
-            UINT32 copylen = min(ci->stripe_length - skip, length - *pos);
-            tree_header* th = (tree_header*)buf;
-            UINT32 crc32;
-            
-            RtlCopyMemory(buf + *pos, &context->stripes[stripe].buf[*stripeoff + skip - ci->stripe_length + stripelen], copylen);
-            
-            crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, node_size - sizeof(th->csum));
-            
-            if (addr != th->address || crc32 != *((UINT32*)th->csum)) {
-                UINT16 j, firststripe = stripe == 0 ? 1 : 0;
-                
-                RtlCopyMemory(buf + *pos, &context->stripes[firststripe].buf[*stripeoff + skip - ci->stripe_length + stripelen], copylen);
-                
-                for (j = firststripe + 1; j < ci->num_stripes; j++) {
-                    if (j != stripe) {
-                        do_xor(buf + *pos, &context->stripes[j].buf[*stripeoff + skip - ci->stripe_length + stripelen], copylen);
-                    }
-                }
-                
-                crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, node_size - sizeof(th->csum));
-                
-                if (addr != th->address || crc32 != *((UINT32*)th->csum)) {
-                    ERR("unrecoverable checksum error\n");
-                    return FALSE;
-                }
-            }
-            
-            RtlCopyMemory(&context->stripes[stripe].buf[*stripeoff + skip - ci->stripe_length + stripelen], buf + *pos, copylen);
-            context->stripes[stripe].rewrite = TRUE;
-            
-            *pos += copylen;
-            
-            if (*pos == length)
-                return TRUE;
-            
-            skip = 0;
-        }
-        
-        stripe = (stripe + 1) % ci->num_stripes;
-    }
-    
-    return FALSE;
-}
-
-static void raid6_reconstruct1(UINT64 off, UINT32 skip, read_data_context* context, CHUNK_ITEM* ci, UINT64* stripeoff, UINT64 maxsize,
-                               BOOL first, UINT32 firststripesize, UINT16 missing) {
-    UINT16 parity1, parity2, stripe;
-    UINT32 stripelen = first ? firststripesize : ci->stripe_length;
-    UINT32 readlen;
-    
-    TRACE("(%llx, %x, %p, %p, %llx, %llx, %u, %x, %x)\n", off, skip, context, ci, *stripeoff, maxsize, first, firststripesize, missing);
-    
-    parity1 = ((off / ((ci->num_stripes - 2) * ci->stripe_length)) + ci->num_stripes - 2) % ci->num_stripes;
-    parity2 = (parity1 + 1) % ci->num_stripes;
-    
-    readlen = min(min(ci->stripe_length - (skip % ci->stripe_length), stripelen), maxsize - *stripeoff);
-    
-    if (missing != parity1 && missing != parity2) {
-        RtlCopyMemory(&context->stripes[missing].buf[*stripeoff], &context->stripes[parity1].buf[*stripeoff], readlen);
-        stripe = parity1 == 0 ? (ci->num_stripes - 1) : (parity1 - 1);
-        
-        do {
-            if (stripe != missing)
-                do_xor(&context->stripes[missing].buf[*stripeoff], &context->stripes[stripe].buf[*stripeoff], readlen);
-            
-            stripe = stripe == 0 ? (ci->num_stripes - 1) : (stripe - 1);
-        } while (stripe != parity2);
-    } else
-        TRACE("skipping parity stripe\n");
-    
-    *stripeoff += stripelen;
-}
-
-static void raid6_reconstruct2(UINT64 off, UINT32 skip, read_data_context* context, CHUNK_ITEM* ci, UINT64* stripeoff, UINT64 maxsize,
-                               BOOL first, UINT32 firststripesize, UINT16 missing1, UINT16 missing2) {
-    UINT16 parity1, parity2, stripe;
-    UINT32 stripelen = first ? firststripesize : ci->stripe_length;
-    UINT32 readlen = min(min(ci->stripe_length - (skip % ci->stripe_length), stripelen), maxsize - *stripeoff);
-    
-    TRACE("(%llx, %x, %p, %p, %llx, %llx, %u, %x, %x, %x)\n", off, skip, context, ci, *stripeoff, maxsize,
-          first, firststripesize, missing1, missing2);
-    
-    parity1 = ((off / ((ci->num_stripes - 2) * ci->stripe_length)) + ci->num_stripes - 2) % ci->num_stripes;
-    parity2 = (parity1 + 1) % ci->num_stripes;
-    
-    // skip if missing stripes are p and q
-    if ((parity1 == missing1 && parity2 == missing2) || (parity1 == missing2 && parity2 == missing1)) {
-        *stripeoff += stripelen;
-        return;
-    }
-    
-    if (missing1 == parity2 || missing2 == parity2) { // reconstruct from p and data
-        UINT16 missing = missing1 == parity2 ? missing2 : missing1;
-        
-        RtlCopyMemory(&context->stripes[missing].buf[*stripeoff], &context->stripes[parity1].buf[*stripeoff], readlen);
-        stripe = parity1 == 0 ? (ci->num_stripes - 1) : (parity1 - 1);
-        
-        do {
-            if (stripe != missing)
-                do_xor(&context->stripes[missing].buf[*stripeoff], &context->stripes[stripe].buf[*stripeoff], readlen);
-            
-            stripe = stripe == 0 ? (ci->num_stripes - 1) : (stripe - 1);
-        } while (stripe != parity2);
-    } else if (missing1 == parity1 || missing2 == parity1) { // reconstruct from q and data
-        UINT16 missing = missing1 == parity1 ? missing2 : missing1;
-        UINT16 i, div;
-        
-        stripe = parity1 == 0 ? (ci->num_stripes - 1) : (parity1 - 1);
-        
-        i = ci->num_stripes - 3;
-        
-        if (stripe == missing) {
-            RtlZeroMemory(&context->stripes[missing].buf[*stripeoff], readlen);
-            div = i;
-        } else
-            RtlCopyMemory(&context->stripes[missing].buf[*stripeoff], &context->stripes[stripe].buf[*stripeoff], readlen);
-        
-        stripe = stripe == 0 ? (ci->num_stripes - 1) : (stripe - 1);
-        
-        i--;
-        do {
-            galois_double(&context->stripes[missing].buf[*stripeoff], readlen);
-            
-            if (stripe != missing)
-                do_xor(&context->stripes[missing].buf[*stripeoff], &context->stripes[stripe].buf[*stripeoff], readlen);
-            else
-                div = i;
-            
-            stripe = stripe == 0 ? (ci->num_stripes - 1) : (stripe - 1);
-            i--;
-        } while (stripe != parity2);
-        
-        do_xor(&context->stripes[missing].buf[*stripeoff], &context->stripes[parity2].buf[*stripeoff], readlen);
-        
-        if (div != 0)
-            galois_divpower(&context->stripes[missing].buf[*stripeoff], div, readlen);
-    } else { // reconstruct from p and q
-        UINT16 x, y, i;
-        UINT8 gyx, gx, denom, a, b, *p, *q, *pxy, *qxy;
-        UINT32 j;
-        
-        stripe = parity1 == 0 ? (ci->num_stripes - 1) : (parity1 - 1);
-        
-        // put qxy in missing1
-        // put pxy in missing2
-        
-        i = ci->num_stripes - 3;
-        if (stripe == missing1 || stripe == missing2) {
-            RtlZeroMemory(&context->stripes[missing1].buf[*stripeoff], readlen);
-            RtlZeroMemory(&context->stripes[missing2].buf[*stripeoff], readlen);
-            
-            if (stripe == missing1)
-                x = i;
-            else
-                y = i;
-        } else {
-            RtlCopyMemory(&context->stripes[missing1].buf[*stripeoff], &context->stripes[stripe].buf[*stripeoff], readlen);
-            RtlCopyMemory(&context->stripes[missing2].buf[*stripeoff], &context->stripes[stripe].buf[*stripeoff], readlen);
-        }
-        
-        stripe = stripe == 0 ? (ci->num_stripes - 1) : (stripe - 1);
-        
-        i--;
-        do {
-            galois_double(&context->stripes[missing1].buf[*stripeoff], readlen);
-            
-            if (stripe != missing1 && stripe != missing2) {
-                do_xor(&context->stripes[missing1].buf[*stripeoff], &context->stripes[stripe].buf[*stripeoff], readlen);
-                do_xor(&context->stripes[missing2].buf[*stripeoff], &context->stripes[stripe].buf[*stripeoff], readlen);
-            } else if (stripe == missing1)
-                x = i;
-            else if (stripe == missing2)
-                y = i;
-            
-            stripe = stripe == 0 ? (ci->num_stripes - 1) : (stripe - 1);
-            i--;
-        } while (stripe != parity2);
-        
-        gyx = gpow2(y > x ? (y-x) : (255-x+y));
-        gx = gpow2(255-x);
-
-        denom = gdiv(1, gyx ^ 1);
-        a = gmul(gyx, denom);
-        b = gmul(gx, denom);
-        
-        p = &context->stripes[parity1].buf[*stripeoff];
-        q = &context->stripes[parity2].buf[*stripeoff];
-        pxy = &context->stripes[missing2].buf[*stripeoff];
-        qxy = &context->stripes[missing1].buf[*stripeoff]; 
-        
-        for (j = 0; j < readlen; j++) {
-            *qxy = gmul(a, *p ^ *pxy) ^ gmul(b, *q ^ *qxy);
-            
-            p++;
-            q++;
-            pxy++;
-            qxy++;
-        }
-        
-        do_xor(&context->stripes[missing2].buf[*stripeoff], &context->stripes[missing1].buf[*stripeoff], readlen);
-        do_xor(&context->stripes[missing2].buf[*stripeoff], &context->stripes[parity1].buf[*stripeoff], readlen);
-    }
-    
-    *stripeoff += stripelen;
-}
-
-static void raid6_decode(UINT64 off, UINT32 skip, read_data_context* context, CHUNK_ITEM* ci, UINT64* stripeoff, UINT8* buf,
-                         UINT32* pos, UINT32 length, UINT32 firststripesize) {
-    UINT16 parity1, stripe;
-    BOOL first = *pos == 0;
-    UINT32 stripelen = first ? firststripesize : ci->stripe_length;
-    
-    parity1 = ((off / ((ci->num_stripes - 2) * ci->stripe_length)) + ci->num_stripes - 2) % ci->num_stripes;
-    
-    stripe = (parity1 + 2) % ci->num_stripes;
-    
-    while (TRUE) {
-        if (stripe == parity1) {
-            *stripeoff += stripelen;
-            return;
-        }
-        
-        if (skip >= ci->stripe_length) {
-            skip -= ci->stripe_length;
-        } else {
-            UINT32 copylen = min(ci->stripe_length - skip, length - *pos);
-            
-            RtlCopyMemory(buf + *pos, &context->stripes[stripe].buf[*stripeoff + skip - ci->stripe_length + stripelen], copylen);
-            
-            *pos += copylen;
-            
-            if (*pos == length)
-                return;
-            
-            skip = 0;
-        }
-        
-        stripe = (stripe + 1) % ci->num_stripes;
-    }
-}
-
-static BOOL raid6_decode_with_checksum(UINT64 off, UINT32 skip, read_data_context* context, CHUNK_ITEM* ci, UINT64* stripeoff, UINT8* buf,
-                                       UINT32* pos, UINT32 length, UINT32 firststripesize, UINT32* csum, UINT32 sector_size) {
-    UINT16 parity1, parity2, stripe;
-    BOOL first = *pos == 0;
-    UINT32 stripelen = first ? firststripesize : ci->stripe_length;
-    
-    parity1 = ((off / ((ci->num_stripes - 2) * ci->stripe_length)) + ci->num_stripes - 2) % ci->num_stripes;
-    parity2 = (parity1 + 1) % ci->num_stripes;
-    stripe = (parity1 + 2) % ci->num_stripes;
-    
-    while (TRUE) {
-        if (stripe == parity1) {
-            *stripeoff += stripelen;
-            return TRUE;
-        }
-        
-        if (skip >= ci->stripe_length) {
-            skip -= ci->stripe_length;
-        } else {
-            UINT32 i;
-            UINT32 copylen = min(ci->stripe_length - skip, length - *pos);
-            
-            RtlCopyMemory(buf + *pos, &context->stripes[stripe].buf[*stripeoff + skip - ci->stripe_length + stripelen], copylen);
-            
-            for (i = 0; i < copylen / sector_size; i ++) {
-                UINT32 crc32 = ~calc_crc32c(0xffffffff, buf + *pos + (i * sector_size), sector_size);
-                
-                if (crc32 != csum[i]) {
-                    UINT16 j, firststripe;
-                    
-                    if (parity2 == 0 && stripe == 1)
-                        firststripe = 2;
-                    else if (parity2 == 0 || stripe == 0)
-                        firststripe = 1;
-                    else
-                        firststripe = 0;
-                    
-                    RtlCopyMemory(buf + *pos + (i * sector_size),
-                                  &context->stripes[firststripe].buf[*stripeoff + skip - ci->stripe_length + stripelen + (i * sector_size)], sector_size);
-                    
-                    for (j = firststripe + 1; j < ci->num_stripes; j++) {
-                        if (j != stripe && j != parity2) {
-                            do_xor(buf + *pos + (i * sector_size), &context->stripes[j].buf[*stripeoff + skip - ci->stripe_length + stripelen + (i * sector_size)], sector_size);
-                        }
-                    }
-                    
-                    crc32 = ~calc_crc32c(0xffffffff, buf + *pos + (i * sector_size), sector_size);
-                    
-                    if (crc32 != csum[i]) {
-                        UINT8 *parity, *buf2;
-                        UINT16 rs, div;
-                        
-                        // assume p is wrong
-                        
-                        parity = ExAllocatePoolWithTag(NonPagedPool, sector_size, ALLOC_TAG);
-                        if (!parity) {
-                            ERR("out of memory\n");
-                            return FALSE;
-                        }
-                        
-                        rs = (parity1 + ci->num_stripes - 1) % ci->num_stripes;
-                        j = ci->num_stripes - 3;
-                        
-                        if (rs == stripe) {
-                            RtlZeroMemory(parity, sector_size);
-                            div = j;
-                        } else
-                            RtlCopyMemory(parity, &context->stripes[rs].buf[*stripeoff + skip - ci->stripe_length + stripelen + (i * sector_size)], sector_size);
-                        
-                        rs = (rs + ci->num_stripes - 1) % ci->num_stripes;
-                        j--;
-                        while (rs != parity2) {
-                            galois_double(parity, sector_size);
-                            
-                            if (rs != stripe)
-                                do_xor(parity, &context->stripes[rs].buf[*stripeoff + skip - ci->stripe_length + stripelen + (i * sector_size)], sector_size);
-                            else
-                                div = j;
-            
-                            rs = (rs + ci->num_stripes - 1) % ci->num_stripes;
-                            j--;
-                        }
-                        
-                        do_xor(parity, &context->stripes[parity2].buf[*stripeoff + skip - ci->stripe_length + stripelen + (i * sector_size)], sector_size);
-                        
-                        if (div != 0)
-                            galois_divpower(parity, div, sector_size);
-                        
-                        crc32 = ~calc_crc32c(0xffffffff, parity, sector_size);
-                        if (crc32 == csum[i]) {
-                            RtlCopyMemory(buf + *pos + (i * sector_size), parity, sector_size);
-                            
-                            // recalculate p
-                            RtlCopyMemory(&context->stripes[parity1].buf[*stripeoff + skip - ci->stripe_length + stripelen + (i * sector_size)], parity, sector_size);
-                            
-                            for (j = 0; j < ci->num_stripes; j++) {
-                                if (j != stripe && j != parity1 && j != parity2) {
-                                    do_xor(&context->stripes[parity1].buf[*stripeoff + skip - ci->stripe_length + stripelen + (i * sector_size)],
-                                           &context->stripes[j].buf[*stripeoff + skip - ci->stripe_length + stripelen + (i * sector_size)], sector_size);
-                                }
-                            }
-                            
-                            context->stripes[parity1].rewrite = TRUE;
-                            
-                            ExFreePool(parity);
-                            goto success;
-                        }
-                        
-                        // assume another of the data stripes is wrong
-                        
-                        buf2 = ExAllocatePoolWithTag(NonPagedPool, sector_size, ALLOC_TAG);
-                        if (!buf2) {
-                            ERR("out of memory\n");
-                            ExFreePool(parity);
-                            return FALSE;
-                        }
-                        
-                        j = (parity2 + 1) % ci->num_stripes;
-                        
-                        while (j != parity1) {
-                            if (j != stripe) {
-                                UINT16 curstripe, k;
-                                UINT32 bufoff = *stripeoff + skip - ci->stripe_length + stripelen + (i * sector_size);
-                                UINT16 x, y;
-                                UINT8 gyx, gx, denom, a, b, *p, *q, *pxy, *qxy;
-                            
-                                curstripe = parity1 == 0 ? (ci->num_stripes - 1) : (parity1 - 1);
-                                
-                                // put qxy in parity
-                                // put pxy in buf2
-                                
-                                k = ci->num_stripes - 3;
-                                if (curstripe == stripe || curstripe == j) {
-                                    RtlZeroMemory(parity, sector_size);
-                                    RtlZeroMemory(buf2, sector_size);
-                                    
-                                    if (curstripe == stripe)
-                                        x = k;
-                                    else
-                                        y = k;
-                                } else {
-                                    RtlCopyMemory(parity, &context->stripes[curstripe].buf[bufoff], sector_size);
-                                    RtlCopyMemory(buf2, &context->stripes[curstripe].buf[bufoff], sector_size);
-                                }
-                                
-                                curstripe = curstripe == 0 ? (ci->num_stripes - 1) : (curstripe - 1);
-                                
-                                k--;
-                                do {
-                                    galois_double(parity, sector_size);
-                                    
-                                    if (curstripe != stripe && curstripe != j) {
-                                        do_xor(parity, &context->stripes[curstripe].buf[bufoff], sector_size);
-                                        do_xor(buf2, &context->stripes[curstripe].buf[bufoff], sector_size);
-                                    } else if (curstripe == stripe)
-                                        x = k;
-                                    else if (curstripe == j)
-                                        y = k;
-                                    
-                                    curstripe = curstripe == 0 ? (ci->num_stripes - 1) : (curstripe - 1);
-                                    k--;
-                                } while (curstripe != parity2);
-                                
-                                gyx = gpow2(y > x ? (y-x) : (255-x+y));
-                                gx = gpow2(255-x);
-
-                                denom = gdiv(1, gyx ^ 1);
-                                a = gmul(gyx, denom);
-                                b = gmul(gx, denom);
-                                
-                                p = &context->stripes[parity1].buf[bufoff];
-                                q = &context->stripes[parity2].buf[bufoff];
-                                pxy = buf2;
-                                qxy = parity; 
-                                
-                                for (k = 0; k < sector_size; k++) {
-                                    *qxy = gmul(a, *p ^ *pxy) ^ gmul(b, *q ^ *qxy);
-                                    
-                                    p++;
-                                    q++;
-                                    pxy++;
-                                    qxy++;
-                                }
-                                
-                                crc32 = ~calc_crc32c(0xffffffff, parity, sector_size);
-                                
-                                if (crc32 == csum[i]) {
-                                    do_xor(buf2, parity, sector_size);
-                                    do_xor(buf2, &context->stripes[parity1].buf[bufoff], sector_size);
-                                    
-                                    RtlCopyMemory(&context->stripes[j].buf[bufoff], buf2, sector_size);
-                                    context->stripes[j].rewrite = TRUE;
-                                    
-                                    RtlCopyMemory(buf + *pos + (i * sector_size), parity, sector_size);
-                                    ExFreePool(parity);
-                                    ExFreePool(buf2);
-                                    goto success;
-                                }
-                            }
-                            
-                            j = (j + 1) % ci->num_stripes;
-                        }
-                            
-                        ExFreePool(parity);
-                        ExFreePool(buf2);
-                        
-                        ERR("unrecoverable checksum error\n");
-                        return FALSE;
-                    }
-                    
-success:
-                    RtlCopyMemory(&context->stripes[stripe].buf[*stripeoff + skip - ci->stripe_length + stripelen + (i * sector_size)], buf + *pos + (i * sector_size), sector_size);
-                    context->stripes[stripe].rewrite = TRUE;
-                }
-            }
-            
-            *pos += copylen;
-            
-            if (*pos == length)
-                return TRUE;
-            
-            skip = 0;
-        }
-        
-        stripe = (stripe + 1) % ci->num_stripes;
-    }
-}
-
-static BOOL raid6_decode_with_checksum_metadata(UINT64 addr, UINT64 off, UINT32 skip, read_data_context* context, CHUNK_ITEM* ci, UINT64* stripeoff, UINT8* buf,
-                                                UINT32* pos, UINT32 length, UINT32 firststripesize, UINT32 node_size) {
-    UINT16 parity1, parity2, stripe;
-    BOOL first = *pos == 0;
-    UINT32 stripelen = first ? firststripesize : ci->stripe_length;
-    
-    parity1 = ((off / ((ci->num_stripes - 2) * ci->stripe_length)) + ci->num_stripes - 2) % ci->num_stripes;
-    parity2 = (parity1 + 1) % ci->num_stripes;
-    stripe = (parity1 + 2) % ci->num_stripes;
-    
-    while (TRUE) {
-        if (stripe == parity1) {
-            *stripeoff += stripelen;
-            return TRUE;
-        }
-        
-        if (skip >= ci->stripe_length) {
-            skip -= ci->stripe_length;
-        } else {
-            UINT32 copylen = min(ci->stripe_length - skip, length - *pos);
-            tree_header* th = (tree_header*)buf;
-            UINT32 crc32;
-            
-            RtlCopyMemory(buf + *pos, &context->stripes[stripe].buf[*stripeoff + skip - ci->stripe_length + stripelen], copylen);
-            
-            crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, node_size - sizeof(th->csum));
-            
-            if (addr != th->address || crc32 != *((UINT32*)th->csum)) {
-                UINT16 j, firststripe;
-                
-                if (parity2 == 0 && stripe == 1)
-                    firststripe = 2;
-                else if (parity2 == 0 || stripe == 0)
-                    firststripe = 1;
-                else
-                    firststripe = 0;
-                
-                RtlCopyMemory(buf + *pos, &context->stripes[firststripe].buf[*stripeoff + skip - ci->stripe_length + stripelen], node_size);
-                
-                for (j = firststripe + 1; j < ci->num_stripes; j++) {
-                    if (j != stripe && j != parity2) {
-                        do_xor(buf + *pos, &context->stripes[j].buf[*stripeoff + skip - ci->stripe_length + stripelen], node_size);
-                    }
-                }
-                
-                crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, node_size - sizeof(th->csum));
-                
-                if (addr != th->address || crc32 != *((UINT32*)th->csum)) {
-                    UINT8 *parity, *buf2;
-                    UINT16 rs, div;
-                    tree_header* th2;
-                    
-                    // assume p is wrong
-                    
-                    parity = ExAllocatePoolWithTag(NonPagedPool, node_size, ALLOC_TAG);
-                    if (!parity) {
-                        ERR("out of memory\n");
-                        return FALSE;
-                    }
-                    
-                    rs = (parity1 + ci->num_stripes - 1) % ci->num_stripes;
-                    j = ci->num_stripes - 3;
-                    
-                    if (rs == stripe) {
-                        RtlZeroMemory(parity, node_size);
-                        div = j;
-                    } else
-                        RtlCopyMemory(parity, &context->stripes[rs].buf[*stripeoff + skip - ci->stripe_length + stripelen], node_size);
-                    
-                    rs = (rs + ci->num_stripes - 1) % ci->num_stripes;
-                    j--;
-                    while (rs != parity2) {
-                        galois_double(parity, node_size);
-                        
-                        if (rs != stripe)
-                            do_xor(parity, &context->stripes[rs].buf[*stripeoff + skip - ci->stripe_length + stripelen], node_size);
-                        else
-                            div = j;
-        
-                        rs = (rs + ci->num_stripes - 1) % ci->num_stripes;
-                        j--;
-                    }
-                    
-                    do_xor(parity, &context->stripes[parity2].buf[*stripeoff + skip - ci->stripe_length + stripelen], node_size);
-                    
-                    if (div != 0)
-                        galois_divpower(parity, div, node_size);
-                    
-                    th2 = (tree_header*)parity;
-                    
-                    crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th2->fs_uuid, node_size - sizeof(th2->csum));
-                
-                    if (addr != th2->address || crc32 == *((UINT32*)th2->csum)) {
-                        RtlCopyMemory(buf + *pos, parity, node_size);
-                        
-                        // recalculate p
-                        RtlCopyMemory(&context->stripes[parity1].buf[*stripeoff + skip - ci->stripe_length + stripelen], parity, node_size);
-                        
-                        for (j = 0; j < ci->num_stripes; j++) {
-                            if (j != stripe && j != parity1 && j != parity2) {
-                                do_xor(&context->stripes[parity1].buf[*stripeoff + skip - ci->stripe_length + stripelen],
-                                        &context->stripes[j].buf[*stripeoff + skip - ci->stripe_length + stripelen], node_size);
-                            }
-                        }
-                        
-                        context->stripes[parity1].rewrite = TRUE;
-                        
-                        ExFreePool(parity);
-                        goto success;
-                    }
-                    
-                    // assume another of the data stripes is wrong
-                    
-                    buf2 = ExAllocatePoolWithTag(NonPagedPool, node_size, ALLOC_TAG);
-                    if (!buf2) {
-                        ERR("out of memory\n");
-                        ExFreePool(parity);
-                        return FALSE;
-                    }
-                    
-                    j = (parity2 + 1) % ci->num_stripes;
-                    
-                    while (j != parity1) {
-                        if (j != stripe) {
-                            UINT16 curstripe, k;
-                            UINT32 bufoff = *stripeoff + skip - ci->stripe_length + stripelen;
-                            UINT16 x, y;
-                            UINT8 gyx, gx, denom, a, b, *p, *q, *pxy, *qxy;
-                        
-                            curstripe = parity1 == 0 ? (ci->num_stripes - 1) : (parity1 - 1);
-                            
-                            // put qxy in parity
-                            // put pxy in buf2
-                            
-                            k = ci->num_stripes - 3;
-                            if (curstripe == stripe || curstripe == j) {
-                                RtlZeroMemory(parity, node_size);
-                                RtlZeroMemory(buf2, node_size);
-                                
-                                if (curstripe == stripe)
-                                    x = k;
-                                else
-                                    y = k;
-                            } else {
-                                RtlCopyMemory(parity, &context->stripes[curstripe].buf[bufoff], node_size);
-                                RtlCopyMemory(buf2, &context->stripes[curstripe].buf[bufoff], node_size);
-                            }
-                            
-                            curstripe = curstripe == 0 ? (ci->num_stripes - 1) : (curstripe - 1);
-                            
-                            k--;
-                            do {
-                                galois_double(parity, node_size);
-                                
-                                if (curstripe != stripe && curstripe != j) {
-                                    do_xor(parity, &context->stripes[curstripe].buf[bufoff], node_size);
-                                    do_xor(buf2, &context->stripes[curstripe].buf[bufoff], node_size);
-                                } else if (curstripe == stripe)
-                                    x = k;
-                                else if (curstripe == j)
-                                    y = k;
-                                
-                                curstripe = curstripe == 0 ? (ci->num_stripes - 1) : (curstripe - 1);
-                                k--;
-                            } while (curstripe != parity2);
-                            
-                            gyx = gpow2(y > x ? (y-x) : (255-x+y));
-                            gx = gpow2(255-x);
-
-                            denom = gdiv(1, gyx ^ 1);
-                            a = gmul(gyx, denom);
-                            b = gmul(gx, denom);
-                            
-                            p = &context->stripes[parity1].buf[bufoff];
-                            q = &context->stripes[parity2].buf[bufoff];
-                            pxy = buf2;
-                            qxy = parity; 
-                            
-                            for (k = 0; k < node_size; k++) {
-                                *qxy = gmul(a, *p ^ *pxy) ^ gmul(b, *q ^ *qxy);
-                                
-                                p++;
-                                q++;
-                                pxy++;
-                                qxy++;
-                            }
-                            
-                            th2 = (tree_header*)parity;
-                    
-                            crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th2->fs_uuid, node_size - sizeof(th2->csum));
-                        
-                            if (addr != th2->address || crc32 == *((UINT32*)th2->csum)) {
-                                do_xor(buf2, parity, node_size);
-                                do_xor(buf2, &context->stripes[parity1].buf[bufoff], node_size);
-                                
-                                RtlCopyMemory(&context->stripes[j].buf[bufoff], buf2, node_size);
-                                context->stripes[j].rewrite = TRUE;
-                                
-                                RtlCopyMemory(buf + *pos, parity, node_size);
-                                ExFreePool(parity);
-                                ExFreePool(buf2);
-                                goto success;
-                            }
-                        }
-                        
-                        j = (j + 1) % ci->num_stripes;
-                    }
-                        
-                    ExFreePool(parity);
-                    ExFreePool(buf2);
-                    
-                    ERR("unrecoverable checksum error\n");
-                    return FALSE;
-                }
-                
-success:
-                RtlCopyMemory(&context->stripes[stripe].buf[*stripeoff + skip - ci->stripe_length + stripelen], buf + *pos, node_size);
-                context->stripes[stripe].rewrite = TRUE;
-            }
-            
-            *pos += copylen;
-            
-            if (*pos == length)
-                return TRUE;
-            
-            skip = 0;
-        }
-        
-        stripe = (stripe + 1) % ci->num_stripes;
-    }
-}
-
-static NTSTATUS check_raid6_nocsum_parity(UINT64 off, UINT32 skip, read_data_context* context, CHUNK_ITEM* ci, UINT64* stripeoff, UINT64 maxsize,
-                                          BOOL first, UINT32 firststripesize, UINT8* scratch) {
-    UINT16 parity1, parity2, stripe;
-    UINT32 stripelen = first ? firststripesize : ci->stripe_length;
-    UINT32 readlen, i;
-    BOOL bad = FALSE;
-    
-    TRACE("(%llx, %x, %p, %p, %llx, %llx, %u, %x, %p)\n", off, skip, context, ci, *stripeoff, maxsize, first, firststripesize, scratch);
-    
-    parity1 = ((off / ((ci->num_stripes - 2) * ci->stripe_length)) + ci->num_stripes - 2) % ci->num_stripes;
-    parity2 = (parity1 + 1) % ci->num_stripes;
-    
-    readlen = min(min(ci->stripe_length - (skip % ci->stripe_length), stripelen), maxsize - *stripeoff);
-    
-    RtlCopyMemory(scratch, &context->stripes[parity1].buf[*stripeoff], readlen);
-    stripe = parity1 == 0 ? (ci->num_stripes - 1) : (parity1 - 1);
-    
-    do {
-        do_xor(scratch, &context->stripes[stripe].buf[*stripeoff], readlen);
-        
-        stripe = stripe == 0 ? (ci->num_stripes - 1) : (stripe - 1);
-    } while (stripe != parity2);
-    
-    for (i = 0; i < readlen; i++) {
-        if (scratch[i] != 0) {
-            bad = TRUE;
-            break;
-        }
-    }
-    
-    if (bad) {
-        UINT16 missing;
-        UINT8* buf2;
-        
-        // assume parity is bad
-        stripe = parity1 == 0 ? (ci->num_stripes - 1) : (parity1 - 1);
-        RtlCopyMemory(scratch, &context->stripes[stripe].buf[*stripeoff], readlen);
-        stripe = stripe == 0 ? (ci->num_stripes - 1) : (stripe - 1);
-        
-        do {
-            galois_double(scratch, readlen);
-            
-            do_xor(scratch, &context->stripes[stripe].buf[*stripeoff], readlen);
-            
-            stripe = stripe == 0 ? (ci->num_stripes - 1) : (stripe - 1);
-        } while (stripe != parity2);
-        
-        if (RtlCompareMemory(scratch, &context->stripes[parity2].buf[*stripeoff], readlen) == readlen) {
-            WARN("recovering from invalid parity stripe\n");
-            
-            // recalc p
-            stripe = parity1 == 0 ? (ci->num_stripes - 1) : (parity1 - 1);
-            RtlCopyMemory(&context->stripes[parity1].buf[*stripeoff], &context->stripes[stripe].buf[*stripeoff], readlen);
-            stripe = stripe == 0 ? (ci->num_stripes - 1) : (stripe - 1);
-        
-            do {
-                do_xor(&context->stripes[parity1].buf[*stripeoff], &context->stripes[stripe].buf[*stripeoff], readlen);
-                
-                stripe = stripe == 0 ? (ci->num_stripes - 1) : (stripe - 1);
-            } while (stripe != parity2);
-            
-            context->stripes[parity1].rewrite = TRUE;
-            goto end;
-        }
-        
-        // assume one of the data stripes is bad
-        
-        buf2 = ExAllocatePoolWithTag(NonPagedPool, readlen, ALLOC_TAG);
-        if (!buf2) {
-            ERR("out of memory\n");
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
-        
-        missing = (parity2 + 1) % ci->num_stripes;
-        while (missing != parity1) {
-            RtlCopyMemory(scratch, &context->stripes[parity1].buf[*stripeoff], readlen);
-            for (i = 0; i < ci->num_stripes; i++) {
-                if (i != parity1 && i != parity2 && i != missing) {
-                    do_xor(scratch, &context->stripes[i].buf[*stripeoff], readlen);
-                }
-            }
-            
-            stripe = parity1 == 0 ? (ci->num_stripes - 1) : (parity1 - 1);
-            RtlCopyMemory(buf2, stripe == missing ? scratch : &context->stripes[stripe].buf[*stripeoff], readlen);
-            stripe = stripe == 0 ? (ci->num_stripes - 1) : (stripe - 1);
-            
-            do {
-                galois_double(buf2, readlen);
-                
-                do_xor(buf2, stripe == missing ? scratch : &context->stripes[stripe].buf[*stripeoff], readlen);
-                
-                stripe = stripe == 0 ? (ci->num_stripes - 1) : (stripe - 1);
-            } while (stripe != parity2);
-            
-            if (RtlCompareMemory(buf2, &context->stripes[parity2].buf[*stripeoff], readlen) == readlen) {
-                WARN("recovering from invalid data stripe\n");
-                
-                RtlCopyMemory(&context->stripes[missing].buf[*stripeoff], scratch, readlen);
-                ExFreePool(buf2);
-                
-                context->stripes[missing].rewrite = TRUE;
-                goto end;
-            }
-            
-            missing = (missing + 1) % ci->num_stripes;
-        }
-        
-        ExFreePool(buf2);
-        
-        ERR("unrecoverable checksum error\n");
-        return STATUS_CRC_ERROR;
-    }
-    
-end:
-    *stripeoff += stripelen;
-    
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS check_csum(device_extension* Vcb, UINT8* data, UINT32 sectors, UINT32* csum) {
+NTSTATUS check_csum(device_extension* Vcb, UINT8* data, UINT32 sectors, UINT32* csum) {
     NTSTATUS Status;
     calc_job* cj;
     UINT32* csum2;
@@ -1118,7 +92,7 @@
     // From experimenting, it seems that 40 sectors is roughly the crossover
     // point where offloading the crc32 calculation becomes worth it.
     
-    if (sectors < 40) {
+    if (sectors < 40 || KeQueryActiveProcessorCount(NULL) < 2) {
         ULONG j;
         
         for (j = 0; j < sectors; j++) {
@@ -1158,337 +132,217 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS read_data_dup(device_extension* Vcb, UINT8* buf, UINT64 addr, UINT32 length, PIRP Irp, read_data_context* context,
-                              CHUNK_ITEM* ci, device** devices, UINT64 *stripestart, UINT64 *stripeend) {
-    UINT64 i;
+static NTSTATUS read_data_dup(device_extension* Vcb, UINT64 addr, read_data_context* context, CHUNK_ITEM* ci, device** devices, UINT64 generation) {
+    ULONG i;
     BOOL checksum_error = FALSE;
-    UINT16 cancelled = 0;
+    UINT16 stripe = 0;
     NTSTATUS Status;
+    CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
     
     for (i = 0; i < ci->num_stripes; i++) {
-        if (context->stripes[i].status == ReadDataStatus_Success) {
-            if (context->tree) {
-                tree_header* th = (tree_header*)context->stripes[i].buf;
-                UINT32 crc32;
-                
-                crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum));
-                
-                if (th->address != context->address || crc32 != *((UINT32*)th->csum)) {
-                    context->stripes[i].status = ReadDataStatus_CRCError;
-                    checksum_error = TRUE;
-                }
-            } else if (context->csum) {
+        if (context->stripes[i].status == ReadDataStatus_Error) {
+            WARN("stripe %llu returned error %08x\n", i, context->stripes[i].iosb.Status);
+            log_device_error(Vcb, devices[i], BTRFS_DEV_STAT_READ_ERRORS);
+            return context->stripes[i].iosb.Status;
+        } else if (context->stripes[i].status == ReadDataStatus_Success) {
+            stripe = i;
+            break;
+        }
+    }
+    
+    if (context->stripes[stripe].status != ReadDataStatus_Success)
+        return STATUS_INTERNAL_ERROR;
+    
+    if (context->tree) {
+        tree_header* th = (tree_header*)context->stripes[stripe].buf;
+        UINT32 crc32;
+        
+        crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum));
+        
+        if (th->address != context->address || crc32 != *((UINT32*)th->csum)) {
+            checksum_error = TRUE;
+            log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+        } else if (generation != 0 && th->generation != generation) {
+            checksum_error = TRUE;
+            log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_GENERATION_ERRORS);
+        }
+    } else if (context->csum) {
 #ifdef DEBUG_STATS
-                LARGE_INTEGER time1, time2;
-                
-                time1 = KeQueryPerformanceCounter(NULL);
+        LARGE_INTEGER time1, time2;
+        
+        time1 = KeQueryPerformanceCounter(NULL);
 #endif
-                Status = check_csum(Vcb, context->stripes[i].buf, context->stripes[i].Irp->IoStatus.Information / context->sector_size, context->csum);
-                
-                if (Status == STATUS_CRC_ERROR) {
-                    context->stripes[i].status = ReadDataStatus_CRCError;
-                    checksum_error = TRUE;
-                    break;
-                } else if (!NT_SUCCESS(Status)) {
-                    ERR("check_csum returned %08x\n", Status);
-                    return Status;
-                }
+        Status = check_csum(Vcb, context->stripes[stripe].buf, context->stripes[stripe].Irp->IoStatus.Information / context->sector_size, context->csum);
+        
+        if (Status == STATUS_CRC_ERROR) {
+            checksum_error = TRUE;
+            log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+        } else if (!NT_SUCCESS(Status)) {
+            ERR("check_csum returned %08x\n", Status);
+            return Status;
+        }
 #ifdef DEBUG_STATS
-                time2 = KeQueryPerformanceCounter(NULL);
-                
-                Vcb->stats.read_csum_time += time2.QuadPart - time1.QuadPart;
+        time2 = KeQueryPerformanceCounter(NULL);
+        
+        Vcb->stats.read_csum_time += time2.QuadPart - time1.QuadPart;
 #endif
-            }
-        } else if (context->stripes[i].status == ReadDataStatus_Cancelled) {
-            cancelled++;
-        }
     }
     
-    if (checksum_error) {
-        CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+    if (!checksum_error)
+        return STATUS_SUCCESS;
+    
+    if (ci->num_stripes == 1)
+        return STATUS_CRC_ERROR;
+    
+    if (context->tree) {
+        tree_header* t2;
+        UINT16 j;
+        BOOL recovered = FALSE;
         
-        // FIXME - update dev stats
+        t2 = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+        if (!t2) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
         
-        if (cancelled > 0) {
-#ifdef DEBUG_STATS
-            LARGE_INTEGER time1, time2;
-#endif
-            context->stripes_left = 0;
-            
-            for (i = 0; i < ci->num_stripes; i++) {
-                if (context->stripes[i].status == ReadDataStatus_Cancelled) {
-                    PIO_STACK_LOCATION IrpSp;
+        for (j = 0; j < ci->num_stripes; j++) {
+            if (j != stripe && devices[j]) {
+                Status = sync_read_phys(devices[j]->devobj, cis[j].offset + context->stripes[stripe].stripestart, Vcb->superblock.node_size, (UINT8*)t2, FALSE);
+                if (!NT_SUCCESS(Status)) {
+                    WARN("sync_read_phys returned %08x\n", Status);
+                    log_device_error(Vcb, devices[j], BTRFS_DEV_STAT_READ_ERRORS);
+                } else {
+                    UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&t2->fs_uuid, Vcb->superblock.node_size - sizeof(t2->csum));
                     
-                    // re-run Irp that we cancelled
-                    
-                    if (context->stripes[i].Irp) {
-                        if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
-                            MmUnlockPages(context->stripes[i].Irp->MdlAddress);
-                            IoFreeMdl(context->stripes[i].Irp->MdlAddress);
-                        }
-                        IoFreeIrp(context->stripes[i].Irp);
-                    }
-                    
-                    if (!Irp) {
-                        context->stripes[i].Irp = IoAllocateIrp(devices[i]->devobj->StackSize, FALSE);
+                    if (t2->address == addr && crc32 == *((UINT32*)t2->csum) && (generation == 0 || t2->generation == generation)) {
+                        RtlCopyMemory(context->stripes[stripe].buf, t2, Vcb->superblock.node_size);
+                        ERR("recovering from checksum error at %llx, device %llx\n", addr, devices[stripe]->devitem.dev_id);
+                        recovered = TRUE;
                         
-                        if (!context->stripes[i].Irp) {
-                            ERR("IoAllocateIrp failed\n");
-                            return STATUS_INSUFFICIENT_RESOURCES;
+                        if (!Vcb->readonly && !devices[stripe]->readonly) { // write good data over bad
+                            Status = write_data_phys(devices[stripe]->devobj, cis[stripe].offset + context->stripes[stripe].stripestart,
+                                                     t2, Vcb->superblock.node_size);
+                            if (!NT_SUCCESS(Status)) {
+                                WARN("write_data_phys returned %08x\n", Status);
+                                log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_WRITE_ERRORS);
+                            }
                         }
-                    } else {
-                        context->stripes[i].Irp = IoMakeAssociatedIrp(Irp, devices[i]->devobj->StackSize);
                         
-                        if (!context->stripes[i].Irp) {
-                            ERR("IoMakeAssociatedIrp failed\n");
-                            return STATUS_INSUFFICIENT_RESOURCES;
-                        }
-                    }
-                    
-                    IrpSp = IoGetNextIrpStackLocation(context->stripes[i].Irp);
-                    IrpSp->MajorFunction = IRP_MJ_READ;
-                    
-                    if (devices[i]->devobj->Flags & DO_BUFFERED_IO) {
-                        FIXME("FIXME - buffered IO\n");
-                    } else if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
-                        context->stripes[i].Irp->MdlAddress = IoAllocateMdl(context->stripes[i].buf, stripeend[i] - stripestart[i], FALSE, FALSE, NULL);
-                        if (!context->stripes[i].Irp->MdlAddress) {
-                            ERR("IoAllocateMdl failed\n");
-                            return STATUS_INSUFFICIENT_RESOURCES;
-                        }
-                        
-                        MmProbeAndLockPages(context->stripes[i].Irp->MdlAddress, KernelMode, IoWriteAccess);
-                    } else {
-                        context->stripes[i].Irp->UserBuffer = context->stripes[i].buf;
-                    }
-
-                    IrpSp->Parameters.Read.Length = stripeend[i] - stripestart[i];
-                    IrpSp->Parameters.Read.ByteOffset.QuadPart = stripestart[i] + cis[i].offset;
-                    
-                    context->stripes[i].Irp->UserIosb = &context->stripes[i].iosb;
-                    
-                    IoSetCompletionRoutine(context->stripes[i].Irp, read_data_completion, &context->stripes[i], TRUE, TRUE, TRUE);
-                    
-                    context->stripes_left++;
-                    context->stripes[i].status = ReadDataStatus_Pending;
+                        break;
+                    } else if (t2->address != addr || crc32 != *((UINT32*)t2->csum))
+                        log_device_error(Vcb, devices[j], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                    else
+                        log_device_error(Vcb, devices[j], BTRFS_DEV_STAT_GENERATION_ERRORS);
                 }
             }
-            
-            context->stripes_cancel = 0;
-            KeClearEvent(&context->Event);
-            
-#ifdef DEBUG_STATS
-            if (!context->tree)
-                time1 = KeQueryPerformanceCounter(NULL);
-#endif
-
-            for (i = 0; i < ci->num_stripes; i++) {
-                if (context->stripes[i].status == ReadDataStatus_Pending) {
-                    IoCallDriver(devices[i]->devobj, context->stripes[i].Irp);
-                }
-            }
-            
-            KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
-            
-#ifdef DEBUG_STATS
-            if (!context->tree) {
-                time2 = KeQueryPerformanceCounter(NULL);
-                
-                Vcb->stats.read_disk_time += time2.QuadPart - time1.QuadPart;
-            }
-#endif
-            for (i = 0; i < ci->num_stripes; i++) {
-                if (context->stripes[i].status == ReadDataStatus_Success) {
-                    if (context->tree) {
-                        tree_header* th = (tree_header*)context->stripes[i].buf;
-                        UINT32 crc32;
-                        
-                        crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum));
-                        
-                        if (th->address != context->address || crc32 != *((UINT32*)th->csum))
-                            context->stripes[i].status = ReadDataStatus_CRCError;
-                    } else if (context->csum) {
-                        NTSTATUS Status;
-#ifdef DEBUG_STATS
-                        time1 = KeQueryPerformanceCounter(NULL);
-#endif
-                        Status = check_csum(Vcb, context->stripes[i].buf, context->stripes[i].Irp->IoStatus.Information / Vcb->superblock.sector_size, context->csum);
-                        
-                        if (Status == STATUS_CRC_ERROR)
-                            context->stripes[i].status = ReadDataStatus_CRCError;
-                        else if (!NT_SUCCESS(Status)) {
-                            ERR("check_csum returned %08x\n", Status);
-                            return Status;
-                        }
-#ifdef DEBUG_STATS
-                        time2 = KeQueryPerformanceCounter(NULL);
-                        
-                        Vcb->stats.read_csum_time += time2.QuadPart - time1.QuadPart;
-#endif
-                    }
-                }
-            }
         }
         
-        for (i = 0; i < ci->num_stripes; i++) {
-            if (context->stripes[i].status == ReadDataStatus_Success) {
-                RtlCopyMemory(buf, context->stripes[i].buf, length);
-                goto raid1write;
-            }
-        }
-        
-        if (context->tree || ci->num_stripes == 1) { // unable to recover from checksum error
+        if (!recovered) {
             ERR("unrecoverable checksum error at %llx\n", addr);
-            
-#ifdef _DEBUG
-            if (context->tree) {
-                for (i = 0; i < ci->num_stripes; i++) {
-                    if (context->stripes[i].status == ReadDataStatus_CRCError) {
-                        tree_header* th = (tree_header*)context->stripes[i].buf;
-                        UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, context->buflen - sizeof(th->csum));
-                        
-                        if (crc32 != *((UINT32*)th->csum)) {
-                            WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)th->csum));
-                            return STATUS_CRC_ERROR;
-                        } else if (addr != th->address) {
-                            WARN("address of tree was %llx, not %llx as expected\n", th->address, addr);
-                            return STATUS_CRC_ERROR;
-                        }
-                    }
-                }
-            }
-#endif
+            ExFreePool(t2);
             return STATUS_CRC_ERROR;
         }
+
+        ExFreePool(t2);
+    } else {
+        ULONG sectors = context->stripes[stripe].Irp->IoStatus.Information / Vcb->superblock.sector_size;
+        UINT8* sector;
         
-        // checksum errors on both stripes - we need to check sector by sector
+        sector = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.sector_size, ALLOC_TAG);
+        if (!sector) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
         
-        for (i = 0; i < (stripeend[0] - stripestart[0]) / context->sector_size; i++) {
-            UINT16 j;
-            BOOL success = FALSE;
-#ifdef DEBUG_STATS
-            LARGE_INTEGER time1, time2;
+        for (i = 0; i < sectors; i++) {
+            UINT32 crc32 = ~calc_crc32c(0xffffffff, context->stripes[stripe].buf + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
             
-            time1 = KeQueryPerformanceCounter(NULL);
-#endif
-            
-            for (j = 0; j < ci->num_stripes; j++) {
-                if (context->stripes[j].status == ReadDataStatus_CRCError) {
-                    UINT32 crc32 = ~calc_crc32c(0xffffffff, context->stripes[j].buf + (i * context->sector_size), context->sector_size);
-                    
-                    if (crc32 == context->csum[i]) {
-                        RtlCopyMemory(buf + (i * context->sector_size), context->stripes[j].buf + (i * context->sector_size), context->sector_size);
-                        success = TRUE;
-                        break;
+            if (context->csum[i] != crc32) {
+                UINT16 j;
+                BOOL recovered = FALSE;
+                
+                for (j = 0; j < ci->num_stripes; j++) {
+                    if (j != stripe && devices[j]) {
+                        Status = sync_read_phys(devices[j]->devobj, cis[j].offset + context->stripes[stripe].stripestart + UInt32x32To64(i, Vcb->superblock.sector_size),
+                                                Vcb->superblock.sector_size, sector, FALSE);
+                        if (!NT_SUCCESS(Status)) {
+                            WARN("sync_read_phys returned %08x\n", Status);
+                            log_device_error(Vcb, devices[j], BTRFS_DEV_STAT_READ_ERRORS);
+                        } else {
+                            UINT32 crc32b = ~calc_crc32c(0xffffffff, sector, Vcb->superblock.sector_size);
+                            
+                            if (crc32b == context->csum[i]) {
+                                RtlCopyMemory(context->stripes[stripe].buf + (i * Vcb->superblock.sector_size), sector, Vcb->superblock.sector_size);
+                                ERR("recovering from checksum error at %llx, device %llx\n", addr + UInt32x32To64(i, Vcb->superblock.sector_size), devices[stripe]->devitem.dev_id);
+                                recovered = TRUE;
+                                
+                                if (!Vcb->readonly && !devices[stripe]->readonly) { // write good data over bad
+                                    Status = write_data_phys(devices[stripe]->devobj, cis[stripe].offset + context->stripes[stripe].stripestart + UInt32x32To64(i, Vcb->superblock.sector_size),
+                                                             sector, Vcb->superblock.sector_size);
+                                    if (!NT_SUCCESS(Status)) {
+                                        WARN("write_data_phys returned %08x\n", Status);
+                                        log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_WRITE_ERRORS);
+                                    }
+                                }
+                                
+                                break;
+                            } else
+                                log_device_error(Vcb, devices[j], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                        }
                     }
                 }
-            }
-            
-#ifdef DEBUG_STATS
-            time2 = KeQueryPerformanceCounter(NULL);
-
-            Vcb->stats.read_csum_time += time2.QuadPart - time1.QuadPart;
-#endif
-            if (!success) {
-                ERR("unrecoverable checksum error at %llx\n", addr + (i * context->sector_size));
-                return STATUS_CRC_ERROR;
-            }
-        }
-        
-raid1write:
-        // write good data over bad
-        
-        if (!Vcb->readonly) {
-            for (i = 0; i < ci->num_stripes; i++) {
-                if (context->stripes[i].status == ReadDataStatus_CRCError && devices[i] && !devices[i]->readonly) {
-                    Status = write_data_phys(devices[i]->devobj, cis[i].offset + stripestart[i], buf, length);
-                    
-                    if (!NT_SUCCESS(Status))
-                        WARN("write_data_phys returned %08x\n", Status);
+                
+                if (!recovered) {
+                    ERR("unrecoverable checksum error at %llx\n", addr + UInt32x32To64(i, Vcb->superblock.sector_size));
+                    ExFreePool(sector);
+                    return STATUS_CRC_ERROR;
                 }
             }
         }
         
-        return STATUS_SUCCESS;
+        ExFreePool(sector);
     }
     
-    // check if any of the stripes succeeded
-    
-    for (i = 0; i < ci->num_stripes; i++) {
-        if (context->stripes[i].status == ReadDataStatus_Success) {
-            RtlCopyMemory(buf, context->stripes[i].buf, length);
-            return STATUS_SUCCESS;
-        }
-    }
-    
-    // failing that, return the first error we encountered
-    
-    for (i = 0; i < ci->num_stripes; i++) {
-        if (context->stripes[i].status == ReadDataStatus_Error)
-            return context->stripes[i].iosb.Status;
-    }
-    
-    // if we somehow get here, return STATUS_INTERNAL_ERROR
-    
-    return STATUS_INTERNAL_ERROR;
+    return STATUS_SUCCESS;
 }
 
 static NTSTATUS read_data_raid0(device_extension* Vcb, UINT8* buf, UINT64 addr, UINT32 length, read_data_context* context,
-                                CHUNK_ITEM* ci, UINT64* stripestart, UINT64* stripeend, UINT16 startoffstripe) {
+                                CHUNK_ITEM* ci, device** devices, UINT64 generation, UINT64 offset) {
     UINT64 i;
-    UINT32 pos, *stripeoff;
-    UINT8 stripe;
     
     for (i = 0; i < ci->num_stripes; i++) {
         if (context->stripes[i].status == ReadDataStatus_Error) {
-            WARN("stripe %llu returned error %08x\n", i, context->stripes[i].iosb.Status); 
+            WARN("stripe %llu returned error %08x\n", i, context->stripes[i].iosb.Status);
+            log_device_error(Vcb, devices[i], BTRFS_DEV_STAT_READ_ERRORS);
             return context->stripes[i].iosb.Status;
         }
     }
-    
-    pos = 0;
-    stripeoff = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT32) * ci->num_stripes, ALLOC_TAG);
-    if (!stripeoff) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    RtlZeroMemory(stripeoff, sizeof(UINT32) * ci->num_stripes);
-    
-    stripe = startoffstripe;
-    while (pos < length) {
-        if (pos == 0) {
-            UINT32 readlen = min(stripeend[stripe] - stripestart[stripe], ci->stripe_length - (stripestart[stripe] % ci->stripe_length));
-            
-            RtlCopyMemory(buf, context->stripes[stripe].buf, readlen);
-            stripeoff[stripe] += readlen;
-            pos += readlen;
-        } else if (length - pos < ci->stripe_length) {
-            RtlCopyMemory(buf + pos, &context->stripes[stripe].buf[stripeoff[stripe]], length - pos);
-            pos = length;
-        } else {
-            RtlCopyMemory(buf + pos, &context->stripes[stripe].buf[stripeoff[stripe]], ci->stripe_length);
-            stripeoff[stripe] += ci->stripe_length;
-            pos += ci->stripe_length;
-        }
-        
-        stripe = (stripe + 1) % ci->num_stripes;
-    }
-    
-    ExFreePool(stripeoff);
-    
-    // FIXME - handle the case where one of the stripes doesn't read everything, i.e. Irp->IoStatus.Information is short
-    
+
     if (context->tree) { // shouldn't happen, as trees shouldn't cross stripe boundaries
         tree_header* th = (tree_header*)buf;
         UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
         
-        if (crc32 != *((UINT32*)th->csum)) {
-            WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)th->csum));
-            return STATUS_CRC_ERROR;
-        } else if (addr != th->address) {
-            WARN("address of tree was %llx, not %llx as expected\n", th->address, addr);
-            return STATUS_CRC_ERROR;
+        if (crc32 != *((UINT32*)th->csum) || addr != th->address || (generation != 0 && generation != th->generation)) {
+            UINT64 off;
+            UINT16 stripe;
+            
+            get_raid0_offset(addr - offset, ci->stripe_length, ci->num_stripes, &off, &stripe);
+            
+            ERR("unrecoverable checksum error at %llx, device %llx\n", addr, devices[stripe]->devitem.dev_id);
+
+            if (crc32 != *((UINT32*)th->csum)) {
+                WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)th->csum));
+                log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                return STATUS_CRC_ERROR;
+            } else if (addr != th->address) {
+                WARN("address of tree was %llx, not %llx as expected\n", th->address, addr);
+                log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                return STATUS_CRC_ERROR;
+            } else if (generation != 0 && generation != th->generation) {
+                WARN("generation of tree was %llx, not %llx as expected\n", th->generation, generation);
+                log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_GENERATION_ERRORS);
+                return STATUS_CRC_ERROR;
+            }
         }
     } else if (context->csum) {
         NTSTATUS Status;
@@ -1500,7 +354,25 @@
         Status = check_csum(Vcb, buf, length / Vcb->superblock.sector_size, context->csum);
         
         if (Status == STATUS_CRC_ERROR) {
-            WARN("checksum error\n");
+            ULONG i;
+            
+            for (i = 0; i < length / Vcb->superblock.sector_size; i++) {
+                UINT32 crc32 = ~calc_crc32c(0xffffffff, buf + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+            
+                if (context->csum[i] != crc32) {
+                    UINT64 off;
+                    UINT16 stripe;
+                    
+                    get_raid0_offset(addr - offset + UInt32x32To64(i, Vcb->superblock.sector_size), ci->stripe_length, ci->num_stripes, &off, &stripe);
+                    
+                    ERR("unrecoverable checksum error at %llx, device %llx\n", addr, devices[stripe]->devitem.dev_id);
+                    
+                    log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+
+                    return Status;
+                }
+            }
+
             return Status;
         } else if (!NT_SUCCESS(Status)) {
             ERR("check_csum returned %08x\n", Status);
@@ -1516,77 +388,23 @@
     return STATUS_SUCCESS;    
 }
 
-static NTSTATUS read_data_raid10(device_extension* Vcb, UINT8* buf, UINT64 addr, UINT32 length, PIRP Irp, read_data_context* context,
-                                 CHUNK_ITEM* ci, device** devices, UINT64* stripestart, UINT64* stripeend, UINT16 startoffstripe) {
+static NTSTATUS read_data_raid10(device_extension* Vcb, UINT8* buf, UINT64 addr, UINT32 length, read_data_context* context,
+                                 CHUNK_ITEM* ci, device** devices, UINT64 generation, UINT64 offset) {
     UINT64 i;
+    UINT16 stripe;
     NTSTATUS Status;
     BOOL checksum_error = FALSE;
-    UINT32 pos, *stripeoff;
-    UINT8 stripe;
-    read_data_stripe** stripes;
-
-    stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_stripe*) * ci->num_stripes / ci->sub_stripes, ALLOC_TAG);
-    if (!stripes) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
+    CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
     
-    RtlZeroMemory(stripes, sizeof(read_data_stripe*) * ci->num_stripes / ci->sub_stripes);
-    
-    for (i = 0; i < ci->num_stripes; i += ci->sub_stripes) {
-        UINT16 j;
-        
-        for (j = 0; j < ci->sub_stripes; j++) {
-            if (context->stripes[i+j].status == ReadDataStatus_Success) {
-                stripes[i / ci->sub_stripes] = &context->stripes[i+j];
-                break;
-            }
-        }
-        
-        if (!stripes[i / ci->sub_stripes]) {
-            for (j = 0; j < ci->sub_stripes; j++) {
-                if (context->stripes[i+j].status == ReadDataStatus_Error) {
-                    // both stripes must have errored if we get here
-                    WARN("stripe %llu returned error %08x\n", i+j, context->stripes[i+j].iosb.Status);
-                    ExFreePool(stripes);
-                    return context->stripes[i].iosb.Status;
-                }
-            }
-        }
+    for (i = 0; i < ci->num_stripes; i++) {
+        if (context->stripes[i].status == ReadDataStatus_Error) {
+            WARN("stripe %llu returned error %08x\n", i, context->stripes[i].iosb.Status);
+            log_device_error(Vcb, devices[i], BTRFS_DEV_STAT_READ_ERRORS);
+            return context->stripes[i].iosb.Status;
+        } else if (context->stripes[i].status == ReadDataStatus_Success)
+            stripe = i;
     }
     
-    pos = 0;
-    stripeoff = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT32) * ci->num_stripes / ci->sub_stripes, ALLOC_TAG);
-    if (!stripeoff) {
-        ERR("out of memory\n");
-        ExFreePool(stripes);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    RtlZeroMemory(stripeoff, sizeof(UINT32) * ci->num_stripes / ci->sub_stripes);
-    
-    stripe = startoffstripe / ci->sub_stripes;
-    while (pos < length) {
-        if (pos == 0) {
-            UINT32 readlen = min(stripeend[stripe * ci->sub_stripes] - stripestart[stripe * ci->sub_stripes], ci->stripe_length - (stripestart[stripe * ci->sub_stripes] % ci->stripe_length));
-            
-            RtlCopyMemory(buf, stripes[stripe]->buf, readlen);
-            stripeoff[stripe] += readlen;
-            pos += readlen;
-        } else if (length - pos < ci->stripe_length) {
-            RtlCopyMemory(buf + pos, &stripes[stripe]->buf[stripeoff[stripe]], length - pos);
-            
-            pos = length;
-        } else {
-            RtlCopyMemory(buf + pos, &stripes[stripe]->buf[stripeoff[stripe]], ci->stripe_length);
-            stripeoff[stripe] += ci->stripe_length;
-
-            pos += ci->stripe_length;
-        }
-        
-        stripe = (stripe + 1) % (ci->num_stripes / ci->sub_stripes);
-    }
-    
     if (context->tree) {
         tree_header* th = (tree_header*)buf;
         UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
@@ -1594,11 +412,15 @@
         if (crc32 != *((UINT32*)th->csum)) {
             WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)th->csum));
             checksum_error = TRUE;
-            stripes[startoffstripe]->status = ReadDataStatus_CRCError;
+            log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
         } else if (addr != th->address) {
             WARN("address of tree was %llx, not %llx as expected\n", th->address, addr);
             checksum_error = TRUE;
-            stripes[startoffstripe]->status = ReadDataStatus_CRCError;
+            log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+        } else if (generation != 0 && generation != th->generation) {
+            WARN("generation of tree was %llx, not %llx as expected\n", th->generation, generation);
+            checksum_error = TRUE;
+            log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_GENERATION_ERRORS);
         }
     } else if (context->csum) {
         NTSTATUS Status;
@@ -1622,380 +444,180 @@
 #endif
     }
     
-    if (checksum_error) {
-        CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
-#ifdef DEBUG_STATS
-        LARGE_INTEGER time1, time2;
-#endif
+    if (!checksum_error)
+        return STATUS_SUCCESS;
+    
+    if (context->tree) {
+        tree_header* t2;
+        UINT16 j;
+        UINT64 off;
+        UINT16 badsubstripe = 0;
+        BOOL recovered = FALSE;
         
-        // FIXME - update dev stats
+        t2 = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+        if (!t2) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
         
-        WARN("checksum error\n");
+        get_raid0_offset(addr - offset, ci->stripe_length, ci->num_stripes / ci->sub_stripes, &off, &stripe);
         
-        if (!context->tree) {
-            RtlZeroMemory(stripeoff, sizeof(UINT32) * ci->num_stripes / ci->sub_stripes);
-            
-            // find out which stripe the error was on
-            pos = 0;
-            stripe = startoffstripe / ci->sub_stripes;
-            while (pos < length) {
-                if (pos == 0) {
-                    UINT32 readlen = min(stripeend[stripe * ci->sub_stripes] - stripestart[stripe * ci->sub_stripes], ci->stripe_length - (stripestart[stripe * ci->sub_stripes] % ci->stripe_length));
-                    
-                    stripeoff[stripe] += readlen;
-                    pos += readlen;
+        stripe *= ci->sub_stripes;
+        
+        for (j = 0; j < ci->sub_stripes; j++) {
+            if (context->stripes[stripe + j].status == ReadDataStatus_Success) {
+                badsubstripe = j;
+                break;
+            }
+        }
 
-                    for (i = 0; i < readlen / Vcb->superblock.sector_size; i++) {
-                        UINT32 crc32 = ~calc_crc32c(0xffffffff, buf + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
-                    
-                        if (crc32 != context->csum[i])
-                            stripes[stripe]->status = ReadDataStatus_CRCError;
-                    }
-                } else if (length - pos < ci->stripe_length) {
-                    for (i = 0; i < (length - pos) / Vcb->superblock.sector_size; i++) {
-                        UINT32 crc32 = ~calc_crc32c(0xffffffff, buf + pos + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
-                    
-                        if (crc32 != context->csum[(pos / Vcb->superblock.sector_size) + i])
-                            stripes[stripe]->status = ReadDataStatus_CRCError;
-                    }
-                    
-                    pos = length;
+        for (j = 0; j < ci->sub_stripes; j++) {
+            if (context->stripes[stripe + j].status != ReadDataStatus_Success && devices[stripe + j]->devobj) {
+                Status = sync_read_phys(devices[stripe + j]->devobj, cis[stripe + j].offset + off,
+                                        Vcb->superblock.node_size, (UINT8*)t2, FALSE);
+                if (!NT_SUCCESS(Status)) {
+                    WARN("sync_read_phys returned %08x\n", Status);
+                    log_device_error(Vcb, devices[stripe + j], BTRFS_DEV_STAT_READ_ERRORS);
                 } else {
-                    stripeoff[stripe] += ci->stripe_length;
+                    UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&t2->fs_uuid, Vcb->superblock.node_size - sizeof(t2->csum));
                     
-                    for (i = 0; i < ci->stripe_length / Vcb->superblock.sector_size; i++) {
-                        UINT32 crc32 = ~calc_crc32c(0xffffffff, buf + pos + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
-                    
-                        if (crc32 != context->csum[(pos / Vcb->superblock.sector_size) + i])
-                            stripes[stripe]->status = ReadDataStatus_CRCError;
-                    }
-                    
-                    pos += ci->stripe_length;
-                }
-                
-                stripe = (stripe + 1) % (ci->num_stripes / ci->sub_stripes);
-            }
-        }
-        
-        context->stripes_left = 0;
-        
-        for (i = 0; i < ci->num_stripes; i++) {
-            if (context->stripes[i].status == ReadDataStatus_CRCError) {
-                UINT16 other_stripe = (i % 1) ? (i - 1) : (i + 1);
-                
-                if (context->stripes[other_stripe].status == ReadDataStatus_Cancelled) {
-                    PIO_STACK_LOCATION IrpSp;
-                    
-                    // re-run Irp that we cancelled
-                    
-                    if (context->stripes[other_stripe].Irp) {
-                        if (devices[other_stripe]->devobj->Flags & DO_DIRECT_IO) {
-                            MmUnlockPages(context->stripes[other_stripe].Irp->MdlAddress);
-                            IoFreeMdl(context->stripes[other_stripe].Irp->MdlAddress);
-                        }
-                        IoFreeIrp(context->stripes[other_stripe].Irp);
-                    }
-                    
-                    if (!Irp) {
-                        context->stripes[other_stripe].Irp = IoAllocateIrp(devices[other_stripe]->devobj->StackSize, FALSE);
+                    if (t2->address == addr && crc32 == *((UINT32*)t2->csum) && (generation == 0 || t2->generation == generation)) {
+                        RtlCopyMemory(buf, t2, Vcb->superblock.node_size);
+                        ERR("recovering from checksum error at %llx, device %llx\n", addr, devices[stripe + j]->devitem.dev_id);
+                        recovered = TRUE;
                         
-                        if (!context->stripes[other_stripe].Irp) {
-                            ERR("IoAllocateIrp failed\n");
-                            return STATUS_INSUFFICIENT_RESOURCES;
+                        if (!Vcb->readonly && !devices[stripe + badsubstripe]->readonly) { // write good data over bad
+                            Status = write_data_phys(devices[stripe + badsubstripe]->devobj, cis[stripe + badsubstripe].offset + off,
+                                                     t2, Vcb->superblock.node_size);
+                            if (!NT_SUCCESS(Status)) {
+                                WARN("write_data_phys returned %08x\n", Status);
+                                log_device_error(Vcb, devices[stripe + badsubstripe], BTRFS_DEV_STAT_WRITE_ERRORS);
+                            }
                         }
-                    } else {
-                        context->stripes[other_stripe].Irp = IoMakeAssociatedIrp(Irp, devices[other_stripe]->devobj->StackSize);
                         
-                        if (!context->stripes[other_stripe].Irp) {
-                            ERR("IoMakeAssociatedIrp failed\n");
-                            return STATUS_INSUFFICIENT_RESOURCES;
-                        }
-                    }
-                    
-                    IrpSp = IoGetNextIrpStackLocation(context->stripes[other_stripe].Irp);
-                    IrpSp->MajorFunction = IRP_MJ_READ;
-                    
-                    if (devices[other_stripe]->devobj->Flags & DO_BUFFERED_IO) {
-                        FIXME("FIXME - buffered IO\n");
-                    } else if (devices[other_stripe]->devobj->Flags & DO_DIRECT_IO) {
-                        context->stripes[other_stripe].Irp->MdlAddress = IoAllocateMdl(context->stripes[other_stripe].buf, stripeend[other_stripe] - stripestart[other_stripe], FALSE, FALSE, NULL);
-                        if (!context->stripes[other_stripe].Irp->MdlAddress) {
-                            ERR("IoAllocateMdl failed\n");
-                            return STATUS_INSUFFICIENT_RESOURCES;
-                        }
-                        
-                        MmProbeAndLockPages(context->stripes[other_stripe].Irp->MdlAddress, KernelMode, IoWriteAccess);
-                    } else {
-                        context->stripes[other_stripe].Irp->UserBuffer = context->stripes[other_stripe].buf;
-                    }
-
-                    IrpSp->Parameters.Read.Length = stripeend[other_stripe] - stripestart[other_stripe];
-                    IrpSp->Parameters.Read.ByteOffset.QuadPart = stripestart[other_stripe] + cis[other_stripe].offset;
-                    
-                    context->stripes[other_stripe].Irp->UserIosb = &context->stripes[other_stripe].iosb;
-                    
-                    IoSetCompletionRoutine(context->stripes[other_stripe].Irp, read_data_completion, &context->stripes[other_stripe], TRUE, TRUE, TRUE);
-                    
-                    context->stripes_left++;
-                    context->stripes[other_stripe].status = ReadDataStatus_Pending;
+                        break;
+                    } else if (t2->address != addr || crc32 != *((UINT32*)t2->csum))
+                        log_device_error(Vcb, devices[stripe + j], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                    else
+                        log_device_error(Vcb, devices[stripe + j], BTRFS_DEV_STAT_GENERATION_ERRORS);
                 }
             }
         }
         
-        if (context->stripes_left == 0) {
-            WARN("could not recover from checksum error\n");
-            ExFreePool(stripes);
-            ExFreePool(stripeoff);
+        if (!recovered) {
+            ERR("unrecoverable checksum error at %llx\n", addr);
+            ExFreePool(t2);
             return STATUS_CRC_ERROR;
         }
+
+        ExFreePool(t2);
+    } else {
+        ULONG sectors = length / Vcb->superblock.sector_size;
+        UINT8* sector;
         
-        context->stripes_cancel = 0;
-        KeClearEvent(&context->Event);
-        
-#ifdef DEBUG_STATS
-        if (!context->tree)
-            time1 = KeQueryPerformanceCounter(NULL);
-#endif
-
-        for (i = 0; i < ci->num_stripes; i++) {
-            if (context->stripes[i].status == ReadDataStatus_Pending) {
-                IoCallDriver(devices[i]->devobj, context->stripes[i].Irp);
-            }
+        sector = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.sector_size, ALLOC_TAG);
+        if (!sector) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
         }
         
-        KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
-        
-#ifdef DEBUG_STATS
-        if (!context->tree) {
-            time2 = KeQueryPerformanceCounter(NULL);
+        for (i = 0; i < sectors; i++) {
+            UINT32 crc32 = ~calc_crc32c(0xffffffff, buf + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
             
-            Vcb->stats.read_disk_time += time2.QuadPart - time1.QuadPart;
-        }
-#endif
-
-        for (i = 0; i < ci->num_stripes; i++) {
-            if (context->stripes[i].status == ReadDataStatus_CRCError) {
-                UINT16 other_stripe = (i % 1) ? (i - 1) : (i + 1);
+            if (context->csum[i] != crc32) {
+                UINT16 j;
+                UINT64 off;
+                UINT16 stripe, badsubstripe = 0;
+                BOOL recovered = FALSE;
                 
-                if (context->stripes[other_stripe].status != ReadDataStatus_Success) {
-                    WARN("could not recover from checksum error\n");
-                    ExFreePool(stripes);
-                    ExFreePool(stripeoff);
-                    return STATUS_CRC_ERROR;
-                }
-            }
-        }
-
-        RtlZeroMemory(stripeoff, sizeof(UINT32) * ci->num_stripes / ci->sub_stripes);
-    
-        pos = 0;
-        stripe = startoffstripe / ci->sub_stripes;
-        while (pos < length) {
-            if (pos == 0) {
-                UINT32 readlen = min(stripeend[stripe * ci->sub_stripes] - stripestart[stripe * ci->sub_stripes], ci->stripe_length - (stripestart[stripe * ci->sub_stripes] % ci->stripe_length));
+                get_raid0_offset(addr - offset + UInt32x32To64(i, Vcb->superblock.sector_size), ci->stripe_length,
+                                 ci->num_stripes / ci->sub_stripes, &off, &stripe);
                 
-                stripeoff[stripe] += readlen;
-                pos += readlen;
+                stripe *= ci->sub_stripes;
                 
-                if (context->csum && stripes[stripe]->status == ReadDataStatus_CRCError) {
-                    for (i = 0; i < readlen / Vcb->superblock.sector_size; i++) {
-                        UINT32 crc32 = ~calc_crc32c(0xffffffff, buf + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
-                    
-                        if (crc32 != context->csum[i]) {
-                            UINT16 other_stripe = (stripe * ci->sub_stripes) + (context->stripes[stripe * ci->sub_stripes].status == ReadDataStatus_CRCError ? 1 : 0);
-                            UINT32 crc32b = ~calc_crc32c(0xffffffff, context->stripes[other_stripe].buf + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
-                            
-                            if (crc32b == context->csum[i]) {
-                                RtlCopyMemory(buf + (i * Vcb->superblock.sector_size), context->stripes[other_stripe].buf + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
-                                RtlCopyMemory(stripes[stripe]->buf + (i * Vcb->superblock.sector_size), context->stripes[other_stripe].buf + (i * Vcb->superblock.sector_size),
-                                                Vcb->superblock.sector_size);
-                                stripes[stripe]->rewrite = TRUE;
-                            } else {
-                                WARN("could not recover from checksum error\n");
-                                ExFreePool(stripes);
-                                ExFreePool(stripeoff);
-                                return STATUS_CRC_ERROR;
-                            }
-                        }
+                for (j = 0; j < ci->sub_stripes; j++) {
+                    if (context->stripes[stripe + j].status == ReadDataStatus_Success) {
+                        badsubstripe = j;
+                        break;
                     }
-                } else if (context->tree) {
-                    UINT16 other_stripe = (stripe * ci->sub_stripes) + (context->stripes[stripe * ci->sub_stripes].status == ReadDataStatus_CRCError ? 1 : 0);
-                    tree_header* th = (tree_header*)buf;
-                    UINT32 crc32;
-                    
-                    RtlCopyMemory(buf, context->stripes[other_stripe].buf, readlen);
-                    
-                    crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
-                    
-                    if (addr != th->address || crc32 != *((UINT32*)th->csum)) {
-                        WARN("could not recover from checksum error\n");
-                        ExFreePool(stripes);
-                        ExFreePool(stripeoff);
-                        return STATUS_CRC_ERROR;
-                    }
-                    
-                    RtlCopyMemory(stripes[stripe]->buf, buf, readlen);
-                    stripes[stripe]->rewrite = TRUE;
                 }
-            } else if (length - pos < ci->stripe_length) {
-                if (context->csum && stripes[stripe]->status == ReadDataStatus_CRCError) {
-                    for (i = 0; i < (length - pos) / Vcb->superblock.sector_size; i++) {
-                        UINT32 crc32 = ~calc_crc32c(0xffffffff, buf + pos + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
-                    
-                        if (crc32 != context->csum[(pos / Vcb->superblock.sector_size) + i]) {
-                            UINT16 other_stripe = (stripe * ci->sub_stripes) + (context->stripes[stripe * ci->sub_stripes].status == ReadDataStatus_CRCError ? 1 : 0);
-                            UINT32 crc32b = ~calc_crc32c(0xffffffff, &context->stripes[other_stripe].buf[stripeoff[stripe] + (i * Vcb->superblock.sector_size)],
-                                                            Vcb->superblock.sector_size);
+                
+                log_device_error(Vcb, devices[stripe + badsubstripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                
+                for (j = 0; j < ci->sub_stripes; j++) {
+                    if (context->stripes[stripe + j].status != ReadDataStatus_Success && devices[stripe + j]->devobj) {
+                        Status = sync_read_phys(devices[stripe + j]->devobj, cis[stripe + j].offset + off,
+                                                Vcb->superblock.sector_size, sector, FALSE);
+                        if (!NT_SUCCESS(Status)) {
+                            WARN("sync_read_phys returned %08x\n", Status);
+                            log_device_error(Vcb, devices[stripe + j], BTRFS_DEV_STAT_READ_ERRORS);
+                        } else {
+                            UINT32 crc32b = ~calc_crc32c(0xffffffff, sector, Vcb->superblock.sector_size);
                             
                             if (crc32b == context->csum[i]) {
-                                RtlCopyMemory(buf + pos + (i * Vcb->superblock.sector_size),
-                                                &context->stripes[other_stripe].buf[stripeoff[stripe] + (i * Vcb->superblock.sector_size)], Vcb->superblock.sector_size);
-                                RtlCopyMemory(&stripes[stripe]->buf[stripeoff[stripe] + (i * Vcb->superblock.sector_size)],
-                                                &context->stripes[other_stripe].buf[stripeoff[stripe] + (i * Vcb->superblock.sector_size)],
-                                                Vcb->superblock.sector_size);
-                                stripes[stripe]->rewrite = TRUE;
-                            } else {
-                                WARN("could not recover from checksum error\n");
-                                ExFreePool(stripes);
-                                ExFreePool(stripeoff);
-                                return STATUS_CRC_ERROR;
-                            }
+                                RtlCopyMemory(buf + (i * Vcb->superblock.sector_size), sector, Vcb->superblock.sector_size);
+                                ERR("recovering from checksum error at %llx, device %llx\n", addr + UInt32x32To64(i, Vcb->superblock.sector_size), devices[stripe + j]->devitem.dev_id);
+                                recovered = TRUE;
+                                
+                                if (!Vcb->readonly && !devices[stripe + badsubstripe]->readonly) { // write good data over bad
+                                    Status = write_data_phys(devices[stripe + badsubstripe]->devobj, cis[stripe + badsubstripe].offset + off,
+                                                             sector, Vcb->superblock.sector_size);
+                                    if (!NT_SUCCESS(Status)) {
+                                        WARN("write_data_phys returned %08x\n", Status);
+                                        log_device_error(Vcb, devices[stripe + badsubstripe], BTRFS_DEV_STAT_READ_ERRORS);
+                                    }
+                                }
+                                
+                                break;
+                            } else
+                                log_device_error(Vcb, devices[stripe + j], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
                         }
                     }
                 }
                 
-                pos = length;
-            } else {
-                if (context->csum && stripes[stripe]->status == ReadDataStatus_CRCError) {
-                    for (i = 0; i < ci->stripe_length / Vcb->superblock.sector_size; i++) {
-                        UINT32 crc32 = ~calc_crc32c(0xffffffff, buf + pos + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
-                    
-                        if (crc32 != context->csum[(pos / Vcb->superblock.sector_size) + i]) {
-                            UINT16 other_stripe = (stripe * ci->sub_stripes) + (context->stripes[stripe * ci->sub_stripes].status == ReadDataStatus_CRCError ? 1 : 0);
-                            UINT32 crc32b = ~calc_crc32c(0xffffffff, &context->stripes[other_stripe].buf[stripeoff[stripe] + (i * Vcb->superblock.sector_size)],
-                                                            Vcb->superblock.sector_size);
-                            
-                            if (crc32b == context->csum[i]) {
-                                RtlCopyMemory(buf + pos + (i * Vcb->superblock.sector_size),
-                                                &context->stripes[other_stripe].buf[stripeoff[stripe] + (i * Vcb->superblock.sector_size)], Vcb->superblock.sector_size);
-                                RtlCopyMemory(&stripes[stripe]->buf[stripeoff[stripe] + (i * Vcb->superblock.sector_size)],
-                                                &context->stripes[other_stripe].buf[stripeoff[stripe] + (i * Vcb->superblock.sector_size)],
-                                                Vcb->superblock.sector_size);
-                                stripes[stripe]->rewrite = TRUE;
-                            } else {
-                                WARN("could not recover from checksum error\n");
-                                ExFreePool(stripes);
-                                ExFreePool(stripeoff);
-                                return STATUS_CRC_ERROR;
-                            }
-                        }
-                    }
+                if (!recovered) {
+                    ERR("unrecoverable checksum error at %llx\n", addr + UInt32x32To64(i, Vcb->superblock.sector_size));
+                    ExFreePool(sector);
+                    return STATUS_CRC_ERROR;
                 }
-                
-                stripeoff[stripe] += ci->stripe_length;
-                pos += ci->stripe_length;
             }
-            
-            stripe = (stripe + 1) % (ci->num_stripes / ci->sub_stripes);
         }
         
-        // write good data over bad
-        
-        if (!Vcb->readonly) {
-            for (i = 0; i < ci->num_stripes; i++) {
-                if (context->stripes[i].rewrite && devices[i] && !devices[i]->readonly) {
-                    Status = write_data_phys(devices[i]->devobj, cis[i].offset + stripestart[i], context->stripes[i].buf, stripeend[i] - stripestart[i]);
-                    
-                    if (!NT_SUCCESS(Status))
-                        WARN("write_data_phys returned %08x\n", Status);
-                }
-            }
-        }
+        ExFreePool(sector);
     }
     
-    ExFreePool(stripes);
-    ExFreePool(stripeoff);
-    
-    // FIXME - handle the case where one of the stripes doesn't read everything, i.e. Irp->IoStatus.Information is short
-    
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS read_data_raid5(device_extension* Vcb, UINT8* buf, UINT64 addr, UINT32 length, PIRP Irp, read_data_context* context, CHUNK_ITEM* ci,
-                                device** devices, UINT64* stripestart, UINT64* stripeend, UINT64 offset, UINT32 firststripesize, BOOL check_nocsum_parity) {
-    UINT32 pos, skip;
+static NTSTATUS read_data_raid5(device_extension* Vcb, UINT8* buf, UINT64 addr, UINT32 length, read_data_context* context, CHUNK_ITEM* ci,
+                                device** devices, UINT64 offset, UINT64 generation, chunk* c) {
+    ULONG i;
     NTSTATUS Status;
-    int num_errors = 0;
-    UINT64 i, off, stripeoff, origoff;
-    BOOL needs_reconstruct = FALSE;
-    UINT64 reconstruct_stripe;
     BOOL checksum_error = FALSE;
+    CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+    UINT64 lockaddr, locklen;
+    UINT16 stripe;
     
     for (i = 0; i < ci->num_stripes; i++) {
         if (context->stripes[i].status == ReadDataStatus_Error) {
-            num_errors++;
-            if (num_errors > 1)
-                break;
-        }
+            WARN("stripe %llu returned error %08x\n", i, context->stripes[i].iosb.Status);
+            log_device_error(Vcb, devices[i], BTRFS_DEV_STAT_READ_ERRORS);
+            return context->stripes[i].iosb.Status;
+        } else if (context->stripes[i].status == ReadDataStatus_Success)
+            stripe = i;
     }
     
-    if (num_errors > 1) {
-        for (i = 0; i < ci->num_stripes; i++) {
-            if (context->stripes[i].status == ReadDataStatus_Error) {
-                WARN("stripe %llu returned error %08x\n", i, context->stripes[i].iosb.Status); 
-                return context->stripes[i].iosb.Status;
-            }
-        }
-    }
-    
-    off = addr - offset;
-    off -= off % ((ci->num_stripes - 1) * ci->stripe_length);
-    skip = addr - offset - off;
-    origoff = off;
-    
-    for (i = 0; i < ci->num_stripes; i++) {
-        if (context->stripes[i].status == ReadDataStatus_Cancelled) {
-            if (needs_reconstruct) {
-                ERR("more than one stripe needs reconstruction\n");
-                return STATUS_INTERNAL_ERROR;
-            } else {
-                needs_reconstruct = TRUE;
-                reconstruct_stripe = i;
-            }
-        }
-    }
-    
-    if (needs_reconstruct) {
-        TRACE("reconstructing stripe %u\n", reconstruct_stripe);
-        
-        stripeoff = 0;
-        
-        raid5_reconstruct(off, skip, context, ci, &stripeoff, stripeend[reconstruct_stripe] - stripestart[reconstruct_stripe], TRUE, firststripesize, reconstruct_stripe);
-        
-        while (stripeoff < stripeend[0] - stripestart[0]) {
-            off += (ci->num_stripes - 1) * ci->stripe_length;
-            raid5_reconstruct(off, 0, context, ci, &stripeoff, stripeend[reconstruct_stripe] - stripestart[reconstruct_stripe], FALSE, 0, reconstruct_stripe);
-        }
-        
-        off = addr - offset;
-        off -= off % ((ci->num_stripes - 1) * ci->stripe_length);
-    }
-    
-    pos = 0;
-    stripeoff = 0;
-    raid5_decode(off, skip, context, ci, &stripeoff, buf, &pos, length, firststripesize);
-    
-    while (pos < length) {
-        off += (ci->num_stripes - 1) * ci->stripe_length;
-        raid5_decode(off, 0, context, ci, &stripeoff, buf, &pos, length, 0);
-    }
-    
     if (context->tree) {
         tree_header* th = (tree_header*)buf;
         UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
         
-        if (addr != th->address || crc32 != *((UINT32*)th->csum))
+        if (addr != th->address || crc32 != *((UINT32*)th->csum)) {
             checksum_error = TRUE;
+            log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+        } else if (generation != 0 && generation != th->generation) {
+            checksum_error = TRUE;
+            log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_GENERATION_ERRORS);
+        }
     } else if (context->csum) {
 #ifdef DEBUG_STATS
         LARGE_INTEGER time1, time2;
@@ -2019,278 +641,291 @@
 #endif
     }
     
-    if (checksum_error) {
-        CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+    if (!checksum_error)
+        return STATUS_SUCCESS;
+    
+    if (c) {
+        get_raid56_lock_range(c, addr, length, &lockaddr, &locklen);
+        chunk_lock_range(Vcb, c, lockaddr, locklen);
+    }
+    
+    if (context->tree) {
+        UINT16 j, parity;
+        UINT64 off;
+        BOOL recovered = FALSE, first = TRUE, failed = FALSE;
+        UINT8* t2;
         
-        if (needs_reconstruct) {
-            PIO_STACK_LOCATION IrpSp;
-#ifdef DEBUG_STATS
-            LARGE_INTEGER time1, time2;
-#endif
-            
-            // re-run Irp that we cancelled
-            
-            if (context->stripes[reconstruct_stripe].Irp) {
-                if (devices[reconstruct_stripe]->devobj->Flags & DO_DIRECT_IO) {
-                    MmUnlockPages(context->stripes[reconstruct_stripe].Irp->MdlAddress);
-                    IoFreeMdl(context->stripes[reconstruct_stripe].Irp->MdlAddress);
-                }
-                IoFreeIrp(context->stripes[reconstruct_stripe].Irp);
-            }
-            
-            if (!Irp) {
-                context->stripes[reconstruct_stripe].Irp = IoAllocateIrp(devices[reconstruct_stripe]->devobj->StackSize, FALSE);
-                
-                if (!context->stripes[reconstruct_stripe].Irp) {
-                    ERR("IoAllocateIrp failed\n");
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-            } else {
-                context->stripes[reconstruct_stripe].Irp = IoMakeAssociatedIrp(Irp, devices[reconstruct_stripe]->devobj->StackSize);
-                
-                if (!context->stripes[reconstruct_stripe].Irp) {
-                    ERR("IoMakeAssociatedIrp failed\n");
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-            }
-            
-            IrpSp = IoGetNextIrpStackLocation(context->stripes[reconstruct_stripe].Irp);
-            IrpSp->MajorFunction = IRP_MJ_READ;
-            
-            if (devices[reconstruct_stripe]->devobj->Flags & DO_BUFFERED_IO) {
-                FIXME("FIXME - buffered IO\n");
-            } else if (devices[reconstruct_stripe]->devobj->Flags & DO_DIRECT_IO) {
-                context->stripes[reconstruct_stripe].Irp->MdlAddress = IoAllocateMdl(context->stripes[reconstruct_stripe].buf,
-                                                                                        stripeend[reconstruct_stripe] - stripestart[reconstruct_stripe], FALSE, FALSE, NULL);
-                if (!context->stripes[reconstruct_stripe].Irp->MdlAddress) {
-                    ERR("IoAllocateMdl failed\n");
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-                
-                MmProbeAndLockPages(context->stripes[reconstruct_stripe].Irp->MdlAddress, KernelMode, IoWriteAccess);
-            } else {
-                context->stripes[reconstruct_stripe].Irp->UserBuffer = context->stripes[reconstruct_stripe].buf;
-            }
-
-            IrpSp->Parameters.Read.Length = stripeend[reconstruct_stripe] - stripestart[reconstruct_stripe];
-            IrpSp->Parameters.Read.ByteOffset.QuadPart = stripestart[reconstruct_stripe] + cis[reconstruct_stripe].offset;
-            
-            context->stripes[reconstruct_stripe].Irp->UserIosb = &context->stripes[reconstruct_stripe].iosb;
-            
-            IoSetCompletionRoutine(context->stripes[reconstruct_stripe].Irp, read_data_completion, &context->stripes[reconstruct_stripe], TRUE, TRUE, TRUE);
-
-            context->stripes[reconstruct_stripe].status = ReadDataStatus_Pending;
-            
-            context->stripes_left = 1;
-            KeClearEvent(&context->Event);
-            
-#ifdef DEBUG_STATS
-            if (!context->tree)
-                time1 = KeQueryPerformanceCounter(NULL);
-#endif
-
-            IoCallDriver(devices[reconstruct_stripe]->devobj, context->stripes[reconstruct_stripe].Irp);
-            
-            KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
-            
-#ifdef DEBUG_STATS
-            if (!context->tree) {
-                time2 = KeQueryPerformanceCounter(NULL);
-                
-                Vcb->stats.read_disk_time += time2.QuadPart - time1.QuadPart;
-            }
-#endif
-
-            if (context->stripes[reconstruct_stripe].status != ReadDataStatus_Success) {
-                ERR("unrecoverable checksum error\n");
-                return STATUS_CRC_ERROR;
-            }
+        t2 = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size * 2, ALLOC_TAG);
+        if (!t2) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
         }
         
-        if (context->tree) {
-            off = origoff;
-            pos = 0;
-            stripeoff = 0;
-            if (!raid5_decode_with_checksum_metadata(addr, off, skip, context, ci, &stripeoff, buf, &pos, length, firststripesize, Vcb->superblock.node_size)) {
-                ERR("unrecoverable metadata checksum error\n");
-                return STATUS_CRC_ERROR;
+        get_raid0_offset(addr - offset, ci->stripe_length, ci->num_stripes - 1, &off, &stripe);
+        
+        parity = (((addr - offset) / ((ci->num_stripes - 1) * ci->stripe_length)) + ci->num_stripes - 1) % ci->num_stripes;
+        
+        stripe = (parity + stripe + 1) % ci->num_stripes;
+        
+        for (j = 0; j < ci->num_stripes; j++) {
+            if (j != stripe) {
+                if (devices[j]->devobj) {
+                    if (first) {
+                        Status = sync_read_phys(devices[j]->devobj, cis[j].offset + off, Vcb->superblock.node_size, t2, FALSE);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("sync_read_phys returned %08x\n", Status);
+                            log_device_error(Vcb, devices[j], BTRFS_DEV_STAT_READ_ERRORS);
+                            failed = TRUE;
+                            break;
+                        }
+                        
+                        first = FALSE;
+                    } else {
+                        Status = sync_read_phys(devices[j]->devobj, cis[j].offset + off, Vcb->superblock.node_size, t2 + Vcb->superblock.node_size, FALSE);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("sync_read_phys returned %08x\n", Status);
+                            log_device_error(Vcb, devices[j], BTRFS_DEV_STAT_READ_ERRORS);
+                            failed = TRUE;
+                            break;
+                        }
+                        
+                        do_xor(t2, t2 + Vcb->superblock.node_size, Vcb->superblock.node_size);
+                    }
+                } else {
+                    failed = TRUE;
+                    break;
+                }
             }
-        } else {
-            off = origoff;
-            pos = 0;
-            stripeoff = 0;
-            if (!raid5_decode_with_checksum(off, skip, context, ci, &stripeoff, buf, &pos, length, firststripesize, context->csum, Vcb->superblock.sector_size))
-                return STATUS_CRC_ERROR;
-            
-            while (pos < length) {
-                off += (ci->num_stripes - 1) * ci->stripe_length;
-                if (!raid5_decode_with_checksum(off, 0, context, ci, &stripeoff, buf, &pos, length, 0, context->csum, Vcb->superblock.sector_size))
-                    return STATUS_CRC_ERROR;
-            }
         }
         
-        // write good data over bad
-        
-        if (!Vcb->readonly) {
-            for (i = 0; i < ci->num_stripes; i++) {
-                if (context->stripes[i].rewrite && devices[i] && !devices[i]->readonly) {
-                    Status = write_data_phys(devices[i]->devobj, cis[i].offset + stripestart[i], context->stripes[i].buf, stripeend[i] - stripestart[i]);
-                    
-                    if (!NT_SUCCESS(Status))
+        if (!failed) {
+            tree_header* t3 = (tree_header*)t2;
+            UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&t3->fs_uuid, Vcb->superblock.node_size - sizeof(t3->csum));
+
+            if (t3->address == addr && crc32 == *((UINT32*)t3->csum) && (generation == 0 || t3->generation == generation)) {
+                RtlCopyMemory(buf, t2, Vcb->superblock.node_size);
+                ERR("recovering from checksum error at %llx, device %llx\n", addr, devices[stripe]->devitem.dev_id);
+                recovered = TRUE;
+                
+                if (!Vcb->readonly && !devices[stripe]->readonly) { // write good data over bad
+                    Status = write_data_phys(devices[stripe]->devobj, cis[stripe].offset + off, t2, Vcb->superblock.node_size);
+                    if (!NT_SUCCESS(Status)) {
                         WARN("write_data_phys returned %08x\n", Status);
+                        log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_WRITE_ERRORS);
+                    }
                 }
             }
         }
-    }
-    
-    if (check_nocsum_parity && !context->tree && !context->csum) {
-        UINT32* parity_buf;
+
+        if (!recovered) {
+            ERR("unrecoverable checksum error at %llx\n", addr);
+            ExFreePool(t2);
+            Status = STATUS_CRC_ERROR;
+            goto end;
+        }
         
-        // We are reading a nodatacsum extent. Even though there's no checksum, we
-        // can still identify errors by checking if the parity is consistent.
+        ExFreePool(t2);
+    } else {
+        ULONG sectors = length / Vcb->superblock.sector_size;
+        UINT8* sector;
         
-        parity_buf = ExAllocatePoolWithTag(NonPagedPool, stripeend[0] - stripestart[0], ALLOC_TAG);
-        
-        if (!parity_buf) {
+        sector = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.sector_size * 2, ALLOC_TAG);
+        if (!sector) {
             ERR("out of memory\n");
-            return STATUS_INSUFFICIENT_RESOURCES;
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
         }
         
-        RtlCopyMemory(parity_buf, context->stripes[0].buf, stripeend[0] - stripestart[0]);
-        
-        for (i = 0; i < ci->num_stripes; i++) {
-            do_xor((UINT8*)parity_buf, context->stripes[i].buf, stripeend[0] - stripestart[0]);
-        }
-        
-        for (i = 0; i < (stripeend[0] - stripestart[0]) / sizeof(UINT32); i++) {
-            if (parity_buf[i] != 0) {
-                ERR("parity error on nodatacsum inode\n");
-                ExFreePool(parity_buf);
-                return STATUS_CRC_ERROR;
+        for (i = 0; i < sectors; i++) {
+            UINT32 crc32 = ~calc_crc32c(0xffffffff, buf + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+            
+            if (context->csum[i] != crc32) {
+                UINT16 j, parity;
+                UINT64 off;
+                BOOL recovered = FALSE, first = TRUE, failed = FALSE;
+                
+                get_raid0_offset(addr - offset + UInt32x32To64(i, Vcb->superblock.sector_size), ci->stripe_length,
+                                 ci->num_stripes - 1, &off, &stripe);
+                
+                parity = (((addr - offset + UInt32x32To64(i, Vcb->superblock.sector_size)) / ((ci->num_stripes - 1) * ci->stripe_length)) + ci->num_stripes - 1) % ci->num_stripes;
+                
+                stripe = (parity + stripe + 1) % ci->num_stripes;
+                log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_READ_ERRORS);
+                
+                for (j = 0; j < ci->num_stripes; j++) {
+                    if (j != stripe) {
+                        if (devices[j]->devobj) {
+                            if (first) {
+                                Status = sync_read_phys(devices[j]->devobj, cis[j].offset + off, Vcb->superblock.sector_size, sector, FALSE);
+                                if (!NT_SUCCESS(Status)) {
+                                    ERR("sync_read_phys returned %08x\n", Status);
+                                    failed = TRUE;
+                                    log_device_error(Vcb, devices[j], BTRFS_DEV_STAT_READ_ERRORS);
+                                    break;
+                                }
+                                
+                                first = FALSE;
+                            } else {
+                                Status = sync_read_phys(devices[j]->devobj, cis[j].offset + off, Vcb->superblock.sector_size, sector + Vcb->superblock.sector_size, FALSE);
+                                if (!NT_SUCCESS(Status)) {
+                                    ERR("sync_read_phys returned %08x\n", Status);
+                                    failed = TRUE;
+                                    log_device_error(Vcb, devices[j], BTRFS_DEV_STAT_READ_ERRORS);
+                                    break;
+                                }
+                                
+                                do_xor(sector, sector + Vcb->superblock.sector_size, Vcb->superblock.sector_size);
+                            }
+                        } else {
+                            failed = TRUE;
+                            break;
+                        }
+                    }
+                }
+                
+                if (!failed) {
+                    crc32 = ~calc_crc32c(0xffffffff, sector, Vcb->superblock.sector_size);
+                    
+                    if (crc32 == context->csum[i]) {
+                        RtlCopyMemory(buf + (i * Vcb->superblock.sector_size), sector, Vcb->superblock.sector_size);
+                        ERR("recovering from checksum error at %llx, device %llx\n", addr + UInt32x32To64(i, Vcb->superblock.sector_size), devices[stripe]->devitem.dev_id);
+                        recovered = TRUE;
+                        
+                        if (!Vcb->readonly && !devices[stripe]->readonly) { // write good data over bad
+                            Status = write_data_phys(devices[stripe]->devobj, cis[stripe].offset + off,
+                                                     sector, Vcb->superblock.sector_size);
+                            if (!NT_SUCCESS(Status)) {
+                                WARN("write_data_phys returned %08x\n", Status);
+                                log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_WRITE_ERRORS);
+                            }
+                        }
+                    }
+                }
+
+                if (!recovered) {
+                    ERR("unrecoverable checksum error at %llx\n", addr + UInt32x32To64(i, Vcb->superblock.sector_size));
+                    ExFreePool(sector);
+                    Status = STATUS_CRC_ERROR;
+                    goto end;
+                }
             }
         }
         
-        ExFreePool(parity_buf);
+        ExFreePool(sector);
     }
+
+    Status = STATUS_SUCCESS;
     
-    return STATUS_SUCCESS;
+end:
+    if (c)
+        chunk_unlock_range(Vcb, c, lockaddr, locklen);
+
+    return Status;
 }
 
-static NTSTATUS read_data_raid6(device_extension* Vcb, UINT8* buf, UINT64 addr, UINT32 length, PIRP Irp, read_data_context* context, CHUNK_ITEM* ci,
-                                device** devices, UINT64* stripestart, UINT64* stripeend, UINT64 offset, UINT32 firststripesize, BOOL check_nocsum_parity) {
-    NTSTATUS Status;
-    UINT32 pos, skip;
-    int num_errors = 0;
-    UINT64 i, off, stripeoff, origoff;
-    UINT8 needs_reconstruct = 0;
-    UINT16 missing1, missing2;
-    BOOL checksum_error = FALSE;
-    
-    for (i = 0; i < ci->num_stripes; i++) {
-        if (context->stripes[i].status == ReadDataStatus_Error) {
-            num_errors++;
-            if (num_errors > 2)
-                break;
-        }
-    }
-    
-    if (num_errors > 2) {
-        for (i = 0; i < ci->num_stripes; i++) {
-            if (context->stripes[i].status == ReadDataStatus_Error) {
-                WARN("stripe %llu returned error %08x\n", i, context->stripes[i].iosb.Status); 
-                return context->stripes[i].iosb.Status;
-            }
-        }
-    }
-    
-    off = addr - offset;
-    off -= off % ((ci->num_stripes - 2) * ci->stripe_length);
-    skip = addr - offset - off;
-    origoff = off;
-    
-    for (i = 0; i < ci->num_stripes; i++) {
-        if (context->stripes[i].status == ReadDataStatus_Cancelled) {
-            if (needs_reconstruct == 2) {
-                ERR("more than two stripes need reconstruction\n");
-                return STATUS_INTERNAL_ERROR;
-            } else if (needs_reconstruct == 1) {
-                needs_reconstruct++;
-                missing2 = i;
-            } else {
-                needs_reconstruct++;
-                missing1 = i;
-            }
-        }
-    }
-    
-    if (needs_reconstruct > 0) {
-        stripeoff = 0;
+static void raid6_recover2(UINT8* sectors, UINT16 num_stripes, ULONG sector_size, UINT16 missing1, UINT16 missing2, UINT8* out) {
+    if (missing1 == num_stripes - 2 || missing2 == num_stripes - 2) { // reconstruct from q and data
+        UINT16 missing = missing1 == (num_stripes - 2) ? missing2 : missing1;
+        UINT16 stripe;
         
-        if (needs_reconstruct == 2) {
-            TRACE("reconstructing stripes %u and %u\n", missing1, missing2);
+        stripe = num_stripes - 3;
         
-            raid6_reconstruct2(off, skip, context, ci, &stripeoff, stripeend[missing1] - stripestart[missing1],
-                                TRUE, firststripesize, missing1, missing2);
+        if (stripe == missing)
+            RtlZeroMemory(out, sector_size);
+        else
+            RtlCopyMemory(out, sectors + (stripe * sector_size), sector_size);
+
+        do {
+            stripe--;
             
-            while (stripeoff < stripeend[0] - stripestart[0]) {
-                off += (ci->num_stripes - 2) * ci->stripe_length;
-                raid6_reconstruct2(off, 0, context, ci, &stripeoff, stripeend[missing1] - stripestart[missing1],
-                                    FALSE, 0, missing1, missing2);
-            }
+            galois_double(out, sector_size);
+            
+            if (stripe != missing)
+                do_xor(out, sectors + (stripe * sector_size), sector_size);
+        } while (stripe > 0);
+        
+        do_xor(out, sectors + ((num_stripes - 1) * sector_size), sector_size);
+        
+        if (missing != 0)
+            galois_divpower(out, missing, sector_size);
+    } else { // reconstruct from p and q
+        UINT16 x, y, stripe;
+        UINT8 gyx, gx, denom, a, b, *p, *q, *pxy, *qxy;
+        UINT32 j;
+        
+        stripe = num_stripes - 3;
+        
+        pxy = out + sector_size;
+        qxy = out; 
+
+        if (stripe == missing1 || stripe == missing2) {
+            RtlZeroMemory(qxy, sector_size);
+            RtlZeroMemory(pxy, sector_size);
+            
+            if (stripe == missing1)
+                x = stripe;
+            else
+                y = stripe;
         } else {
-            TRACE("reconstructing stripe %u\n", missing1);
+            RtlCopyMemory(qxy, sectors + (stripe * sector_size), sector_size);
+            RtlCopyMemory(pxy, sectors + (stripe * sector_size), sector_size);
+        }
+
+        do {
+            stripe--;
             
-            raid6_reconstruct1(off, skip, context, ci, &stripeoff, stripeend[missing1] - stripestart[missing1], TRUE, firststripesize, missing1);
+            galois_double(qxy, sector_size);
             
-            while (stripeoff < stripeend[0] - stripestart[0]) {
-                off += (ci->num_stripes - 2) * ci->stripe_length;
-                raid6_reconstruct1(off, 0, context, ci, &stripeoff, stripeend[missing1] - stripestart[missing1], FALSE, 0, missing1);
-            }
-        }
+            if (stripe != missing1 && stripe != missing2) {
+                do_xor(qxy, sectors + (stripe * sector_size), sector_size);
+                do_xor(pxy, sectors + (stripe * sector_size), sector_size);
+            } else if (stripe == missing1)
+                x = stripe;
+            else if (stripe == missing2)
+                y = stripe;
+        } while (stripe > 0);
         
-        off = origoff;
-    }
-    
-    if (check_nocsum_parity && !context->tree && !context->csum) {
-        UINT8* scratch;
+        gyx = gpow2(y > x ? (y-x) : (255-x+y));
+        gx = gpow2(255-x);
+
+        denom = gdiv(1, gyx ^ 1);
+        a = gmul(gyx, denom);
+        b = gmul(gx, denom);
         
-        scratch = ExAllocatePoolWithTag(NonPagedPool, ci->stripe_length, ALLOC_TAG);
-        if (!scratch) {
-            ERR("out of memory\n");
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
+        p = sectors + ((num_stripes - 2) * sector_size);
+        q = sectors + ((num_stripes - 1) * sector_size);
         
-        stripeoff = 0;
-        Status = check_raid6_nocsum_parity(off, skip, context, ci, &stripeoff, stripeend[0] - stripestart[0], TRUE, firststripesize, scratch);
-        if (!NT_SUCCESS(Status)) {
-            ERR("check_raid6_nocsum_parity returned %08x\n", Status);
-            ExFreePool(scratch);
-            return Status;
-        }
+        for (j = 0; j < sector_size; j++) {
+            *qxy = gmul(a, *p ^ *pxy) ^ gmul(b, *q ^ *qxy);
             
-        while (stripeoff < stripeend[0] - stripestart[0]) {
-            off += (ci->num_stripes - 2) * ci->stripe_length;
-            Status = check_raid6_nocsum_parity(off, 0, context, ci, &stripeoff, stripeend[0] - stripestart[0], FALSE, 0, scratch);
-            
-            if (!NT_SUCCESS(Status)) {
-                ERR("check_raid6_nocsum_parity returned %08x\n", Status);
-                ExFreePool(scratch);
-                return Status;
-            }
+            p++;
+            q++;
+            pxy++;
+            qxy++;
         }
         
-        ExFreePool(scratch);
-        
-        off = origoff;
+        do_xor(out + sector_size, out, sector_size);
+        do_xor(out + sector_size, sectors + ((num_stripes - 2) * sector_size), sector_size);
     }
+}
+
+static NTSTATUS read_data_raid6(device_extension* Vcb, UINT8* buf, UINT64 addr, UINT32 length, read_data_context* context, CHUNK_ITEM* ci,
+                                device** devices, UINT64 offset, UINT64 generation, chunk* c) {
+    NTSTATUS Status;
+    ULONG i;
+    BOOL checksum_error = FALSE;
+    CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+    UINT64 lockaddr, locklen;
+    UINT16 stripe;
     
-    pos = 0;
-    stripeoff = 0;
-    raid6_decode(off, skip, context, ci, &stripeoff, buf, &pos, length, firststripesize);
-    
-    while (pos < length) {
-        off += (ci->num_stripes - 2) * ci->stripe_length;
-        raid6_decode(off, 0, context, ci, &stripeoff, buf, &pos, length, 0);
+    for (i = 0; i < ci->num_stripes; i++) {
+        if (context->stripes[i].status == ReadDataStatus_Error) {
+            WARN("stripe %llu returned error %08x\n", i, context->stripes[i].iosb.Status);
+            log_device_error(Vcb, devices[i], BTRFS_DEV_STAT_READ_ERRORS);
+            return context->stripes[i].iosb.Status;
+        } else if (context->stripes[i].status == ReadDataStatus_Success)
+            stripe = i;
     }
     
     if (context->tree) {
@@ -2297,8 +932,13 @@
         tree_header* th = (tree_header*)buf;
         UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
         
-        if (addr != th->address || crc32 != *((UINT32*)th->csum))
+        if (addr != th->address || crc32 != *((UINT32*)th->csum)) {
             checksum_error = TRUE;
+            log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+        } else if (generation != 0 && generation != th->generation) {
+            checksum_error = TRUE;
+            log_device_error(Vcb, devices[stripe], BTRFS_DEV_STAT_GENERATION_ERRORS);
+        }
     } else if (context->csum) {
 #ifdef DEBUG_STATS
         LARGE_INTEGER time1, time2;
@@ -2321,157 +961,388 @@
 #endif
     }
     
-    if (checksum_error) {
-        CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+    if (!checksum_error)
+        return STATUS_SUCCESS;
+    
+    if (c) {
+        get_raid56_lock_range(c, addr, length, &lockaddr, &locklen);
+        chunk_lock_range(Vcb, c, lockaddr, locklen);
+    }
+    
+    if (context->tree) {
+        UINT8* sector;
+        UINT16 j, stripe, physstripe, parity1, parity2, error_stripe;
+        UINT64 off;
+        BOOL recovered = FALSE, failed = FALSE;
+        ULONG num_errors = 0;
         
-        for (i = 0; i < needs_reconstruct; i++) {
-            PIO_STACK_LOCATION IrpSp;
-            UINT16 reconstruct_stripe = i == 0 ? missing1 : missing2;
-            
-            // re-run Irps that we cancelled
-            
-            if (context->stripes[reconstruct_stripe].Irp) {
-                if (devices[reconstruct_stripe]->devobj->Flags & DO_DIRECT_IO) {
-                    MmUnlockPages(context->stripes[reconstruct_stripe].Irp->MdlAddress);
-                    IoFreeMdl(context->stripes[reconstruct_stripe].Irp->MdlAddress);
+        sector = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size * (ci->num_stripes + 2), ALLOC_TAG);
+        if (!sector) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
+        }
+        
+        get_raid0_offset(addr - offset, ci->stripe_length, ci->num_stripes - 2, &off, &stripe);
+        
+        parity1 = (((addr - offset) / ((ci->num_stripes - 2) * ci->stripe_length)) + ci->num_stripes - 2) % ci->num_stripes;
+        parity2 = (parity1 + 1) % ci->num_stripes;
+        
+        physstripe = (parity2 + stripe + 1) % ci->num_stripes;
+        
+        j = (parity2 + 1) % ci->num_stripes;
+        do {
+            if (j != stripe) {
+                if (devices[j]->devobj) {
+                    Status = sync_read_phys(devices[j]->devobj, cis[j].offset + off, Vcb->superblock.node_size, sector + (j * Vcb->superblock.node_size), FALSE);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("sync_read_phys returned %08x\n", Status);
+                        log_device_error(Vcb, devices[j], BTRFS_DEV_STAT_READ_ERRORS);
+                        num_errors++;
+                        error_stripe = j;
+                        
+                        if (num_errors > 1) {
+                            failed = TRUE;
+                            break;
+                        }
+                    }
+                } else {
+                    num_errors++;
+                    error_stripe = j;
+                    
+                    if (num_errors > 1) {
+                        failed = TRUE;
+                        break;
+                    }
                 }
-                IoFreeIrp(context->stripes[reconstruct_stripe].Irp);
             }
             
-            if (!Irp) {
-                context->stripes[reconstruct_stripe].Irp = IoAllocateIrp(devices[reconstruct_stripe]->devobj->StackSize, FALSE);
+            j = (j + 1) % ci->num_stripes;
+        } while (j != (parity2 + 1) % ci->num_stripes);
+        
+        if (!failed) {
+            if (num_errors == 0) {
+                tree_header* th = (tree_header*)(sector + (stripe * Vcb->superblock.node_size));
+                UINT32 crc32;
                 
-                if (!context->stripes[reconstruct_stripe].Irp) {
-                    ERR("IoAllocateIrp failed\n");
-                    return STATUS_INSUFFICIENT_RESOURCES;
+                j = (stripe + 1) % ci->num_stripes;
+                
+                RtlCopyMemory(sector + (stripe * Vcb->superblock.node_size), sector + (j * Vcb->superblock.node_size), Vcb->superblock.node_size);
+                
+                j = (j + 1) % ci->num_stripes;
+                while (j != stripe) {
+                    if (j != parity2)
+                        do_xor(sector + (stripe * Vcb->superblock.node_size), sector + (j * Vcb->superblock.node_size), Vcb->superblock.node_size);
+                    
+                    j = (j + 1) % ci->num_stripes;
                 }
-            } else {
-                context->stripes[reconstruct_stripe].Irp = IoMakeAssociatedIrp(Irp, devices[reconstruct_stripe]->devobj->StackSize);
                 
-                if (!context->stripes[reconstruct_stripe].Irp) {
-                    ERR("IoMakeAssociatedIrp failed\n");
-                    return STATUS_INSUFFICIENT_RESOURCES;
+                crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+
+                if (th->address == addr && crc32 == *((UINT32*)th->csum) && (generation == 0 || th->generation == generation)) {
+                    RtlCopyMemory(buf, sector + (stripe * Vcb->superblock.node_size), Vcb->superblock.node_size);
+                    ERR("recovering from checksum error at %llx, device %llx\n", addr, devices[physstripe]->devitem.dev_id);
+                    recovered = TRUE;
+                    
+                    if (!Vcb->readonly && !devices[physstripe]->readonly) { // write good data over bad
+                        Status = write_data_phys(devices[physstripe]->devobj, cis[physstripe].offset + off,
+                                                 sector + (stripe * Vcb->superblock.node_size), Vcb->superblock.node_size);
+                        if (!NT_SUCCESS(Status)) {
+                            WARN("write_data_phys returned %08x\n", Status);
+                            log_device_error(Vcb, devices[physstripe], BTRFS_DEV_STAT_WRITE_ERRORS);
+                        }
+                    }
                 }
             }
             
-            IrpSp = IoGetNextIrpStackLocation(context->stripes[reconstruct_stripe].Irp);
-            IrpSp->MajorFunction = IRP_MJ_READ;
-            
-            if (devices[reconstruct_stripe]->devobj->Flags & DO_BUFFERED_IO) {
-                FIXME("FIXME - buffered IO\n");
-            } else if (devices[reconstruct_stripe]->devobj->Flags & DO_DIRECT_IO) {
-                context->stripes[reconstruct_stripe].Irp->MdlAddress = IoAllocateMdl(context->stripes[reconstruct_stripe].buf,
-                                                                                        stripeend[reconstruct_stripe] - stripestart[reconstruct_stripe], FALSE, FALSE, NULL);
-                if (!context->stripes[reconstruct_stripe].Irp->MdlAddress) {
-                    ERR("IoAllocateMdl failed\n");
-                    return STATUS_INSUFFICIENT_RESOURCES;
+            if (!recovered) {
+                UINT32 crc32;
+                tree_header* th = (tree_header*)(sector + (ci->num_stripes * Vcb->superblock.node_size));
+                
+                if (num_errors == 1) {
+                    raid6_recover2(sector, ci->num_stripes, Vcb->superblock.node_size, stripe, error_stripe, sector + (ci->num_stripes * Vcb->superblock.node_size));
+                    
+                    crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+
+                    if (th->address == addr && crc32 == *((UINT32*)th->csum) && (generation == 0 || th->generation == generation))
+                        recovered = TRUE;
+                } else {
+                    for (j = 0; j < ci->num_stripes - 1; j++) {
+                        if (j != stripe) {
+                            raid6_recover2(sector, ci->num_stripes, Vcb->superblock.node_size, stripe, j, sector + (ci->num_stripes * Vcb->superblock.node_size));
+                            
+                            crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+
+                            if (th->address == addr && crc32 == *((UINT32*)th->csum) && (generation == 0 || th->generation == generation)) {
+                                recovered = TRUE;
+                                error_stripe = j;
+                                break;
+                            }
+                        }
+                    }
                 }
                 
-                MmProbeAndLockPages(context->stripes[reconstruct_stripe].Irp->MdlAddress, KernelMode, IoWriteAccess);
-            } else {
-                context->stripes[reconstruct_stripe].Irp->UserBuffer = context->stripes[reconstruct_stripe].buf;
+                if (recovered) {
+                    UINT16 error_stripe_phys = (parity2 + error_stripe + 1) % ci->num_stripes;
+                    
+                    ERR("recovering from checksum error at %llx, device %llx\n", addr, devices[physstripe]->devitem.dev_id);
+                    RtlCopyMemory(buf, sector + (ci->num_stripes * Vcb->superblock.node_size), Vcb->superblock.node_size);
+                    
+                    if (!Vcb->readonly && devices[physstripe]->devobj && !devices[physstripe]->readonly) { // write good data over bad
+                        Status = write_data_phys(devices[physstripe]->devobj, cis[physstripe].offset + off,
+                                                 sector + (ci->num_stripes * Vcb->superblock.node_size), Vcb->superblock.node_size);
+                        if (!NT_SUCCESS(Status)) {
+                            WARN("write_data_phys returned %08x\n", Status);
+                            log_device_error(Vcb, devices[physstripe], BTRFS_DEV_STAT_WRITE_ERRORS);
+                        }
+                    }
+                    
+                    if (error_stripe == ci->num_stripes - 2) {
+                        ERR("recovering from parity error at %llx, device %llx\n", addr, devices[error_stripe_phys]->devitem.dev_id);
+                        
+                        RtlZeroMemory(sector + ((ci->num_stripes - 2) * Vcb->superblock.node_size), Vcb->superblock.node_size);
+                        log_device_error(Vcb, devices[error_stripe_phys], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                        
+                        for (j = 0; j < ci->num_stripes - 2; j++) {
+                            if (j == stripe) {
+                                do_xor(sector + ((ci->num_stripes - 2) * Vcb->superblock.node_size), sector + (ci->num_stripes * Vcb->superblock.node_size),
+                                        Vcb->superblock.node_size);
+                            } else {
+                                do_xor(sector + ((ci->num_stripes - 2) * Vcb->superblock.node_size), sector + (j * Vcb->superblock.node_size),
+                                        Vcb->superblock.node_size);
+                            }
+                        }
+                    } else {
+                        ERR("recovering from checksum error at %llx, device %llx\n", addr + ((error_stripe - stripe) * ci->stripe_length),
+                            devices[error_stripe_phys]->devitem.dev_id);
+                        
+                        log_device_error(Vcb, devices[error_stripe_phys], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                        
+                        RtlCopyMemory(sector + (error_stripe * Vcb->superblock.node_size),
+                                      sector + ((ci->num_stripes + 1) * Vcb->superblock.node_size), Vcb->superblock.node_size);
+                    }
+                    
+                    if (!Vcb->readonly && devices[error_stripe_phys]->devobj && !devices[error_stripe_phys]->readonly) { // write good data over bad
+                        Status = write_data_phys(devices[error_stripe_phys]->devobj, cis[error_stripe_phys].offset + off,
+                                                 sector + (error_stripe * Vcb->superblock.node_size), Vcb->superblock.node_size);
+                        if (!NT_SUCCESS(Status)) {
+                            WARN("write_data_phys returned %08x\n", Status);
+                            log_device_error(Vcb, devices[error_stripe_phys], BTRFS_DEV_STAT_WRITE_ERRORS);
+                        }
+                    }
+                }
             }
+        }
 
-            IrpSp->Parameters.Read.Length = stripeend[reconstruct_stripe] - stripestart[reconstruct_stripe];
-            IrpSp->Parameters.Read.ByteOffset.QuadPart = stripestart[reconstruct_stripe] + cis[reconstruct_stripe].offset;
-            
-            context->stripes[reconstruct_stripe].Irp->UserIosb = &context->stripes[reconstruct_stripe].iosb;
-            
-            IoSetCompletionRoutine(context->stripes[reconstruct_stripe].Irp, read_data_completion, &context->stripes[reconstruct_stripe], TRUE, TRUE, TRUE);
-
-            context->stripes[reconstruct_stripe].status = ReadDataStatus_Pending;
+        if (!recovered) {
+            ERR("unrecoverable checksum error at %llx\n", addr);
+            ExFreePool(sector);
+            Status = STATUS_CRC_ERROR;
+            goto end;
         }
+        
+        ExFreePool(sector);
+    } else {
+        ULONG sectors = length / Vcb->superblock.sector_size;
+        UINT8* sector;
+        
+        sector = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.sector_size * (ci->num_stripes + 2), ALLOC_TAG);
+        if (!sector) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
+        }
+        
+        for (i = 0; i < sectors; i++) {
+            UINT32 crc32 = ~calc_crc32c(0xffffffff, buf + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
             
-        if (needs_reconstruct > 0) {
-#ifdef DEBUG_STATS
-            LARGE_INTEGER time1, time2;
-#endif
-            context->stripes_left = needs_reconstruct;
-            KeClearEvent(&context->Event);
-            
-#ifdef DEBUG_STATS
-            if (!context->tree)
-                time1 = KeQueryPerformanceCounter(NULL);
-#endif
-            
-            for (i = 0; i < needs_reconstruct; i++) {
-                UINT16 reconstruct_stripe = i == 0 ? missing1 : missing2;
+            if (context->csum[i] != crc32) {
+                UINT16 j, stripe, physstripe, parity1, parity2, error_stripe;
+                UINT64 off;
+                BOOL recovered = FALSE, failed = FALSE;
+                ULONG num_errors = 0;
                 
-                IoCallDriver(devices[reconstruct_stripe]->devobj, context->stripes[reconstruct_stripe].Irp);
-            }
-            
-            KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
-            
-#ifdef DEBUG_STATS
-            if (!context->tree) {
-                time2 = KeQueryPerformanceCounter(NULL);
+                get_raid0_offset(addr - offset + UInt32x32To64(i, Vcb->superblock.sector_size), ci->stripe_length,
+                                 ci->num_stripes - 2, &off, &stripe);
                 
-                Vcb->stats.read_disk_time += time2.QuadPart - time1.QuadPart;
-            }
-#endif
+                parity1 = (((addr - offset + UInt32x32To64(i, Vcb->superblock.sector_size)) / ((ci->num_stripes - 2) * ci->stripe_length)) + ci->num_stripes - 2) % ci->num_stripes;
+                parity2 = (parity1 + 1) % ci->num_stripes;
+                
+                physstripe = (parity2 + stripe + 1) % ci->num_stripes;
+                
+                log_device_error(Vcb, devices[physstripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                
+                j = (parity2 + 1) % ci->num_stripes;
+                do {
+                    if (j != stripe) {
+                        if (devices[j]->devobj) {
+                            Status = sync_read_phys(devices[j]->devobj, cis[j].offset + off, Vcb->superblock.sector_size, sector + (j * Vcb->superblock.sector_size), FALSE);
+                            if (!NT_SUCCESS(Status)) {
+                                ERR("sync_read_phys returned %08x\n", Status);
+                                log_device_error(Vcb, devices[j], BTRFS_DEV_STAT_READ_ERRORS);
+                                num_errors++;
+                                error_stripe = j;
+                                
+                                if (num_errors > 1) {
+                                    failed = TRUE;
+                                    break;
+                                }
+                            }
+                        } else {
+                            num_errors++;
+                            error_stripe = j;
+                            
+                            if (num_errors > 1) {
+                                failed = TRUE;
+                                break;
+                            }
+                        }
+                    }
+                    
+                    j = (j + 1) % ci->num_stripes;
+                } while (j != (parity2 + 1) % ci->num_stripes);
+                
+                if (!failed) {
+                    if (num_errors == 0) {
+                        j = (stripe + 1) % ci->num_stripes;
+                
+                        RtlCopyMemory(sector + (stripe * Vcb->superblock.sector_size), sector + (j * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+                        
+                        j = (j + 1) % ci->num_stripes;
+                        while (j != stripe) {
+                            if (j != parity2)
+                                do_xor(sector + (stripe * Vcb->superblock.sector_size), sector + (j * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+                            
+                            j = (j + 1) % ci->num_stripes;
+                        }
+                        
+                        crc32 = ~calc_crc32c(0xffffffff, sector + (stripe * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+                        
+                        if (crc32 == context->csum[i]) {
+                            RtlCopyMemory(buf + (i * Vcb->superblock.sector_size), sector + (stripe * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+                            ERR("recovering from checksum error at %llx, device %llx\n", addr + UInt32x32To64(i, Vcb->superblock.sector_size), devices[physstripe]->devitem.dev_id);
+                            recovered = TRUE;
+                            
+                            if (!Vcb->readonly && !devices[physstripe]->readonly) { // write good data over bad
+                                Status = write_data_phys(devices[physstripe]->devobj, cis[physstripe].offset + off,
+                                                         sector + (stripe * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+                                if (!NT_SUCCESS(Status)) {
+                                    WARN("write_data_phys returned %08x\n", Status);
+                                    log_device_error(Vcb, devices[physstripe], BTRFS_DEV_STAT_WRITE_ERRORS);
+                                }
+                            }
+                        }
+                    }
+                    
+                    if (!recovered) {
+                        if (num_errors == 1) {
+                            raid6_recover2(sector, ci->num_stripes, Vcb->superblock.sector_size, stripe, error_stripe, sector + (ci->num_stripes * Vcb->superblock.sector_size));
+                            
+                            crc32 = ~calc_crc32c(0xffffffff, sector + (ci->num_stripes * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
 
-            for (i = 0; i < needs_reconstruct; i++) {
-                UINT16 reconstruct_stripe = i == 0 ? missing1 : missing2;
-                
-                if (context->stripes[reconstruct_stripe].status != ReadDataStatus_Success) {
-                    ERR("unrecoverable checksum error\n");
-                    return STATUS_CRC_ERROR;
+                            if (crc32 == context->csum[i])
+                                recovered = TRUE;
+                        } else {
+                            for (j = 0; j < ci->num_stripes - 1; j++) {
+                                if (j != stripe) {
+                                    raid6_recover2(sector, ci->num_stripes, Vcb->superblock.sector_size, stripe, j, sector + (ci->num_stripes * Vcb->superblock.sector_size));
+                                    
+                                    crc32 = ~calc_crc32c(0xffffffff, sector + (ci->num_stripes * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+
+                                    if (crc32 == context->csum[i]) {
+                                        recovered = TRUE;
+                                        error_stripe = j;
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                        
+                        if (recovered) {
+                            UINT16 error_stripe_phys = (parity2 + error_stripe + 1) % ci->num_stripes;
+                            
+                            ERR("recovering from checksum error at %llx, device %llx\n", addr + UInt32x32To64(i, Vcb->superblock.sector_size), devices[physstripe]->devitem.dev_id);
+                            RtlCopyMemory(buf + (i * Vcb->superblock.sector_size), sector + (ci->num_stripes * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+                            
+                            if (!Vcb->readonly && devices[physstripe]->devobj && !devices[physstripe]->readonly) { // write good data over bad
+                                Status = write_data_phys(devices[physstripe]->devobj, cis[physstripe].offset + off,
+                                                         sector + (ci->num_stripes * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+                                if (!NT_SUCCESS(Status)) {
+                                    WARN("write_data_phys returned %08x\n", Status);
+                                    log_device_error(Vcb, devices[physstripe], BTRFS_DEV_STAT_WRITE_ERRORS);
+                                }
+                            }
+                            
+                            if (error_stripe == ci->num_stripes - 2) {
+                                ERR("recovering from parity error at %llx, device %llx\n", addr + UInt32x32To64(i, Vcb->superblock.sector_size), devices[error_stripe_phys]->devitem.dev_id);
+                                
+                                log_device_error(Vcb, devices[error_stripe_phys], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                                
+                                RtlZeroMemory(sector + ((ci->num_stripes - 2) * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+                                
+                                for (j = 0; j < ci->num_stripes - 2; j++) {
+                                    if (j == stripe) {
+                                        do_xor(sector + ((ci->num_stripes - 2) * Vcb->superblock.sector_size), sector + (ci->num_stripes * Vcb->superblock.sector_size),
+                                               Vcb->superblock.sector_size);
+                                    } else {
+                                        do_xor(sector + ((ci->num_stripes - 2) * Vcb->superblock.sector_size), sector + (j * Vcb->superblock.sector_size),
+                                               Vcb->superblock.sector_size);
+                                    }
+                                }
+                            } else {
+                                ERR("recovering from checksum error at %llx, device %llx\n", addr + UInt32x32To64(i, Vcb->superblock.sector_size) + ((error_stripe - stripe) * ci->stripe_length),
+                                    devices[error_stripe_phys]->devitem.dev_id);
+                                
+                                log_device_error(Vcb, devices[error_stripe_phys], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                                
+                                RtlCopyMemory(sector + (error_stripe * Vcb->superblock.sector_size),
+                                              sector + ((ci->num_stripes + 1) * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+                            }
+                            
+                            if (!Vcb->readonly && devices[error_stripe_phys]->devobj && !devices[error_stripe_phys]->readonly) { // write good data over bad
+                                Status = write_data_phys(devices[error_stripe_phys]->devobj, cis[error_stripe_phys].offset + off,
+                                                         sector + (error_stripe * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+                                if (!NT_SUCCESS(Status)) {
+                                    WARN("write_data_phys returned %08x\n", Status);
+                                    log_device_error(Vcb, devices[error_stripe_phys], BTRFS_DEV_STAT_WRITE_ERRORS);
+                                }
+                            }
+                        }
+                    }
                 }
+
+                if (!recovered) {
+                    ERR("unrecoverable checksum error at %llx\n", addr + UInt32x32To64(i, Vcb->superblock.sector_size));
+                    ExFreePool(sector);
+                    Status = STATUS_CRC_ERROR;
+                    goto end;
+                }
             }
         }
         
-        off = origoff;
-        
-        if (context->tree) {
-            pos = 0;
-            stripeoff = 0;
-            if (!raid6_decode_with_checksum_metadata(addr, off, skip, context, ci, &stripeoff, buf, &pos, length, firststripesize, Vcb->superblock.node_size)) {
-                ERR("unrecoverable metadata checksum error\n");
-                return STATUS_CRC_ERROR;
-            }
-        } else {
-            pos = 0;
-            stripeoff = 0;
-            if (!raid6_decode_with_checksum(off, skip, context, ci, &stripeoff, buf, &pos, length, firststripesize, context->csum, Vcb->superblock.sector_size))
-                return STATUS_CRC_ERROR;
-            
-            while (pos < length) {
-                off += (ci->num_stripes - 1) * ci->stripe_length;
-                if (!raid6_decode_with_checksum(off, 0, context, ci, &stripeoff, buf, &pos, length, 0, context->csum, Vcb->superblock.sector_size))
-                    return STATUS_CRC_ERROR;
-            }
-        }
+        ExFreePool(sector);
     }
     
-    // write good data over bad
+    Status = STATUS_SUCCESS;
     
-    if (!Vcb->readonly) {
-        CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
-        
-        for (i = 0; i < ci->num_stripes; i++) {
-            if (context->stripes[i].rewrite && devices[i] && !devices[i]->readonly) {
-                Status = write_data_phys(devices[i]->devobj, cis[i].offset + stripestart[i], context->stripes[i].buf, stripeend[i] - stripestart[i]);
-                
-                if (!NT_SUCCESS(Status))
-                    WARN("write_data_phys returned %08x\n", Status);
-            }
-        }
-    }
+end:
+    if (c)
+        chunk_unlock_range(Vcb, c, lockaddr, locklen);
     
-    return STATUS_SUCCESS;
+    return Status;
 }
 
 NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UINT32* csum, BOOL is_tree, UINT8* buf, chunk* c, chunk** pc,
-                           PIRP Irp, BOOL check_nocsum_parity) {
+                           PIRP Irp, UINT64 generation, BOOL file_read, UINT32 irp_offset) {
     CHUNK_ITEM* ci;
     CHUNK_ITEM_STRIPE* cis;
-    read_data_context* context;
-    UINT64 i, type, offset;
+    read_data_context context;
+    UINT64 i, type, offset, total_reading = 0;
     NTSTATUS Status;
     device** devices;
-    UINT64 *stripestart = NULL, *stripeend = NULL;
-    UINT32 firststripesize;
     UINT16 startoffstripe, allowed_missing, missing_devices = 0;
+    UINT8* dummypage = NULL;
+    PMDL dummy_mdl = NULL;
 #ifdef DEBUG_STATS
     LARGE_INTEGER time1, time2;
 #endif
@@ -2536,7 +1407,7 @@
     
     if (ci->type & BLOCK_FLAG_DUPLICATE) {
         type = BLOCK_FLAG_DUPLICATE;
-        allowed_missing = 0;
+        allowed_missing = ci->num_stripes - 1;
     } else if (ci->type & BLOCK_FLAG_RAID0) {
         type = BLOCK_FLAG_RAID0;
         allowed_missing = 0;
@@ -2559,76 +1430,143 @@
 
     cis = (CHUNK_ITEM_STRIPE*)&ci[1];
 
-    context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_context), ALLOC_TAG);
-    if (!context) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
+    RtlZeroMemory(&context, sizeof(read_data_context));
+    KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
     
-    RtlZeroMemory(context, sizeof(read_data_context));
-    KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
-    
-    context->stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_stripe) * ci->num_stripes, ALLOC_TAG);
-    if (!context->stripes) {
+    context.stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_stripe) * ci->num_stripes, ALLOC_TAG);
+    if (!context.stripes) {
         ERR("out of memory\n");
-        ExFreePool(context);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    RtlZeroMemory(context->stripes, sizeof(read_data_stripe) * ci->num_stripes);
+    RtlZeroMemory(context.stripes, sizeof(read_data_stripe) * ci->num_stripes);
     
-    context->buflen = length;
-    context->num_stripes = ci->num_stripes;
-    context->stripes_left = context->num_stripes;
-    context->sector_size = Vcb->superblock.sector_size;
-    context->csum = csum;
-    context->tree = is_tree;
-    context->type = type;
-    context->check_nocsum_parity = check_nocsum_parity;
+    context.buflen = length;
+    context.num_stripes = ci->num_stripes;
+    context.stripes_left = context.num_stripes;
+    context.sector_size = Vcb->superblock.sector_size;
+    context.csum = csum;
+    context.tree = is_tree;
+    context.type = type;
     
-    stripestart = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT64) * ci->num_stripes, ALLOC_TAG);
-    if (!stripestart) {
-        ERR("out of memory\n");
-        ExFreePool(context);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    stripeend = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT64) * ci->num_stripes, ALLOC_TAG);
-    if (!stripeend) {
-        ERR("out of memory\n");
-        ExFreePool(stripestart);
-        ExFreePool(context);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
     if (type == BLOCK_FLAG_RAID0) {
         UINT64 startoff, endoff;
-        UINT16 endoffstripe;
+        UINT16 endoffstripe, stripe;
+        UINT32 *stripeoff, pos;
+        PMDL master_mdl;
+        PFN_NUMBER* pfns;
         
+        // FIXME - test this still works if page size isn't the same as sector size
+        
+        // This relies on the fact that MDLs are followed in memory by the page file numbers,
+        // so with a bit of jiggery-pokery you can trick your disks into deinterlacing your RAID0
+        // data for you without doing a memcpy yourself.
+        // MDLs are officially opaque, so this might very well break in future versions of Windows.
+        
         get_raid0_offset(addr - offset, ci->stripe_length, ci->num_stripes, &startoff, &startoffstripe);
         get_raid0_offset(addr + length - offset - 1, ci->stripe_length, ci->num_stripes, &endoff, &endoffstripe);
         
+        if (file_read) {
+            // Unfortunately we can't avoid doing at least one memcpy, as Windows can give us an MDL
+            // with duplicated dummy PFNs, which confuse check_csum. Ah well.
+            // See https://msdn.microsoft.com/en-us/library/windows/hardware/Dn614012.aspx if you're interested.
+            
+            context.va = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
+            
+            if (!context.va) {
+                ERR("out of memory\n");
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+        } else
+            context.va = buf;
+        
+        master_mdl = IoAllocateMdl(context.va, length, FALSE, FALSE, NULL);
+        if (!master_mdl) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        MmProbeAndLockPages(master_mdl, KernelMode, IoWriteAccess);
+        pfns = (PFN_NUMBER*)(master_mdl + 1);
+        
         for (i = 0; i < ci->num_stripes; i++) {
-            if (startoffstripe > i) {
-                stripestart[i] = startoff - (startoff % ci->stripe_length) + ci->stripe_length;
-            } else if (startoffstripe == i) {
-                stripestart[i] = startoff;
-            } else {
-                stripestart[i] = startoff - (startoff % ci->stripe_length);
+            if (startoffstripe > i)
+                context.stripes[i].stripestart = startoff - (startoff % ci->stripe_length) + ci->stripe_length;
+            else if (startoffstripe == i)
+                context.stripes[i].stripestart = startoff;
+            else
+                context.stripes[i].stripestart = startoff - (startoff % ci->stripe_length);
+            
+            if (endoffstripe > i)
+                context.stripes[i].stripeend = endoff - (endoff % ci->stripe_length) + ci->stripe_length;
+            else if (endoffstripe == i)
+                context.stripes[i].stripeend = endoff + 1;
+            else
+                context.stripes[i].stripeend = endoff - (endoff % ci->stripe_length);
+            
+            if (context.stripes[i].stripestart != context.stripes[i].stripeend) {
+                context.stripes[i].mdl = IoAllocateMdl(context.va, context.stripes[i].stripeend - context.stripes[i].stripestart, FALSE, FALSE, NULL);
+
+                if (!context.stripes[i].mdl) {
+                    ERR("IoAllocateMdl failed\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto exit;
+                }
             }
+        }
+        
+        stripeoff = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT32) * ci->num_stripes, ALLOC_TAG);
+        if (!stripeoff) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        RtlZeroMemory(stripeoff, sizeof(UINT32) * ci->num_stripes);
+
+        pos = 0;
+        stripe = startoffstripe;
+        while (pos < length) {
+            PFN_NUMBER* stripe_pfns = (PFN_NUMBER*)(context.stripes[stripe].mdl + 1);
             
-            if (endoffstripe > i) {
-                stripeend[i] = endoff - (endoff % ci->stripe_length) + ci->stripe_length;
-            } else if (endoffstripe == i) {
-                stripeend[i] = endoff + 1;
+            if (pos == 0) {
+                UINT32 readlen = min(context.stripes[stripe].stripeend - context.stripes[stripe].stripestart, ci->stripe_length - (context.stripes[stripe].stripestart % ci->stripe_length));
+
+                RtlCopyMemory(stripe_pfns, pfns, readlen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                stripeoff[stripe] += readlen;
+                pos += readlen;
+            } else if (length - pos < ci->stripe_length) {
+                RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (length - pos) * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                pos = length;
             } else {
-                stripeend[i] = endoff - (endoff % ci->stripe_length);
+                RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], ci->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                stripeoff[stripe] += ci->stripe_length;
+                pos += ci->stripe_length;
             }
+            
+            stripe = (stripe + 1) % ci->num_stripes;
         }
+        
+        MmUnlockPages(master_mdl);
+        IoFreeMdl(master_mdl);
+
+        ExFreePool(stripeoff);
     } else if (type == BLOCK_FLAG_RAID10) {
         UINT64 startoff, endoff;
-        UINT16 endoffstripe, j;
+        UINT16 endoffstripe, j, stripe;
+        ULONG orig_ls;
+        PMDL master_mdl;
+        PFN_NUMBER* pfns;
+        UINT32* stripeoff, pos;
+        read_data_stripe** stripes;
         
+        if (c)
+            orig_ls = c->last_stripe;
+        else
+            orig_ls = 0;
+        
         get_raid0_offset(addr - offset, ci->stripe_length, ci->num_stripes / ci->sub_stripes, &startoff, &startoffstripe);
         get_raid0_offset(addr + length - offset - 1, ci->stripe_length, ci->num_stripes / ci->sub_stripes, &endoff, &endoffstripe);
         
@@ -2638,149 +1576,669 @@
             goto exit;
         }
         
-        context->firstoff = (startoff % ci->stripe_length) / Vcb->superblock.sector_size;
-        context->startoffstripe = startoffstripe;
-        context->sectors_per_stripe = ci->stripe_length / Vcb->superblock.sector_size;
+        if (file_read) {
+            context.va = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
+            
+            if (!context.va) {
+                ERR("out of memory\n");
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+        } else
+            context.va = buf;
         
+        context.firstoff = (startoff % ci->stripe_length) / Vcb->superblock.sector_size;
+        context.startoffstripe = startoffstripe;
+        context.sectors_per_stripe = ci->stripe_length / Vcb->superblock.sector_size;
+        
         startoffstripe *= ci->sub_stripes;
         endoffstripe *= ci->sub_stripes;
         
+        if (c)
+            c->last_stripe = (orig_ls + 1) % ci->sub_stripes;
+        
+        master_mdl = IoAllocateMdl(context.va, length, FALSE, FALSE, NULL);
+        if (!master_mdl) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        MmProbeAndLockPages(master_mdl, KernelMode, IoWriteAccess);
+        pfns = (PFN_NUMBER*)(master_mdl + 1);
+        
+        stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_stripe*) * ci->num_stripes / ci->sub_stripes, ALLOC_TAG);
+        if (!stripes) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlZeroMemory(stripes, sizeof(read_data_stripe*) * ci->num_stripes / ci->sub_stripes);
+        
         for (i = 0; i < ci->num_stripes; i += ci->sub_stripes) {
-            if (startoffstripe > i) {
-                stripestart[i] = startoff - (startoff % ci->stripe_length) + ci->stripe_length;
-            } else if (startoffstripe == i) {
-                stripestart[i] = startoff;
-            } else {
-                stripestart[i] = startoff - (startoff % ci->stripe_length);
+            UINT64 sstart, send;
+            
+            if (startoffstripe > i)
+                sstart = startoff - (startoff % ci->stripe_length) + ci->stripe_length;
+            else if (startoffstripe == i)
+                sstart = startoff;
+            else
+                sstart = startoff - (startoff % ci->stripe_length);
+            
+            if (endoffstripe > i)
+                send = endoff - (endoff % ci->stripe_length) + ci->stripe_length;
+            else if (endoffstripe == i)
+                send = endoff + 1;
+            else
+                send = endoff - (endoff % ci->stripe_length);
+            
+            for (j = 0; j < ci->sub_stripes; j++) {
+                if (j == orig_ls) {
+                    context.stripes[i+j].stripestart = sstart;
+                    context.stripes[i+j].stripeend = send;
+                    stripes[i / ci->sub_stripes] = &context.stripes[i+j];
+                    
+                    if (sstart != send) {
+                        context.stripes[i+j].mdl = IoAllocateMdl(context.va, send - sstart, FALSE, FALSE, NULL);
+
+                        if (!context.stripes[i+j].mdl) {
+                            ERR("IoAllocateMdl failed\n");
+                            Status = STATUS_INSUFFICIENT_RESOURCES;
+                            goto exit;
+                        }
+                    }
+                } else
+                    context.stripes[i+j].status = ReadDataStatus_Skip;
             }
+        }
+        
+        stripeoff = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT32) * ci->num_stripes / ci->sub_stripes, ALLOC_TAG);
+        if (!stripeoff) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        RtlZeroMemory(stripeoff, sizeof(UINT32) * ci->num_stripes / ci->sub_stripes);
+
+        pos = 0;
+        stripe = startoffstripe / ci->sub_stripes;
+        while (pos < length) {
+            PFN_NUMBER* stripe_pfns = (PFN_NUMBER*)(stripes[stripe]->mdl + 1);
             
-            if (endoffstripe > i) {
-                stripeend[i] = endoff - (endoff % ci->stripe_length) + ci->stripe_length;
-            } else if (endoffstripe == i) {
-                stripeend[i] = endoff + 1;
+            if (pos == 0) {
+                UINT32 readlen = min(stripes[stripe]->stripeend - stripes[stripe]->stripestart, ci->stripe_length - (stripes[stripe]->stripestart % ci->stripe_length));
+
+                RtlCopyMemory(stripe_pfns, pfns, readlen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                stripeoff[stripe] += readlen;
+                pos += readlen;
+            } else if (length - pos < ci->stripe_length) {
+                RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (length - pos) * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                pos = length;
             } else {
-                stripeend[i] = endoff - (endoff % ci->stripe_length);
+                RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], ci->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                stripeoff[stripe] += ci->stripe_length;
+                pos += ci->stripe_length;
             }
             
-            for (j = 1; j < ci->sub_stripes; j++) {
-                stripestart[i+j] = stripestart[i];
-                stripeend[i+j] = stripeend[i];
+            stripe = (stripe + 1) % (ci->num_stripes / ci->sub_stripes);
+        }
+
+        MmUnlockPages(master_mdl);
+        IoFreeMdl(master_mdl);
+
+        ExFreePool(stripeoff);
+        ExFreePool(stripes);
+    } else if (type == BLOCK_FLAG_DUPLICATE) {
+        UINT64 orig_ls;
+        
+        if (c)
+            orig_ls = i = c->last_stripe;
+        else
+            orig_ls = i = 0;
+        
+        while (!devices[i]) {
+            i = (i + 1) % ci->num_stripes;
+            
+            if (i == orig_ls) {
+                ERR("no devices available to service request\n");
+                Status = STATUS_DEVICE_NOT_READY;
+                goto exit;
             }
         }
         
-        context->stripes_cancel = 1;
-    } else if (type == BLOCK_FLAG_DUPLICATE) {
-        for (i = 0; i < ci->num_stripes; i++) {
-            stripestart[i] = addr - offset;
-            stripeend[i] = stripestart[i] + length;
+        if (c)
+            c->last_stripe = (i + 1) % ci->num_stripes;
+
+        context.stripes[i].stripestart = addr - offset;
+        context.stripes[i].stripeend = context.stripes[i].stripestart + length;
+
+        context.stripes[i].buf = buf;
+        context.stripes[i].not_alloc = TRUE;
+        
+        if (file_read) {
+            UINT8* va;
+    
+            va = (UINT8*)MmGetMdlVirtualAddress(Irp->MdlAddress) + irp_offset;
+            
+            context.stripes[i].mdl = IoAllocateMdl(va, context.stripes[i].stripeend - context.stripes[i].stripestart, FALSE, FALSE, NULL);
+            if (!context.stripes[i].mdl) {
+                ERR("IoAllocateMdl failed\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto exit;
+            }
+            
+            IoBuildPartialMdl(Irp->MdlAddress, context.stripes[i].mdl, va, context.stripes[i].stripeend - context.stripes[i].stripestart);
+        } else {
+            context.stripes[i].mdl = IoAllocateMdl(context.stripes[i].buf, context.stripes[i].stripeend - context.stripes[i].stripestart, FALSE, FALSE, NULL);
+
+            if (!context.stripes[i].mdl) {
+                ERR("IoAllocateMdl failed\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto exit;
+            }
+            
+            MmProbeAndLockPages(context.stripes[i].mdl, KernelMode, IoWriteAccess);
         }
-        
-        context->stripes_cancel = ci->num_stripes - 1;
     } else if (type == BLOCK_FLAG_RAID5) {
         UINT64 startoff, endoff;
-        UINT16 endoffstripe;
-        UINT64 start = 0xffffffffffffffff, end = 0;
+        UINT16 endoffstripe, parity;
+        UINT32 *stripeoff, pos;
+        PMDL master_mdl;
+        PFN_NUMBER *pfns, dummy;
+        BOOL need_dummy = FALSE;
         
         get_raid0_offset(addr - offset, ci->stripe_length, ci->num_stripes - 1, &startoff, &startoffstripe);
         get_raid0_offset(addr + length - offset - 1, ci->stripe_length, ci->num_stripes - 1, &endoff, &endoffstripe);
         
-        for (i = 0; i < ci->num_stripes - 1; i++) {
-            UINT64 ststart, stend;
+        if (file_read) {
+            context.va = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
             
-            if (startoffstripe > i) {
-                ststart = startoff - (startoff % ci->stripe_length) + ci->stripe_length;
-            } else if (startoffstripe == i) {
-                ststart = startoff;
-            } else {
-                ststart = startoff - (startoff % ci->stripe_length);
+            if (!context.va) {
+                ERR("out of memory\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto exit;
             }
-              
-            if (endoffstripe > i) {
-                stend = endoff - (endoff % ci->stripe_length) + ci->stripe_length;
-            } else if (endoffstripe == i) {
-                stend = endoff + 1;
-            } else {
-                stend = endoff - (endoff % ci->stripe_length);
-            }
+        } else
+            context.va = buf;
+
+        master_mdl = IoAllocateMdl(context.va, length, FALSE, FALSE, NULL);
+        if (!master_mdl) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
+        
+        MmProbeAndLockPages(master_mdl, KernelMode, IoWriteAccess);
+        pfns = (PFN_NUMBER*)(master_mdl + 1);
+        
+        pos = 0;
+        while (pos < length) {
+            parity = (((addr - offset + pos) / ((ci->num_stripes - 1) * ci->stripe_length)) + ci->num_stripes - 1) % ci->num_stripes;
             
-            if (ststart != stend) {
-                if (ststart < start) {
-                    start = ststart;
-                    firststripesize = ci->stripe_length - (ststart % ci->stripe_length);
+            if (pos == 0) {
+                UINT16 stripe = (parity + startoffstripe + 1) % ci->num_stripes;
+                ULONG skip, readlen;
+                
+                i = startoffstripe;
+                while (stripe != parity) {
+                    if (i == startoffstripe) {
+                        readlen = min(length, ci->stripe_length - (startoff % ci->stripe_length));
+                        
+                        context.stripes[stripe].stripestart = startoff;
+                        context.stripes[stripe].stripeend = startoff + readlen;
+                        
+                        pos += readlen;
+                        
+                        if (pos == length)
+                            break;
+                    } else {
+                        readlen = min(length - pos, ci->stripe_length);
+                        
+                        context.stripes[stripe].stripestart = startoff - (startoff % ci->stripe_length);
+                        context.stripes[stripe].stripeend = context.stripes[stripe].stripestart + readlen;
+                        
+                        pos += readlen;
+                        
+                        if (pos == length)
+                            break;
+                    }
+                    
+                    i++;
+                    stripe = (stripe + 1) % ci->num_stripes;
                 }
                 
-                if (stend > end)
-                    end = stend;
+                if (pos == length)
+                    break;
+                
+                for (i = 0; i < startoffstripe; i++) {
+                    UINT16 stripe = (parity + i + 1) % ci->num_stripes;
+                    
+                    context.stripes[stripe].stripestart = context.stripes[stripe].stripeend = startoff - (startoff % ci->stripe_length) + ci->stripe_length;
+                }
+                
+                context.stripes[parity].stripestart = context.stripes[parity].stripeend = startoff - (startoff % ci->stripe_length) + ci->stripe_length;
+                
+                if (length - pos > ci->num_stripes * (ci->num_stripes - 1) * ci->stripe_length) {
+                    skip = ((length - pos) / (ci->num_stripes * (ci->num_stripes - 1) * ci->stripe_length)) - 1;
+                    
+                    for (i = 0; i < ci->num_stripes; i++) {
+                        context.stripes[i].stripeend += skip * ci->num_stripes * ci->stripe_length;
+                    }
+                    
+                    pos += skip * (ci->num_stripes - 1) * ci->num_stripes * ci->stripe_length;
+                    need_dummy = TRUE;
+                }
+            } else if (length - pos >= ci->stripe_length * (ci->num_stripes - 1)) {
+                for (i = 0; i < ci->num_stripes; i++) {
+                    context.stripes[i].stripeend += ci->stripe_length;
+                }
+                
+                pos += ci->stripe_length * (ci->num_stripes - 1);
+                need_dummy = TRUE;
+            } else {
+                UINT16 stripe = (parity + 1) % ci->num_stripes;
+                
+                i = 0;
+                while (stripe != parity) {
+                    if (endoffstripe == i) {
+                        context.stripes[stripe].stripeend = endoff + 1;
+                        break;
+                    } else if (endoffstripe > i)
+                        context.stripes[stripe].stripeend = endoff - (endoff % ci->stripe_length) + ci->stripe_length;
+                    
+                    i++;
+                    stripe = (stripe + 1) % ci->num_stripes;
+                }
+                
+                break;
             }
         }
         
         for (i = 0; i < ci->num_stripes; i++) {
-            stripestart[i] = start;
-            stripeend[i] = end;
+            if (context.stripes[i].stripestart != context.stripes[i].stripeend) {
+                context.stripes[i].mdl = IoAllocateMdl(context.va, context.stripes[i].stripeend - context.stripes[i].stripestart, FALSE, FALSE, NULL);
+
+                if (!context.stripes[i].mdl) {
+                    ERR("IoAllocateMdl failed\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto exit;
+                }
+            }
         }
         
-        context->stripes_cancel = Vcb->options.raid5_recalculation;
+        if (need_dummy) {
+            dummypage = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, ALLOC_TAG);
+            if (!dummypage) {
+                ERR("out of memory\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto exit;
+            }
+            
+            dummy_mdl = IoAllocateMdl(dummypage, PAGE_SIZE, FALSE, FALSE, NULL);
+            if (!dummy_mdl) {
+                ERR("IoAllocateMdl failed\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                ExFreePool(dummypage);
+                goto exit;
+            }
+            
+            MmBuildMdlForNonPagedPool(dummy_mdl);
+            
+            dummy = *(PFN_NUMBER*)(dummy_mdl + 1);
+        }
+        
+        stripeoff = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT32) * ci->num_stripes, ALLOC_TAG);
+        if (!stripeoff) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
+
+        RtlZeroMemory(stripeoff, sizeof(UINT32) * ci->num_stripes);
+
+        pos = 0;
+        
+        while (pos < length) {
+            PFN_NUMBER* stripe_pfns;
+            
+            parity = (((addr - offset + pos) / ((ci->num_stripes - 1) * ci->stripe_length)) + ci->num_stripes - 1) % ci->num_stripes;
+            
+            if (pos == 0) {
+                UINT16 stripe = (parity + startoffstripe + 1) % ci->num_stripes;
+                UINT32 readlen = min(length - pos, min(context.stripes[stripe].stripeend - context.stripes[stripe].stripestart,
+                                                       ci->stripe_length - (context.stripes[stripe].stripestart % ci->stripe_length)));
+                
+                stripe_pfns = (PFN_NUMBER*)(context.stripes[stripe].mdl + 1);
+                
+                RtlCopyMemory(stripe_pfns, pfns, readlen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                stripeoff[stripe] = readlen;
+                pos += readlen;
+                
+                stripe = (stripe + 1) % ci->num_stripes;
+                
+                while (stripe != parity) {
+                    stripe_pfns = (PFN_NUMBER*)(context.stripes[stripe].mdl + 1);
+                    readlen = min(length - pos, min(context.stripes[stripe].stripeend - context.stripes[stripe].stripestart, ci->stripe_length));
+                    
+                    if (readlen == 0)
+                        break;
+                    
+                    RtlCopyMemory(stripe_pfns, &pfns[pos >> PAGE_SHIFT], readlen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                    
+                    stripeoff[stripe] = readlen;
+                    pos += readlen;
+                    
+                    stripe = (stripe + 1) % ci->num_stripes;
+                }
+            } else if (length - pos >= ci->stripe_length * (ci->num_stripes - 1)) {
+                UINT16 stripe = (parity + 1) % ci->num_stripes;
+                
+                while (stripe != parity) {
+                    stripe_pfns = (PFN_NUMBER*)(context.stripes[stripe].mdl + 1);
+                    
+                    RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], ci->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                    
+                    stripeoff[stripe] += ci->stripe_length;
+                    pos += ci->stripe_length;
+                    
+                    stripe = (stripe + 1) % ci->num_stripes;
+                }
+                
+                stripe_pfns = (PFN_NUMBER*)(context.stripes[parity].mdl + 1);
+                
+                for (i = 0; i < ci->stripe_length >> PAGE_SHIFT; i++) {
+                    stripe_pfns[stripeoff[parity] >> PAGE_SHIFT] = dummy;
+                    stripeoff[parity] += PAGE_SIZE;
+                }
+            } else {
+                UINT16 stripe = (parity + 1) % ci->num_stripes;
+                UINT32 readlen;
+                
+                while (pos < length) {
+                    stripe_pfns = (PFN_NUMBER*)(context.stripes[stripe].mdl + 1);
+                    readlen = min(length - pos, min(context.stripes[stripe].stripeend - context.stripes[stripe].stripestart, ci->stripe_length));
+                    
+                    if (readlen == 0)
+                        break;
+                    
+                    RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], readlen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                    
+                    stripeoff[stripe] += readlen;
+                    pos += readlen;
+                    
+                    stripe = (stripe + 1) % ci->num_stripes;
+                }
+            }
+        }
+        
+        MmUnlockPages(master_mdl);
+        IoFreeMdl(master_mdl);
+
+        ExFreePool(stripeoff);
     } else if (type == BLOCK_FLAG_RAID6) {
         UINT64 startoff, endoff;
-        UINT16 endoffstripe;
-        UINT64 start = 0xffffffffffffffff, end = 0;
+        UINT16 endoffstripe, parity1;
+        UINT32 *stripeoff, pos;
+        PMDL master_mdl;
+        PFN_NUMBER *pfns, dummy;
+        BOOL need_dummy = FALSE;
         
         get_raid0_offset(addr - offset, ci->stripe_length, ci->num_stripes - 2, &startoff, &startoffstripe);
         get_raid0_offset(addr + length - offset - 1, ci->stripe_length, ci->num_stripes - 2, &endoff, &endoffstripe);
         
-        for (i = 0; i < ci->num_stripes - 2; i++) {
-            UINT64 ststart, stend;
+        if (file_read) {
+            context.va = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
             
-            if (startoffstripe > i) {
-                ststart = startoff - (startoff % ci->stripe_length) + ci->stripe_length;
-            } else if (startoffstripe == i) {
-                ststart = startoff;
-            } else {
-                ststart = startoff - (startoff % ci->stripe_length);
+            if (!context.va) {
+                ERR("out of memory\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto exit;
             }
-              
-            if (endoffstripe > i) {
-                stend = endoff - (endoff % ci->stripe_length) + ci->stripe_length;
-            } else if (endoffstripe == i) {
-                stend = endoff + 1;
-            } else {
-                stend = endoff - (endoff % ci->stripe_length);
-            }
+        } else
+            context.va = buf;
+
+        master_mdl = IoAllocateMdl(context.va, length, FALSE, FALSE, NULL);
+        if (!master_mdl) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
+        
+        MmProbeAndLockPages(master_mdl, KernelMode, IoWriteAccess);
+        pfns = (PFN_NUMBER*)(master_mdl + 1);
+        
+        pos = 0;
+        while (pos < length) {
+            parity1 = (((addr - offset + pos) / ((ci->num_stripes - 2) * ci->stripe_length)) + ci->num_stripes - 2) % ci->num_stripes;
             
-            if (ststart != stend) {
-                if (ststart < start) {
-                    start = ststart;
-                    firststripesize = ci->stripe_length - (ststart % ci->stripe_length);
+            if (pos == 0) {
+                UINT16 stripe = (parity1 + startoffstripe + 2) % ci->num_stripes, parity2;
+                ULONG skip, readlen;
+                
+                i = startoffstripe;
+                while (stripe != parity1) {
+                    if (i == startoffstripe) {
+                        readlen = min(length, ci->stripe_length - (startoff % ci->stripe_length));
+                        
+                        context.stripes[stripe].stripestart = startoff;
+                        context.stripes[stripe].stripeend = startoff + readlen;
+                        
+                        pos += readlen;
+                        
+                        if (pos == length)
+                            break;
+                    } else {
+                        readlen = min(length - pos, ci->stripe_length);
+                        
+                        context.stripes[stripe].stripestart = startoff - (startoff % ci->stripe_length);
+                        context.stripes[stripe].stripeend = context.stripes[stripe].stripestart + readlen;
+                        
+                        pos += readlen;
+                        
+                        if (pos == length)
+                            break;
+                    }
+                    
+                    i++;
+                    stripe = (stripe + 1) % ci->num_stripes;
                 }
                 
-                if (stend > end)
-                    end = stend;
+                if (pos == length)
+                    break;
+                
+                for (i = 0; i < startoffstripe; i++) {
+                    UINT16 stripe = (parity1 + i + 2) % ci->num_stripes;
+                    
+                    context.stripes[stripe].stripestart = context.stripes[stripe].stripeend = startoff - (startoff % ci->stripe_length) + ci->stripe_length;
+                }
+                
+                context.stripes[parity1].stripestart = context.stripes[parity1].stripeend = startoff - (startoff % ci->stripe_length) + ci->stripe_length;
+                
+                parity2 = (parity1 + 1) % ci->num_stripes;
+                context.stripes[parity2].stripestart = context.stripes[parity2].stripeend = startoff - (startoff % ci->stripe_length) + ci->stripe_length;
+                
+                if (length - pos > ci->num_stripes * (ci->num_stripes - 2) * ci->stripe_length) {
+                    skip = ((length - pos) / (ci->num_stripes * (ci->num_stripes - 2) * ci->stripe_length)) - 1;
+                    
+                    for (i = 0; i < ci->num_stripes; i++) {
+                        context.stripes[i].stripeend += skip * ci->num_stripes * ci->stripe_length;
+                    }
+                    
+                    pos += skip * (ci->num_stripes - 2) * ci->num_stripes * ci->stripe_length;
+                    need_dummy = TRUE;
+                }
+            } else if (length - pos >= ci->stripe_length * (ci->num_stripes - 2)) {
+                for (i = 0; i < ci->num_stripes; i++) {
+                    context.stripes[i].stripeend += ci->stripe_length;
+                }
+                
+                pos += ci->stripe_length * (ci->num_stripes - 2);
+                need_dummy = TRUE;
+            } else {
+                UINT16 stripe = (parity1 + 2) % ci->num_stripes;
+                
+                i = 0;
+                while (stripe != parity1) {
+                    if (endoffstripe == i) {
+                        context.stripes[stripe].stripeend = endoff + 1;
+                        break;
+                    } else if (endoffstripe > i)
+                        context.stripes[stripe].stripeend = endoff - (endoff % ci->stripe_length) + ci->stripe_length;
+                    
+                    i++;
+                    stripe = (stripe + 1) % ci->num_stripes;
+                }
+                
+                break;
             }
         }
         
         for (i = 0; i < ci->num_stripes; i++) {
-            stripestart[i] = start;
-            stripeend[i] = end;
+            if (context.stripes[i].stripestart != context.stripes[i].stripeend) {
+                context.stripes[i].mdl = IoAllocateMdl(context.va, context.stripes[i].stripeend - context.stripes[i].stripestart, FALSE, FALSE, NULL);
+
+                if (!context.stripes[i].mdl) {
+                    ERR("IoAllocateMdl failed\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto exit;
+                }
+            }
         }
         
-        context->stripes_cancel = Vcb->options.raid6_recalculation;
+        if (need_dummy) {
+            dummypage = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, ALLOC_TAG);
+            if (!dummypage) {
+                ERR("out of memory\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto exit;
+            }
+            
+            dummy_mdl = IoAllocateMdl(dummypage, PAGE_SIZE, FALSE, FALSE, NULL);
+            if (!dummy_mdl) {
+                ERR("IoAllocateMdl failed\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                ExFreePool(dummypage);
+                goto exit;
+            }
+            
+            MmBuildMdlForNonPagedPool(dummy_mdl);
+            
+            dummy = *(PFN_NUMBER*)(dummy_mdl + 1);
+        }
+        
+        stripeoff = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT32) * ci->num_stripes, ALLOC_TAG);
+        if (!stripeoff) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
+
+        RtlZeroMemory(stripeoff, sizeof(UINT32) * ci->num_stripes);
+
+        pos = 0;
+        
+        while (pos < length) {
+            PFN_NUMBER* stripe_pfns;
+            
+            parity1 = (((addr - offset + pos) / ((ci->num_stripes - 2) * ci->stripe_length)) + ci->num_stripes - 2) % ci->num_stripes;
+            
+            if (pos == 0) {
+                UINT16 stripe = (parity1 + startoffstripe + 2) % ci->num_stripes;
+                UINT32 readlen = min(length - pos, min(context.stripes[stripe].stripeend - context.stripes[stripe].stripestart,
+                                                       ci->stripe_length - (context.stripes[stripe].stripestart % ci->stripe_length)));
+                
+                stripe_pfns = (PFN_NUMBER*)(context.stripes[stripe].mdl + 1);
+                
+                RtlCopyMemory(stripe_pfns, pfns, readlen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                stripeoff[stripe] = readlen;
+                pos += readlen;
+                
+                stripe = (stripe + 1) % ci->num_stripes;
+                
+                while (stripe != parity1) {
+                    stripe_pfns = (PFN_NUMBER*)(context.stripes[stripe].mdl + 1);
+                    readlen = min(length - pos, min(context.stripes[stripe].stripeend - context.stripes[stripe].stripestart, ci->stripe_length));
+                    
+                    if (readlen == 0)
+                        break;
+                    
+                    RtlCopyMemory(stripe_pfns, &pfns[pos >> PAGE_SHIFT], readlen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                    
+                    stripeoff[stripe] = readlen;
+                    pos += readlen;
+                    
+                    stripe = (stripe + 1) % ci->num_stripes;
+                }
+            } else if (length - pos >= ci->stripe_length * (ci->num_stripes - 2)) {
+                UINT16 stripe = (parity1 + 2) % ci->num_stripes;
+                UINT16 parity2 = (parity1 + 1) % ci->num_stripes;
+                
+                while (stripe != parity1) {
+                    stripe_pfns = (PFN_NUMBER*)(context.stripes[stripe].mdl + 1);
+                    
+                    RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], ci->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                    
+                    stripeoff[stripe] += ci->stripe_length;
+                    pos += ci->stripe_length;
+                    
+                    stripe = (stripe + 1) % ci->num_stripes;
+                }
+                
+                stripe_pfns = (PFN_NUMBER*)(context.stripes[parity1].mdl + 1);
+                
+                for (i = 0; i < ci->stripe_length >> PAGE_SHIFT; i++) {
+                    stripe_pfns[stripeoff[parity1] >> PAGE_SHIFT] = dummy;
+                    stripeoff[parity1] += PAGE_SIZE;
+                }
+                
+                stripe_pfns = (PFN_NUMBER*)(context.stripes[parity2].mdl + 1);
+                
+                for (i = 0; i < ci->stripe_length >> PAGE_SHIFT; i++) {
+                    stripe_pfns[stripeoff[parity2] >> PAGE_SHIFT] = dummy;
+                    stripeoff[parity2] += PAGE_SIZE;
+                }
+            } else {
+                UINT16 stripe = (parity1 + 2) % ci->num_stripes;
+                UINT32 readlen;
+                
+                while (pos < length) {
+                    stripe_pfns = (PFN_NUMBER*)(context.stripes[stripe].mdl + 1);
+                    readlen = min(length - pos, min(context.stripes[stripe].stripeend - context.stripes[stripe].stripestart, ci->stripe_length));
+                    
+                    if (readlen == 0)
+                        break;
+                    
+                    RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], readlen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                    
+                    stripeoff[stripe] += readlen;
+                    pos += readlen;
+                    
+                    stripe = (stripe + 1) % ci->num_stripes;
+                }
+            }
+        }
+        
+        MmUnlockPages(master_mdl);
+        IoFreeMdl(master_mdl);
+
+        ExFreePool(stripeoff);
     }
     
-    KeInitializeSpinLock(&context->spin_lock);
+    context.address = addr;
     
-    context->address = addr;
-    
     for (i = 0; i < ci->num_stripes; i++) {
-        if (!devices[i] || stripestart[i] == stripeend[i]) {
-            context->stripes[i].status = ReadDataStatus_MissingDevice;
-            context->stripes[i].buf = NULL;
-            context->stripes_left--;
+        if (!devices[i] || context.stripes[i].stripestart == context.stripes[i].stripeend) {
+            context.stripes[i].status = ReadDataStatus_MissingDevice;
+            context.stripes_left--;
             
             if (!devices[i])
                 missing_devices++;
         }
     }
-      
+
     if (missing_devices > allowed_missing) {
         ERR("not enough devices to service request (%u missing)\n", missing_devices);
         Status = STATUS_UNEXPECTED_IO_ERROR;
@@ -2790,32 +2248,25 @@
     for (i = 0; i < ci->num_stripes; i++) {
         PIO_STACK_LOCATION IrpSp;
         
-        if (devices[i] && stripestart[i] != stripeend[i]) {
-            context->stripes[i].context = (struct read_data_context*)context;
-            context->stripes[i].buf = ExAllocatePoolWithTag(NonPagedPool, stripeend[i] - stripestart[i], ALLOC_TAG);
-            
-            if (!context->stripes[i].buf) {
-                ERR("out of memory\n");
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto exit;
-            }
-            
+        if (devices[i] && context.stripes[i].stripestart != context.stripes[i].stripeend && context.stripes[i].status != ReadDataStatus_Skip) {
+            context.stripes[i].context = (struct read_data_context*)&context;
+
             if (type == BLOCK_FLAG_RAID10) {
-                context->stripes[i].stripenum = i / ci->sub_stripes;
+                context.stripes[i].stripenum = i / ci->sub_stripes;
             }
 
             if (!Irp) {
-                context->stripes[i].Irp = IoAllocateIrp(devices[i]->devobj->StackSize, FALSE);
+                context.stripes[i].Irp = IoAllocateIrp(devices[i]->devobj->StackSize, FALSE);
                 
-                if (!context->stripes[i].Irp) {
+                if (!context.stripes[i].Irp) {
                     ERR("IoAllocateIrp failed\n");
                     Status = STATUS_INSUFFICIENT_RESOURCES;
                     goto exit;
                 }
             } else {
-                context->stripes[i].Irp = IoMakeAssociatedIrp(Irp, devices[i]->devobj->StackSize);
+                context.stripes[i].Irp = IoMakeAssociatedIrp(Irp, devices[i]->devobj->StackSize);
                 
-                if (!context->stripes[i].Irp) {
+                if (!context.stripes[i].Irp) {
                     ERR("IoMakeAssociatedIrp failed\n");
                     Status = STATUS_INSUFFICIENT_RESOURCES;
                     goto exit;
@@ -2822,32 +2273,34 @@
                 }
             }
             
-            IrpSp = IoGetNextIrpStackLocation(context->stripes[i].Irp);
+            IrpSp = IoGetNextIrpStackLocation(context.stripes[i].Irp);
             IrpSp->MajorFunction = IRP_MJ_READ;
             
             if (devices[i]->devobj->Flags & DO_BUFFERED_IO) {
-                FIXME("FIXME - buffered IO\n");
-            } else if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
-                context->stripes[i].Irp->MdlAddress = IoAllocateMdl(context->stripes[i].buf, stripeend[i] - stripestart[i], FALSE, FALSE, NULL);
-                if (!context->stripes[i].Irp->MdlAddress) {
-                    ERR("IoAllocateMdl failed\n");
-                    Status = STATUS_INSUFFICIENT_RESOURCES;
-                    goto exit;
+                context.stripes[i].Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, context.stripes[i].stripeend - context.stripes[i].stripestart, ALLOC_TAG);
+                if (!context.stripes[i].Irp->AssociatedIrp.SystemBuffer) {
+                    ERR("out of memory\n");
+                    return STATUS_INSUFFICIENT_RESOURCES;
                 }
-                
-                MmProbeAndLockPages(context->stripes[i].Irp->MdlAddress, KernelMode, IoWriteAccess);
-            } else {
-                context->stripes[i].Irp->UserBuffer = context->stripes[i].buf;
-            }
 
-            IrpSp->Parameters.Read.Length = stripeend[i] - stripestart[i];
-            IrpSp->Parameters.Read.ByteOffset.QuadPart = stripestart[i] + cis[i].offset;
+                context.stripes[i].Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
+               
+                context.stripes[i].Irp->UserBuffer = MmGetSystemAddressForMdlSafe(context.stripes[i].mdl, NormalPagePriority);
+            } else if (devices[i]->devobj->Flags & DO_DIRECT_IO)
+                context.stripes[i].Irp->MdlAddress = context.stripes[i].mdl;
+            else
+                context.stripes[i].Irp->UserBuffer = MmGetSystemAddressForMdlSafe(context.stripes[i].mdl, NormalPagePriority);
+
+            IrpSp->Parameters.Read.Length = context.stripes[i].stripeend - context.stripes[i].stripestart;
+            IrpSp->Parameters.Read.ByteOffset.QuadPart = context.stripes[i].stripestart + cis[i].offset;
             
-            context->stripes[i].Irp->UserIosb = &context->stripes[i].iosb;
+            total_reading += IrpSp->Parameters.Read.Length;
             
-            IoSetCompletionRoutine(context->stripes[i].Irp, read_data_completion, &context->stripes[i], TRUE, TRUE, TRUE);
+            context.stripes[i].Irp->UserIosb = &context.stripes[i].iosb;
+            
+            IoSetCompletionRoutine(context.stripes[i].Irp, read_data_completion, &context.stripes[i], TRUE, TRUE, TRUE);
 
-            context->stripes[i].status = ReadDataStatus_Pending;
+            context.stripes[i].status = ReadDataStatus_Pending;
         }
     }
     
@@ -2857,12 +2310,12 @@
 #endif
     
     for (i = 0; i < ci->num_stripes; i++) {
-        if (context->stripes[i].status != ReadDataStatus_MissingDevice) {
-            IoCallDriver(devices[i]->devobj, context->stripes[i].Irp);
+        if (context.stripes[i].status != ReadDataStatus_MissingDevice && context.stripes[i].status != ReadDataStatus_Skip) {
+            IoCallDriver(devices[i]->devobj, context.stripes[i].Irp);
         }
     }
 
-    KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
+    KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
    
 #ifdef DEBUG_STATS
     if (!is_tree) {
@@ -2871,12 +2324,15 @@
         Vcb->stats.read_disk_time += time2.QuadPart - time1.QuadPart;
     }
 #endif
+
+    if (diskacc)
+        FsRtlUpdateDiskCounters(total_reading, 0);
     
     // check if any of the devices return a "user-induced" error
     
     for (i = 0; i < ci->num_stripes; i++) {
-        if (context->stripes[i].status == ReadDataStatus_Error && IoIsErrorUserInduced(context->stripes[i].iosb.Status)) {
-            if (Irp && context->stripes[i].iosb.Status == STATUS_VERIFY_REQUIRED) {
+        if (context.stripes[i].status == ReadDataStatus_Error && IoIsErrorUserInduced(context.stripes[i].iosb.Status)) {
+            if (Irp && context.stripes[i].iosb.Status == STATUS_VERIFY_REQUIRED) {
                 PDEVICE_OBJECT dev;
                 
                 dev = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
@@ -2892,64 +2348,105 @@
                 if (dev)
                     IoVerifyVolume(dev, FALSE);
             }
-//             IoSetHardErrorOrVerifyDevice(context->stripes[i].Irp, devices[i]->devobj);
+//             IoSetHardErrorOrVerifyDevice(context.stripes[i].Irp, devices[i]->devobj);
             
-            Status = context->stripes[i].iosb.Status;
+            Status = context.stripes[i].iosb.Status;
             goto exit;
         }
     }
     
     if (type == BLOCK_FLAG_RAID0) {
-        Status = read_data_raid0(Vcb, buf, addr, length, context, ci, stripestart, stripeend, startoffstripe);
+        Status = read_data_raid0(Vcb, file_read ? context.va : buf, addr, length, &context, ci, devices, generation, offset);
         if (!NT_SUCCESS(Status)) {
             ERR("read_data_raid0 returned %08x\n", Status);
+            
+            if (file_read)
+                ExFreePool(context.va);
+            
             goto exit;
         }
+        
+        if (file_read) {
+            RtlCopyMemory(buf, context.va, length);
+            ExFreePool(context.va);
+        }
     } else if (type == BLOCK_FLAG_RAID10) {
-        Status = read_data_raid10(Vcb, buf, addr, length, Irp, context, ci, devices, stripestart, stripeend, startoffstripe);
+        Status = read_data_raid10(Vcb, file_read ? context.va : buf, addr, length, &context, ci, devices, generation, offset);
+        
         if (!NT_SUCCESS(Status)) {
             ERR("read_data_raid10 returned %08x\n", Status);
+            
+            if (file_read)
+                ExFreePool(context.va);
+            
             goto exit;
         }
+        
+        if (file_read) {
+            RtlCopyMemory(buf, context.va, length);
+            ExFreePool(context.va);
+        }
     } else if (type == BLOCK_FLAG_DUPLICATE) {
-        Status = read_data_dup(Vcb, buf, addr, length, Irp, context, ci, devices, stripestart, stripeend);
+        Status = read_data_dup(Vcb, addr, &context, ci, devices, generation);
         if (!NT_SUCCESS(Status)) {
             ERR("read_data_dup returned %08x\n", Status);
             goto exit;
         }
     } else if (type == BLOCK_FLAG_RAID5) {
-        Status = read_data_raid5(Vcb, buf, addr, length, Irp, context, ci, devices, stripestart, stripeend, offset, firststripesize, check_nocsum_parity);
+        Status = read_data_raid5(Vcb, file_read ? context.va : buf, addr, length, &context, ci, devices, offset, generation, c);
         if (!NT_SUCCESS(Status)) {
             ERR("read_data_raid5 returned %08x\n", Status);
+            
+            if (file_read)
+                ExFreePool(context.va);
+            
             goto exit;
         }
+        
+        if (file_read) {
+            RtlCopyMemory(buf, context.va, length);
+            ExFreePool(context.va);
+        }
     } else if (type == BLOCK_FLAG_RAID6) {
-        Status = read_data_raid6(Vcb, buf, addr, length, Irp, context, ci, devices, stripestart, stripeend, offset, firststripesize, check_nocsum_parity);
+        Status = read_data_raid6(Vcb, file_read ? context.va : buf, addr, length, &context, ci, devices, offset, generation, c);
         if (!NT_SUCCESS(Status)) {
             ERR("read_data_raid6 returned %08x\n", Status);
+            
+            if (file_read)
+                ExFreePool(context.va);
+            
             goto exit;
         }
+
+        if (file_read) {
+            RtlCopyMemory(buf, context.va, length);
+            ExFreePool(context.va);
+        }
     }
 
 exit:
-    if (stripestart) ExFreePool(stripestart);
-    if (stripeend) ExFreePool(stripeend);
+    if (dummy_mdl)
+        IoFreeMdl(dummy_mdl);
+    
+    if (dummypage)
+        ExFreePool(dummypage);
 
     for (i = 0; i < ci->num_stripes; i++) {
-        if (context->stripes[i].Irp) {
-            if (devices[i]->devobj->Flags & DO_DIRECT_IO) {
-                MmUnlockPages(context->stripes[i].Irp->MdlAddress);
-                IoFreeMdl(context->stripes[i].Irp->MdlAddress);
-            }
-            IoFreeIrp(context->stripes[i].Irp);
+        if (context.stripes[i].mdl) {
+            if (context.stripes[i].mdl->MdlFlags & MDL_PAGES_LOCKED)
+                MmUnlockPages(context.stripes[i].mdl);
+
+            IoFreeMdl(context.stripes[i].mdl);
         }
         
-        if (context->stripes[i].buf)
-            ExFreePool(context->stripes[i].buf);
+        if (context.stripes[i].Irp)
+            IoFreeIrp(context.stripes[i].Irp);
+        
+        if (context.stripes[i].buf && !context.stripes[i].not_alloc)
+            ExFreePool(context.stripes[i].buf);
     }
 
-    ExFreePool(context->stripes);
-    ExFreePool(context);
+    ExFreePool(context.stripes);
     
     if (!Vcb->log_to_phys_loaded)
         ExFreePool(devices);
@@ -2990,7 +2487,7 @@
     return Status;
 }
 
-NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr, PIRP Irp, BOOL check_nocsum_parity) {
+NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr, PIRP Irp) {
     NTSTATUS Status;
     EXTENT_DATA* ed;
     UINT64 bytes_read = 0;
@@ -3025,7 +2522,7 @@
         EXTENT_DATA2* ed2;
         
         if (!ext->ignore) {
-            ed = ext->data;
+            ed = &ext->extent_data;
             
             ed2 = (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ed->data : NULL;
             
@@ -3081,9 +2578,11 @@
                     UINT64 off = start + bytes_read - ext->offset;
                     UINT32 to_read, read;
                     UINT8* buf;
+                    BOOL mdl = (Irp && Irp->MdlAddress) ? TRUE : FALSE;
                     BOOL buf_free;
-                    UINT32 bumpoff = 0;
-                    UINT64 addr, lockaddr, locklen;
+                    UINT32 bumpoff = 0, *csum;
+                    UINT64 addr;
+//                     UINT64 lockaddr, locklen;
                     chunk* c;
                     
                     read = len - off;
@@ -3116,6 +2615,8 @@
                             Status = STATUS_INSUFFICIENT_RESOURCES;
                             goto exit;
                         }
+                        
+                        mdl = FALSE;
                     }
                     
                     c = get_chunk_from_address(fcb->Vcb, addr);
@@ -3129,18 +2630,25 @@
                         goto exit;
                     }
                     
-                    if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6) {
-                        get_raid56_lock_range(c, addr, to_read, &lockaddr, &locklen);
-                        chunk_lock_range(fcb->Vcb, c, lockaddr, locklen);
-                    }
+//                     if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6) {
+//                         get_raid56_lock_range(c, addr, to_read, &lockaddr, &locklen);
+//                         chunk_lock_range(fcb->Vcb, c, lockaddr, locklen);
+//                     }
                     
-                    Status = read_data(fcb->Vcb, addr, to_read, ext->csum ? &ext->csum[off / fcb->Vcb->superblock.sector_size] : NULL, FALSE,
-                                       buf, c, NULL, Irp, check_nocsum_parity);
+                    if (ext->csum) {
+                        if (ed->compression == BTRFS_COMPRESSION_NONE)
+                            csum = &ext->csum[off / fcb->Vcb->superblock.sector_size];
+                        else
+                            csum = ext->csum;
+                    } else
+                        csum = NULL;
+                    
+                    Status = read_data(fcb->Vcb, addr, to_read, csum, FALSE, buf, c, NULL, Irp, 0, mdl, bytes_read);
                     if (!NT_SUCCESS(Status)) {
                         ERR("read_data returned %08x\n", Status);
                         
-                        if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6)
-                            chunk_unlock_range(fcb->Vcb, c, lockaddr, locklen);
+//                         if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6)
+//                             chunk_unlock_range(fcb->Vcb, c, lockaddr, locklen);
                         
                         if (buf_free)
                             ExFreePool(buf);
@@ -3148,37 +2656,103 @@
                         goto exit;
                     }
                     
-                    if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6)
-                        chunk_unlock_range(fcb->Vcb, c, lockaddr, locklen);
+//                     if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6)
+//                         chunk_unlock_range(fcb->Vcb, c, lockaddr, locklen);
                     
                     if (ed->compression == BTRFS_COMPRESSION_NONE) {
                         if (buf_free)
                             RtlCopyMemory(data + bytes_read, buf + bumpoff, read);
                     } else {
-                        UINT8* decomp = NULL;
+                        UINT8 *decomp = NULL, *buf2;
+                        ULONG outlen, inlen, off2;
+                        UINT32 inpageoff = 0;
                         
-                        // FIXME - don't mess around with decomp if we're reading the whole extent
+                        off2 = ed2->offset + off;
+                        buf2 = buf;
+                        inlen = ed2->size;
                         
-                        decomp = ExAllocatePoolWithTag(PagedPool, ed->decoded_size, ALLOC_TAG);
-                        if (!decomp) {
-                            ERR("out of memory\n");
-                            ExFreePool(buf);
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            goto exit;
+                        if (ed->compression == BTRFS_COMPRESSION_LZO) {
+                            ULONG inoff = sizeof(UINT32);
+                            
+                            inlen -= sizeof(UINT32);
+                            
+                            // If reading a few sectors in, skip to the interesting bit
+                            while (off2 > LINUX_PAGE_SIZE) {
+                                UINT32 partlen;
+                                
+                                if (inlen < sizeof(UINT32))
+                                    break;
+                                
+                                partlen = *(UINT32*)(buf2 + inoff);
+                                
+                                if (partlen < inlen) {
+                                    off2 -= LINUX_PAGE_SIZE;
+                                    inoff += partlen + sizeof(UINT32);
+                                    inlen -= partlen + sizeof(UINT32);
+                                    
+                                    if (LINUX_PAGE_SIZE - (inoff % LINUX_PAGE_SIZE) < sizeof(UINT32))
+                                        inoff = ((inoff / LINUX_PAGE_SIZE) + 1) * LINUX_PAGE_SIZE;
+                                } else
+                                    break;
+                            }
+                            
+                            buf2 = &buf2[inoff];
+                            inpageoff = inoff % LINUX_PAGE_SIZE;
                         }
                         
-                        Status = decompress(ed->compression, buf, ed2->size, decomp, ed->decoded_size);
+                        if (off2 != 0) {
+                            outlen = off2 + min(read, ed2->num_bytes - off);
+                            
+                            decomp = ExAllocatePoolWithTag(PagedPool, outlen, ALLOC_TAG);
+                            if (!decomp) {
+                                ERR("out of memory\n");
+                                ExFreePool(buf);
+                                Status = STATUS_INSUFFICIENT_RESOURCES;
+                                goto exit;
+                            }
+                        } else
+                            outlen = min(read, ed2->num_bytes - off);
                         
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("decompress returned %08x\n", Status);
+                        if (ed->compression == BTRFS_COMPRESSION_ZLIB) {
+                            Status = zlib_decompress(buf2, inlen, decomp ? decomp : (data + bytes_read), outlen);
+                            
+                            if (!NT_SUCCESS(Status)) {
+                                ERR("zlib_decompress returned %08x\n", Status);
+                                ExFreePool(buf);
+                                
+                                if (decomp)
+                                    ExFreePool(decomp);
+                                
+                                goto exit;
+                            }
+                        } else if (ed->compression == BTRFS_COMPRESSION_LZO) {
+                            Status = lzo_decompress(buf2, inlen, decomp ? decomp : (data + bytes_read), outlen, inpageoff);
+                        
+                            if (!NT_SUCCESS(Status)) {
+                                ERR("lzo_decompress returned %08x\n", Status);
+                                ExFreePool(buf);
+                                
+                                if (decomp)
+                                    ExFreePool(decomp);
+                                
+                                goto exit;
+                            }
+                        } else {
+                            ERR("unsupported compression type %x\n", ed->compression);
+                            Status = STATUS_NOT_SUPPORTED;
+                            
                             ExFreePool(buf);
-                            ExFreePool(decomp);
+                            
+                            if (decomp)
+                                ExFreePool(decomp);
+                            
                             goto exit;
                         }
                         
-                        RtlCopyMemory(data + bytes_read, decomp + ed2->offset + off, min(read, ed2->num_bytes - off));
-                        
-                        ExFreePool(decomp);
+                        if (decomp) {
+                            RtlCopyMemory(data + bytes_read, decomp + off2, min(read, ed2->num_bytes - off));
+                            ExFreePool(decomp);
+                        }
                     }
                     
                     if (buf_free)
@@ -3380,7 +2954,7 @@
         if (fcb->ads)
             Status = read_stream(fcb, data, start, length, bytes_read);
         else
-            Status = read_file(fcb, data, start, length, bytes_read, Irp, TRUE);
+            Status = read_file(fcb, data, start, length, bytes_read, Irp);
         
         *bytes_read += addon;
         TRACE("read %u bytes\n", *bytes_read);
@@ -3422,9 +2996,12 @@
     
     TRACE("read\n");
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_read(DeviceObject, Irp);
         goto exit2;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
     
     Irp->IoStatus.Information = 0;
@@ -3493,8 +3070,6 @@
 exit:
     if (fcb_lock)
         ExReleaseResourceLite(fcb->Header.Resource);
-
-    Irp->IoStatus.Status = Status;
     
     if (FileObject->Flags & FO_SYNCHRONOUS_IO && !(Irp->Flags & IRP_PAGING_IO))
         FileObject->CurrentByteOffset.QuadPart = IrpSp->Parameters.Read.ByteOffset.QuadPart + (NT_SUCCESS(Status) ? bytes_read : 0);
@@ -3503,6 +3078,9 @@
     if (Irp->UserIosb)
         *Irp->UserIosb = Irp->IoStatus;
     
+end:
+    Irp->IoStatus.Status = Status;
+    
     TRACE("Irp->IoStatus.Status = %08x\n", Irp->IoStatus.Status);
     TRACE("Irp->IoStatus.Information = %lu\n", Irp->IoStatus.Information);
     TRACE("returning %08x\n", Status);
Index: drivers/filesystems/btrfs/registry.c
===================================================================
--- drivers/filesystems/btrfs/registry.c	(révision 74430)
+++ drivers/filesystems/btrfs/registry.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -27,7 +27,7 @@
     BTRFS_UUID* uuid = &Vcb->superblock.uuid;
     mount_options* options = &Vcb->options;
     UNICODE_STRING path, ignoreus, compressus, compressforceus, compresstypeus, readonlyus, zliblevelus, flushintervalus,
-                   maxinlineus, subvolidus, raid5recalcus, raid6recalcus, skipbalanceus;
+                   maxinlineus, subvolidus, skipbalanceus, nobarrierus, notrimus, clearcacheus;
     OBJECT_ATTRIBUTES oa;
     NTSTATUS Status;
     ULONG i, j, kvfilen, index, retlen;
@@ -41,9 +41,10 @@
     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->raid5_recalculation = mount_raid5_recalculation;
-    options->raid6_recalculation = mount_raid6_recalculation;
     options->skip_balance = mount_skip_balance;
+    options->no_barrier = mount_no_barrier;
+    options->no_trim = mount_no_trim;
+    options->clear_cache = mount_clear_cache;
     options->subvol_id = 0;
     
     path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
@@ -102,9 +103,10 @@
     RtlInitUnicodeString(&flushintervalus, L"FlushInterval");
     RtlInitUnicodeString(&maxinlineus, L"MaxInline");
     RtlInitUnicodeString(&subvolidus, L"SubvolId");
-    RtlInitUnicodeString(&raid5recalcus, L"Raid5Recalculation");
-    RtlInitUnicodeString(&raid6recalcus, L"Raid6Recalculation");
     RtlInitUnicodeString(&skipbalanceus, L"SkipBalance");
+    RtlInitUnicodeString(&nobarrierus, L"NoBarrier");
+    RtlInitUnicodeString(&notrimus, L"NoTrim");
+    RtlInitUnicodeString(&clearcacheus, L"ClearCache");
     
     do {
         Status = ZwEnumerateValueKey(h, index, KeyValueFullInformation, kvfi, kvfilen, &retlen);
@@ -153,18 +155,22 @@
                 UINT64* val = (UINT64*)((UINT8*)kvfi + kvfi->DataOffset);
                 
                 options->subvol_id = *val;
-            } else if (FsRtlAreNamesEqual(&raid5recalcus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+            } else if (FsRtlAreNamesEqual(&skipbalanceus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
                 DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
                 
-                options->raid5_recalculation = *val;
-            } else if (FsRtlAreNamesEqual(&raid6recalcus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+                options->skip_balance = *val;
+            } else if (FsRtlAreNamesEqual(&nobarrierus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
                 DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
                 
-                options->raid6_recalculation = *val;
-            } else if (FsRtlAreNamesEqual(&skipbalanceus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+                options->no_barrier = *val;
+            } else if (FsRtlAreNamesEqual(&notrimus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
                 DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
                 
-                options->skip_balance = *val;
+                options->no_trim = *val;
+            } else if (FsRtlAreNamesEqual(&clearcacheus, &us, TRUE, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
+                DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+                
+                options->clear_cache = *val;
             }
         } else if (Status != STATUS_NO_MORE_ENTRIES) {
             ERR("ZwEnumerateValueKey returned %08x\n", Status);
@@ -181,12 +187,6 @@
     if (options->flush_interval == 0)
         options->flush_interval = mount_flush_interval;
     
-    if (options->raid5_recalculation > 1)
-        options->raid5_recalculation = 1;
-    
-    if (options->raid6_recalculation > 2)
-        options->raid6_recalculation = 2;
-
     Status = STATUS_SUCCESS;
     
 end2:
@@ -636,7 +636,7 @@
     KEY_VALUE_FULL_INFORMATION* kvfi;
 #endif
     
-#ifndef __REACTOS__    
+#ifndef __REACTOS__
     static WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log";
 #endif
     
@@ -659,19 +659,14 @@
     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));
-    get_registry_value(h, L"Raid5Recalculation", REG_DWORD, &mount_raid5_recalculation, sizeof(mount_raid5_recalculation));
-    get_registry_value(h, L"Raid6Recalculation", REG_DWORD, &mount_raid6_recalculation, sizeof(mount_raid6_recalculation));
     get_registry_value(h, L"SkipBalance", REG_DWORD, &mount_skip_balance, sizeof(mount_skip_balance));
+    get_registry_value(h, L"NoBarrier", REG_DWORD, &mount_no_barrier, sizeof(mount_no_barrier));
+    get_registry_value(h, L"NoTrim", REG_DWORD, &mount_no_trim, sizeof(mount_no_trim));
+    get_registry_value(h, L"ClearCache", REG_DWORD, &mount_clear_cache, sizeof(mount_clear_cache));
     
     if (mount_flush_interval == 0)
         mount_flush_interval = 1;
     
-    if (mount_raid5_recalculation > 1)
-        mount_raid5_recalculation = 1;
-    
-    if (mount_raid6_recalculation > 2)
-        mount_raid6_recalculation = 2;
-    
 #ifdef _DEBUG
     get_registry_value(h, L"DebugLogLevel", REG_DWORD, &debug_log_level, sizeof(debug_log_level));
     
Index: drivers/filesystems/btrfs/reparse.c
===================================================================
--- drivers/filesystems/btrfs/reparse.c	(révision 74430)
+++ drivers/filesystems/btrfs/reparse.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -56,7 +56,7 @@
             }
             
             TRACE("data = %p, size = %x\n", data, fcb->inode_item.st_size);
-            Status = read_file(fcb, (UINT8*)data, 0, fcb->inode_item.st_size, NULL, NULL, TRUE);
+            Status = read_file(fcb, (UINT8*)data, 0, fcb->inode_item.st_size, NULL, NULL);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("read_file returned %08x\n", Status);
@@ -119,7 +119,7 @@
         if (fcb->type == BTRFS_TYPE_FILE) {
             ULONG len;
             
-            Status = read_file(fcb, buffer, 0, buflen, &len, NULL, TRUE);
+            Status = read_file(fcb, buffer, 0, buflen, &len, NULL);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("read_file returned %08x\n", Status);
@@ -215,7 +215,7 @@
         offset.QuadPart = 0;
         tlength = target.Length;
         Status = write_file2(fileref->fcb->Vcb, Irp, offset, target.Buffer, &tlength, FALSE, TRUE,
-                            TRUE, FALSE, rollback);
+                             TRUE, FALSE, FALSE, rollback);
         ExFreePool(target.Buffer);
     } else
         Status = STATUS_SUCCESS;
@@ -348,7 +348,7 @@
             
             offset.QuadPart = 0;
             
-            Status = write_file2(fcb->Vcb, Irp, offset, buffer, &buflen, FALSE, TRUE, TRUE, FALSE, &rollback);
+            Status = write_file2(fcb->Vcb, Irp, offset, buffer, &buflen, FALSE, TRUE, TRUE, FALSE, FALSE, &rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("write_file2 returned %08x\n", Status);
                 goto end;
@@ -381,7 +381,7 @@
     
 end:
     if (NT_SUCCESS(Status))
-        clear_rollback(fcb->Vcb, &rollback);
+        clear_rollback(&rollback);
     else
         do_rollback(fcb->Vcb, &rollback);
 
@@ -575,7 +575,7 @@
     
 end:
     if (NT_SUCCESS(Status))
-        clear_rollback(fcb->Vcb, &rollback);
+        clear_rollback(&rollback);
     else
         do_rollback(fcb->Vcb, &rollback);
     
Index: drivers/filesystems/btrfs/scrub.c
===================================================================
--- drivers/filesystems/btrfs/scrub.c	(nonexistent)
+++ drivers/filesystems/btrfs/scrub.c	(copie de travail)
@@ -0,0 +1,3318 @@
+/* Copyright (c) Mark Harmstone 2017
+ * 
+ * This file is part of WinBtrfs.
+ * 
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ * 
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public Licence for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include "btrfs_drv.h"
+
+#define SCRUB_UNIT 0x100000 // 1 MB
+
+struct _scrub_context;
+
+typedef struct {
+    struct _scrub_context* context;
+    PIRP Irp;
+    UINT64 start;
+    UINT64 length;
+    IO_STATUS_BLOCK iosb;
+    UINT8* buf;
+    BOOL csum_error;
+    UINT32* bad_csums;
+} scrub_context_stripe;
+
+typedef struct _scrub_context {
+    KEVENT Event;
+    scrub_context_stripe* stripes;
+    LONG stripes_left;
+} scrub_context;
+
+typedef struct {
+    ANSI_STRING name;
+    BOOL orig_subvol;
+    LIST_ENTRY list_entry;
+} path_part;
+
+static void log_file_checksum_error(device_extension* Vcb, UINT64 addr, UINT64 devid, UINT64 subvol, UINT64 inode, UINT64 offset) {
+    LIST_ENTRY *le, parts;
+    root* r = NULL;
+    KEY searchkey;
+    traverse_ptr tp;
+    UINT64 dir;
+    BOOL orig_subvol = TRUE, not_in_tree = FALSE;
+    ANSI_STRING fn;
+    scrub_error* err;
+    NTSTATUS Status;
+    ULONG utf16len;
+    
+    le = Vcb->roots.Flink;
+    while (le != &Vcb->roots) {
+        root* r2 = CONTAINING_RECORD(le, root, list_entry);
+        
+        if (r2->id == subvol) {
+            r = r2;
+            break;
+        }
+        
+        le = le->Flink;
+    }
+    
+    if (!r) {
+        ERR("could not find subvol %llx\n", subvol);
+        return;
+    }
+    
+    InitializeListHead(&parts);
+    
+    dir = inode;
+    
+    while (TRUE) {
+        NTSTATUS Status;
+        
+        if (dir == r->root_item.objid) {
+            if (r == Vcb->root_fileref->fcb->subvol)
+                break;
+            
+            searchkey.obj_id = r->id;
+            searchkey.obj_type = TYPE_ROOT_BACKREF;
+            searchkey.offset = 0xffffffffffffffff;
+            
+            Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, NULL);
+            if (!NT_SUCCESS(Status)) {
+                ERR("find_item returned %08x\n", Status);
+                goto end;
+            }
+            
+            if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+                ROOT_REF* rr = (ROOT_REF*)tp.item->data;
+                path_part* pp;
+                
+                if (tp.item->size < sizeof(ROOT_REF)) {
+                    ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF));
+                    goto end;
+                }
+                
+                if (tp.item->size < offsetof(ROOT_REF, name[0]) + rr->n) {
+                    ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+                        tp.item->size, offsetof(ROOT_REF, name[0]) + rr->n);
+                    goto end;
+                }
+                
+                pp = ExAllocatePoolWithTag(PagedPool, sizeof(path_part), ALLOC_TAG);
+                if (!pp) {
+                    ERR("out of memory\n");
+                    goto end;
+                }
+                
+                pp->name.Buffer = rr->name;
+                pp->name.Length = pp->name.MaximumLength = rr->n;
+                pp->orig_subvol = FALSE;
+                
+                InsertTailList(&parts, &pp->list_entry);
+                
+                r = NULL;
+                
+                le = Vcb->roots.Flink;
+                while (le != &Vcb->roots) {
+                    root* r2 = CONTAINING_RECORD(le, root, list_entry);
+                    
+                    if (r2->id == tp.item->key.offset) {
+                        r = r2;
+                        break;
+                    }
+                    
+                    le = le->Flink;
+                }
+                
+                if (!r) {
+                    ERR("could not find subvol %llx\n", tp.item->key.offset);
+                    goto end;
+                }
+                
+                dir = rr->dir;
+                orig_subvol = FALSE;
+            } else {
+                not_in_tree = TRUE;
+                break;
+            }
+        } else {
+            searchkey.obj_id = dir;
+            searchkey.obj_type = TYPE_INODE_EXTREF;
+            searchkey.offset = 0xffffffffffffffff;
+            
+            Status = find_item(Vcb, r, &tp, &searchkey, FALSE, NULL);
+            if (!NT_SUCCESS(Status)) {
+                ERR("find_item returned %08x\n", Status);
+                goto end;
+            }
+            
+            if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_INODE_REF) {
+                INODE_REF* ir = (INODE_REF*)tp.item->data;
+                path_part* pp;
+                
+                if (tp.item->size < sizeof(INODE_REF)) {
+                    ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_REF));
+                    goto end;
+                }
+                
+                if (tp.item->size < offsetof(INODE_REF, name[0]) + ir->n) {
+                    ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+                        tp.item->size, offsetof(INODE_REF, name[0]) + ir->n);
+                    goto end;
+                }
+                
+                pp = ExAllocatePoolWithTag(PagedPool, sizeof(path_part), ALLOC_TAG);
+                if (!pp) {
+                    ERR("out of memory\n");
+                    goto end;
+                }
+                
+                pp->name.Buffer = ir->name;
+                pp->name.Length = pp->name.MaximumLength = ir->n;
+                pp->orig_subvol = orig_subvol;
+                
+                InsertTailList(&parts, &pp->list_entry);
+                
+                if (dir == tp.item->key.offset)
+                    break;
+                
+                dir = tp.item->key.offset;
+            } else if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_INODE_EXTREF) {
+                INODE_EXTREF* ier = (INODE_EXTREF*)tp.item->data;
+                path_part* pp;
+
+                if (tp.item->size < sizeof(INODE_EXTREF)) {
+                    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(INODE_EXTREF));
+                    goto end;
+                }
+                
+                if (tp.item->size < offsetof(INODE_EXTREF, name[0]) + ier->n) {
+                    ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+                        tp.item->size, offsetof(INODE_EXTREF, name[0]) + ier->n);
+                    goto end;
+                }
+                
+                pp = ExAllocatePoolWithTag(PagedPool, sizeof(path_part), ALLOC_TAG);
+                if (!pp) {
+                    ERR("out of memory\n");
+                    goto end;
+                }
+                
+                pp->name.Buffer = ier->name;
+                pp->name.Length = pp->name.MaximumLength = ier->n;
+                pp->orig_subvol = orig_subvol;
+                
+                InsertTailList(&parts, &pp->list_entry);
+                
+                if (dir == ier->dir)
+                    break;
+                
+                dir = ier->dir;
+            } else {
+                ERR("could not find INODE_REF for inode %llx in subvol %llx\n", dir, r->id);
+                goto end;
+            }
+        }
+    }
+    
+    fn.MaximumLength = 0;
+    
+    if (not_in_tree) {
+        le = parts.Blink;
+        while (le != &parts) {
+            path_part* pp = CONTAINING_RECORD(le, path_part, list_entry);
+            LIST_ENTRY* le2 = le->Blink;
+            
+            if (pp->orig_subvol)
+                break;
+            
+            RemoveTailList(&parts);
+            ExFreePool(pp);
+            
+            le = le2;
+        }
+    }
+    
+    le = parts.Flink;
+    while (le != &parts) {
+        path_part* pp = CONTAINING_RECORD(le, path_part, list_entry);
+        
+        fn.MaximumLength += pp->name.Length + 1;
+            
+        le = le->Flink;
+    }
+    
+    fn.Buffer = ExAllocatePoolWithTag(PagedPool, fn.MaximumLength, ALLOC_TAG);
+    if (!fn.Buffer) {
+        ERR("out of memory\n");
+        goto end;
+    }
+    
+    fn.Length = 0;
+    
+    le = parts.Blink;
+    while (le != &parts) {
+        path_part* pp = CONTAINING_RECORD(le, path_part, list_entry);
+        
+        fn.Buffer[fn.Length] = '\\';
+        fn.Length++;
+        
+        RtlCopyMemory(&fn.Buffer[fn.Length], pp->name.Buffer, pp->name.Length);
+        fn.Length += pp->name.Length;
+            
+        le = le->Blink;
+    }
+    
+    if (not_in_tree)
+        ERR("subvol %llx, %.*s, offset %llx\n", subvol, fn.Length, fn.Buffer, offset);
+    else
+        ERR("%.*s, offset %llx\n", fn.Length, fn.Buffer, offset);
+    
+    Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, fn.Buffer, fn.Length);
+    if (!NT_SUCCESS(Status)) {
+        ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+        goto end;
+    }
+    
+    err = ExAllocatePoolWithTag(PagedPool, offsetof(scrub_error, data.filename[0]) + utf16len, ALLOC_TAG);
+    if (!err) {
+        ERR("out of memory\n");
+        goto end;
+    }
+    
+    err->address = addr;
+    err->device = devid;
+    err->recovered = FALSE;
+    err->is_metadata = FALSE;
+    err->parity = FALSE;
+    
+    err->data.subvol = not_in_tree ? subvol : 0;
+    err->data.offset = offset;
+    err->data.filename_length = utf16len;
+    
+    Status = RtlUTF8ToUnicodeN(err->data.filename, utf16len, &utf16len, fn.Buffer, fn.Length);
+    if (!NT_SUCCESS(Status)) {
+        ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+        ExFreePool(err);
+        goto end;
+    }
+
+    ExAcquireResourceExclusiveLite(&Vcb->scrub.stats_lock, TRUE);
+    
+    Vcb->scrub.num_errors++;
+    InsertTailList(&Vcb->scrub.errors, &err->list_entry);
+    
+    ExReleaseResourceLite(&Vcb->scrub.stats_lock);
+    
+    ExFreePool(fn.Buffer);
+    
+end:
+    while (!IsListEmpty(&parts)) {
+        path_part* pp = CONTAINING_RECORD(RemoveHeadList(&parts), path_part, list_entry);
+        
+        ExFreePool(pp);
+    }
+}
+
+static void log_file_checksum_error_shared(device_extension* Vcb, UINT64 treeaddr, UINT64 addr, UINT64 devid, UINT64 extent) {
+    tree_header* tree;
+    NTSTATUS Status;
+    leaf_node* ln;
+    ULONG i;
+    
+    tree = ExAllocatePoolWithTag(PagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+    if (!tree) {
+        ERR("out of memory\n");
+        return;
+    }
+    
+    Status = read_data(Vcb, treeaddr, Vcb->superblock.node_size, NULL, TRUE, (UINT8*)tree, NULL, NULL, NULL, 0, FALSE, 0);
+    if (!NT_SUCCESS(Status)) {
+        ERR("read_data returned %08x\n", Status);
+        goto end;
+    }
+    
+    if (tree->level != 0) {
+        ERR("tree level was %x, expected 0\n", tree->level);
+        goto end;
+    }
+    
+    ln = (leaf_node*)&tree[1];
+    
+    for (i = 0; i < tree->num_items; i++) {
+        if (ln[i].key.obj_type == TYPE_EXTENT_DATA && ln[i].size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
+            EXTENT_DATA* ed = (EXTENT_DATA*)((UINT8*)tree + sizeof(tree_header) + ln[i].offset);
+            EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
+
+            if (ed->type == EXTENT_TYPE_REGULAR && ed2->size != 0 && ed2->address == addr)
+                log_file_checksum_error(Vcb, addr, devid, tree->tree_id, ln[i].key.obj_id, ln[i].key.offset + addr - extent);
+        }
+    }
+    
+end:
+    ExFreePool(tree);
+}
+
+static void log_tree_checksum_error(device_extension* Vcb, UINT64 addr, UINT64 devid, UINT64 root, UINT8 level, KEY* firstitem) {
+    scrub_error* err;
+        
+    err = ExAllocatePoolWithTag(PagedPool, sizeof(scrub_error), ALLOC_TAG);
+    if (!err) {
+        ERR("out of memory\n");
+        return;
+    }
+    
+    err->address = addr;
+    err->device = devid;
+    err->recovered = FALSE;
+    err->is_metadata = TRUE;
+    err->parity = FALSE;
+    
+    err->metadata.root = root;
+    err->metadata.level = level;
+        
+    if (firstitem) {
+        ERR("root %llx, level %u, first item (%llx,%x,%llx)\n", root, level, firstitem->obj_id,
+                                                                firstitem->obj_type, firstitem->offset);
+        
+        err->metadata.firstitem = *firstitem;
+    } else {
+        ERR("root %llx, level %u\n", root, level);
+        
+        RtlZeroMemory(&err->metadata.firstitem, sizeof(KEY));
+    }
+    
+    ExAcquireResourceExclusiveLite(&Vcb->scrub.stats_lock, TRUE);
+
+    Vcb->scrub.num_errors++;
+    InsertTailList(&Vcb->scrub.errors, &err->list_entry);
+    
+    ExReleaseResourceLite(&Vcb->scrub.stats_lock);
+}
+
+static void log_tree_checksum_error_shared(device_extension* Vcb, UINT64 offset, UINT64 address, UINT64 devid) {
+    tree_header* tree;
+    NTSTATUS Status;
+    internal_node* in;
+    ULONG i;
+    
+    tree = ExAllocatePoolWithTag(PagedPool, Vcb->superblock.node_size, ALLOC_TAG);
+    if (!tree) {
+        ERR("out of memory\n");
+        return;
+    }
+    
+    Status = read_data(Vcb, offset, Vcb->superblock.node_size, NULL, TRUE, (UINT8*)tree, NULL, NULL, NULL, 0, FALSE, 0);
+    if (!NT_SUCCESS(Status)) {
+        ERR("read_data returned %08x\n", Status);
+        goto end;
+    }
+    
+    if (tree->level == 0) {
+        ERR("tree level was 0\n");
+        goto end;
+    }
+    
+    in = (internal_node*)&tree[1];
+    
+    for (i = 0; i < tree->num_items; i++) {
+        if (in[i].address == address) {
+            log_tree_checksum_error(Vcb, address, devid, tree->tree_id, tree->level - 1, &in[i].key);
+            break;
+        }
+    }
+    
+end:
+    ExFreePool(tree);
+}
+
+static void log_unrecoverable_error(device_extension* Vcb, UINT64 address, UINT64 devid) {
+    KEY searchkey;
+    traverse_ptr tp;
+    NTSTATUS Status;
+    EXTENT_ITEM* ei;
+    EXTENT_ITEM2* ei2 = NULL;
+    UINT8* ptr;
+    ULONG len;
+    UINT64 rc;
+    
+    // FIXME - still log even if rest of this function fails
+    
+    searchkey.obj_id = address;
+    searchkey.obj_type = TYPE_METADATA_ITEM;
+    searchkey.offset = 0xffffffffffffffff;
+    
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, NULL);
+    if (!NT_SUCCESS(Status)) {
+        ERR("find_item returned %08x\n", Status);
+        return;
+    }
+    
+    if ((tp.item->key.obj_type != TYPE_EXTENT_ITEM && tp.item->key.obj_type != TYPE_METADATA_ITEM) ||
+        tp.item->key.obj_id >= address + Vcb->superblock.sector_size ||
+        (tp.item->key.obj_type == TYPE_EXTENT_ITEM && tp.item->key.obj_id + tp.item->key.offset <= address) ||
+        (tp.item->key.obj_type == TYPE_METADATA_ITEM && tp.item->key.obj_id + Vcb->superblock.node_size <= address)
+    )
+        return;
+    
+    if (tp.item->size < sizeof(EXTENT_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(EXTENT_ITEM));
+        return;
+    }
+    
+    ei = (EXTENT_ITEM*)tp.item->data;
+    ptr = (UINT8*)&ei[1];
+    len = tp.item->size - sizeof(EXTENT_ITEM);
+    
+    if (tp.item->key.obj_id == TYPE_EXTENT_ITEM && ei->flags & EXTENT_ITEM_TREE_BLOCK) {
+        if (tp.item->size < sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2)) {
+            ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+                                                                       tp.item->size, sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2));
+            return;
+        }
+        
+        ei2 = (EXTENT_ITEM2*)ptr;
+        
+        ptr += sizeof(EXTENT_ITEM2);
+        len -= sizeof(EXTENT_ITEM2);
+    }
+    
+    rc = 0;
+    
+    while (len > 0) {
+        UINT8 type = *ptr;
+        
+        ptr++;
+        len--;
+        
+        if (type == TYPE_TREE_BLOCK_REF) {
+            TREE_BLOCK_REF* tbr;
+            
+            if (len < sizeof(TREE_BLOCK_REF)) {
+                ERR("TREE_BLOCK_REF takes up %u bytes, but only %u remaining\n", sizeof(TREE_BLOCK_REF), len);
+                break;
+            }
+            
+            tbr = (TREE_BLOCK_REF*)ptr;
+            
+            log_tree_checksum_error(Vcb, address, devid, tbr->offset, ei2 ? ei2->level : tp.item->key.offset, ei2 ? &ei2->firstitem : NULL);
+            
+            rc++;
+            
+            ptr += sizeof(TREE_BLOCK_REF);
+            len -= sizeof(TREE_BLOCK_REF);
+        } else if (type == TYPE_EXTENT_DATA_REF) {
+            EXTENT_DATA_REF* edr;
+            
+            if (len < sizeof(EXTENT_DATA_REF)) {
+                ERR("EXTENT_DATA_REF takes up %u bytes, but only %u remaining\n", sizeof(EXTENT_DATA_REF), len);
+                break;
+            }
+            
+            edr = (EXTENT_DATA_REF*)ptr;
+            
+            log_file_checksum_error(Vcb, address, devid, edr->root, edr->objid, edr->offset + address - tp.item->key.obj_id);
+            
+            rc += edr->count;
+            
+            ptr += sizeof(EXTENT_DATA_REF);
+            len -= sizeof(EXTENT_DATA_REF);
+        } else if (type == TYPE_SHARED_BLOCK_REF) {
+            SHARED_BLOCK_REF* sbr;
+            
+            if (len < sizeof(SHARED_BLOCK_REF)) {
+                ERR("SHARED_BLOCK_REF takes up %u bytes, but only %u remaining\n", sizeof(SHARED_BLOCK_REF), len);
+                break;
+            }
+            
+            sbr = (SHARED_BLOCK_REF*)ptr;
+            
+            log_tree_checksum_error_shared(Vcb, sbr->offset, address, devid);
+            
+            rc++;
+            
+            ptr += sizeof(SHARED_BLOCK_REF);
+            len -= sizeof(SHARED_BLOCK_REF);
+        } else if (type == TYPE_SHARED_DATA_REF) {
+            SHARED_DATA_REF* sdr;
+            
+            if (len < sizeof(SHARED_DATA_REF)) {
+                ERR("SHARED_DATA_REF takes up %u bytes, but only %u remaining\n", sizeof(SHARED_DATA_REF), len);
+                break;
+            }
+            
+            sdr = (SHARED_DATA_REF*)ptr;
+            
+            log_file_checksum_error_shared(Vcb, sdr->offset, address, devid, tp.item->key.obj_id);
+            
+            rc += sdr->count;
+            
+            ptr += sizeof(SHARED_DATA_REF);
+            len -= sizeof(SHARED_DATA_REF);
+        } else {
+            ERR("unknown extent type %x\n", type);
+            break;
+        }
+    }
+    
+    if (rc < ei->refcount) {
+        do {
+            traverse_ptr next_tp;
+            
+            if (find_next_item(Vcb, &tp, &next_tp, FALSE, NULL))
+                tp = next_tp;
+            else
+                break;
+            
+            if (tp.item->key.obj_id == address) {
+                if (tp.item->key.obj_type == TYPE_TREE_BLOCK_REF) {
+                    TREE_BLOCK_REF* tbr;
+                    
+                    if (tp.item->size < sizeof(TREE_BLOCK_REF)) {
+                        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(TREE_BLOCK_REF));
+                        break;
+                    }
+                    
+                    tbr = (TREE_BLOCK_REF*)tp.item->data;
+                    
+                    log_tree_checksum_error(Vcb, address, devid, tbr->offset, ei2 ? ei2->level : tp.item->key.offset, ei2 ? &ei2->firstitem : NULL);
+                } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA_REF) {
+                    EXTENT_DATA_REF* edr;
+                    
+                    if (tp.item->size < sizeof(EXTENT_DATA_REF)) {
+                        ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+                                                                          tp.item->size, sizeof(EXTENT_DATA_REF));
+                        break;
+                    }
+                    
+                    edr = (EXTENT_DATA_REF*)tp.item->data;
+                    
+                    log_file_checksum_error(Vcb, address, devid, edr->root, edr->objid, edr->offset + address - tp.item->key.obj_id);
+                } else if (tp.item->key.obj_type == TYPE_SHARED_BLOCK_REF) {
+                    SHARED_BLOCK_REF* sbr;
+                    
+                    if (tp.item->size < sizeof(SHARED_BLOCK_REF)) {
+                        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(SHARED_BLOCK_REF));
+                        break;
+                    }
+                    
+                    sbr = (SHARED_BLOCK_REF*)tp.item->data;
+                    
+                    log_tree_checksum_error_shared(Vcb, sbr->offset, address, devid);
+                } else if (tp.item->key.obj_type == TYPE_SHARED_DATA_REF) {
+                    SHARED_DATA_REF* sdr;
+                    
+                    if (tp.item->size < sizeof(SHARED_DATA_REF)) {
+                        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(SHARED_DATA_REF));
+                        break;
+                    }
+                    
+                    sdr = (SHARED_DATA_REF*)tp.item->data;
+                    
+                    log_file_checksum_error_shared(Vcb, sdr->offset, address, devid, tp.item->key.obj_id);
+                }
+            } else
+                break;
+        } while (TRUE);
+    }
+}
+
+static void log_error(device_extension* Vcb, UINT64 addr, UINT64 devid, BOOL metadata, BOOL recoverable, BOOL parity) {
+    if (recoverable) {
+        scrub_error* err;
+        
+        if (parity) {
+            ERR("recovering from parity error at %llx on device %llx\n", addr, devid);
+        } else {
+            if (metadata)
+                ERR("recovering from metadata checksum error at %llx on device %llx\n", addr, devid);
+            else
+                ERR("recovering from data checksum error at %llx on device %llx\n", addr, devid);
+        }
+        
+        err = ExAllocatePoolWithTag(PagedPool, sizeof(scrub_error), ALLOC_TAG);
+        if (!err) {
+            ERR("out of memory\n");
+            return;
+        }
+        
+        err->address = addr;
+        err->device = devid;
+        err->recovered = TRUE;
+        err->is_metadata = metadata;
+        err->parity = parity;
+        
+        if (metadata)
+            RtlZeroMemory(&err->metadata, sizeof(err->metadata));
+        else
+            RtlZeroMemory(&err->data, sizeof(err->data));
+        
+        ExAcquireResourceExclusiveLite(&Vcb->scrub.stats_lock, TRUE);
+        
+        Vcb->scrub.num_errors++;
+        InsertTailList(&Vcb->scrub.errors, &err->list_entry);
+        
+        ExReleaseResourceLite(&Vcb->scrub.stats_lock);
+    } else {
+        if (metadata)
+            ERR("unrecoverable metadata checksum error at %llx\n", addr);
+        else
+            ERR("unrecoverable data checksum error at %llx\n", addr);
+        
+        log_unrecoverable_error(Vcb, addr, devid);
+    }
+}
+
+static NTSTATUS STDCALL scrub_read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+    scrub_context_stripe* stripe = conptr;
+    scrub_context* context = (scrub_context*)stripe->context;
+    ULONG left = InterlockedDecrement(&context->stripes_left);
+    
+    UNUSED(DeviceObject);
+    
+    stripe->iosb = Irp->IoStatus;
+    
+    if (left == 0)
+        KeSetEvent(&context->Event, 0, FALSE);
+    
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS scrub_extent_dup(device_extension* Vcb, chunk* c, UINT64 offset, UINT32* csum, scrub_context* context) {
+    NTSTATUS Status;
+    BOOL csum_error = FALSE;
+    ULONG i;
+    CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+    
+    if (csum) {
+        ULONG good_stripe = 0xffffffff;
+        
+        for (i = 0; i < c->chunk_item->num_stripes; i++) {
+            // if first stripe is okay, we only need to check that the others are identical to it
+            if (good_stripe != 0xffffffff) {
+                if (RtlCompareMemory(context->stripes[i].buf, context->stripes[good_stripe].buf,
+                                     context->stripes[good_stripe].length) != context->stripes[i].length) {
+                    context->stripes[i].csum_error = TRUE;
+                    csum_error = TRUE;
+                    log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                }
+            } else {
+                Status = check_csum(Vcb, context->stripes[i].buf, context->stripes[i].length / Vcb->superblock.sector_size, csum);
+                if (Status == STATUS_CRC_ERROR) {
+                    context->stripes[i].csum_error = TRUE;
+                    csum_error = TRUE;
+                    log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                } else if (!NT_SUCCESS(Status)) {
+                    ERR("check_csum returned %08x\n", Status);
+                    return Status;
+                } else
+                    good_stripe = i;
+            }
+        }
+    } else {
+        ULONG good_stripe = 0xffffffff;
+        
+        for (i = 0; i < c->chunk_item->num_stripes; i++) {
+            ULONG j;
+            
+            // if first stripe is okay, we only need to check that the others are identical to it
+            if (good_stripe != 0xffffffff) {
+                if (RtlCompareMemory(context->stripes[i].buf, context->stripes[good_stripe].buf,
+                                     context->stripes[good_stripe].length) != context->stripes[i].length) {
+                    context->stripes[i].csum_error = TRUE;
+                    csum_error = TRUE;
+                    log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                }
+            } else {
+                for (j = 0; j < context->stripes[i].length / Vcb->superblock.node_size; j++) {
+                    tree_header* th = (tree_header*)&context->stripes[i].buf[j * Vcb->superblock.node_size];
+                    UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+                    
+                    if (crc32 != *((UINT32*)th->csum) || th->address != offset + UInt32x32To64(j, Vcb->superblock.node_size)) {
+                        context->stripes[i].csum_error = TRUE;
+                        csum_error = TRUE;
+                        log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                    }
+                }
+                
+                if (!context->stripes[i].csum_error)
+                    good_stripe = i;
+            }
+        }
+    }
+    
+    if (!csum_error)
+        return STATUS_SUCCESS;
+    
+    // handle checksum error
+    
+    for (i = 0; i < c->chunk_item->num_stripes; i++) {
+        if (context->stripes[i].csum_error) {
+            if (csum) {
+                context->stripes[i].bad_csums = ExAllocatePoolWithTag(PagedPool, context->stripes[i].length * sizeof(UINT32) / Vcb->superblock.sector_size, ALLOC_TAG);
+                if (!context->stripes[i].bad_csums) {
+                    ERR("out of memory\n");
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+                
+                Status = calc_csum(Vcb, context->stripes[i].buf, context->stripes[i].length / Vcb->superblock.sector_size, context->stripes[i].bad_csums);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("calc_csum returned %08x\n", Status);
+                    return Status;
+                }
+            } else {
+                ULONG j;
+                
+                context->stripes[i].bad_csums = ExAllocatePoolWithTag(PagedPool, context->stripes[i].length * sizeof(UINT32) / Vcb->superblock.node_size, ALLOC_TAG);
+                if (!context->stripes[i].bad_csums) {
+                    ERR("out of memory\n");
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+                
+                for (j = 0; j < context->stripes[i].length / Vcb->superblock.node_size; j++) {
+                    tree_header* th = (tree_header*)&context->stripes[i].buf[j * Vcb->superblock.node_size];
+                    UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+                    
+                    context->stripes[i].bad_csums[j] = crc32;
+                }
+            }
+        }
+    }
+    
+    if (c->chunk_item->num_stripes > 1) {
+        ULONG good_stripe = 0xffffffff;
+        
+        for (i = 0; i < c->chunk_item->num_stripes; i++) {
+            if (!context->stripes[i].csum_error) {
+                good_stripe = i;
+                break;
+            }
+        }
+        
+        if (good_stripe != 0xffffffff) {
+            // log
+            
+            for (i = 0; i < c->chunk_item->num_stripes; i++) {
+                if (context->stripes[i].csum_error) {
+                    ULONG j;
+                    
+                    if (csum) {
+                        for (j = 0; j < context->stripes[i].length / Vcb->superblock.sector_size; j++) {
+                            if (context->stripes[i].bad_csums[j] != csum[j]) {
+                                UINT64 addr = offset + UInt32x32To64(j, Vcb->superblock.sector_size);
+                                
+                                log_error(Vcb, addr, c->devices[i]->devitem.dev_id, FALSE, TRUE, FALSE);
+                                log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                            }
+                        }
+                    } else {
+                        for (j = 0; j < context->stripes[i].length / Vcb->superblock.node_size; j++) {
+                            tree_header* th = (tree_header*)&context->stripes[i].buf[j * Vcb->superblock.node_size];
+                            UINT64 addr = offset + UInt32x32To64(j, Vcb->superblock.node_size);
+                            
+                            if (context->stripes[i].bad_csums[j] != *((UINT32*)th->csum) || th->address != addr) {
+                                log_error(Vcb, addr, c->devices[i]->devitem.dev_id, TRUE, TRUE, FALSE);
+                                log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                            }
+                        }
+                    }
+                }
+            }
+            
+            // write good data over bad
+            
+            for (i = 0; i < c->chunk_item->num_stripes; i++) {
+                if (context->stripes[i].csum_error && !c->devices[i]->readonly) {
+                    Status = write_data_phys(c->devices[i]->devobj, cis[i].offset + offset - c->offset,
+                                             context->stripes[good_stripe].buf, context->stripes[i].length);
+                    
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("write_data_phys returned %08x\n", Status);
+                        log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_WRITE_ERRORS);
+                        return Status;
+                    }
+                }
+            }
+            
+            return STATUS_SUCCESS;
+        }
+        
+        // if csum errors on all stripes, check sector by sector
+        
+        for (i = 0; i < c->chunk_item->num_stripes; i++) {
+            ULONG j;
+            
+            if (csum) {
+                for (j = 0; j < context->stripes[i].length / Vcb->superblock.sector_size; j++) {
+                    if (context->stripes[i].bad_csums[j] != csum[j]) {
+                        ULONG k;
+                        UINT64 addr = offset + UInt32x32To64(j, Vcb->superblock.sector_size);
+                        BOOL recovered = FALSE;
+                        
+                        for (k = 0; k < c->chunk_item->num_stripes; k++) {
+                            if (i != k && context->stripes[k].bad_csums[j] == csum[j]) {
+                                log_error(Vcb, addr, c->devices[i]->devitem.dev_id, FALSE, TRUE, FALSE);
+                                log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                                
+                                RtlCopyMemory(context->stripes[i].buf + (j * Vcb->superblock.sector_size),
+                                              context->stripes[k].buf + (j * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+                                
+                                recovered = TRUE;
+                                break;
+                            }
+                        }
+                        
+                        if (!recovered) {
+                            log_error(Vcb, addr, c->devices[i]->devitem.dev_id, FALSE, FALSE, FALSE);
+                            log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                        }
+                    }
+                }
+            } else {
+                for (j = 0; j < context->stripes[i].length / Vcb->superblock.node_size; j++) {
+                    tree_header* th = (tree_header*)&context->stripes[i].buf[j * Vcb->superblock.node_size];
+                    UINT64 addr = offset + UInt32x32To64(j, Vcb->superblock.node_size);
+                    
+                    if (context->stripes[i].bad_csums[j] != *((UINT32*)th->csum) || th->address != addr) {
+                        ULONG k;
+                        BOOL recovered = FALSE;
+                        
+                        for (k = 0; k < c->chunk_item->num_stripes; k++) {
+                            if (i != k) {
+                                tree_header* th2 = (tree_header*)&context->stripes[k].buf[j * Vcb->superblock.node_size];
+                                
+                                if (context->stripes[k].bad_csums[j] == *((UINT32*)th2->csum) && th2->address == addr) {
+                                    log_error(Vcb, addr, c->devices[i]->devitem.dev_id, TRUE, TRUE, FALSE);
+                                    log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                                    
+                                    RtlCopyMemory(th, th2, Vcb->superblock.node_size);
+                                    
+                                    recovered = TRUE;
+                                    break;
+                                }
+                            }
+                        }
+                        
+                        if (!recovered) {
+                            log_error(Vcb, addr, c->devices[i]->devitem.dev_id, TRUE, FALSE, FALSE);
+                            log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                        }
+                    }
+                }
+            }
+        }
+                   
+        // write good data over bad
+        
+        for (i = 0; i < c->chunk_item->num_stripes; i++) {
+            if (!c->devices[i]->readonly) {
+                Status = write_data_phys(c->devices[i]->devobj, cis[i].offset + offset - c->offset,
+                                         context->stripes[i].buf, context->stripes[i].length);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("write_data_phys returned %08x\n", Status);
+                    log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                    return Status;
+                }
+            }
+        }
+        
+        return STATUS_SUCCESS;
+    }
+    
+    for (i = 0; i < c->chunk_item->num_stripes; i++) {
+        ULONG j;
+        
+        if (csum) {
+            for (j = 0; j < context->stripes[i].length / Vcb->superblock.sector_size; j++) {
+                if (context->stripes[i].bad_csums[j] != csum[j]) {
+                    UINT64 addr = offset + UInt32x32To64(j, Vcb->superblock.sector_size);
+                    
+                    log_error(Vcb, addr, c->devices[i]->devitem.dev_id, FALSE, FALSE, FALSE);
+                }
+            }
+        } else {
+            for (j = 0; j < context->stripes[i].length / Vcb->superblock.node_size; j++) {
+                tree_header* th = (tree_header*)&context->stripes[i].buf[j * Vcb->superblock.node_size];
+                UINT64 addr = offset + UInt32x32To64(j, Vcb->superblock.node_size);
+                
+                if (context->stripes[i].bad_csums[j] != *((UINT32*)th->csum) || th->address != addr)
+                    log_error(Vcb, addr, c->devices[i]->devitem.dev_id, TRUE, FALSE, FALSE);
+            }
+        }
+    }
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS scrub_extent_raid0(device_extension* Vcb, chunk* c, UINT64 offset, UINT32 length, UINT16 startoffstripe, UINT32* csum, scrub_context* context) {
+    ULONG j;
+    UINT16 stripe;
+    UINT32 pos, *stripeoff;
+    
+    pos = 0;
+    stripeoff = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT32) * c->chunk_item->num_stripes, ALLOC_TAG);
+    if (!stripeoff) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlZeroMemory(stripeoff, sizeof(UINT32) * c->chunk_item->num_stripes);
+    
+    stripe = startoffstripe;
+    while (pos < length) {
+        UINT32 readlen;
+        
+        if (pos == 0)
+            readlen = min(context->stripes[stripe].length, c->chunk_item->stripe_length - (context->stripes[stripe].start % c->chunk_item->stripe_length));
+        else
+            readlen = min(length - pos, c->chunk_item->stripe_length);
+        
+        if (csum) {
+            for (j = 0; j < readlen; j += Vcb->superblock.sector_size) {
+                UINT32 crc32 = ~calc_crc32c(0xffffffff, context->stripes[stripe].buf + stripeoff[stripe], Vcb->superblock.sector_size);
+                
+                if (crc32 != csum[pos / Vcb->superblock.sector_size]) {
+                    UINT64 addr = offset + pos;
+                    
+                    log_error(Vcb, addr, c->devices[stripe]->devitem.dev_id, FALSE, FALSE, FALSE);
+                    log_device_error(Vcb, c->devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                }
+                
+                pos += Vcb->superblock.sector_size;
+                stripeoff[stripe] += Vcb->superblock.sector_size;
+            }
+        } else {
+            for (j = 0; j < readlen; j += Vcb->superblock.node_size) {
+                tree_header* th = (tree_header*)(context->stripes[stripe].buf + stripeoff[stripe]);
+                UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+                UINT64 addr = offset + pos;
+                
+                if (crc32 != *((UINT32*)th->csum) || th->address != addr) {
+                    log_error(Vcb, addr, c->devices[stripe]->devitem.dev_id, TRUE, FALSE, FALSE);
+                    log_device_error(Vcb, c->devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                }
+                
+                pos += Vcb->superblock.node_size;
+                stripeoff[stripe] += Vcb->superblock.node_size;
+            }
+        }
+        
+        stripe = (stripe + 1) % c->chunk_item->num_stripes;
+    }
+    
+    ExFreePool(stripeoff);
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS scrub_extent_raid10(device_extension* Vcb, chunk* c, UINT64 offset, UINT32 length, UINT16 startoffstripe, UINT32* csum, scrub_context* context) {
+    ULONG j;
+    UINT16 stripe, sub_stripes = max(c->chunk_item->sub_stripes, 1);
+    UINT32 pos, *stripeoff;
+    BOOL csum_error = FALSE;
+    NTSTATUS Status;
+    
+    pos = 0;
+    stripeoff = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT32) * c->chunk_item->num_stripes / sub_stripes, ALLOC_TAG);
+    if (!stripeoff) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlZeroMemory(stripeoff, sizeof(UINT32) * c->chunk_item->num_stripes / sub_stripes);
+    
+    stripe = startoffstripe;
+    while (pos < length) {
+        UINT32 readlen;
+        
+        if (pos == 0)
+            readlen = min(context->stripes[stripe * sub_stripes].length,
+                          c->chunk_item->stripe_length - (context->stripes[stripe * sub_stripes].start % c->chunk_item->stripe_length));
+        else
+            readlen = min(length - pos, c->chunk_item->stripe_length);
+        
+        if (csum) {
+            ULONG good_stripe = 0xffffffff;
+            UINT16 k;
+
+            for (k = 0; k < sub_stripes; k++) {
+                // if first stripe is okay, we only need to check that the others are identical to it
+                if (good_stripe != 0xffffffff) {
+                    if (RtlCompareMemory(context->stripes[(stripe * sub_stripes) + k].buf + stripeoff[stripe],
+                                         context->stripes[(stripe * sub_stripes) + good_stripe].buf + stripeoff[stripe],
+                                         readlen) != readlen) {
+                        context->stripes[(stripe * sub_stripes) + k].csum_error = TRUE;
+                        csum_error = TRUE;
+                        log_device_error(Vcb, c->devices[(stripe * sub_stripes) + k], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                    }
+                } else {
+                    for (j = 0; j < readlen; j += Vcb->superblock.sector_size) {
+                        UINT32 crc32 = ~calc_crc32c(0xffffffff, context->stripes[(stripe * sub_stripes) + k].buf + stripeoff[stripe] + j, Vcb->superblock.sector_size);
+                        
+                        if (crc32 != csum[(pos + j) / Vcb->superblock.sector_size]) {
+                            csum_error = TRUE;
+                            context->stripes[(stripe * sub_stripes) + k].csum_error = TRUE;
+                            log_device_error(Vcb, c->devices[(stripe * sub_stripes) + k], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                            break;
+                        }
+                    }
+                    
+                    if (!context->stripes[(stripe * sub_stripes) + k].csum_error)
+                        good_stripe = k;
+                }
+            }
+            
+            pos += readlen;
+            stripeoff[stripe] += readlen;
+        } else {
+            ULONG good_stripe = 0xffffffff;
+            UINT16 k;
+
+            for (k = 0; k < sub_stripes; k++) {
+                // if first stripe is okay, we only need to check that the others are identical to it
+                if (good_stripe != 0xffffffff) {
+                    if (RtlCompareMemory(context->stripes[(stripe * sub_stripes) + k].buf + stripeoff[stripe],
+                                         context->stripes[(stripe * sub_stripes) + good_stripe].buf + stripeoff[stripe],
+                                         readlen) != readlen) {
+                        context->stripes[(stripe * sub_stripes) + k].csum_error = TRUE;
+                        csum_error = TRUE;
+                        log_device_error(Vcb, c->devices[(stripe * sub_stripes) + k], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                    }
+                } else {
+                    for (j = 0; j < readlen; j += Vcb->superblock.node_size) {
+                        tree_header* th = (tree_header*)(context->stripes[(stripe * sub_stripes) + k].buf + stripeoff[stripe] + j);
+                        UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+                        UINT64 addr = offset + pos + j;
+                        
+                        if (crc32 != *((UINT32*)th->csum) || th->address != addr) {
+                            csum_error = TRUE;
+                            context->stripes[(stripe * sub_stripes) + k].csum_error = TRUE;
+                            log_device_error(Vcb, c->devices[(stripe * sub_stripes) + k], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                            break;
+                        }
+                    }
+                    
+                    if (!context->stripes[(stripe * sub_stripes) + k].csum_error)
+                        good_stripe = k;
+                }
+            }
+            
+            pos += readlen;
+            stripeoff[stripe] += readlen;
+        }
+        
+        stripe = (stripe + 1) % (c->chunk_item->num_stripes / sub_stripes);
+    }
+    
+    if (!csum_error) {
+        Status = STATUS_SUCCESS;
+        goto end;
+    }
+    
+    for (j = 0; j < c->chunk_item->num_stripes; j += sub_stripes) {
+        ULONG goodstripe = 0xffffffff;
+        UINT16 k;
+        BOOL hasbadstripe = FALSE;
+        
+        if (context->stripes[j].length == 0)
+            continue;
+        
+        for (k = 0; k < sub_stripes; k++) {
+            if (!context->stripes[j + k].csum_error)
+                goodstripe = k;
+            else
+                hasbadstripe = TRUE;
+        }
+        
+        if (hasbadstripe) {
+            if (goodstripe != 0xffffffff) {
+                for (k = 0; k < sub_stripes; k++) {
+                    if (context->stripes[j + k].csum_error) {
+                        UINT32 so = 0;
+                        BOOL recovered = FALSE;
+                        
+                        pos = 0;
+                        
+                        stripe = startoffstripe;
+                        while (pos < length) {
+                            UINT32 readlen;
+                            
+                            if (pos == 0)
+                                readlen = min(context->stripes[stripe * sub_stripes].length,
+                                              c->chunk_item->stripe_length - (context->stripes[stripe * sub_stripes].start % c->chunk_item->stripe_length));
+                            else
+                                readlen = min(length - pos, c->chunk_item->stripe_length);
+                            
+                            if (stripe == j / sub_stripes) {
+                                if (csum) {
+                                    ULONG l;
+                                    
+                                    for (l = 0; l < readlen; l += Vcb->superblock.sector_size) {
+                                        if (RtlCompareMemory(context->stripes[j + k].buf + so,
+                                                            context->stripes[j + goodstripe].buf + so,
+                                                            Vcb->superblock.sector_size) != Vcb->superblock.sector_size) {
+                                            UINT64 addr = offset + pos;
+
+                                            log_error(Vcb, addr, c->devices[j + k]->devitem.dev_id, FALSE, TRUE, FALSE);
+                                            
+                                            recovered = TRUE;
+                                        }
+                                        
+                                        pos += Vcb->superblock.sector_size;
+                                        so += Vcb->superblock.sector_size;
+                                    }
+                                } else {
+                                    ULONG l;
+                                    
+                                    for (l = 0; l < readlen; l += Vcb->superblock.node_size) {
+                                        if (RtlCompareMemory(context->stripes[j + k].buf + so,
+                                                            context->stripes[j + goodstripe].buf + so,
+                                                            Vcb->superblock.node_size) != Vcb->superblock.node_size) {
+                                            UINT64 addr = offset + pos;
+
+                                            log_error(Vcb, addr, c->devices[j + k]->devitem.dev_id, TRUE, TRUE, FALSE);
+                                            
+                                            recovered = TRUE;
+                                        }
+                                        
+                                        pos += Vcb->superblock.node_size;
+                                        so += Vcb->superblock.node_size;
+                                    }
+                                }
+                            } else
+                                pos += readlen;
+                            
+                            stripe = (stripe + 1) % (c->chunk_item->num_stripes / sub_stripes);
+                        }
+                        
+                        if (recovered) {
+                            // write good data over bad
+                            
+                            if (!c->devices[j + k]->readonly) {
+                                CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+                                
+                                Status = write_data_phys(c->devices[j + k]->devobj, cis[j + k].offset + offset - c->offset,
+                                                         context->stripes[j + goodstripe].buf, context->stripes[j + goodstripe].length);
+                        
+                                if (!NT_SUCCESS(Status)) {
+                                    ERR("write_data_phys returned %08x\n", Status);
+                                    log_device_error(Vcb, c->devices[j + k], BTRFS_DEV_STAT_WRITE_ERRORS);
+                                    goto end;
+                                }
+                            }
+                        }
+                    }
+                }
+            } else {
+                UINT32 so = 0;
+                BOOL recovered = FALSE;
+                
+                if (csum) {
+                    for (k = 0; k < sub_stripes; k++) {
+                        context->stripes[j + k].bad_csums = ExAllocatePoolWithTag(PagedPool, context->stripes[j + k].length * sizeof(UINT32) / Vcb->superblock.sector_size, ALLOC_TAG);
+                        if (!context->stripes[j + k].bad_csums) {
+                            ERR("out of memory\n");
+                            Status = STATUS_INSUFFICIENT_RESOURCES;
+                            goto end;
+                        }
+                        
+                        Status = calc_csum(Vcb, context->stripes[j + k].buf, context->stripes[j + k].length / Vcb->superblock.sector_size, context->stripes[j + k].bad_csums);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("calc_csum returned %08x\n", Status);
+                            goto end;
+                        }
+                    }
+                } else {
+                    for (k = 0; k < sub_stripes; k++) {
+                        ULONG l;
+                        
+                        context->stripes[j + k].bad_csums = ExAllocatePoolWithTag(PagedPool, context->stripes[j + k].length * sizeof(UINT32) / Vcb->superblock.node_size, ALLOC_TAG);
+                        if (!context->stripes[j + k].bad_csums) {
+                            ERR("out of memory\n");
+                            Status = STATUS_INSUFFICIENT_RESOURCES;
+                            goto end;
+                        }
+                        
+                        for (l = 0; l < context->stripes[j + k].length / Vcb->superblock.node_size; l++) {
+                            tree_header* th = (tree_header*)&context->stripes[j + k].buf[l * Vcb->superblock.node_size];
+                            UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+                            
+                            context->stripes[j + k].bad_csums[l] = crc32;
+                        }
+                    }
+                }
+                
+                pos = 0;
+                
+                stripe = startoffstripe;
+                while (pos < length) {
+                    UINT32 readlen;
+                    
+                    if (pos == 0)
+                        readlen = min(context->stripes[stripe * sub_stripes].length,
+                                        c->chunk_item->stripe_length - (context->stripes[stripe * sub_stripes].start % c->chunk_item->stripe_length));
+                    else
+                        readlen = min(length - pos, c->chunk_item->stripe_length);
+                    
+                    if (stripe == j / sub_stripes) {
+                        ULONG l;
+                        
+                        if (csum) {
+                            for (l = 0; l < readlen; l += Vcb->superblock.sector_size) {
+                                UINT32 crc32 = csum[pos / Vcb->superblock.sector_size];
+                                ULONG goodstripe = 0xffffffff;
+                                BOOL has_error = FALSE;
+                                
+                                for (k = 0; k < sub_stripes; k++) {
+                                    if (context->stripes[j + k].bad_csums[so / Vcb->superblock.sector_size] != crc32)
+                                        has_error = TRUE;
+                                    else
+                                        goodstripe = k;
+                                }
+                                
+                                if (has_error) {
+                                    if (goodstripe != 0xffffffff) {
+                                        for (k = 0; k < sub_stripes; k++) {
+                                            if (context->stripes[j + k].bad_csums[so / Vcb->superblock.sector_size] != crc32) {
+                                                UINT64 addr = offset + pos;
+
+                                                log_error(Vcb, addr, c->devices[j + k]->devitem.dev_id, FALSE, TRUE, FALSE);
+                                                
+                                                recovered = TRUE;
+                                                
+                                                RtlCopyMemory(context->stripes[j + k].buf + so, context->stripes[j + goodstripe].buf + so, 
+                                                            Vcb->superblock.sector_size);
+                                            }
+                                        }
+                                    } else {
+                                        UINT64 addr = offset + pos;
+                                        
+                                        for (k = 0; k < sub_stripes; k++) {
+                                            log_error(Vcb, addr, c->devices[j + k]->devitem.dev_id, FALSE, FALSE, FALSE);
+                                            log_device_error(Vcb, c->devices[j + k], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                                        }
+                                    }
+                                }
+                                
+                                pos += Vcb->superblock.sector_size;
+                                so += Vcb->superblock.sector_size;
+                            }
+                        } else {
+                            for (l = 0; l < readlen; l += Vcb->superblock.node_size) {
+                                for (k = 0; k < sub_stripes; k++) {
+                                    tree_header* th = (tree_header*)&context->stripes[j + k].buf[so];
+                                    UINT64 addr = offset + pos;
+                        
+                                    if (context->stripes[j + k].bad_csums[so / Vcb->superblock.node_size] != *((UINT32*)th->csum) || th->address != addr) {
+                                        ULONG m;
+                                        BOOL recovered = FALSE;
+                                        
+                                        for (m = 0; m < sub_stripes; m++) {
+                                            if (m != k) {
+                                                tree_header* th2 = (tree_header*)&context->stripes[j + m].buf[so];
+                                                
+                                                if (context->stripes[j + m].bad_csums[so / Vcb->superblock.node_size] == *((UINT32*)th2->csum) && th2->address == addr) {
+                                                    log_error(Vcb, addr, c->devices[j + k]->devitem.dev_id, TRUE, TRUE, FALSE);
+                                                    
+                                                    RtlCopyMemory(th, th2, Vcb->superblock.node_size);
+                                                    
+                                                    recovered = TRUE;
+                                                    break;
+                                                } else
+                                                    log_device_error(Vcb, c->devices[j + m], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                                            }
+                                        }
+                                        
+                                        if (!recovered)
+                                            log_error(Vcb, addr, c->devices[j + k]->devitem.dev_id, TRUE, FALSE, FALSE);
+                                    }
+                                }
+                                
+                                pos += Vcb->superblock.node_size;
+                                so += Vcb->superblock.node_size;
+                            }
+                        }
+                    } else
+                        pos += readlen;
+                    
+                    stripe = (stripe + 1) % (c->chunk_item->num_stripes / sub_stripes);
+                }
+                
+                if (recovered) {
+                    // write good data over bad
+                    
+                    for (k = 0; k < sub_stripes; k++) {
+                        if (!c->devices[j + k]->readonly) {
+                            CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+                            
+                            Status = write_data_phys(c->devices[j + k]->devobj, cis[j + k].offset + offset - c->offset,
+                                                        context->stripes[j + k].buf, context->stripes[j + k].length);
+                    
+                            if (!NT_SUCCESS(Status)) {
+                                ERR("write_data_phys returned %08x\n", Status);
+                                log_device_error(Vcb, c->devices[j + k], BTRFS_DEV_STAT_WRITE_ERRORS);
+                                goto end;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
+    Status = STATUS_SUCCESS;
+    
+end:
+    ExFreePool(stripeoff);
+    
+    return Status;
+}
+
+static NTSTATUS scrub_extent(device_extension* Vcb, chunk* c, ULONG type, UINT64 offset, UINT64 size, UINT32* csum) {
+    ULONG i;
+    scrub_context context;
+    CHUNK_ITEM_STRIPE* cis;
+    NTSTATUS Status;
+    UINT16 startoffstripe;
+    
+    // FIXME - make work when degraded
+    
+    TRACE("(%p, %p, %llx, %llx, %p)\n", Vcb, c, offset, size, csum);
+    
+    context.stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(scrub_context_stripe) * c->chunk_item->num_stripes, ALLOC_TAG);
+    if (!context.stripes) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto end;
+    }
+    
+    RtlZeroMemory(context.stripes, sizeof(scrub_context_stripe) * c->chunk_item->num_stripes);
+    
+    context.stripes_left = 0;
+    
+    cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+    
+    if (type == BLOCK_FLAG_RAID0) {
+        UINT64 startoff, endoff;
+        UINT16 endoffstripe;
+        
+        get_raid0_offset(offset - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &startoff, &startoffstripe);
+        get_raid0_offset(offset + size - c->offset - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &endoff, &endoffstripe);
+        
+        for (i = 0; i < c->chunk_item->num_stripes; i++) {
+            if (startoffstripe > i)
+                context.stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
+            else if (startoffstripe == i)
+                context.stripes[i].start = startoff;
+            else
+                context.stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length);
+            
+            if (endoffstripe > i)
+                context.stripes[i].length = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length - context.stripes[i].start;
+            else if (endoffstripe == i)
+                context.stripes[i].length = endoff + 1 - context.stripes[i].start;
+            else
+                context.stripes[i].length = endoff - (endoff % c->chunk_item->stripe_length) - context.stripes[i].start;
+        }
+    } else if (type == BLOCK_FLAG_RAID10) {
+        UINT64 startoff, endoff;
+        UINT16 endoffstripe, j, sub_stripes = max(c->chunk_item->sub_stripes, 1);
+        
+        get_raid0_offset(offset - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes / sub_stripes, &startoff, &startoffstripe);
+        get_raid0_offset(offset + size - c->offset - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes / sub_stripes, &endoff, &endoffstripe);
+        
+        if ((c->chunk_item->num_stripes % sub_stripes) != 0) {
+            ERR("chunk %llx: num_stripes %x was not a multiple of sub_stripes %x!\n", c->offset, c->chunk_item->num_stripes, sub_stripes);
+            Status = STATUS_INTERNAL_ERROR;
+            goto end;
+        }
+        
+        startoffstripe *= sub_stripes;
+        endoffstripe *= sub_stripes;
+        
+        for (i = 0; i < c->chunk_item->num_stripes; i += sub_stripes) {
+            if (startoffstripe > i)
+                context.stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
+            else if (startoffstripe == i)
+                context.stripes[i].start = startoff;
+            else
+                context.stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length);
+            
+            if (endoffstripe > i)
+                context.stripes[i].length = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length - context.stripes[i].start;
+            else if (endoffstripe == i)
+                context.stripes[i].length = endoff + 1 - context.stripes[i].start;
+            else
+                context.stripes[i].length = endoff - (endoff % c->chunk_item->stripe_length) - context.stripes[i].start;
+            
+            for (j = 1; j < sub_stripes; j++) {
+                context.stripes[i+j].start = context.stripes[i].start;
+                context.stripes[i+j].length = context.stripes[i].length;
+            }
+        }
+        
+        startoffstripe /= sub_stripes;
+    }
+    
+    for (i = 0; i < c->chunk_item->num_stripes; i++) {
+        PIO_STACK_LOCATION IrpSp;
+        
+        context.stripes[i].context = (struct _scrub_context*)&context;
+        
+        if (type == BLOCK_FLAG_DUPLICATE) {
+            context.stripes[i].start = offset - c->offset;
+            context.stripes[i].length = size;
+        } else if (type != BLOCK_FLAG_RAID0 && type != BLOCK_FLAG_RAID10) {
+            ERR("unexpected chunk type %x\n", type);
+            Status = STATUS_INTERNAL_ERROR;
+            goto end;
+        }
+        
+        if (context.stripes[i].length > 0) {
+            context.stripes[i].buf = ExAllocatePoolWithTag(NonPagedPool, context.stripes[i].length, ALLOC_TAG);
+            
+            if (!context.stripes[i].buf) {
+                ERR("out of memory\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto end;
+            }
+            
+            context.stripes[i].Irp = IoAllocateIrp(c->devices[i]->devobj->StackSize, FALSE);
+            
+            if (!context.stripes[i].Irp) {
+                ERR("IoAllocateIrp failed\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto end;
+            }
+            
+            IrpSp = IoGetNextIrpStackLocation(context.stripes[i].Irp);
+            IrpSp->MajorFunction = IRP_MJ_READ;
+            
+            if (c->devices[i]->devobj->Flags & DO_BUFFERED_IO) {
+                context.stripes[i].Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, context.stripes[i].length, ALLOC_TAG);
+                if (!context.stripes[i].Irp->AssociatedIrp.SystemBuffer) {
+                    ERR("out of memory\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto end;
+                }
+
+                context.stripes[i].Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
+
+                context.stripes[i].Irp->UserBuffer = context.stripes[i].buf;
+            } else if (c->devices[i]->devobj->Flags & DO_DIRECT_IO) {
+                context.stripes[i].Irp->MdlAddress = IoAllocateMdl(context.stripes[i].buf, context.stripes[i].length, FALSE, FALSE, NULL);
+                if (!context.stripes[i].Irp->MdlAddress) {
+                    ERR("IoAllocateMdl failed\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto end;
+                }
+                
+                MmProbeAndLockPages(context.stripes[i].Irp->MdlAddress, KernelMode, IoWriteAccess);
+            } else
+                context.stripes[i].Irp->UserBuffer = context.stripes[i].buf;
+
+            IrpSp->Parameters.Read.Length = context.stripes[i].length;
+            IrpSp->Parameters.Read.ByteOffset.QuadPart = context.stripes[i].start + cis[i].offset;
+            
+            context.stripes[i].Irp->UserIosb = &context.stripes[i].iosb;
+            
+            IoSetCompletionRoutine(context.stripes[i].Irp, scrub_read_completion, &context.stripes[i], TRUE, TRUE, TRUE);
+            
+            context.stripes_left++;
+            
+            Vcb->scrub.data_scrubbed += context.stripes[i].length;
+        }
+    }
+    
+    if (context.stripes_left == 0) {
+        ERR("error - not reading any stripes\n");
+        Status = STATUS_INTERNAL_ERROR;
+        goto end;
+    }
+    
+    KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
+    
+    for (i = 0; i < c->chunk_item->num_stripes; i++) {
+        if (context.stripes[i].length > 0)
+            IoCallDriver(c->devices[i]->devobj, context.stripes[i].Irp);
+    }
+    
+    KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+    
+    // return an error if any of the stripes returned an error
+    for (i = 0; i < c->chunk_item->num_stripes; i++) {
+        if (!NT_SUCCESS(context.stripes[i].iosb.Status)) {
+            Status = context.stripes[i].iosb.Status;
+            log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_READ_ERRORS);
+            goto end;
+        }
+    }
+    
+    if (type == BLOCK_FLAG_DUPLICATE) {
+        Status = scrub_extent_dup(Vcb, c, offset, csum, &context);
+        if (!NT_SUCCESS(Status)) {
+            ERR("scrub_extent_dup returned %08x\n", Status);
+            goto end;
+        }
+    } else if (type == BLOCK_FLAG_RAID0) {
+        Status = scrub_extent_raid0(Vcb, c, offset, size, startoffstripe, csum, &context);
+        if (!NT_SUCCESS(Status)) {
+            ERR("scrub_extent_raid0 returned %08x\n", Status);
+            goto end;
+        }
+    } else if (type == BLOCK_FLAG_RAID10) {
+        Status = scrub_extent_raid10(Vcb, c, offset, size, startoffstripe, csum, &context);
+        if (!NT_SUCCESS(Status)) {
+            ERR("scrub_extent_raid10 returned %08x\n", Status);
+            goto end;
+        }
+    }
+    
+end:
+    if (context.stripes) {
+        for (i = 0; i < c->chunk_item->num_stripes; i++) {
+            if (context.stripes[i].Irp) {
+                if (c->devices[i]->devobj->Flags & DO_DIRECT_IO && context.stripes[i].Irp->MdlAddress) {
+                    MmUnlockPages(context.stripes[i].Irp->MdlAddress);
+                    IoFreeMdl(context.stripes[i].Irp->MdlAddress);
+                }
+                IoFreeIrp(context.stripes[i].Irp);
+            }
+            
+            if (context.stripes[i].buf)
+                ExFreePool(context.stripes[i].buf);
+            
+            if (context.stripes[i].bad_csums)
+                ExFreePool(context.stripes[i].bad_csums);
+        }
+        
+        ExFreePool(context.stripes);
+    }
+
+    return Status;
+}
+
+static NTSTATUS scrub_data_extent(device_extension* Vcb, chunk* c, UINT64 offset, ULONG type, UINT32* csum, RTL_BITMAP* bmp) {
+    NTSTATUS Status;
+    ULONG runlength, index;
+    
+    runlength = RtlFindFirstRunClear(bmp, &index);
+        
+    while (runlength != 0) {
+        do {
+            ULONG rl;
+            
+            if (runlength * Vcb->superblock.sector_size > SCRUB_UNIT)
+                rl = SCRUB_UNIT / Vcb->superblock.sector_size;
+            else
+                rl = runlength;
+            
+            Status = scrub_extent(Vcb, c, type, offset + UInt32x32To64(index, Vcb->superblock.sector_size), rl * Vcb->superblock.sector_size, &csum[index]);
+            if (!NT_SUCCESS(Status)) {
+                ERR("scrub_data_extent_dup returned %08x\n", Status);
+                return Status;
+            }
+
+            runlength -= rl;
+            index += rl;
+        } while (runlength > 0);
+        
+        runlength = RtlFindNextForwardRunClear(bmp, index, &index);
+    }
+    
+    return STATUS_SUCCESS;
+}
+
+typedef struct {
+    UINT8* buf;
+    PIRP Irp;
+    void* context;
+    IO_STATUS_BLOCK iosb;
+    UINT64 offset;
+    BOOL rewrite;
+    RTL_BITMAP error;
+    ULONG* errorarr;
+} scrub_context_raid56_stripe;
+
+typedef struct {
+    scrub_context_raid56_stripe* stripes;
+    LONG stripes_left;
+    KEVENT Event;
+    RTL_BITMAP alloc;
+    RTL_BITMAP has_csum;
+    RTL_BITMAP is_tree;
+    UINT32* csum;
+    UINT8* parity_scratch;
+    UINT8* parity_scratch2;
+} scrub_context_raid56;
+
+static NTSTATUS STDCALL scrub_read_completion_raid56(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+    scrub_context_raid56_stripe* stripe = conptr;
+    scrub_context_raid56* context = (scrub_context_raid56*)stripe->context;
+    LONG left = InterlockedDecrement(&context->stripes_left);
+    
+    UNUSED(DeviceObject);
+    
+    stripe->iosb = Irp->IoStatus;
+    
+    if (left == 0)
+        KeSetEvent(&context->Event, 0, FALSE);
+    
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static void scrub_raid5_stripe(device_extension* Vcb, chunk* c, scrub_context_raid56* context, UINT64 stripe_start, UINT64 bit_start, UINT64 num) {
+    ULONG sectors_per_stripe = c->chunk_item->stripe_length / Vcb->superblock.sector_size, i;
+    UINT16 stripe, parity = (bit_start + num + c->chunk_item->num_stripes - 1) % c->chunk_item->num_stripes;
+    UINT64 off, stripeoff;
+    
+    stripe = (parity + 1) % c->chunk_item->num_stripes;
+    off = (bit_start + num - stripe_start) * sectors_per_stripe * (c->chunk_item->num_stripes - 1);
+    stripeoff = num * sectors_per_stripe;
+    
+    RtlCopyMemory(context->parity_scratch, &context->stripes[parity].buf[num * c->chunk_item->stripe_length], c->chunk_item->stripe_length);
+    
+    while (stripe != parity) {
+        RtlClearAllBits(&context->stripes[stripe].error);
+        
+        for (i = 0; i < sectors_per_stripe; i++) {
+            if (RtlCheckBit(&context->alloc, off)) {
+                if (RtlCheckBit(&context->is_tree, off)) {
+                    tree_header* th = (tree_header*)&context->stripes[stripe].buf[stripeoff * Vcb->superblock.sector_size];
+                    UINT64 addr = c->offset + (stripe_start * (c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length) + (off * Vcb->superblock.sector_size);
+                    UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+                    
+                    if (crc32 != *((UINT32*)th->csum) || th->address != addr) {
+                        RtlSetBits(&context->stripes[stripe].error, i, Vcb->superblock.node_size / Vcb->superblock.sector_size);
+                        log_device_error(Vcb, c->devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                    }
+                    
+                    off += Vcb->superblock.node_size / Vcb->superblock.sector_size;
+                    stripeoff += Vcb->superblock.node_size / Vcb->superblock.sector_size;
+                    i += (Vcb->superblock.node_size / Vcb->superblock.sector_size) - 1;
+                    
+                    continue;
+                } else if (RtlCheckBit(&context->has_csum, off)) {
+                    UINT32 crc32 = ~calc_crc32c(0xffffffff, context->stripes[stripe].buf + (stripeoff * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+                    
+                    if (crc32 != context->csum[off]) {
+                        RtlSetBit(&context->stripes[stripe].error, i);
+                        log_device_error(Vcb, c->devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                    }
+                }
+            }
+            
+            off++;
+            stripeoff++;
+        }
+        
+        do_xor(context->parity_scratch, &context->stripes[stripe].buf[num * c->chunk_item->stripe_length], c->chunk_item->stripe_length);
+        
+        stripe = (stripe + 1) % c->chunk_item->num_stripes;
+        stripeoff = num * sectors_per_stripe;
+    }
+    
+    // check parity
+    
+    RtlClearAllBits(&context->stripes[parity].error);
+    
+    for (i = 0; i < sectors_per_stripe; i++) {
+        ULONG o, j;
+        
+        o = i * Vcb->superblock.sector_size;
+        for (j = 0; j < Vcb->superblock.sector_size; j++) { // FIXME - use SSE
+            if (context->parity_scratch[o] != 0) {
+                RtlSetBit(&context->stripes[parity].error, i);
+                break;
+            }
+            o++;
+        }
+    }
+    
+    // log and fix errors
+    
+    for (i = 0; i < sectors_per_stripe; i++) {
+        ULONG num_errors = 0;
+        UINT64 bad_stripe, bad_off;
+        BOOL alloc = FALSE;
+        
+        stripe = (parity + 1) % c->chunk_item->num_stripes;
+        off = ((bit_start + num - stripe_start) * sectors_per_stripe * (c->chunk_item->num_stripes - 1)) + i;
+        
+        while (stripe != parity) {
+            if (RtlCheckBit(&context->alloc, off)) {
+                alloc = TRUE;
+                
+                if (RtlCheckBit(&context->stripes[stripe].error, i)) {
+                    bad_stripe = stripe;
+                    bad_off = off;
+                    num_errors++;
+                }
+            }
+            
+            off += sectors_per_stripe;
+            stripe = (stripe + 1) % c->chunk_item->num_stripes;
+        }
+        
+        if (!alloc)
+            continue;
+        
+        if (num_errors == 0 && !RtlCheckBit(&context->stripes[parity].error, i)) // everything fine
+            continue;
+        
+        if (num_errors == 0 && RtlCheckBit(&context->stripes[parity].error, i)) { // parity error
+            UINT64 addr;
+            
+            do_xor(&context->stripes[parity].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                   &context->parity_scratch[i * Vcb->superblock.sector_size],
+                   Vcb->superblock.sector_size);
+            
+            bad_off = ((bit_start + num - stripe_start) * sectors_per_stripe * (c->chunk_item->num_stripes - 1)) + i;
+            addr = c->offset + (stripe_start * (c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length) + (bad_off * Vcb->superblock.sector_size);
+            
+            context->stripes[parity].rewrite = TRUE;
+            
+            log_error(Vcb, addr, c->devices[parity]->devitem.dev_id, FALSE, TRUE, TRUE);
+            log_device_error(Vcb, c->devices[parity], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+        } else if (num_errors == 1) {
+            UINT32 crc32;
+            UINT64 addr = c->offset + (stripe_start * (c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length) + (bad_off * Vcb->superblock.sector_size);
+            
+            if (RtlCheckBit(&context->is_tree, bad_off)) {
+                tree_header* th;
+                
+                do_xor(&context->parity_scratch[i * Vcb->superblock.sector_size],
+                       &context->stripes[bad_stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                       Vcb->superblock.node_size);
+                
+                th = (tree_header*)&context->parity_scratch[i * Vcb->superblock.sector_size];
+                crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+                
+                if (crc32 == *((UINT32*)th->csum) && th->address == addr) {
+                    RtlCopyMemory(&context->stripes[bad_stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                  &context->parity_scratch[i * Vcb->superblock.sector_size], Vcb->superblock.node_size);
+                    
+                    context->stripes[bad_stripe].rewrite = TRUE;
+                    
+                    RtlClearBits(&context->stripes[bad_stripe].error, i + 1, (Vcb->superblock.node_size / Vcb->superblock.sector_size) - 1);
+                    
+                    log_error(Vcb, addr, c->devices[bad_stripe]->devitem.dev_id, TRUE, TRUE, FALSE);
+                } else
+                    log_error(Vcb, addr, c->devices[bad_stripe]->devitem.dev_id, TRUE, FALSE, FALSE);
+            } else {
+                do_xor(&context->parity_scratch[i * Vcb->superblock.sector_size],
+                       &context->stripes[bad_stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                       Vcb->superblock.sector_size);
+                
+                crc32 = ~calc_crc32c(0xffffffff, &context->parity_scratch[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size);
+                
+                if (crc32 == context->csum[bad_off]) {
+                    RtlCopyMemory(&context->stripes[bad_stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                  &context->parity_scratch[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size);
+                    
+                    context->stripes[bad_stripe].rewrite = TRUE;
+                    
+                    log_error(Vcb, addr, c->devices[bad_stripe]->devitem.dev_id, FALSE, TRUE, FALSE);
+                } else
+                    log_error(Vcb, addr, c->devices[bad_stripe]->devitem.dev_id, FALSE, FALSE, FALSE);
+            }
+        } else {
+            stripe = (parity + 1) % c->chunk_item->num_stripes;
+            off = ((bit_start + num - stripe_start) * sectors_per_stripe * (c->chunk_item->num_stripes - 1)) + i;
+            
+            while (stripe != parity) {
+                if (RtlCheckBit(&context->alloc, off)) {
+                    if (RtlCheckBit(&context->stripes[stripe].error, i)) {
+                        UINT64 addr = c->offset + (stripe_start * (c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length) + (off * Vcb->superblock.sector_size);
+
+                        log_error(Vcb, addr, c->devices[stripe]->devitem.dev_id, RtlCheckBit(&context->is_tree, off), FALSE, FALSE);
+                    }
+                }
+                
+                off += sectors_per_stripe;
+                stripe = (stripe + 1) % c->chunk_item->num_stripes;
+            }
+        }
+    }
+}
+
+static void scrub_raid6_stripe(device_extension* Vcb, chunk* c, scrub_context_raid56* context, UINT64 stripe_start, UINT64 bit_start, UINT64 num) {
+    ULONG sectors_per_stripe = c->chunk_item->stripe_length / Vcb->superblock.sector_size, i;
+    UINT16 stripe, parity1 = (bit_start + num + c->chunk_item->num_stripes - 2) % c->chunk_item->num_stripes;
+    UINT16 parity2 = (parity1 + 1) % c->chunk_item->num_stripes;
+    UINT64 off, stripeoff;
+    
+    stripe = (parity1 + 2) % c->chunk_item->num_stripes;
+    off = (bit_start + num - stripe_start) * sectors_per_stripe * (c->chunk_item->num_stripes - 2);
+    stripeoff = num * sectors_per_stripe;
+    
+    RtlCopyMemory(context->parity_scratch, &context->stripes[parity1].buf[num * c->chunk_item->stripe_length], c->chunk_item->stripe_length);
+    RtlZeroMemory(context->parity_scratch2, c->chunk_item->stripe_length);
+    
+    while (stripe != parity1) {
+        RtlClearAllBits(&context->stripes[stripe].error);
+        
+        for (i = 0; i < sectors_per_stripe; i++) {
+            if (RtlCheckBit(&context->alloc, off)) {
+                if (RtlCheckBit(&context->is_tree, off)) {
+                    tree_header* th = (tree_header*)&context->stripes[stripe].buf[stripeoff * Vcb->superblock.sector_size];
+                    UINT64 addr = c->offset + (stripe_start * (c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length) + (off * Vcb->superblock.sector_size);
+                    UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+                    
+                    if (crc32 != *((UINT32*)th->csum) || th->address != addr) {
+                        RtlSetBits(&context->stripes[stripe].error, i, Vcb->superblock.node_size / Vcb->superblock.sector_size);
+                        log_device_error(Vcb, c->devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                    }
+                    
+                    off += Vcb->superblock.node_size / Vcb->superblock.sector_size;
+                    stripeoff += Vcb->superblock.node_size / Vcb->superblock.sector_size;
+                    i += (Vcb->superblock.node_size / Vcb->superblock.sector_size) - 1;
+                    
+                    continue;
+                } else if (RtlCheckBit(&context->has_csum, off)) {
+                    UINT32 crc32 = ~calc_crc32c(0xffffffff, context->stripes[stripe].buf + (stripeoff * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
+                    
+                    if (crc32 != context->csum[off]) {
+                        RtlSetBit(&context->stripes[stripe].error, i);
+                        log_device_error(Vcb, c->devices[stripe], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                    }
+                }
+            }
+            
+            off++;
+            stripeoff++;
+        }
+        
+        do_xor(context->parity_scratch, &context->stripes[stripe].buf[num * c->chunk_item->stripe_length], c->chunk_item->stripe_length);
+        
+        stripe = (stripe + 1) % c->chunk_item->num_stripes;
+        stripeoff = num * sectors_per_stripe;
+    }
+    
+    // check parity 1
+    
+    RtlClearAllBits(&context->stripes[parity1].error);
+    
+    for (i = 0; i < sectors_per_stripe; i++) {
+        ULONG o, j;
+        
+        o = i * Vcb->superblock.sector_size;
+        for (j = 0; j < Vcb->superblock.sector_size; j++) { // FIXME - use SSE
+            if (context->parity_scratch[o] != 0) {
+                RtlSetBit(&context->stripes[parity1].error, i);
+                break;
+            }
+            o++;
+        }
+    }
+    
+    // check parity 2
+    
+    stripe = parity1 == 0 ? (c->chunk_item->num_stripes - 1) : (parity1 - 1);
+    
+    while (stripe != parity2) {
+        galois_double(context->parity_scratch2, c->chunk_item->stripe_length);
+        do_xor(context->parity_scratch2, &context->stripes[stripe].buf[num * c->chunk_item->stripe_length], c->chunk_item->stripe_length);
+        
+        stripe = stripe == 0 ? (c->chunk_item->num_stripes - 1) : (stripe - 1);
+    }
+    
+    RtlClearAllBits(&context->stripes[parity2].error);
+    
+    for (i = 0; i < sectors_per_stripe; i++) {
+        if (RtlCompareMemory(&context->stripes[parity2].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                             &context->parity_scratch2[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size) != Vcb->superblock.sector_size)
+            RtlSetBit(&context->stripes[parity2].error, i);
+    }
+    
+    // log and fix errors
+    
+    for (i = 0; i < sectors_per_stripe; i++) {
+        ULONG num_errors = 0;
+        UINT64 bad_stripe1, bad_stripe2, bad_off1, bad_off2;
+        BOOL alloc = FALSE;
+        
+        stripe = (parity1 + 2) % c->chunk_item->num_stripes;
+        off = ((bit_start + num - stripe_start) * sectors_per_stripe * (c->chunk_item->num_stripes - 2)) + i;
+        
+        while (stripe != parity1) {
+            if (RtlCheckBit(&context->alloc, off)) {
+                alloc = TRUE;
+                
+                if (RtlCheckBit(&context->stripes[stripe].error, i)) {
+                    if (num_errors == 0) {
+                        bad_stripe1 = stripe;
+                        bad_off1 = off;
+                    } else if (num_errors == 1) {
+                        bad_stripe2 = stripe;
+                        bad_off2 = off;
+                    }
+                    num_errors++;
+                }
+            }
+            
+            off += sectors_per_stripe;
+            stripe = (stripe + 1) % c->chunk_item->num_stripes;
+        }
+        
+        if (!alloc)
+            continue;
+        
+        if (num_errors == 0 && !RtlCheckBit(&context->stripes[parity1].error, i) && !RtlCheckBit(&context->stripes[parity2].error, i)) // everything fine
+            continue;
+        
+        if (num_errors == 0) { // parity error
+            UINT64 addr;
+            
+            if (RtlCheckBit(&context->stripes[parity1].error, i)) {
+                do_xor(&context->stripes[parity1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                       &context->parity_scratch[i * Vcb->superblock.sector_size],
+                       Vcb->superblock.sector_size);
+                
+                bad_off1 = ((bit_start + num - stripe_start) * sectors_per_stripe * (c->chunk_item->num_stripes - 2)) + i;
+                addr = c->offset + (stripe_start * (c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length) + (bad_off1 * Vcb->superblock.sector_size);
+                
+                context->stripes[parity1].rewrite = TRUE;
+                
+                log_error(Vcb, addr, c->devices[parity1]->devitem.dev_id, FALSE, TRUE, TRUE);
+                log_device_error(Vcb, c->devices[parity1], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+            }
+            
+            if (RtlCheckBit(&context->stripes[parity2].error, i)) {
+                RtlCopyMemory(&context->stripes[parity2].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                              &context->parity_scratch2[i * Vcb->superblock.sector_size],
+                              Vcb->superblock.sector_size);
+                
+                bad_off1 = ((bit_start + num - stripe_start) * sectors_per_stripe * (c->chunk_item->num_stripes - 2)) + i;
+                addr = c->offset + (stripe_start * (c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length) + (bad_off1 * Vcb->superblock.sector_size);
+                
+                context->stripes[parity2].rewrite = TRUE;
+                
+                log_error(Vcb, addr, c->devices[parity2]->devitem.dev_id, FALSE, TRUE, TRUE);
+                log_device_error(Vcb, c->devices[parity2], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+            }
+        } else if (num_errors == 1) {
+            UINT32 crc32a, crc32b, len, stripe_num, bad_stripe_num;
+            UINT64 addr = c->offset + (stripe_start * (c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length) + (bad_off1 * Vcb->superblock.sector_size);
+            UINT8* scratch;
+            
+            len = RtlCheckBit(&context->is_tree, bad_off1)? Vcb->superblock.node_size : Vcb->superblock.sector_size;
+            
+            do_xor(&context->parity_scratch[i * Vcb->superblock.sector_size],
+                   &context->stripes[bad_stripe1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)], len);
+            
+            scratch = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
+            if (!scratch) {
+                ERR("out of memory\n");
+                return;
+            }
+            
+            RtlZeroMemory(scratch, len);
+            
+            stripe = parity1 == 0 ? (c->chunk_item->num_stripes - 1) : (parity1 - 1);
+    
+            stripe_num = c->chunk_item->num_stripes - 3;
+            while (stripe != parity2) {
+                galois_double(scratch, len);
+                
+                if (stripe != bad_stripe1)
+                    do_xor(scratch, &context->stripes[stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)], len);
+                else
+                    bad_stripe_num = stripe_num;
+                
+                stripe = stripe == 0 ? (c->chunk_item->num_stripes - 1) : (stripe - 1);
+                stripe_num--;
+            }
+            
+            do_xor(scratch, &context->stripes[parity2].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)], len);
+            
+            if (bad_stripe_num != 0)
+                galois_divpower(scratch, bad_stripe_num, len);
+            
+            if (RtlCheckBit(&context->is_tree, bad_off1)) {
+                tree_header *th1, *th2;
+                
+                th1 = (tree_header*)&context->parity_scratch[i * Vcb->superblock.sector_size];
+                th2 = (tree_header*)scratch;
+                crc32a = ~calc_crc32c(0xffffffff, (UINT8*)&th1->fs_uuid, Vcb->superblock.node_size - sizeof(th1->csum));
+                crc32b = ~calc_crc32c(0xffffffff, (UINT8*)&th2->fs_uuid, Vcb->superblock.node_size - sizeof(th2->csum));
+                
+                if ((crc32a == *((UINT32*)th1->csum) && th1->address == addr) || (crc32b == *((UINT32*)th2->csum) && th2->address == addr)) {
+                    if (crc32a != *((UINT32*)th1->csum) || th1->address != addr) {
+                        RtlCopyMemory(&context->stripes[bad_stripe1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                      scratch, Vcb->superblock.node_size);
+                        
+                        // fix parity 1
+                        
+                        stripe = (parity1 + 2) % c->chunk_item->num_stripes;
+                        
+                        RtlCopyMemory(&context->stripes[parity1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                      &context->stripes[stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                      Vcb->superblock.node_size);
+                        
+                        stripe = (stripe + 1) % c->chunk_item->num_stripes;
+                        
+                        while (stripe != parity1) {
+                            do_xor(&context->stripes[parity1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                   &context->stripes[stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                   Vcb->superblock.node_size);
+                            
+                            stripe = (stripe + 1) % c->chunk_item->num_stripes;
+                        }
+                        
+                        context->stripes[parity1].rewrite = TRUE;
+                    
+                        log_error(Vcb, addr, c->devices[parity1]->devitem.dev_id, FALSE, TRUE, TRUE);
+                        log_device_error(Vcb, c->devices[parity1], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                    } else {
+                        RtlCopyMemory(&context->stripes[bad_stripe1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                      &context->parity_scratch[i * Vcb->superblock.sector_size], Vcb->superblock.node_size);
+                        
+                        if (crc32b != *((UINT32*)th2->csum) || th2->address != addr) {
+                            // fix parity 2
+                            stripe = parity1 == 0 ? (c->chunk_item->num_stripes - 1) : (parity1 - 1);
+    
+                            RtlCopyMemory(&context->stripes[parity2].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                          &context->stripes[stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                          Vcb->superblock.node_size);
+                            
+                            stripe = stripe == 0 ? (c->chunk_item->num_stripes - 1) : (stripe - 1);
+                            
+                            while (stripe != parity2) {
+                                galois_double(&context->stripes[parity2].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)], Vcb->superblock.node_size);
+                                
+                                do_xor(&context->stripes[parity2].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                       &context->stripes[stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                       Vcb->superblock.node_size);
+                                
+                                stripe = stripe == 0 ? (c->chunk_item->num_stripes - 1) : (stripe - 1);
+                            }
+                            
+                            context->stripes[parity2].rewrite = TRUE;
+                    
+                            log_error(Vcb, addr, c->devices[parity2]->devitem.dev_id, FALSE, TRUE, TRUE);
+                            log_device_error(Vcb, c->devices[parity2], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                        }
+                    }
+                    
+                    context->stripes[bad_stripe1].rewrite = TRUE;
+                    
+                    RtlClearBits(&context->stripes[bad_stripe1].error, i + 1, (Vcb->superblock.node_size / Vcb->superblock.sector_size) - 1);
+                    
+                    log_error(Vcb, addr, c->devices[bad_stripe1]->devitem.dev_id, TRUE, TRUE, FALSE);
+                } else
+                    log_error(Vcb, addr, c->devices[bad_stripe1]->devitem.dev_id, TRUE, FALSE, FALSE);
+            } else {
+                crc32a = ~calc_crc32c(0xffffffff, &context->parity_scratch[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size);
+                crc32b = ~calc_crc32c(0xffffffff, scratch, Vcb->superblock.sector_size);
+                
+                if (crc32a == context->csum[bad_off1] || crc32b == context->csum[bad_off1]) {
+                    if (crc32a != context->csum[bad_off1]) {
+                        RtlCopyMemory(&context->stripes[bad_stripe1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                      scratch, Vcb->superblock.sector_size);
+                        
+                        // fix parity 1
+                        
+                        stripe = (parity1 + 2) % c->chunk_item->num_stripes;
+                        
+                        RtlCopyMemory(&context->stripes[parity1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                      &context->stripes[stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                      Vcb->superblock.sector_size);
+                        
+                        stripe = (stripe + 1) % c->chunk_item->num_stripes;
+                        
+                        while (stripe != parity1) {
+                            do_xor(&context->stripes[parity1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                   &context->stripes[stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                   Vcb->superblock.sector_size);
+                            
+                            stripe = (stripe + 1) % c->chunk_item->num_stripes;
+                        }
+                        
+                        context->stripes[parity1].rewrite = TRUE;
+                    
+                        log_error(Vcb, addr, c->devices[parity1]->devitem.dev_id, FALSE, TRUE, TRUE);
+                        log_device_error(Vcb, c->devices[parity1], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                    } else {
+                        RtlCopyMemory(&context->stripes[bad_stripe1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                      &context->parity_scratch[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size);
+                        
+                        if (crc32b != context->csum[bad_off1]) {
+                            // fix parity 2
+                            stripe = parity1 == 0 ? (c->chunk_item->num_stripes - 1) : (parity1 - 1);
+    
+                            RtlCopyMemory(&context->stripes[parity2].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                        &context->stripes[stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                        Vcb->superblock.sector_size);
+                            
+                            stripe = stripe == 0 ? (c->chunk_item->num_stripes - 1) : (stripe - 1);
+                            
+                            while (stripe != parity2) {
+                                galois_double(&context->stripes[parity2].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)], Vcb->superblock.sector_size);
+                                
+                                do_xor(&context->stripes[parity2].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                       &context->stripes[stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                       Vcb->superblock.sector_size);
+                                
+                                stripe = stripe == 0 ? (c->chunk_item->num_stripes - 1) : (stripe - 1);
+                            }
+                            
+                            context->stripes[parity2].rewrite = TRUE;
+                    
+                            log_error(Vcb, addr, c->devices[parity2]->devitem.dev_id, FALSE, TRUE, TRUE);
+                            log_device_error(Vcb, c->devices[parity2], BTRFS_DEV_STAT_CORRUPTION_ERRORS);
+                        }
+                    }
+                    
+                    context->stripes[bad_stripe1].rewrite = TRUE;
+                    
+                    log_error(Vcb, addr, c->devices[bad_stripe1]->devitem.dev_id, FALSE, TRUE, FALSE);
+                } else
+                    log_error(Vcb, addr, c->devices[bad_stripe1]->devitem.dev_id, FALSE, FALSE, FALSE);
+            }
+            
+            ExFreePool(scratch);
+        } else if (num_errors == 2) {
+            UINT16 x, y, k;
+            UINT64 addr;
+            UINT32 len = (RtlCheckBit(&context->is_tree, bad_off1) || RtlCheckBit(&context->is_tree, bad_off2)) ? Vcb->superblock.node_size : Vcb->superblock.sector_size;
+            UINT8 gyx, gx, denom, a, b, *p, *q, *pxy, *qxy;
+            UINT32 j;
+            
+            stripe = parity1 == 0 ? (c->chunk_item->num_stripes - 1) : (parity1 - 1);
+            
+            // put qxy in parity_scratch
+            // put pxy in parity_scratch2
+            
+            k = c->chunk_item->num_stripes - 3;
+            if (stripe == bad_stripe1 || stripe == bad_stripe2) {
+                RtlZeroMemory(&context->parity_scratch[i * Vcb->superblock.sector_size], len);
+                RtlZeroMemory(&context->parity_scratch2[i * Vcb->superblock.sector_size], len);
+                
+                if (stripe == bad_stripe1)
+                    x = k;
+                else
+                    y = k;
+            } else {
+                RtlCopyMemory(&context->parity_scratch[i * Vcb->superblock.sector_size],
+                              &context->stripes[stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)], len);
+                RtlCopyMemory(&context->parity_scratch2[i * Vcb->superblock.sector_size],
+                              &context->stripes[stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)], len);
+            }
+            
+            stripe = stripe == 0 ? (c->chunk_item->num_stripes - 1) : (stripe - 1);
+            
+            k--;
+            do {
+                galois_double(&context->parity_scratch[i * Vcb->superblock.sector_size], len);
+                
+                if (stripe != bad_stripe1 && stripe != bad_stripe2) {
+                    do_xor(&context->parity_scratch[i * Vcb->superblock.sector_size],
+                           &context->stripes[stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)], len);
+                    do_xor(&context->parity_scratch2[i * Vcb->superblock.sector_size],
+                           &context->stripes[stripe].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)], len);
+                } else if (stripe == bad_stripe1)
+                    x = k;
+                else if (stripe == bad_stripe2)
+                    y = k;
+                
+                stripe = stripe == 0 ? (c->chunk_item->num_stripes - 1) : (stripe - 1);
+                k--;
+            } while (stripe != parity2);
+            
+            gyx = gpow2(y > x ? (y-x) : (255-x+y));
+            gx = gpow2(255-x);
+
+            denom = gdiv(1, gyx ^ 1);
+            a = gmul(gyx, denom);
+            b = gmul(gx, denom);
+            
+            p = &context->stripes[parity1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)];
+            q = &context->stripes[parity2].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)];
+            pxy = &context->parity_scratch2[i * Vcb->superblock.sector_size];
+            qxy = &context->parity_scratch[i * Vcb->superblock.sector_size]; 
+            
+            for (j = 0; j < len; j++) {
+                *qxy = gmul(a, *p ^ *pxy) ^ gmul(b, *q ^ *qxy);
+                
+                p++;
+                q++;
+                pxy++;
+                qxy++;
+            }
+            
+            do_xor(&context->parity_scratch2[i * Vcb->superblock.sector_size], &context->parity_scratch[i * Vcb->superblock.sector_size], len);
+            do_xor(&context->parity_scratch2[i * Vcb->superblock.sector_size], &context->stripes[parity1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)], len);
+            
+            addr = c->offset + (stripe_start * (c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length) + (bad_off1 * Vcb->superblock.sector_size);
+            
+            if (RtlCheckBit(&context->is_tree, bad_off1)) {
+                tree_header* th = (tree_header*)&context->parity_scratch[i * Vcb->superblock.sector_size];
+                UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+                
+                if (crc32 == *((UINT32*)th->csum) && th->address == addr) {
+                    RtlCopyMemory(&context->stripes[bad_stripe1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                  &context->parity_scratch[i * Vcb->superblock.sector_size], Vcb->superblock.node_size);
+                    
+                    context->stripes[bad_stripe1].rewrite = TRUE;
+                    
+                    RtlClearBits(&context->stripes[bad_stripe1].error, i + 1, (Vcb->superblock.node_size / Vcb->superblock.sector_size) - 1);
+                    
+                    log_error(Vcb, addr, c->devices[bad_stripe1]->devitem.dev_id, TRUE, TRUE, FALSE);
+                } else
+                    log_error(Vcb, addr, c->devices[bad_stripe1]->devitem.dev_id, TRUE, FALSE, FALSE);
+            } else {
+                UINT32 crc32 = ~calc_crc32c(0xffffffff, &context->parity_scratch[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size);
+                
+                if (crc32 == context->csum[bad_off1]) {
+                    RtlCopyMemory(&context->stripes[bad_stripe1].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                  &context->parity_scratch[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size);
+                    
+                    context->stripes[bad_stripe1].rewrite = TRUE;
+                    
+                    log_error(Vcb, addr, c->devices[bad_stripe1]->devitem.dev_id, FALSE, TRUE, FALSE);
+                } else
+                    log_error(Vcb, addr, c->devices[bad_stripe1]->devitem.dev_id, FALSE, FALSE, FALSE);
+            }
+            
+            addr = c->offset + (stripe_start * (c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length) + (bad_off2 * Vcb->superblock.sector_size);
+            
+            if (RtlCheckBit(&context->is_tree, bad_off2)) {
+                tree_header* th = (tree_header*)&context->parity_scratch2[i * Vcb->superblock.sector_size];
+                UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum));
+                
+                if (crc32 == *((UINT32*)th->csum) && th->address == addr) {
+                    RtlCopyMemory(&context->stripes[bad_stripe2].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                  &context->parity_scratch2[i * Vcb->superblock.sector_size], Vcb->superblock.node_size);
+                    
+                    context->stripes[bad_stripe2].rewrite = TRUE;
+                    
+                    RtlClearBits(&context->stripes[bad_stripe2].error, i + 1, (Vcb->superblock.node_size / Vcb->superblock.sector_size) - 1);
+                    
+                    log_error(Vcb, addr, c->devices[bad_stripe2]->devitem.dev_id, TRUE, TRUE, FALSE);
+                } else
+                    log_error(Vcb, addr, c->devices[bad_stripe2]->devitem.dev_id, TRUE, FALSE, FALSE);
+            } else {
+                UINT32 crc32 = ~calc_crc32c(0xffffffff, &context->parity_scratch2[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size);
+                
+                if (crc32 == context->csum[bad_off2]) {
+                    RtlCopyMemory(&context->stripes[bad_stripe2].buf[(num * c->chunk_item->stripe_length) + (i * Vcb->superblock.sector_size)],
+                                  &context->parity_scratch2[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size);
+                    
+                    context->stripes[bad_stripe2].rewrite = TRUE;
+                    
+                    log_error(Vcb, addr, c->devices[bad_stripe2]->devitem.dev_id, FALSE, TRUE, FALSE);
+                } else
+                    log_error(Vcb, addr, c->devices[bad_stripe2]->devitem.dev_id, FALSE, FALSE, FALSE);
+            }
+        } else {
+            stripe = (parity2 + 1) % c->chunk_item->num_stripes;
+            off = ((bit_start + num - stripe_start) * sectors_per_stripe * (c->chunk_item->num_stripes - 2)) + i;
+            
+            while (stripe != parity1) {
+                if (RtlCheckBit(&context->alloc, off)) {
+                    if (RtlCheckBit(&context->stripes[stripe].error, i)) {
+                        UINT64 addr = c->offset + (stripe_start * (c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length) + (off * Vcb->superblock.sector_size);
+
+                        log_error(Vcb, addr, c->devices[stripe]->devitem.dev_id, RtlCheckBit(&context->is_tree, off), FALSE, FALSE);
+                    }
+                }
+                
+                off += sectors_per_stripe;
+                stripe = (stripe + 1) % c->chunk_item->num_stripes;
+            }
+        }
+    }
+}
+
+static NTSTATUS scrub_chunk_raid56_stripe_run(device_extension* Vcb, chunk* c, UINT64 stripe_start, UINT64 stripe_end) {
+    NTSTATUS Status;
+    KEY searchkey;
+    traverse_ptr tp;
+    BOOL b;
+    UINT64 run_start, run_end, num_sectors, full_stripe_len, max_read, stripe;
+    ULONG arrlen, *allocarr, *csumarr, *treearr, num_parity_stripes = c->chunk_item->type & BLOCK_FLAG_RAID6 ? 2 : 1;
+    scrub_context_raid56 context;
+    UINT16 i;
+    CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+    
+    TRACE("(%p, %p, %llx, %llx)\n", Vcb, c, stripe_start, stripe_end);
+    
+    full_stripe_len = (c->chunk_item->num_stripes - num_parity_stripes) * c->chunk_item->stripe_length;
+    run_start = c->offset + (stripe_start * full_stripe_len);
+    run_end = c->offset + ((stripe_end + 1) * full_stripe_len);
+    
+    searchkey.obj_id = run_start;
+    searchkey.obj_type = TYPE_METADATA_ITEM;
+    searchkey.offset = 0xffffffffffffffff;
+    
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, NULL);
+    if (!NT_SUCCESS(Status)) {
+        ERR("find_item returned %08x\n", Status);
+        return Status;
+    }
+    
+    num_sectors = (stripe_end - stripe_start + 1) * full_stripe_len / Vcb->superblock.sector_size;
+    arrlen = sector_align((num_sectors / 8) + 1, sizeof(ULONG));
+    
+    allocarr = ExAllocatePoolWithTag(PagedPool, arrlen, ALLOC_TAG);
+    if (!allocarr) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    treearr = ExAllocatePoolWithTag(PagedPool, arrlen, ALLOC_TAG);
+    if (!treearr) {
+        ERR("out of memory\n");
+        ExFreePool(allocarr);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlInitializeBitMap(&context.alloc, allocarr, num_sectors);
+    RtlClearAllBits(&context.alloc);
+    
+    RtlInitializeBitMap(&context.is_tree, treearr, num_sectors);
+    RtlClearAllBits(&context.is_tree);
+    
+    context.parity_scratch = ExAllocatePoolWithTag(PagedPool, c->chunk_item->stripe_length, ALLOC_TAG);
+    if (!context.parity_scratch) {
+        ERR("out of memory\n");
+        ExFreePool(allocarr);
+        ExFreePool(treearr);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    if (c->chunk_item->type & BLOCK_FLAG_DATA) {
+        csumarr = ExAllocatePoolWithTag(PagedPool, arrlen, ALLOC_TAG);
+        if (!csumarr) {
+            ERR("out of memory\n");
+            ExFreePool(allocarr);
+            ExFreePool(treearr);
+            ExFreePool(context.parity_scratch);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlInitializeBitMap(&context.has_csum, csumarr, num_sectors);
+        RtlClearAllBits(&context.has_csum);
+        
+        context.csum = ExAllocatePoolWithTag(PagedPool, num_sectors * sizeof(UINT32), ALLOC_TAG);
+        if (!context.csum) {
+            ERR("out of memory\n");
+            ExFreePool(allocarr);
+            ExFreePool(treearr);
+            ExFreePool(context.parity_scratch);
+            ExFreePool(csumarr);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+    
+    if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
+        context.parity_scratch2 = ExAllocatePoolWithTag(PagedPool, c->chunk_item->stripe_length, ALLOC_TAG);
+        if (!context.parity_scratch2) {
+            ERR("out of memory\n");
+            ExFreePool(allocarr);
+            ExFreePool(treearr);
+            ExFreePool(context.parity_scratch);
+            
+            if (c->chunk_item->type & BLOCK_FLAG_DATA) {
+                ExFreePool(csumarr);
+                ExFreePool(context.csum);
+            }
+            
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+    
+    do {
+        traverse_ptr next_tp;
+        
+        if (tp.item->key.obj_id >= run_end)
+            break;
+        
+        if (tp.item->key.obj_type == TYPE_EXTENT_ITEM || tp.item->key.obj_type == TYPE_METADATA_ITEM) {
+            UINT64 size = tp.item->key.obj_type == TYPE_METADATA_ITEM ? Vcb->superblock.node_size : tp.item->key.offset;
+            
+            if (tp.item->key.obj_id + size > run_start) {
+                UINT64 extent_start = max(run_start, tp.item->key.obj_id);
+                UINT64 extent_end = min(tp.item->key.obj_id + size, run_end);
+                BOOL extent_is_tree = FALSE;
+                
+                RtlSetBits(&context.alloc, (extent_start - run_start) / Vcb->superblock.sector_size, (extent_end - extent_start) / Vcb->superblock.sector_size);
+                
+                if (tp.item->key.obj_type == TYPE_METADATA_ITEM)
+                    extent_is_tree = TRUE;
+                else {
+                    EXTENT_ITEM* ei = (EXTENT_ITEM*)tp.item->data;
+                    
+                    if (tp.item->size < sizeof(EXTENT_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(EXTENT_ITEM));
+                        Status = STATUS_INTERNAL_ERROR;
+                        goto end;
+                    }
+                    
+                    if (ei->flags & EXTENT_ITEM_TREE_BLOCK)
+                        extent_is_tree = TRUE;
+                }
+                
+                if (extent_is_tree)
+                    RtlSetBits(&context.is_tree, (extent_start - run_start) / Vcb->superblock.sector_size, (extent_end - extent_start) / Vcb->superblock.sector_size);
+                else if (c->chunk_item->type & BLOCK_FLAG_DATA) {
+                    traverse_ptr tp2;
+                    BOOL b2;
+                    
+                    searchkey.obj_id = EXTENT_CSUM_ID;
+                    searchkey.obj_type = TYPE_EXTENT_CSUM;
+                    searchkey.offset = extent_start;
+                    
+                    Status = find_item(Vcb, Vcb->checksum_root, &tp2, &searchkey, FALSE, NULL);
+                    if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) {
+                        ERR("find_item returned %08x\n", Status);
+                        goto end;
+                    }
+                    
+                    do {
+                        traverse_ptr next_tp2;
+                        
+                        if (tp2.item->key.offset >= extent_end)
+                            break;
+                        
+                        if (tp2.item->key.offset >= extent_start) {
+                            UINT64 csum_start = max(extent_start, tp2.item->key.offset);
+                            UINT64 csum_end = min(extent_end, tp2.item->key.offset + (tp2.item->size * Vcb->superblock.sector_size / sizeof(UINT32)));
+                            
+                            RtlSetBits(&context.has_csum, (csum_start - run_start) / Vcb->superblock.sector_size, (csum_end - csum_start) / Vcb->superblock.sector_size);
+                            
+                            RtlCopyMemory(&context.csum[(csum_start - run_start) / Vcb->superblock.sector_size],
+                                          tp2.item->data + ((csum_start - tp2.item->key.offset) * sizeof(UINT32) / Vcb->superblock.sector_size),
+                                          (csum_end - csum_start) * sizeof(UINT32) / Vcb->superblock.sector_size);
+                        }
+                        
+                        b2 = find_next_item(Vcb, &tp2, &next_tp2, FALSE, NULL);
+        
+                        if (b2)
+                            tp2 = next_tp2;
+                    } while (b2);
+                }
+            }
+        }
+        
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, NULL);
+        
+        if (b)
+            tp = next_tp;
+    } while (b);
+    
+    context.stripes = ExAllocatePoolWithTag(PagedPool, sizeof(scrub_context_raid56_stripe) * c->chunk_item->num_stripes, ALLOC_TAG);
+    if (!context.stripes) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto end;
+    }
+    
+    max_read = min(1048576 / c->chunk_item->stripe_length, stripe_end - stripe_start + 1); // only process 1 MB of data at a time
+    
+    for (i = 0; i < c->chunk_item->num_stripes; i++) {
+        context.stripes[i].buf = ExAllocatePoolWithTag(PagedPool, max_read * c->chunk_item->stripe_length, ALLOC_TAG);
+        if (!context.stripes[i].buf) {
+            UINT64 j;
+            
+            ERR("out of memory\n");
+            
+            for (j = 0; j < i; j++) {
+                ExFreePool(context.stripes[j].buf);
+            }
+            ExFreePool(context.stripes);
+            
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
+        }
+        
+        context.stripes[i].errorarr = ExAllocatePoolWithTag(PagedPool, sector_align(((c->chunk_item->stripe_length / Vcb->superblock.sector_size) / 8) + 1, sizeof(ULONG)), ALLOC_TAG);
+        if (!context.stripes[i].errorarr) {
+            UINT64 j;
+            
+            ERR("out of memory\n");
+            
+            ExFreePool(context.stripes[i].buf);
+            
+            for (j = 0; j < i; j++) {
+                ExFreePool(context.stripes[j].buf);
+            }
+            ExFreePool(context.stripes);
+            
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
+        }
+        
+        RtlInitializeBitMap(&context.stripes[i].error, context.stripes[i].errorarr, c->chunk_item->stripe_length / Vcb->superblock.sector_size);
+        
+        context.stripes[i].context = &context;
+        context.stripes[i].rewrite = FALSE;
+    }
+    
+    stripe = stripe_start;
+    
+    chunk_lock_range(Vcb, c, run_start, run_end - run_start);
+    
+    do {
+        ULONG read_stripes;
+        
+        if (max_read < stripe_end + 1 - stripe)
+            read_stripes = max_read;
+        else
+            read_stripes = stripe_end + 1 - stripe;
+        
+        context.stripes_left = c->chunk_item->num_stripes;
+        
+        // read megabyte by megabyte
+        for (i = 0; i < c->chunk_item->num_stripes; i++) {
+            PIO_STACK_LOCATION IrpSp;
+
+            context.stripes[i].Irp = IoAllocateIrp(c->devices[i]->devobj->StackSize, FALSE);
+            
+            if (!context.stripes[i].Irp) {
+                ERR("IoAllocateIrp failed\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto end2;
+            }
+            
+            IrpSp = IoGetNextIrpStackLocation(context.stripes[i].Irp);
+            IrpSp->MajorFunction = IRP_MJ_READ;
+            
+            if (c->devices[i]->devobj->Flags & DO_BUFFERED_IO) {
+                context.stripes[i].Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, read_stripes * c->chunk_item->stripe_length, ALLOC_TAG);
+                if (!context.stripes[i].Irp->AssociatedIrp.SystemBuffer) {
+                    ERR("out of memory\n");
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+
+                context.stripes[i].Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
+
+                context.stripes[i].Irp->UserBuffer = context.stripes[i].buf;
+            } else if (c->devices[i]->devobj->Flags & DO_DIRECT_IO) {
+                context.stripes[i].Irp->MdlAddress = IoAllocateMdl(context.stripes[i].buf, read_stripes * c->chunk_item->stripe_length, FALSE, FALSE, NULL);
+                if (!context.stripes[i].Irp->MdlAddress) {
+                    ERR("IoAllocateMdl failed\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto end;
+                }
+                
+                MmProbeAndLockPages(context.stripes[i].Irp->MdlAddress, KernelMode, IoWriteAccess);
+            } else
+                context.stripes[i].Irp->UserBuffer = context.stripes[i].buf;
+            
+            context.stripes[i].offset = stripe * c->chunk_item->stripe_length;
+
+            IrpSp->Parameters.Read.Length = read_stripes * c->chunk_item->stripe_length;
+            IrpSp->Parameters.Read.ByteOffset.QuadPart = cis[i].offset + context.stripes[i].offset;
+            
+            context.stripes[i].Irp->UserIosb = &context.stripes[i].iosb;
+            
+            IoSetCompletionRoutine(context.stripes[i].Irp, scrub_read_completion_raid56, &context.stripes[i], TRUE, TRUE, TRUE);
+            
+            Vcb->scrub.data_scrubbed += read_stripes * c->chunk_item->stripe_length;
+        }
+        
+        KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
+        
+        for (i = 0; i < c->chunk_item->num_stripes; i++) {
+            IoCallDriver(c->devices[i]->devobj, context.stripes[i].Irp);
+        }
+        
+        KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+        
+        // return an error if any of the stripes returned an error
+        for (i = 0; i < c->chunk_item->num_stripes; i++) {
+            if (!NT_SUCCESS(context.stripes[i].iosb.Status)) {
+                Status = context.stripes[i].iosb.Status;
+                log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_READ_ERRORS);
+                goto end2;
+            }
+        }
+        
+        if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
+            for (i = 0; i < read_stripes; i++) {
+                scrub_raid6_stripe(Vcb, c, &context, stripe_start, stripe, i);
+            }
+        } else {
+            for (i = 0; i < read_stripes; i++) {
+                scrub_raid5_stripe(Vcb, c, &context, stripe_start, stripe, i);
+            }
+        }
+        stripe += read_stripes;
+        
+        for (i = 0; i < c->chunk_item->num_stripes; i++) {
+            if (context.stripes[i].Irp) {
+                if (c->devices[i]->devobj->Flags & DO_DIRECT_IO && context.stripes[i].Irp->MdlAddress) {
+                    MmUnlockPages(context.stripes[i].Irp->MdlAddress);
+                    IoFreeMdl(context.stripes[i].Irp->MdlAddress);
+                }
+                IoFreeIrp(context.stripes[i].Irp);
+                context.stripes[i].Irp = NULL;
+                
+                if (context.stripes[i].rewrite) {
+                    Status = write_data_phys(c->devices[i]->devobj, cis[i].offset + context.stripes[i].offset,
+                                             context.stripes[i].buf, read_stripes * c->chunk_item->stripe_length);
+                    
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("write_data_phys returned %08x\n", Status);
+                        log_device_error(Vcb, c->devices[i], BTRFS_DEV_STAT_WRITE_ERRORS);
+                        return Status;
+                    }
+                }
+            }
+        }
+    } while (stripe < stripe_end);
+    
+    chunk_unlock_range(Vcb, c, run_start, run_end - run_start);
+    
+    Status = STATUS_SUCCESS;
+    
+end2:
+    for (i = 0; i < c->chunk_item->num_stripes; i++) {
+        ExFreePool(context.stripes[i].buf);
+        ExFreePool(context.stripes[i].errorarr);
+    }
+    ExFreePool(context.stripes);
+    
+end:
+    ExFreePool(treearr);
+    ExFreePool(allocarr);
+    ExFreePool(context.parity_scratch);
+    
+    if (c->chunk_item->type & BLOCK_FLAG_RAID6)
+        ExFreePool(context.parity_scratch2);
+    
+    if (c->chunk_item->type & BLOCK_FLAG_DATA) {
+        ExFreePool(csumarr);
+        ExFreePool(context.csum);
+    }
+    
+    return Status;
+}
+
+static NTSTATUS scrub_chunk_raid56(device_extension* Vcb, chunk* c, UINT64* offset, BOOL* changed) {
+    NTSTATUS Status;
+    KEY searchkey;
+    traverse_ptr tp;
+    BOOL b;
+    UINT64 full_stripe_len, stripe, stripe_start, stripe_end, total_data = 0;
+    ULONG num_extents = 0, num_parity_stripes = c->chunk_item->type & BLOCK_FLAG_RAID6 ? 2 : 1;
+    
+    full_stripe_len = (c->chunk_item->num_stripes - num_parity_stripes) * c->chunk_item->stripe_length;
+    stripe = (*offset - c->offset) / full_stripe_len;
+    
+    *offset = c->offset + (stripe * full_stripe_len);
+    
+    searchkey.obj_id = *offset;
+    searchkey.obj_type = TYPE_METADATA_ITEM;
+    searchkey.offset = 0xffffffffffffffff;
+    
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, NULL);
+    if (!NT_SUCCESS(Status)) {
+        ERR("find_item returned %08x\n", Status);
+        return Status;
+    }
+    
+    *changed = FALSE;
+    
+    do {
+        traverse_ptr next_tp;
+        
+        if (tp.item->key.obj_id >= c->offset + c->chunk_item->size)
+            break;
+        
+        if (tp.item->key.obj_id >= *offset && (tp.item->key.obj_type == TYPE_EXTENT_ITEM || tp.item->key.obj_type == TYPE_METADATA_ITEM)) {
+            UINT64 size = tp.item->key.obj_type == TYPE_METADATA_ITEM ? Vcb->superblock.node_size : tp.item->key.offset;
+            
+            TRACE("%llx\n", tp.item->key.obj_id);
+            
+            if (size < Vcb->superblock.sector_size) {
+                ERR("extent %llx has size less than sector_size (%llx < %x)\n", tp.item->key.obj_id, Vcb->superblock.sector_size);
+                return STATUS_INTERNAL_ERROR;
+            }
+            
+            stripe = (tp.item->key.obj_id - c->offset) / full_stripe_len;
+            
+            if (*changed) {
+                if (stripe > stripe_end + 1) {
+                    Status = scrub_chunk_raid56_stripe_run(Vcb, c, stripe_start, stripe_end);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("scrub_chunk_raid56_stripe_run returned %08x\n", Status);
+                        return Status;
+                    }
+                    
+                    stripe_start = stripe;
+                }
+            } else
+                stripe_start = stripe;
+            
+            stripe_end = (tp.item->key.obj_id + size - 1 - c->offset) / full_stripe_len;
+
+            *changed = TRUE;
+            
+            total_data += size;
+            num_extents++;
+            
+            // only do so much at a time
+            if (num_extents >= 64 || total_data >= 0x8000000) // 128 MB
+                break;
+        }
+        
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, NULL);
+        
+        if (b)
+            tp = next_tp;
+    } while (b);
+    
+    if (*changed) {
+        Status = scrub_chunk_raid56_stripe_run(Vcb, c, stripe_start, stripe_end);
+        if (!NT_SUCCESS(Status)) {
+            ERR("scrub_chunk_raid56_stripe_run returned %08x\n", Status);
+            return Status;
+        }
+        
+        *offset = c->offset + ((stripe_end + 1) * full_stripe_len);
+    }
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS scrub_chunk(device_extension* Vcb, chunk* c, UINT64* offset, BOOL* changed) {
+    NTSTATUS Status;
+    KEY searchkey;
+    traverse_ptr tp;
+    BOOL b = FALSE, tree_run = FALSE;
+    ULONG type, num_extents = 0;
+    UINT64 total_data = 0, tree_run_start, tree_run_end;
+    
+    TRACE("chunk %llx\n", c->offset);
+    
+    ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
+    
+    if (c->chunk_item->type & BLOCK_FLAG_DUPLICATE)
+        type = BLOCK_FLAG_DUPLICATE;
+    else if (c->chunk_item->type & BLOCK_FLAG_RAID0)
+        type = BLOCK_FLAG_RAID0;
+    else if (c->chunk_item->type & BLOCK_FLAG_RAID1)
+        type = BLOCK_FLAG_DUPLICATE;
+    else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
+        type = BLOCK_FLAG_RAID10;
+    else if (c->chunk_item->type & BLOCK_FLAG_RAID5) {
+        Status = scrub_chunk_raid56(Vcb, c, offset, changed);
+        goto end;
+    } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
+        Status = scrub_chunk_raid56(Vcb, c, offset, changed);
+        goto end;
+    } else // SINGLE
+        type = BLOCK_FLAG_DUPLICATE;
+    
+    searchkey.obj_id = *offset;
+    searchkey.obj_type = TYPE_METADATA_ITEM;
+    searchkey.offset = 0xffffffffffffffff;
+    
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, NULL);
+    if (!NT_SUCCESS(Status)) {
+        ERR("error - find_item returned %08x\n", Status);
+        goto end;
+    }
+    
+    do {
+        traverse_ptr next_tp;
+        
+        if (tp.item->key.obj_id >= c->offset + c->chunk_item->size)
+            break;
+        
+        if (tp.item->key.obj_id >= *offset && (tp.item->key.obj_type == TYPE_EXTENT_ITEM || tp.item->key.obj_type == TYPE_METADATA_ITEM)) {
+            UINT64 size = tp.item->key.obj_type == TYPE_METADATA_ITEM ? Vcb->superblock.node_size : tp.item->key.offset;
+            BOOL is_tree;
+            UINT32* csum = NULL;
+            RTL_BITMAP bmp;
+            ULONG* bmparr = NULL;
+            
+            TRACE("%llx\n", tp.item->key.obj_id);
+            
+            is_tree = FALSE;
+            
+            if (tp.item->key.obj_type == TYPE_METADATA_ITEM)
+                is_tree = TRUE;
+            else {
+                EXTENT_ITEM* ei = (EXTENT_ITEM*)tp.item->data;
+                
+                if (tp.item->size < sizeof(EXTENT_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(EXTENT_ITEM));
+                    Status = STATUS_INTERNAL_ERROR;
+                    goto end;
+                }
+                
+                if (ei->flags & EXTENT_ITEM_TREE_BLOCK)
+                    is_tree = TRUE;
+            }
+            
+            if (size < Vcb->superblock.sector_size) {
+                ERR("extent %llx has size less than sector_size (%llx < %x)\n", tp.item->key.obj_id, Vcb->superblock.sector_size);
+                Status = STATUS_INTERNAL_ERROR;
+                goto end;
+            }
+            
+            // load csum
+            if (!is_tree) {
+                traverse_ptr tp2;
+                
+                csum = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * size / Vcb->superblock.sector_size, ALLOC_TAG);
+                if (!csum) {
+                    ERR("out of memory\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto end;
+                }
+                
+                bmparr = ExAllocatePoolWithTag(PagedPool, sector_align(((size / Vcb->superblock.sector_size) >> 3) + 1, sizeof(ULONG)), ALLOC_TAG);
+                if (!bmparr) {
+                    ERR("out of memory\n");
+                    ExFreePool(csum);
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto end;
+                }
+                    
+                RtlInitializeBitMap(&bmp, bmparr, size / Vcb->superblock.sector_size);
+                RtlSetAllBits(&bmp); // 1 = no csum, 0 = csum
+                
+                searchkey.obj_id = EXTENT_CSUM_ID;
+                searchkey.obj_type = TYPE_EXTENT_CSUM;
+                searchkey.offset = tp.item->key.obj_id;
+                
+                Status = find_item(Vcb, Vcb->checksum_root, &tp2, &searchkey, FALSE, NULL);
+                if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) {
+                    ERR("find_item returned %08x\n", Status);
+                    ExFreePool(csum);
+                    ExFreePool(bmparr);
+                    goto end;
+                }
+                
+                if (Status != STATUS_NOT_FOUND) {
+                    do {
+                        traverse_ptr next_tp2;
+                        
+                        if (tp2.item->key.obj_type == TYPE_EXTENT_CSUM) {
+                            if (tp2.item->key.offset >= tp.item->key.obj_id + size)
+                                break;
+                            else if (tp2.item->size >= sizeof(UINT32) && tp2.item->key.offset + (tp2.item->size * Vcb->superblock.sector_size / sizeof(UINT32)) >= tp.item->key.obj_id) {
+                                UINT64 cs = max(tp.item->key.obj_id, tp2.item->key.offset);
+                                UINT64 ce = min(tp.item->key.obj_id + size, tp2.item->key.offset + (tp2.item->size * Vcb->superblock.sector_size / sizeof(UINT32)));
+                                
+                                RtlCopyMemory(csum + ((cs - tp.item->key.obj_id) / Vcb->superblock.sector_size),
+                                              tp2.item->data + ((cs - tp2.item->key.offset) * sizeof(UINT32) / Vcb->superblock.sector_size),
+                                              (ce - cs) * sizeof(UINT32) / Vcb->superblock.sector_size);
+                                
+                                RtlClearBits(&bmp, (cs - tp.item->key.obj_id) / Vcb->superblock.sector_size, (ce - cs) / Vcb->superblock.sector_size);
+                                
+                                if (ce == tp.item->key.obj_id + size)
+                                    break;
+                            }
+                        }
+                        
+                        if (find_next_item(Vcb, &tp2, &next_tp2, FALSE, NULL))
+                            tp2 = next_tp2;
+                        else
+                            break;
+                    } while (TRUE);
+                }
+            }
+            
+            if (tree_run) {
+                if (!is_tree || tp.item->key.obj_id > tree_run_end) {
+                    Status = scrub_extent(Vcb, c, type, tree_run_start, tree_run_end - tree_run_start, NULL);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("scrub_extent returned %08x\n", Status);
+                        goto end;
+                    }
+                    
+                    if (!is_tree)
+                        tree_run = FALSE;
+                    else {
+                        tree_run_start = tp.item->key.obj_id;
+                        tree_run_end = tp.item->key.obj_id + Vcb->superblock.node_size;
+                    }
+                } else
+                    tree_run_end = tp.item->key.obj_id + Vcb->superblock.node_size;
+            } else if (is_tree) {
+                tree_run = TRUE;
+                tree_run_start = tp.item->key.obj_id;
+                tree_run_end = tp.item->key.obj_id + Vcb->superblock.node_size;
+            }
+            
+            if (!is_tree) {
+                Status = scrub_data_extent(Vcb, c, tp.item->key.obj_id, type, csum, &bmp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("scrub_data_extent returned %08x\n", Status);
+                    goto end;
+                }
+            }
+            
+            if (!is_tree) {
+                ExFreePool(csum);
+                ExFreePool(bmparr);
+            }
+            
+            *offset = tp.item->key.obj_id + size;
+            *changed = TRUE;
+            
+            total_data += size;
+            num_extents++;
+            
+            // only do so much at a time
+            if (num_extents >= 64 || total_data >= 0x8000000) // 128 MB
+                break;
+        }
+        
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, NULL);
+        
+        if (b)
+            tp = next_tp;
+    } while (b);
+    
+    if (tree_run) {
+        Status = scrub_extent(Vcb, c, type, tree_run_start, tree_run_end - tree_run_start, NULL);
+        if (!NT_SUCCESS(Status)) {
+            ERR("scrub_extent returned %08x\n", Status);
+            goto end;
+        }
+    }
+    
+    Status = STATUS_SUCCESS;
+    
+end:
+    ExReleaseResourceLite(&Vcb->tree_lock);
+    
+    return Status;
+}
+
+#ifdef __REACTOS__
+static void NTAPI scrub_thread(void* context) {
+#else
+static void scrub_thread(void* context) {
+#endif
+    device_extension* Vcb = context;
+    LIST_ENTRY chunks, *le;
+    NTSTATUS Status;
+    LARGE_INTEGER time;
+    
+    KeInitializeEvent(&Vcb->scrub.finished, NotificationEvent, FALSE);
+    
+    InitializeListHead(&chunks);
+    
+    ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+
+    if (Vcb->need_write && !Vcb->readonly)
+        Status = do_write(Vcb, NULL);
+    else
+        Status = STATUS_SUCCESS;
+    
+    free_trees(Vcb);
+    
+    if (!NT_SUCCESS(Status)) {
+        ERR("do_write returned %08x\n", Status);
+        Vcb->scrub.error = Status;
+        goto end;
+    }
+    
+    ExConvertExclusiveToSharedLite(&Vcb->tree_lock);
+    
+    ExAcquireResourceExclusiveLite(&Vcb->scrub.stats_lock, TRUE);
+    
+    KeQuerySystemTime(&Vcb->scrub.start_time);
+    Vcb->scrub.finish_time.QuadPart = 0;
+    Vcb->scrub.resume_time.QuadPart = Vcb->scrub.start_time.QuadPart;
+    Vcb->scrub.duration.QuadPart = 0;
+    Vcb->scrub.total_chunks = 0;
+    Vcb->scrub.chunks_left = 0;
+    Vcb->scrub.data_scrubbed = 0;
+    Vcb->scrub.num_errors = 0;
+    
+    while (!IsListEmpty(&Vcb->scrub.errors)) {
+        scrub_error* err = CONTAINING_RECORD(RemoveHeadList(&Vcb->scrub.errors), scrub_error, list_entry);
+        ExFreePool(err);
+    }
+    
+    ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE);
+    
+    le = Vcb->chunks.Flink;
+    while (le != &Vcb->chunks) {
+        chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
+        
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+               
+        if (!c->readonly) {
+            InsertTailList(&chunks, &c->list_entry_balance);
+            Vcb->scrub.total_chunks++;
+            Vcb->scrub.chunks_left++;
+        }
+
+        ExReleaseResourceLite(&c->lock);
+        
+        le = le->Flink;
+    }
+    
+    ExReleaseResourceLite(&Vcb->chunk_lock);
+    
+    ExReleaseResource(&Vcb->scrub.stats_lock);
+
+    ExReleaseResourceLite(&Vcb->tree_lock);
+    
+    while (!IsListEmpty(&chunks)) {
+        chunk* c = CONTAINING_RECORD(RemoveHeadList(&chunks), chunk, list_entry_balance);
+        UINT64 offset = c->offset;
+        BOOL changed;
+        
+        c->reloc = TRUE;
+        
+        KeWaitForSingleObject(&Vcb->scrub.event, Executive, KernelMode, FALSE, NULL);
+        
+        if (!Vcb->scrub.stopping) {
+            do {
+                changed = FALSE;
+                
+                Status = scrub_chunk(Vcb, c, &offset, &changed);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("scrub_chunk returned %08x\n", Status);
+                    Vcb->scrub.stopping = TRUE;
+                    Vcb->scrub.error = Status;
+                    break;
+                }
+                
+                if (offset == c->offset + c->chunk_item->size || Vcb->scrub.stopping)
+                    break;
+                
+                KeWaitForSingleObject(&Vcb->scrub.event, Executive, KernelMode, FALSE, NULL);
+            } while (changed);
+        }
+        
+        ExAcquireResourceExclusiveLite(&Vcb->scrub.stats_lock, TRUE);
+        
+        if (!Vcb->scrub.stopping)
+            Vcb->scrub.chunks_left--;
+        
+        if (IsListEmpty(&chunks))
+            KeQuerySystemTime(&Vcb->scrub.finish_time);
+        
+        ExReleaseResource(&Vcb->scrub.stats_lock);
+        
+        c->reloc = FALSE;
+        c->list_entry_balance.Flink = NULL;
+    }
+    
+    KeQuerySystemTime(&time);
+    Vcb->scrub.duration.QuadPart += time.QuadPart - Vcb->scrub.resume_time.QuadPart;
+    
+end:
+    ZwClose(Vcb->scrub.thread);
+    Vcb->scrub.thread = NULL;
+    
+    KeSetEvent(&Vcb->scrub.finished, 0, FALSE);
+}
+
+NTSTATUS start_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode) {
+    NTSTATUS Status;
+    
+    if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
+        return STATUS_PRIVILEGE_NOT_HELD;
+    
+    if (Vcb->locked) {
+        WARN("cannot start scrub while locked\n");
+        return STATUS_DEVICE_NOT_READY;
+    }
+    
+    if (Vcb->balance.thread) {
+        WARN("cannot start scrub while balance running\n");
+        return STATUS_DEVICE_NOT_READY;
+    }
+    
+    if (Vcb->scrub.thread) {
+        WARN("scrub already running\n");
+        return STATUS_DEVICE_NOT_READY;
+    }
+    
+    if (Vcb->readonly)
+        return STATUS_MEDIA_WRITE_PROTECTED;
+    
+    Vcb->scrub.stopping = FALSE;
+    Vcb->scrub.paused = FALSE;
+    Vcb->scrub.error = STATUS_SUCCESS;
+    KeInitializeEvent(&Vcb->scrub.event, NotificationEvent, !Vcb->scrub.paused);
+    
+    Status = PsCreateSystemThread(&Vcb->scrub.thread, 0, NULL, NULL, NULL, scrub_thread, Vcb);
+    if (!NT_SUCCESS(Status)) {
+        ERR("PsCreateSystemThread returned %08x\n", Status);
+        return Status;
+    }
+    
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS query_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode, void* data, ULONG length) {
+    btrfs_query_scrub* bqs = (btrfs_query_scrub*)data;
+    ULONG len;
+    NTSTATUS Status;
+    LIST_ENTRY* le;
+    btrfs_scrub_error* bse = NULL;
+    
+    if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
+        return STATUS_PRIVILEGE_NOT_HELD;
+    
+    if (length < offsetof(btrfs_query_scrub, errors))
+        return STATUS_BUFFER_TOO_SMALL;
+    
+    ExAcquireResourceSharedLite(&Vcb->scrub.stats_lock, TRUE);
+    
+    if (Vcb->scrub.thread && Vcb->scrub.chunks_left > 0)
+        bqs->status = Vcb->scrub.paused ? BTRFS_SCRUB_PAUSED : BTRFS_SCRUB_RUNNING;
+    else
+        bqs->status = BTRFS_SCRUB_STOPPED;
+    
+    bqs->start_time.QuadPart = Vcb->scrub.start_time.QuadPart;
+    bqs->finish_time.QuadPart = Vcb->scrub.finish_time.QuadPart;
+    bqs->chunks_left = Vcb->scrub.chunks_left;
+    bqs->total_chunks = Vcb->scrub.total_chunks;
+    bqs->data_scrubbed = Vcb->scrub.data_scrubbed;
+    
+    bqs->duration = Vcb->scrub.duration.QuadPart;
+    
+    if (bqs->status == BTRFS_SCRUB_RUNNING) {
+        LARGE_INTEGER time;
+        
+        KeQuerySystemTime(&time);
+        bqs->duration += time.QuadPart - Vcb->scrub.resume_time.QuadPart;
+    }
+    
+    bqs->error = Vcb->scrub.error;
+    
+    bqs->num_errors = Vcb->scrub.num_errors;
+    
+    len = length - offsetof(btrfs_query_scrub, errors);
+    
+    le = Vcb->scrub.errors.Flink;
+    while (le != &Vcb->scrub.errors) {
+        scrub_error* err = CONTAINING_RECORD(le, scrub_error, list_entry);
+        ULONG errlen;
+        
+        if (err->is_metadata)
+            errlen = offsetof(btrfs_scrub_error, metadata.firstitem) + sizeof(KEY);
+        else
+            errlen = offsetof(btrfs_scrub_error, data.filename) + err->data.filename_length;
+
+        if (len < errlen) {
+            Status = STATUS_BUFFER_OVERFLOW;
+            goto end;
+        }
+        
+        if (!bse)
+            bse = &bqs->errors;
+        else {
+            ULONG lastlen;
+            
+            if (bse->is_metadata)
+                lastlen = offsetof(btrfs_scrub_error, metadata.firstitem) + sizeof(KEY);
+            else
+                lastlen = offsetof(btrfs_scrub_error, data.filename) + bse->data.filename_length;
+            
+            bse->next_entry = lastlen;
+            bse = (btrfs_scrub_error*)(((UINT8*)bse) + lastlen);
+        }
+        
+        bse->next_entry = 0;
+        bse->address = err->address;
+        bse->device = err->device;
+        bse->recovered = err->recovered;
+        bse->is_metadata = err->is_metadata;
+        bse->parity = err->parity;
+        
+        if (err->is_metadata) {
+            bse->metadata.root = err->metadata.root;
+            bse->metadata.level = err->metadata.level;
+            bse->metadata.firstitem = err->metadata.firstitem;
+        } else {
+            bse->data.subvol = err->data.subvol;
+            bse->data.offset = err->data.offset;
+            bse->data.filename_length = err->data.filename_length;
+            RtlCopyMemory(bse->data.filename, err->data.filename, err->data.filename_length);
+        }
+        
+        len -= errlen;
+        le = le->Flink;
+    }
+    
+    Status = STATUS_SUCCESS;
+    
+end:
+    ExReleaseResourceLite(&Vcb->scrub.stats_lock);
+    
+    return Status;
+}
+
+NTSTATUS pause_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode) {
+    LARGE_INTEGER time;
+    
+    if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
+        return STATUS_PRIVILEGE_NOT_HELD;
+    
+    if (!Vcb->scrub.thread)
+        return STATUS_DEVICE_NOT_READY;
+    
+    if (Vcb->scrub.paused)
+        return STATUS_DEVICE_NOT_READY;
+    
+    Vcb->scrub.paused = TRUE;
+    KeClearEvent(&Vcb->scrub.event);
+    
+    KeQuerySystemTime(&time);
+    Vcb->scrub.duration.QuadPart += time.QuadPart - Vcb->scrub.resume_time.QuadPart;
+    
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS resume_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode) {
+    if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
+        return STATUS_PRIVILEGE_NOT_HELD;
+    
+    if (!Vcb->scrub.thread)
+        return STATUS_DEVICE_NOT_READY;
+    
+    if (!Vcb->scrub.paused)
+        return STATUS_DEVICE_NOT_READY;
+    
+    Vcb->scrub.paused = FALSE;
+    KeSetEvent(&Vcb->scrub.event, 0, FALSE);
+    
+    KeQuerySystemTime(&Vcb->scrub.resume_time);
+    
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS stop_scrub(device_extension* Vcb, KPROCESSOR_MODE processor_mode) {
+    if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
+        return STATUS_PRIVILEGE_NOT_HELD;
+    
+    if (!Vcb->scrub.thread)
+        return STATUS_DEVICE_NOT_READY;
+    
+    Vcb->scrub.paused = FALSE;
+    Vcb->scrub.stopping = TRUE;
+    KeSetEvent(&Vcb->scrub.event, 0, FALSE);
+    
+    return STATUS_SUCCESS;
+}

Modification de propriétés sur drivers/filesystems/btrfs/scrub.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: drivers/filesystems/btrfs/search.c
===================================================================
--- drivers/filesystems/btrfs/search.c	(révision 74430)
+++ drivers/filesystems/btrfs/search.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -23,164 +23,22 @@
 #include <mountmgr.h>
 #include <windef.h>
 #endif
+#include <ntddstor.h>
+#include <ntdddisk.h>
 
 #include <initguid.h>
-#ifndef __REACTOS__
-#include <winioctl.h>
-#endif
 #include <wdmguid.h>
 
-extern LIST_ENTRY volumes;
-extern ERESOURCE volumes_lock;
-extern LIST_ENTRY pnp_disks;
+extern ERESOURCE volume_list_lock;
+extern LIST_ENTRY volume_list;
 
-static NTSTATUS create_part0(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT DeviceObject, PUNICODE_STRING devpath,
-                             PUNICODE_STRING nameus, BTRFS_UUID* uuid) {
-    PDEVICE_OBJECT newdevobj;
-    UNICODE_STRING name;
-    NTSTATUS Status;
-    part0_device_extension* p0de;
-    
-    static const WCHAR part0_suffix[] = L"Btrfs";
-    
-    name.Length = name.MaximumLength = devpath->Length + (wcslen(part0_suffix) * sizeof(WCHAR));
-    name.Buffer = ExAllocatePoolWithTag(PagedPool, name.Length, ALLOC_TAG);
-    if (!name.Buffer) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    RtlCopyMemory(name.Buffer, devpath->Buffer, devpath->Length);
-    RtlCopyMemory(&name.Buffer[devpath->Length / sizeof(WCHAR)], part0_suffix, wcslen(part0_suffix) * sizeof(WCHAR));
-    
-    Status = IoCreateDevice(DriverObject, sizeof(part0_device_extension), &name, FILE_DEVICE_DISK, FILE_DEVICE_SECURE_OPEN, FALSE, &newdevobj);
-    if (!NT_SUCCESS(Status)) {
-        ERR("IoCreateDevice returned %08x\n", Status);
-        ExFreePool(name.Buffer);
-        return Status;
-    }
-    
-    p0de = newdevobj->DeviceExtension;
-    p0de->type = VCB_TYPE_PARTITION0;
-    p0de->devobj = DeviceObject;
-    RtlCopyMemory(&p0de->uuid, uuid, sizeof(BTRFS_UUID));
-    
-    p0de->name.Length = name.Length;
-    p0de->name.MaximumLength = name.MaximumLength;
-    p0de->name.Buffer = ExAllocatePoolWithTag(PagedPool, p0de->name.MaximumLength, ALLOC_TAG);
-    
-    if (!p0de->name.Buffer) {
-        ERR("out of memory\b");
-        ExFreePool(name.Buffer);
-        ExFreePool(p0de->name.Buffer);
-        IoDeleteDevice(newdevobj);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    RtlCopyMemory(p0de->name.Buffer, name.Buffer, name.Length);
-    
-    ObReferenceObject(DeviceObject);
-    
-    newdevobj->StackSize = DeviceObject->StackSize + 1;
-    newdevobj->SectorSize = DeviceObject->SectorSize;
-    
-    newdevobj->Flags |= DO_DIRECT_IO;
-    newdevobj->Flags &= ~DO_DEVICE_INITIALIZING;
-    
-    *nameus = name;
-    
-    return STATUS_SUCCESS;
-}
+typedef void (*pnp_callback)(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath);
 
-void add_volume(PDEVICE_OBJECT mountmgr, PUNICODE_STRING us) {
-    ULONG tnsize;
-    MOUNTMGR_TARGET_NAME* tn;
-    KEVENT Event;
-    IO_STATUS_BLOCK IoStatusBlock;
-    PIRP Irp;
-    NTSTATUS Status;
-    ULONG mmdltsize;
-    MOUNTMGR_DRIVE_LETTER_TARGET* mmdlt;
-    MOUNTMGR_DRIVE_LETTER_INFORMATION mmdli;
-    
-    TRACE("found BTRFS volume\n");
-    
-    tnsize = sizeof(MOUNTMGR_TARGET_NAME) - sizeof(WCHAR) + us->Length;
-    tn = ExAllocatePoolWithTag(NonPagedPool, tnsize, ALLOC_TAG);
-    if (!tn) {
-        ERR("out of memory\n");
-        return;
-    }
-    
-    tn->DeviceNameLength = us->Length;
-    RtlCopyMemory(tn->DeviceName, us->Buffer, tn->DeviceNameLength);
-    
-    KeInitializeEvent(&Event, NotificationEvent, FALSE);
-    Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION,
-                                        mountmgr, tn, tnsize, 
-                                        NULL, 0, FALSE, &Event, &IoStatusBlock);
-    if (!Irp) {
-        ERR("%.*S: IoBuildDeviceIoControlRequest 1 failed\n", us->Length / sizeof(WCHAR), us->Buffer);
-        ExFreePool(tn);
-        return;
-    }
+extern PDEVICE_OBJECT devobj;
 
-    Status = IoCallDriver(mountmgr, Irp);
-    if (Status == STATUS_PENDING) {
-        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-        Status = IoStatusBlock.Status;
-    }
-
-    if (!NT_SUCCESS(Status)) {
-        ERR("%.*S: IoCallDriver 1 returned %08x\n", us->Length / sizeof(WCHAR), us->Buffer, Status);
-        return;
-    }
-    
-    ExFreePool(tn);
-    
-    mmdltsize = offsetof(MOUNTMGR_DRIVE_LETTER_TARGET, DeviceName[0]) + us->Length;
-    
-    mmdlt = ExAllocatePoolWithTag(NonPagedPool, mmdltsize, ALLOC_TAG);
-    if (!mmdlt) {
-        ERR("out of memory\n");
-        return;
-    }
-    
-    mmdlt->DeviceNameLength = us->Length;
-    RtlCopyMemory(&mmdlt->DeviceName, us->Buffer, us->Length);
-    TRACE("mmdlt = %.*S\n", mmdlt->DeviceNameLength / sizeof(WCHAR), mmdlt->DeviceName);
-    
-    KeInitializeEvent(&Event, NotificationEvent, FALSE);
-    Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER,
-                                        mountmgr, mmdlt, mmdltsize, 
-                                        &mmdli, sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION), FALSE, &Event, &IoStatusBlock);
-    if (!Irp) {
-        ERR("%.*S: IoBuildDeviceIoControlRequest 2 failed\n", us->Length / sizeof(WCHAR), us->Buffer);
-        return;
-    }
-
-    Status = IoCallDriver(mountmgr, Irp);
-    if (Status == STATUS_PENDING) {
-        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-        Status = IoStatusBlock.Status;
-    }
-
-    if (!NT_SUCCESS(Status)) {
-        ERR("%.*S: IoCallDriver 2 returned %08x\n", us->Length / sizeof(WCHAR), us->Buffer, Status);
-    } else
-        TRACE("DriveLetterWasAssigned = %u, CurrentDriveLetter = %c\n", mmdli.DriveLetterWasAssigned, mmdli.CurrentDriveLetter);
-    
-    ExFreePool(mmdlt);
-}
-
-static void STDCALL test_vol(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath, DWORD disk_num, DWORD part_num, LIST_ENTRY* volumes) {
-    KEVENT Event;
-    PIRP Irp;
-    IO_STATUS_BLOCK IoStatusBlock;
+static void STDCALL test_vol(PDEVICE_OBJECT mountmgr, PDEVICE_OBJECT DeviceObject, PUNICODE_STRING devpath,
+                             DWORD disk_num, DWORD part_num, PUNICODE_STRING pnp_name, UINT64 length) {
     NTSTATUS Status;
-    PFILE_OBJECT FileObject;
-    PDEVICE_OBJECT DeviceObject;
-    LARGE_INTEGER Offset;
     ULONG toread;
     UINT8* data = NULL;
     UINT32 sector_size;
@@ -187,12 +45,6 @@
     
     TRACE("%.*S\n", devpath->Length / sizeof(WCHAR), devpath->Buffer);
     
-    Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
-    if (!NT_SUCCESS(Status)) {
-        ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
-        return;
-    }
-
     sector_size = DeviceObject->SectorSize;
     
     if (sector_size == 0) {
@@ -200,7 +52,7 @@
         IO_STATUS_BLOCK iosb;
         
         Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
-                        &geometry, sizeof(DISK_GEOMETRY), TRUE, &iosb);
+                           &geometry, sizeof(DISK_GEOMETRY), TRUE, &iosb);
         
         if (!NT_SUCCESS(Status)) {
             ERR("%.*S had a sector size of 0, and IOCTL_DISK_GET_DRIVE_GEOMETRY returned %08x\n",
@@ -221,10 +73,6 @@
         }
     }
     
-    KeInitializeEvent(&Event, NotificationEvent, FALSE);
-
-    Offset.QuadPart = superblock_addrs[0];
-    
     toread = sector_align(sizeof(superblock), sector_size);
     data = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG);
     if (!data) {
@@ -231,182 +79,84 @@
         ERR("out of memory\n");
         goto deref;
     }
-
-    Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, data, toread, &Offset, &Event, &IoStatusBlock);
     
-    if (!Irp) {
-        ERR("IoBuildSynchronousFsdRequest failed\n");
-        goto deref;
-    }
+    Status = sync_read_phys(DeviceObject, superblock_addrs[0], toread, data, TRUE);
 
-    Status = IoCallDriver(DeviceObject, Irp);
-
-    if (Status == STATUS_PENDING) {
-        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-        Status = IoStatusBlock.Status;
-    }
-
-    if (NT_SUCCESS(Status) && IoStatusBlock.Information > 0 && ((superblock*)data)->magic == BTRFS_MAGIC) {
-        int i;
-        GET_LENGTH_INFORMATION gli;
+    if (NT_SUCCESS(Status) && ((superblock*)data)->magic == BTRFS_MAGIC) {
         superblock* sb = (superblock*)data;
-        volume* v = ExAllocatePoolWithTag(PagedPool, sizeof(volume), ALLOC_TAG);
-        
-        if (!v) {
-            ERR("out of memory\n");
-            goto deref;
-        }
-        
-        Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
-                           &gli, sizeof(gli), TRUE, NULL);
-        if (!NT_SUCCESS(Status)) {
-            ERR("error reading length information: %08x\n", Status);
-            ExFreePool(v);
-            goto deref;
-        }
-        
-        if (part_num == 0) {
-            UNICODE_STRING us3;
-            
-            Status = create_part0(DriverObject, DeviceObject, devpath, &us3, &sb->dev_item.device_uuid);
-            
-            if (!NT_SUCCESS(Status)) {
-                ERR("create_part0 returned %08x\n", Status);
-                ExFreePool(v);
-                goto deref;
-            }
-            
-            v->devpath = us3;
-        } else {
-            v->devpath.Length = v->devpath.MaximumLength = devpath->Length;
-            v->devpath.Buffer = ExAllocatePoolWithTag(PagedPool, v->devpath.Length, ALLOC_TAG);
-            
-            if (!v->devpath.Buffer) {
-                ERR("out of memory\n");
-                ExFreePool(v);
-                goto deref;
-            }
-            
-            RtlCopyMemory(v->devpath.Buffer, devpath->Buffer, v->devpath.Length);
-        }
-        
-        RtlCopyMemory(&v->fsuuid, &sb->uuid, sizeof(BTRFS_UUID));
-        RtlCopyMemory(&v->devuuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID));
-        v->devnum = sb->dev_item.dev_id;
-        v->processed = FALSE;
-        v->length = gli.Length.QuadPart;
-        v->gen1 = sb->generation;
-        v->gen2 = 0;
-        v->seeding = sb->flags & BTRFS_SUPERBLOCK_FLAGS_SEEDING ? TRUE : FALSE;
-        v->disk_num = disk_num;
-        v->part_num = part_num;
-        
-        ExAcquireResourceExclusiveLite(&volumes_lock, TRUE);
-        InsertTailList(volumes, &v->list_entry);
-        ExReleaseResourceLite(&volumes_lock);
-        
-        i = 1;
-        while (superblock_addrs[i] != 0 && superblock_addrs[i] + toread <= v->length) {
-            KeInitializeEvent(&Event, NotificationEvent, FALSE);
-            
-            Offset.QuadPart = superblock_addrs[i];
-            
-            Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, data, toread, &Offset, &Event, &IoStatusBlock);
-            
-            if (!Irp) {
-                ERR("IoBuildSynchronousFsdRequest failed\n");
-                goto deref;
-            }
+        UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
 
-            Status = IoCallDriver(DeviceObject, Irp);
-
-            if (Status == STATUS_PENDING) {
-                KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-                Status = IoStatusBlock.Status;
-            }
+        if (crc32 != *((UINT32*)sb->checksum))
+            ERR("checksum error on superblock\n");
+        else {
+            TRACE("volume found\n");
             
-            if (NT_SUCCESS(Status)) {
-                UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+            if (length >= superblock_addrs[1] + toread) {
+                ULONG i = 1;
                 
-                if (crc32 != *((UINT32*)sb->checksum))
-                    WARN("superblock %u CRC error\n", i);
-                else if (sb->generation > v->gen1) {
-                    v->gen2 = v->gen1;
-                    v->gen1 = sb->generation;
+                superblock* sb2 = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG);
+                if (!sb2) {
+                    ERR("out of memory\n");
+                    goto deref;
                 }
-            } else {
-                ERR("got error %08x while reading superblock %u\n", Status, i);
+                
+                while (superblock_addrs[i] > 0 && length >= superblock_addrs[i] + toread) {
+                    Status = sync_read_phys(DeviceObject, superblock_addrs[i], toread, (PUCHAR)sb2, TRUE);
+                    
+                    if (NT_SUCCESS(Status) && sb2->magic == BTRFS_MAGIC) {
+                        crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb2->uuid, (ULONG)sizeof(superblock) - sizeof(sb2->checksum));
+                        
+                        if (crc32 == *((UINT32*)sb2->checksum) && sb2->generation > sb->generation)
+                            RtlCopyMemory(sb, sb2, toread);
+                    }
+                    
+                    i++;
+                }
+                
+                ExFreePool(sb2);
             }
             
-            i++;
+            DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
+            add_volume_device(sb, mountmgr, pnp_name, length, disk_num, part_num, devpath);
         }
-        
-        TRACE("volume found\n");
-        TRACE("gen1 = %llx, gen2 = %llx\n", v->gen1, v->gen2);
-        TRACE("FS uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
-              v->fsuuid.uuid[0], v->fsuuid.uuid[1], v->fsuuid.uuid[2], v->fsuuid.uuid[3], v->fsuuid.uuid[4], v->fsuuid.uuid[5], v->fsuuid.uuid[6], v->fsuuid.uuid[7],
-              v->fsuuid.uuid[8], v->fsuuid.uuid[9], v->fsuuid.uuid[10], v->fsuuid.uuid[11], v->fsuuid.uuid[12], v->fsuuid.uuid[13], v->fsuuid.uuid[14], v->fsuuid.uuid[15]);
-        TRACE("device uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
-              v->devuuid.uuid[0], v->devuuid.uuid[1], v->devuuid.uuid[2], v->devuuid.uuid[3], v->devuuid.uuid[4], v->devuuid.uuid[5], v->devuuid.uuid[6], v->devuuid.uuid[7],
-              v->devuuid.uuid[8], v->devuuid.uuid[9], v->devuuid.uuid[10], v->devuuid.uuid[11], v->devuuid.uuid[12], v->devuuid.uuid[13], v->devuuid.uuid[14], v->devuuid.uuid[15]);
-        TRACE("device number %llx\n", v->devnum);
     }
     
 deref:
     if (data)
         ExFreePool(data);
-    
-    ObDereferenceObject(FileObject);
 }
 
-void remove_drive_letter(PDEVICE_OBJECT mountmgr, volume* v) {
+NTSTATUS remove_drive_letter(PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath) {
     NTSTATUS Status;
-    KEVENT Event;
-    PIRP Irp;
     MOUNTMGR_MOUNT_POINT* mmp;
     ULONG mmpsize;
     MOUNTMGR_MOUNT_POINTS mmps1, *mmps2;
-    IO_STATUS_BLOCK IoStatusBlock;
     
-    mmpsize = sizeof(MOUNTMGR_MOUNT_POINT) + v->devpath.Length;
+    mmpsize = sizeof(MOUNTMGR_MOUNT_POINT) + devpath->Length;
     
     mmp = ExAllocatePoolWithTag(PagedPool, mmpsize, ALLOC_TAG);
     if (!mmp) {
         ERR("out of memory\n");
-        return;
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
     
     RtlZeroMemory(mmp, mmpsize);
     
     mmp->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
-    mmp->DeviceNameLength = v->devpath.Length;
-    RtlCopyMemory(&mmp[1], v->devpath.Buffer, v->devpath.Length);
+    mmp->DeviceNameLength = devpath->Length;
+    RtlCopyMemory(&mmp[1], devpath->Buffer, devpath->Length);
     
-    KeInitializeEvent(&Event, NotificationEvent, FALSE);
-    Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_DELETE_POINTS,
-                                        mountmgr, mmp, mmpsize, 
-                                        &mmps1, sizeof(MOUNTMGR_MOUNT_POINTS), FALSE, &Event, &IoStatusBlock);
-    if (!Irp) {
-        ERR("%.*S: IoBuildDeviceIoControlRequest 1 failed\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer);
-        ExFreePool(mmp);
-        return;
-    }
+    Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_DELETE_POINTS, mmp, mmpsize, &mmps1, sizeof(MOUNTMGR_MOUNT_POINTS), FALSE, NULL);
 
-    Status = IoCallDriver(mountmgr, Irp);
-    if (Status == STATUS_PENDING) {
-        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-        Status = IoStatusBlock.Status;
-    }
-
     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
-        ERR("%.*S: IoCallDriver 1  returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
+        ERR("IOCTL_MOUNTMGR_DELETE_POINTS 1 returned %08x\n", Status);
         ExFreePool(mmp);
-        return;
+        return Status;
     }
     
     if (Status != STATUS_BUFFER_OVERFLOW || mmps1.Size == 0) {
         ExFreePool(mmp);
-        return;
+        return STATUS_NOT_FOUND;
     }
     
     mmps2 = ExAllocatePoolWithTag(PagedPool, mmps1.Size, ALLOC_TAG);
@@ -413,130 +163,33 @@
     if (!mmps2) {
         ERR("out of memory\n");
         ExFreePool(mmp);
-        return;
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    KeInitializeEvent(&Event, NotificationEvent, FALSE);
-    Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_DELETE_POINTS,
-                                        mountmgr, mmp, mmpsize, 
-                                        mmps2, mmps1.Size, FALSE, &Event, &IoStatusBlock);
-    if (!Irp) {
-        ERR("%.*S: IoBuildDeviceIoControlRequest 2 failed\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer);
-        ExFreePool(mmps2);
-        ExFreePool(mmp);
-        return;
-    }
+    Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_DELETE_POINTS, mmp, mmpsize, mmps2, mmps1.Size, FALSE, NULL);
 
-    Status = IoCallDriver(mountmgr, Irp);
-    if (Status == STATUS_PENDING) {
-        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-        Status = IoStatusBlock.Status;
-    }
+    if (!NT_SUCCESS(Status))
+        ERR("IOCTL_MOUNTMGR_DELETE_POINTS 2 returned %08x\n", Status);
 
-    if (!NT_SUCCESS(Status)) {
-        ERR("%.*S: IoCallDriver 2 returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
-        ExFreePool(mmps2);
-        ExFreePool(mmp);
-        return;
-    }
-    
     ExFreePool(mmps2);
     ExFreePool(mmp);
-}
-
-static void refresh_mountmgr(PDEVICE_OBJECT mountmgr, LIST_ENTRY* volumes) {
-    LIST_ENTRY* le;
     
-    ExAcquireResourceExclusiveLite(&volumes_lock, TRUE);
-    
-    le = volumes->Flink;
-    while (le != volumes) {
-        volume* v = CONTAINING_RECORD(le, volume, list_entry);
-        
-        if (!v->processed) {
-            LIST_ENTRY* le2 = le;
-            volume* mountvol = v;
-            
-            while (le2 != volumes) {
-                volume* v2 = CONTAINING_RECORD(le2, volume, list_entry);
-                
-                if (RtlCompareMemory(&v2->fsuuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
-                    v2->processed = TRUE;
-                    
-                    if (v2->devnum < mountvol->devnum) {
-                        remove_drive_letter(mountmgr, mountvol);
-                        mountvol = v2;
-                    } else if (v2->devnum > mountvol->devnum)
-                        remove_drive_letter(mountmgr, v2);
-                }
-                
-                le2 = le2->Flink;
-            }
-            
-            add_volume(mountmgr, &mountvol->devpath);
-        }
-        
-        le = le->Flink;
-    }
-    
-    ExReleaseResourceLite(&volumes_lock);
+    return Status;
 }
 
-static void add_pnp_disk(ULONG disk_num, PUNICODE_STRING devpath) {
-    LIST_ENTRY* le;
-    pnp_disk* disk;
-    
-    le = pnp_disks.Flink;
-    while (le != &pnp_disks) {
-        disk = CONTAINING_RECORD(le, pnp_disk, list_entry);
-        
-        if (disk->devpath.Length == devpath->Length &&
-            RtlCompareMemory(disk->devpath.Buffer, devpath->Buffer, devpath->Length) == devpath->Length)
-            return;
-        
-        le = le->Flink;
-    }
-    
-    disk = ExAllocatePoolWithTag(PagedPool, sizeof(pnp_disk), ALLOC_TAG);
-    if (!disk) {
-        ERR("out of memory\n");
-        return;
-    }
-    
-    disk->devpath.Length = disk->devpath.MaximumLength = devpath->Length;
-    disk->devpath.Buffer = ExAllocatePoolWithTag(PagedPool, devpath->Length, ALLOC_TAG);
-    
-    if (!disk->devpath.Buffer) {
-        ERR("out of memory\n");
-        ExFreePool(disk);
-        return;
-    }
-    
-    RtlCopyMemory(disk->devpath.Buffer, devpath->Buffer, devpath->Length);
-    
-    disk->disk_num = disk_num;
-    
-    InsertTailList(&pnp_disks, &disk->list_entry);
-}
-
-static void disk_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
-    PFILE_OBJECT FileObject, FileObject2;
+void disk_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
+    PFILE_OBJECT FileObject, mountmgrfo;
     PDEVICE_OBJECT devobj, mountmgr;
     NTSTATUS Status;
     STORAGE_DEVICE_NUMBER sdn;
     ULONG dlisize;
-    DRIVE_LAYOUT_INFORMATION_EX* dli;
+    DRIVE_LAYOUT_INFORMATION_EX* dli = NULL;
     IO_STATUS_BLOCK iosb;
-    int i, num_parts = 0;
-    UNICODE_STRING devname, num, bspus, mmdevpath;
-    WCHAR devnamew[255], numw[20];
-    USHORT preflen;
+    GET_LENGTH_INFORMATION gli;
+    UNICODE_STRING mmdevpath;
+
+    UNUSED(DriverObject);
     
-    static WCHAR device_harddisk[] = L"\\Device\\Harddisk";
-    static WCHAR bs_partition[] = L"\\Partition";
-    
-    // FIXME - work with CD-ROMs and floppies(?)
-        
     Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &FileObject, &devobj);
     if (!NT_SUCCESS(Status)) {
         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
@@ -544,7 +197,7 @@
     }
     
     RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
-    Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject2, &mountmgr);
+    Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
     if (!NT_SUCCESS(Status)) {
         ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
         ObDereferenceObject(FileObject);
@@ -551,129 +204,418 @@
         return;
     }
     
-    Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
-                       &sdn, sizeof(STORAGE_DEVICE_NUMBER), TRUE, &iosb);
-    if (!NT_SUCCESS(Status)) {
-        ERR("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status);
-        goto end;
-    }
-    
-    ExAcquireResourceExclusiveLite(&volumes_lock, TRUE);
-    add_pnp_disk(sdn.DeviceNumber, devpath);
-    ExReleaseResourceLite(&volumes_lock);
-    
     dlisize = 0;
     
     do {
         dlisize += 1024;
+        
+        if (dli)
+            ExFreePool(dli);
+        
         dli = ExAllocatePoolWithTag(PagedPool, dlisize, ALLOC_TAG);
+        if (!dli) {
+            ERR("out of memory\n");
+            goto end;
+        }
     
         Status = dev_ioctl(devobj, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0,
                            dli, dlisize, TRUE, &iosb);
     } while (Status == STATUS_BUFFER_TOO_SMALL);
     
-    if (!NT_SUCCESS(Status)) {
+    // only consider disk as a potential filesystem if it has no partitions
+    if (NT_SUCCESS(Status) && dli->PartitionCount > 0) {
         ExFreePool(dli);
-        goto no_parts;
+        goto end;
     }
     
-    wcscpy(devnamew, device_harddisk);
-    devname.Buffer = devnamew;
-    devname.MaximumLength = sizeof(devnamew);
-    devname.Length = wcslen(device_harddisk) * sizeof(WCHAR);
+    ExFreePool(dli);
+    
+    Status = dev_ioctl(devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
+                        &gli, sizeof(gli), TRUE, NULL);
+    
+    if (!NT_SUCCESS(Status)) {
+        ERR("error reading length information: %08x\n", Status);
+        goto end;
+    }
 
-    num.Buffer = numw;
-    num.MaximumLength = sizeof(numw);
-    RtlIntegerToUnicodeString(sdn.DeviceNumber, 10, &num);
-    RtlAppendUnicodeStringToString(&devname, &num);
+    Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
+                       &sdn, sizeof(STORAGE_DEVICE_NUMBER), TRUE, NULL);
+    if (!NT_SUCCESS(Status)) {
+        TRACE("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status);
+        sdn.DeviceNumber = 0xffffffff;
+        sdn.PartitionNumber = 0;
+    } else
+        TRACE("DeviceType = %u, DeviceNumber = %u, PartitionNumber = %u\n", sdn.DeviceType, sdn.DeviceNumber, sdn.PartitionNumber);
+
+    test_vol(mountmgr, devobj, devpath, sdn.DeviceNumber, sdn.PartitionNumber, devpath, gli.Length.QuadPart);
     
-    bspus.Buffer = bs_partition;
-    bspus.Length = bspus.MaximumLength = wcslen(bs_partition) * sizeof(WCHAR);
-    RtlAppendUnicodeStringToString(&devname, &bspus);
+end:
+    ObDereferenceObject(FileObject);
+    ObDereferenceObject(mountmgrfo);
+}
+
+void remove_volume_child(volume_device_extension* vde, volume_child* vc, BOOL no_release_lock) {
+    NTSTATUS Status;
     
-    preflen = devname.Length;
+    if (vc->notification_entry)
+#ifndef __REACTOS__
+        IoUnregisterPlugPlayNotificationEx(vc->notification_entry);
+#else
+        IoUnregisterPlugPlayNotification(vc->notification_entry);
+#endif
     
-    for (i = 0; i < dli->PartitionCount; i++) {
-        if (dli->PartitionEntry[i].PartitionLength.QuadPart != 0 && dli->PartitionEntry[i].PartitionNumber != 0) {
-            devname.Length = preflen;
-            RtlIntegerToUnicodeString(dli->PartitionEntry[i].PartitionNumber, 10, &num);
-            RtlAppendUnicodeStringToString(&devname, &num);
+    ObDereferenceObject(vc->devobj);
+    ExFreePool(vc->pnp_name.Buffer);
+    RemoveEntryList(&vc->list_entry);
+    ExFreePool(vc);
+    
+    vde->children_loaded--;
+    
+    if (vde->mounted_device) {
+//         device_extension* Vcb = vde->mounted_device->DeviceExtension;
+// 
+        // FIXME - hangs
+//         Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT);
+//         if (!NT_SUCCESS(Status))
+//             WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status);
+        
+        Status = pnp_surprise_removal(vde->mounted_device, NULL);
+        if (!NT_SUCCESS(Status))
+            ERR("pnp_surprise_removal returned %08x\n", Status);
+    }
+    
+    Status = IoSetDeviceInterfaceState(&vde->bus_name, FALSE);
+    if (!NT_SUCCESS(Status))
+        WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
+    
+    if (vde->children_loaded > 0) {
+        UNICODE_STRING mmdevpath;
+        PFILE_OBJECT FileObject;
+        PDEVICE_OBJECT mountmgr;
+        LIST_ENTRY* le;
+        
+        RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
+        Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
+        if (!NT_SUCCESS(Status))
+            ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+        else {
+            le = vde->children.Flink;
             
-            test_vol(DriverObject, mountmgr, &devname, sdn.DeviceNumber, dli->PartitionEntry[i].PartitionNumber, &volumes);
+            while (le != &vde->children) {
+                volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+                
+                if (vc->had_drive_letter) { // re-add entry to mountmgr
+                    MOUNTDEV_NAME mdn;
+                    
+                    Status = dev_ioctl(vc->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), TRUE, NULL);
+                    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
+                        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
+                    else {
+                        MOUNTDEV_NAME* mdn2;
+                        ULONG mdnsize = offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
+                        
+                        mdn2 = ExAllocatePoolWithTag(PagedPool, mdnsize, ALLOC_TAG);
+                        if (!mdn2)
+                            ERR("out of memory\n");
+                        else {
+                            Status = dev_ioctl(vc->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, TRUE, NULL);
+                            if (!NT_SUCCESS(Status))
+                                ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
+                            else {
+                                UNICODE_STRING name;
+                                
+                                name.Buffer = mdn2->Name;
+                                name.Length = name.MaximumLength = mdn2->NameLength;
+                                
+                                Status = mountmgr_add_drive_letter(mountmgr, &name);
+                                if (!NT_SUCCESS(Status))
+                                    WARN("mountmgr_add_drive_letter returned %08x\n", Status);
+                            }
+                            
+                            ExFreePool(mdn2);
+                        }
+                    }
+                }
+                
+                le = le->Flink;
+            }
             
-            num_parts++;
+            ObDereferenceObject(FileObject);
         }
+        
+        if (vde->device->Characteristics & FILE_REMOVABLE_MEDIA) {
+            vde->device->Characteristics &= ~FILE_REMOVABLE_MEDIA;
+            
+            le = vde->children.Flink;
+            while (le != &vde->children) {
+                volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+                
+                if (vc->devobj->Characteristics & FILE_REMOVABLE_MEDIA) {
+                    vde->device->Characteristics |= FILE_REMOVABLE_MEDIA;
+                    break;
+                }
+                
+                le = le->Flink;
+            }
+        }
     }
     
-    ExFreePool(dli);
+    if (vde->children_loaded == 0) { // remove volume device
+        UNICODE_STRING mmdevpath;
+        PDEVICE_OBJECT mountmgr;
+        PFILE_OBJECT mountmgrfo;
+        
+        ExReleaseResourceLite(&vde->child_lock);
+        RemoveEntryList(&vde->list_entry);
+        
+        RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
+        Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
+        if (!NT_SUCCESS(Status))
+            ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+        else {
+            remove_drive_letter(mountmgr, &vde->name);
+            
+            ObDereferenceObject(mountmgrfo);
+        }
+        
+        if (vde->name.Buffer)
+            ExFreePool(vde->name.Buffer);
+        
+        ExDeleteResourceLite(&vde->child_lock);
+        
+        IoSetDeviceInterfaceState(&vde->bus_name, FALSE);
+        
+        IoDetachDevice(vde->pdo);
+        
+        IoDeleteDevice(vde->device);
+    } else if (!no_release_lock)
+        ExReleaseResourceLite(&vde->child_lock);
+}
+
+void volume_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
+    STORAGE_DEVICE_NUMBER sdn;
+    PFILE_OBJECT FileObject, mountmgrfo;
+    UNICODE_STRING mmdevpath;
+    PDEVICE_OBJECT devobj, mountmgr;
+    GET_LENGTH_INFORMATION gli;
+    NTSTATUS Status;
     
-no_parts:
-    if (num_parts == 0) {
-        devname.Length = preflen;
-        devname.Buffer[devname.Length / sizeof(WCHAR)] = '0';
-        devname.Length += sizeof(WCHAR);
+    TRACE("%.*S\n", devpath->Length / sizeof(WCHAR), devpath->Buffer);
+    
+    Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &FileObject, &devobj);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+        return;
+    }
+    
+    // make sure we're not processing devices we've created ourselves
+    
+    if (devobj->DriverObject == DriverObject)
+        goto end;
+
+    Status = dev_ioctl(devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), TRUE, NULL);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08x\n", Status);
+        goto end;
+    }
+    
+    Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
+                       &sdn, sizeof(STORAGE_DEVICE_NUMBER), TRUE, NULL);
+    if (!NT_SUCCESS(Status)) {
+        TRACE("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status);
+        sdn.DeviceNumber = 0xffffffff;
+        sdn.PartitionNumber = 0;
+    } else
+        TRACE("DeviceType = %u, DeviceNumber = %u, PartitionNumber = %u\n", sdn.DeviceType, sdn.DeviceNumber, sdn.PartitionNumber);
+    
+    // If we've just added a partition to a whole-disk filesystem, unmount it
+    if (sdn.DeviceNumber != 0xffffffff) {
+        LIST_ENTRY* le;
         
-        test_vol(DriverObject, mountmgr, &devname, sdn.DeviceNumber, 0, &volumes);
+        ExAcquireResourceExclusiveLite(&volume_list_lock, TRUE);
+        
+        le = volume_list.Flink;
+        while (le != &volume_list) {
+            volume_device_extension* vde = CONTAINING_RECORD(le, volume_device_extension, list_entry);
+            LIST_ENTRY* le2;
+            BOOL changed = FALSE;
+            
+            ExAcquireResourceExclusiveLite(&vde->child_lock, TRUE);
+            
+            le2 = vde->children.Flink;
+            while (le2 != &vde->children) {
+                volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
+                LIST_ENTRY* le3 = le2->Flink;
+                
+                if (vc->disk_num == sdn.DeviceNumber && vc->part_num == 0) {
+                    TRACE("removing device\n");
+                    
+                    remove_volume_child(vde, vc, FALSE);
+                    changed = TRUE;
+
+                    break;
+                }
+                
+                le2 = le3;
+            }
+            
+            if (!changed)
+                ExReleaseResourceLite(&vde->child_lock);
+            else
+                break;
+            
+            le = le->Flink;
+        }
+        
+        ExReleaseResourceLite(&volume_list_lock);
     }
     
+    RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
+    Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+        goto end;
+    }
+
+    test_vol(mountmgr, devobj, devpath, sdn.DeviceNumber, sdn.PartitionNumber, devpath, gli.Length.QuadPart);
+    
+    ObDereferenceObject(mountmgrfo);
+    
 end:
-    refresh_mountmgr(mountmgr, &volumes);
-
     ObDereferenceObject(FileObject);
-    ObDereferenceObject(FileObject2);
 }
 
-static void disk_removal(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
+void volume_removal(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) {
     LIST_ENTRY* le;
-    pnp_disk* disk = NULL;
+    UNICODE_STRING devpath2;
     
-    // FIXME - remove Partition0Btrfs devices and unlink from mountmgr
-    // FIXME - emergency unmount of RAIDed volumes
+    TRACE("%.*S\n", devpath->Length / sizeof(WCHAR), devpath->Buffer);
     
-    ExAcquireResourceExclusiveLite(&volumes_lock, TRUE);
+    UNUSED(DriverObject);
     
-    le = pnp_disks.Flink;
-    while (le != &pnp_disks) {
-        pnp_disk* disk2 = CONTAINING_RECORD(le, pnp_disk, list_entry);
+    devpath2 = *devpath;
+    
+    if (devpath->Length > 4 * sizeof(WCHAR) && devpath->Buffer[0] == '\\' && (devpath->Buffer[1] == '\\' || devpath->Buffer[1] == '?') &&
+        devpath->Buffer[2] == '?' && devpath->Buffer[3] == '\\') {
+        devpath2.Buffer = &devpath2.Buffer[3];
+        devpath2.Length -= 3 * sizeof(WCHAR);
+        devpath2.MaximumLength -= 3 * sizeof(WCHAR);
+    }
+    
+    ExAcquireResourceExclusiveLite(&volume_list_lock, TRUE);
+    
+    le = volume_list.Flink;
+    while (le != &volume_list) {
+        volume_device_extension* vde = CONTAINING_RECORD(le, volume_device_extension, list_entry);
+        LIST_ENTRY* le2;
+        BOOL changed = FALSE;
+
+        ExAcquireResourceExclusiveLite(&vde->child_lock, TRUE);
         
-        if (disk2->devpath.Length == devpath->Length &&
-            RtlCompareMemory(disk2->devpath.Buffer, devpath->Buffer, devpath->Length) == devpath->Length) {
-            disk = disk2;
-            break;
+        le2 = vde->children.Flink;
+        while (le2 != &vde->children) {
+            volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
+            LIST_ENTRY* le3 = le2->Flink;
+            
+            if (vc->pnp_name.Length == devpath2.Length && RtlCompareMemory(vc->pnp_name.Buffer, devpath2.Buffer, devpath2.Length) == devpath2.Length) {
+                TRACE("removing device\n");
+                
+                remove_volume_child(vde, vc, FALSE);
+                changed = TRUE;
+                
+                break;
+            }
+            
+            le2 = le3;
         }
         
+        if (!changed)
+            ExReleaseResourceLite(&vde->child_lock);
+        else
+            break;
+        
         le = le->Flink;
     }
     
-    if (!disk) {
-        ExReleaseResourceLite(&volumes_lock);
+    ExReleaseResourceLite(&volume_list_lock);
+}
+
+typedef struct {
+    PDRIVER_OBJECT DriverObject;
+    UNICODE_STRING name;
+    pnp_callback func;
+    PIO_WORKITEM work_item;
+} pnp_callback_context;
+
+#ifdef __REACTOS__
+static void NTAPI do_pnp_callback(PDEVICE_OBJECT DeviceObject, PVOID con) {
+#else
+static void do_pnp_callback(PDEVICE_OBJECT DeviceObject, PVOID con) {
+#endif
+    pnp_callback_context* context = con;
+    
+    UNUSED(DeviceObject);
+    
+    context->func(context->DriverObject, &context->name);
+    
+    if (context->name.Buffer)
+        ExFreePool(context->name.Buffer);
+    
+    IoFreeWorkItem(context->work_item);
+}
+
+static void enqueue_pnp_callback(PDRIVER_OBJECT DriverObject, PUNICODE_STRING name, pnp_callback func) {
+    PIO_WORKITEM work_item;
+    pnp_callback_context* context;
+
+    work_item = IoAllocateWorkItem(devobj);
+        
+    context = ExAllocatePoolWithTag(PagedPool, sizeof(pnp_callback_context), ALLOC_TAG);
+    
+    if (!context) {
+        ERR("out of memory\n");
+        IoFreeWorkItem(work_item);
         return;
     }
-
-    le = volumes.Flink;
-    while (le != &volumes) {
-        volume* v = CONTAINING_RECORD(le, volume, list_entry);
-        LIST_ENTRY* le2 = le->Flink;
-        
-        if (v->disk_num == disk->disk_num) {
-            if (v->devpath.Buffer)
-                ExFreePool(v->devpath.Buffer);
-            
-            RemoveEntryList(&v->list_entry);
-            
-            ExFreePool(v);
+    
+    context->DriverObject = DriverObject;
+    
+    if (name->Length > 0) {
+        context->name.Buffer = ExAllocatePoolWithTag(PagedPool, name->Length, ALLOC_TAG);
+        if (!context->name.Buffer) {
+            ERR("out of memory\n");
+            ExFreePool(context);
+            IoFreeWorkItem(work_item);
+            return;
         }
         
-        le = le2;
+        RtlCopyMemory(context->name.Buffer, name->Buffer, name->Length);
+        context->name.Length = context->name.MaximumLength = name->Length;
+    } else {
+        context->name.Length = context->name.MaximumLength = 0;
+        context->name.Buffer = NULL;
     }
     
-    ExReleaseResourceLite(&volumes_lock);
+    context->func = func;
+    context->work_item = work_item;
     
-    ExFreePool(disk->devpath.Buffer);
+    IoQueueWorkItem(work_item, do_pnp_callback, DelayedWorkQueue, context);
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI volume_notification(PVOID NotificationStructure, PVOID Context) {
+#else
+NTSTATUS volume_notification(PVOID NotificationStructure, PVOID Context) {
+#endif
+    DEVICE_INTERFACE_CHANGE_NOTIFICATION* dicn = (DEVICE_INTERFACE_CHANGE_NOTIFICATION*)NotificationStructure;
+    PDRIVER_OBJECT DriverObject = (PDRIVER_OBJECT)Context;
     
-    RemoveEntryList(&disk->list_entry);
+    if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID))
+        enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, volume_arrival);
+    else if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID))
+        enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, volume_removal);
     
-    ExFreePool(disk);
+    return STATUS_SUCCESS;
 }
 
 #ifdef __REACTOS__
@@ -685,9 +627,9 @@
     PDRIVER_OBJECT DriverObject = (PDRIVER_OBJECT)Context;
     
     if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID))
-        disk_arrival(DriverObject, dicn->SymbolicLinkName);
+        enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, disk_arrival);
     else if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID))
-        disk_removal(DriverObject, dicn->SymbolicLinkName);
+        enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, volume_removal);
     
     return STATUS_SUCCESS;
 }
Index: drivers/filesystems/btrfs/security.c
===================================================================
--- drivers/filesystems/btrfs/security.c	(révision 74430)
+++ drivers/filesystems/btrfs/security.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -695,7 +695,7 @@
     ExFreePool(groupsid);
 }
 
-static NTSTATUS STDCALL get_file_security(device_extension* Vcb, PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* relsd, ULONG* buflen, SECURITY_INFORMATION flags) {
+static NTSTATUS STDCALL get_file_security(PFILE_OBJECT FileObject, SECURITY_DESCRIPTOR* relsd, ULONG* buflen, SECURITY_INFORMATION flags) {
     NTSTATUS Status;
     fcb* fcb = FileObject->FsContext;
     ccb* ccb = FileObject->FsContext2;
@@ -739,9 +739,12 @@
 
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_query_security(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
     
     if (!ccb) {
@@ -786,7 +789,7 @@
     
     buflen = IrpSp->Parameters.QuerySecurity.Length;
     
-    Status = get_file_security(Vcb, IrpSp->FileObject, sd, &buflen, IrpSp->Parameters.QuerySecurity.SecurityInformation);
+    Status = get_file_security(IrpSp->FileObject, sd, &buflen, IrpSp->Parameters.QuerySecurity.SecurityInformation);
     
     if (NT_SUCCESS(Status))
         Irp->IoStatus.Information = IrpSp->Parameters.QuerySecurity.Length;
@@ -803,7 +806,6 @@
 
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
     
-exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);    
     
@@ -910,9 +912,12 @@
 
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
-        goto exit;
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_set_security(DeviceObject, Irp);
+        goto end;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
     
     if (!ccb) {
@@ -961,7 +966,6 @@
     
     TRACE("returning %08x\n", Status);
 
-exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
 
Index: drivers/filesystems/btrfs/treefuncs.c
===================================================================
--- drivers/filesystems/btrfs/treefuncs.c	(révision 74430)
+++ drivers/filesystems/btrfs/treefuncs.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -19,7 +19,7 @@
 
 // #define DEBUG_TREE_LOCKS
 
-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 load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, UINT64 generation, PIRP Irp) {
     UINT8* buf;
     NTSTATUS Status;
     tree_header* th;
@@ -30,8 +30,6 @@
     BOOL inserted;
     LIST_ENTRY* le;
     
-    TRACE("(%p, %llx)\n", Vcb, addr);
-    
     buf = ExAllocatePoolWithTag(PagedPool, Vcb->superblock.node_size, ALLOC_TAG);
     if (!buf) {
         ERR("out of memory\n");
@@ -38,7 +36,7 @@
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, &c, Irp, FALSE);
+    Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, &c, Irp, generation, FALSE, 0);
     if (!NT_SUCCESS(Status)) {
         ERR("read_data returned 0x%08x\n", Status);
         ExFreePool(buf);
@@ -69,6 +67,7 @@
     t->has_new_address = FALSE;
     t->updated_extents = FALSE;
     t->write = FALSE;
+    t->uniqueness_determined = FALSE;
     
 //     ExInitializeResourceLite(&t->nonpaged->load_tree_lock);
     
@@ -96,16 +95,9 @@
             td->key = ln[i].key;
 //             TRACE("load_tree: leaf item %u (%x,%x,%x)\n", i, (UINT32)ln[i].key.obj_id, ln[i].key.obj_type, (UINT32)ln[i].key.offset);
             
-            if (ln[i].size > 0) {
-                td->data = ExAllocatePoolWithTag(PagedPool, ln[i].size, ALLOC_TAG);
-                if (!td->data) {
-                    ERR("out of memory\n");
-                    ExFreePool(buf);
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-                
-                RtlCopyMemory(td->data, buf + sizeof(tree_header) + ln[i].offset, ln[i].size);
-            } else
+            if (ln[i].size > 0)
+                td->data = buf + sizeof(tree_header) + ln[i].offset;
+            else
                 td->data = NULL;
             
             td->size = ln[i].size;
@@ -118,6 +110,7 @@
         }
         
         t->size += t->header.num_items * sizeof(leaf_node);
+        t->buf = buf;
     } else {
         internal_node* in = (internal_node*)(buf + sizeof(tree_header));
         unsigned int i;
@@ -150,10 +143,10 @@
         }
         
         t->size = t->header.num_items * sizeof(internal_node);
+        t->buf = NULL;
+        ExFreePool(buf);
     }
     
-    ExFreePool(buf);
-    
     InterlockedIncrement(&Vcb->open_trees);
     InsertTailList(&Vcb->trees, &t->list_entry);
     
@@ -204,7 +197,7 @@
     return STATUS_SUCCESS;
 }
 
-static tree* free_tree2(tree* t, const char* func, const char* file, unsigned int line) {
+static tree* free_tree2(tree* t) {
     LIST_ENTRY* le;
     tree_data* td;
     tree* par;
@@ -237,7 +230,7 @@
         le = RemoveHeadList(&t->itemlist);
         td = CONTAINING_RECORD(le, tree_data, list_entry);
         
-        if (t->header.level == 0 && td->data)
+        if (t->header.level == 0 && td->data && td->inserted)
             ExFreePool(td->data);
             
         ExFreeToPagedLookasideList(&t->Vcb->tree_data_lookaside, td);
@@ -269,50 +262,24 @@
         RemoveEntryList(&t->list_entry_hash);
     }
     
+    if (t->buf)
+        ExFreePool(t->buf);
+    
     ExFreePool(t);
 
     return NULL;
 }
 
-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;
+NTSTATUS STDCALL do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, PIRP Irp) {
     BOOL ret;
     
-//     ExAcquireResourceExclusiveLite(&thnp->lock, TRUE);
     ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
-    
-//     KeAcquireSpinLock(&thnp->spin_lock, &irql);
-//     
-//     if (thnp->status == tree_header_loading) {
-//         KeReleaseSpinLock(&thnp->spin_lock, irql);
-//         
-//         // FIXME - wait for Event
-//     } else if (thnp->status == tree_header_unloaded || thnp->status == tree_header_unloading) {
-//         if (thnp->status == tree_header_unloading) {
-//             KeReleaseSpinLock(&thnp->spin_lock, irql);
-//             // FIXME - wait for Event
-//         }
-//         
-//         // FIXME - change status
-//         thnp->status = tree_header_loading;
-//         KeReleaseSpinLock(&thnp->spin_lock, irql);
-//         
-//         // FIXME - load
-//         // FIXME - change status
-//         // FIXME - trigger event
-//     } else if (thnp->status == tree_header_loaded) {
-//         _increase_tree_rc(th->tree, func, file, line);
-//         KeReleaseSpinLock(&thnp->spin_lock, irql);
-//         
-//         ret = FALSE;
-//     }
 
     if (!th->tree) {
         NTSTATUS Status;
+        tree* nt;
         
-        Status = _load_tree(Vcb, th->address, r, &th->tree, t, Irp, func, file, line);
+        Status = load_tree(Vcb, th->address, r, &nt, th->generation, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("load_tree returned %08x\n", Status);
             ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
@@ -319,21 +286,20 @@
             return Status;
         }
         
-        th->tree->parent = t;
+        nt->parent = t;
         
 #ifdef DEBUG_PARANOID
-        if (t && t->header.level <= th->tree->header.level) int3;
+        if (t && t->header.level <= nt->header.level) int3;
 #endif
         
-        th->tree->paritem = td;
+        nt->paritem = td;
         
+        th->tree = nt;
+        
         ret = TRUE;
     } else
         ret = FALSE;
     
-//     KeReleaseSpinLock(&thnp->spin_lock, irql);
-    
-//     ExReleaseResourceLite(&thnp->lock);
     ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
     
     *loaded = ret;
@@ -341,13 +307,13 @@
     return STATUS_SUCCESS;
 }
 
-tree* STDCALL _free_tree(tree* t, const char* func, const char* file, unsigned int line) {
+tree* STDCALL free_tree(tree* t) {
     tree* ret;
     root* r = t->root;
     
     ExAcquireResourceExclusiveLite(&r->nonpaged->load_tree_lock, TRUE);
 
-    ret = free_tree2(t, func, file, line);
+    ret = free_tree2(t);
 
     ExReleaseResourceLite(&r->nonpaged->load_tree_lock);
     
@@ -381,14 +347,11 @@
     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, UINT8 level, PIRP Irp,
-                                          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, UINT8 level, PIRP Irp) {
     int cmp;
     tree_data *td, *lasttd;
     KEY key2;
     
-    TRACE("(%p, %p, %p, %p, %u)\n", Vcb, t, tp, searchkey, ignore);
-    
     cmp = 1;
     td = first_item(t);
     lasttd = NULL;
@@ -433,7 +396,7 @@
             oldtp.tree = t;
             oldtp.item = td;
             
-            while (_find_prev_item(Vcb, &oldtp, tp, TRUE, Irp, func, file, line)) {
+            while (find_prev_item(Vcb, &oldtp, tp, Irp)) {
                 if (!tp->item->ignore)
                     return STATUS_SUCCESS;
                 
@@ -445,7 +408,7 @@
             oldtp.tree = t;
             oldtp.item = td;
             
-            while (_find_next_item(Vcb, &oldtp, tp, TRUE, Irp, func, file, line)) {
+            while (find_next_item(Vcb, &oldtp, tp, TRUE, Irp)) {
                 if (!tp->item->ignore)
                     return STATUS_SUCCESS;
                 
@@ -479,27 +442,27 @@
 //         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, Irp, func, file, line);
-        if (!NT_SUCCESS(Status)) {
-            ERR("do_load_tree returned %08x\n", Status);
-            return Status;
+        if (!td->treeholder.tree) {
+            Status = do_load_tree(Vcb, &td->treeholder, t->root, t, td, &loaded, Irp);
+            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, level, Irp, func, file, line);
+        Status = find_item_in_tree(Vcb, td->treeholder.tree, tp, searchkey, ignore, level, Irp);
         
         return Status;
     }
 }
 
-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 STDCALL find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, PIRP Irp) {
     NTSTATUS Status;
     BOOL loaded;
 //     KIRQL irql;
     
-    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, Irp, func, file, line);
+        Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("do_load_tree returned %08x\n", Status);
             return Status;
@@ -506,7 +469,7 @@
         }
     }
 
-    Status = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, 0, Irp, func, file, line);
+    Status = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, 0, Irp);
     if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) {
         ERR("find_item_in_tree returned %08x\n", Status);
     }
@@ -521,15 +484,12 @@
     return Status;
 }
 
-NTSTATUS STDCALL _find_item_to_level(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, UINT8 level,
-                                     PIRP Irp, const char* func, const char* file, unsigned int line) {
+NTSTATUS STDCALL find_item_to_level(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, UINT8 level, PIRP Irp) {
     NTSTATUS Status;
     BOOL loaded;
     
-    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, Irp, func, file, line);
+        Status = do_load_tree(Vcb, &r->treeholder, r, NULL, NULL, &loaded, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("do_load_tree returned %08x\n", Status);
             return Status;
@@ -536,7 +496,7 @@
         }
     }
 
-    Status = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, level, Irp, func, file, line);
+    Status = find_item_in_tree(Vcb, r->treeholder.tree, tp, searchkey, ignore, level, Irp);
     if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) {
         ERR("find_item_in_tree returned %08x\n", Status);
     }
@@ -549,8 +509,7 @@
     return Status;
 }
 
-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_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, PIRP Irp) {
     tree* t;
     tree_data *td, *next;
     NTSTATUS Status;
@@ -594,7 +553,7 @@
     if (!t)
         return FALSE;
     
-    Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, &loaded, Irp, func, file, line);
+    Status = do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, &loaded, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("do_load_tree returned %08x\n", Status);
         return FALSE;
@@ -607,7 +566,7 @@
        
         fi = first_item(t);
         
-        Status = _do_load_tree(Vcb, &fi->treeholder, t->parent->root, t, fi, &loaded, Irp, func, file, line);
+        Status = do_load_tree(Vcb, &fi->treeholder, t->parent->root, t, fi, &loaded, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("do_load_tree returned %08x\n", Status);
             return FALSE;
@@ -623,7 +582,7 @@
         traverse_ptr ntp2;
         BOOL b;
         
-        while ((b = _find_next_item(Vcb, next_tp, &ntp2, TRUE, Irp, func, file, line))) {
+        while ((b = find_next_item(Vcb, next_tp, &ntp2, TRUE, Irp))) {
             *next_tp = ntp2;
             
             if (!next_tp->item->ignore)
@@ -653,8 +612,7 @@
     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, 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, PIRP Irp) {
     tree* t;
     tree_data* td;
     NTSTATUS Status;
@@ -681,7 +639,7 @@
     
     td = prev_item(t->parent, t->paritem);
     
-    Status = _do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, &loaded, Irp, func, file, line);
+    Status = do_load_tree(Vcb, &td->treeholder, t->parent->root, t->parent, td, &loaded, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("do_load_tree returned %08x\n", Status);
         return FALSE;
@@ -694,7 +652,7 @@
         
         li = last_item(t);
         
-        Status = _do_load_tree(Vcb, &li->treeholder, t->parent->root, t, li, &loaded, Irp, func, file, line);
+        Status = do_load_tree(Vcb, &li->treeholder, t->parent->root, t, li, &loaded, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("do_load_tree returned %08x\n", Status);
             return FALSE;
@@ -740,7 +698,7 @@
                     
                     empty = FALSE;
                     
-                    free_tree2(t, funcname, __FILE__, __LINE__);
+                    free_tree2(t);
                     if (top && r->treeholder.tree == t)
                         r->treeholder.tree = NULL;
                     
@@ -777,7 +735,7 @@
                 
                 empty = FALSE;
                 
-                free_tree2(t, funcname, __FILE__, __LINE__);
+                free_tree2(t);
                 if (top && r->treeholder.tree == t)
                     r->treeholder.tree = NULL;
                 
@@ -794,7 +752,7 @@
     }
 }
 
-void add_rollback(device_extension* Vcb, LIST_ENTRY* rollback, enum rollback_type type, void* ptr) {
+void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr) {
     rollback_item* ri;
     
     ri = ExAllocatePoolWithTag(PagedPool, sizeof(rollback_item), ALLOC_TAG);
@@ -808,7 +766,7 @@
     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, PIRP Irp, LIST_ENTRY* rollback) {
+NTSTATUS 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) {
     traverse_ptr tp;
     KEY searchkey;
     int cmp;
@@ -818,11 +776,9 @@
     LIST_ENTRY* le;
     KEY firstitem = {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
 #endif
-    traverse_ptr* tp2;
-    BOOL success = FALSE;
     NTSTATUS Status;
     
-    TRACE("(%p, %p, %llx, %x, %llx, %p, %x, %p, %p)\n", Vcb, r, obj_id, obj_type, offset, data, size, ptp, rollback);
+    TRACE("(%p, %p, %llx, %x, %llx, %p, %x, %p)\n", Vcb, r, obj_id, obj_type, offset, data, size, ptp);
     
 // #ifdef DEBUG_PARANOID
 //     if (!ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
@@ -854,6 +810,7 @@
                 tp.item = NULL;
             } else {
                 ERR("error: unable to load tree for root %llx\n", r->id);
+                Status = STATUS_INTERNAL_ERROR;
                 goto end;
             }
         } else {
@@ -874,6 +831,7 @@
         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;
+            Status = STATUS_INTERNAL_ERROR;
             goto end;
         }
     } else
@@ -882,6 +840,7 @@
     td = ExAllocateFromPagedLookasideList(&Vcb->tree_data_lookaside);
     if (!td) {
         ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
         goto end;
     }
     
@@ -939,8 +898,6 @@
             t->paritem->ignore = FALSE;
             t->parent->header.num_items++;
             t->parent->size += sizeof(internal_node);
-            
-            // FIXME - do we need to add a rollback entry here?
         }
 
         t->header.generation = Vcb->superblock.generation;
@@ -947,23 +904,10 @@
         t = t->parent;
     }
     
-    // FIXME - free this correctly
-    
-    tp2 = ExAllocateFromPagedLookasideList(&Vcb->traverse_ptr_lookaside);
-    if (!tp2) {
-        ERR("out of memory\n");
-        goto end;
-    }
-    
-    tp2->tree = tp.tree;
-    tp2->item = td;
-    
-    add_rollback(Vcb, rollback, ROLLBACK_INSERT_ITEM, tp2);
-    
-    success = TRUE;
+    Status = STATUS_SUCCESS;
 
 end:
-    return success;
+    return Status;
 }
 
 static __inline tree_data* first_valid_item(tree* t) {
@@ -981,10 +925,9 @@
     return NULL;
 }
 
-void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp, LIST_ENTRY* rollback) {
+NTSTATUS STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp) {
     tree* t;
     UINT64 gen;
-    traverse_ptr* tp2;
 
     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");
     
@@ -997,6 +940,7 @@
     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);
         int3;
+        return STATUS_INTERNAL_ERROR;
     }
 #endif
 
@@ -1022,19 +966,10 @@
         t = t->parent;
     }
     
-    tp2 = ExAllocateFromPagedLookasideList(&Vcb->traverse_ptr_lookaside);
-    if (!tp2) {
-        ERR("out of memory\n");
-        return;
-    }
-    
-    tp2->tree = tp->tree;
-    tp2->item = tp->item;
-
-    add_rollback(Vcb, rollback, ROLLBACK_DELETE_ITEM, tp2);
+    return STATUS_SUCCESS;
 }
 
-void clear_rollback(device_extension* Vcb, LIST_ENTRY* rollback) {
+void clear_rollback(LIST_ENTRY* rollback) {
     rollback_item* ri;
     
     while (!IsListEmpty(rollback)) {
@@ -1042,11 +977,6 @@
         ri = CONTAINING_RECORD(le, rollback_item, list_entry);
         
         switch (ri->type) {
-            case ROLLBACK_INSERT_ITEM:
-            case ROLLBACK_DELETE_ITEM:
-                ExFreeToPagedLookasideList(&Vcb->traverse_ptr_lookaside, ri->ptr);
-                break;
-                
             case ROLLBACK_ADD_SPACE:
             case ROLLBACK_SUBTRACT_SPACE:
             case ROLLBACK_INSERT_EXTENT:
@@ -1071,42 +1001,6 @@
         ri = CONTAINING_RECORD(le, rollback_item, list_entry);
         
         switch (ri->type) {
-            case ROLLBACK_INSERT_ITEM:
-            {
-                traverse_ptr* tp = ri->ptr;
-                
-                if (!tp->item->ignore) {
-                    tp->item->ignore = TRUE;
-                    tp->tree->header.num_items--;
-                
-                    if (tp->tree->header.level == 0)
-                        tp->tree->size -= sizeof(leaf_node) + tp->item->size;
-                    else
-                        tp->tree->size -= sizeof(internal_node);
-                }
-                
-                ExFreeToPagedLookasideList(&Vcb->traverse_ptr_lookaside, tp);
-                break;
-            }
-                
-            case ROLLBACK_DELETE_ITEM:
-            {
-                traverse_ptr* tp = ri->ptr;
-                
-                if (tp->item->ignore) {
-                    tp->item->ignore = FALSE;
-                    tp->tree->header.num_items++;
-                
-                    if (tp->tree->header.level == 0)
-                        tp->tree->size += sizeof(leaf_node) + tp->item->size;
-                    else
-                        tp->tree->size += sizeof(internal_node);
-                }
-                
-                ExFreeToPagedLookasideList(&Vcb->traverse_ptr_lookaside, tp);
-                break;
-            }
-            
             case ROLLBACK_INSERT_EXTENT:
             {
                 rollback_extent* re = ri->ptr;
@@ -1113,8 +1007,8 @@
                 
                 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 (re->ext->extent_data.type == EXTENT_TYPE_REGULAR || re->ext->extent_data.type == EXTENT_TYPE_PREALLOC) {
+                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)re->ext->extent_data.data;
                     
                     if (ed2->size != 0) {
                         chunk* c = get_chunk_from_address(Vcb, ed2->address);
@@ -1142,8 +1036,8 @@
                 
                 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 (re->ext->extent_data.type == EXTENT_TYPE_REGULAR || re->ext->extent_data.type == EXTENT_TYPE_PREALLOC) {
+                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)re->ext->extent_data.data;
                     
                     if (ed2->size != 0) {
                         chunk* c = get_chunk_from_address(Vcb, ed2->address);
@@ -1174,9 +1068,9 @@
                     ExAcquireResourceExclusiveLite(&rs->chunk->lock, TRUE);
                 
                 if (ri->type == ROLLBACK_ADD_SPACE)
-                    space_list_subtract2(Vcb, rs->list, rs->list_size, rs->address, rs->length, NULL);
+                    space_list_subtract2(rs->list, rs->list_size, rs->address, rs->length, NULL, NULL);
                 else
-                    space_list_add2(Vcb, rs->list, rs->list_size, rs->address, rs->length, NULL);
+                    space_list_add2(rs->list, rs->list_size, rs->address, rs->length, NULL, NULL);
                 
                 if (rs->chunk) {
                     LIST_ENTRY* le2 = le->Blink;
@@ -1190,9 +1084,9 @@
                             
                             if (rs2->chunk == rs->chunk) {
                                 if (ri2->type == ROLLBACK_ADD_SPACE)
-                                    space_list_subtract2(Vcb, rs2->list, rs2->list_size, rs2->address, rs2->length, NULL);
+                                    space_list_subtract2(rs2->list, rs2->list_size, rs2->address, rs2->length, NULL, NULL);
                                 else
-                                    space_list_add2(Vcb, rs2->list, rs2->list_size, rs2->address, rs2->length, NULL);
+                                    space_list_add2(rs2->list, rs2->list_size, rs2->address, rs2->length, NULL, NULL);
                                 
                                 ExFreePool(rs2);
                                 RemoveEntryList(&ri2->list_entry);
@@ -1305,10 +1199,10 @@
     InsertTailList(listhead, &bi2->list_entry);
 }
 
-static BOOL handle_batch_collision(device_extension* Vcb, batch_item* bi, tree* t, tree_data* td, tree_data* newtd, LIST_ENTRY* listhead, LIST_ENTRY* rollback) {
+static BOOL handle_batch_collision(device_extension* Vcb, batch_item* bi, tree* t, tree_data* td, tree_data* newtd, LIST_ENTRY* listhead) {
     if (bi->operation == Batch_Delete || bi->operation == Batch_SetXattr || bi->operation == Batch_DirItem || bi->operation == Batch_InodeRef ||
         bi->operation == Batch_InodeExtRef || bi->operation == Batch_DeleteDirItem || bi->operation == Batch_DeleteInodeRef ||
-        bi->operation == Batch_DeleteInodeExtRef) {
+        bi->operation == Batch_DeleteInodeExtRef || bi->operation == Batch_DeleteXattr) {
         UINT16 maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
         
         switch (bi->operation) {
@@ -1779,6 +1673,78 @@
                 break;
             }
             
+            case Batch_DeleteXattr: {
+                if (td->size < sizeof(DIR_ITEM)) {
+                    WARN("XATTR_ITEM was %u bytes, expected at least %u\n", td->size, sizeof(DIR_ITEM));
+                    return TRUE;
+                } else {
+                    DIR_ITEM *di, *deldi;
+                    LONG len;
+                    
+                    deldi = (DIR_ITEM*)bi->data;
+                    di = (DIR_ITEM*)td->data;
+                    len = td->size;
+                    
+                    do {
+                        if (di->n == deldi->n && RtlCompareMemory(di->name, deldi->name, di->n) == di->n) {
+                            ULONG newlen = td->size - (sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m);
+                            
+                            if (newlen == 0)
+                                TRACE("deleting XATTR_ITEM\n");
+                            else {
+                                UINT8 *newdi = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *dioff;
+                                tree_data* td2;
+                                
+                                if (!newdi) {
+                                    ERR("out of memory\n");
+                                    return TRUE;
+                                }
+                                
+                                TRACE("modifying XATTR_ITEM\n");
+
+                                if ((UINT8*)di > td->data) {
+                                    RtlCopyMemory(newdi, td->data, (UINT8*)di - td->data);
+                                    dioff = newdi + ((UINT8*)di - td->data);
+                                } else
+                                    dioff = newdi;
+                                
+                                if ((UINT8*)&di->name[di->n + di->m] - td->data < td->size)
+                                    RtlCopyMemory(dioff, &di->name[di->n + di->m], td->size - ((UINT8*)&di->name[di->n + di->m] - td->data));
+                                
+                                td2 = ExAllocateFromPagedLookasideList(&Vcb->tree_data_lookaside);
+                                if (!td2) {
+                                    ERR("out of memory\n");
+                                    return TRUE;
+                                }
+                                
+                                td2->key = bi->key;
+                                td2->size = newlen;
+                                td2->data = newdi;
+                                td2->ignore = FALSE;
+                                td2->inserted = TRUE;
+                                
+                                InsertHeadList(td->list_entry.Blink, &td2->list_entry);
+                                
+                                t->header.num_items++;
+                                t->size += newlen + sizeof(leaf_node);
+                                t->write = TRUE;
+                            }
+                            
+                            break;
+                        }
+                        
+                        len -= sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m;
+                        di = (DIR_ITEM*)&di->name[di->n + di->m];
+                        
+                        if (len == 0) {
+                            TRACE("could not find DIR_ITEM to delete\n");
+                            return TRUE;
+                        }
+                    } while (len > 0);
+                }
+                break;
+            }
+            
             case Batch_Delete:
                 break;
             
@@ -1790,26 +1756,11 @@
         
         // delete old item
         if (!td->ignore) {
-            traverse_ptr* tp2;
-            
             td->ignore = TRUE;
         
             t->header.num_items--;
             t->size -= sizeof(leaf_node) + td->size;
             t->write = TRUE;
-            
-            if (rollback) {
-                tp2 = ExAllocateFromPagedLookasideList(&Vcb->traverse_ptr_lookaside);
-                if (!tp2) {
-                    ERR("out of memory\n");
-                    return FALSE;
-                }
-                
-                tp2->tree = t;
-                tp2->item = td;
-    
-                add_rollback(Vcb, rollback, ROLLBACK_DELETE_ITEM, tp2);
-            }
         }
 
         if (newtd) {
@@ -1825,7 +1776,7 @@
     return FALSE;
 }
 
-static void commit_batch_list_root(device_extension* Vcb, batch_root* br, PIRP Irp, LIST_ENTRY* rollback) {
+static void commit_batch_list_root(device_extension* Vcb, batch_root* br, PIRP Irp) {
     LIST_ENTRY* le;
     NTSTATUS Status;
     
@@ -1835,7 +1786,7 @@
     while (le != &br->items) {
         batch_item* bi = CONTAINING_RECORD(le, batch_item, list_entry);
         LIST_ENTRY *le2, *listhead;
-        traverse_ptr tp, *tp2;
+        traverse_ptr tp;
         KEY tree_end;
         BOOL no_end;
         tree_data* td;
@@ -1845,7 +1796,7 @@
         
         TRACE("(%llx,%x,%llx)\n", bi->key.obj_id, bi->key.obj_type, bi->key.offset);
         
-        Status = find_item(Vcb, br->r, &tp, &bi->key, FALSE, Irp);
+        Status = find_item(Vcb, br->r, &tp, &bi->key, TRUE, Irp);
         if (!NT_SUCCESS(Status)) { // FIXME - handle STATUS_NOT_FOUND
             ERR("find_item returned %08x\n", Status);
             return;
@@ -1915,9 +1866,82 @@
                     }
                 }
             }
+        } else if (bi->operation == Batch_DeleteExtentData) {
+            if (tp.item->key.obj_id < bi->key.obj_id || (tp.item->key.obj_id == bi->key.obj_id && tp.item->key.obj_type < bi->key.obj_type)) {
+                traverse_ptr tp2;
+                
+                if (find_next_item(Vcb, &tp, &tp2, FALSE, Irp)) {
+                    if (tp2.item->key.obj_id == bi->key.obj_id && tp2.item->key.obj_type == bi->key.obj_type) {
+                        tp = tp2;
+                        find_tree_end(tp.tree, &tree_end, &no_end);
+                    }
+                }
+            }
+            
+            if (tp.item->key.obj_id == bi->key.obj_id && tp.item->key.obj_type == bi->key.obj_type) {
+                BOOL ended = FALSE;
+                
+                td = tp.item;
+                
+                if (!tp.item->ignore) {
+                    tp.item->ignore = TRUE;
+                    tp.tree->header.num_items--;
+                    tp.tree->size -= tp.item->size + sizeof(leaf_node);
+                    tp.tree->write = TRUE;
+                }
+                
+                le2 = tp.item->list_entry.Flink;
+                while (le2 != &tp.tree->itemlist) {
+                    td = CONTAINING_RECORD(le2, tree_data, list_entry);
+                    
+                    if (td->key.obj_id == bi->key.obj_id && td->key.obj_type == bi->key.obj_type) {
+                        if (!td->ignore) {
+                            td->ignore = TRUE;
+                            tp.tree->header.num_items--;
+                            tp.tree->size -= td->size + sizeof(leaf_node);
+                            tp.tree->write = TRUE;
+                        }
+                    } else {
+                        ended = TRUE;
+                        break;
+                    }
+                    
+                    le2 = le2->Flink;
+                }
+                
+                while (!ended) {
+                    traverse_ptr next_tp;
+                    
+                    tp.item = td;
+                    
+                    if (!find_next_item(Vcb, &tp, &next_tp, FALSE, Irp))
+                        break;
+                    
+                    tp = next_tp;
+                    
+                    le2 = &tp.item->list_entry;
+                    while (le2 != &tp.tree->itemlist) {
+                        td = CONTAINING_RECORD(le2, tree_data, list_entry);
+                        
+                        if (td->key.obj_id == bi->key.obj_id && td->key.obj_type == bi->key.obj_type) {
+                            if (!td->ignore) {
+                                td->ignore = TRUE;
+                                tp.tree->header.num_items--;
+                                tp.tree->size -= td->size + sizeof(leaf_node);
+                                tp.tree->write = TRUE;
+                            }
+                        } else {
+                            ended = TRUE;
+                            break;
+                        }
+                        
+                        le2 = le2->Flink;
+                    }
+                }
+            }
         } else {
-            if (bi->operation == Batch_Delete || bi->operation == Batch_DeleteDirItem ||
-                bi->operation == Batch_DeleteInodeRef || bi->operation == Batch_DeleteInodeExtRef)
+            if (bi->operation == Batch_Delete || bi->operation == Batch_DeleteDirItem || bi->operation == Batch_DeleteInodeRef ||
+                bi->operation == Batch_DeleteInodeExtRef || bi->operation == Batch_DeleteXattr)
                 td = NULL;
             else {
                 td = ExAllocateFromPagedLookasideList(&Vcb->tree_data_lookaside);
@@ -1952,7 +1976,11 @@
                     }
                 }
             } else if (cmp == 0) { // item already exists
-                ignore = handle_batch_collision(Vcb, bi, tp.tree, tp.item, td, &br->items, rollback);
+                if (tp.item->ignore) {
+                    if (td)
+                        InsertHeadList(tp.item->list_entry.Blink, &td->list_entry);
+                } else
+                    ignore = handle_batch_collision(Vcb, bi, tp.tree, tp.item, td, &br->items);
             } else if (td) {
                 InsertHeadList(&tp.item->list_entry, &td->list_entry);
             }
@@ -1966,20 +1994,6 @@
                 tp.tree->size += bi->datalen + sizeof(leaf_node);
                 tp.tree->write = TRUE;
                 
-                if (rollback) {
-                    // FIXME - free this correctly
-                    tp2 = ExAllocateFromPagedLookasideList(&Vcb->traverse_ptr_lookaside);
-                    if (!tp2) {
-                        ERR("out of memory\n");
-                        return;
-                    }
-                    
-                    tp2->tree = tp.tree;
-                    tp2->item = td;
-
-                    add_rollback(Vcb, rollback, ROLLBACK_INSERT_ITEM, tp2);
-                }
-                
                 listhead = &td->list_entry;
             } else {
                 listhead = &tp.item->list_entry;
@@ -1996,7 +2010,7 @@
             while (le2 != &br->items) {
                 batch_item* bi2 = CONTAINING_RECORD(le2, batch_item, list_entry);
                 
-                if (bi2->operation == Batch_DeleteInode)
+                if (bi2->operation == Batch_DeleteInode || bi2->operation == Batch_DeleteExtentData)
                     break;
                 
                 if (no_end || keycmp(bi2->key, tree_end) == -1) {
@@ -2005,8 +2019,8 @@
                     
                     ignore = FALSE;
                     
-                    if (bi2->operation == Batch_Delete || bi2->operation == Batch_DeleteDirItem ||
-                        bi2->operation == Batch_DeleteInodeRef || bi2->operation == Batch_DeleteInodeExtRef)
+                    if (bi2->operation == Batch_Delete || bi2->operation == Batch_DeleteDirItem || bi2->operation == Batch_DeleteInodeRef ||
+                        bi2->operation == Batch_DeleteInodeExtRef || bi2->operation == Batch_DeleteXattr)
                         td = NULL;
                     else {
                         td = ExAllocateFromPagedLookasideList(&Vcb->tree_data_lookaside);
@@ -2026,14 +2040,10 @@
                     while (le3 != &tp.tree->itemlist) {
                         tree_data* td2 = CONTAINING_RECORD(le3, tree_data, list_entry);
                         
-                        if (!td2->ignore) {
-                            cmp = keycmp(bi2->key, td2->key);
+                        cmp = keycmp(bi2->key, td2->key);
 
-                            if (cmp == 0) {
-                                ignore = handle_batch_collision(Vcb, bi2, tp.tree, td2, td, &br->items, rollback);
-                                inserted = TRUE;
-                                break;
-                            } else if (cmp == -1) {
+                        if (cmp == 0) {
+                            if (td2->ignore) {
                                 if (td) {
                                     InsertHeadList(le3->Blink, &td->list_entry);
                                     inserted = TRUE;
@@ -2040,8 +2050,19 @@
                                 } else if (bi2->operation == Batch_DeleteInodeRef && Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
                                     add_delete_inode_extref(Vcb, bi2, &br->items);
                                 }
-                                break;
+                            } else
+                                ignore = handle_batch_collision(Vcb, bi2, tp.tree, td2, td, &br->items);
+
+                            inserted = TRUE;
+                            break;
+                        } else if (cmp == -1) {
+                            if (td) {
+                                InsertHeadList(le3->Blink, &td->list_entry);
+                                inserted = TRUE;
+                            } else if (bi2->operation == Batch_DeleteInodeRef && Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
+                                add_delete_inode_extref(Vcb, bi2, &br->items);
                             }
+                            break;
                         }
                         
                         le3 = le3->Flink;
@@ -2055,20 +2076,6 @@
                             tp.tree->header.num_items++;
                             tp.tree->size += bi2->datalen + sizeof(leaf_node);
                             
-                            if (rollback) {
-                                // FIXME - free this correctly
-                                tp2 = ExAllocateFromPagedLookasideList(&Vcb->traverse_ptr_lookaside);
-                                if (!tp2) {
-                                    ERR("out of memory\n");
-                                    return;
-                                }
-                                
-                                tp2->tree = tp.tree;
-                                tp2->item = td;
-                                
-                                add_rollback(Vcb, rollback, ROLLBACK_INSERT_ITEM, tp2);
-                            }
-                            
                             listhead = &td->list_entry;
                         }
                     } else if (!inserted && bi2->operation == Batch_DeleteInodeRef && Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
@@ -2088,8 +2095,6 @@
                     t->paritem->ignore = FALSE;
                     t->parent->header.num_items++;
                     t->parent->size += sizeof(internal_node);
-                    
-                    // FIXME - do we need to add a rollback entry here?
                 }
 
                 t->header.generation = Vcb->superblock.generation;
@@ -2105,7 +2110,8 @@
         LIST_ENTRY* le = RemoveHeadList(&br->items);
         batch_item* bi = CONTAINING_RECORD(le, batch_item, list_entry);
         
-        if ((bi->operation == Batch_DeleteDirItem || bi->operation == Batch_DeleteInodeRef || bi->operation == Batch_DeleteInodeExtRef) && bi->data)
+        if ((bi->operation == Batch_DeleteDirItem || bi->operation == Batch_DeleteInodeRef ||
+            bi->operation == Batch_DeleteInodeExtRef || bi->operation == Batch_DeleteXattr) && bi->data)
             ExFreePool(bi->data);
         
         ExFreeToPagedLookasideList(&Vcb->batch_item_lookaside, bi);
@@ -2112,12 +2118,12 @@
     }
 }
 
-void commit_batch_list(device_extension* Vcb, LIST_ENTRY* batchlist, PIRP Irp, LIST_ENTRY* rollback) {
+void commit_batch_list(device_extension* Vcb, LIST_ENTRY* batchlist, PIRP Irp) {
     while (!IsListEmpty(batchlist)) {
         LIST_ENTRY* le = RemoveHeadList(batchlist);
         batch_root* br2 = CONTAINING_RECORD(le, batch_root, list_entry);
         
-        commit_batch_list_root(Vcb, br2, Irp, rollback);
+        commit_batch_list_root(Vcb, br2, Irp);
         
         ExFreePool(br2);
     }
Index: drivers/filesystems/btrfs/volume.c
===================================================================
--- drivers/filesystems/btrfs/volume.c	(nonexistent)
+++ drivers/filesystems/btrfs/volume.c	(copie de travail)
@@ -0,0 +1,1109 @@
+/* Copyright (c) Mark Harmstone 2016-17
+ * 
+ * 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"
+#include <mountdev.h>
+#include <ntddvol.h>
+#include <ntddstor.h>
+#include <ntdddisk.h>
+#include <wdmguid.h>
+
+#define IOCTL_VOLUME_IS_DYNAMIC     CTL_CODE(IOCTL_VOLUME_BASE, 18, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_VOLUME_POST_ONLINE    CTL_CODE(IOCTL_VOLUME_BASE, 25, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+extern PDRIVER_OBJECT drvobj;
+extern ERESOURCE volume_list_lock;
+extern LIST_ENTRY volume_list;
+
+NTSTATUS STDCALL vol_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    Irp->IoStatus.Information = FILE_OPENED;
+    
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS STDCALL vol_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+    
+    Irp->IoStatus.Information = 0;
+
+    return STATUS_SUCCESS;
+}
+
+typedef struct {
+    IO_STATUS_BLOCK iosb;
+    KEVENT Event;
+} vol_read_context;
+
+#ifdef __REACTOS__
+static NTSTATUS NTAPI vol_read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+#else
+static NTSTATUS vol_read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+#endif
+    vol_read_context* context = conptr;
+    
+    UNUSED(DeviceObject);
+    
+    context->iosb = Irp->IoStatus;
+    KeSetEvent(&context->Event, 0, FALSE);
+    
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+NTSTATUS STDCALL vol_read(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    volume_device_extension* vde = DeviceObject->DeviceExtension;
+    volume_child* vc;
+    NTSTATUS Status;
+    PIRP Irp2;
+    vol_read_context context;
+    PIO_STACK_LOCATION IrpSp, IrpSp2;
+    
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    ExAcquireResourceSharedLite(&vde->child_lock, TRUE);
+    
+    if (IsListEmpty(&vde->children)) {
+        ExReleaseResourceLite(&vde->child_lock);
+        Status = STATUS_INVALID_DEVICE_REQUEST;
+        goto end;
+    }
+    
+    vc = CONTAINING_RECORD(vde->children.Flink, volume_child, list_entry);
+    
+    // We can't use IoSkipCurrentIrpStackLocation as the device isn't in our stack
+    
+    Irp2 = IoAllocateIrp(vc->devobj->StackSize, FALSE);
+        
+    if (!Irp2) {
+        ERR("IoAllocateIrp failed\n");
+        ExReleaseResourceLite(&vde->child_lock);
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto end;
+    }
+    
+    IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    IrpSp2 = IoGetNextIrpStackLocation(Irp2);
+    
+    IrpSp2->MajorFunction = IRP_MJ_READ;
+    
+    if (vc->devobj->Flags & DO_BUFFERED_IO) {
+        Irp2->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, IrpSp->Parameters.Read.Length, ALLOC_TAG);
+        if (!Irp2->AssociatedIrp.SystemBuffer) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        Irp2->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
+
+        Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
+    } else if (vc->devobj->Flags & DO_DIRECT_IO)
+        Irp2->MdlAddress = Irp->MdlAddress;
+    else
+        Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
+
+    IrpSp2->Parameters.Read.Length = IrpSp->Parameters.Read.Length;
+    IrpSp2->Parameters.Read.ByteOffset.QuadPart = IrpSp->Parameters.Read.ByteOffset.QuadPart;
+
+    KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
+    Irp2->UserIosb = &context.iosb;
+
+    IoSetCompletionRoutine(Irp2, vol_read_completion, &context, TRUE, TRUE, TRUE);
+
+    Status = IoCallDriver(vc->devobj, Irp2);
+
+    if (Status == STATUS_PENDING) {
+        KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+        Status = context.iosb.Status;
+    }
+    
+    ExReleaseResourceLite(&vde->child_lock);
+    
+    Irp->IoStatus.Information = context.iosb.Information;
+    
+end:
+    Irp->IoStatus.Status = Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return Status;
+}
+
+NTSTATUS STDCALL vol_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    volume_device_extension* vde = DeviceObject->DeviceExtension;
+    volume_child* vc;
+    NTSTATUS Status;
+    PIRP Irp2;
+    vol_read_context context;
+    PIO_STACK_LOCATION IrpSp, IrpSp2;
+    
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    ExAcquireResourceSharedLite(&vde->child_lock, TRUE);
+    
+    if (IsListEmpty(&vde->children)) {
+        ExReleaseResourceLite(&vde->child_lock);
+        Status = STATUS_INVALID_DEVICE_REQUEST;
+        goto end;
+    }
+    
+    vc = CONTAINING_RECORD(vde->children.Flink, volume_child, list_entry);
+    
+    if (vc->list_entry.Flink != &vde->children) { // more than once device
+        ExReleaseResourceLite(&vde->child_lock);
+        Status = STATUS_ACCESS_DENIED;
+        goto end;
+    }
+    
+    // We can't use IoSkipCurrentIrpStackLocation as the device isn't in our stack
+    
+    Irp2 = IoAllocateIrp(vc->devobj->StackSize, FALSE);
+        
+    if (!Irp2) {
+        ERR("IoAllocateIrp failed\n");
+        ExReleaseResourceLite(&vde->child_lock);
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto end;
+    }
+    
+    IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    IrpSp2 = IoGetNextIrpStackLocation(Irp2);
+    
+    IrpSp2->MajorFunction = IRP_MJ_WRITE;
+    
+    if (vc->devobj->Flags & DO_BUFFERED_IO) {
+        Irp2->AssociatedIrp.SystemBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
+
+        Irp2->Flags |= IRP_BUFFERED_IO;
+
+        Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
+    } else if (vc->devobj->Flags & DO_DIRECT_IO)
+        Irp2->MdlAddress = Irp->MdlAddress;
+    else
+        Irp2->UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
+
+    IrpSp2->Parameters.Write.Length = IrpSp->Parameters.Write.Length;
+    IrpSp2->Parameters.Write.ByteOffset.QuadPart = IrpSp->Parameters.Write.ByteOffset.QuadPart;
+
+    KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
+    Irp2->UserIosb = &context.iosb;
+
+    IoSetCompletionRoutine(Irp2, vol_read_completion, &context, TRUE, TRUE, TRUE);
+
+    Status = IoCallDriver(vc->devobj, Irp2);
+
+    if (Status == STATUS_PENDING) {
+        KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+        Status = context.iosb.Status;
+    }
+    
+    ExReleaseResourceLite(&vde->child_lock);
+    
+    Irp->IoStatus.Information = context.iosb.Information;
+    
+end:
+    Irp->IoStatus.Status = Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return Status;
+}
+
+NTSTATUS STDCALL vol_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+NTSTATUS STDCALL vol_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+NTSTATUS STDCALL vol_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+NTSTATUS STDCALL vol_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+NTSTATUS STDCALL vol_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+NTSTATUS STDCALL vol_query_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+NTSTATUS STDCALL vol_set_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+NTSTATUS STDCALL vol_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    Irp->IoStatus.Information = 0;
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS STDCALL vol_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+NTSTATUS STDCALL vol_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+NTSTATUS STDCALL vol_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+static NTSTATUS vol_query_device_name(volume_device_extension* vde, PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    PMOUNTDEV_NAME name;
+
+    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) {
+        Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
+        return STATUS_BUFFER_TOO_SMALL;
+    }
+
+    name = Irp->AssociatedIrp.SystemBuffer;
+    name->NameLength = vde->name.Length;
+
+    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength) {
+        Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
+        return STATUS_BUFFER_OVERFLOW;
+    }
+    
+    RtlCopyMemory(name->Name, vde->name.Buffer, vde->name.Length);
+
+    Irp->IoStatus.Information = offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength;
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS vol_query_unique_id(volume_device_extension* vde, PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    MOUNTDEV_UNIQUE_ID* mduid;
+
+    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) {
+        Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
+        return STATUS_BUFFER_TOO_SMALL;
+    }
+
+    mduid = Irp->AssociatedIrp.SystemBuffer;
+    mduid->UniqueIdLength = sizeof(BTRFS_UUID);
+
+    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_UNIQUE_ID, UniqueId[0]) + mduid->UniqueIdLength) {
+        Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
+        return STATUS_BUFFER_OVERFLOW;
+    }
+
+    RtlCopyMemory(mduid->UniqueId, &vde->uuid, sizeof(BTRFS_UUID));
+
+    Irp->IoStatus.Information = offsetof(MOUNTDEV_UNIQUE_ID, UniqueId[0]) + mduid->UniqueIdLength;
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS vol_is_dynamic(PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    UINT8* buf;
+    
+    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == 0 || !Irp->AssociatedIrp.SystemBuffer)
+        return STATUS_INVALID_PARAMETER;
+    
+    buf = (UINT8*)Irp->AssociatedIrp.SystemBuffer;
+    
+    *buf = 1;
+    
+    Irp->IoStatus.Information = 1;
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS vol_check_verify(volume_device_extension* vde) {
+    NTSTATUS Status;
+    LIST_ENTRY* le;
+    
+    ExAcquireResourceSharedLite(&vde->child_lock, TRUE);
+    
+    le = vde->children.Flink;
+    while (le != &vde->children) {
+        volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+        
+        Status = dev_ioctl(vc->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, FALSE, NULL);
+        if (!NT_SUCCESS(Status))
+            goto end;
+        
+        le = le->Flink;
+    }
+    
+    Status = STATUS_SUCCESS;
+    
+end:
+    ExReleaseResourceLite(&vde->child_lock);
+    
+    return Status;
+}
+
+static NTSTATUS vol_get_disk_extents(volume_device_extension* vde, PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    LIST_ENTRY* le;
+    ULONG num_extents = 0, i, max_extents = 1;
+    NTSTATUS Status;
+    VOLUME_DISK_EXTENTS *ext, *ext3;
+    
+    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_DISK_EXTENTS))
+        return STATUS_BUFFER_TOO_SMALL;
+    
+    ExAcquireResourceSharedLite(&vde->child_lock, TRUE);
+    
+    le = vde->children.Flink;
+    while (le != &vde->children) {
+        volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+        VOLUME_DISK_EXTENTS ext2;
+            
+        Status = dev_ioctl(vc->devobj, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &ext2, sizeof(VOLUME_DISK_EXTENTS), FALSE, NULL);
+        if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
+            ERR("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS returned %08x\n", Status);
+            goto end;
+        }
+        
+        num_extents += ext2.NumberOfDiskExtents;
+        
+        if (ext2.NumberOfDiskExtents > max_extents)
+            max_extents = ext2.NumberOfDiskExtents;
+        
+        le = le->Flink;
+    }
+    
+    ext = Irp->AssociatedIrp.SystemBuffer;
+    
+    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (num_extents * sizeof(DISK_EXTENT))) {
+        Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]);
+        ext->NumberOfDiskExtents = num_extents;
+        Status = STATUS_BUFFER_OVERFLOW;
+        goto end;
+    }
+    
+    ext3 = ExAllocatePoolWithTag(PagedPool, offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (max_extents * sizeof(DISK_EXTENT)), ALLOC_TAG);
+    if (!ext3) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto end;
+    }
+    
+    i = 0;
+    ext->NumberOfDiskExtents = 0;
+    
+    le = vde->children.Flink;
+    while (le != &vde->children) {
+        volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+        
+        Status = dev_ioctl(vc->devobj, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, ext3,
+                            offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (max_extents * sizeof(DISK_EXTENT)), FALSE, NULL);
+        if (!NT_SUCCESS(Status)) {
+            ERR("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS returned %08x\n", Status);
+            ExFreePool(ext3);
+            goto end;
+        }
+        
+        if (i + ext3->NumberOfDiskExtents > num_extents) {
+            Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]);
+            ext->NumberOfDiskExtents = i + ext3->NumberOfDiskExtents;
+            Status = STATUS_BUFFER_OVERFLOW;
+            goto end;
+        }
+        
+        RtlCopyMemory(&ext->Extents[i], ext3->Extents, sizeof(DISK_EXTENT) * ext3->NumberOfDiskExtents);
+        i += ext3->NumberOfDiskExtents;
+        
+        le = le->Flink;
+    }
+    
+    ExFreePool(ext3);
+    
+    Status = STATUS_SUCCESS;
+    
+    ext->NumberOfDiskExtents = i;
+    Irp->IoStatus.Information = offsetof(VOLUME_DISK_EXTENTS, Extents[0]) + (i * sizeof(DISK_EXTENT));
+    
+end:
+    ExReleaseResourceLite(&vde->child_lock);
+    
+    return Status;
+}
+
+static NTSTATUS vol_is_writable(volume_device_extension* vde) {
+    NTSTATUS Status;
+    LIST_ENTRY* le;
+    BOOL writable = FALSE;
+    
+    ExAcquireResourceSharedLite(&vde->child_lock, TRUE);
+    
+    le = vde->children.Flink;
+    while (le != &vde->children) {
+        volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+        
+        Status = dev_ioctl(vc->devobj, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0, TRUE, NULL);
+        
+        if (NT_SUCCESS(Status)) {
+            writable = TRUE;
+            break;
+        } else if (Status != STATUS_MEDIA_WRITE_PROTECTED)
+            goto end;
+        
+        le = le->Flink;
+    }
+    
+    Status = writable ? STATUS_SUCCESS : STATUS_MEDIA_WRITE_PROTECTED;
+    
+end:
+    ExReleaseResourceLite(&vde->child_lock);
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS vol_get_length(volume_device_extension* vde, PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    GET_LENGTH_INFORMATION* gli;
+    LIST_ENTRY* le;
+    
+    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GET_LENGTH_INFORMATION))
+        return STATUS_BUFFER_TOO_SMALL;
+    
+    gli = (GET_LENGTH_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
+    
+    gli->Length.QuadPart = 0;
+    
+    ExAcquireResourceSharedLite(&vde->child_lock, TRUE);
+    
+    le = vde->children.Flink;
+    while (le != &vde->children) {
+        volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+        
+        gli->Length.QuadPart += vc->size;
+        
+        le = le->Flink;
+    }
+    
+    ExReleaseResourceLite(&vde->child_lock);
+    
+    Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS vol_get_drive_geometry(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+    volume_device_extension* vde = DeviceObject->DeviceExtension;
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    DISK_GEOMETRY* geom;
+    UINT64 length;
+    LIST_ENTRY* le;
+    
+    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY))
+        return STATUS_BUFFER_TOO_SMALL;
+
+    length = 0;
+    
+    ExAcquireResourceSharedLite(&vde->child_lock, TRUE);
+    
+    le = vde->children.Flink;
+    while (le != &vde->children) {
+        volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+        
+        length += vc->size;
+        
+        le = le->Flink;
+    }
+    
+    ExReleaseResourceLite(&vde->child_lock);
+    
+    geom = (DISK_GEOMETRY*)Irp->AssociatedIrp.SystemBuffer;
+    geom->BytesPerSector = DeviceObject->SectorSize == 0 ? 0x200 : DeviceObject->SectorSize;
+    geom->SectorsPerTrack = 0x3f;
+    geom->TracksPerCylinder = 0xff;
+    geom->Cylinders.QuadPart = length / (UInt32x32To64(geom->TracksPerCylinder, geom->SectorsPerTrack) * geom->BytesPerSector);
+    geom->MediaType = DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA ? RemovableMedia : FixedMedia;
+    
+    Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS vol_get_gpt_attributes(PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    VOLUME_GET_GPT_ATTRIBUTES_INFORMATION* vggai;
+    
+    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION))
+        return STATUS_BUFFER_TOO_SMALL;
+    
+    vggai = (VOLUME_GET_GPT_ATTRIBUTES_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
+    
+    vggai->GptAttributes = 0;
+    
+    Irp->IoStatus.Information = sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION);
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS vol_get_device_number(volume_device_extension* vde, PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    volume_child* vc;
+    STORAGE_DEVICE_NUMBER* sdn;
+    
+    // If only one device, return its disk number. This is needed for ejection to work.
+    
+    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DEVICE_NUMBER))
+        return STATUS_BUFFER_TOO_SMALL;
+    
+    ExAcquireResourceSharedLite(&vde->child_lock, TRUE);
+    
+    if (IsListEmpty(&vde->children)) {
+        ExReleaseResourceLite(&vde->child_lock);
+        return STATUS_INVALID_DEVICE_REQUEST;
+    }
+    
+    vc = CONTAINING_RECORD(vde->children.Flink, volume_child, list_entry);
+    
+    if (vc->list_entry.Flink != &vde->children) { // more than once device
+        ExReleaseResourceLite(&vde->child_lock);
+        return STATUS_INVALID_DEVICE_REQUEST;
+    }
+    
+    if (vc->disk_num == 0xffffffff) {
+        ExReleaseResourceLite(&vde->child_lock);
+        return STATUS_INVALID_DEVICE_REQUEST;
+    }
+    
+    sdn = (STORAGE_DEVICE_NUMBER*)Irp->AssociatedIrp.SystemBuffer;
+    
+    sdn->DeviceType = FILE_DEVICE_DISK;
+    sdn->DeviceNumber = vc->disk_num;
+    sdn->PartitionNumber = vc->part_num;
+    
+    ExReleaseResourceLite(&vde->child_lock);
+    
+    Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
+    
+    return STATUS_SUCCESS;
+}
+
+#ifdef __REACTOS__
+static NTSTATUS NTAPI vol_ioctl_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+#else
+static NTSTATUS vol_ioctl_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+#endif
+    KEVENT* event = conptr;
+    
+    UNUSED(DeviceObject);
+    UNUSED(Irp);
+    
+    KeSetEvent(event, 0, FALSE);
+    
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS vol_ioctl_passthrough(volume_device_extension* vde, PIRP Irp) {
+    NTSTATUS Status;
+    volume_child* vc;
+    PIRP Irp2;
+    PIO_STACK_LOCATION IrpSp, IrpSp2;
+    KEVENT Event;
+    
+    TRACE("(%p, %p)\n", vde, Irp);
+    
+    ExAcquireResourceSharedLite(&vde->child_lock, TRUE);
+    
+    if (IsListEmpty(&vde->children)) {
+        ExReleaseResourceLite(&vde->child_lock);
+        return STATUS_INVALID_DEVICE_REQUEST;
+    }
+    
+    vc = CONTAINING_RECORD(vde->children.Flink, volume_child, list_entry);
+    
+    if (vc->list_entry.Flink != &vde->children) { // more than once device
+        ExReleaseResourceLite(&vde->child_lock);
+        return STATUS_ACCESS_DENIED;
+    }
+    
+    Irp2 = IoAllocateIrp(vc->devobj->StackSize, FALSE);
+        
+    if (!Irp2) {
+        ERR("IoAllocateIrp failed\n");
+        ExReleaseResourceLite(&vde->child_lock);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    IrpSp2 = IoGetNextIrpStackLocation(Irp2);
+    
+    IrpSp2->MajorFunction = IrpSp->MajorFunction;
+    IrpSp2->MinorFunction = IrpSp->MinorFunction;
+    
+    IrpSp2->Parameters.DeviceIoControl.OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+    IrpSp2->Parameters.DeviceIoControl.InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+    IrpSp2->Parameters.DeviceIoControl.IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
+    IrpSp2->Parameters.DeviceIoControl.Type3InputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
+    
+    Irp2->AssociatedIrp.SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
+    Irp2->MdlAddress = Irp->MdlAddress;
+    Irp2->UserBuffer = Irp->UserBuffer;
+    Irp2->Flags = Irp->Flags;
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    IoSetCompletionRoutine(Irp2, vol_ioctl_completion, &Event, TRUE, TRUE, TRUE);
+
+    Status = IoCallDriver(vc->devobj, Irp2);
+
+    if (Status == STATUS_PENDING) {
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+        Status = Irp2->IoStatus.Status;
+    }
+    
+    Irp->IoStatus.Status = Irp2->IoStatus.Status;
+    Irp->IoStatus.Information = Irp2->IoStatus.Information;
+    
+    ExReleaseResourceLite(&vde->child_lock);
+
+    return Status;
+}
+
+NTSTATUS STDCALL vol_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    volume_device_extension* vde = DeviceObject->DeviceExtension;
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    Irp->IoStatus.Information = 0;
+    
+    switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
+        case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
+            return vol_query_device_name(vde, Irp);
+            
+        case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
+            return vol_query_unique_id(vde, Irp);
+            
+        case IOCTL_STORAGE_GET_DEVICE_NUMBER:
+            return vol_get_device_number(vde, Irp);
+
+        case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME:
+            TRACE("unhandled control code IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME\n");
+            break;
+
+        case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
+            TRACE("unhandled control code IOCTL_MOUNTDEV_QUERY_STABLE_GUID\n");
+            break;
+
+        case IOCTL_MOUNTDEV_LINK_CREATED:
+            TRACE("unhandled control code IOCTL_MOUNTDEV_LINK_CREATED\n");
+            break;
+
+        case IOCTL_VOLUME_GET_GPT_ATTRIBUTES:
+            return vol_get_gpt_attributes(Irp);
+
+        case IOCTL_VOLUME_IS_DYNAMIC:
+            return vol_is_dynamic(Irp);
+
+        case IOCTL_VOLUME_ONLINE:
+            TRACE("unhandled control code IOCTL_VOLUME_ONLINE\n");
+            break;
+
+        case IOCTL_VOLUME_POST_ONLINE:
+            TRACE("unhandled control code IOCTL_VOLUME_POST_ONLINE\n");
+            break;
+
+        case IOCTL_DISK_GET_DRIVE_GEOMETRY:
+            return vol_get_drive_geometry(DeviceObject, Irp);
+
+        case IOCTL_DISK_IS_WRITABLE:
+            return vol_is_writable(vde);
+
+        case IOCTL_DISK_GET_LENGTH_INFO:
+            return vol_get_length(vde, Irp);
+            
+        case IOCTL_STORAGE_CHECK_VERIFY:
+            return vol_check_verify(vde);
+
+        case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
+            return vol_get_disk_extents(vde, Irp);
+
+        default: // pass ioctl through if only one child device
+            return vol_ioctl_passthrough(vde, Irp);
+    }
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+NTSTATUS STDCALL vol_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+NTSTATUS STDCALL vol_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+NTSTATUS STDCALL vol_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+NTSTATUS STDCALL vol_power(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    NTSTATUS Status;
+    
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    if (IrpSp->MinorFunction == IRP_MN_SET_POWER || IrpSp->MinorFunction == IRP_MN_QUERY_POWER)
+        Irp->IoStatus.Status = STATUS_SUCCESS;
+    
+    Status = Irp->IoStatus.Status;
+    PoStartNextPowerIrp(Irp);
+
+    return Status;
+}
+
+NTSTATUS mountmgr_add_drive_letter(PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath) {
+    NTSTATUS Status;
+    ULONG mmdltsize;
+    MOUNTMGR_DRIVE_LETTER_TARGET* mmdlt;
+    MOUNTMGR_DRIVE_LETTER_INFORMATION mmdli;
+    
+    mmdltsize = offsetof(MOUNTMGR_DRIVE_LETTER_TARGET, DeviceName[0]) + devpath->Length;
+    
+    mmdlt = ExAllocatePoolWithTag(NonPagedPool, mmdltsize, ALLOC_TAG);
+    if (!mmdlt) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    mmdlt->DeviceNameLength = devpath->Length;
+    RtlCopyMemory(&mmdlt->DeviceName, devpath->Buffer, devpath->Length);
+    TRACE("mmdlt = %.*S\n", mmdlt->DeviceNameLength / sizeof(WCHAR), mmdlt->DeviceName);
+    
+    Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER, mmdlt, mmdltsize, &mmdli, sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION), FALSE, NULL);
+    
+    if (!NT_SUCCESS(Status))
+        ERR("IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER returned %08x\n", Status);
+    else
+        TRACE("DriveLetterWasAssigned = %u, CurrentDriveLetter = %c\n", mmdli.DriveLetterWasAssigned, mmdli.CurrentDriveLetter);
+    
+    return Status;
+}
+
+static __inline WCHAR hex_digit(UINT8 n) {
+    if (n >= 0 && n <= 9)
+        return n + '0';
+    else
+        return n - 0xa + 'a';
+}
+
+#ifndef __REACTOS__
+NTSTATUS pnp_removal(PVOID NotificationStructure, PVOID Context) {
+#else
+NTSTATUS NTAPI pnp_removal(PVOID NotificationStructure, PVOID Context) {
+#endif
+    TARGET_DEVICE_REMOVAL_NOTIFICATION* tdrn = (TARGET_DEVICE_REMOVAL_NOTIFICATION*)NotificationStructure;
+    volume_device_extension* vde = (volume_device_extension*)Context;
+    
+    if (RtlCompareMemory(&tdrn->Event, &GUID_TARGET_DEVICE_QUERY_REMOVE, sizeof(GUID)) == sizeof(GUID)) {
+        TRACE("GUID_TARGET_DEVICE_QUERY_REMOVE\n");
+        
+        if (vde->mounted_device)
+            return pnp_query_remove_device(vde->mounted_device, NULL);
+    }
+    
+    return STATUS_SUCCESS;
+}
+
+void add_volume_device(superblock* sb, PDEVICE_OBJECT mountmgr, PUNICODE_STRING devpath, UINT64 length, ULONG disk_num,
+                       ULONG part_num, PUNICODE_STRING partname) {
+    NTSTATUS Status;
+    LIST_ENTRY* le;
+    UNICODE_STRING volname;
+    PDEVICE_OBJECT voldev, DeviceObject;
+    volume_device_extension* vde = NULL;
+    int i, j;
+    BOOL new_vde = FALSE;
+    volume_child* vc;
+    PFILE_OBJECT FileObject;
+    
+    if (devpath->Length == 0)
+        return;
+    
+    ExAcquireResourceExclusiveLite(&volume_list_lock, TRUE);
+    
+    le = volume_list.Flink;
+    while (le != &volume_list) {
+        volume_device_extension* vde2 = CONTAINING_RECORD(le, volume_device_extension, list_entry);
+        
+        if (RtlCompareMemory(&vde2->uuid, &sb->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+            vde = vde2;
+            break;
+        }
+        
+        le = le->Flink;
+    }
+    
+    Status = IoGetDeviceObjectPointer(devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
+    if (!NT_SUCCESS(Status)) {
+        ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+        ExReleaseResourceLite(&volume_list_lock);
+        goto end;
+    }
+    
+    if (!vde) {
+        PDEVICE_OBJECT pdo = NULL;
+        
+        Status = IoReportDetectedDevice(drvobj, InterfaceTypeUndefined, 0xFFFFFFFF, 0xFFFFFFFF,
+                                        NULL, NULL, 0, &pdo);
+        if (!NT_SUCCESS(Status)) {
+            ERR("IoReportDetectedDevice returned %08x\n", Status);
+            ExReleaseResourceLite(&volume_list_lock);
+            goto end;
+        }
+        
+        volname.Length = volname.MaximumLength = (wcslen(BTRFS_VOLUME_PREFIX) + 36 + 1) * sizeof(WCHAR);
+        volname.Buffer = ExAllocatePoolWithTag(PagedPool, volname.MaximumLength, ALLOC_TAG); // FIXME - when do we free this?
+        
+        if (!volname.Buffer) {
+            ERR("out of memory\n");
+            ExReleaseResourceLite(&volume_list_lock);
+            goto end;
+        }
+        
+        RtlCopyMemory(volname.Buffer, BTRFS_VOLUME_PREFIX, wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR));
+        
+        j = wcslen(BTRFS_VOLUME_PREFIX);
+        for (i = 0; i < 16; i++) {
+            volname.Buffer[j] = hex_digit(sb->uuid.uuid[i] >> 4); j++;
+            volname.Buffer[j] = hex_digit(sb->uuid.uuid[i] & 0xf); j++;
+            
+            if (i == 3 || i == 5 || i == 7 || i == 9) {
+                volname.Buffer[j] = '-';
+                j++;
+            }
+        }
+        
+        volname.Buffer[j] = '}';
+        
+        Status = IoCreateDevice(drvobj, sizeof(volume_device_extension), &volname, FILE_DEVICE_DISK, FILE_DEVICE_SECURE_OPEN, FALSE, &voldev);
+        if (!NT_SUCCESS(Status)) {
+            ERR("IoCreateDevice returned %08x\n", Status);
+            ExReleaseResourceLite(&volume_list_lock);
+            goto end;
+        }
+        
+        voldev->SectorSize = sb->sector_size;
+        voldev->Flags |= DO_DIRECT_IO;
+        
+        vde = voldev->DeviceExtension;
+        vde->type = VCB_TYPE_VOLUME;
+        vde->uuid = sb->uuid;
+        vde->name = volname;
+        vde->device = voldev;
+        vde->mounted_device = NULL;
+        vde->pdo = pdo;
+        
+        ExInitializeResourceLite(&vde->child_lock);
+        InitializeListHead(&vde->children);
+        vde->num_children = sb->num_devices;
+        vde->children_loaded = 0;
+        
+        new_vde = TRUE;
+        
+        Status = IoRegisterDeviceInterface(pdo, &GUID_DEVINTERFACE_VOLUME, NULL, &vde->bus_name);
+        if (!NT_SUCCESS(Status))
+            WARN("IoRegisterDeviceInterface returned %08x\n", Status);
+        
+        pdo->SectorSize = sb->sector_size;
+        vde->attached_device = IoAttachDeviceToDeviceStack(voldev, pdo);
+        
+        pdo->Flags &= ~DO_DEVICE_INITIALIZING;
+    } else {
+        ExAcquireResourceExclusiveLite(&vde->child_lock, TRUE);
+        ExConvertExclusiveToSharedLite(&volume_list_lock);
+        
+        le = vde->children.Flink;
+        while (le != &vde->children) {
+            volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry);
+            
+            if (RtlCompareMemory(&vc2->uuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+                // duplicate, ignore
+                ExReleaseResourceLite(&vde->child_lock);
+                ExReleaseResourceLite(&volume_list_lock);
+                goto end;
+            }
+            
+            le = le->Flink;
+        }
+    }
+    
+    vc = ExAllocatePoolWithTag(PagedPool, sizeof(volume_child), ALLOC_TAG);
+    if (!vc)
+        ERR("out of memory\n");
+    else {
+        UNICODE_STRING devpath2;
+        BOOL inserted = FALSE;
+        
+        vc->uuid = sb->dev_item.device_uuid;
+        vc->devid = sb->dev_item.dev_id;
+        vc->generation = sb->generation;
+        vc->notification_entry = NULL;
+        
+        Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, FileObject,
+                                                drvobj, pnp_removal, vde, &vc->notification_entry);
+        if (!NT_SUCCESS(Status))
+            WARN("IoRegisterPlugPlayNotification returned %08x\n", Status);
+        
+        ObReferenceObject(DeviceObject);
+        vc->devobj = DeviceObject;
+        
+        devpath2 = *devpath;
+        
+        // The PNP path sometimes begins \\?\ and sometimes \??\. We need to remove this prefix
+        // so we can compare properly if the device is removed.
+        if (devpath->Length > 4 * sizeof(WCHAR) && devpath->Buffer[0] == '\\' && (devpath->Buffer[1] == '\\' || devpath->Buffer[1] == '?') &&
+            devpath->Buffer[2] == '?' && devpath->Buffer[3] == '\\') {
+            devpath2.Buffer = &devpath2.Buffer[3];
+            devpath2.Length -= 3 * sizeof(WCHAR);
+            devpath2.MaximumLength -= 3 * sizeof(WCHAR);
+        }
+        
+        vc->pnp_name.Length = vc->pnp_name.MaximumLength = devpath2.Length;
+        vc->pnp_name.Buffer = ExAllocatePoolWithTag(PagedPool, devpath2.Length, ALLOC_TAG);
+        
+        if (vc->pnp_name.Buffer)
+            RtlCopyMemory(vc->pnp_name.Buffer, devpath2.Buffer, devpath2.Length);
+        else {
+            ERR("out of memory\n");
+            vc->pnp_name.Length = vc->pnp_name.MaximumLength = 0;
+        }
+        
+        vc->size = length;
+        vc->seeding = sb->flags & BTRFS_SUPERBLOCK_FLAGS_SEEDING ? TRUE : FALSE;
+        vc->disk_num = disk_num;
+        vc->part_num = part_num;
+        vc->had_drive_letter = FALSE;
+        
+        le = vde->children.Flink;
+        while (le != &vde->children) {
+            volume_child* vc2 = CONTAINING_RECORD(le, volume_child, list_entry);
+            
+            if (vc2->generation < vc->generation) {
+                if (le == vde->children.Flink)
+                    vde->num_children = sb->num_devices;
+                
+                InsertHeadList(vc2->list_entry.Blink, &vc->list_entry);
+                inserted = TRUE;
+                break;
+            }
+            
+            le = le->Flink;
+        }
+        
+        if (!inserted)
+            InsertTailList(&vde->children, &vc->list_entry);
+        
+        vde->children_loaded++;
+    }
+    
+    if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
+        voldev->Characteristics |= FILE_REMOVABLE_MEDIA;
+    
+    if (vde->num_children == vde->children_loaded) {
+        if (vde->num_children == 1) {
+            Status = remove_drive_letter(mountmgr, partname);
+            if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND)
+                WARN("remove_drive_letter returned %08x\n", Status);
+            
+            vc->had_drive_letter = NT_SUCCESS(Status);
+        } else {
+            le = vde->children.Flink;
+            
+            while (le != &vde->children) {
+                UNICODE_STRING name;
+                
+                vc = CONTAINING_RECORD(le, volume_child, list_entry);
+                
+                name.Length = name.MaximumLength = vc->pnp_name.Length + (3 * sizeof(WCHAR));
+                name.Buffer = ExAllocatePoolWithTag(PagedPool, name.Length, ALLOC_TAG);
+                
+                if (!name.Buffer) {
+                    ERR("out of memory\n");
+                    goto end;
+                }
+                
+                RtlCopyMemory(name.Buffer, L"\\??", 3 * sizeof(WCHAR));
+                RtlCopyMemory(&name.Buffer[3], vc->pnp_name.Buffer, vc->pnp_name.Length);
+                
+                Status = remove_drive_letter(mountmgr, &name);
+                
+                if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND)
+                    WARN("remove_drive_letter returned %08x\n", Status);
+                
+                ExFreePool(name.Buffer);
+                
+                vc->had_drive_letter = NT_SUCCESS(Status);
+                
+                le = le->Flink;
+            }
+        }
+        
+        Status = IoSetDeviceInterfaceState(&vde->bus_name, TRUE);
+        if (!NT_SUCCESS(Status))
+            WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
+    }
+    
+    if (!new_vde) {
+        ExReleaseResourceLite(&vde->child_lock);
+        ExReleaseResourceLite(&volume_list_lock);
+    } else {
+        InsertTailList(&volume_list, &vde->list_entry);
+        
+        voldev->Flags &= ~DO_DEVICE_INITIALIZING;
+        
+        ExReleaseResourceLite(&volume_list_lock);
+    }
+    
+end:
+    ObDereferenceObject(FileObject);
+}

Modification de propriétés sur drivers/filesystems/btrfs/volume.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: drivers/filesystems/btrfs/worker-thread.c
===================================================================
--- drivers/filesystems/btrfs/worker-thread.c	(révision 74430)
+++ drivers/filesystems/btrfs/worker-thread.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
Index: drivers/filesystems/btrfs/write.c
===================================================================
--- drivers/filesystems/btrfs/write.c	(révision 74430)
+++ drivers/filesystems/btrfs/write.c	(copie de travail)
@@ -1,4 +1,4 @@
-/* Copyright (c) Mark Harmstone 2016
+/* Copyright (c) Mark Harmstone 2016-17
  * 
  * This file is part of WinBtrfs.
  * 
@@ -24,22 +24,26 @@
     UINT64 start;
     UINT64 end;
     UINT8* data;
-    UINT32 skip_start;
-    UINT32 skip_end;
+    PMDL mdl;
+    UINT32 irp_offset;
 } write_stripe;
 
-typedef struct {
-    LONG stripes_left;
-    KEVENT event;
-} read_stripe_master;
+struct _read_context;
 
 typedef struct {
     PIRP Irp;
-    PDEVICE_OBJECT devobj;
     IO_STATUS_BLOCK iosb;
-    read_stripe_master* master;
-} read_stripe;
+    device* dev;
+    struct _read_context* context;
+} read_context_stripe;
 
+typedef struct _read_context {
+    KEVENT Event;
+    ULONG total;
+    LONG left;
+    read_context_stripe* stripes;
+} read_context;
+
 // 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 remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback);
@@ -46,6 +50,7 @@
 
 extern tPsUpdateDiskCounters PsUpdateDiskCounters;
 extern tCcCopyWriteEx CcCopyWriteEx;
+extern tFsRtlUpdateDiskCounters FsRtlUpdateDiskCounters;
 extern BOOL diskacc;
 
 BOOL find_data_address_in_chunk(device_extension* Vcb, chunk* c, UINT64 length, UINT64* address) {
@@ -54,6 +59,18 @@
     
     TRACE("(%p, %llx, %llx, %p)\n", Vcb, c->offset, length, address);
     
+    if (length > c->chunk_item->size - c->used)
+        return FALSE;
+    
+    if (!c->cache_loaded) {
+        NTSTATUS Status = load_cache_chunk(Vcb, c, NULL);
+        
+        if (!NT_SUCCESS(Status)) {
+            ERR("load_cache_chunk returned %08x\n", Status);
+            return FALSE;
+        }
+    }
+    
     if (IsListEmpty(&c->space_size))
         return FALSE;
     
@@ -246,7 +263,7 @@
     return TRUE;
 }
 
-static BOOL find_new_stripe(device_extension* Vcb, stripe* stripes, UINT16 i, UINT64 max_stripe_size, UINT16 type) {
+static BOOL find_new_stripe(device_extension* Vcb, stripe* stripes, UINT16 i, UINT64 max_stripe_size) {
     UINT64 k, devusage = 0xffffffffffffffff;
     space* devdh = NULL;
     LIST_ENTRY* le;
@@ -453,7 +470,7 @@
             num_stripes = max_stripes;
     } else {
         for (i = 0; i < max_stripes; i++) {
-            if (!find_new_stripe(Vcb, stripes, i, max_stripe_size, type))
+            if (!find_new_stripe(Vcb, stripes, i, max_stripe_size))
                 break;
             else
                 num_stripes++;
@@ -552,10 +569,12 @@
     c->size = cisize;
     c->offset = logaddr;
     c->used = c->oldused = 0;
-    c->cache = NULL;
+    c->cache = c->old_cache = NULL;
     c->readonly = FALSE;
     c->reloc = FALSE;
     c->last_alloc_set = FALSE;
+    c->last_stripe = 0;
+    c->cache_loaded = TRUE;
     
     InitializeListHead(&c->space);
     InitializeListHead(&c->space_size);
@@ -563,7 +582,7 @@
     InitializeListHead(&c->changed_extents);
     
     InitializeListHead(&c->range_locks);
-    KeInitializeSpinLock(&c->range_locks_spinlock);
+    ExInitializeResourceLite(&c->range_locks_lock);
     KeInitializeEvent(&c->range_locks_event, NotificationEvent, FALSE);
     
     ExInitializeResourceLite(&c->lock);
@@ -585,7 +604,7 @@
     for (i = 0; i < num_stripes; i++) {
         stripes[i].device->devitem.bytes_used += stripe_size;
         
-        space_list_subtract2(Vcb, &stripes[i].device->space, NULL, cis[i].offset, stripe_size, NULL);
+        space_list_subtract2(&stripes[i].device->space, NULL, cis[i].offset, stripe_size, NULL, NULL);
     }
     
     success = TRUE;
@@ -631,11 +650,14 @@
     return success ? c : NULL;
 }
 
-static NTSTATUS prepare_raid0_write(chunk* c, UINT64 address, void* data, UINT32 length, write_stripe* stripes) {
+static NTSTATUS prepare_raid0_write(chunk* c, UINT64 address, void* data, UINT32 length, write_stripe* stripes, PIRP Irp, UINT32 irp_offset, write_data_context* wtc) {
     UINT64 startoff, endoff;
     UINT16 startoffstripe, endoffstripe, stripenum;
     UINT64 pos, *stripeoff;
     UINT32 i;
+    BOOL file_write = Irp && Irp->MdlAddress && (Irp->MdlAddress->ByteOffset == 0);
+    PMDL master_mdl;
+    PFN_NUMBER* pfns;
     
     stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG);
     if (!stripeoff) {
@@ -646,28 +668,64 @@
     get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &startoff, &startoffstripe);
     get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &endoff, &endoffstripe);
     
+    if (file_write) {
+        master_mdl = Irp->MdlAddress;
+        
+        pfns = (PFN_NUMBER*)(Irp->MdlAddress + 1);
+        pfns = &pfns[irp_offset >> PAGE_SHIFT];
+    } else if (((ULONG_PTR)data % PAGE_SIZE) != 0) {
+        wtc->scratch = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
+        if (!wtc->scratch) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlCopyMemory(wtc->scratch, data, length);
+        
+        master_mdl = IoAllocateMdl(wtc->scratch, length, FALSE, FALSE, NULL);
+        if (!master_mdl) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        MmBuildMdlForNonPagedPool(master_mdl);
+        
+        wtc->mdl = master_mdl;
+        
+        pfns = (PFN_NUMBER*)(master_mdl + 1);
+    } else {
+        master_mdl = IoAllocateMdl(data, length, FALSE, FALSE, NULL);
+        if (!master_mdl) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        MmProbeAndLockPages(master_mdl, KernelMode, IoReadAccess);
+        
+        wtc->mdl = master_mdl;
+        
+        pfns = (PFN_NUMBER*)(master_mdl + 1);
+    }
+    
     for (i = 0; i < c->chunk_item->num_stripes; i++) {
-        if (startoffstripe > i) {
+        if (startoffstripe > i)
             stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
-        } else if (startoffstripe == i) {
+        else if (startoffstripe == i)
             stripes[i].start = startoff;
-        } else {
+        else
             stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length);
-        }
         
-        if (endoffstripe > i) {
+        if (endoffstripe > i)
             stripes[i].end = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
-        } else if (endoffstripe == i) {
+        else if (endoffstripe == i)
             stripes[i].end = endoff + 1;
-        } else {
+        else
             stripes[i].end = endoff - (endoff % c->chunk_item->stripe_length);
-        }
         
         if (stripes[i].start != stripes[i].end) {
-            stripes[i].data = ExAllocatePoolWithTag(NonPagedPool, stripes[i].end - stripes[i].start, ALLOC_TAG);
-            
-            if (!stripes[i].data) {
-                ERR("out of memory\n");
+            stripes[i].mdl = IoAllocateMdl(NULL, stripes[i].end - stripes[i].start, FALSE, FALSE, NULL);
+            if (!stripes[i].mdl) {
+                ERR("IoAllocateMdl failed\n");
                 ExFreePool(stripeoff);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
@@ -678,23 +736,28 @@
     RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes);
     
     stripenum = startoffstripe;
+    
     while (pos < length) {
+        PFN_NUMBER* stripe_pfns = (PFN_NUMBER*)(stripes[stripenum].mdl + 1);
+        
         if (pos == 0) {
             UINT32 writelen = min(stripes[stripenum].end - stripes[stripenum].start,
-                                  c->chunk_item->stripe_length - (stripes[stripenum].start % c->chunk_item->stripe_length));
+                                    c->chunk_item->stripe_length - (stripes[stripenum].start % c->chunk_item->stripe_length));
             
-            RtlCopyMemory(stripes[stripenum].data, data, writelen);
+            RtlCopyMemory(stripe_pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            
             stripeoff[stripenum] += writelen;
             pos += writelen;
         } else if (length - pos < c->chunk_item->stripe_length) {
-            RtlCopyMemory(stripes[stripenum].data + stripeoff[stripenum], (UINT8*)data + pos, length - pos);
+            RtlCopyMemory(&stripe_pfns[stripeoff[stripenum] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (length - pos) * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
             break;
         } else {
-            RtlCopyMemory(stripes[stripenum].data + stripeoff[stripenum], (UINT8*)data + pos, c->chunk_item->stripe_length);
+            RtlCopyMemory(&stripe_pfns[stripeoff[stripenum] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            
             stripeoff[stripenum] += c->chunk_item->stripe_length;
             pos += c->chunk_item->stripe_length;
         }
-        
+    
         stripenum = (stripenum + 1) % c->chunk_item->num_stripes;
     }
 
@@ -703,11 +766,14 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS prepare_raid10_write(chunk* c, UINT64 address, void* data, UINT32 length, write_stripe* stripes) {
+static NTSTATUS prepare_raid10_write(chunk* c, UINT64 address, void* data, UINT32 length, write_stripe* stripes, PIRP Irp, UINT32 irp_offset, write_data_context* wtc) {
     UINT64 startoff, endoff;
     UINT16 startoffstripe, endoffstripe, stripenum;
     UINT64 pos, *stripeoff;
     UINT32 i;
+    BOOL file_write = Irp && Irp->MdlAddress && (Irp->MdlAddress->ByteOffset == 0);
+    PMDL master_mdl;
+    PFN_NUMBER* pfns;
 
     stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes / c->chunk_item->sub_stripes, ALLOC_TAG);
     if (!stripeoff) {
@@ -720,40 +786,75 @@
 
     startoffstripe *= c->chunk_item->sub_stripes;
     endoffstripe *= c->chunk_item->sub_stripes;
+    
+    if (file_write) {
+        master_mdl = Irp->MdlAddress;
+        
+        pfns = (PFN_NUMBER*)(Irp->MdlAddress + 1);
+        pfns = &pfns[irp_offset >> PAGE_SHIFT];
+    } else if (((ULONG_PTR)data % PAGE_SIZE) != 0) {
+        wtc->scratch = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
+        if (!wtc->scratch) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlCopyMemory(wtc->scratch, data, length);
+        
+        master_mdl = IoAllocateMdl(wtc->scratch, length, FALSE, FALSE, NULL);
+        if (!master_mdl) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        MmBuildMdlForNonPagedPool(master_mdl);
+        
+        wtc->mdl = master_mdl;
+        
+        pfns = (PFN_NUMBER*)(master_mdl + 1);
+    } else {
+        master_mdl = IoAllocateMdl(data, length, FALSE, FALSE, NULL);
+        if (!master_mdl) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        MmProbeAndLockPages(master_mdl, KernelMode, IoReadAccess);
+        
+        wtc->mdl = master_mdl;
+        
+        pfns = (PFN_NUMBER*)(master_mdl + 1);
+    }
 
     for (i = 0; i < c->chunk_item->num_stripes; i += c->chunk_item->sub_stripes) {
         UINT16 j;
         
-        if (startoffstripe > i) {
+        if (startoffstripe > i)
             stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
-        } else if (startoffstripe == i) {
+        else if (startoffstripe == i)
             stripes[i].start = startoff;
-        } else {
+        else
             stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length);
-        }
         
-        if (endoffstripe > i) {
+        if (endoffstripe > i)
             stripes[i].end = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
-        } else if (endoffstripe == i) {
+        else if (endoffstripe == i)
             stripes[i].end = endoff + 1;
-        } else {
+        else
             stripes[i].end = endoff - (endoff % c->chunk_item->stripe_length);
+
+        stripes[i].mdl = IoAllocateMdl(NULL, stripes[i].end - stripes[i].start, FALSE, FALSE, NULL);
+        if (!stripes[i].mdl) {
+            ERR("IoAllocateMdl failed\n");
+            ExFreePool(stripeoff);
+            return STATUS_INSUFFICIENT_RESOURCES;
         }
         
-        if (stripes[i].start != stripes[i].end) {
-            stripes[i].data = ExAllocatePoolWithTag(NonPagedPool, stripes[i].end - stripes[i].start, ALLOC_TAG);
-            
-            if (!stripes[i].data) {
-                ERR("out of memory\n");
-                ExFreePool(stripeoff);
-                return STATUS_INSUFFICIENT_RESOURCES;
-            }
-        }
-        
         for (j = 1; j < c->chunk_item->sub_stripes; j++) {
             stripes[i+j].start = stripes[i].start;
             stripes[i+j].end = stripes[i].end;
             stripes[i+j].data = stripes[i].data;
+            stripes[i+j].mdl = stripes[i].mdl;
         }
     }
 
@@ -761,23 +862,28 @@
     RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes / c->chunk_item->sub_stripes);
 
     stripenum = startoffstripe / c->chunk_item->sub_stripes;
+    
     while (pos < length) {
+        PFN_NUMBER* stripe_pfns = (PFN_NUMBER*)(stripes[stripenum * c->chunk_item->sub_stripes].mdl + 1);
+        
         if (pos == 0) {
             UINT32 writelen = min(stripes[stripenum * c->chunk_item->sub_stripes].end - stripes[stripenum * c->chunk_item->sub_stripes].start,
-                                  c->chunk_item->stripe_length - (stripes[stripenum * c->chunk_item->sub_stripes].start % c->chunk_item->stripe_length));
+                                    c->chunk_item->stripe_length - (stripes[stripenum * c->chunk_item->sub_stripes].start % c->chunk_item->stripe_length));
             
-            RtlCopyMemory(stripes[stripenum * c->chunk_item->sub_stripes].data, data, writelen);
+            RtlCopyMemory(stripe_pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            
             stripeoff[stripenum] += writelen;
             pos += writelen;
         } else if (length - pos < c->chunk_item->stripe_length) {
-            RtlCopyMemory(stripes[stripenum * c->chunk_item->sub_stripes].data + stripeoff[stripenum], (UINT8*)data + pos, length - pos);
+            RtlCopyMemory(&stripe_pfns[stripeoff[stripenum] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (length - pos) * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
             break;
         } else {
-            RtlCopyMemory(stripes[stripenum * c->chunk_item->sub_stripes].data + stripeoff[stripenum], (UINT8*)data + pos, c->chunk_item->stripe_length);
+            RtlCopyMemory(&stripe_pfns[stripeoff[stripenum] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            
             stripeoff[stripenum] += c->chunk_item->stripe_length;
             pos += c->chunk_item->stripe_length;
         }
-        
+
         stripenum = (stripenum + 1) % (c->chunk_item->num_stripes / c->chunk_item->sub_stripes);
     }
 
@@ -786,755 +892,1155 @@
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS STDCALL read_stripe_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID ptr) {
-    read_stripe* stripe = ptr;
-    read_stripe_master* master = stripe->master;
-    ULONG stripes_left = InterlockedDecrement(&master->stripes_left);
+static NTSTATUS STDCALL async_read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+    read_context_stripe* stripe = conptr;
+    LONG left = InterlockedDecrement(&stripe->context->left);
     
+    UNUSED(DeviceObject);
+    
     stripe->iosb = Irp->IoStatus;
     
-    if (stripes_left == 0)
-        KeSetEvent(&master->event, 0, FALSE);
+    if (left == 0)
+        KeSetEvent(&stripe->context->Event, 0, FALSE);
     
     return STATUS_MORE_PROCESSING_REQUIRED;
 }
 
-static NTSTATUS make_read_irp(PIRP old_irp, read_stripe* stripe, UINT64 offset, void* data, UINT32 length) {
+static NTSTATUS async_read_phys(read_context* context, read_context_stripe* stripe, PDEVICE_OBJECT DeviceObject, UINT64 StartingOffset, ULONG Length, PUCHAR Buffer) {
+    LARGE_INTEGER Offset;
+    PIRP Irp;
     PIO_STACK_LOCATION IrpSp;
-    PIRP Irp;
+    NTSTATUS Status;
+
+    Offset.QuadPart = StartingOffset;
+
+    Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
     
-    if (!old_irp) {
-        Irp = IoAllocateIrp(stripe->devobj->StackSize, FALSE);
-        
-        if (!Irp) {
-            ERR("IoAllocateIrp failed\n");
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
-    } else {
-        Irp = IoMakeAssociatedIrp(old_irp, stripe->devobj->StackSize);
-        
-        if (!Irp) {
-            ERR("IoMakeAssociatedIrp failed\n");
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
+    if (!Irp) {
+        ERR("IoAllocateIrp failed\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto fail;
     }
     
     IrpSp = IoGetNextIrpStackLocation(Irp);
     IrpSp->MajorFunction = IRP_MJ_READ;
     
-    if (stripe->devobj->Flags & DO_BUFFERED_IO) {
-        FIXME("FIXME - buffered IO\n");
-        IoFreeIrp(Irp);
-        return STATUS_INTERNAL_ERROR;
-    } else if (stripe->devobj->Flags & DO_DIRECT_IO) {
-        Irp->MdlAddress = IoAllocateMdl(data, length, FALSE, FALSE, NULL);
+    if (DeviceObject->Flags & DO_BUFFERED_IO) {
+        Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, Length, ALLOC_TAG);
+        if (!Irp->AssociatedIrp.SystemBuffer) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto fail;
+        }
+
+        Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
+
+        Irp->UserBuffer = Buffer;
+    } else if (DeviceObject->Flags & DO_DIRECT_IO) {
+        Irp->MdlAddress = IoAllocateMdl(Buffer, Length, FALSE, FALSE, NULL);
         if (!Irp->MdlAddress) {
             ERR("IoAllocateMdl failed\n");
-            IoFreeIrp(Irp);
-            return STATUS_INSUFFICIENT_RESOURCES;
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto fail;
         }
         
         MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
-    } else {
-        Irp->UserBuffer = data;
-    }
+    } else
+        Irp->UserBuffer = Buffer;
 
-    IrpSp->Parameters.Read.Length = length;
-    IrpSp->Parameters.Read.ByteOffset.QuadPart = offset;
+    IrpSp->Parameters.Read.Length = Length;
+    IrpSp->Parameters.Read.ByteOffset = Offset;
     
+    stripe->context = context;
+    stripe->Irp = Irp;
     Irp->UserIosb = &stripe->iosb;
     
-    IoSetCompletionRoutine(Irp, read_stripe_completion, stripe, TRUE, TRUE, TRUE);
+    IoSetCompletionRoutine(Irp, async_read_completion, stripe, TRUE, TRUE, TRUE);
+
+    Status = IoCallDriver(DeviceObject, Irp);
     
-    stripe->Irp = Irp;
+    if (Status != STATUS_PENDING && !NT_SUCCESS(Status)) {
+        ERR("read returned %08x\n", Status);
+        
+        if (Irp->MdlAddress) {
+            MmUnlockPages(Irp->MdlAddress);
+            IoFreeMdl(Irp->MdlAddress);
+        }
+        
+        goto fail;
+    }
     
     return STATUS_SUCCESS;
+    
+fail:
+    IoFreeIrp(Irp);
+    
+    return Status;
 }
 
-static NTSTATUS prepare_raid5_write(PIRP Irp, chunk* c, UINT64 address, void* data, UINT32 length, write_stripe* stripes) {
-    UINT64 startoff, endoff;
-    UINT16 startoffstripe, endoffstripe, stripenum, parity, logstripe;
-    UINT64 start = 0xffffffffffffffff, end = 0;
-    UINT64 pos, stripepos;
-    UINT32 firststripesize, laststripesize;
+typedef struct {
+    PMDL mdl;
+    PFN_NUMBER* pfns;
+} log_stripe;
+
+static NTSTATUS prepare_raid5_write(device_extension* Vcb, chunk* c, UINT64 address, void* data, UINT32 length, write_stripe* stripes, PIRP Irp,
+                                    UINT32 irp_offset, write_data_context* wtc) {
+    UINT64 startoff, endoff, parity_start, parity_end;
+    UINT16 startoffstripe, endoffstripe, parity;
+    UINT64 pos, parity_pos, *stripeoff = NULL;
     UINT32 i;
-    UINT8* data2 = (UINT8*)data;
-    UINT32 num_reads;
-    BOOL same_stripe = FALSE, multiple_stripes;
+    BOOL file_write = Irp && Irp->MdlAddress && (Irp->MdlAddress->ByteOffset == 0);
+    PMDL master_mdl, fragment_mdl = NULL;
+    NTSTATUS Status;
+    PFN_NUMBER *pfns, *parity_pfns, *fragment_pfns;
+    ULONG fragment_len = 0, num_fragments = 0, frag_num = 0;
+    log_stripe* log_stripes = NULL;
+    UINT8 *fragments = NULL, *fragments2;
+    CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+    read_context context;
     
+    context.stripes = NULL;
+    
     get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes - 1, &startoff, &startoffstripe);
     get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes - 1, &endoff, &endoffstripe);
     
-    for (i = 0; i < c->chunk_item->num_stripes - 1; i++) {
-        UINT64 ststart, stend;
+    pos = 0;
+    while (pos < length) {
+        parity = (((address - c->offset + pos) / ((c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 1) % c->chunk_item->num_stripes;
         
-        if (startoffstripe > i) {
-            ststart = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
-        } else if (startoffstripe == i) {
-            ststart = startoff;
+        if (pos == 0) {
+            UINT16 stripe = (parity + startoffstripe + 1) % c->chunk_item->num_stripes;
+            ULONG skip, writelen;
+            
+            i = startoffstripe;
+            while (stripe != parity) {
+                if (i == startoffstripe) {
+                    writelen = min(length, c->chunk_item->stripe_length - (startoff % c->chunk_item->stripe_length));
+                    
+                    stripes[stripe].start = startoff;
+                    stripes[stripe].end = startoff + writelen;
+                    
+                    pos += writelen;
+                    
+                    if (pos == length)
+                        break;
+                } else {
+                    writelen = min(length - pos, c->chunk_item->stripe_length);
+                    
+                    stripes[stripe].start = startoff - (startoff % c->chunk_item->stripe_length);
+                    stripes[stripe].end = stripes[stripe].start + writelen;
+                    
+                    pos += writelen;
+                    
+                    if (pos == length)
+                        break;
+                }
+                
+                i++;
+                stripe = (stripe + 1) % c->chunk_item->num_stripes;
+            }
+            
+            if (pos == length)
+                break;
+            
+            for (i = 0; i < startoffstripe; i++) {
+                stripe = (parity + i + 1) % c->chunk_item->num_stripes;
+                
+                stripes[stripe].start = stripes[stripe].end = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
+            }
+            
+            stripes[parity].start = stripes[parity].end = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
+            
+            if (length - pos > c->chunk_item->num_stripes * (c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length) {
+                skip = ((length - pos) / (c->chunk_item->num_stripes * (c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length)) - 1;
+                
+                for (i = 0; i < c->chunk_item->num_stripes; i++) {
+                    stripes[i].end += skip * c->chunk_item->num_stripes * c->chunk_item->stripe_length;
+                }
+                
+                pos += skip * (c->chunk_item->num_stripes - 1) * c->chunk_item->num_stripes * c->chunk_item->stripe_length;
+            }
+        } else if (length - pos >= c->chunk_item->stripe_length * (c->chunk_item->num_stripes - 1)) {
+            for (i = 0; i < c->chunk_item->num_stripes; i++) {
+                stripes[i].end += c->chunk_item->stripe_length;
+            }
+            
+            pos += c->chunk_item->stripe_length * (c->chunk_item->num_stripes - 1);
         } else {
-            ststart = startoff - (startoff % c->chunk_item->stripe_length);
-        }
-
-        if (endoffstripe > i) {
-            stend = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
-        } else if (endoffstripe == i) {
-            stend = endoff + 1;
-        } else {
-            stend = endoff - (endoff % c->chunk_item->stripe_length);
-        }
-
-        if (ststart != stend) {
-            stripes[i].start = ststart;
-            stripes[i].end = stend;
+            UINT16 stripe = (parity + 1) % c->chunk_item->num_stripes;
             
-            if (ststart < start) {
-                start = ststart;
-                firststripesize = c->chunk_item->stripe_length - (ststart % c->chunk_item->stripe_length);
+            i = 0;
+            while (stripe != parity) {
+                if (endoffstripe == i) {
+                    stripes[stripe].end = endoff + 1;
+                    break;
+                } else if (endoffstripe > i)
+                    stripes[stripe].end = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
+                
+                i++;
+                stripe = (stripe + 1) % c->chunk_item->num_stripes;
             }
-
-            if (stend > end) {
-                end = stend;
-                laststripesize = stend % c->chunk_item->stripe_length;
-                if (laststripesize == 0)
-                    laststripesize = c->chunk_item->stripe_length;
-            }
+            
+            break;
         }
     }
     
-    if (start == end) {
-        ERR("error: start == end (%llx)\n", start);
-        return STATUS_INTERNAL_ERROR;
-    }
+    parity_start = 0xffffffffffffffff;
+    parity_end = 0;
     
-    if (startoffstripe == endoffstripe && start / c->chunk_item->stripe_length == end / c->chunk_item->stripe_length) {
-        firststripesize = end - start;
-        laststripesize = firststripesize;
-    }
-
     for (i = 0; i < c->chunk_item->num_stripes; i++) {
-        stripes[i].data = ExAllocatePoolWithTag(NonPagedPool, end - start, ALLOC_TAG);
-        if (!stripes[i].data) {
-            ERR("out of memory\n");
-            return STATUS_INSUFFICIENT_RESOURCES;
+        if (stripes[i].start != 0 || stripes[i].end != 0) {
+            parity_start = min(stripes[i].start, parity_start);
+            parity_end = max(stripes[i].end, parity_end);
         }
-        
-        if (i < c->chunk_item->num_stripes - 1) {
-            if (stripes[i].start == 0 && stripes[i].end == 0)
-                stripes[i].start = stripes[i].end = start;
-        }
     }
     
-    num_reads = 0;
-    multiple_stripes = (end - 1) / c->chunk_item->stripe_length != start / c->chunk_item->stripe_length;
+    if (parity_end == parity_start) {
+        Status = STATUS_SUCCESS;
+        goto exit;
+    }
     
-    for (i = 0; i < c->chunk_item->num_stripes - 1; i++) {
-        if (stripes[i].start == stripes[i].end) {
-            num_reads++;
-            
-            if (multiple_stripes)
-                num_reads++;
+    parity = (((address - c->offset) / ((c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 1) % c->chunk_item->num_stripes;
+    stripes[parity].start = parity_start;
+    
+    parity = (((address - c->offset + length - 1) / ((c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 1) % c->chunk_item->num_stripes;
+    stripes[parity].end = parity_end;
+    
+    for (i = 0; i < c->chunk_item->num_stripes; i++) {
+        if (stripes[i].start == 0 && stripes[i].end == 0) {
+            fragment_len += parity_end - parity_start;
+            num_fragments++;
         } else {
-            if (stripes[i].start > start)
-                num_reads++;
+            if (stripes[i].start > parity_start) {
+                fragment_len += stripes[i].start - parity_start;
+                num_fragments++;
+            }
             
-            if (stripes[i].end < end)
-                num_reads++;
+            if (stripes[i].end < parity_end) {
+                fragment_len += parity_end - stripes[i].end;
+                num_fragments++;
+            }
         }
     }
     
-    if (num_reads > 0) {
-        UINT32 j;
-        read_stripe_master* master;
-        read_stripe* read_stripes;
-        CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
-        NTSTATUS Status;
+    if (fragment_len > 0) {
+        fragments = ExAllocatePoolWithTag(NonPagedPool, fragment_len, ALLOC_TAG);
+        if (!fragments) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
+
+        fragment_mdl = IoAllocateMdl(fragments, fragment_len, FALSE, FALSE, NULL);
+        if (!fragment_mdl) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
         
-        master = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_stripe_master), ALLOC_TAG);
-        if (!master) {
+        MmBuildMdlForNonPagedPool(fragment_mdl);
+        
+        fragment_pfns = (PFN_NUMBER*)(fragment_mdl + 1);
+        
+        context.stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context_stripe) * num_fragments, ALLOC_TAG);
+        if (!context.stripes) {
             ERR("out of memory\n");
-            return STATUS_INSUFFICIENT_RESOURCES;
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
         }
         
-        read_stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_stripe) * num_reads, ALLOC_TAG);
-        if (!read_stripes) {
+        RtlZeroMemory(context.stripes, sizeof(read_context_stripe) * num_fragments);
+        
+        KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
+        context.total = context.left = num_fragments;
+    }
+    
+    log_stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(log_stripe) * (c->chunk_item->num_stripes - 1), ALLOC_TAG);
+    if (!log_stripes) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+    
+    RtlZeroMemory(log_stripes, sizeof(log_stripe) * (c->chunk_item->num_stripes - 1));
+    
+    for (i = 0; i < c->chunk_item->num_stripes - 1; i++) {
+        log_stripes[i].mdl = IoAllocateMdl(NULL, parity_end - parity_start, FALSE, FALSE, NULL);
+        if (!log_stripes[i].mdl) {
             ERR("out of memory\n");
-            ExFreePool(master);
-            return STATUS_INSUFFICIENT_RESOURCES;
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
         }
         
-        parity = (((address - c->offset) / ((c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 1) % c->chunk_item->num_stripes;
-        stripenum = (parity + 1) % c->chunk_item->num_stripes;
+        log_stripes[i].mdl->MdlFlags |= MDL_PARTIAL;
+        log_stripes[i].pfns = (PFN_NUMBER*)(log_stripes[i].mdl + 1);
+    }
+    
+    parity = (((address - c->offset) / ((c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 1) % c->chunk_item->num_stripes;
+    
+    if (fragment_len > 0) {
+        UINT16 stripe = (parity + 1) % c->chunk_item->num_stripes;
         
-        j = 0;
+        fragments2 = fragments;
+        
         for (i = 0; i < c->chunk_item->num_stripes - 1; i++) {
-            if (stripes[i].start > start || stripes[i].start == stripes[i].end) {
-                ULONG readlen;
+            if ((stripes[stripe].start == 0 && stripes[stripe].end == 0) || stripes[stripe].start > parity_start) {
+                ULONG len;
                 
-                read_stripes[j].Irp = NULL;
-                read_stripes[j].devobj = c->devices[stripenum]->devobj;
-                read_stripes[j].master = master;
-                
-                if (stripes[i].start != stripes[i].end)
-                    readlen = stripes[i].start - start;
+                if (stripes[stripe].start == 0 && stripes[stripe].end == 0)
+                    len = parity_end - parity_start;
                 else
-                    readlen = firststripesize;
+                    len = stripes[stripe].start - parity_start;
                 
-                Status = make_read_irp(Irp, &read_stripes[j], start + cis[stripenum].offset, stripes[stripenum].data, readlen);
-                
+                context.stripes[frag_num].dev = c->devices[stripe];
+                Status = async_read_phys(&context, &context.stripes[frag_num], c->devices[stripe]->devobj, cis[stripe].offset + parity_start, len, fragments2);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("make_read_irp returned %08x\n", Status);
-                    j++;
-                    goto readend;
+                    ERR("async_read_phys returned %08x\n", Status);
+                    goto exit;
                 }
                 
-                stripes[stripenum].skip_start = readlen;
+                frag_num++;
                 
-                j++;
-                if (j == num_reads) break;
+                RtlCopyMemory(log_stripes[i].pfns, fragment_pfns, len * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                log_stripes[i].pfns += len >> PAGE_SHIFT;
+                
+                fragments2 += len;
+                fragment_pfns += len >> PAGE_SHIFT;
             }
             
-            stripenum = (stripenum + 1) % c->chunk_item->num_stripes;
+            stripe = (stripe + 1) % c->chunk_item->num_stripes;
         }
+    }
+    
+    parity = (((address - c->offset + length - 1) / ((c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 1) % c->chunk_item->num_stripes;
+    
+    if (fragment_len > 0) {
+        UINT16 stripe = (parity + 1) % c->chunk_item->num_stripes;
         
-        if (j < num_reads) {
-            parity = (((address + length - 1 - c->offset) / ((c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 1) % c->chunk_item->num_stripes;
-            stripenum = (parity + 1) % c->chunk_item->num_stripes;
-            
-            for (i = 0; i < c->chunk_item->num_stripes - 1; i++) {
-                if ((stripes[i].start != stripes[i].end && stripes[i].end < end) || (stripes[i].start == stripes[i].end && multiple_stripes)) {
-                    read_stripes[j].Irp = NULL;
-                    read_stripes[j].devobj = c->devices[stripenum]->devobj;
-                    read_stripes[j].master = master;
+        for (i = 0; i < c->chunk_item->num_stripes - 1; i++) {
+            if ((stripes[stripe].start != 0 || stripes[stripe].end != 0) && stripes[stripe].end < parity_end) {
+                ULONG len = parity_end - stripes[stripe].end;
+                PFN_NUMBER* pl;
                 
-                    if (stripes[i].start == stripes[i].end) {
-                        Status = make_read_irp(Irp, &read_stripes[j], start + firststripesize + cis[stripenum].offset, &stripes[stripenum].data[firststripesize], laststripesize);
-                        stripes[stripenum].skip_end = laststripesize;
-                    } else {
-                        Status = make_read_irp(Irp, &read_stripes[j], stripes[i].end + cis[stripenum].offset, &stripes[stripenum].data[stripes[i].end - start], end - stripes[i].end);
-                        stripes[stripenum].skip_end = end - stripes[i].end;
-                    }
-                    
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("make_read_irp returned %08x\n", Status);
-                        j++;
-                        goto readend;
-                    }
-                    
-                    j++;
-                    if (j == num_reads) break;
+                context.stripes[frag_num].dev = c->devices[stripe];
+                Status = async_read_phys(&context, &context.stripes[frag_num], c->devices[stripe]->devobj, cis[stripe].offset + stripes[stripe].end, len, fragments2);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("async_read_phys returned %08x\n", Status);
+                    goto exit;
                 }
                 
-                stripenum = (stripenum + 1) % c->chunk_item->num_stripes;
+                frag_num++;
+                
+                pl = (PFN_NUMBER*)(log_stripes[i].mdl + 1);
+                
+                RtlCopyMemory(&pl[(parity_end - parity_start - len) >> PAGE_SHIFT], fragment_pfns, len * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                fragments2 += len;
+                fragment_pfns += len >> PAGE_SHIFT;
             }
+            
+            stripe = (stripe + 1) % c->chunk_item->num_stripes;
         }
+    }
+    
+    wtc->parity1 = ExAllocatePoolWithTag(NonPagedPool, parity_end - parity_start, ALLOC_TAG);
+    if (!wtc->parity1) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+    
+    wtc->parity1_mdl = IoAllocateMdl(wtc->parity1, parity_end - parity_start, FALSE, FALSE, NULL);
+    if (!wtc->parity1_mdl) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+    
+    MmBuildMdlForNonPagedPool(wtc->parity1_mdl);
+    
+    if (file_write)
+        master_mdl = Irp->MdlAddress;
+    else if (((ULONG_PTR)data % PAGE_SIZE) != 0) {
+        wtc->scratch = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
+        if (!wtc->scratch) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
         
-        master->stripes_left = j;
-        KeInitializeEvent(&master->event, NotificationEvent, FALSE);
+        RtlCopyMemory(wtc->scratch, data, length);
         
-        for (i = 0; i < j; i++) {
-            Status = IoCallDriver(read_stripes[i].devobj, read_stripes[i].Irp);
-            if (!NT_SUCCESS(Status)) {
-                ERR("IoCallDriver returned %08x\n", Status);
-                goto readend;
-            }
+        master_mdl = IoAllocateMdl(wtc->scratch, length, FALSE, FALSE, NULL);
+        if (!master_mdl) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
         }
         
-        KeWaitForSingleObject(&master->event, Executive, KernelMode, FALSE, NULL);
+        MmBuildMdlForNonPagedPool(master_mdl);
         
-        for (i = 0; i < j; i++) {
-            if (!NT_SUCCESS(read_stripes[i].iosb.Status)) {
-                Status = read_stripes[i].iosb.Status;
-                goto readend;
-            }
+        wtc->mdl = master_mdl;
+    } else {
+        master_mdl = IoAllocateMdl(data, length, FALSE, FALSE, NULL);
+        if (!master_mdl) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
         }
         
-        Status = STATUS_SUCCESS;
-
-readend:
-        for (i = 0; i < j; i++) {
-            if (read_stripes[i].Irp) {
-                if (read_stripes[i].devobj->Flags & DO_DIRECT_IO) {
-                    MmUnlockPages(read_stripes[i].Irp->MdlAddress);
-                    IoFreeMdl(read_stripes[i].Irp->MdlAddress);
-                }
-                
-                IoFreeIrp(read_stripes[i].Irp); // FIXME - what if IoCallDriver fails and other Irps are still running?
+        MmProbeAndLockPages(master_mdl, KernelMode, IoReadAccess);
+        
+        wtc->mdl = master_mdl;
+    }
+    
+    pfns = (PFN_NUMBER*)(master_mdl + 1);
+    parity_pfns = (PFN_NUMBER*)(wtc->parity1_mdl + 1);
+    
+    for (i = 0; i < c->chunk_item->num_stripes; i++) {
+        if (stripes[i].start != stripes[i].end) {
+            stripes[i].mdl = IoAllocateMdl((UINT8*)MmGetMdlVirtualAddress(master_mdl) + irp_offset, stripes[i].end - stripes[i].start, FALSE, FALSE, NULL);
+            if (!stripes[i].mdl) {
+                ERR("IoAllocateMdl failed\n");
+                return STATUS_INSUFFICIENT_RESOURCES;
             }
         }
-        
-        ExFreePool(read_stripes);
-        ExFreePool(master);
-        
-        if (!NT_SUCCESS(Status))
-            return Status;
     }
     
+    stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG);
+    if (!stripeoff) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+    
+    RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes);
+    
     pos = 0;
+    parity_pos = 0;
     
-    parity = (((address - c->offset) / ((c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 1) % c->chunk_item->num_stripes;
-    stripepos = 0;
-    
-    if ((address - c->offset) % (c->chunk_item->stripe_length * (c->chunk_item->num_stripes - 1)) > 0) {
-        UINT16 firstdata;
-        BOOL first = TRUE;
+    while (pos < length) {
+        PFN_NUMBER* stripe_pfns;
         
-        stripenum = (parity + 1) % c->chunk_item->num_stripes;
+        parity = (((address - c->offset + pos) / ((c->chunk_item->num_stripes - 1) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 1) % c->chunk_item->num_stripes;
         
-        for (logstripe = 0; logstripe < c->chunk_item->num_stripes - 1; logstripe++) {
-            ULONG copylen;
+        if (pos == 0) {
+            UINT16 stripe = (parity + startoffstripe + 1) % c->chunk_item->num_stripes;
+            UINT32 writelen = min(length - pos, min(stripes[stripe].end - stripes[stripe].start,
+                                                    c->chunk_item->stripe_length - (stripes[stripe].start % c->chunk_item->stripe_length)));
+            UINT32 maxwritelen = writelen;
             
-            if (pos >= length)
-                break;
+            stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
             
-            if (stripes[logstripe].start < start + firststripesize && stripes[logstripe].start != stripes[logstripe].end) {
-                copylen = min(start + firststripesize - stripes[logstripe].start, length - pos);
+            RtlCopyMemory(stripe_pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            
+            RtlCopyMemory(log_stripes[startoffstripe].pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            log_stripes[startoffstripe].pfns += writelen >> PAGE_SHIFT;
+            
+            stripeoff[stripe] = writelen;
+            pos += writelen;
+            
+            stripe = (stripe + 1) % c->chunk_item->num_stripes;
+            i = startoffstripe + 1;
+            
+            while (stripe != parity) {
+                stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
+                writelen = min(length - pos, min(stripes[stripe].end - stripes[stripe].start, c->chunk_item->stripe_length));
                 
-                if (!first && copylen < c->chunk_item->stripe_length) {
-                    same_stripe = TRUE;
+                if (writelen == 0)
                     break;
-                }
-
-                RtlCopyMemory(&stripes[stripenum].data[firststripesize - copylen], &data2[pos], copylen);
                 
-                pos += copylen;
-                first = FALSE;
+                if (writelen > maxwritelen)
+                    maxwritelen = writelen;
+                
+                RtlCopyMemory(stripe_pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                log_stripes[i].pfns += writelen >> PAGE_SHIFT;
+                
+                stripeoff[stripe] = writelen;
+                pos += writelen;
+                
+                stripe = (stripe + 1) % c->chunk_item->num_stripes;
+                i++;
             }
             
-            stripenum = (stripenum + 1) % c->chunk_item->num_stripes;
+            stripe_pfns = (PFN_NUMBER*)(stripes[parity].mdl + 1);
+            
+            RtlCopyMemory(stripe_pfns, parity_pfns, maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            stripeoff[parity] = maxwritelen;
+            parity_pos = maxwritelen;
+        } else if (length - pos >= c->chunk_item->stripe_length * (c->chunk_item->num_stripes - 1)) {
+            UINT16 stripe = (parity + 1) % c->chunk_item->num_stripes;
+            
+            i = 0;
+            while (stripe != parity) {
+                stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
+                
+                RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                log_stripes[i].pfns += c->chunk_item->stripe_length >> PAGE_SHIFT;
+                
+                stripeoff[stripe] += c->chunk_item->stripe_length;
+                pos += c->chunk_item->stripe_length;
+                
+                stripe = (stripe + 1) % c->chunk_item->num_stripes;
+                i++;
+            }
+            
+            stripe_pfns = (PFN_NUMBER*)(stripes[parity].mdl + 1);
+            
+            RtlCopyMemory(&stripe_pfns[stripeoff[parity] >> PAGE_SHIFT], &parity_pfns[parity_pos >> PAGE_SHIFT], c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            stripeoff[parity] += c->chunk_item->stripe_length;
+            parity_pos += c->chunk_item->stripe_length;
+        } else {
+            UINT16 stripe = (parity + 1) % c->chunk_item->num_stripes;
+            UINT32 writelen, maxwritelen = 0;
+            
+            i = 0;
+            while (pos < length) {
+                stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
+                writelen = min(length - pos, min(stripes[stripe].end - stripes[stripe].start, c->chunk_item->stripe_length));
+                
+                if (writelen == 0)
+                    break;
+                
+                if (writelen > maxwritelen)
+                    maxwritelen = writelen;
+                
+                RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                log_stripes[i].pfns += writelen >> PAGE_SHIFT;
+                
+                stripeoff[stripe] += writelen;
+                pos += writelen;
+                
+                stripe = (stripe + 1) % c->chunk_item->num_stripes;
+                i++;
+            }
+            
+            stripe_pfns = (PFN_NUMBER*)(stripes[parity].mdl + 1);
+            
+            RtlCopyMemory(&stripe_pfns[stripeoff[parity] >> PAGE_SHIFT], &parity_pfns[parity_pos >> PAGE_SHIFT], maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
         }
+    }
+    
+    if (fragment_len > 0) {
+        KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
         
-        firstdata = parity == 0 ? 1 : 0;
-        
-        RtlCopyMemory(stripes[parity].data, stripes[firstdata].data, firststripesize);
-        
-        for (i = firstdata + 1; i < c->chunk_item->num_stripes; i++) {
-            if (i != parity)
-                do_xor(&stripes[parity].data[0], &stripes[i].data[0], firststripesize);
+        for (i = 0; i < context.total; i++) {
+            if (!NT_SUCCESS(context.stripes[i].iosb.Status)) {
+                Status = context.stripes[i].iosb.Status;
+                ERR("read returned %08x\n", Status);
+                log_device_error(Vcb, context.stripes[i].dev, BTRFS_DEV_STAT_READ_ERRORS);
+                goto exit;
+            }
         }
+    }
+
+    for (i = 0; i < c->chunk_item->num_stripes - 1; i++) {
+        UINT8* ss = MmGetSystemAddressForMdlSafe(log_stripes[i].mdl, NormalPagePriority);
         
-        if (!same_stripe) {
-            stripepos = firststripesize;
-            parity = (parity + 1) % c->chunk_item->num_stripes;
-        }
+        if (i == 0)
+            RtlCopyMemory(wtc->parity1, ss, parity_end - parity_start);
+        else
+            do_xor(wtc->parity1, ss, parity_end - parity_start);
     }
     
-    while (length >= pos + c->chunk_item->stripe_length * (c->chunk_item->num_stripes - 1)) {
-        UINT16 firstdata;
-        
-        stripenum = (parity + 1) % c->chunk_item->num_stripes;
-        
+    Status = STATUS_SUCCESS;
+    
+exit:
+    if (log_stripes) {
         for (i = 0; i < c->chunk_item->num_stripes - 1; i++) {
-            RtlCopyMemory(&stripes[stripenum].data[stripepos], &data2[pos], c->chunk_item->stripe_length);
-            
-            pos += c->chunk_item->stripe_length;
-            stripenum = (stripenum +1) % c->chunk_item->num_stripes;
+            if (log_stripes[i].mdl)
+                IoFreeMdl(log_stripes[i].mdl);
         }
         
-        firstdata = parity == 0 ? 1 : 0;
-        
-        RtlCopyMemory(&stripes[parity].data[stripepos], &stripes[firstdata].data[stripepos], c->chunk_item->stripe_length);
-        
-        for (i = firstdata + 1; i < c->chunk_item->num_stripes; i++) {
-            if (i != parity)
-                do_xor(&stripes[parity].data[stripepos], &stripes[i].data[stripepos], c->chunk_item->stripe_length);
-        }
-        
-        parity = (parity + 1) % c->chunk_item->num_stripes;
-        stripepos += c->chunk_item->stripe_length;
+        ExFreePool(log_stripes);
     }
     
-    if (pos < length) {
-        UINT16 firstdata;
-        
-        if (!same_stripe) {
-            stripenum = (parity + 1) % c->chunk_item->num_stripes;
-            i = 0;
-        } else
-            i = logstripe;
-        
-        while (pos < length) {
-            ULONG copylen;
+    if (context.stripes) {
+        if (context.total > 0) {
+            KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
             
-            copylen = min(stripes[i].end - start - stripepos, length - pos);
-
-            RtlCopyMemory(&stripes[stripenum].data[stripepos], &data2[pos], copylen);
-            
-            pos += copylen;
-            stripenum = (stripenum + 1) % c->chunk_item->num_stripes;
-            i++;
+            for (i = 0; i < context.total; i++) {
+                if (context.stripes[i].Irp && context.stripes[i].Irp->MdlAddress) {
+                    MmUnlockPages(context.stripes[i].Irp->MdlAddress);
+                    IoFreeMdl(context.stripes[i].Irp->MdlAddress);
+                }
+            }
         }
         
-        firstdata = parity == 0 ? 1 : 0;
-        
-        RtlCopyMemory(&stripes[parity].data[stripepos], &stripes[firstdata].data[stripepos], laststripesize);
-        
-        for (i = firstdata + 1; i < c->chunk_item->num_stripes; i++) {
-            if (i != parity)
-                do_xor(&stripes[parity].data[stripepos], &stripes[i].data[stripepos], laststripesize);
-        }
+        ExFreePool(context.stripes);
     }
     
-    for (i = 0; i < c->chunk_item->num_stripes; i++) {
-        stripes[i].start = start;
-        stripes[i].end = end;
-    }
+    if (fragment_mdl)
+        IoFreeMdl(fragment_mdl);
     
-    return STATUS_SUCCESS;
+    if (fragments)
+        ExFreePool(fragments);
+
+    if (stripeoff)
+        ExFreePool(stripeoff);
+    
+    return Status;
 }
 
-static NTSTATUS prepare_raid6_write(PIRP Irp, chunk* c, UINT64 address, void* data, UINT32 length, write_stripe* stripes) {
-    UINT64 startoff, endoff;
-    UINT16 startoffstripe, endoffstripe, stripenum, parity1, parity2, logstripe;
-    UINT64 start = 0xffffffffffffffff, end = 0;
-    UINT64 pos, stripepos;
-    UINT32 firststripesize, laststripesize;
+static NTSTATUS prepare_raid6_write(device_extension* Vcb, chunk* c, UINT64 address, void* data, UINT32 length, write_stripe* stripes, PIRP Irp,
+                                    UINT32 irp_offset, write_data_context* wtc) {
+    UINT64 startoff, endoff, parity_start, parity_end;
+    UINT16 startoffstripe, endoffstripe, parity1;
+    UINT64 pos, parity_pos, *stripeoff = NULL;
     UINT32 i;
-    UINT8* data2 = (UINT8*)data;
-    UINT32 num_reads;
-    BOOL same_stripe = FALSE, multiple_stripes;
+    BOOL file_write = Irp && Irp->MdlAddress && (Irp->MdlAddress->ByteOffset == 0);
+    PMDL master_mdl, fragment_mdl = NULL;
+    NTSTATUS Status;
+    PFN_NUMBER *pfns, *parity1_pfns, *parity2_pfns, *fragment_pfns;
+    ULONG fragment_len = 0, num_fragments = 0, frag_num = 0;
+    log_stripe* log_stripes = NULL;
+    UINT8 *fragments = NULL, *fragments2;
+    CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
+    read_context context;
     
+    context.stripes = NULL;
+    
     get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes - 2, &startoff, &startoffstripe);
     get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes - 2, &endoff, &endoffstripe);
     
-    for (i = 0; i < c->chunk_item->num_stripes - 2; i++) {
-        UINT64 ststart, stend;
+    pos = 0;
+    while (pos < length) {
+        parity1 = (((address - c->offset + pos) / ((c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 2) % c->chunk_item->num_stripes;
         
-        if (startoffstripe > i) {
-            ststart = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
-        } else if (startoffstripe == i) {
-            ststart = startoff;
+        if (pos == 0) {
+            UINT16 stripe = (parity1 + startoffstripe + 2) % c->chunk_item->num_stripes;
+            UINT16 parity2 = (parity1 + 1) % c->chunk_item->num_stripes;
+            ULONG skip, writelen;
+            
+            i = startoffstripe;
+            while (stripe != parity1) {
+                if (i == startoffstripe) {
+                    writelen = min(length, c->chunk_item->stripe_length - (startoff % c->chunk_item->stripe_length));
+                    
+                    stripes[stripe].start = startoff;
+                    stripes[stripe].end = startoff + writelen;
+                    
+                    pos += writelen;
+                    
+                    if (pos == length)
+                        break;
+                } else {
+                    writelen = min(length - pos, c->chunk_item->stripe_length);
+                    
+                    stripes[stripe].start = startoff - (startoff % c->chunk_item->stripe_length);
+                    stripes[stripe].end = stripes[stripe].start + writelen;
+                    
+                    pos += writelen;
+                    
+                    if (pos == length)
+                        break;
+                }
+                
+                i++;
+                stripe = (stripe + 1) % c->chunk_item->num_stripes;
+            }
+            
+            if (pos == length)
+                break;
+            
+            for (i = 0; i < startoffstripe; i++) {
+                stripe = (parity1 + i + 2) % c->chunk_item->num_stripes;
+                
+                stripes[stripe].start = stripes[stripe].end = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
+            }
+            
+            stripes[parity1].start = stripes[parity1].end = stripes[parity2].start = stripes[parity2].end = 
+                startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
+            
+            if (length - pos > c->chunk_item->num_stripes * (c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length) {
+                skip = ((length - pos) / (c->chunk_item->num_stripes * (c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length)) - 1;
+                
+                for (i = 0; i < c->chunk_item->num_stripes; i++) {
+                    stripes[i].end += skip * c->chunk_item->num_stripes * c->chunk_item->stripe_length;
+                }
+                
+                pos += skip * (c->chunk_item->num_stripes - 2) * c->chunk_item->num_stripes * c->chunk_item->stripe_length;
+            }
+        } else if (length - pos >= c->chunk_item->stripe_length * (c->chunk_item->num_stripes - 2)) {
+            for (i = 0; i < c->chunk_item->num_stripes; i++) {
+                stripes[i].end += c->chunk_item->stripe_length;
+            }
+            
+            pos += c->chunk_item->stripe_length * (c->chunk_item->num_stripes - 2);
         } else {
-            ststart = startoff - (startoff % c->chunk_item->stripe_length);
-        }
-
-        if (endoffstripe > i) {
-            stend = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
-        } else if (endoffstripe == i) {
-            stend = endoff + 1;
-        } else {
-            stend = endoff - (endoff % c->chunk_item->stripe_length);
-        }
-
-        if (ststart != stend) {
-            stripes[i].start = ststart;
-            stripes[i].end = stend;
+            UINT16 stripe = (parity1 + 2) % c->chunk_item->num_stripes;
             
-            if (ststart < start) {
-                start = ststart;
-                firststripesize = c->chunk_item->stripe_length - (ststart % c->chunk_item->stripe_length);
+            i = 0;
+            while (stripe != parity1) {
+                if (endoffstripe == i) {
+                    stripes[stripe].end = endoff + 1;
+                    break;
+                } else if (endoffstripe > i)
+                    stripes[stripe].end = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
+                
+                i++;
+                stripe = (stripe + 1) % c->chunk_item->num_stripes;
             }
-
-            if (stend > end) {
-                end = stend;
-                laststripesize = stend % c->chunk_item->stripe_length;
-                if (laststripesize == 0)
-                    laststripesize = c->chunk_item->stripe_length;
-            }
+            
+            break;
         }
     }
     
-    if (start == end) {
-        ERR("error: start == end (%llx)\n", start);
-        return STATUS_INTERNAL_ERROR;
-    }
+    parity_start = 0xffffffffffffffff;
+    parity_end = 0;
     
-    if (startoffstripe == endoffstripe && start / c->chunk_item->stripe_length == end / c->chunk_item->stripe_length) {
-        firststripesize = end - start;
-        laststripesize = firststripesize;
-    }
-
     for (i = 0; i < c->chunk_item->num_stripes; i++) {
-        stripes[i].data = ExAllocatePoolWithTag(NonPagedPool, end - start, ALLOC_TAG);
-        if (!stripes[i].data) {
-            ERR("out of memory\n");
-            return STATUS_INSUFFICIENT_RESOURCES;
+        if (stripes[i].start != 0 || stripes[i].end != 0) {
+            parity_start = min(stripes[i].start, parity_start);
+            parity_end = max(stripes[i].end, parity_end);
         }
-        
-        if (i < c->chunk_item->num_stripes - 2) {
-            if (stripes[i].start == 0 && stripes[i].end == 0)
-                stripes[i].start = stripes[i].end = start;
-        }
     }
     
-    num_reads = 0;
-    multiple_stripes = (end - 1) / c->chunk_item->stripe_length != start / c->chunk_item->stripe_length;
+    if (parity_end == parity_start) {
+        Status = STATUS_SUCCESS;
+        goto exit;
+    }
     
-    for (i = 0; i < c->chunk_item->num_stripes - 2; i++) {
-        if (stripes[i].start == stripes[i].end) {
-            num_reads++;
-            
-            if (multiple_stripes)
-                num_reads++;
+    parity1 = (((address - c->offset) / ((c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 2) % c->chunk_item->num_stripes;
+    stripes[parity1].start = stripes[(parity1 + 1) % c->chunk_item->num_stripes].start = parity_start;
+    
+    parity1 = (((address - c->offset + length - 1) / ((c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 2) % c->chunk_item->num_stripes;
+    stripes[parity1].end = stripes[(parity1 + 1) % c->chunk_item->num_stripes].end = parity_end;
+    
+    for (i = 0; i < c->chunk_item->num_stripes; i++) {
+        if (stripes[i].start == 0 && stripes[i].end == 0) {
+            fragment_len += parity_end - parity_start;
+            num_fragments++;
         } else {
-            if (stripes[i].start > start)
-                num_reads++;
+            if (stripes[i].start > parity_start) {
+                fragment_len += stripes[i].start - parity_start;
+                num_fragments++;
+            }
             
-            if (stripes[i].end < end)
-                num_reads++;
+            if (stripes[i].end < parity_end) {
+                fragment_len += parity_end - stripes[i].end;
+                num_fragments++;
+            }
         }
     }
     
-    if (num_reads > 0) {
-        UINT32 j;
-        read_stripe_master* master;
-        read_stripe* read_stripes;
-        CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
-        NTSTATUS Status;
+    if (fragment_len > 0) {
+        fragments = ExAllocatePoolWithTag(NonPagedPool, fragment_len, ALLOC_TAG);
+        if (!fragments) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
+
+        fragment_mdl = IoAllocateMdl(fragments, fragment_len, FALSE, FALSE, NULL);
+        if (!fragment_mdl) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
         
-        master = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_stripe_master), ALLOC_TAG);
-        if (!master) {
+        MmBuildMdlForNonPagedPool(fragment_mdl);
+        
+        fragment_pfns = (PFN_NUMBER*)(fragment_mdl + 1);
+        
+        context.stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context_stripe) * num_fragments, ALLOC_TAG);
+        if (!context.stripes) {
             ERR("out of memory\n");
-            return STATUS_INSUFFICIENT_RESOURCES;
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
         }
         
-        read_stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_stripe) * num_reads, ALLOC_TAG);
-        if (!read_stripes) {
+        RtlZeroMemory(context.stripes, sizeof(read_context_stripe) * num_fragments);
+        
+        KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
+        context.total = context.left = num_fragments;
+    }
+    
+    log_stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(log_stripe) * (c->chunk_item->num_stripes - 2), ALLOC_TAG);
+    if (!log_stripes) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+    
+    RtlZeroMemory(log_stripes, sizeof(log_stripe) * (c->chunk_item->num_stripes - 2));
+    
+    for (i = 0; i < c->chunk_item->num_stripes - 2; i++) {
+        log_stripes[i].mdl = IoAllocateMdl(NULL, parity_end - parity_start, FALSE, FALSE, NULL);
+        if (!log_stripes[i].mdl) {
             ERR("out of memory\n");
-            ExFreePool(master);
-            return STATUS_INSUFFICIENT_RESOURCES;
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
         }
         
-        parity1 = (((address - c->offset) / ((c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 2) % c->chunk_item->num_stripes;
-        stripenum = (parity1 + 2) % c->chunk_item->num_stripes;
+        log_stripes[i].mdl->MdlFlags |= MDL_PARTIAL;
+        log_stripes[i].pfns = (PFN_NUMBER*)(log_stripes[i].mdl + 1);
+    }
+    
+    parity1 = (((address - c->offset) / ((c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 2) % c->chunk_item->num_stripes;
+    
+    if (fragment_len > 0) {
+        UINT16 stripe = (parity1 + 2) % c->chunk_item->num_stripes;
         
-        j = 0;
+        fragments2 = fragments;
+        
         for (i = 0; i < c->chunk_item->num_stripes - 2; i++) {
-            if (stripes[i].start > start || stripes[i].start == stripes[i].end) {
-                ULONG readlen;
+            if ((stripes[stripe].start == 0 && stripes[stripe].end == 0) || stripes[stripe].start > parity_start) {
+                ULONG len;
                 
-                read_stripes[j].Irp = NULL;
-                read_stripes[j].devobj = c->devices[stripenum]->devobj;
-                read_stripes[j].master = master;
-                
-                if (stripes[i].start != stripes[i].end)
-                    readlen = stripes[i].start - start;
+                if (stripes[stripe].start == 0 && stripes[stripe].end == 0)
+                    len = parity_end - parity_start;
                 else
-                    readlen = firststripesize;
+                    len = stripes[stripe].start - parity_start;
                 
-                Status = make_read_irp(Irp, &read_stripes[j], start + cis[stripenum].offset, stripes[stripenum].data, readlen);
-                
+                context.stripes[frag_num].dev = c->devices[stripe];
+                Status = async_read_phys(&context, &context.stripes[frag_num], c->devices[stripe]->devobj, cis[stripe].offset + parity_start, len, fragments2);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("make_read_irp returned %08x\n", Status);
-                    j++;
-                    goto readend;
+                    ERR("async_read_phys returned %08x\n", Status);
+                    goto exit;
                 }
                 
-                stripes[stripenum].skip_start = readlen;
+                frag_num++;
                 
-                j++;
-                if (j == num_reads) break;
+                RtlCopyMemory(log_stripes[i].pfns, fragment_pfns, len * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                log_stripes[i].pfns += len >> PAGE_SHIFT;
+                
+                fragments2 += len;
+                fragment_pfns += len >> PAGE_SHIFT;
             }
             
-            stripenum = (stripenum + 1) % c->chunk_item->num_stripes;
+            stripe = (stripe + 1) % c->chunk_item->num_stripes;
         }
+    }
+    
+    parity1 = (((address - c->offset + length - 1) / ((c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 2) % c->chunk_item->num_stripes;
+    
+    if (fragment_len > 0) {
+        UINT16 stripe = (parity1 + 2) % c->chunk_item->num_stripes;
         
-        if (j < num_reads) {
-            parity1 = (((address + length - 1 - c->offset) / ((c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 2) % c->chunk_item->num_stripes;
-            stripenum = (parity1 + 2) % c->chunk_item->num_stripes;
-            
-            for (i = 0; i < c->chunk_item->num_stripes - 2; i++) {
-                if ((stripes[i].start != stripes[i].end && stripes[i].end < end) || (stripes[i].start == stripes[i].end && multiple_stripes)) {
-                    read_stripes[j].Irp = NULL;
-                    read_stripes[j].devobj = c->devices[stripenum]->devobj;
-                    read_stripes[j].master = master;
+        for (i = 0; i < c->chunk_item->num_stripes - 2; i++) {
+            if ((stripes[stripe].start != 0 || stripes[stripe].end != 0) && stripes[stripe].end < parity_end) {
+                ULONG len = parity_end - stripes[stripe].end;
+                PFN_NUMBER* pl;
                 
-                    if (stripes[i].start == stripes[i].end) {
-                        Status = make_read_irp(Irp, &read_stripes[j], start + firststripesize + cis[stripenum].offset, &stripes[stripenum].data[firststripesize], laststripesize);
-                        stripes[stripenum].skip_end = laststripesize;
-                    } else {
-                        Status = make_read_irp(Irp, &read_stripes[j], stripes[i].end + cis[stripenum].offset, &stripes[stripenum].data[stripes[i].end - start], end - stripes[i].end);
-                        stripes[stripenum].skip_end = end - stripes[i].end;
-                    }
-                    
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("make_read_irp returned %08x\n", Status);
-                        j++;
-                        goto readend;
-                    }
-                    
-                    j++;
-                    if (j == num_reads) break;
+                context.stripes[frag_num].dev = c->devices[stripe];
+                Status = async_read_phys(&context, &context.stripes[frag_num], c->devices[stripe]->devobj, cis[stripe].offset + stripes[stripe].end, len, fragments2);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("async_read_phys returned %08x\n", Status);
+                    goto exit;
                 }
                 
-                stripenum = (stripenum + 1) % c->chunk_item->num_stripes;
+                frag_num++;
+                
+                pl = (PFN_NUMBER*)(log_stripes[i].mdl + 1);
+                
+                RtlCopyMemory(&pl[(parity_end - parity_start - len) >> PAGE_SHIFT], fragment_pfns, len * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                fragments2 += len;
+                fragment_pfns += len >> PAGE_SHIFT;
             }
+            
+            stripe = (stripe + 1) % c->chunk_item->num_stripes;
         }
+    }
+    
+    wtc->parity1 = ExAllocatePoolWithTag(NonPagedPool, parity_end - parity_start, ALLOC_TAG);
+    if (!wtc->parity1) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+    
+    wtc->parity2 = ExAllocatePoolWithTag(NonPagedPool, parity_end - parity_start, ALLOC_TAG);
+    if (!wtc->parity2) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+    
+    wtc->parity1_mdl = IoAllocateMdl(wtc->parity1, parity_end - parity_start, FALSE, FALSE, NULL);
+    if (!wtc->parity1_mdl) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+    
+    MmBuildMdlForNonPagedPool(wtc->parity1_mdl);
+    
+    wtc->parity2_mdl = IoAllocateMdl(wtc->parity2, parity_end - parity_start, FALSE, FALSE, NULL);
+    if (!wtc->parity2_mdl) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+    
+    MmBuildMdlForNonPagedPool(wtc->parity2_mdl);
+    
+    if (file_write)
+        master_mdl = Irp->MdlAddress;
+    else if (((ULONG_PTR)data % PAGE_SIZE) != 0) {
+        wtc->scratch = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
+        if (!wtc->scratch) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
         
-        master->stripes_left = j;
-        KeInitializeEvent(&master->event, NotificationEvent, FALSE);
+        RtlCopyMemory(wtc->scratch, data, length);
         
-        for (i = 0; i < j; i++) {
-            Status = IoCallDriver(read_stripes[i].devobj, read_stripes[i].Irp);
-            if (!NT_SUCCESS(Status)) {
-                ERR("IoCallDriver returned %08x\n", Status);
-                goto readend;
-            }
+        master_mdl = IoAllocateMdl(wtc->scratch, length, FALSE, FALSE, NULL);
+        if (!master_mdl) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
         }
         
-        KeWaitForSingleObject(&master->event, Executive, KernelMode, FALSE, NULL);
+        MmBuildMdlForNonPagedPool(master_mdl);
         
-        for (i = 0; i < j; i++) {
-            if (!NT_SUCCESS(read_stripes[i].iosb.Status)) {
-                Status = read_stripes[i].iosb.Status;
-                goto readend;
-            }
+        wtc->mdl = master_mdl;
+    } else {
+        master_mdl = IoAllocateMdl(data, length, FALSE, FALSE, NULL);
+        if (!master_mdl) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
         }
         
-        Status = STATUS_SUCCESS;
-
-readend:
-        for (i = 0; i < j; i++) {
-            if (read_stripes[i].Irp) {
-                if (read_stripes[i].devobj->Flags & DO_DIRECT_IO) {
-                    MmUnlockPages(read_stripes[i].Irp->MdlAddress);
-                    IoFreeMdl(read_stripes[i].Irp->MdlAddress);
-                }
-                
-                IoFreeIrp(read_stripes[i].Irp); // FIXME - what if IoCallDriver fails and other Irps are still running?
+        MmProbeAndLockPages(master_mdl, KernelMode, IoReadAccess);
+        
+        wtc->mdl = master_mdl;
+    }
+    
+    pfns = (PFN_NUMBER*)(master_mdl + 1);
+    parity1_pfns = (PFN_NUMBER*)(wtc->parity1_mdl + 1);
+    parity2_pfns = (PFN_NUMBER*)(wtc->parity2_mdl + 1);
+    
+    for (i = 0; i < c->chunk_item->num_stripes; i++) {
+        if (stripes[i].start != stripes[i].end) {
+            stripes[i].mdl = IoAllocateMdl((UINT8*)MmGetMdlVirtualAddress(master_mdl) + irp_offset, stripes[i].end - stripes[i].start, FALSE, FALSE, NULL);
+            if (!stripes[i].mdl) {
+                ERR("IoAllocateMdl failed\n");
+                return STATUS_INSUFFICIENT_RESOURCES;
             }
         }
-        
-        ExFreePool(read_stripes);
-        ExFreePool(master);
-        
-        if (!NT_SUCCESS(Status))
-            return Status;
     }
     
+    stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG);
+    if (!stripeoff) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+    
+    RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes);
+    
     pos = 0;
+    parity_pos = 0;
     
-    parity1 = (((address - c->offset) / ((c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 2) % c->chunk_item->num_stripes;
-    parity2 = (parity1 + 1) % c->chunk_item->num_stripes;
-    stripepos = 0;
-    
-    if ((address - c->offset) % (c->chunk_item->stripe_length * (c->chunk_item->num_stripes - 2)) > 0) {
-        BOOL first = TRUE;
+    while (pos < length) {
+        PFN_NUMBER* stripe_pfns;
         
-        stripenum = (parity2 + 1) % c->chunk_item->num_stripes;
+        parity1 = (((address - c->offset + pos) / ((c->chunk_item->num_stripes - 2) * c->chunk_item->stripe_length)) + c->chunk_item->num_stripes - 2) % c->chunk_item->num_stripes;
         
-        for (logstripe = 0; logstripe < c->chunk_item->num_stripes - 2; logstripe++) {
-            ULONG copylen;
+        if (pos == 0) {
+            UINT16 stripe = (parity1 + startoffstripe + 2) % c->chunk_item->num_stripes, parity2;
+            UINT32 writelen = min(length - pos, min(stripes[stripe].end - stripes[stripe].start,
+                                                    c->chunk_item->stripe_length - (stripes[stripe].start % c->chunk_item->stripe_length)));
+            UINT32 maxwritelen = writelen;
             
-            if (pos >= length)
-                break;
+            stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
             
-            if (stripes[logstripe].start < start + firststripesize && stripes[logstripe].start != stripes[logstripe].end) {
-                copylen = min(start + firststripesize - stripes[logstripe].start, length - pos);
+            RtlCopyMemory(stripe_pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            
+            RtlCopyMemory(log_stripes[startoffstripe].pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            log_stripes[startoffstripe].pfns += writelen >> PAGE_SHIFT;
+            
+            stripeoff[stripe] = writelen;
+            pos += writelen;
+            
+            stripe = (stripe + 1) % c->chunk_item->num_stripes;
+            i = startoffstripe + 1;
+            
+            while (stripe != parity1) {
+                stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
+                writelen = min(length - pos, min(stripes[stripe].end - stripes[stripe].start, c->chunk_item->stripe_length));
                 
-                if (!first && copylen < c->chunk_item->stripe_length) {
-                    same_stripe = TRUE;
+                if (writelen == 0)
                     break;
-                }
-
-                RtlCopyMemory(&stripes[stripenum].data[firststripesize - copylen], &data2[pos], copylen);
                 
-                pos += copylen;
-                first = FALSE;
+                if (writelen > maxwritelen)
+                    maxwritelen = writelen;
+                
+                RtlCopyMemory(stripe_pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                log_stripes[i].pfns += writelen >> PAGE_SHIFT;
+                
+                stripeoff[stripe] = writelen;
+                pos += writelen;
+                
+                stripe = (stripe + 1) % c->chunk_item->num_stripes;
+                i++;
             }
             
-            stripenum = (stripenum + 1) % c->chunk_item->num_stripes;
+            stripe_pfns = (PFN_NUMBER*)(stripes[parity1].mdl + 1);
+            RtlCopyMemory(stripe_pfns, parity1_pfns, maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            stripeoff[parity1] = maxwritelen;
+            
+            parity2 = (parity1 + 1) % c->chunk_item->num_stripes;
+            
+            stripe_pfns = (PFN_NUMBER*)(stripes[parity2].mdl + 1);
+            RtlCopyMemory(stripe_pfns, parity2_pfns, maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            stripeoff[parity2] = maxwritelen;
+            
+            parity_pos = maxwritelen;
+        } else if (length - pos >= c->chunk_item->stripe_length * (c->chunk_item->num_stripes - 2)) {
+            UINT16 stripe = (parity1 + 2) % c->chunk_item->num_stripes, parity2;
+            
+            i = 0;
+            while (stripe != parity1) {
+                stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
+                
+                RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                log_stripes[i].pfns += c->chunk_item->stripe_length >> PAGE_SHIFT;
+                
+                stripeoff[stripe] += c->chunk_item->stripe_length;
+                pos += c->chunk_item->stripe_length;
+                
+                stripe = (stripe + 1) % c->chunk_item->num_stripes;
+                i++;
+            }
+            
+            stripe_pfns = (PFN_NUMBER*)(stripes[parity1].mdl + 1);
+            RtlCopyMemory(&stripe_pfns[stripeoff[parity1] >> PAGE_SHIFT], &parity1_pfns[parity_pos >> PAGE_SHIFT], c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            stripeoff[parity1] += c->chunk_item->stripe_length;
+            
+            parity2 = (parity1 + 1) % c->chunk_item->num_stripes;
+            
+            stripe_pfns = (PFN_NUMBER*)(stripes[parity2].mdl + 1);
+            RtlCopyMemory(&stripe_pfns[stripeoff[parity2] >> PAGE_SHIFT], &parity2_pfns[parity_pos >> PAGE_SHIFT], c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            stripeoff[parity2] += c->chunk_item->stripe_length;
+            
+            parity_pos += c->chunk_item->stripe_length;
+        } else {
+            UINT16 stripe = (parity1 + 2) % c->chunk_item->num_stripes, parity2;
+            UINT32 writelen, maxwritelen = 0;
+            
+            i = 0;
+            while (pos < length) {
+                stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
+                writelen = min(length - pos, min(stripes[stripe].end - stripes[stripe].start, c->chunk_item->stripe_length));
+                
+                if (writelen == 0)
+                    break;
+                
+                if (writelen > maxwritelen)
+                    maxwritelen = writelen;
+                
+                RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                
+                RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+                log_stripes[i].pfns += writelen >> PAGE_SHIFT;
+                
+                stripeoff[stripe] += writelen;
+                pos += writelen;
+                
+                stripe = (stripe + 1) % c->chunk_item->num_stripes;
+                i++;
+            }
+            
+            stripe_pfns = (PFN_NUMBER*)(stripes[parity1].mdl + 1);
+            RtlCopyMemory(&stripe_pfns[stripeoff[parity1] >> PAGE_SHIFT], &parity1_pfns[parity_pos >> PAGE_SHIFT], maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
+            
+            parity2 = (parity1 + 1) % c->chunk_item->num_stripes;
+            
+            stripe_pfns = (PFN_NUMBER*)(stripes[parity2].mdl + 1);
+            RtlCopyMemory(&stripe_pfns[stripeoff[parity2] >> PAGE_SHIFT], &parity2_pfns[parity_pos >> PAGE_SHIFT], maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
         }
+    }
+    
+    if (fragment_len > 0) {
+        KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
         
-        i = parity1 == 0 ? (c->chunk_item->num_stripes - 1) : (parity1 - 1);
-        RtlCopyMemory(stripes[parity1].data, stripes[i].data, firststripesize);
-        RtlCopyMemory(stripes[parity2].data, stripes[i].data, firststripesize);
-        i = i == 0 ? (c->chunk_item->num_stripes - 1) : (i - 1);
+        for (i = 0; i < context.total; i++) {
+            if (!NT_SUCCESS(context.stripes[i].iosb.Status)) {
+                Status = context.stripes[i].iosb.Status;
+                ERR("read returned %08x\n", Status);
+                log_device_error(Vcb, context.stripes[i].dev, BTRFS_DEV_STAT_READ_ERRORS);
+                goto exit;
+            }
+        }
+    }
+
+    for (i = 0; i < c->chunk_item->num_stripes - 2; i++) {
+        UINT8* ss = MmGetSystemAddressForMdlSafe(log_stripes[c->chunk_item->num_stripes - 3 - i].mdl, NormalPagePriority);
         
-        do {
-            do_xor(stripes[parity1].data, stripes[i].data, firststripesize);
+        if (i == 0) {
+            RtlCopyMemory(wtc->parity1, ss, parity_end - parity_start);
+            RtlCopyMemory(wtc->parity2, ss, parity_end - parity_start);
+        } else {
+            do_xor(wtc->parity1, ss, parity_end - parity_start);
             
-            galois_double(stripes[parity2].data, firststripesize);
-            do_xor(stripes[parity2].data, stripes[i].data, firststripesize);
-            
-            i = i == 0 ? (c->chunk_item->num_stripes - 1) : (i - 1);
-        } while (i != parity2);
-        
-        if (!same_stripe) {
-            stripepos = firststripesize;
-            parity1 = parity2;
-            parity2 = (parity2 + 1) % c->chunk_item->num_stripes;
+            galois_double(wtc->parity2, parity_end - parity_start);
+            do_xor(wtc->parity2, ss, parity_end - parity_start);
         }
     }
     
-    while (length >= pos + c->chunk_item->stripe_length * (c->chunk_item->num_stripes - 2)) {
-        stripenum = (parity2 + 1) % c->chunk_item->num_stripes;
-        
+    Status = STATUS_SUCCESS;
+    
+exit:
+    if (log_stripes) {
         for (i = 0; i < c->chunk_item->num_stripes - 2; i++) {
-            RtlCopyMemory(&stripes[stripenum].data[stripepos], &data2[pos], c->chunk_item->stripe_length);
-            
-            pos += c->chunk_item->stripe_length;
-            stripenum = (stripenum +1) % c->chunk_item->num_stripes;
+            if (log_stripes[i].mdl)
+                IoFreeMdl(log_stripes[i].mdl);
         }
         
-        i = parity1 == 0 ? (c->chunk_item->num_stripes - 1) : (parity1 - 1);
-        RtlCopyMemory(&stripes[parity1].data[stripepos], &stripes[i].data[stripepos], c->chunk_item->stripe_length);
-        RtlCopyMemory(&stripes[parity2].data[stripepos], &stripes[i].data[stripepos], c->chunk_item->stripe_length);
-        i = i == 0 ? (c->chunk_item->num_stripes - 1) : (i - 1);
-
-        do {
-            do_xor(&stripes[parity1].data[stripepos], &stripes[i].data[stripepos], c->chunk_item->stripe_length);
-            
-            galois_double(&stripes[parity2].data[stripepos], c->chunk_item->stripe_length);
-            do_xor(&stripes[parity2].data[stripepos], &stripes[i].data[stripepos], c->chunk_item->stripe_length);
-            
-            i = i == 0 ? (c->chunk_item->num_stripes - 1) : (i - 1);
-        } while (i != parity2);
-        
-        parity1 = parity2;
-        parity2 = (parity2 + 1) % c->chunk_item->num_stripes;
-        stripepos += c->chunk_item->stripe_length;
+        ExFreePool(log_stripes);
     }
     
-    if (pos < length) {
-        if (!same_stripe) {
-            stripenum = (parity2 + 1) % c->chunk_item->num_stripes;
-            i = 0;
-        } else
-            i = logstripe;
-        
-        while (pos < length) {
-            ULONG copylen;
+    if (context.stripes) {
+        if (context.total > 0) {
+            KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
             
-            copylen = min(stripes[i].end - start - stripepos, length - pos);
-
-            RtlCopyMemory(&stripes[stripenum].data[stripepos], &data2[pos], copylen);
-            
-            pos += copylen;
-            stripenum = (stripenum + 1) % c->chunk_item->num_stripes;
-            i++;
+            for (i = 0; i < context.total; i++) {
+                if (context.stripes[i].Irp && context.stripes[i].Irp->MdlAddress) {
+                    MmUnlockPages(context.stripes[i].Irp->MdlAddress);
+                    IoFreeMdl(context.stripes[i].Irp->MdlAddress);
+                }
+            }
         }
         
-        i = parity1 == 0 ? (c->chunk_item->num_stripes - 1) : (parity1 - 1);
-        RtlCopyMemory(&stripes[parity1].data[stripepos], &stripes[i].data[stripepos], laststripesize);
-        RtlCopyMemory(&stripes[parity2].data[stripepos], &stripes[i].data[stripepos], laststripesize);
-        i = i == 0 ? (c->chunk_item->num_stripes - 1) : (i - 1);
-
-        do {
-            do_xor(&stripes[parity1].data[stripepos], &stripes[i].data[stripepos], laststripesize);
-            
-            galois_double(&stripes[parity2].data[stripepos], laststripesize);
-            do_xor(&stripes[parity2].data[stripepos], &stripes[i].data[stripepos], laststripesize);
-            
-            i = i == 0 ? (c->chunk_item->num_stripes - 1) : (i - 1);
-        } while (i != parity2);
+        ExFreePool(context.stripes);
     }
     
-    for (i = 0; i < c->chunk_item->num_stripes; i++) {
-        stripes[i].start = start;
-        stripes[i].end = end;
-    }
+    if (fragment_mdl)
+        IoFreeMdl(fragment_mdl);
     
-    return STATUS_SUCCESS;
+    if (fragments)
+        ExFreePool(fragments);
+
+    if (stripeoff)
+        ExFreePool(stripeoff);
+    
+    return Status;
 }
 
-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(device_extension* Vcb, UINT64 address, void* data, UINT32 length, write_data_context* wtc, PIRP Irp,
+                            chunk* c, BOOL file_write, UINT32 irp_offset) {
     NTSTATUS Status;
     UINT32 i;
     CHUNK_ITEM_STRIPE* cis;
     write_data_stripe* stripe;
     write_stripe* stripes = NULL;
-    BOOL need_free2;
+    UINT64 total_writing = 0;
     
     TRACE("(%p, %llx, %p, %x)\n", Vcb, address, data, length);
     
@@ -1557,60 +2063,61 @@
     cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
     
     if (c->chunk_item->type & BLOCK_FLAG_RAID0) {
-        Status = prepare_raid0_write(c, address, data, length, stripes);
+        Status = prepare_raid0_write(c, address, data, length, stripes, file_write ? Irp : NULL, irp_offset, wtc);
         if (!NT_SUCCESS(Status)) {
             ERR("prepare_raid0_write returned %08x\n", Status);
-            ExFreePool(stripes);
-            return Status;
+            goto prepare_failed;
         }
-        
-        if (need_free)
-            ExFreePool(data);
-
-        need_free2 = TRUE;
     } else if (c->chunk_item->type & BLOCK_FLAG_RAID10) {
-        Status = prepare_raid10_write(c, address, data, length, stripes);
+        Status = prepare_raid10_write(c, address, data, length, stripes, file_write ? Irp : NULL, irp_offset, wtc);
         if (!NT_SUCCESS(Status)) {
             ERR("prepare_raid10_write returned %08x\n", Status);
-            ExFreePool(stripes);
-            return Status;
+            goto prepare_failed;
         }
-        
-        if (need_free)
-            ExFreePool(data);
-
-        need_free2 = TRUE;
     } else if (c->chunk_item->type & BLOCK_FLAG_RAID5) {
-        Status = prepare_raid5_write(Irp, c, address, data, length, stripes);
+        Status = prepare_raid5_write(Vcb, c, address, data, length, stripes, file_write ? Irp : NULL, irp_offset, wtc);
         if (!NT_SUCCESS(Status)) {
             ERR("prepare_raid5_write returned %08x\n", Status);
-            ExFreePool(stripes);
-            return Status;
+            goto prepare_failed;
         }
-        
-        if (need_free)
-            ExFreePool(data);
-
-        need_free2 = TRUE;
     } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
-        Status = prepare_raid6_write(Irp, c, address, data, length, stripes);
+        Status = prepare_raid6_write(Vcb, c, address, data, length, stripes, file_write ? Irp : NULL, irp_offset, wtc);
         if (!NT_SUCCESS(Status)) {
             ERR("prepare_raid6_write returned %08x\n", Status);
-            ExFreePool(stripes);
-            return Status;
+            goto prepare_failed;
         }
-        
-        if (need_free)
-            ExFreePool(data);
-
-        need_free2 = TRUE;
     } else {  // write same data to every location - SINGLE, DUP, RAID1
         for (i = 0; i < c->chunk_item->num_stripes; i++) {
             stripes[i].start = address - c->offset;
             stripes[i].end = stripes[i].start + length;
             stripes[i].data = data;
+            stripes[i].irp_offset = irp_offset;
+            
+            if (file_write) {
+                UINT8* va;
+                ULONG writelen = stripes[i].end - stripes[i].start;
+                
+                va = (UINT8*)MmGetMdlVirtualAddress(Irp->MdlAddress) + stripes[i].irp_offset;
+                
+                stripes[i].mdl = IoAllocateMdl(va, writelen, FALSE, FALSE, NULL);
+                if (!stripes[i].mdl) {
+                    ERR("IoAllocateMdl failed\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto prepare_failed;
+                }
+                
+                IoBuildPartialMdl(Irp->MdlAddress, stripes[i].mdl, va, writelen);
+            } else {
+                stripes[i].mdl = IoAllocateMdl(stripes[i].data, stripes[i].end - stripes[i].start, FALSE, FALSE, NULL);
+                if (!stripes[i].mdl) {
+                    ERR("IoAllocateMdl failed\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto end;
+                }
+                
+                MmProbeAndLockPages(stripes[i].mdl, KernelMode, IoReadAccess);
+            }
         }
-        need_free2 = need_free;
     }
 
     for (i = 0; i < c->chunk_item->num_stripes; i++) {
@@ -1625,18 +2132,18 @@
             goto end;
         }
         
-        if (stripes[i].start + stripes[i].skip_start == stripes[i].end - stripes[i].skip_end || stripes[i].start == stripes[i].end) {
+        if (stripes[i].start == stripes[i].end) {
             stripe->status = WriteDataStatus_Ignore;
             stripe->Irp = NULL;
             stripe->buf = stripes[i].data;
-            stripe->need_free = need_free2;
+            stripe->mdl = NULL;
         } else {
             stripe->context = (struct _write_data_context*)wtc;
             stripe->buf = stripes[i].data;
-            stripe->need_free = need_free2;
             stripe->device = c->devices[i];
             RtlZeroMemory(&stripe->iosb, sizeof(IO_STATUS_BLOCK));
             stripe->status = WriteDataStatus_Pending;
+            stripe->mdl = stripes[i].mdl;
             
             if (!Irp) {
                 stripe->Irp = IoAllocateIrp(stripe->device->devobj->StackSize, FALSE);
@@ -1643,7 +2150,7 @@
             
                 if (!stripe->Irp) {
                     ERR("IoAllocateIrp failed\n");
-                    Status = STATUS_INTERNAL_ERROR;
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
                     goto end;
                 }
             } else {
@@ -1651,7 +2158,7 @@
                 
                 if (!stripe->Irp) {
                     ERR("IoMakeAssociatedIrp failed\n");
-                    Status = STATUS_INTERNAL_ERROR;
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
                     goto end;
                 }
             }
@@ -1660,34 +2167,26 @@
             IrpSp->MajorFunction = IRP_MJ_WRITE;
             
             if (stripe->device->devobj->Flags & DO_BUFFERED_IO) {
-                stripe->Irp->AssociatedIrp.SystemBuffer = stripes[i].data + stripes[i].skip_start;
+                stripe->Irp->AssociatedIrp.SystemBuffer = MmGetSystemAddressForMdlSafe(stripes[i].mdl, NormalPagePriority);
 
                 stripe->Irp->Flags = IRP_BUFFERED_IO;
-            } else if (stripe->device->devobj->Flags & DO_DIRECT_IO) {
-                stripe->Irp->MdlAddress = IoAllocateMdl(stripes[i].data + stripes[i].skip_start,
-                                                        stripes[i].end - stripes[i].start - stripes[i].skip_start - stripes[i].skip_end, FALSE, FALSE, NULL);
-                if (!stripe->Irp->MdlAddress) {
-                    ERR("IoAllocateMdl failed\n");
-                    Status = STATUS_INTERNAL_ERROR;
-                    goto end;
-                }
-                
-                MmProbeAndLockPages(stripe->Irp->MdlAddress, KernelMode, IoWriteAccess);
-            } else {
-                stripe->Irp->UserBuffer = stripes[i].data + stripes[i].skip_start;
-            }
+            } else if (stripe->device->devobj->Flags & DO_DIRECT_IO)
+                stripe->Irp->MdlAddress = stripe->mdl;
+            else
+                stripe->Irp->UserBuffer = MmGetSystemAddressForMdlSafe(stripes[i].mdl, NormalPagePriority);
             
 #ifdef DEBUG_PARANOID
-            if (stripes[i].end < stripes[i].start + stripes[i].skip_start + stripes[i].skip_end) {
-                ERR("trying to write stripe with negative length (%llx < %llx + %x + %x)\n",
-                    stripes[i].end, stripes[i].start, stripes[i].skip_start, stripes[i].skip_end);
+            if (stripes[i].end < stripes[i].start) {
+                ERR("trying to write stripe with negative length (%llx < %llx)\n", stripes[i].end, stripes[i].start);
                 int3;
             }
 #endif
 
-            IrpSp->Parameters.Write.Length = stripes[i].end - stripes[i].start - stripes[i].skip_start - stripes[i].skip_end;
-            IrpSp->Parameters.Write.ByteOffset.QuadPart = stripes[i].start + cis[i].offset + stripes[i].skip_start;
+            IrpSp->Parameters.Write.Length = stripes[i].end - stripes[i].start;
+            IrpSp->Parameters.Write.ByteOffset.QuadPart = stripes[i].start + cis[i].offset;
             
+            total_writing += IrpSp->Parameters.Write.Length;
+            
             stripe->Irp->UserIosb = &stripe->iosb;
             wtc->stripes_left++;
 
@@ -1697,6 +2196,9 @@
         InsertTailList(&wtc->stripes, &stripe->list_entry);
     }
     
+    if (diskacc)
+        FsRtlUpdateDiskCounters(0, total_writing);
+    
     Status = STATUS_SUCCESS;
     
 end:
@@ -1703,11 +2205,52 @@
 
     if (stripes) ExFreePool(stripes);
     
-    if (!NT_SUCCESS(Status)) {
+    if (!NT_SUCCESS(Status))
         free_write_data_stripes(wtc);
-        ExFreePool(wtc);
+    
+    return Status;
+    
+prepare_failed:
+    for (i = 0; i < c->chunk_item->num_stripes; i++) {
+        if (stripes[i].mdl && (i == 0 || stripes[i].mdl != stripes[i-1].mdl)) {
+            if (stripes[i].mdl->MdlFlags & MDL_PAGES_LOCKED)
+                MmUnlockPages(stripes[i].mdl);
+
+            IoFreeMdl(stripes[i].mdl);
+        }
     }
     
+    if (wtc->parity1_mdl) {
+        if (wtc->parity1_mdl->MdlFlags & MDL_PAGES_LOCKED)
+            MmUnlockPages(wtc->parity1_mdl);
+        
+        IoFreeMdl(wtc->parity1_mdl);
+    }
+    
+    if (wtc->parity2_mdl) {
+        if (wtc->parity2_mdl->MdlFlags & MDL_PAGES_LOCKED)
+            MmUnlockPages(wtc->parity2_mdl);
+        
+        IoFreeMdl(wtc->parity2_mdl);
+    }
+    
+    if (wtc->mdl) {
+        if (wtc->mdl->MdlFlags & MDL_PAGES_LOCKED)
+            MmUnlockPages(wtc->mdl);
+        
+        IoFreeMdl(wtc->mdl);
+    }
+    
+    if (wtc->parity1)
+        ExFreePool(wtc->parity1);
+    
+    if (wtc->parity2)
+        ExFreePool(wtc->parity2);
+    
+    if (wtc->scratch)
+        ExFreePool(wtc->scratch);
+
+    ExFreePool(stripes);
     return Status;
 }
 
@@ -1714,53 +2257,21 @@
 void get_raid56_lock_range(chunk* c, UINT64 address, UINT64 length, UINT64* lockaddr, UINT64* locklen) {
     UINT64 startoff, endoff;
     UINT16 startoffstripe, endoffstripe, datastripes;
-    UINT64 start = 0xffffffffffffffff, end = 0, logend;
-    UINT16 i;
     
     datastripes = c->chunk_item->num_stripes - (c->chunk_item->type & BLOCK_FLAG_RAID5 ? 1 : 2);
     
     get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, datastripes, &startoff, &startoffstripe);
     get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, datastripes, &endoff, &endoffstripe);
-
-    for (i = 0; i < datastripes; i++) {
-        UINT64 ststart, stend;
-        
-        if (startoffstripe > i) {
-            ststart = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
-        } else if (startoffstripe == i) {
-            ststart = startoff;
-        } else {
-            ststart = startoff - (startoff % c->chunk_item->stripe_length);
-        }
-
-        if (endoffstripe > i) {
-            stend = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
-        } else if (endoffstripe == i) {
-            stend = endoff + 1;
-        } else {
-            stend = endoff - (endoff % c->chunk_item->stripe_length);
-        }
-
-        if (ststart != stend) {
-            if (ststart < start)
-                start = ststart;
-
-            if (stend > end)
-                end = stend;
-        }
-    }
     
-    *lockaddr = c->offset + ((start / c->chunk_item->stripe_length) * c->chunk_item->stripe_length * datastripes) +
-                start % c->chunk_item->stripe_length;
-               
-    logend = c->offset + ((end / c->chunk_item->stripe_length) * c->chunk_item->stripe_length * datastripes);
-    logend += c->chunk_item->stripe_length * (datastripes - 1);
-    logend += end % c->chunk_item->stripe_length == 0 ? c->chunk_item->stripe_length : (end % c->chunk_item->stripe_length);
-    *locklen = logend - *lockaddr;
+    startoff -= startoff % c->chunk_item->stripe_length;
+    endoff = sector_align(endoff, c->chunk_item->stripe_length);
+    
+    *lockaddr = c->offset + (startoff * datastripes);
+    *locklen = (endoff - startoff) * datastripes;
 }
 
-NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp, chunk* c) {
-    write_data_context* wtc;
+NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp, chunk* c, BOOL file_write, UINT32 irp_offset) {
+    write_data_context wtc;
     NTSTATUS Status;
     UINT64 lockaddr, locklen;
 // #ifdef DEBUG_PARANOID
@@ -1767,16 +2278,12 @@
 //     UINT8* buf2;
 // #endif
     
-    wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_context), ALLOC_TAG);
-    if (!wtc) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-
-    KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE);
-    InitializeListHead(&wtc->stripes);
-    wtc->tree = FALSE;
-    wtc->stripes_left = 0;
+    KeInitializeEvent(&wtc.Event, NotificationEvent, FALSE);
+    InitializeListHead(&wtc.stripes);
+    wtc.tree = FALSE;
+    wtc.stripes_left = 0;
+    wtc.parity1 = wtc.parity2 = wtc.scratch = NULL;
+    wtc.mdl = wtc.parity1_mdl = wtc.parity2_mdl = NULL;
     
     if (!c) {
         c = get_chunk_from_address(Vcb, address);
@@ -1791,7 +2298,7 @@
         chunk_lock_range(Vcb, c, lockaddr, locklen);
     }
     
-    Status = write_data(Vcb, address, data, FALSE, length, wtc, Irp, c);
+    Status = write_data(Vcb, address, data, length, &wtc, Irp, c, file_write, irp_offset);
     if (!NT_SUCCESS(Status)) {
         ERR("write_data returned %08x\n", Status);
         
@@ -1798,15 +2305,14 @@
         if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6)
             chunk_unlock_range(Vcb, c, lockaddr, locklen);
         
-        free_write_data_stripes(wtc);
-        ExFreePool(wtc);
+        free_write_data_stripes(&wtc);
         return Status;
     }
     
-    if (wtc->stripes.Flink != &wtc->stripes) {
+    if (wtc.stripes.Flink != &wtc.stripes) {
         // launch writes and wait
-        LIST_ENTRY* le = wtc->stripes.Flink;
-        while (le != &wtc->stripes) {
+        LIST_ENTRY* le = wtc.stripes.Flink;
+        while (le != &wtc.stripes) {
             write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
             
             if (stripe->status != WriteDataStatus_Ignore)
@@ -1815,14 +2321,16 @@
             le = le->Flink;
         }
         
-        KeWaitForSingleObject(&wtc->Event, Executive, KernelMode, FALSE, NULL);
+        KeWaitForSingleObject(&wtc.Event, Executive, KernelMode, FALSE, NULL);
         
-        le = wtc->stripes.Flink;
-        while (le != &wtc->stripes) {
+        le = wtc.stripes.Flink;
+        while (le != &wtc.stripes) {
             write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
             
             if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) {
                 Status = stripe->iosb.Status;
+                
+                log_device_error(Vcb, stripe->device, BTRFS_DEV_STAT_WRITE_ERRORS);
                 break;
             }
             
@@ -1829,14 +2337,12 @@
             le = le->Flink;
         }
         
-        free_write_data_stripes(wtc);
+        free_write_data_stripes(&wtc);
     }
     
     if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6)
         chunk_unlock_range(Vcb, c, lockaddr, locklen);
 
-    ExFreePool(wtc);
-
 // #ifdef DEBUG_PARANOID
 //     buf2 = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
 //     Status = read_data(Vcb, address, length, NULL, FALSE, buf2, NULL, Irp);
@@ -1847,7 +2353,7 @@
 //     ExFreePool(buf2);
 // #endif
 
-    return STATUS_SUCCESS;
+    return Status;
 }
 
 static NTSTATUS STDCALL write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
@@ -1855,6 +2361,8 @@
     write_data_context* context = (write_data_context*)stripe->context;
     LIST_ENTRY* le;
     
+    UNUSED(DeviceObject);
+    
     // FIXME - we need a lock here
     
     if (stripe->status == WriteDataStatus_Cancelling) {
@@ -1891,47 +2399,61 @@
 }
 
 void free_write_data_stripes(write_data_context* wtc) {
-    LIST_ENTRY *le, *le2, *nextle;
+    LIST_ENTRY* le;
+    PMDL last_mdl = NULL;
     
-    le = wtc->stripes.Flink;
-    while (le != &wtc->stripes) {
-        write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
+    if (wtc->parity1_mdl) {
+        if (wtc->parity1_mdl->MdlFlags & MDL_PAGES_LOCKED)
+            MmUnlockPages(wtc->parity1_mdl);
         
-        if (stripe->Irp) {
-            if (stripe->device->devobj->Flags & DO_DIRECT_IO) {
-                MmUnlockPages(stripe->Irp->MdlAddress);
-                IoFreeMdl(stripe->Irp->MdlAddress);
-            }
-        }
+        IoFreeMdl(wtc->parity1_mdl);
+    }
+    
+    if (wtc->parity2_mdl) {
+        if (wtc->parity2_mdl->MdlFlags & MDL_PAGES_LOCKED)
+            MmUnlockPages(wtc->parity2_mdl);
         
-        le = le->Flink;
+        IoFreeMdl(wtc->parity2_mdl);
     }
     
+    if (wtc->mdl) {
+        if (wtc->mdl->MdlFlags & MDL_PAGES_LOCKED)
+            MmUnlockPages(wtc->mdl);
+        
+        IoFreeMdl(wtc->mdl);
+    }
+    
+    if (wtc->parity1)
+        ExFreePool(wtc->parity1);
+    
+    if (wtc->parity2)
+        ExFreePool(wtc->parity2);
+    
+    if (wtc->scratch)
+        ExFreePool(wtc->scratch);
+    
     le = wtc->stripes.Flink;
     while (le != &wtc->stripes) {
         write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
         
-        nextle = le->Flink;
+        if (stripe->mdl && stripe->mdl != last_mdl) {
+            if (stripe->mdl->MdlFlags & MDL_PAGES_LOCKED)
+                MmUnlockPages(stripe->mdl);
 
-        if (stripe->buf && stripe->need_free) {
-            ExFreePool(stripe->buf);
-            
-            le2 = le->Flink;
-            while (le2 != &wtc->stripes) {
-                write_data_stripe* s2 = CONTAINING_RECORD(le2, write_data_stripe, list_entry);
-                
-                if (s2->buf == stripe->buf)
-                    s2->buf = NULL;
-                
-                le2 = le2->Flink;
-            }
-            
+            IoFreeMdl(stripe->mdl);
         }
         
-        ExFreePool(stripe);
+        last_mdl = stripe->mdl;
         
-        le = nextle;
+        le = le->Flink;
     }
+    
+    le = wtc->stripes.Flink;
+    while (!IsListEmpty(&wtc->stripes)) {
+        write_data_stripe* stripe = CONTAINING_RECORD(RemoveHeadList(&wtc->stripes), write_data_stripe, list_entry);
+       
+        ExFreePool(stripe);
+    }
 }
 
 NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, PIRP Irp, LIST_ENTRY* rollback) {
@@ -1943,26 +2465,13 @@
     while (le != &fcb->extents) {
         LIST_ENTRY* le2 = le->Flink;
         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
-        EXTENT_DATA* ed = ext->data;
+        EXTENT_DATA* ed = &ext->extent_data;
         EXTENT_DATA2* ed2;
         UINT64 len;
         
         if (!ext->ignore) {
-            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));
-                Status = STATUS_INTERNAL_ERROR;
-                goto end;
-            }
-            
-            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));
-                    Status = STATUS_INTERNAL_ERROR;
-                    goto end;
-                }
-                
+            if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC)
                 ed2 = (EXTENT_DATA2*)ed->data;
-            }
             
             len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
             
@@ -1974,38 +2483,28 @@
                         fcb->inode_item.st_blocks -= len;
                         fcb->inode_item_changed = TRUE;
                     } else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning
-                        EXTENT_DATA* ned;
                         UINT64 size;
                         extent* newext;
                         
                         size = len - (end_data - ext->offset);
                         
-                        ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
-                        if (!ned) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            goto end;
-                        }
-                        
-                        newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+                        newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
                         if (!newext) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
-                            ExFreePool(ned);
                             goto end;
                         }
                         
-                        ned->generation = Vcb->superblock.generation;
-                        ned->decoded_size = size;
-                        ned->compression = ed->compression;
-                        ned->encryption = ed->encryption;
-                        ned->encoding = ed->encoding;
-                        ned->type = ed->type;
+                        newext->extent_data.generation = Vcb->superblock.generation;
+                        newext->extent_data.decoded_size = size;
+                        newext->extent_data.compression = ed->compression;
+                        newext->extent_data.encryption = ed->encryption;
+                        newext->extent_data.encoding = ed->encoding;
+                        newext->extent_data.type = ed->type;
                         
-                        RtlCopyMemory(&ned->data[0], &ed->data[end_data - ext->offset], size);
+                        RtlCopyMemory(newext->extent_data.data, &ed->data[end_data - ext->offset], size);
                         
                         newext->offset = end_data;
-                        newext->data = ned;
                         newext->datalen = sizeof(EXTENT_DATA) - 1 + size;
                         newext->unique = ext->unique;
                         newext->ignore = FALSE;
@@ -2018,38 +2517,28 @@
                         fcb->inode_item.st_blocks -= end_data - ext->offset;
                         fcb->inode_item_changed = TRUE;
                     } else if (start_data > ext->offset && end_data >= ext->offset + len) { // remove end
-                        EXTENT_DATA* ned;
                         UINT64 size;
                         extent* newext;
                         
                         size = start_data - ext->offset;
                         
-                        ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
-                        if (!ned) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            goto end;
-                        }
-                        
-                        newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+                        newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
                         if (!newext) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
-                            ExFreePool(ned);
                             goto end;
                         }
                         
-                        ned->generation = Vcb->superblock.generation;
-                        ned->decoded_size = size;
-                        ned->compression = ed->compression;
-                        ned->encryption = ed->encryption;
-                        ned->encoding = ed->encoding;
-                        ned->type = ed->type;
+                        newext->extent_data.generation = Vcb->superblock.generation;
+                        newext->extent_data.decoded_size = size;
+                        newext->extent_data.compression = ed->compression;
+                        newext->extent_data.encryption = ed->encryption;
+                        newext->extent_data.encoding = ed->encoding;
+                        newext->extent_data.type = ed->type;
                         
-                        RtlCopyMemory(&ned->data[0], &ed->data[0], size);
+                        RtlCopyMemory(newext->extent_data.data, ed->data, size);
                         
                         newext->offset = ext->offset;
-                        newext->data = ned;
                         newext->datalen = sizeof(EXTENT_DATA) - 1 + size;
                         newext->unique = ext->unique;
                         newext->ignore = FALSE;
@@ -2062,38 +2551,28 @@
                         fcb->inode_item.st_blocks -= ext->offset + len - start_data;
                         fcb->inode_item_changed = TRUE;
                     } else if (start_data > ext->offset && end_data < ext->offset + len) { // remove middle
-                        EXTENT_DATA *ned1, *ned2;
                         UINT64 size;
                         extent *newext1, *newext2;
                         
                         size = start_data - ext->offset;
                         
-                        ned1 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
-                        if (!ned1) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            goto end;
-                        }
-                        
-                        newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+                        newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
                         if (!newext1) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
-                            ExFreePool(ned1);
                             goto end;
                         }
                         
-                        ned1->generation = Vcb->superblock.generation;
-                        ned1->decoded_size = size;
-                        ned1->compression = ed->compression;
-                        ned1->encryption = ed->encryption;
-                        ned1->encoding = ed->encoding;
-                        ned1->type = ed->type;
+                        newext1->extent_data.generation = Vcb->superblock.generation;
+                        newext1->extent_data.decoded_size = size;
+                        newext1->extent_data.compression = ed->compression;
+                        newext1->extent_data.encryption = ed->encryption;
+                        newext1->extent_data.encoding = ed->encoding;
+                        newext1->extent_data.type = ed->type;
                         
-                        RtlCopyMemory(&ned1->data[0], &ed->data[0], size);
+                        RtlCopyMemory(newext1->extent_data.data, ed->data, size);
 
                         newext1->offset = ext->offset;
-                        newext1->data = ned1;
                         newext1->datalen = sizeof(EXTENT_DATA) - 1 + size;
                         newext1->unique = ext->unique;
                         newext1->ignore = FALSE;
@@ -2102,36 +2581,24 @@
                         
                         size = ext->offset + len - end_data;
                         
-                        ned2 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
-                        if (!ned2) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            ExFreePool(ned1);
-                            ExFreePool(newext1);
-                            goto end;
-                        }
-                        
-                        newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+                        newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
                         if (!newext2) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
-                            ExFreePool(ned1);
                             ExFreePool(newext1);
-                            ExFreePool(ned2);
                             goto end;
                         }
                         
-                        ned2->generation = Vcb->superblock.generation;
-                        ned2->decoded_size = size;
-                        ned2->compression = ed->compression;
-                        ned2->encryption = ed->encryption;
-                        ned2->encoding = ed->encoding;
-                        ned2->type = ed->type;
+                        newext2->extent_data.generation = Vcb->superblock.generation;
+                        newext2->extent_data.decoded_size = size;
+                        newext2->extent_data.compression = ed->compression;
+                        newext2->extent_data.encryption = ed->encryption;
+                        newext2->extent_data.encoding = ed->encoding;
+                        newext2->extent_data.type = ed->type;
                         
-                        RtlCopyMemory(&ned2->data[0], &ed->data[end_data - ext->offset], size);
+                        RtlCopyMemory(newext2->extent_data.data, &ed->data[end_data - ext->offset], size);
                         
                         newext2->offset = end_data;
-                        newext2->data = ned2;
                         newext2->datalen = sizeof(EXTENT_DATA) - 1 + size;
                         newext2->unique = ext->unique;
                         newext2->ignore = FALSE;
@@ -2170,7 +2637,6 @@
                         
                         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;
                         extent* newext;
                         
@@ -2179,29 +2645,21 @@
                             fcb->inode_item_changed = TRUE;
                         }
                         
-                        ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
-                        if (!ned) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            goto end;
-                        }
-                        
-                        newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+                        newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
                         if (!newext) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
-                            ExFreePool(ned);
                             goto end;
                         }
                         
-                        ned2 = (EXTENT_DATA2*)&ned->data[0];
+                        ned2 = (EXTENT_DATA2*)newext->extent_data.data;
                         
-                        ned->generation = Vcb->superblock.generation;
-                        ned->decoded_size = ed->decoded_size;
-                        ned->compression = ed->compression;
-                        ned->encryption = ed->encryption;
-                        ned->encoding = ed->encoding;
-                        ned->type = ed->type;
+                        newext->extent_data.generation = Vcb->superblock.generation;
+                        newext->extent_data.decoded_size = ed->decoded_size;
+                        newext->extent_data.compression = ed->compression;
+                        newext->extent_data.encryption = ed->encryption;
+                        newext->extent_data.encoding = ed->encoding;
+                        newext->extent_data.type = ed->type;
                         ned2->address = ed2->address;
                         ned2->size = ed2->size;
                         ned2->offset = ed2->offset + (end_data - ext->offset);
@@ -2208,7 +2666,6 @@
                         ned2->num_bytes = ed2->num_bytes - (end_data - ext->offset);
 
                         newext->offset = end_data;
-                        newext->data = ned;
                         newext->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
                         newext->unique = ext->unique;
                         newext->ignore = FALSE;
@@ -2220,7 +2677,6 @@
                                 if (!newext->csum) {
                                     ERR("out of memory\n");
                                     Status = STATUS_INSUFFICIENT_RESOURCES;
-                                    ExFreePool(ned);
                                     ExFreePool(newext);
                                     goto end;
                                 }
@@ -2232,7 +2688,6 @@
                                 if (!newext->csum) {
                                     ERR("out of memory\n");
                                     Status = STATUS_INSUFFICIENT_RESOURCES;
-                                    ExFreePool(ned);
                                     ExFreePool(newext);
                                     goto end;
                                 }
@@ -2246,7 +2701,6 @@
                         
                         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;
                         extent* newext;
                         
@@ -2255,29 +2709,21 @@
                             fcb->inode_item_changed = TRUE;
                         }
                         
-                        ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
-                        if (!ned) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            goto end;
-                        }
-                        
-                        newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+                        newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
                         if (!newext) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
-                            ExFreePool(ned);
                             goto end;
                         }
                         
-                        ned2 = (EXTENT_DATA2*)&ned->data[0];
+                        ned2 = (EXTENT_DATA2*)newext->extent_data.data;
                         
-                        ned->generation = Vcb->superblock.generation;
-                        ned->decoded_size = ed->decoded_size;
-                        ned->compression = ed->compression;
-                        ned->encryption = ed->encryption;
-                        ned->encoding = ed->encoding;
-                        ned->type = ed->type;
+                        newext->extent_data.generation = Vcb->superblock.generation;
+                        newext->extent_data.decoded_size = ed->decoded_size;
+                        newext->extent_data.compression = ed->compression;
+                        newext->extent_data.encryption = ed->encryption;
+                        newext->extent_data.encoding = ed->encoding;
+                        newext->extent_data.type = ed->type;
                         ned2->address = ed2->address;
                         ned2->size = ed2->size;
                         ned2->offset = ed2->offset;
@@ -2284,7 +2730,6 @@
                         ned2->num_bytes = start_data - ext->offset;
 
                         newext->offset = ext->offset;
-                        newext->data = ned;
                         newext->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
                         newext->unique = ext->unique;
                         newext->ignore = FALSE;
@@ -2296,7 +2741,6 @@
                                 if (!newext->csum) {
                                     ERR("out of memory\n");
                                     Status = STATUS_INSUFFICIENT_RESOURCES;
-                                    ExFreePool(ned);
                                     ExFreePool(newext);
                                     goto end;
                                 }
@@ -2307,7 +2751,6 @@
                                 if (!newext->csum) {
                                     ERR("out of memory\n");
                                     Status = STATUS_INSUFFICIENT_RESOURCES;
-                                    ExFreePool(ned);
                                     ExFreePool(newext);
                                     goto end;
                                 }
@@ -2321,7 +2764,6 @@
                         
                         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;
                         extent *newext1, *newext2;
                         
@@ -2345,61 +2787,42 @@
                             }
                         }
                         
-                        neda = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
-                        if (!neda) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            goto end;
-                        }
-                        
-                        newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+                        newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
                         if (!newext1) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
-                            ExFreePool(neda);
                             goto end;
                         }
                         
-                        nedb = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
-                        if (!nedb) {
-                            ERR("out of memory\n");
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            ExFreePool(neda);
-                            ExFreePool(newext1);
-                            goto end;
-                        }
-                        
-                        newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+                        newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
                         if (!newext2) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
-                            ExFreePool(neda);
                             ExFreePool(newext1);
-                            ExFreePool(nedb);
                             goto end;
                         }
                         
-                        neda2 = (EXTENT_DATA2*)&neda->data[0];
+                        neda2 = (EXTENT_DATA2*)newext1->extent_data.data;
                         
-                        neda->generation = Vcb->superblock.generation;
-                        neda->decoded_size = ed->decoded_size;
-                        neda->compression = ed->compression;
-                        neda->encryption = ed->encryption;
-                        neda->encoding = ed->encoding;
-                        neda->type = ed->type;
+                        newext1->extent_data.generation = Vcb->superblock.generation;
+                        newext1->extent_data.decoded_size = ed->decoded_size;
+                        newext1->extent_data.compression = ed->compression;
+                        newext1->extent_data.encryption = ed->encryption;
+                        newext1->extent_data.encoding = ed->encoding;
+                        newext1->extent_data.type = ed->type;
                         neda2->address = ed2->address;
                         neda2->size = ed2->size;
                         neda2->offset = ed2->offset;
                         neda2->num_bytes = start_data - ext->offset;
 
-                        nedb2 = (EXTENT_DATA2*)&nedb->data[0];
+                        nedb2 = (EXTENT_DATA2*)newext2->extent_data.data;
                         
-                        nedb->generation = Vcb->superblock.generation;
-                        nedb->decoded_size = ed->decoded_size;
-                        nedb->compression = ed->compression;
-                        nedb->encryption = ed->encryption;
-                        nedb->encoding = ed->encoding;
-                        nedb->type = ed->type;
+                        newext2->extent_data.generation = Vcb->superblock.generation;
+                        newext2->extent_data.decoded_size = ed->decoded_size;
+                        newext2->extent_data.compression = ed->compression;
+                        newext2->extent_data.encryption = ed->encryption;
+                        newext2->extent_data.encoding = ed->encoding;
+                        newext2->extent_data.type = ed->type;
                         nedb2->address = ed2->address;
                         nedb2->size = ed2->size;
                         nedb2->offset = ed2->offset + (end_data - ext->offset);
@@ -2406,7 +2829,6 @@
                         nedb2->num_bytes = ext->offset + len - end_data;
                         
                         newext1->offset = ext->offset;
-                        newext1->data = neda;
                         newext1->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
                         newext1->unique = ext->unique;
                         newext1->ignore = FALSE;
@@ -2413,7 +2835,6 @@
                         newext1->inserted = TRUE;
                         
                         newext2->offset = end_data;
-                        newext2->data = nedb;
                         newext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
                         newext2->unique = ext->unique;
                         newext2->ignore = FALSE;
@@ -2425,9 +2846,7 @@
                                 if (!newext1->csum) {
                                     ERR("out of memory\n");
                                     Status = STATUS_INSUFFICIENT_RESOURCES;
-                                    ExFreePool(neda);
                                     ExFreePool(newext1);
-                                    ExFreePool(nedb);
                                     ExFreePool(newext2);
                                     goto end;
                                 }
@@ -2437,9 +2856,7 @@
                                     ERR("out of memory\n");
                                     Status = STATUS_INSUFFICIENT_RESOURCES;
                                     ExFreePool(newext1->csum);
-                                    ExFreePool(neda);
                                     ExFreePool(newext1);
-                                    ExFreePool(nedb);
                                     ExFreePool(newext2);
                                     goto end;
                                 }
@@ -2452,9 +2869,7 @@
                                 if (!newext1->csum) {
                                     ERR("out of memory\n");
                                     Status = STATUS_INSUFFICIENT_RESOURCES;
-                                    ExFreePool(neda);
                                     ExFreePool(newext1);
-                                    ExFreePool(nedb);
                                     ExFreePool(newext2);
                                     goto end;
                                 }
@@ -2464,9 +2879,7 @@
                                     ERR("out of memory\n");
                                     Status = STATUS_INSUFFICIENT_RESOURCES;
                                     ExFreePool(newext1->csum);
-                                    ExFreePool(neda);
                                     ExFreePool(newext1);
-                                    ExFreePool(nedb);
                                     ExFreePool(newext2);
                                     goto end;
                                 }
@@ -2512,7 +2925,7 @@
     re->fcb = fcb;
     re->ext = ext;
     
-    add_rollback(fcb->Vcb, rollback, ROLLBACK_INSERT_EXTENT, re);
+    add_rollback(rollback, ROLLBACK_INSERT_EXTENT, re);
 }
 
 static BOOL add_extent_to_fcb(fcb* fcb, UINT64 offset, EXTENT_DATA* ed, ULONG edsize, BOOL unique, UINT32* csum, LIST_ENTRY* rollback) {
@@ -2519,7 +2932,7 @@
     extent* ext;
     LIST_ENTRY* le;
     
-    ext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+    ext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + edsize, ALLOC_TAG);
     if (!ext) {
         ERR("out of memory\n");
         return FALSE;
@@ -2526,7 +2939,6 @@
     }
     
     ext->offset = offset;
-    ext->data = ed;
     ext->datalen = edsize;
     ext->unique = unique;
     ext->ignore = FALSE;
@@ -2533,6 +2945,8 @@
     ext->inserted = TRUE;
     ext->csum = csum;
     
+    RtlCopyMemory(&ext->extent_data, ed, edsize);
+    
     le = fcb->extents.Flink;
     while (le != &fcb->extents) {
         extent* oldext = CONTAINING_RECORD(le, extent, list_entry);
@@ -2570,11 +2984,11 @@
         re->fcb = fcb;
         re->ext = ext;
         
-        add_rollback(fcb->Vcb, rollback, ROLLBACK_DELETE_EXTENT, re);
+        add_rollback(rollback, ROLLBACK_DELETE_EXTENT, re);
     }
 }
 
-static NTSTATUS calc_csum(device_extension* Vcb, UINT8* data, UINT32 sectors, UINT32* csum) {
+NTSTATUS calc_csum(device_extension* Vcb, UINT8* data, UINT32 sectors, UINT32* csum) {
     NTSTATUS Status;
     calc_job* cj;
     
@@ -2581,7 +2995,7 @@
     // From experimenting, it seems that 40 sectors is roughly the crossover
     // point where offloading the crc32 calculation becomes worth it.
     
-    if (sectors < 40) {
+    if (sectors < 40 || KeQueryActiveProcessorCount(NULL) < 2) {
         ULONG j;
         
         for (j = 0; j < sectors; j++) {
@@ -2604,7 +3018,7 @@
 }
 
 BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, BOOL prealloc, void* data,
-                         PIRP Irp, LIST_ENTRY* rollback, UINT8 compression, UINT64 decoded_size) {
+                         PIRP Irp, LIST_ENTRY* rollback, UINT8 compression, UINT64 decoded_size, BOOL file_write, UINT32 irp_offset) {
     UINT64 address;
     NTSTATUS Status;
     EXTENT_DATA* ed;
@@ -2677,7 +3091,7 @@
         return FALSE;
     }
     
-    increase_chunk_usage(c, length);
+    c->used += length;
     space_list_subtract(Vcb, c, FALSE, address, length, rollback);
     
     fcb->inode_item.st_blocks += decoded_size;
@@ -2695,7 +3109,7 @@
     ExReleaseResourceLite(&c->lock);
       
     if (data) {
-        Status = write_data_complete(Vcb, address, data, length, Irp, NULL);
+        Status = write_data_complete(Vcb, address, data, length, Irp, NULL, file_write, irp_offset);
         if (!NT_SUCCESS(Status))
             ERR("write_data_complete returned %08x\n", Status);
     }
@@ -2704,7 +3118,7 @@
 }
 
 static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
-                            PIRP Irp, UINT64* written, LIST_ENTRY* rollback) {
+                            PIRP Irp, UINT64* written, BOOL file_write, UINT32 irp_offset, LIST_ENTRY* rollback) {
     BOOL success = FALSE;
     EXTENT_DATA* ed;
     EXTENT_DATA2* ed2;
@@ -2734,13 +3148,8 @@
     if (!ext)
         return FALSE;
 
-    ed = ext->data;
+    ed = &ext->extent_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));
-        return FALSE;
-    }
-    
     if (ed->type != EXTENT_TYPE_REGULAR && ed->type != EXTENT_TYPE_PREALLOC) {
         TRACE("not extending extent which is not regular or prealloc\n");
         return FALSE;
@@ -2747,11 +3156,6 @@
     }
     
     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));
-        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);
@@ -2765,6 +3169,21 @@
     
     ExAcquireResourceExclusiveLite(&c->lock, TRUE);
     
+    if (length > c->chunk_item->size - c->used) {
+        ExReleaseResourceLite(&c->lock);
+        return FALSE;
+    }
+    
+    if (!c->cache_loaded) {
+        NTSTATUS Status = load_cache_chunk(Vcb, c, NULL);
+        
+        if (!NT_SUCCESS(Status)) {
+            ERR("load_cache_chunk returned %08x\n", Status);
+            ExReleaseResourceLite(&c->lock);
+            return FALSE;
+        }
+    }
+    
     le = c->space.Flink;
     while (le != &c->space) {
         s = CONTAINING_RECORD(le, space, list_entry);
@@ -2772,7 +3191,7 @@
         if (s->address == ed2->address + ed2->size) {
             UINT64 newlen = min(min(s->size, length), MAX_EXTENT_SIZE);
             
-            success = insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen);
+            success = insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset);
             
             if (success)
                 *written += newlen;
@@ -2818,7 +3237,7 @@
                 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, !page_file, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen)) {
+                    if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen, FALSE, 0)) {
                         ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
                         goto cont;
                     }
@@ -2840,7 +3259,7 @@
             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, !page_file, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen))
+                if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen, FALSE, 0))
                     goto cont;
             }
             
@@ -2872,7 +3291,8 @@
 //     }
 // }
 
-NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, PIRP Irp, LIST_ENTRY* rollback) {
+static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
+                              PIRP Irp, BOOL file_write, UINT32 irp_offset, LIST_ENTRY* rollback) {
     LIST_ENTRY* le;
     chunk* c;
     UINT64 flags, orig_length = length, written = 0;
@@ -2880,12 +3300,13 @@
     TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, length, data);
     
     if (start_data > 0) {
-        try_extend_data(Vcb, fcb, start_data, length, data, Irp, &written, rollback);
+        try_extend_data(Vcb, fcb, start_data, length, data, Irp, &written, file_write, irp_offset, rollback);
         
         if (written == length)
             return STATUS_SUCCESS;
         else if (written > 0) {
             start_data += written;
+            irp_offset += written;
             length -= written;
             data = &((UINT8*)data)[written];
         }
@@ -2910,7 +3331,7 @@
                 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
                 
                 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen &&
-                    insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen)) {
+                    insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset)) {
                     written += newlen;
                     
                     if (written == orig_length) {
@@ -2919,6 +3340,7 @@
                     } else {
                         done = TRUE;
                         start_data += newlen;
+                        irp_offset += newlen;
                         length -= newlen;
                         data = &((UINT8*)data)[newlen];
                         break;
@@ -2944,7 +3366,7 @@
             ExAcquireResourceExclusiveLite(&c->lock, TRUE);
             
             if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen &&
-                insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen)) {
+                insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset)) {
                 written += newlen;
                 
                 if (written == orig_length)
@@ -2952,6 +3374,7 @@
                 else {
                     done = TRUE;
                     start_data += newlen;
+                    irp_offset += newlen;
                     length -= newlen;
                     data = &((UINT8*)data)[newlen];
                 }
@@ -3005,7 +3428,7 @@
     TRACE("(%p, %p, %x, %u)\n", fcb, fileref, end, prealloc);
 
     if (fcb->ads)
-        return stream_set_end_of_file_information(fcb->Vcb, end, fcb, fileref, NULL, FALSE, rollback);
+        return stream_set_end_of_file_information(fcb->Vcb, end, fcb, fileref, NULL, FALSE);
     else {
         extent* ext = NULL;
         LIST_ENTRY* le;
@@ -3024,14 +3447,9 @@
         
         oldalloc = 0;
         if (ext) {
-            EXTENT_DATA* ed = ext->data;
+            EXTENT_DATA* ed = &ext->extent_data;
             EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->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));
-                return STATUS_INTERNAL_ERROR;
-            }
-            
             oldalloc = ext->offset + (ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes);
             cur_inline = ed->type == EXTENT_TYPE_INLINE;
         
@@ -3073,7 +3491,7 @@
                         return Status;
                     }
                 } else {
-                    Status = insert_extent(fcb->Vcb, fcb, offset, length, data, Irp, rollback);
+                    Status = insert_extent(fcb->Vcb, fcb, offset, length, data, Irp, FALSE, 0, rollback);
                     if (!NT_SUCCESS(Status)) {
                         ERR("insert_extent returned %08x\n", Status);
                         ExFreePool(data);
@@ -3099,7 +3517,7 @@
                     }
                     
                     RtlZeroMemory(ed, edsize);
-                    RtlCopyMemory(ed, ext->data, ext->datalen);
+                    RtlCopyMemory(ed, &ext->extent_data, ext->datalen);
                     
                     ed->decoded_size = end - ext->offset;
                     
@@ -3221,34 +3639,27 @@
 }
 
 static NTSTATUS do_write_file_prealloc(fcb* fcb, extent* ext, UINT64 start_data, UINT64 end_data, void* data, UINT64* written,
-                                       PIRP Irp, LIST_ENTRY* rollback) {
-    EXTENT_DATA* ed = ext->data;
+                                       PIRP Irp, BOOL file_write, UINT32 irp_offset, LIST_ENTRY* rollback) {
+    EXTENT_DATA* ed = &ext->extent_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);
+        newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
         if (!newext) {
             ERR("out of memory\n");
-            ExFreePool(ned);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
-        RtlCopyMemory(ned, ext->data, ext->datalen);
+        RtlCopyMemory(&newext->extent_data, &ext->extent_data, ext->datalen);
         
-        ned->type = EXTENT_TYPE_REGULAR;
+        newext->extent_data.type = EXTENT_TYPE_REGULAR;
         
-        Status = write_data_complete(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, ed2->num_bytes, Irp, NULL);
+        Status = write_data_complete(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, ed2->num_bytes, Irp,
+                                     NULL, file_write, irp_offset + ext->offset - start_data);
         if (!NT_SUCCESS(Status)) {
             ERR("write_data_complete returned %08x\n", Status);
             return Status;
@@ -3260,7 +3671,6 @@
             
             if (!csum) {
                 ERR("out of memory\n");
-                ExFreePool(ned);
                 ExFreePool(newext);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
@@ -3269,7 +3679,6 @@
             if (!NT_SUCCESS(Status)) {
                 ERR("calc_csum returned %08x\n", Status);
                 ExFreePool(csum);
-                ExFreePool(ned);
                 ExFreePool(newext);
                 return Status;
             }
@@ -3281,7 +3690,6 @@
         *written = ed2->num_bytes;
         
         newext->offset = ext->offset;
-        newext->data = ned;
         newext->datalen = ext->datalen;
         newext->unique = ext->unique;
         newext->ignore = FALSE;
@@ -3292,51 +3700,34 @@
         
         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);
+        newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
         if (!newext1) {
             ERR("out of memory\n");
-            ExFreePool(ned);
-            ExFreePool(nedb);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
-        newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+        newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, 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;
+        RtlCopyMemory(&newext1->extent_data, &ext->extent_data, ext->datalen);
+        newext1->extent_data.type = EXTENT_TYPE_REGULAR;
+        ned2 = (EXTENT_DATA2*)newext1->extent_data.data;
         ned2->num_bytes = end_data - ext->offset;
         
-        RtlCopyMemory(nedb, ext->data, ext->datalen);
-        ned2 = (EXTENT_DATA2*)nedb->data;
+        RtlCopyMemory(&newext2->extent_data, &ext->extent_data, ext->datalen);
+        ned2 = (EXTENT_DATA2*)newext2->extent_data.data;
         ned2->offset += end_data - ext->offset;
         ned2->num_bytes -= end_data - ext->offset;
         
-        Status = write_data_complete(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, end_data - ext->offset, Irp, NULL);
+        Status = write_data_complete(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, end_data - ext->offset,
+                                     Irp, NULL, file_write, irp_offset + ext->offset - start_data);
         if (!NT_SUCCESS(Status)) {
             ERR("write_data_complete returned %08x\n", Status);
             return Status;
@@ -3348,8 +3739,6 @@
             
             if (!csum) {
                 ERR("out of memory\n");
-                ExFreePool(ned);
-                ExFreePool(nedb);
                 ExFreePool(newext1);
                 ExFreePool(newext2);
                 return STATUS_INSUFFICIENT_RESOURCES;
@@ -3358,8 +3747,6 @@
             Status = calc_csum(fcb->Vcb, (UINT8*)data + ext->offset - start_data, sl, csum);
             if (!NT_SUCCESS(Status)) {
                 ERR("calc_csum returned %08x\n", Status);
-                ExFreePool(ned);
-                ExFreePool(nedb);
                 ExFreePool(newext1);
                 ExFreePool(newext2);
                 ExFreePool(csum);
@@ -3373,7 +3760,6 @@
         *written = end_data - ext->offset;
         
         newext1->offset = ext->offset;
-        newext1->data = ned;
         newext1->datalen = ext->datalen;
         newext1->unique = ext->unique;
         newext1->ignore = FALSE;
@@ -3383,7 +3769,6 @@
         add_insert_extent_rollback(rollback, fcb, newext1);
         
         newext2->offset = end_data;
-        newext2->data = nedb;
         newext2->datalen = ext->datalen;
         newext2->unique = ext->unique;
         newext2->ignore = FALSE;
@@ -3409,53 +3794,35 @@
 
         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);
+        newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
         if (!newext1) {
             ERR("out of memory\n");
-            ExFreePool(ned);
-            ExFreePool(nedb);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
-        newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+        newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, 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);
+        RtlCopyMemory(&newext1->extent_data, &ext->extent_data, ext->datalen);
         
-        ned2 = (EXTENT_DATA2*)ned->data;
+        ned2 = (EXTENT_DATA2*)newext1->extent_data.data;
         ned2->num_bytes = start_data - ext->offset;
         
-        RtlCopyMemory(nedb, ext->data, ext->datalen);
+        RtlCopyMemory(&newext2->extent_data, &ext->extent_data, ext->datalen);
         
-        nedb->type = EXTENT_TYPE_REGULAR;
-        ned2 = (EXTENT_DATA2*)nedb->data;
+        newext2->extent_data.type = EXTENT_TYPE_REGULAR;
+        ned2 = (EXTENT_DATA2*)newext2->extent_data.data;
         ned2->offset += start_data - ext->offset;
         ned2->num_bytes = ext->offset + ed2->num_bytes - start_data;
         
-        Status = write_data_complete(fcb->Vcb, ed2->address + ned2->offset, data, ned2->num_bytes, Irp, NULL);
+        Status = write_data_complete(fcb->Vcb, ed2->address + ned2->offset, data, ned2->num_bytes, Irp, NULL, file_write, irp_offset);
         if (!NT_SUCCESS(Status)) {
             ERR("write_data_complete returned %08x\n", Status);
             return Status;
@@ -3467,8 +3834,6 @@
             
             if (!csum) {
                 ERR("out of memory\n");
-                ExFreePool(ned);
-                ExFreePool(nedb);
                 ExFreePool(newext1);
                 ExFreePool(newext2);
                 return STATUS_INSUFFICIENT_RESOURCES;
@@ -3477,8 +3842,6 @@
             Status = calc_csum(fcb->Vcb, data, sl, csum);
             if (!NT_SUCCESS(Status)) {
                 ERR("calc_csum returned %08x\n", Status);
-                ExFreePool(ned);
-                ExFreePool(nedb);
                 ExFreePool(newext1);
                 ExFreePool(newext2);
                 ExFreePool(csum);
@@ -3492,7 +3855,6 @@
         *written = ned2->num_bytes;
         
         newext1->offset = ext->offset;
-        newext1->data = ned;
         newext1->datalen = ext->datalen;
         newext1->unique = ext->unique;
         newext1->ignore = FALSE;
@@ -3503,7 +3865,6 @@
         add_insert_extent_rollback(rollback, fcb, newext1);
         
         newext2->offset = start_data;
-        newext2->data = nedb;
         newext2->datalen = ext->datalen;
         newext2->unique = ext->unique;
         newext2->ignore = FALSE;
@@ -3528,79 +3889,48 @@
 
         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);
+        newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, 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);
+        newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, 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);
+        newext3 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, 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);
+        RtlCopyMemory(&newext1->extent_data, &ext->extent_data, ext->datalen);
+        RtlCopyMemory(&newext2->extent_data, &ext->extent_data, ext->datalen);
+        RtlCopyMemory(&newext3->extent_data, &ext->extent_data, ext->datalen);
         
-        ned2 = (EXTENT_DATA2*)ned->data;
+        ned2 = (EXTENT_DATA2*)newext1->extent_data.data;
         ned2->num_bytes = start_data - ext->offset;
         
-        nedb->type = EXTENT_TYPE_REGULAR;
-        ned2 = (EXTENT_DATA2*)nedb->data;
+        newext2->extent_data.type = EXTENT_TYPE_REGULAR;
+        ned2 = (EXTENT_DATA2*)newext2->extent_data.data;
         ned2->offset += start_data - ext->offset;
         ned2->num_bytes = end_data - start_data;
         
-        ned2 = (EXTENT_DATA2*)nedc->data;
+        ned2 = (EXTENT_DATA2*)newext3->extent_data.data;
         ned2->offset += end_data - ext->offset;
         ned2->num_bytes -= end_data - ext->offset;
         
-        ned2 = (EXTENT_DATA2*)nedb->data;
-        Status = write_data_complete(fcb->Vcb, ed2->address + ned2->offset, data, end_data - start_data, Irp, NULL);
+        ned2 = (EXTENT_DATA2*)newext2->extent_data.data;
+        Status = write_data_complete(fcb->Vcb, ed2->address + ned2->offset, data, end_data - start_data, Irp, NULL, file_write, irp_offset);
         if (!NT_SUCCESS(Status)) {
             ERR("write_data_complete returned %08x\n", Status);
             return Status;
@@ -3612,9 +3942,6 @@
             
             if (!csum) {
                 ERR("out of memory\n");
-                ExFreePool(ned);
-                ExFreePool(nedb);
-                ExFreePool(nedc);
                 ExFreePool(newext1);
                 ExFreePool(newext2);
                 ExFreePool(newext3);
@@ -3624,9 +3951,6 @@
             Status = calc_csum(fcb->Vcb, data, sl, csum);
             if (!NT_SUCCESS(Status)) {
                 ERR("calc_csum returned %08x\n", Status);
-                ExFreePool(ned);
-                ExFreePool(nedb);
-                ExFreePool(nedc);
                 ExFreePool(newext1);
                 ExFreePool(newext2);
                 ExFreePool(newext3);
@@ -3641,7 +3965,6 @@
         *written = end_data - start_data;
         
         newext1->offset = ext->offset;
-        newext1->data = ned;
         newext1->datalen = ext->datalen;
         newext1->unique = ext->unique;
         newext1->ignore = FALSE;
@@ -3652,7 +3975,6 @@
         add_insert_extent_rollback(rollback, fcb, newext1);
         
         newext2->offset = start_data;
-        newext2->data = nedb;
         newext2->datalen = ext->datalen;
         newext2->unique = ext->unique;
         newext2->ignore = FALSE;
@@ -3662,7 +3984,6 @@
         add_insert_extent_rollback(rollback, fcb, newext2);
         
         newext3->offset = end_data;
-        newext3->data = nedc;
         newext3->datalen = ext->datalen;
         newext3->unique = ext->unique;
         newext3->ignore = FALSE;
@@ -3692,7 +4013,7 @@
     return STATUS_SUCCESS;
 }
 
-NTSTATUS do_write_file(fcb* fcb, UINT64 start, UINT64 end_data, void* data, PIRP Irp, LIST_ENTRY* rollback) {
+NTSTATUS do_write_file(fcb* fcb, UINT64 start, UINT64 end_data, void* data, PIRP Irp, BOOL file_write, UINT32 irp_offset, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     LIST_ENTRY *le, *le2;
     UINT64 written = 0, length = end_data - start;
@@ -3710,7 +4031,7 @@
         le2 = le->Flink;
         
         if (!ext->ignore) {
-            EXTENT_DATA* ed = ext->data;
+            EXTENT_DATA* ed = &ext->extent_data;
             EXTENT_DATA2* ed2 = ed->type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ed->data;
             UINT64 len;
             
@@ -3732,7 +4053,7 @@
                         return Status;
                     }
                     
-                    Status = insert_extent(fcb->Vcb, fcb, start_write, ext->offset - start_write, data, Irp, rollback);
+                    Status = insert_extent(fcb->Vcb, fcb, start_write, ext->offset - start_write, (UINT8*)data + written, Irp, file_write, irp_offset + written, rollback);
                     if (!NT_SUCCESS(Status)) {
                         ERR("insert_extent returned %08x\n", Status);
                         return Status;
@@ -3751,7 +4072,7 @@
                                     
                     TRACE("doing non-COW write to %llx\n", writeaddr);
                     
-                    Status = write_data_complete(fcb->Vcb, writeaddr, (UINT8*)data + written, write_len, Irp, NULL);
+                    Status = write_data_complete(fcb->Vcb, writeaddr, (UINT8*)data + written, write_len, Irp, NULL, file_write, irp_offset + written);
                     if (!NT_SUCCESS(Status)) {
                         ERR("write_data_complete returned %08x\n", Status);
                         return Status;
@@ -3774,7 +4095,7 @@
                     UINT64 write_len;
                     
                     Status = do_write_file_prealloc(fcb, ext, start + written, end_data, (UINT8*)data + written, &write_len,
-                                                    Irp, rollback);
+                                                    Irp, file_write, irp_offset + written, rollback);
                     if (!NT_SUCCESS(Status)) {
                         ERR("do_write_file_prealloc returned %08x\n", Status);
                         return Status;
@@ -3804,7 +4125,7 @@
             return Status;
         }
         
-        Status = insert_extent(fcb->Vcb, fcb, start_write, end_data - start_write, data, Irp, rollback);
+        Status = insert_extent(fcb->Vcb, fcb, start_write, end_data - start_write, (UINT8*)data + written, Irp, file_write, irp_offset + written, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("insert_extent returned %08x\n", Status);
             return Status;
@@ -3867,7 +4188,7 @@
             
             // write subsequent data non-compressed
             if (e2 < end_data) {
-                Status = do_write_file(fcb, e2, end_data, (UINT8*)data + e2, Irp, rollback);
+                Status = do_write_file(fcb, e2, end_data, (UINT8*)data + e2, Irp, FALSE, 0, rollback);
                 
                 if (!NT_SUCCESS(Status)) {
                     ERR("do_write_file returned %08x\n", Status);
@@ -3883,7 +4204,7 @@
 }
 
 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) {
+                     BOOL wait, BOOL deferred_write, BOOL write_irp, LIST_ENTRY* rollback) {
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     PFILE_OBJECT FileObject = IrpSp->FileObject;
     EXTENT_DATA* ed2;
@@ -4136,7 +4457,7 @@
         if (fileref)
             mark_fileref_dirty(fileref);
     } else {
-        BOOL compress = write_fcb_compressed(fcb);
+        BOOL compress = write_fcb_compressed(fcb), no_buf = FALSE;
         
         if (make_inline) {
             start_data = 0;
@@ -4156,36 +4477,41 @@
         fcb->Header.ValidDataLength.QuadPart = newlength;
         TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart);
     
-        data = ExAllocatePoolWithTag(PagedPool, end_data - start_data + bufhead, ALLOC_TAG);
-        if (!data) {
-            ERR("out of memory\n");
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto end;
-        }
-        
-        RtlZeroMemory(data + bufhead, end_data - start_data);
-        
-        TRACE("start_data = %llx\n", start_data);
-        TRACE("end_data = %llx\n", end_data);
-        
-        if (offset.QuadPart > start_data || offset.QuadPart + *length < end_data) {
-            if (changed_length) {
-                if (fcb->inode_item.st_size > start_data) 
-                    Status = read_file(fcb, data + bufhead, start_data, fcb->inode_item.st_size - start_data, NULL, Irp, TRUE);
-                else
-                    Status = STATUS_SUCCESS;
-            } else
-                Status = read_file(fcb, data + bufhead, start_data, end_data - start_data, NULL, Irp, TRUE);
-            
-            if (!NT_SUCCESS(Status)) {
-                ERR("read_file returned %08x\n", Status);
-                ExFreePool(data);
+        if (!make_inline && !compress && offset.QuadPart == start_data && offset.QuadPart + *length == end_data) {
+            data = buf;
+            no_buf = TRUE;
+        } else {
+            data = ExAllocatePoolWithTag(PagedPool, end_data - start_data + bufhead, ALLOC_TAG);
+            if (!data) {
+                ERR("out of memory\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
                 goto end;
             }
+            
+            RtlZeroMemory(data + bufhead, end_data - start_data);
+            
+            TRACE("start_data = %llx\n", start_data);
+            TRACE("end_data = %llx\n", end_data);
+            
+            if (offset.QuadPart > start_data || offset.QuadPart + *length < end_data) {
+                if (changed_length) {
+                    if (fcb->inode_item.st_size > start_data) 
+                        Status = read_file(fcb, data + bufhead, start_data, fcb->inode_item.st_size - start_data, NULL, Irp);
+                    else
+                        Status = STATUS_SUCCESS;
+                } else
+                    Status = read_file(fcb, data + bufhead, start_data, end_data - start_data, NULL, Irp);
+                
+                if (!NT_SUCCESS(Status)) {
+                    ERR("read_file returned %08x\n", Status);
+                    ExFreePool(data);
+                    goto end;
+                }
+            }
+            
+            RtlCopyMemory(data + bufhead + offset.QuadPart - start_data, buf, *length);
         }
         
-        RtlCopyMemory(data + bufhead + offset.QuadPart - start_data, buf, *length);
-        
         if (make_inline) {
             Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
@@ -4221,7 +4547,18 @@
             
             ExFreePool(data);
         } else {
-            Status = do_write_file(fcb, start_data, end_data, data, Irp, rollback);
+            if (write_irp && Irp->MdlAddress) {
+                BOOL locked = Irp->MdlAddress->MdlFlags & MDL_PAGES_LOCKED;
+                
+                if (!locked)
+                    MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoReadAccess);
+                
+                Status = do_write_file(fcb, start_data, end_data, data, Irp, TRUE, 0, rollback);
+                
+                if (!locked)
+                    MmUnlockPages(Irp->MdlAddress);
+            } else
+                Status = do_write_file(fcb, start_data, end_data, data, Irp, FALSE, 0, rollback);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("do_write_file returned %08x\n", Status);
@@ -4229,7 +4566,8 @@
                 goto end;
             }
             
-            ExFreePool(data);
+            if (!no_buf)
+                ExFreePool(data);
         }
     }
     
@@ -4385,7 +4723,7 @@
     
 //     ERR("Irp->Flags = %x\n", Irp->Flags);
     Status = write_file2(Vcb, Irp, offset, buf, &IrpSp->Parameters.Write.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE,
-                         wait, deferred_write, &rollback);
+                         wait, deferred_write, TRUE, &rollback);
     
     if (Status == STATUS_PENDING)
         goto exit;
@@ -4425,7 +4763,7 @@
 exit:
 //     if (locked) {
         if (NT_SUCCESS(Status))
-            clear_rollback(Vcb, &rollback);
+            clear_rollback(&rollback);
         else
             do_rollback(Vcb, &rollback);
 //         
@@ -4453,9 +4791,12 @@
 
     top_level = is_top_level(Irp);
     
-    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
-        Status = part0_passthrough(DeviceObject, Irp);
+    if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+        Status = vol_write(DeviceObject, Irp);
         goto exit;
+    } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+        Status = STATUS_INVALID_PARAMETER;
+        goto end;
     }
     
     if (!fcb) {
Index: sdk/include/ddk/ata.h
===================================================================
--- sdk/include/ddk/ata.h	(révision 74430)
+++ sdk/include/ddk/ata.h	(copie de travail)
@@ -252,6 +252,7 @@
 } IDENTIFY_DEVICE_DATA, *PIDENTIFY_DEVICE_DATA;
 #include <poppack.h>
 
-#define IDE_COMMAND_IDENTIFY 0xEC
+#define IDE_COMMAND_FLUSH_CACHE 0xE7
+#define IDE_COMMAND_IDENTIFY    0xEC
 
 #endif
Index: sdk/lib/wdmguid/wdmguid.c
===================================================================
--- sdk/lib/wdmguid/wdmguid.c	(révision 74430)
+++ sdk/lib/wdmguid/wdmguid.c	(copie de travail)
@@ -16,5 +16,9 @@
   0x53f56308L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
 DEFINE_GUID(GUID_DEVINTERFACE_PARTITION,
   0x53f5630aL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+DEFINE_GUID(GUID_DEVINTERFACE_VOLUME,
+  0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+DEFINE_GUID(GUID_DEVINTERFACE_HIDDEN_VOLUME,
+  0x7f108a28L, 0x9833, 0x4b3b, 0xb7, 0x80, 0x2c, 0x6b, 0x5f, 0xa5, 0xc0, 0x62);
 
 /* EOF */
