[BTRFS]
authorPierre Schweitzer <pierre@reactos.org>
Wed, 27 Jul 2016 19:24:26 +0000 (19:24 +0000)
committerPierre Schweitzer <pierre@reactos.org>
Wed, 27 Jul 2016 19:24:26 +0000 (19:24 +0000)
Sync btrfs to 0.5.
This breaks BTRFS in ReactOS.

CORE-11674

svn path=/trunk/; revision=72023

21 files changed:
reactos/drivers/filesystems/btrfs/CMakeLists.txt
reactos/drivers/filesystems/btrfs/btrfs.c
reactos/drivers/filesystems/btrfs/btrfs.rc
reactos/drivers/filesystems/btrfs/btrfs_drv.h
reactos/drivers/filesystems/btrfs/create.c
reactos/drivers/filesystems/btrfs/dirctrl.c
reactos/drivers/filesystems/btrfs/extent-tree.c
reactos/drivers/filesystems/btrfs/fastio.c
reactos/drivers/filesystems/btrfs/fileinfo.c
reactos/drivers/filesystems/btrfs/flushthread.c
reactos/drivers/filesystems/btrfs/free-space.c
reactos/drivers/filesystems/btrfs/fsctl.c
reactos/drivers/filesystems/btrfs/pnp.c
reactos/drivers/filesystems/btrfs/read.c
reactos/drivers/filesystems/btrfs/registry.c [new file with mode: 0644]
reactos/drivers/filesystems/btrfs/reparse.c
reactos/drivers/filesystems/btrfs/search.c
reactos/drivers/filesystems/btrfs/security.c
reactos/drivers/filesystems/btrfs/treefuncs.c
reactos/drivers/filesystems/btrfs/worker-thread.c [new file with mode: 0644]
reactos/drivers/filesystems/btrfs/write.c

index b39737e..1156757 100644 (file)
@@ -16,10 +16,12 @@ list(APPEND SOURCE
     fsctl.c
     pnp.c
     read.c
+    registry.c
     reparse.c
     search.c
     security.c
     treefuncs.c
+    worker-thread.c
     write.c
     btrfs_drv.h)
 
index b31059a..54723e2 100644 (file)
@@ -33,6 +33,7 @@
 #else
 #include <rtlfuncs.h>
 #endif
+#include <mountdev.h>
 
 #define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \
                             BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
@@ -53,7 +54,7 @@ LIST_ENTRY VcbList;
 ERESOURCE global_loading_lock;
 UINT32 debug_log_level = 0;
 BOOL log_started = FALSE;
-UNICODE_STRING log_device, log_file;
+UNICODE_STRING log_device, log_file, registry_path;
 
 #ifdef _DEBUG
 PFILE_OBJECT comfo = NULL;
@@ -217,7 +218,7 @@ exit2:
 }
 #endif
 
-ULONG sector_align( ULONG NumberToBeAligned, ULONG Alignment )
+UINT64 sector_align( UINT64 NumberToBeAligned, UINT64 Alignment )
 {
     if( Alignment & ( Alignment - 1 ) )
     {
@@ -307,6 +308,9 @@ static void STDCALL DriverUnload(PDRIVER_OBJECT DriverObject) {
     
     if (log_file.Buffer)
         ExFreePool(log_file.Buffer);
+    
+    if (registry_path.Buffer)
+        ExFreePool(registry_path.Buffer);
 }
 
 BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) {
@@ -415,234 +419,6 @@ BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char*
     return FALSE;
 }
 
-NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, LIST_ENTRY* rollback) {
-    KEY searchkey;
-    traverse_ptr tp;
-    ULONG xasize;
-    DIR_ITEM* xa;
-    NTSTATUS Status;
-    
-    TRACE("(%p, %llx, %llx, %s, %08x, %p, %u)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
-    
-    searchkey.obj_id = inode;
-    searchkey.obj_type = TYPE_XATTR_ITEM;
-    searchkey.offset = crc32;
-    
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return Status;
-    }
-    
-    xasize = sizeof(DIR_ITEM) - 1 + (ULONG)strlen(name) + datalen;
-    
-    // FIXME - make sure xasize not too big
-    
-    if (!keycmp(&tp.item->key, &searchkey)) { // key exists
-        UINT8* newdata;
-        ULONG size = tp.item->size;
-        
-        xa = (DIR_ITEM*)tp.item->data;
-        
-        if (tp.item->size < sizeof(DIR_ITEM)) {
-            ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
-        } else {
-            while (TRUE) {
-                ULONG oldxasize;
-                
-                if (size < sizeof(DIR_ITEM) || size < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) {
-                    ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                    break;
-                }
-                
-                oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
-                
-                if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
-                    UINT64 pos;
-                    
-                    // replace
-                    newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize - oldxasize, ALLOC_TAG);
-                    if (!newdata) {
-                        ERR("out of memory\n");
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    pos = (UINT8*)xa - tp.item->data;
-                    if (pos + oldxasize < tp.item->size) { // copy after changed xattr
-                        RtlCopyMemory(newdata + pos + xasize, tp.item->data + pos + oldxasize, tp.item->size - pos - oldxasize);
-                    }
-                    
-                    if (pos > 0) { // copy before changed xattr
-                        RtlCopyMemory(newdata, tp.item->data, pos);
-                        xa = (DIR_ITEM*)(newdata + pos);
-                    } else
-                        xa = (DIR_ITEM*)newdata;
-                    
-                    xa->key.obj_id = 0;
-                    xa->key.obj_type = 0;
-                    xa->key.offset = 0;
-                    xa->transid = Vcb->superblock.generation;
-                    xa->m = datalen;
-                    xa->n = (UINT16)strlen(name);
-                    xa->type = BTRFS_TYPE_EA;
-                    RtlCopyMemory(xa->name, name, strlen(name));
-                    RtlCopyMemory(xa->name + strlen(name), data, datalen);
-                    
-                    delete_tree_item(Vcb, &tp, rollback);
-                    insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize - oldxasize, NULL, rollback);
-                    
-                    break;
-                }
-                
-                if (xa->m + xa->n >= size) { // FIXME - test this works
-                    // not found, add to end of data
-                    newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize, ALLOC_TAG);
-                    if (!newdata) {
-                        ERR("out of memory\n");
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    RtlCopyMemory(newdata, tp.item->data, tp.item->size);
-                    
-                    xa = (DIR_ITEM*)((UINT8*)newdata + tp.item->size);
-                    xa->key.obj_id = 0;
-                    xa->key.obj_type = 0;
-                    xa->key.offset = 0;
-                    xa->transid = Vcb->superblock.generation;
-                    xa->m = datalen;
-                    xa->n = (UINT16)strlen(name);
-                    xa->type = BTRFS_TYPE_EA;
-                    RtlCopyMemory(xa->name, name, strlen(name));
-                    RtlCopyMemory(xa->name + strlen(name), data, datalen);
-                    
-                    delete_tree_item(Vcb, &tp, rollback);
-                    insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize, NULL, rollback);
-                    
-                    break;
-                } else {
-                    xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
-                    size -= oldxasize;
-                }
-            }
-        }
-    } else {
-        // add new DIR_ITEM struct
-        
-        xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG);
-        if (!xa) {
-            ERR("out of memory\n");
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
-        
-        xa->key.obj_id = 0;
-        xa->key.obj_type = 0;
-        xa->key.offset = 0;
-        xa->transid = Vcb->superblock.generation;
-        xa->m = datalen;
-        xa->n = (UINT16)strlen(name);
-        xa->type = BTRFS_TYPE_EA;
-        RtlCopyMemory(xa->name, name, strlen(name));
-        RtlCopyMemory(xa->name + strlen(name), data, datalen);
-        
-        insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, NULL, rollback);
-    }
-    
-    return STATUS_SUCCESS;
-}
-
-BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, LIST_ENTRY* rollback) {
-    KEY searchkey;
-    traverse_ptr tp;
-    DIR_ITEM* xa;
-    NTSTATUS Status;
-    
-    TRACE("(%p, %llx, %llx, %s, %08x)\n", Vcb, subvol->id, inode, name, crc32);
-    
-    searchkey.obj_id = inode;
-    searchkey.obj_type = TYPE_XATTR_ITEM;
-    searchkey.offset = crc32;
-    
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return FALSE;
-    }
-    
-    if (!keycmp(&tp.item->key, &searchkey)) { // key exists
-        ULONG size = tp.item->size;
-        
-        if (tp.item->size < sizeof(DIR_ITEM)) {
-            ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
-            
-            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, 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;
-    }
-}
-
 NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
@@ -690,41 +466,10 @@ NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32
     return STATUS_SUCCESS;
 }
 
-UINT64 find_next_dir_index(device_extension* Vcb, root* subvol, UINT64 inode) {
-    KEY searchkey;
-    traverse_ptr tp, prev_tp;
-    UINT64 dirpos;
-    NTSTATUS Status;
-    
-    searchkey.obj_id = inode;
-    searchkey.obj_type = TYPE_DIR_INDEX + 1;
-    searchkey.offset = 0;
-    
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return 0;
-    }
-    
-    if (!keycmp(&searchkey, &tp.item->key)) {
-        if (!find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
-            tp = prev_tp;
-            
-            TRACE("moving back to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-        }
-    }
-    
-    if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) {
-        dirpos = tp.item->key.offset + 1;
-    } else
-        dirpos = 2;
-    
-    return dirpos;
-}
-
 static NTSTATUS STDCALL drv_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     NTSTATUS Status;
     PIO_STACK_LOCATION IrpSp;
+    device_extension* Vcb = DeviceObject->DeviceExtension;
     BOOL top_level;
 
     TRACE("close\n");
@@ -733,7 +478,7 @@ static NTSTATUS STDCALL drv_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
 
     top_level = is_top_level(Irp);
     
-    if (DeviceObject == devobj) {
+    if (DeviceObject == devobj || (Vcb && Vcb->type == VCB_TYPE_PARTITION0)) {
         TRACE("Closing file system\n");
         Status = STATUS_SUCCESS;
         goto exit;
@@ -762,55 +507,20 @@ exit:
     return Status;
 }
 
-static NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
-    NTSTATUS Status;
-    BOOL top_level;
-    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
-
-    FsRtlEnterFileSystem();
-
-    top_level = is_top_level(Irp);
-    
-//     ERR("recursive = %s\n", Irp != IoGetTopLevelIrp() ? "TRUE" : "FALSE");
-    
-    _SEH2_TRY {
-        if (IrpSp->MinorFunction & IRP_MN_COMPLETE) {
-            CcMdlWriteComplete(IrpSp->FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress);
-            
-            Irp->MdlAddress = NULL;
-            Irp->IoStatus.Status = STATUS_SUCCESS;
-        } else {
-            Status = write_file(DeviceObject, Irp);
-        }
-    } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
-        Status = _SEH2_GetExceptionCode();
-    } _SEH2_END;
-    
-    Irp->IoStatus.Status = Status;
-
-    TRACE("wrote %u bytes\n", Irp->IoStatus.Information);
-    
-    if (Status != STATUS_PENDING)
-        IoCompleteRequest(Irp, IO_NO_INCREMENT);
-    
-    if (top_level) 
-        IoSetTopLevelIrp(NULL);
-    
-    FsRtlExitFileSystem();
-    
-    TRACE("returning %08x\n", Status);
-
-    return Status;
-}
-
 static NTSTATUS STDCALL drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     NTSTATUS Status;
     BOOL top_level;
+    device_extension* Vcb = DeviceObject->DeviceExtension;
 
     FsRtlEnterFileSystem();
 
     top_level = is_top_level(Irp);
     
+    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
+        Status = part0_passthrough(DeviceObject, Irp);
+        goto exit;
+    }
+    
     FIXME("STUB: query ea\n");
     Status = STATUS_NOT_IMPLEMENTED;
     
@@ -818,7 +528,8 @@ static NTSTATUS STDCALL drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp
     Irp->IoStatus.Information = 0;
 
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
-    
+
+exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -836,6 +547,11 @@ static NTSTATUS STDCALL drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
 
     top_level = is_top_level(Irp);
     
+    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
+        Status = part0_passthrough(DeviceObject, Irp);
+        goto exit;
+    }
+    
     FIXME("STUB: set ea\n");
     Status = STATUS_NOT_IMPLEMENTED;
     
@@ -849,6 +565,7 @@ static NTSTATUS STDCALL drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
 
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
     
+exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -862,6 +579,7 @@ static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIR
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
     PFILE_OBJECT FileObject = IrpSp->FileObject;
     fcb* fcb = FileObject->FsContext;
+    device_extension* Vcb = DeviceObject->DeviceExtension;
     BOOL top_level;
 
     TRACE("flush buffers\n");
@@ -870,6 +588,11 @@ static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIR
 
     top_level = is_top_level(Irp);
     
+    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
+        Status = part0_passthrough(DeviceObject, Irp);
+        goto exit;
+    }
+    
     Status = STATUS_SUCCESS;
     Irp->IoStatus.Status = Status;
     Irp->IoStatus.Information = 0;
@@ -887,6 +610,7 @@ static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIR
     
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
     
+exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -895,6 +619,18 @@ static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIR
     return Status;
 }
 
+static void calculate_total_space(device_extension* Vcb, LONGLONG* totalsize, LONGLONG* freespace) {
+    UINT8 factor;
+    
+    if (Vcb->data_flags & BLOCK_FLAG_DUPLICATE || Vcb->data_flags & BLOCK_FLAG_RAID1 || Vcb->data_flags & BLOCK_FLAG_RAID10)
+        factor = 2;
+    else
+        factor = 1;
+    
+    *totalsize = (Vcb->superblock.total_bytes / Vcb->superblock.sector_size) / factor;
+    *freespace = ((Vcb->superblock.total_bytes - Vcb->superblock.bytes_used) / Vcb->superblock.sector_size) / factor;
+}
+
 static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     PIO_STACK_LOCATION IrpSp;
     NTSTATUS Status;
@@ -920,6 +656,11 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj
     FsRtlEnterFileSystem();
     top_level = is_top_level(Irp);
     
+    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
+        Status = part0_passthrough(DeviceObject, Irp);
+        goto exit;
+    }    
+    
     IrpSp = IoGetCurrentIrpStackLocation(Irp);
     
     Status = STATUS_NOT_IMPLEMENTED;
@@ -944,7 +685,7 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj
             
             data->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH |
                                          FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS | FILE_SUPPORTS_HARD_LINKS | FILE_PERSISTENT_ACLS |
-                                         FILE_SUPPORTS_REPARSE_POINTS;
+                                         FILE_SUPPORTS_REPARSE_POINTS | FILE_SUPPORTS_SPARSE_FILES | FILE_SUPPORTS_OBJECT_IDS;
             if (Vcb->readonly)
                 data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
                                          
@@ -973,16 +714,10 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj
         case FileFsFullSizeInformation:
         {
             FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
-            UINT64 totalsize, freespace;
             
             TRACE("FileFsFullSizeInformation\n");
             
-            // FIXME - calculate correctly for RAID
-            totalsize = Vcb->superblock.total_bytes / Vcb->superblock.sector_size;
-            freespace = (Vcb->superblock.total_bytes - Vcb->superblock.bytes_used) / Vcb->superblock.sector_size;
-            
-            ffsi->TotalAllocationUnits.QuadPart = totalsize;
-            ffsi->ActualAvailableAllocationUnits.QuadPart = freespace;
+            calculate_total_space(Vcb, &ffsi->TotalAllocationUnits.QuadPart, &ffsi->ActualAvailableAllocationUnits.QuadPart);
             ffsi->CallerAvailableAllocationUnits.QuadPart = ffsi->ActualAvailableAllocationUnits.QuadPart;
             ffsi->SectorsPerAllocationUnit = 1;
             ffsi->BytesPerSector = Vcb->superblock.sector_size;
@@ -994,23 +729,27 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj
         }
 
         case FileFsObjectIdInformation:
-            FIXME("STUB: FileFsObjectIdInformation\n");
+        {
+            FILE_FS_OBJECTID_INFORMATION* ffoi = Irp->AssociatedIrp.SystemBuffer;
+            
+            TRACE("FileFsObjectIdInformation\n");
+            
+            RtlCopyMemory(ffoi->ObjectId, &Vcb->superblock.uuid.uuid[0], sizeof(UCHAR) * 16);
+            RtlZeroMemory(ffoi->ExtendedInfo, sizeof(ffoi->ExtendedInfo));
+            
+            BytesCopied = sizeof(FILE_FS_OBJECTID_INFORMATION);
+            Status = STATUS_SUCCESS;
+            
             break;
+        }
 
         case FileFsSizeInformation:
         {
             FILE_FS_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
-            UINT64 totalsize, freespace;
             
             TRACE("FileFsSizeInformation\n");
             
-            // FIXME - calculate correctly for RAID
-            // FIXME - is this returning the right free space?
-            totalsize = Vcb->superblock.dev_item.num_bytes / Vcb->superblock.sector_size;
-            freespace = (Vcb->superblock.dev_item.num_bytes - Vcb->superblock.dev_item.bytes_used) / Vcb->superblock.sector_size;
-            
-            ffsi->TotalAllocationUnits.QuadPart = totalsize;
-            ffsi->AvailableAllocationUnits.QuadPart = freespace;
+            calculate_total_space(Vcb, &ffsi->TotalAllocationUnits.QuadPart, &ffsi->AvailableAllocationUnits.QuadPart);
             ffsi->SectorsPerAllocationUnit = 1;
             ffsi->BytesPerSector = Vcb->superblock.sector_size;
             
@@ -1030,7 +769,7 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj
             TRACE("FileFsVolumeInformation\n");
             TRACE("max length = %u\n", IrpSp->Parameters.QueryVolume.Length);
             
-            acquire_tree_lock(Vcb, FALSE);
+            ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
             
 //             orig_label_len = label_len = (ULONG)(wcslen(Vcb->label) * sizeof(WCHAR));
             RtlUTF8ToUnicodeN(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
@@ -1062,7 +801,7 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj
                 TRACE("label = %.*S\n", label_len / sizeof(WCHAR), data->VolumeLabel);
             }
             
-            release_tree_lock(Vcb, FALSE);
+            ExReleaseResourceLite(&Vcb->tree_lock);
 
             BytesCopied = sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len;
             Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
@@ -1071,7 +810,7 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj
 
         default:
             Status = STATUS_INVALID_PARAMETER;
-            WARN("unknown FsInformatClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
+            WARN("unknown FsInformationClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
             break;
     }
     
@@ -1090,6 +829,7 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj
     
     IoCompleteRequest( Irp, IO_DISK_INCREMENT );
     
+exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -1277,7 +1017,7 @@ NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_t
         InsertTailList(&Vcb->trees, &t->list_entry);
         
         t->write = TRUE;
-        Vcb->write_trees++;
+        Vcb->need_write = TRUE;
     }
     
     *rootptr = r;
@@ -1308,6 +1048,114 @@ NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_t
 //     }
 // }
 
+// 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;
+// }
+
 static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) {
     ULONG utf8len;
     NTSTATUS Status;
@@ -1325,14 +1173,14 @@ static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATI
     
     // FIXME - check for '/' and '\\' and reject
     
-    acquire_tree_lock(Vcb, TRUE);
-    
 //     utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
     
     Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE * sizeof(WCHAR), &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
     if (!NT_SUCCESS(Status))
         goto release;
     
+    ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+    
     if (utf8len < MAX_LABEL_SIZE * sizeof(WCHAR))
         RtlZeroMemory(Vcb->superblock.label + utf8len, (MAX_LABEL_SIZE * sizeof(WCHAR)) - utf8len);
     
@@ -1340,11 +1188,13 @@ static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATI
 //     test_tree_splitting(Vcb);
 //     test_dropping_tree(Vcb);
 //     test_creating_root(Vcb);
+//     test_alloc_chunk(Vcb);
+//     test_space_list(Vcb);
     
-    Status = consider_write(Vcb);
+    Vcb->need_write = TRUE;
     
 release:  
-    release_tree_lock(Vcb, TRUE);
+    ExReleaseResourceLite(&Vcb->tree_lock);
 
 end:
     TRACE("returning %08x\n", Status);
@@ -1364,6 +1214,11 @@ static NTSTATUS STDCALL drv_set_volume_information(IN PDEVICE_OBJECT DeviceObjec
 
     top_level = is_top_level(Irp);
     
+    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
+        Status = part0_passthrough(DeviceObject, Irp);
+        goto exit;
+    }
+    
     Status = STATUS_NOT_IMPLEMENTED;
     
     if (Vcb->readonly) {
@@ -1371,7 +1226,7 @@ static NTSTATUS STDCALL drv_set_volume_information(IN PDEVICE_OBJECT DeviceObjec
         goto end;
     }
     
-    if (Vcb->removing) {
+    if (Vcb->removing || Vcb->locked) {
         Status = STATUS_ACCESS_DENIED;
         goto end;
     }
@@ -1402,6 +1257,7 @@ end:
 
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
     
+exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -1480,7 +1336,7 @@ NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, U
     return STATUS_SUCCESS;
 }
 
-NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback) {
+NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     BOOL changed = FALSE;
@@ -1547,9 +1403,6 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN
                         insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, rollback);
                     }
                     
-                    if (index)
-                        *index = ir->index;
-                    
                     break;
                 }
                 
@@ -1635,9 +1488,6 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN
                         insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, rollback);
                     }
                     
-                    if (index)
-                        *index = ier->index;
-                    
                     break;
                 }
                 
@@ -1655,156 +1505,6 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN
     return changed ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
 }
 
-static NTSTATUS delete_subvol(file_ref* fileref, LIST_ENTRY* rollback) {
-    NTSTATUS Status;
-    UINT64 index;
-    KEY searchkey;
-    traverse_ptr tp;
-    UINT32 crc32;
-    ROOT_ITEM* ri;
-    BOOL no_ref = FALSE;
-    fcb* fcb = fileref->fcb;
-    
-    // delete ROOT_REF in root tree
-    
-    Status = delete_root_ref(fcb->Vcb, fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, &fileref->utf8, &index, rollback);
-    
-    // A bug in Linux means that if you create a snapshot of a subvol containing another subvol,
-    // the ROOT_REF and ROOT_BACKREF items won't be created, nor will num_references of ROOT_ITEM
-    // be increased. In this case, we just unlink the subvol from its parent, and don't worry
-    // about anything else.
-    
-    if (Status == STATUS_NOT_FOUND)
-        no_ref = TRUE;
-    else if (!NT_SUCCESS(Status)) {
-        ERR("delete_root_ref returned %08x\n", Status);
-        return Status;
-    }
-    
-    if (!no_ref) {
-        // delete ROOT_BACKREF in root tree
-        
-        Status = update_root_backref(fcb->Vcb, fcb->subvol->id, fileref->parent->fcb->subvol->id, rollback);
-        if (!NT_SUCCESS(Status)) {
-            ERR("update_root_backref returned %08x\n", Status);
-            return Status;
-        }
-    }
-    
-    // delete DIR_ITEM in parent
-    
-    crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length);
-    Status = delete_dir_item(fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, &fileref->utf8, rollback);
-    if (!NT_SUCCESS(Status)) {
-        ERR("delete_dir_item returned %08x\n", Status);
-        return Status;
-    }
-    
-    // delete DIR_INDEX in parent
-    
-    if (!no_ref) {
-        searchkey.obj_id = fileref->parent->fcb->inode;
-        searchkey.obj_type = TYPE_DIR_INDEX;
-        searchkey.offset = index;
-        
-        Status = find_item(fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE);
-        if (!NT_SUCCESS(Status)) {
-            ERR("find_item 1 returned %08x\n", Status);
-            return Status;
-        }
-    
-        if (!keycmp(&searchkey, &tp.item->key)) {
-            delete_tree_item(fcb->Vcb, &tp, rollback);
-            TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-        }
-    } else {
-        BOOL b;
-        traverse_ptr next_tp;
-        
-        // If we have no ROOT_REF, we have to look through all the DIR_INDEX entries manually :-(
-        
-        searchkey.obj_id = fileref->parent->fcb->inode;
-        searchkey.obj_type = TYPE_DIR_INDEX;
-        searchkey.offset = 0;
-        
-        Status = find_item(fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE);
-        if (!NT_SUCCESS(Status)) {
-            ERR("find_item 1 returned %08x\n", Status);
-            return Status;
-        }
-        
-        do {
-            if (tp.item->key.obj_type == TYPE_DIR_INDEX && tp.item->size >= sizeof(DIR_ITEM)) {
-                DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
-                
-                if (di->key.obj_id == fcb->subvol->id && di->key.obj_type == TYPE_ROOT_ITEM && di->n == fileref->utf8.Length &&
-                    tp.item->size >= sizeof(DIR_ITEM) - 1 + di->m + di->n && RtlCompareMemory(fileref->utf8.Buffer, di->name, di->n) == di->n) {
-                    delete_tree_item(fcb->Vcb, &tp, rollback);
-                    break;
-                }
-            }
-        
-            b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
-            
-            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 (no_ref)
-        return STATUS_SUCCESS;
-    
-    if (fcb->subvol->root_item.num_references > 1) {
-        UINT64 offset;
-        
-        // change ROOT_ITEM num_references
-        
-        fcb->subvol->root_item.num_references--;
-        
-        searchkey.obj_id = fcb->subvol->id;
-        searchkey.obj_type = TYPE_ROOT_ITEM;
-        searchkey.offset = 0xffffffffffffffff;
-        
-        Status = find_item(fcb->Vcb, fcb->Vcb->root_root, &tp, &searchkey, FALSE);
-        if (!NT_SUCCESS(Status)) {
-            ERR("find_item 2 returned %08x\n", Status);
-            return Status;
-        }
-        
-        if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
-            delete_tree_item(fcb->Vcb, &tp, rollback);
-            TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-            offset = tp.item->key.offset;
-        } else {
-            ERR("could not find ROOT_ITEM for subvol %llx\n", fcb->subvol->id);
-            offset = 0;
-        }
-        
-        ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
-        if (!ri) {
-            ERR("out of memory\n");
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
-        
-        RtlCopyMemory(ri, &fcb->subvol->root_item, sizeof(ROOT_ITEM));
-        
-        if (!insert_tree_item(fcb->Vcb, fcb->Vcb->root_root, fcb->subvol->id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), NULL, rollback)) {
-            ERR("insert_tree_item failed\n");
-            return STATUS_INTERNAL_ERROR;
-        }
-    } else {
-        RemoveEntryList(&fcb->subvol->list_entry);
-        
-        InsertTailList(&fcb->Vcb->drop_roots, &fcb->subvol->list_entry);
-    }
-    
-    return STATUS_SUCCESS;
-}
-
 static WCHAR* file_desc_fcb(fcb* fcb) {
     char s[60];
     UNICODE_STRING us;
@@ -1813,6 +1513,9 @@ static WCHAR* file_desc_fcb(fcb* fcb) {
     if (fcb->debug_desc)
         return fcb->debug_desc;
     
+    if (fcb == fcb->Vcb->volume_fcb)
+        return L"volume FCB";
+    
     fcb->debug_desc = ExAllocatePoolWithTag(PagedPool, 60 * sizeof(WCHAR), ALLOC_TAG);
     if (!fcb->debug_desc)
         return L"(memory error)";
@@ -1838,15 +1541,27 @@ static WCHAR* file_desc_fcb(fcb* fcb) {
 }
 
 WCHAR* file_desc_fileref(file_ref* fileref) {
+    NTSTATUS Status;
+    UNICODE_STRING fn;
+    
     if (fileref->debug_desc)
         return fileref->debug_desc;
     
-    fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, fileref->full_filename.Length + sizeof(WCHAR), ALLOC_TAG);
-    if (!fileref->debug_desc)
+    Status = fileref_get_filename(fileref, &fn, NULL);
+    if (!NT_SUCCESS(Status)) {
+        return L"ERROR";
+    }
+    
+    fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, fn.Length + sizeof(WCHAR), ALLOC_TAG);
+    if (!fileref->debug_desc) {
+        ExFreePool(fn.Buffer);
         return L"(memory error)";
+    }
     
-    RtlCopyMemory(fileref->debug_desc, fileref->full_filename.Buffer, fileref->full_filename.Length);
-    fileref->debug_desc[fileref->full_filename.Length / sizeof(WCHAR)] = 0;
+    RtlCopyMemory(fileref->debug_desc, fn.Buffer, fn.Length);
+    fileref->debug_desc[fn.Length / sizeof(WCHAR)] = 0;
+    
+    ExFreePool(fn.Buffer);
     
     return fileref->debug_desc;
 }
@@ -1863,349 +1578,155 @@ WCHAR* file_desc(PFILE_OBJECT FileObject) {
 }
 
 void send_notification_fileref(file_ref* fileref, ULONG filter_match, ULONG action) {
-    fcb* fcb = fileref->fcb;
-    
-    FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fileref->full_filename, fileref->name_offset * sizeof(WCHAR),
-                                NULL, NULL, filter_match, action, NULL);
-}
-
-NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
-    ULONG bytecount;
+    UNICODE_STRING fn;
     NTSTATUS Status;
-    char* utf8 = NULL;
-    UINT32 crc32;
-    KEY searchkey;
-    traverse_ptr tp, tp2;
-    UINT64 parinode, index;
-    root* parsubvol;
-    INODE_ITEM *ii, *dirii;
-    LARGE_INTEGER time;
-    BTRFS_TIME now;
-    LIST_ENTRY changed_sector_list;
+    USHORT name_offset;
     fcb* fcb = fileref->fcb;
-#ifdef _DEBUG
-    LARGE_INTEGER freq, time1, time2;
-#endif
     
-    if (fileref->deleted || fcb->deleted) {
-        WARN("trying to delete already-deleted file\n");
-        return STATUS_SUCCESS;
+    Status = fileref_get_filename(fileref, &fn, &name_offset);
+    if (!NT_SUCCESS(Status)) {
+        ERR("fileref_get_filename returned %08x\n", Status);
+        return;
     }
     
-    if (fileref == fcb->Vcb->root_fileref) {
-        ERR("error - trying to delete root FCB\n");
-        return STATUS_INTERNAL_ERROR;
-    }
+    FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, name_offset,
+                                  NULL, NULL, filter_match, action, NULL, NULL);
+    ExFreePool(fn.Buffer);
+}
+
+void send_notification_fcb(file_ref* fileref, ULONG filter_match, ULONG action) {
+    fcb* fcb = fileref->fcb;
+    LIST_ENTRY* le;
+    NTSTATUS Status;
     
-    if (fcb->inode == SUBVOL_ROOT_INODE) {
-        Status = delete_subvol(fileref, rollback);
-        
-        if (!NT_SUCCESS(Status))
-            goto exit;
-        else {
-            parinode = fileref->parent->fcb->inode;
-            parsubvol = fileref->parent->fcb->subvol;
-            bytecount = fileref->utf8.Length;
-            goto success2;
-        }
+    // no point looking for hardlinks if st_nlink == 1
+    if (fileref->fcb->inode_item.st_nlink == 1) {
+        send_notification_fileref(fileref, filter_match, action);
+        return;
     }
     
-#ifdef _DEBUG
-    time1 = KeQueryPerformanceCounter(&freq);
-#endif
-    
-    KeQuerySystemTime(&time);
-    win_time_to_unix(time, &now);
+    ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
     
-    if (fcb->ads) {
-        char* s;
-        TRACE("deleting ADS\n");
+    le = fcb->hardlinks.Flink;
+    while (le != &fcb->hardlinks) {
+        hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
+        file_ref* parfr;
         
-        s = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.Length + 1, ALLOC_TAG);
-        if (!s) {
-            ERR("out of memory\n");
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto exit;
-        }
-        
-        RtlCopyMemory(s, fcb->adsxattr.Buffer, fcb->adsxattr.Length);
-        s[fcb->adsxattr.Length] = 0;
+        Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr);
         
-        if (!delete_xattr(fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, s, fcb->adshash, rollback)) {
-            ERR("failed to delete xattr %s\n", s);
-        }
-        
-        ExFreePool(s);
-        
-        fileref->parent->fcb->inode_item.transid = fcb->Vcb->superblock.generation;
-        fileref->parent->fcb->inode_item.sequence++;
-        fileref->parent->fcb->inode_item.st_ctime = now;
-        
-        searchkey.obj_id = fileref->parent->fcb->inode;
-        searchkey.obj_type = TYPE_INODE_ITEM;
-        searchkey.offset = 0xffffffffffffffff;
-        
-        Status = find_item(fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE);
         if (!NT_SUCCESS(Status)) {
-            ERR("error - find_item returned %08x\n", Status);
-            goto exit;
-        }
-        
-        if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
-            ERR("error - could not find INODE_ITEM for inode %llx in subvol %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->subvol->id);
-            Status = STATUS_INTERNAL_ERROR;
-            goto exit;
-        }
-
-        ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
-        if (!ii) {
-            ERR("out of memory\n");
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto exit;
+            ERR("open_fileref_by_inode returned %08x\n", Status);
+        } else if (!parfr->deleted) {
+            LIST_ENTRY* le2;
+            BOOL found = FALSE, deleted = FALSE;
+            UNICODE_STRING* fn;
+            
+            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;
+            }
+            
+            if (!found)
+                fn = &hl->name;
+            
+            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);
+                }
+            }
+            
+            free_fileref(parfr);
         }
         
-        RtlCopyMemory(ii, &fileref->parent->fcb->inode_item, sizeof(INODE_ITEM));
-        delete_tree_item(fcb->Vcb, &tp, rollback);
-        
-        insert_tree_item(fcb->Vcb, fileref->parent->fcb->subvol, searchkey.obj_id, searchkey.obj_type, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
-        
-        fileref->parent->fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
-        fileref->parent->fcb->subvol->root_item.ctime = now;
-        
-        goto success;
-    }
-    
-    Status = RtlUnicodeToUTF8N(NULL, 0, &bytecount, fileref->filepart.Buffer, fileref->filepart.Length);
-    if (!NT_SUCCESS(Status)) {
-        ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
-        return Status;
-    }
-    
-    utf8 = ExAllocatePoolWithTag(PagedPool, bytecount + 1, ALLOC_TAG);
-    if (!utf8) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
+        le = le->Flink;
     }
     
-    RtlUnicodeToUTF8N(utf8, bytecount, &bytecount, fileref->filepart.Buffer, fileref->filepart.Length);
-    utf8[bytecount] = 0;
-    
-    crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, bytecount);
+    ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
+}
 
-    TRACE("deleting %.*S\n", file_desc_fileref(fileref));
-    
-    if (fileref->parent->fcb->subvol == fcb->subvol)
-        parinode = fileref->parent->fcb->inode;
-    else
-        parinode = SUBVOL_ROOT_INODE;
-    
-    parsubvol = fcb->subvol;
-    
-    // delete DIR_ITEM (0x54)
-    
-    Status = delete_dir_item(fcb->Vcb, fcb->subvol, parinode, crc32, &fileref->utf8, rollback);
-    if (!NT_SUCCESS(Status)) {
-        ERR("delete_dir_item returned %08x\n", Status);
-        return Status;
-    }
-    
-    // delete INODE_REF (0xc)
-    
-    index = 0;
-    
-    Status = delete_inode_ref(fcb->Vcb, fcb->subvol, fcb->inode, parinode, &fileref->utf8, &index, rollback);
-    
-    // delete DIR_INDEX (0x60)
-    
-    searchkey.obj_id = parinode;
-    searchkey.obj_type = TYPE_DIR_INDEX;
-    searchkey.offset = index;
-    
-    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        Status = STATUS_INTERNAL_ERROR;
-        goto exit;
-    }
-    
-    if (!keycmp(&searchkey, &tp.item->key)) {
-        delete_tree_item(fcb->Vcb, &tp, rollback);
-        TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-    }
-    
-    // delete INODE_ITEM (0x1)
-    
-    searchkey.obj_id = fcb->inode;
-    searchkey.obj_type = TYPE_INODE_ITEM;
-    searchkey.offset = 0;
-    
-    Status = find_item(fcb->Vcb, fcb->subvol, &tp2, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        goto exit;
-    }
-    
-    tp = tp2;
-    
-    if (keycmp(&searchkey, &tp.item->key)) {
-        ERR("error - INODE_ITEM not found\n");
-        Status = STATUS_INTERNAL_ERROR;
-        goto exit;
-    }
-    
-    if (tp.item->size < sizeof(INODE_ITEM)) {
-        ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_ITEM));
-        Status = STATUS_INTERNAL_ERROR;
-        goto exit;
-    }
-    
-    ii = (INODE_ITEM*)tp.item->data;
-    TRACE("nlink = %u\n", ii->st_nlink);
-    
-    if (ii->st_nlink > 1) {
-        INODE_ITEM* newii;
+void mark_fcb_dirty(fcb* fcb) {
+    if (!fcb->dirty) {
+#ifdef DEBUG_FCB_REFCOUNTS
+        LONG rc;
+#endif
+        dirty_fcb* dirt = ExAllocatePoolWithTag(NonPagedPool, sizeof(dirty_fcb), ALLOC_TAG);
         
-        newii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
-        if (!newii) {
-            ERR("out of memory\n");
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto exit;
+        if (!dirt) {
+            ExFreePool("out of memory\n");
+            return;
         }
         
-        RtlCopyMemory(newii, ii, sizeof(INODE_ITEM));
-        newii->st_nlink--;
-        newii->transid = fcb->Vcb->superblock.generation;
-        newii->sequence++;
-        newii->st_ctime = now;
+        fcb->dirty = TRUE;
         
-        TRACE("replacing (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-        
-        delete_tree_item(fcb->Vcb, &tp, rollback);
+#ifdef DEBUG_FCB_REFCOUNTS
+        rc = InterlockedIncrement(&fcb->refcount);
+        WARN("fcb %p: refcount now %i\n", fcb, rc);
+#else
+        InterlockedIncrement(&fcb->refcount);
+#endif
         
-        if (!insert_tree_item(fcb->Vcb, fcb->subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newii, sizeof(INODE_ITEM), NULL, rollback))
-            ERR("error - failed to insert item\n");
+        dirt->fcb = fcb;
         
-        goto success2;
+        ExInterlockedInsertTailList(&fcb->Vcb->dirty_fcbs, &dirt->list_entry, &fcb->Vcb->dirty_fcbs_lock);
     }
     
-    delete_tree_item(fcb->Vcb, &tp, rollback);
-    TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-    
-    fcb->deleted = TRUE;
-    
-    // delete XATTR_ITEM (0x18)
-    
-    while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE)) {
-        tp = tp2;
+    fcb->Vcb->need_write = TRUE;
+}
+
+void mark_fileref_dirty(file_ref* fileref) {
+    if (!fileref->dirty) {
+        dirty_fileref* dirt = ExAllocatePoolWithTag(NonPagedPool, sizeof(dirty_fileref), ALLOC_TAG);
         
-        if (tp.item->key.obj_id == fcb->inode) {
-            // FIXME - do metadata thing here too?
-            if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
-                delete_tree_item(fcb->Vcb, &tp, rollback);
-                TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-            }
-        } else
-            break;
-    }
-    
-    // excise extents
-    
-    InitializeListHead(&changed_sector_list);
-    
-    if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) {
-        Status = excise_extents(fcb->Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), &changed_sector_list, rollback);
-        if (!NT_SUCCESS(Status)) {
-            ERR("excise_extents returned %08x\n", Status);
-            goto exit;
+        if (!dirt) {
+            ExFreePool("out of memory\n");
+            return;
         }
         
-        if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM))
-            update_checksum_tree(fcb->Vcb, &changed_sector_list, rollback);
-    }
-    
-success2:
-    // update INODE_ITEM of parent
-    
-    searchkey.obj_id = parinode;
-    searchkey.obj_type = TYPE_INODE_ITEM;
-    searchkey.offset = 0;
-    
-    Status = find_item(fcb->Vcb, parsubvol, &tp, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_tree returned %08x\n", Status);
-        goto exit;
-    }
-    
-    if (keycmp(&searchkey, &tp.item->key)) {
-        ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parinode, parsubvol->id);
-        Status = STATUS_INTERNAL_ERROR;
-        goto exit;
-    }
-    
-    TRACE("fileref->parent->fcb->inode_item.st_size was %llx\n", fileref->parent->fcb->inode_item.st_size);
-    fileref->parent->fcb->inode_item.st_size -= bytecount * 2;
-    TRACE("fileref->parent->fcb->inode_item.st_size now %llx\n", fileref->parent->fcb->inode_item.st_size);
-    fileref->parent->fcb->inode_item.transid = 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;
-
-    dirii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
-    if (!dirii) {
-        ERR("out of memory\n");
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto exit;
-    }
-    
-    RtlCopyMemory(dirii, &fileref->parent->fcb->inode_item, sizeof(INODE_ITEM));
-    delete_tree_item(fcb->Vcb, &tp, rollback);
-    
-    insert_tree_item(fcb->Vcb, parsubvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, dirii, sizeof(INODE_ITEM), NULL, rollback);
-    
-    parsubvol->root_item.ctransid = fcb->Vcb->superblock.generation;
-    parsubvol->root_item.ctime = now;
-    
-success:
-    consider_write(fcb->Vcb);
-    
-    fileref->deleted = TRUE;
-    
-    fcb->Header.AllocationSize.QuadPart = 0;
-    fcb->Header.FileSize.QuadPart = 0;
-    fcb->Header.ValidDataLength.QuadPart = 0;
-    
-    if (FileObject && FileObject->PrivateCacheMap) {
-        CC_FILE_SIZES ccfs;
+        fileref->dirty = TRUE;
+        increase_fileref_refcount(fileref);
         
-        ccfs.AllocationSize = fcb->Header.AllocationSize;
-        ccfs.FileSize = fcb->Header.FileSize;
-        ccfs.ValidDataLength = fcb->Header.ValidDataLength;
+        dirt->fileref = fileref;
         
-        CcSetFileSizes(FileObject, &ccfs);
+        ExInterlockedInsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &dirt->list_entry, &fileref->fcb->Vcb->dirty_filerefs_lock);
     }
     
-    // FIXME - set deleted flag of any open FCBs for ADS
-    
-    if (FileObject && FileObject->FsContext2) {
-        ccb* ccb = FileObject->FsContext2;
-        
-        if (ccb->fileref)
-            send_notification_fileref(ccb->fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED);
-    }
-    
-#ifdef _DEBUG
-    time2 = KeQueryPerformanceCounter(NULL);
-#endif
-    
-    TRACE("time = %u (freq = %u)\n", (UINT32)(time2.QuadPart - time1.QuadPart), (UINT32)freq.QuadPart);
-    
-    Status = STATUS_SUCCESS;
-    
-exit:
-    if (utf8)
-        ExFreePool(utf8);
-    
-    return Status;
+    fileref->fcb->Vcb->need_write = TRUE;
 }
 
 void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) {
@@ -2226,26 +1747,66 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line)
         return;
     
     ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
+    
+    if (fcb->list_entry.Flink)
+        RemoveEntryList(&fcb->list_entry);
+    
+    if (fcb->list_entry_all.Flink)
+        RemoveEntryList(&fcb->list_entry_all);
+    
+    ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
    
     ExDeleteResourceLite(&fcb->nonpaged->resource);
     ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
+    ExDeleteResourceLite(&fcb->nonpaged->index_lock);
     ExFreePool(fcb->nonpaged);
     
-    if (fcb->list_entry.Flink)
-        RemoveEntryList(&fcb->list_entry);
-    
     if (fcb->sd)
         ExFreePool(fcb->sd);
     
     if (fcb->adsxattr.Buffer)
         ExFreePool(fcb->adsxattr.Buffer);
     
+    if (fcb->reparse_xattr.Buffer)
+        ExFreePool(fcb->reparse_xattr.Buffer);
+    
+    if (fcb->adsdata.Buffer)
+        ExFreePool(fcb->adsdata.Buffer);
+    
     if (fcb->debug_desc)
         ExFreePool(fcb->debug_desc);
     
-    FsRtlUninitializeFileLock(&fcb->lock);
+    while (!IsListEmpty(&fcb->extents)) {
+        LIST_ENTRY* le = RemoveHeadList(&fcb->extents);
+        extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+        
+        ExFreePool(ext->data);
+        ExFreePool(ext);
+    }
     
-    ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
+    while (!IsListEmpty(&fcb->index_list)) {
+        LIST_ENTRY* le = RemoveHeadList(&fcb->index_list);
+        index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry);
+
+        if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
+        if (ie->filepart_uc.Buffer) ExFreePool(ie->filepart_uc.Buffer);
+        ExFreePool(ie);
+    }
+    
+    while (!IsListEmpty(&fcb->hardlinks)) {
+        LIST_ENTRY* le = RemoveHeadList(&fcb->hardlinks);
+        hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
+        
+        if (hl->name.Buffer)
+            ExFreePool(hl->name.Buffer);
+        
+        if (hl->utf8.Buffer)
+            ExFreePool(hl->utf8.Buffer);
+
+        ExFreePool(hl);
+    }
+    
+    FsRtlUninitializeFileLock(&fcb->lock);
     
     ExFreePool(fcb);
 #ifdef DEBUG_FCB_REFCOUNTS
@@ -2279,6 +1840,9 @@ void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned in
     
     if (rc > 0)
         return;
+        
+    if (fr->parent)
+        ExAcquireResourceExclusiveLite(&fr->parent->nonpaged->children_lock, TRUE);
     
     // FIXME - do we need a file_ref lock?
     
@@ -2287,25 +1851,33 @@ void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned in
     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->full_filename.Buffer)
-        ExFreePool(fr->full_filename.Buffer);
-    
     if (fr->debug_desc)
         ExFreePool(fr->debug_desc);
     
+    ExDeleteResourceLite(&fr->nonpaged->children_lock);
+    
+    ExFreePool(fr->nonpaged);
+    
     // FIXME - throw error if children not empty
     
-    free_fcb(fr->fcb);
+    if (fr->fcb->fileref == fr)
+        fr->fcb->fileref = NULL;
     
     if (fr->list_entry.Flink)
         RemoveEntryList(&fr->list_entry);
     
-    if (fr->parent)
-        free_fileref((file_ref*)fr->parent);
+    if (fr->parent) {
+        ExReleaseResourceLite(&fr->parent->nonpaged->children_lock);
+        free_fileref(fr->parent);
+    }
     
+    free_fcb(fr->fcb);
     ExFreePool(fr);
 }
 
@@ -2326,14 +1898,15 @@ static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObjec
     
     TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject), fcb);
     
-    FsRtlNotifyCleanup(Vcb->NotifySync, &Vcb->DirNotifyList, ccb);
-    
     // FIXME - make sure notification gets sent if file is being deleted
     
     if (ccb) {    
         if (ccb->query_string.Buffer)
             RtlFreeUnicodeString(&ccb->query_string);
         
+        if (ccb->filename.Buffer)
+            ExFreePool(ccb->filename.Buffer);
+        
         // FIXME - use refcounts for fileref
         fileref = ccb->fileref;
         
@@ -2355,32 +1928,67 @@ static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObjec
 }
 
 void STDCALL uninit(device_extension* Vcb, BOOL flush) {
-    chunk* c;
     space* s;
     UINT64 i;
     LIST_ENTRY rollback;
+    NTSTATUS Status;
+    LIST_ENTRY* le;
+    LARGE_INTEGER time;
+    
+    RemoveEntryList(&Vcb->list_entry);
+    
+    Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
+    if (!NT_SUCCESS(Status))
+        WARN("registry_mark_volume_unmounted returned %08x\n", Status);
     
     if (flush) {
         InitializeListHead(&rollback);
         
-        acquire_tree_lock(Vcb, TRUE);
+        ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
 
-        if (Vcb->write_trees > 0)
+        if (Vcb->need_write)
             do_write(Vcb, &rollback);
         
         free_trees(Vcb);
         
         clear_rollback(&rollback);
 
-        release_tree_lock(Vcb, TRUE);
+        ExReleaseResourceLite(&Vcb->tree_lock);
+    }
+    
+    for (i = 0; i < Vcb->threads.num_threads; i++) {
+        Vcb->threads.threads[i].quit = TRUE;
+        KeSetEvent(&Vcb->threads.threads[i].event, 0, FALSE);
+        
+        KeWaitForSingleObject(&Vcb->threads.threads[i].finished, Executive, KernelMode, FALSE, NULL);
+        
+        ZwClose(Vcb->threads.threads[i].handle);
     }
     
-    // FIXME - stop async threads
+    ExFreePool(Vcb->threads.threads);
+    
+    Vcb->removing = TRUE;
+    
+    time.QuadPart = 0;
+    KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
+    KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, FALSE, NULL);
     
     free_fcb(Vcb->volume_fcb);
-    free_fileref(Vcb->root_fileref);
     
-    // FIXME - free any open fcbs?
+    if (Vcb->root_file)
+        ObDereferenceObject(Vcb->root_file);
+    
+    le = Vcb->chunks.Flink;
+    while (le != &Vcb->chunks) {
+        chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
+        
+        if (c->cache) {
+            free_fcb(c->cache);
+            c->cache = NULL;
+        }
+        
+        le = le->Flink;
+    }
 
     while (!IsListEmpty(&Vcb->roots)) {
         LIST_ENTRY* le = RemoveHeadList(&Vcb->roots);
@@ -2392,7 +2000,9 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) {
     }
     
     while (!IsListEmpty(&Vcb->chunks)) {
-        LIST_ENTRY* le = RemoveHeadList(&Vcb->chunks);
+        chunk* c;
+        
+        le = RemoveHeadList(&Vcb->chunks);
         c = CONTAINING_RECORD(le, chunk, list_entry);
         
         while (!IsListEmpty(&c->space)) {
@@ -2402,19 +2012,42 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) {
             ExFreePool(s);
         }
         
+        while (!IsListEmpty(&c->deleting)) {
+            LIST_ENTRY* le2 = RemoveHeadList(&c->deleting);
+            s = CONTAINING_RECORD(le2, space, list_entry);
+            
+            ExFreePool(s);
+        }
+        
         if (c->devices)
             ExFreePool(c->devices);
         
+        if (c->cache)
+            free_fcb(c->cache);
+        
+        ExDeleteResourceLite(&c->nonpaged->lock);
+        ExDeleteResourceLite(&c->nonpaged->changed_extents_lock);
+        
+        ExFreePool(c->nonpaged);
         ExFreePool(c->chunk_item);
         ExFreePool(c);
     }
     
+    // FIXME - free any open fcbs?
+    
+    while (!IsListEmpty(&Vcb->sector_checksums)) {
+        LIST_ENTRY* le = RemoveHeadList(&Vcb->sector_checksums);
+        changed_sector* cs = (changed_sector*)le;
+        
+        ExFreePool(cs);
+    }
+    
     for (i = 0; i < Vcb->superblock.num_devices; i++) {
-        while (!IsListEmpty(&Vcb->devices[i].disk_holes)) {
-            LIST_ENTRY* le = RemoveHeadList(&Vcb->devices[i].disk_holes);
-            disk_hole* dh = CONTAINING_RECORD(le, disk_hole, listentry);
+        while (!IsListEmpty(&Vcb->devices[i].space)) {
+            LIST_ENTRY* le = RemoveHeadList(&Vcb->devices[i].space);
+            space* s = CONTAINING_RECORD(le, space, list_entry);
             
-            ExFreePool(dh);
+            ExFreePool(s);
         }
     }
     
@@ -2423,14 +2056,149 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) {
     ExDeleteResourceLite(&Vcb->fcb_lock);
     ExDeleteResourceLite(&Vcb->load_lock);
     ExDeleteResourceLite(&Vcb->tree_lock);
+    ExDeleteResourceLite(&Vcb->checksum_lock);
+    ExDeleteResourceLite(&Vcb->chunk_lock);
     
     ZwClose(Vcb->flush_thread_handle);
 }
 
+NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
+    LARGE_INTEGER newlength, time;
+    BTRFS_TIME now;
+    NTSTATUS Status;
+
+    KeQuerySystemTime(&time);
+    win_time_to_unix(time, &now);
+
+    ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, TRUE);
+    
+    if (fileref->deleted) {
+        ExReleaseResourceLite(fileref->fcb->Header.Resource);
+        return STATUS_SUCCESS;
+    }
+    
+    fileref->deleted = TRUE;
+    mark_fileref_dirty(fileref);
+    
+    // delete INODE_ITEM (0x1)
+
+    TRACE("nlink = %u\n", fileref->fcb->inode_item.st_nlink);
+    
+    if (!fileref->fcb->ads) {
+        if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
+            LIST_ENTRY* le;
+            
+            mark_fcb_dirty(fileref->fcb);
+            
+            if (fileref->fcb->inode_item.st_nlink > 1) {
+                fileref->fcb->inode_item.st_nlink--;
+                fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
+                fileref->fcb->inode_item.sequence++;
+                fileref->fcb->inode_item.st_ctime = now;
+            } else {
+                fileref->fcb->deleted = TRUE;
+            
+                // excise extents
+                
+                if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
+                    Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), rollback);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("excise_extents returned %08x\n", Status);
+                        ExReleaseResourceLite(fileref->fcb->Header.Resource);
+                        return Status;
+                    }
+                }
+                
+                fileref->fcb->Header.AllocationSize.QuadPart = 0;
+                fileref->fcb->Header.FileSize.QuadPart = 0;
+                fileref->fcb->Header.ValidDataLength.QuadPart = 0;
+                
+                if (FileObject) {
+                    CC_FILE_SIZES ccfs;
+                    
+                    ccfs.AllocationSize = fileref->fcb->Header.AllocationSize;
+                    ccfs.FileSize = fileref->fcb->Header.FileSize;
+                    ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength;
+                    
+                    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 (hl->name.Buffer)
+                        ExFreePool(hl->name.Buffer);
+                    
+                    if (hl->utf8.Buffer)
+                        ExFreePool(hl->utf8.Buffer);
+                    
+                    ExFreePool(hl);
+                    break;
+                }
+                
+                le = le->Flink;
+            }
+        } else { // subvolume
+            if (fileref->fcb->subvol->root_item.num_references > 1) {
+                fileref->fcb->subvol->root_item.num_references--;
+                
+                mark_fcb_dirty(fileref->fcb); // so ROOT_ITEM gets updated
+            } else {
+                // FIXME - we need a lock here
+                
+                RemoveEntryList(&fileref->fcb->subvol->list_entry);
+                
+                InsertTailList(&fileref->fcb->Vcb->drop_roots, &fileref->fcb->subvol->list_entry);
+            }
+        }
+    } else {
+        fileref->fcb->deleted = TRUE;
+        mark_fcb_dirty(fileref->fcb);
+    }
+    
+    // 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;
+    ExReleaseResourceLite(fileref->parent->fcb->Header.Resource);
+
+    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;
+    
+    if (FileObject && FileObject->Flags & FO_CACHE_SUPPORTED && fileref->fcb->nonpaged->segment_object.DataSectionObject)
+        CcPurgeCacheSection(&fileref->fcb->nonpaged->segment_object, NULL, 0, FALSE);
+    
+    newlength.QuadPart = 0;
+    
+    if (FileObject && !CcUninitializeCacheMap(FileObject, &newlength, NULL))
+        TRACE("CcUninitializeCacheMap failed\n");
+
+    ExReleaseResourceLite(fileref->fcb->Header.Resource);
+    
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     NTSTATUS Status;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     PFILE_OBJECT FileObject = IrpSp->FileObject;
+    device_extension* Vcb = DeviceObject->DeviceExtension;
     fcb* fcb;
     BOOL top_level;
 
@@ -2440,6 +2208,11 @@ static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
 
     top_level = is_top_level(Irp);
     
+    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
+        Status = part0_passthrough(DeviceObject, Irp);
+        goto exit2;
+    }
+    
     if (DeviceObject == devobj) {
         TRACE("closing file system\n");
         Status = STATUS_SUCCESS;
@@ -2460,6 +2233,8 @@ static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
         
         IoRemoveShareAccess(FileObject, &fcb->share_access);
         
+        FsRtlNotifyCleanup(Vcb->NotifySync, &Vcb->DirNotifyList, ccb);    
+        
         oc = InterlockedDecrement(&fcb->open_count);
 #ifdef DEBUG_FCB_REFCOUNTS
         ERR("fcb %p: open_count now %i\n", fcb, oc);
@@ -2471,47 +2246,51 @@ static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
         if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0)
             fileref->delete_on_close = FALSE;
         
+        if (Vcb->locked && Vcb->locked_fileobj == FileObject) {
+            TRACE("unlocking volume\n");
+            do_unlock_volume(Vcb);
+            FsRtlNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK);
+        }
+        
         if (oc == 0) {
-            if (fileref && fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
+            if (!Vcb->removing) {
                 LIST_ENTRY rollback;
+        
                 InitializeListHead(&rollback);
-                
-                acquire_tree_lock(fcb->Vcb, TRUE);
-                
-                Status = delete_fileref(fileref, FileObject, &rollback);
-                
-                if (NT_SUCCESS(Status)) {
-                    LARGE_INTEGER newlength;
-
-                    if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject)
-                        CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE);
+            
+                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);
                     
-                    newlength.QuadPart = 0;
+                    ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
                     
-                    if (!CcUninitializeCacheMap(FileObject, &newlength, NULL)) {
-                        TRACE("CcUninitializeCacheMap failed\n");
+                    Status = delete_fileref(fileref, FileObject, &rollback);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_fileref returned %08x\n", Status);
+                        do_rollback(Vcb, &rollback);
+                        ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+                        goto exit;
                     }
                     
+                    ExReleaseResourceLite(&fcb->Vcb->tree_lock);
                     clear_rollback(&rollback);
-                } else
-                    do_rollback(fcb->Vcb, &rollback);
-                
-                release_tree_lock(fcb->Vcb, TRUE);
-            } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) {
-                IO_STATUS_BLOCK iosb;
-                CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
-                
-                if (!NT_SUCCESS(iosb.Status)) {
-                    ERR("CcFlushCache returned %08x\n", iosb.Status);
-                }
+                } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) {
+                    IO_STATUS_BLOCK iosb;
+                    CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
+                    
+                    if (!NT_SUCCESS(iosb.Status)) {
+                        ERR("CcFlushCache returned %08x\n", iosb.Status);
+                    }
 
-                ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
-                ExReleaseResourceLite(fcb->Header.PagingIoResource);
+                    if (!ExIsResourceAcquiredSharedLite(fcb->Header.PagingIoResource)) {
+                        ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
+                        ExReleaseResourceLite(fcb->Header.PagingIoResource);
+                    }
 
-                CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE);
-                
-                TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
-                      FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
+                    CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE);
+                    
+                    TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
+                        FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
+                }
             }
             
             if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
@@ -2529,6 +2308,7 @@ exit:
     
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
     
+exit2:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -2606,10 +2386,6 @@ ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r
     return att;
 }
 
-// static int STDCALL utf8icmp(char* a, char* b) {
-//     return strcmp(a, b); // FIXME - don't treat as ASCII
-// }
-
 NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer) {
     IO_STATUS_BLOCK* IoStatus;
     LARGE_INTEGER Offset;
@@ -2714,7 +2490,7 @@ exit:
     return Status;
 }
 
-static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT device) {
+static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT device, UINT64 length) {
     NTSTATUS Status;
     superblock* sb;
     unsigned int i, to_read;
@@ -2731,7 +2507,7 @@ static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT de
     i = 0;
     
     while (superblock_addrs[i] > 0) {
-        if (i > 0 && superblock_addrs[i] + sizeof(superblock) > Vcb->length)
+        if (i > 0 && superblock_addrs[i] + sizeof(superblock) > length)
             break;
         
         Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb);
@@ -2741,6 +2517,8 @@ static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT de
             return Status;
         }
         
+        // FIXME - check checksum before accepting?
+        
         TRACE("got superblock %u!\n", i);
 
         if (i == 0 || sb->generation > Vcb->superblock.generation)
@@ -2805,31 +2583,6 @@ NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID
     return Status;
 }
 
-// static void STDCALL find_chunk_root(device_extension* Vcb) {
-//     UINT32 i;
-//     KEY* key;
-//     
-//     i = 0;
-//     while (i < Vcb->superblock.n) {
-//         key = &Vcb->superblock.sys_chunk_array[i];
-//         i += sizeof(KEY);
-//     }
-//     
-//     // FIXME
-// }
-
-// static void STDCALL insert_ltp(device_extension* Vcb, log_to_phys* ltp) {
-//     if (!Vcb->log_to_phys) {
-//         Vcb->log_to_phys = ltp;
-//         ltp->next = NULL;
-//         return;
-//     }
-//     
-//     // FIXME - these should be ordered
-//     ltp->next = Vcb->log_to_phys;
-//     Vcb->log_to_phys = ltp;
-// }
-
 static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, traverse_ptr* tp) {
     root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
     if (!r) {
@@ -2841,7 +2594,6 @@ static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr,
     r->path.Buffer = NULL;
     r->treeholder.address = addr;
     r->treeholder.tree = NULL;
-    init_tree_holder(&r->treeholder);
     InitializeListHead(&r->fcbs);
 
     r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
@@ -2936,23 +2688,6 @@ static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS add_disk_hole(LIST_ENTRY* disk_holes, UINT64 address, UINT64 size) {
-    disk_hole* dh = ExAllocatePoolWithTag(PagedPool, sizeof(disk_hole), ALLOC_TAG);
-    
-    if (!dh) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    dh->address = address;
-    dh->size = size;
-    dh->provisional = FALSE;
-    
-    InsertTailList(disk_holes, &dh->listentry);
-    
-    return STATUS_SUCCESS;
-}
-
 static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
@@ -2960,7 +2695,7 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
     UINT64 lastaddr;
     NTSTATUS Status;
     
-    InitializeListHead(&dev->disk_holes);
+    InitializeListHead(&dev->space);
     
     searchkey.obj_id = dev->devitem.dev_id;
     searchkey.obj_type = TYPE_DEV_EXTENT;
@@ -2980,9 +2715,9 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
                 DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
                 
                 if (tp.item->key.offset > lastaddr) {
-                    Status = add_disk_hole(&dev->disk_holes, lastaddr, tp.item->key.offset - lastaddr);
+                    Status = add_space_entry(&dev->space, NULL, lastaddr, tp.item->key.offset - lastaddr);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("add_disk_hole returned %08x\n", Status);
+                        ERR("add_space_entry returned %08x\n", Status);
                         return Status;
                     }
                 }
@@ -3003,22 +2738,20 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
     } while (b);
     
     if (lastaddr < dev->devitem.num_bytes) {
-        Status = add_disk_hole(&dev->disk_holes, lastaddr, dev->devitem.num_bytes - lastaddr);
+        Status = add_space_entry(&dev->space, NULL, lastaddr, dev->devitem.num_bytes - lastaddr);
         if (!NT_SUCCESS(Status)) {
-            ERR("add_disk_hole returned %08x\n", Status);
+            ERR("add_space_entry returned %08x\n", Status);
             return Status;
         }
     }
     
-    // FIXME - free disk_holes when unmounting
-    
     return STATUS_SUCCESS;
 }
 
 device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) {
     UINT64 i;
     
-    for (i = 0; i < Vcb->superblock.num_devices; i++) {
+    for (i = 0; i < Vcb->devices_loaded; i++) {
         TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i,
             Vcb->devices[i].devitem.device_uuid.uuid[0], Vcb->devices[i].devitem.device_uuid.uuid[1], Vcb->devices[i].devitem.device_uuid.uuid[2], Vcb->devices[i].devitem.device_uuid.uuid[3], Vcb->devices[i].devitem.device_uuid.uuid[4], Vcb->devices[i].devitem.device_uuid.uuid[5], Vcb->devices[i].devitem.device_uuid.uuid[6], Vcb->devices[i].devitem.device_uuid.uuid[7],
             Vcb->devices[i].devitem.device_uuid.uuid[8], Vcb->devices[i].devitem.device_uuid.uuid[9], Vcb->devices[i].devitem.device_uuid.uuid[10], Vcb->devices[i].devitem.device_uuid.uuid[11], Vcb->devices[i].devitem.device_uuid.uuid[12], Vcb->devices[i].devitem.device_uuid.uuid[13], Vcb->devices[i].devitem.device_uuid.uuid[14], Vcb->devices[i].devitem.device_uuid.uuid[15]);
@@ -3029,6 +2762,41 @@ device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) {
         }
     }
     
+    if (Vcb->devices_loaded < Vcb->superblock.num_devices && !IsListEmpty(&volumes)) {
+        LIST_ENTRY* 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) &&
+                RtlCompareMemory(uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
+            ) {
+                NTSTATUS Status;
+                PFILE_OBJECT FileObject;
+                PDEVICE_OBJECT DeviceObject;
+                
+                Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
+                    return NULL;
+                }
+                
+                DeviceObject = FileObject->DeviceObject;
+                
+                ObReferenceObject(DeviceObject);
+                ObDereferenceObject(FileObject);
+                
+                Vcb->devices[Vcb->devices_loaded].devobj = DeviceObject;
+                Vcb->devices[Vcb->devices_loaded].devitem.device_uuid = *uuid;
+                Vcb->devices_loaded++;
+                
+                return &Vcb->devices[Vcb->devices_loaded - 1];
+            }
+            
+            le = le->Flink;
+        }
+    }
+    
     WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
          uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
          uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
@@ -3036,6 +2804,58 @@ device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) {
     return NULL;
 }
 
+static BOOL is_device_removable(PDEVICE_OBJECT devobj) {
+    NTSTATUS Status;
+    STORAGE_HOTPLUG_INFO shi;
+    
+    Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), TRUE, NULL);
+    
+    if (!NT_SUCCESS(Status)) {
+        ERR("dev_ioctl returned %08x\n", Status);
+        return FALSE;
+    }
+    
+    return shi.MediaRemovable != 0 ? TRUE : FALSE;
+}
+
+static ULONG get_device_change_count(PDEVICE_OBJECT devobj) {
+    NTSTATUS Status;
+    ULONG cc;
+    IO_STATUS_BLOCK iosb;
+    
+    Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+    
+    if (!NT_SUCCESS(Status)) {
+        ERR("dev_ioctl returned %08x\n", Status);
+        return 0;
+    }
+    
+    if (iosb.Information < sizeof(ULONG)) {
+        ERR("iosb.Information was too short\n");
+        return 0;
+    }
+    
+    return cc;
+}
+
+static void init_device(device_extension* Vcb, device* dev, BOOL get_length) {
+    NTSTATUS Status;
+    GET_LENGTH_INFORMATION gli;
+    
+    dev->removable = is_device_removable(dev->devobj);
+    dev->change_count = dev->removable ? get_device_change_count(dev->devobj) : 0;
+    
+    if (get_length) {
+        Status = dev_ioctl(dev->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);
+        }
+        
+        dev->length = gli.Length.QuadPart;
+    }
+}
+
 static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
     traverse_ptr tp, next_tp;
     KEY searchkey;
@@ -3048,6 +2868,8 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
     searchkey.obj_type = 0;
     searchkey.offset = 0;
     
+    Vcb->data_flags = 0;
+    
     Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
@@ -3057,10 +2879,71 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
     do {
         TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
         
-        if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM && tp.item->key.offset == 1) {
-            // FIXME - this is a hack; make this work with multiple devices!
-            if (tp.item->size > 0)
-                RtlCopyMemory(&Vcb->devices[0].devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
+        if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM) {
+            if (tp.item->size < sizeof(DEV_ITEM)) {
+                ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_ITEM));
+            } else {
+                DEV_ITEM* di = (DEV_ITEM*)tp.item->data;
+                BOOL done = FALSE;
+                
+                for (i = 0; i < Vcb->devices_loaded; i++) {
+                    if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, &di->device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+                        RtlCopyMemory(&Vcb->devices[i].devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
+                        
+                        if (i > 0)
+                            init_device(Vcb, &Vcb->devices[i], TRUE);
+                        
+                        done = TRUE;
+                        break;
+                    }
+                }
+                
+                if (!done) {
+                    if (!IsListEmpty(&volumes) && Vcb->devices_loaded < Vcb->superblock.num_devices) {
+                        LIST_ENTRY* 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) &&
+                                RtlCompareMemory(&di->device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
+                            ) {
+                                PFILE_OBJECT FileObject;
+                                PDEVICE_OBJECT DeviceObject;
+                                
+                                Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_DATA | FILE_WRITE_DATA, &FileObject, &DeviceObject);
+                                if (!NT_SUCCESS(Status)) {
+                                    ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
+                                    return Status;
+                                }
+                                
+                                DeviceObject = FileObject->DeviceObject;
+                                
+                                ObReferenceObject(DeviceObject);
+                                ObDereferenceObject(FileObject);
+                                
+                                Vcb->devices[Vcb->devices_loaded].devobj = DeviceObject;
+                                RtlCopyMemory(&Vcb->devices[Vcb->devices_loaded].devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
+                                init_device(Vcb, &Vcb->devices[i], FALSE);
+                                Vcb->devices[i].length = v->length;
+                                Vcb->devices_loaded++;
+
+                                done = TRUE;
+                                break;
+                            }
+                            
+                            le = le->Flink;
+                        }
+                        
+                        if (!done) {
+                            ERR("volume not found: device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", tp.item->key.offset,
+                            di->device_uuid.uuid[0], di->device_uuid.uuid[1], di->device_uuid.uuid[2], di->device_uuid.uuid[3], di->device_uuid.uuid[4], di->device_uuid.uuid[5], di->device_uuid.uuid[6], di->device_uuid.uuid[7],
+                            di->device_uuid.uuid[8], di->device_uuid.uuid[9], di->device_uuid.uuid[10], di->device_uuid.uuid[11], di->device_uuid.uuid[12], di->device_uuid.uuid[13], di->device_uuid.uuid[14], di->device_uuid.uuid[15]);
+                        }
+                    } else
+                        ERR("unexpected device %llx found\n", tp.item->key.offset);
+                }
+            }
         } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
             if (tp.item->size < sizeof(CHUNK_ITEM)) {
                 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(CHUNK_ITEM));
@@ -3072,22 +2955,34 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
                 
+                c->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk_nonpaged), ALLOC_TAG);
+                
+                if (!c->nonpaged) {
+                    ERR("out of memory\n");
+                    ExFreePool(c);
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+                
                 c->size = tp.item->size;
                 c->offset = tp.item->key.offset;
                 c->used = c->oldused = 0;
-                c->space_changed = FALSE;
-                c->cache_inode = 0;
-                c->cache_size = 0;
+                c->cache = NULL;
+                c->created = FALSE;
                 
                 c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
                 
                 if (!c->chunk_item) {
                     ERR("out of memory\n");
+                    ExFreePool(c);
+                    ExFreePool(c->nonpaged);
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
             
                 RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
                 
+                if (c->chunk_item->type & BLOCK_FLAG_DATA && c->chunk_item->type > Vcb->data_flags)
+                    Vcb->data_flags = c->chunk_item->type;
+                
                 if (c->chunk_item->num_stripes > 0) {
                     CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
                     
@@ -3095,6 +2990,9 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
                     
                     if (!c->devices) {
                         ERR("out of memory\n");
+                        ExFreePool(c);
+                        ExFreePool(c->nonpaged);
+                        ExFreePool(c->chunk_item);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
                     
@@ -3105,9 +3003,17 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
                 } else
                     c->devices = NULL;
                 
+                ExInitializeResourceLite(&c->nonpaged->lock);
+                ExInitializeResourceLite(&c->nonpaged->changed_extents_lock);
+                
                 InitializeListHead(&c->space);
+                InitializeListHead(&c->space_size);
+                InitializeListHead(&c->deleting);
+                InitializeListHead(&c->changed_extents);
 
                 InsertTailList(&Vcb->chunks, &c->list_entry);
+                
+                c->list_entry_changed.Flink = NULL;
             }
         }
     
@@ -3119,29 +3025,66 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
     
     Vcb->log_to_phys_loaded = TRUE;
     
+    if (Vcb->data_flags == 0)
+        Vcb->data_flags = BLOCK_FLAG_DATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID0 : 0);
+    
     return STATUS_SUCCESS;
 }
 
 void protect_superblocks(device_extension* Vcb, chunk* c) {
-    int i = 0, j;
+    UINT16 i = 0, j;
     UINT64 off_start, off_end;
     
-    // FIXME - this will need modifying for RAID
+    // The Linux driver also protects all the space before the first superblock.
+    // I realize this confuses physical and logical addresses, but this is what btrfs-progs does - 
+    // evidently Linux assumes the chunk at 0 is always SINGLE.
+    if (c->offset < superblock_addrs[0])
+        space_list_subtract(Vcb, c, FALSE, c->offset, superblock_addrs[0] - c->offset, NULL);
     
     while (superblock_addrs[i] != 0) {
         CHUNK_ITEM* ci = c->chunk_item;
         CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
         
-        for (j = 0; j < ci->num_stripes; j++) {
-            if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
-                TRACE("cut out superblock in chunk %llx\n", c->offset);
+        if (ci->type & BLOCK_FLAG_RAID0 || ci->type & BLOCK_FLAG_RAID10) {
+            for (j = 0; j < ci->num_stripes; j++) {
+                ULONG sub_stripes = max(ci->sub_stripes, 1);
                 
-                // The Linux driver protects the whole stripe in which the superblock lives
-                
-                off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length;
-                off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length);
-                
-                add_to_space_list(c, c->offset + off_start, off_end - off_start, SPACE_TYPE_USED);
+                if (cis[j].offset + (ci->size * ci->num_stripes / sub_stripes) > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
+#ifdef _DEBUG
+                    UINT64 startoff;
+                    UINT16 startoffstripe;
+#endif
+                    
+                    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;
+                    off_start *= ci->num_stripes / sub_stripes;
+                    off_start += (j / sub_stripes) * ci->stripe_length;
+
+                    off_end = off_start + ci->stripe_length;
+                    
+#ifdef _DEBUG
+                    get_raid0_offset(off_start, ci->stripe_length, ci->num_stripes / sub_stripes, &startoff, &startoffstripe);
+                    TRACE("j = %u, startoffstripe = %u\n", j, startoffstripe);
+                    TRACE("startoff = %llx, superblock = %llx\n", startoff + cis[j].offset, superblock_addrs[i]);
+#endif
+                    
+                    space_list_subtract(Vcb, c, FALSE, c->offset + off_start, off_end - off_start, NULL);
+                }
+            }
+        } else { // SINGLE, DUPLICATE, RAID1
+            for (j = 0; j < ci->num_stripes; j++) {
+                if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
+                    TRACE("cut out superblock in chunk %llx\n", c->offset);
+                    
+                    // The Linux driver protects the whole stripe in which the superblock lives
+
+                    off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length;
+                    off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length);
+                    
+                    space_list_subtract(Vcb, c, FALSE, c->offset + off_start, off_end - off_start, NULL);
+                }
             }
         }
         
@@ -3309,25 +3252,68 @@ static NTSTATUS load_sys_chunks(device_extension* Vcb) {
 }
 
 static root* find_default_subvol(device_extension* Vcb) {
-    root* subvol;
-    UINT64 inode;
-    UINT8 type;
-    UNICODE_STRING filename;
     LIST_ENTRY* le;
     
-    static WCHAR fn[] = L"default";
+    static char fn[] = "default";
     static UINT32 crc32 = 0x8dbfc2d2;
     
     if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
-        filename.Buffer = fn;
-        filename.Length = filename.MaximumLength = (USHORT)wcslen(fn) * sizeof(WCHAR);
+        NTSTATUS Status;
+        KEY searchkey;
+        traverse_ptr tp;
+        DIR_ITEM* di;
+        
+        searchkey.obj_id = Vcb->superblock.root_dir_objectid;
+        searchkey.obj_type = TYPE_DIR_ITEM;
+        searchkey.offset = crc32;
+        
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+        if (!NT_SUCCESS(Status)) {
+            ERR("error - find_item returned %08x\n", Status);
+            goto end;
+        }
+        
+        if (keycmp(&tp.item->key, &searchkey)) {
+            ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+            goto end;
+        }
+        
+        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));
+            goto end;
+        }
+        
+        di = (DIR_ITEM*)tp.item->data;
+        
+        if (tp.item->size < sizeof(DIR_ITEM) - 1 + 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->n);
+            goto end;
+        }
         
-        if (!find_file_in_dir_with_crc32(Vcb, &filename, crc32, Vcb->root_root, Vcb->superblock.root_dir_objectid, &subvol, &inode, &type, NULL))
-            WARN("couldn't find default subvol DIR_ITEM, using default tree\n");
-        else
-            return subvol;
+        if (di->n != strlen(fn) || RtlCompareMemory(di->name, fn, di->n) != di->n) {
+            ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
+            goto end;
+        }
+        
+        if (di->key.obj_type != TYPE_ROOT_ITEM) {
+            ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di->key.obj_id, di->key.obj_type, di->key.offset);
+            goto end;
+        }
+        
+        le = Vcb->roots.Flink;
+        while (le != &Vcb->roots) {
+            root* r = CONTAINING_RECORD(le, root, list_entry);
+            
+            if (r->id == di->key.obj_id)
+                return r;
+            
+            le = le->Flink;
+        }
+        
+        ERR("could not find root %llx, using default instead\n", di->key.obj_id);
     }
     
+end:
     le = Vcb->roots.Flink;
     while (le != &Vcb->roots) {
         root* r = CONTAINING_RECORD(le, root, list_entry);
@@ -3341,38 +3327,95 @@ static root* find_default_subvol(device_extension* Vcb) {
     return NULL;
 }
 
-static BOOL is_device_removable(PDEVICE_OBJECT devobj) {
+static NTSTATUS create_worker_threads(PDEVICE_OBJECT DeviceObject) {
+    device_extension* Vcb = DeviceObject->DeviceExtension;
+    ULONG i;
     NTSTATUS Status;
-    STORAGE_HOTPLUG_INFO shi;
     
-    Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), TRUE, NULL);
+    Vcb->threads.num_threads = max(3, KeQueryActiveProcessorCount(NULL)); // FIXME - number of processors?
     
-    if (!NT_SUCCESS(Status)) {
-        ERR("dev_ioctl returned %08x\n", Status);
-        return FALSE;
+    Vcb->threads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_thread) * Vcb->threads.num_threads, ALLOC_TAG);
+    if (!Vcb->threads.threads) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    return shi.MediaRemovable != 0 ? TRUE : FALSE;
+    RtlZeroMemory(Vcb->threads.threads, sizeof(drv_thread) * Vcb->threads.num_threads);
+    
+    for (i = 0; i < Vcb->threads.num_threads; i++) {
+        Vcb->threads.threads[i].DeviceObject = DeviceObject;
+        KeInitializeEvent(&Vcb->threads.threads[i].event, SynchronizationEvent, FALSE);
+        KeInitializeEvent(&Vcb->threads.threads[i].finished, NotificationEvent, FALSE);
+        InitializeListHead(&Vcb->threads.threads[i].jobs);
+        KeInitializeSpinLock(&Vcb->threads.threads[i].spin_lock);
+        
+        Status = PsCreateSystemThread(&Vcb->threads.threads[i].handle, 0, NULL, NULL, NULL, worker_thread, &Vcb->threads.threads[i]);
+        if (!NT_SUCCESS(Status)) {
+            ULONG j;
+            
+            ERR("PsCreateSystemThread returned %08x\n", Status);
+            
+            for (j = 0; j < i; j++) {
+                Vcb->threads.threads[i].quit = TRUE;
+                KeSetEvent(&Vcb->threads.threads[i].event, 0, FALSE);
+            }
+            
+            return Status;
+        }
+    }
+    
+    return STATUS_SUCCESS;
 }
 
-static ULONG get_device_change_count(PDEVICE_OBJECT devobj) {
-    NTSTATUS Status;
-    ULONG cc;
-    IO_STATUS_BLOCK iosb;
+BOOL add_thread_job(device_extension* Vcb, PIRP Irp) {
+    ULONG threadnum;
+    thread_job* tj;
     
-    Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+    threadnum = InterlockedIncrement(&Vcb->threads.next_thread) % Vcb->threads.num_threads;
     
-    if (!NT_SUCCESS(Status)) {
-        ERR("dev_ioctl returned %08x\n", Status);
-        return 0;
+    if (Vcb->threads.threads[threadnum].quit)
+        return FALSE;
+    
+    tj = ExAllocatePoolWithTag(NonPagedPool, sizeof(thread_job), ALLOC_TAG);
+    if (!tj) {
+        Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+        Irp->IoStatus.Information = 0;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        return FALSE;
     }
     
-    if (iosb.Information < sizeof(ULONG)) {
-        ERR("iosb.Information was too short\n");
-        return 0;
+    tj->Irp = Irp;
+    
+    ExInterlockedInsertTailList(&Vcb->threads.threads[threadnum].jobs, &tj->list_entry, &Vcb->threads.threads[threadnum].spin_lock);
+    KeSetEvent(&Vcb->threads.threads[threadnum].event, 0, FALSE);
+    
+    return TRUE;
+}
+
+static BOOL raid_generations_okay(device_extension* Vcb) {
+    UINT64 i;
+    
+    // FIXME - if the difference between superblocks is small, we should try to recover
+    
+    for (i = 0; i < Vcb->superblock.num_devices; i++) {
+        LIST_ENTRY* 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) &&
+                RtlCompareMemory(&Vcb->devices[i].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", i, v->gen1, Vcb->superblock.generation - 1);
+                    return FALSE;
+                } else
+                    break;
+            }
+            le = le->Flink;
+        }
     }
     
-    return cc;
+    return TRUE;
 }
 
 static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
@@ -3381,12 +3424,13 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     PDEVICE_OBJECT DeviceToMount;
     NTSTATUS Status;
     device_extension* Vcb = NULL;
-    PARTITION_INFORMATION_EX piex;
+    GET_LENGTH_INFORMATION gli;
     UINT64 i;
     LIST_ENTRY* le;
     KEY searchkey;
     traverse_ptr tp;
     fcb* root_fcb = NULL;
+    ccb* root_ccb = NULL;
     
     TRACE("mount_vol called\n");
     
@@ -3399,16 +3443,10 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     Stack = IoGetCurrentIrpStackLocation(Irp);
     DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
 
-//     Status = NtfsHasFileSystem(DeviceToMount);
-//     if (!NT_SUCCESS(Status))
-//     {
-//         goto ByeBye;
-//     }
-
-    Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0,
-                       &piex, sizeof(piex), TRUE, NULL);
+    Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
+                       &gli, sizeof(gli), TRUE, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("error reading partition information: %08x\n", Status);
+        ERR("error reading length information: %08x\n", Status);
         Status = STATUS_UNRECOGNIZED_VOLUME;
         goto exit;
     }
@@ -3431,14 +3469,16 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     NewDeviceObject->Flags |= DO_DIRECT_IO;
     Vcb = (PVOID)NewDeviceObject->DeviceExtension;
     RtlZeroMemory(Vcb, sizeof(device_extension));
+    Vcb->type = VCB_TYPE_VOLUME;
     
     ExInitializeResourceLite(&Vcb->tree_lock);
-    Vcb->tree_lock_counter = 0;
     Vcb->open_trees = 0;
-    Vcb->write_trees = 0;
+    Vcb->need_write = FALSE;
 
     ExInitializeResourceLite(&Vcb->fcb_lock);
     ExInitializeResourceLite(&Vcb->DirResource);
+    ExInitializeResourceLite(&Vcb->checksum_lock);
+    ExInitializeResourceLite(&Vcb->chunk_lock);
 
     ExAcquireResourceExclusiveLite(&global_loading_lock, TRUE);
     InsertTailList(&VcbList, &Vcb->list_entry);
@@ -3469,10 +3509,9 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
 //                       Vcb->geometry.SectorsPerTrack, Vcb->geometry.BytesPerSector);
 //     }
     
-    Vcb->length = piex.PartitionLength.QuadPart;
-    TRACE("partition length = %u\n", piex.PartitionLength);
+    TRACE("partition length = %llx\n", gli.Length.QuadPart);
 
-    Status = read_superblock(Vcb, DeviceToMount);
+    Status = read_superblock(Vcb, DeviceToMount, gli.Length.QuadPart);
     if (!NT_SUCCESS(Status)) {
         Status = STATUS_UNRECOGNIZED_VOLUME;
         goto exit;
@@ -3485,6 +3524,18 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     } else {
         TRACE("btrfs magic found\n");
     }
+    
+    Status = registry_load_volume_options(&Vcb->superblock.uuid, &Vcb->options);
+    if (!NT_SUCCESS(Status)) {
+        ERR("registry_load_volume_options returned %08x\n", Status);
+        goto exit;
+    }
+    
+    if (Vcb->options.ignore) {
+        TRACE("ignoring volume\n");
+        Status = STATUS_UNRECOGNIZED_VOLUME;
+        goto exit;
+    }
 
     if (Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED) {
         WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED);
@@ -3498,7 +3549,6 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         
         if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && v->devnum < Vcb->superblock.dev_item.dev_id) {
             // skipping over device in RAID which isn't the first one
-            // FIXME - hide this in My Computer
             Status = STATUS_UNRECOGNIZED_VOLUME;
             goto exit;
         }
@@ -3506,13 +3556,6 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         le = le->Flink;
     }
     
-    // FIXME - remove this when we can
-    if (Vcb->superblock.num_devices > 1) {
-        WARN("not mounting - multiple devices not yet implemented\n");
-        Status = STATUS_UNRECOGNIZED_VOLUME;
-        goto exit;
-    }
-    
     Vcb->readonly = FALSE;
     if (Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED) {
         WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED);
@@ -3531,13 +3574,13 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     
     Vcb->devices[0].devobj = DeviceToMount;
     RtlCopyMemory(&Vcb->devices[0].devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
-    Vcb->devices[0].removable = is_device_removable(Vcb->devices[0].devobj);
-    Vcb->devices[0].change_count = Vcb->devices[0].removable ? get_device_change_count(Vcb->devices[0].devobj) : 0;
+    init_device(Vcb, &Vcb->devices[0], FALSE);
+    Vcb->devices[0].length = gli.Length.QuadPart;
     
     if (Vcb->superblock.num_devices > 1)
         RtlZeroMemory(&Vcb->devices[1], sizeof(DEV_ITEM) * (Vcb->superblock.num_devices - 1));
     
-    // FIXME - find other devices, if there are any
+    Vcb->devices_loaded = 1;
     
     TRACE("DeviceToMount = %p\n", DeviceToMount);
     TRACE("Stack->Parameters.MountVolume.Vpb = %p\n", Stack->Parameters.MountVolume.Vpb);
@@ -3545,19 +3588,12 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     NewDeviceObject->StackSize = DeviceToMount->StackSize + 1;
     NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
     
-//     find_chunk_root(Vcb);
-//     Vcb->chunk_root_phys_addr = Vcb->superblock.chunk_tree_addr; // FIXME - map from logical to physical (bootstrapped)
-    
-//     Vcb->root_tree_phys_addr = logical_to_physical(Vcb, Vcb->superblock.root_tree_addr);
-    
     InitializeListHead(&Vcb->roots);
     InitializeListHead(&Vcb->drop_roots);
     
     Vcb->log_to_phys_loaded = FALSE;
     
     Vcb->max_inline = Vcb->superblock.node_size / 2;
-    
-//     Vcb->write_trees = NULL;
 
     add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, NULL);
     
@@ -3566,7 +3602,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         Status = STATUS_INTERNAL_ERROR;
         goto exit;
     }
-    
+       
     InitializeListHead(&Vcb->sys_chunks);
     Status = load_sys_chunks(Vcb);
     if (!NT_SUCCESS(Status)) {
@@ -3575,7 +3611,17 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     }
     
     InitializeListHead(&Vcb->chunks);
+    InitializeListHead(&Vcb->chunks_changed);
     InitializeListHead(&Vcb->trees);
+    InitializeListHead(&Vcb->all_fcbs);
+    InitializeListHead(&Vcb->dirty_fcbs);
+    InitializeListHead(&Vcb->dirty_filerefs);
+    InitializeListHead(&Vcb->shared_extents);
+    InitializeListHead(&Vcb->sector_checksums);
+    
+    KeInitializeSpinLock(&Vcb->dirty_fcbs_lock);
+    KeInitializeSpinLock(&Vcb->dirty_filerefs_lock);
+    KeInitializeSpinLock(&Vcb->shared_extents_lock);
     
     InitializeListHead(&Vcb->DirNotifyList);
 
@@ -3587,6 +3633,26 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         goto exit;
     }
     
+    if (Vcb->superblock.num_devices > 1) {
+        if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
+            ERR("could not mount as %u device(s) missing\n", Vcb->superblock.num_devices - Vcb->devices_loaded);
+            
+            IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL);
+
+            Status = STATUS_INTERNAL_ERROR;
+            goto exit;
+        }
+        
+        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;
+        }
+    }
+    
     add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, NULL);
     
     if (!Vcb->root_root) {
@@ -3606,7 +3672,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         ERR("find_chunk_usage returned %08x\n", Status);
         goto exit;
     }
-        
+    
     // We've already increased the generation by one
     if (!Vcb->readonly && Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation) {
         WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb->superblock.generation - 1, Vcb->superblock.cache_generation);
@@ -3683,18 +3749,26 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     
     Vcb->root_fileref->fcb = root_fcb;
     InsertTailList(&root_fcb->subvol->fcbs, &root_fcb->list_entry);
+    InsertTailList(&Vcb->all_fcbs, &root_fcb->list_entry_all);
     
-    Vcb->root_fileref->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(WCHAR), ALLOC_TAG);
+    root_fcb->fileref = Vcb->root_fileref;
     
-    if (!Vcb->root_fileref->full_filename.Buffer) {
+    root_ccb = ExAllocatePoolWithTag(PagedPool, sizeof(ccb), ALLOC_TAG);
+    if (!root_ccb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto exit;
     }
     
-    Vcb->root_fileref->full_filename.Buffer[0] = '\\';
-    Vcb->root_fileref->full_filename.Length = Vcb->root_fileref->full_filename.MaximumLength = sizeof(WCHAR);
-
+    Vcb->root_file = IoCreateStreamFileObject(NULL, DeviceToMount);
+    Vcb->root_file->FsContext = root_fcb;
+    
+    RtlZeroMemory(root_ccb, sizeof(ccb));
+    root_ccb->NodeType = BTRFS_NODE_TYPE_CCB;
+    root_ccb->NodeSize = sizeof(ccb);
+    
+    Vcb->root_file->FsContext = root_ccb;
+    
     for (i = 0; i < Vcb->superblock.num_devices; i++) {
         Status = find_disk_holes(Vcb, &Vcb->devices[i]);
         if (!NT_SUCCESS(Status)) {
@@ -3705,74 +3779,18 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     
 //     root_test(Vcb);
     
-//     Vcb->StreamFileObject = IoCreateStreamFileObject(NULL,
-//                                                      Vcb->StorageDevice);
-// 
-//     InitializeListHead(&Vcb->FcbListHead);
-// 
-//     Fcb = NtfsCreateFCB(NULL, Vcb);
-//     if (Fcb == NULL)
-//     {
-//         Status = STATUS_INSUFFICIENT_RESOURCES;
-//         goto ByeBye;
-//     }
-// 
-//     Ccb = ExAllocatePoolWithTag(NonPagedPool,
-//                                 sizeof(NTFS_CCB),
-//                                 TAG_CCB);
-//     if (Ccb == NULL)
-//     {
-//         Status =  STATUS_INSUFFICIENT_RESOURCES;
-//         goto ByeBye;
-//     }
-// 
-//     RtlZeroMemory(Ccb, sizeof(NTFS_CCB));
-// 
-//     Ccb->Identifier.Type = NTFS_TYPE_CCB;
-//     Ccb->Identifier.Size = sizeof(NTFS_TYPE_CCB);
-// 
-//     Vcb->StreamFileObject->FsContext = Fcb;
-//     Vcb->StreamFileObject->FsContext2 = Ccb;
-//     Vcb->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
-//     Vcb->StreamFileObject->PrivateCacheMap = NULL;
-//     Vcb->StreamFileObject->Vpb = Vcb->Vpb;
-//     Ccb->PtrFileObject = Vcb->StreamFileObject;
-//     Fcb->FileObject = Vcb->StreamFileObject;
-//     Fcb->Vcb = (PDEVICE_EXTENSION)Vcb->StorageDevice;
-// 
-//     Fcb->Flags = FCB_IS_VOLUME_STREAM;
-// 
-//     Fcb->RFCB.FileSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
-//     Fcb->RFCB.ValidDataLength.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
-//     Fcb->RFCB.AllocationSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; /* Correct? */
-// 
-//     CcInitializeCacheMap(Vcb->StreamFileObject,
-//                          (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
-//                          FALSE,
-//                          &(NtfsGlobalData->CacheMgrCallbacks),
-//                          Fcb);
-// 
-//     ExInitializeResourceLite(&Vcb->LogToPhysLock);
-
     KeInitializeSpinLock(&Vcb->FcbListLock);
-// 
-//     /* Get serial number */
-//     NewDeviceObject->Vpb->SerialNumber = Vcb->NtfsInfo.SerialNumber;
-// 
-//     /* Get volume label */
-//     NewDeviceObject->Vpb->VolumeLabelLength = Vcb->NtfsInfo.VolumeLabelLength;
-//     RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel,
-//                   Vcb->NtfsInfo.VolumeLabel,
-//                   Vcb->NtfsInfo.VolumeLabelLength);
 
     NewDeviceObject->Vpb = Stack->Parameters.MountVolume.Vpb;
     Stack->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
-    Stack->Parameters.MountVolume.Vpb->RealDevice = DeviceToMount;
     Stack->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED;
     NewDeviceObject->Vpb->VolumeLabelLength = 4; // FIXME
     NewDeviceObject->Vpb->VolumeLabel[0] = '?';
     NewDeviceObject->Vpb->VolumeLabel[1] = 0;
     NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point?
+    Vcb->Vpb = NewDeviceObject->Vpb;
+    
+    KeInitializeEvent(&Vcb->flush_thread_finished, NotificationEvent, FALSE);
     
     Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, NewDeviceObject);
     if (!NT_SUCCESS(Status)) {
@@ -3780,32 +3798,28 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         goto exit;
     }
     
+    Status = create_worker_threads(NewDeviceObject);
+    if (!NT_SUCCESS(Status)) {
+        ERR("create_worker_threads returned %08x\n", Status);
+        goto exit;
+    }
+    
+    Status = registry_mark_volume_mounted(&Vcb->superblock.uuid);
+    if (!NT_SUCCESS(Status))
+        WARN("registry_mark_volume_mounted returned %08x\n", Status);
+    
     Status = STATUS_SUCCESS;
 
 exit:
-//     if (!NT_SUCCESS(Status))
-//     {
-//         /* Cleanup */
-//         if (Vcb && Vcb->StreamFileObject)
-//             ObDereferenceObject(Vcb->StreamFileObject);
-// 
-//         if (Fcb)
-//             ExFreePool(Fcb);
-// 
-//         if (Ccb)
-//             ExFreePool(Ccb);
-// 
-//         if (NewDeviceObject)
-//             IoDeleteDevice(NewDeviceObject);
-//     }
-
     if (Vcb) {
         ExReleaseResourceLite(&Vcb->load_lock);
     }
 
     if (!NT_SUCCESS(Status)) {
         if (Vcb) {
-            if (Vcb->root_fileref)
+            if (Vcb->root_file)
+                ObDereferenceObject(Vcb->root_file);
+            else if (Vcb->root_fileref)
                 free_fileref(Vcb->root_fileref);
             else if (root_fcb)
                 free_fcb(root_fcb);
@@ -3817,6 +3831,8 @@ exit:
             ExDeleteResourceLite(&Vcb->load_lock);
             ExDeleteResourceLite(&Vcb->fcb_lock);
             ExDeleteResourceLite(&Vcb->DirResource);
+            ExDeleteResourceLite(&Vcb->checksum_lock);
+            ExDeleteResourceLite(&Vcb->chunk_lock);
 
             if (Vcb->devices)
                 ExFreePoolWithTag(Vcb->devices, ALLOC_TAG);
@@ -3826,7 +3842,8 @@ exit:
 
         if (NewDeviceObject)
             IoDeleteDevice(NewDeviceObject);
-    }
+    } else
+        FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_MOUNT);
 
     TRACE("mount_vol done (status: %lx)\n", Status);
 
@@ -3835,7 +3852,8 @@ exit:
 
 static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     PIO_STACK_LOCATION IrpSp;
-    NTSTATUS status;
+    NTSTATUS Status;
+    device_extension* Vcb = DeviceObject->DeviceExtension;
     BOOL top_level;
 
     TRACE("file system control\n");
@@ -3844,7 +3862,12 @@ static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject,
 
     top_level = is_top_level(Irp);
     
-    status = STATUS_NOT_IMPLEMENTED;
+    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
+        Status = part0_passthrough(DeviceObject, Irp);
+        goto exit;
+    }
+    
+    Status = STATUS_NOT_IMPLEMENTED;
 
     IrpSp = IoGetCurrentIrpStackLocation( Irp );
     
@@ -3854,26 +3877,19 @@ static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject,
         case IRP_MN_MOUNT_VOLUME:
             TRACE("IRP_MN_MOUNT_VOLUME\n");
             
-//             Irp->IoStatus.Status = STATUS_SUCCESS;
-            status = mount_vol(DeviceObject, Irp);
-//             IrpSp->Parameters.MountVolume.DeviceObject = 0x0badc0de;
-//             IrpSp->Parameters.MountVolume.Vpb = 0xdeadbeef;
-            
-//             IoCompleteRequest( Irp, IO_DISK_INCREMENT );
-            
-//             return Irp->IoStatus.Status;
+            Status = mount_vol(DeviceObject, Irp);
             break;
             
         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, FALSE);
             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, TRUE);
             break;
             
         case IRP_MN_VERIFY_VOLUME:
@@ -3884,34 +3900,42 @@ static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject,
             break;
     }
 
-    Irp->IoStatus.Status = status;
+    Irp->IoStatus.Status = Status;
 
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
     
+exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
     FsRtlExitFileSystem();
 
-    return status;
+    return Status;
 }
 
 static NTSTATUS STDCALL drv_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     NTSTATUS Status;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     fcb* fcb = IrpSp->FileObject->FsContext;
+    device_extension* Vcb = DeviceObject->DeviceExtension;
     BOOL top_level;
 
     FsRtlEnterFileSystem();
 
     top_level = is_top_level(Irp);
     
+    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
+        Status = part0_passthrough(DeviceObject, Irp);
+        goto exit;
+    }
+    
     TRACE("lock control\n");
     
     Status = FsRtlProcessFileLock(&fcb->lock, Irp, NULL);
 
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
     
+exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -3920,6 +3944,101 @@ static NTSTATUS STDCALL drv_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP
     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 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 < sizeof(MOUNTDEV_NAME) - 1 + 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 = sizeof(MOUNTDEV_NAME) - 1 + 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 STDCALL drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     NTSTATUS Status;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
@@ -3928,21 +4047,23 @@ static NTSTATUS STDCALL drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PI
     fcb* fcb;
     BOOL top_level;
 
-    FIXME("STUB: device control\n");
-    
     FsRtlEnterFileSystem();
 
     top_level = is_top_level(Irp);
     
     Irp->IoStatus.Information = 0;
     
-    WARN("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
+    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
+        Status = part0_device_control(DeviceObject, Irp);
+        goto end2;
+    }
+    
+    TRACE("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
     
     if (!FileObject) {
         ERR("FileObject was NULL\n");
         Status = STATUS_INVALID_PARAMETER;
         goto end;
-        
     }
     
     fcb = FileObject->FsContext;
@@ -3953,33 +4074,24 @@ static NTSTATUS STDCALL drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PI
         goto end;
     }
     
-    if (fcb == Vcb->volume_fcb) {
-        FIXME("FIXME - pass through\n");
+    if (fcb != Vcb->volume_fcb) {
         Status = STATUS_NOT_IMPLEMENTED;
-    } else {
-        TRACE("filename = %S\n", file_desc(FileObject));
-        
-        switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
-            case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
-                TRACE("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME\n");
-                Status = STATUS_INVALID_PARAMETER;
-                break;
-
-            default:
-                WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
-                                        IrpSp->Parameters.DeviceIoControl.IoControlCode, (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0xff0000) >> 16,
-                                        (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0xc000) >> 14, (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0x3ffc) >> 2,
-                                        IrpSp->Parameters.DeviceIoControl.IoControlCode & 0x3);
-                Status = STATUS_INVALID_PARAMETER;
-                break;
-        }
+        goto end;
     }
     
+    IoSkipCurrentIrpStackLocation(Irp);
+    
+    Status = IoCallDriver(Vcb->devices[0].devobj, Irp);
+    
+    goto end2;
+    
 end:
     Irp->IoStatus.Status = Status;
 
-    IoCompleteRequest( Irp, IO_NO_INCREMENT );
+    if (Status != STATUS_PENDING)
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
     
+end2:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -3991,18 +4103,24 @@ end:
 static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     NTSTATUS Status;
     BOOL top_level;
+    device_extension* Vcb = DeviceObject->DeviceExtension;
 
-    ERR("shutdown\n");
+    TRACE("shutdown\n");
     
     FsRtlEnterFileSystem();
 
     top_level = is_top_level(Irp);
     
+    if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
+        Status = part0_passthrough(DeviceObject, Irp);
+        goto exit;
+    }    
+    
     Status = STATUS_SUCCESS;
 
     while (!IsListEmpty(&VcbList)) {
         LIST_ENTRY* le = RemoveHeadList(&VcbList);
-        device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
+        Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
         
         TRACE("shutting down Vcb %p\n", Vcb);
         
@@ -4013,7 +4131,8 @@ static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp
     Irp->IoStatus.Information = 0;
 
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
-    
+
+exit:
     if (top_level) 
         IoSetTopLevelIrp(NULL);
     
@@ -4072,250 +4191,6 @@ static void STDCALL check_cpu() {
 }
 #endif
 
-static void STDCALL read_registry(PUNICODE_STRING regpath) {
-    HANDLE h;
-    UNICODE_STRING us;
-    OBJECT_ATTRIBUTES oa;
-    ULONG dispos;
-    NTSTATUS Status;
-    WCHAR* path;
-    ULONG kvfilen, retlen, i;
-    KEY_VALUE_FULL_INFORMATION* kvfi;
-    
-    const WCHAR mappings[] = L"\\Mappings";
-#ifndef __REACTOS__
-    static WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log";
-#endif
-    
-    path = ExAllocatePoolWithTag(PagedPool, regpath->Length + (wcslen(mappings) * sizeof(WCHAR)), ALLOC_TAG);
-    if (!path) {
-        ERR("out of memory\n");
-        return;
-    }
-    
-    RtlCopyMemory(path, regpath->Buffer, regpath->Length);
-    RtlCopyMemory((UINT8*)path + regpath->Length, mappings, wcslen(mappings) * sizeof(WCHAR));
-    
-    us.Buffer = path;
-    us.Length = us.MaximumLength = regpath->Length + ((USHORT)wcslen(mappings) * sizeof(WCHAR));
-    
-    InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
-    
-    // FIXME - keep open and do notify for changes
-    Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
-    
-    if (!NT_SUCCESS(Status)) {
-        ERR("ZwCreateKey returned %08x\n", Status);
-        ExFreePool(path);
-        return;
-    }
-
-    if (dispos == REG_OPENED_EXISTING_KEY) {
-        kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) + 256;
-        kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
-        
-        if (!kvfi) {
-            ERR("out of memory\n");
-            ExFreePool(path);
-            ZwClose(h);
-            return;
-        }
-        
-        i = 0;
-        do {
-            Status = ZwEnumerateValueKey(h, i, KeyValueFullInformation, kvfi, kvfilen, &retlen);
-            
-            if (NT_SUCCESS(Status) && kvfi->DataLength > 0) {
-                UINT32 val = 0;
-                
-                RtlCopyMemory(&val, (UINT8*)kvfi + kvfi->DataOffset, min(kvfi->DataLength, sizeof(UINT32)));
-                
-                TRACE("entry %u = %.*S = %u\n", i, kvfi->NameLength / sizeof(WCHAR), kvfi->Name, val);
-                
-                add_user_mapping(kvfi->Name, kvfi->NameLength / sizeof(WCHAR), val);
-            }
-            
-            i = i + 1;
-        } while (Status != STATUS_NO_MORE_ENTRIES);
-    }
-    
-    ZwClose(h);
-
-    ExFreePool(path);
-    
-#ifdef _DEBUG
-    InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
-    
-    Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
-    
-    if (!NT_SUCCESS(Status)) {
-        ERR("ZwCreateKey returned %08x\n", Status);
-        return;
-    }
-    
-    RtlInitUnicodeString(&us, L"DebugLogLevel");
-    
-    kvfi = NULL;
-    kvfilen = 0;
-    Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
-    
-    if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) {
-        kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
-        
-        if (!kvfi) {
-            ERR("out of memory\n");
-            ZwClose(h);
-            return;
-        }
-        
-        Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
-        
-        if (NT_SUCCESS(Status)) {
-            if (kvfi->Type == REG_DWORD && kvfi->DataLength >= sizeof(UINT32)) {
-                RtlCopyMemory(&debug_log_level, ((UINT8*)kvfi) + kvfi->DataOffset, sizeof(UINT32));
-            } else {
-                Status = ZwDeleteValueKey(h, &us);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("ZwDeleteValueKey returned %08x\n", Status);
-                }
-
-                Status = ZwSetValueKey(h, &us, 0, REG_DWORD, &debug_log_level, sizeof(debug_log_level));
-                if (!NT_SUCCESS(Status)) {
-                    ERR("ZwSetValueKey reutrned %08x\n", Status);
-                }
-            }
-        }
-        
-        ExFreePool(kvfi);
-    } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
-        Status = ZwSetValueKey(h, &us, 0, REG_DWORD, &debug_log_level, sizeof(debug_log_level));
-        
-        if (!NT_SUCCESS(Status)) {
-            ERR("ZwSetValueKey reutrned %08x\n", Status);
-        }
-    } else {
-        ERR("ZwQueryValueKey returned %08x\n", Status);
-    }
-    
-    RtlInitUnicodeString(&us, L"LogDevice");
-    
-    kvfi = NULL;
-    kvfilen = 0;
-    Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
-    
-    if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) {
-        kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
-        
-        if (!kvfi) {
-            ERR("out of memory\n");
-            ZwClose(h);
-            return;
-        }
-        
-        Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
-        
-        if (NT_SUCCESS(Status)) {
-            if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) {
-                log_device.Length = log_device.MaximumLength = kvfi->DataLength;
-                log_device.Buffer = ExAllocatePoolWithTag(PagedPool, kvfi->DataLength, ALLOC_TAG);
-                
-                if (!log_device.Buffer) {
-                    ERR("out of memory\n");
-                    ExFreePool(kvfi);
-                    ZwClose(h);
-                    return;
-                }
-
-                RtlCopyMemory(log_device.Buffer, ((UINT8*)kvfi) + kvfi->DataOffset, kvfi->DataLength);
-                
-                if (log_device.Buffer[(log_device.Length / sizeof(WCHAR)) - 1] == 0)
-                    log_device.Length -= sizeof(WCHAR);
-            } else {
-                ERR("LogDevice was type %u, length %u\n", kvfi->Type, kvfi->DataLength);
-                
-                Status = ZwDeleteValueKey(h, &us);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("ZwDeleteValueKey returned %08x\n", Status);
-                }
-            }
-        }
-        
-        ExFreePool(kvfi);
-    } else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
-        ERR("ZwQueryValueKey returned %08x\n", Status);
-    }
-    
-    RtlInitUnicodeString(&us, L"LogFile");
-    
-    kvfi = NULL;
-    kvfilen = 0;
-    Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
-    
-    if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) {
-        kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
-        
-        if (!kvfi) {
-            ERR("out of memory\n");
-            ZwClose(h);
-            return;
-        }
-        
-        Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
-        
-        if (NT_SUCCESS(Status)) {
-            if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) {
-                log_file.Length = log_file.MaximumLength = kvfi->DataLength;
-                log_file.Buffer = ExAllocatePoolWithTag(PagedPool, kvfi->DataLength, ALLOC_TAG);
-                
-                if (!log_file.Buffer) {
-                    ERR("out of memory\n");
-                    ExFreePool(kvfi);
-                    ZwClose(h);
-                    return;
-                }
-
-                RtlCopyMemory(log_file.Buffer, ((UINT8*)kvfi) + kvfi->DataOffset, kvfi->DataLength);
-                
-                if (log_file.Buffer[(log_file.Length / sizeof(WCHAR)) - 1] == 0)
-                    log_file.Length -= sizeof(WCHAR);
-            } else {
-                ERR("LogFile was type %u, length %u\n", kvfi->Type, kvfi->DataLength);
-                
-                Status = ZwDeleteValueKey(h, &us);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("ZwDeleteValueKey returned %08x\n", Status);
-                }
-            }
-        }
-        
-        ExFreePool(kvfi);
-    } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
-        Status = ZwSetValueKey(h, &us, 0, REG_SZ, def_log_file, (wcslen(def_log_file) + 1) * sizeof(WCHAR));
-        
-        if (!NT_SUCCESS(Status)) {
-            ERR("ZwSetValueKey returned %08x\n", Status);
-        }
-    } else {
-        ERR("ZwQueryValueKey returned %08x\n", Status);
-    }
-    
-    if (log_file.Length == 0) {
-        log_file.Length = log_file.MaximumLength = wcslen(def_log_file) * sizeof(WCHAR);
-        log_file.Buffer = ExAllocatePoolWithTag(PagedPool, log_file.MaximumLength, ALLOC_TAG);
-        
-        if (!log_file.Buffer) {
-            ERR("out of memory\n");
-            ZwClose(h);
-            return;
-        }
-        
-        RtlCopyMemory(log_file.Buffer, def_log_file, log_file.Length);
-    }
-    
-    ZwClose(h);
-#endif
-}
-
 #ifdef _DEBUG
 static void init_logging() {
     if (log_device.Length > 0)
@@ -4418,6 +4293,16 @@ NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Regist
 #endif
 
     TRACE("DriverEntry\n");
+    
+    registry_path.Length = registry_path.MaximumLength = RegistryPath->Length;
+    registry_path.Buffer = ExAllocatePoolWithTag(PagedPool, registry_path.Length, ALLOC_TAG);
+    
+    if (!registry_path.Buffer) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlCopyMemory(registry_path.Buffer, RegistryPath->Buffer, registry_path.Length);
    
 #ifndef __REACTOS__
     check_cpu();
@@ -4457,7 +4342,7 @@ NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Regist
     dosdevice_nameW.Buffer = dosdevice_name;
     dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
 
-    Status = IoCreateDevice(DriverObject, 0, &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM, 0, FALSE, &DeviceObject);
+    Status = IoCreateDevice(DriverObject, 0, &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
     if (!NT_SUCCESS(Status)) {
         ERR("IoCreateDevice returned %08x\n", Status);
         return Status;
@@ -4480,7 +4365,7 @@ NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Regist
     }
 
     InitializeListHead(&volumes);
-    look_for_vols(&volumes);
+    look_for_vols(DriverObject, &volumes);
     
     InitializeListHead(&VcbList);
     ExInitializeResourceLite(&global_loading_lock);
index b3ba777..3246f5d 100644 (file)
@@ -70,12 +70,12 @@ BEGIN
         BLOCK "080904b0"
         BEGIN
             VALUE "FileDescription", "WinBtrfs"
-            VALUE "FileVersion", "0.4"
+            VALUE "FileVersion", "0.5"
             VALUE "InternalName", "btrfs"
             VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016"
             VALUE "OriginalFilename", "btrfs.sys"
             VALUE "ProductName", "WinBtrfs"
-            VALUE "ProductVersion", "0.4"
+            VALUE "ProductVersion", "0.5"
         END
     END
     BLOCK "VarFileInfo"
index 0aae320..d0b62fa 100644 (file)
@@ -73,6 +73,8 @@
 
 #define READ_AHEAD_GRANULARITY 0x10000 // 64 KB
 
+#define MAX_EXTENT_SIZE 0x8000000 // 128 MB
+
 #ifdef _MSC_VER
 #define try __try
 #define except __except
 struct device_extension;
 
 typedef struct {
-    PDEVICE_OBJECT devobj;
     BTRFS_UUID fsuuid;
     BTRFS_UUID devuuid;
     UINT64 devnum;
     UNICODE_STRING devpath;
+    UINT64 length;
+    UINT64 gen1, gen2;
     BOOL processed;
     LIST_ENTRY list_entry;
 } volume;
@@ -102,10 +105,42 @@ typedef struct _fcb_nonpaged {
     SECTION_OBJECT_POINTERS segment_object;
     ERESOURCE resource;
     ERESOURCE paging_resource;
+    ERESOURCE index_lock;
 } fcb_nonpaged;
 
 struct _root;
 
+typedef struct {
+    UINT64 offset;
+    EXTENT_DATA* data;
+    ULONG datalen;
+    BOOL unique;
+    BOOL ignore;
+    
+    LIST_ENTRY list_entry;
+} extent;
+
+typedef struct {
+    UINT32 hash;
+    KEY key;
+    UINT8 type;
+    UINT64 index;
+    ANSI_STRING utf8;
+    UNICODE_STRING filepart_uc;
+
+    LIST_ENTRY list_entry;
+} index_entry;
+
+typedef struct {
+    UINT64 parent;
+    UINT64 index;
+    UNICODE_STRING name;
+    ANSI_STRING utf8;
+    LIST_ENTRY list_entry;
+} hardlink;
+
+struct _file_ref;
+
 typedef struct _fcb {
     FSRTL_ADVANCED_FCB_HEADER Header;
     struct _fcb_nonpaged* nonpaged;
@@ -123,33 +158,66 @@ typedef struct _fcb {
     ULONG atts;
     SHARE_ACCESS share_access;
     WCHAR* debug_desc;
+    LIST_ENTRY extents;
+    UINT64 last_dir_index;
+    ANSI_STRING reparse_xattr;
+    LIST_ENTRY hardlinks;
+    struct _file_ref* fileref;
+    
+    BOOL index_loaded;
+    LIST_ENTRY index_list;
+    
+    BOOL dirty;
+    BOOL sd_dirty;
+    BOOL atts_changed, atts_deleted;
+    BOOL extents_changed;
+    BOOL reparse_xattr_changed;
+    BOOL created;
     
     BOOL ads;
-    UINT32 adssize;
     UINT32 adshash;
     ANSI_STRING adsxattr;
+    ANSI_STRING adsdata;
     
     LIST_ENTRY list_entry;
+    LIST_ENTRY list_entry_all;
 } fcb;
 
-struct _file_ref;
+typedef struct {
+    fcb* fcb;
+    LIST_ENTRY list_entry;
+} dirty_fcb;
+
+typedef struct {
+    ERESOURCE children_lock;
+} file_ref_nonpaged;
 
 typedef struct _file_ref {
     fcb* fcb;
     UNICODE_STRING filepart;
+    UNICODE_STRING filepart_uc;
     ANSI_STRING utf8;
-    UNICODE_STRING full_filename;
-    ULONG name_offset;
+    ANSI_STRING oldutf8;
+    UINT64 index;
     BOOL delete_on_close;
     BOOL deleted;
+    BOOL created;
+    file_ref_nonpaged* nonpaged;
     LIST_ENTRY children;
     LONG refcount;
     struct _file_ref* parent;
     WCHAR* debug_desc;
     
+    BOOL dirty;
+    
     LIST_ENTRY list_entry;
 } file_ref;
 
+typedef struct {
+    file_ref* fileref;
+    LIST_ENTRY list_entry;
+} dirty_fileref;
+
 typedef struct _ccb {
     USHORT NodeType;
     CSHORT NodeSize;
@@ -162,6 +230,7 @@ typedef struct _ccb {
     BOOL specific_file;
     ACCESS_MASK access;
     file_ref* fileref;
+    UNICODE_STRING filename;
 } ccb;
 
 // typedef struct _log_to_phys {
@@ -259,47 +328,77 @@ typedef struct _root_cache {
     struct _root_cache* next;
 } root_cache;
 
-#define SPACE_TYPE_FREE     0
-#define SPACE_TYPE_USED     1
-#define SPACE_TYPE_DELETING 2
-#define SPACE_TYPE_WRITING  3
-
 typedef struct {
-    UINT64 offset;
+    UINT64 address;
     UINT64 size;
-    UINT8 type;
     LIST_ENTRY list_entry;
+    LIST_ENTRY list_entry_size;
 } space;
 
-typedef struct {
-    UINT64 address;
-    UINT64 size;
-    BOOL provisional;
-    LIST_ENTRY listentry;
-} disk_hole;
-
 typedef struct {
     PDEVICE_OBJECT devobj;
     DEV_ITEM devitem;
     BOOL removable;
     ULONG change_count;
-    LIST_ENTRY disk_holes;
+    UINT64 length;
+    LIST_ENTRY space;
 } device;
 
+typedef struct {
+    ERESOURCE lock;
+    ERESOURCE changed_extents_lock;
+} chunk_nonpaged;
+
 typedef struct {
     CHUNK_ITEM* chunk_item;
     UINT32 size;
     UINT64 offset;
     UINT64 used;
     UINT32 oldused;
-    BOOL space_changed;
     device** devices;
-    UINT64 cache_size;
-    UINT64 cache_inode;
+    fcb* cache;
     LIST_ENTRY space;
+    LIST_ENTRY space_size;
+    LIST_ENTRY deleting;
+    LIST_ENTRY changed_extents;
+    chunk_nonpaged* nonpaged;
+    BOOL created;
+    
     LIST_ENTRY list_entry;
+    LIST_ENTRY list_entry_changed;
 } chunk;
 
+typedef struct {
+    UINT64 address;
+    UINT64 size;
+    UINT64 old_size;
+    UINT64 count;
+    UINT64 old_count;
+    BOOL no_csum;
+    LIST_ENTRY refs;
+    LIST_ENTRY old_refs;
+    LIST_ENTRY list_entry;
+} changed_extent;
+
+typedef struct {
+    EXTENT_DATA_REF edr;
+    LIST_ENTRY list_entry;
+} changed_extent_ref;
+
+typedef struct {
+    UINT64 address;
+    UINT64 size;
+    EXTENT_DATA_REF edr;
+    LIST_ENTRY list_entry;
+} shared_data_entry;
+
+typedef struct {
+    UINT64 address;
+    UINT64 parent;
+    LIST_ENTRY entries;
+    LIST_ENTRY list_entry;
+} shared_data;
+
 typedef struct {
     KEY key;
     void* data;
@@ -307,14 +406,46 @@ typedef struct {
     LIST_ENTRY list_entry;
 } sys_chunk;
 
+typedef struct {
+    PIRP Irp;
+    LIST_ENTRY list_entry;
+} thread_job;
+
+typedef struct {
+    PDEVICE_OBJECT DeviceObject;
+    HANDLE handle;
+    KEVENT event, finished;
+    BOOL quit;
+    LIST_ENTRY jobs;
+    KSPIN_LOCK spin_lock;
+} drv_thread;
+
+typedef struct {
+    ULONG num_threads;
+    LONG next_thread;
+    drv_thread* threads;
+} drv_threads;
+
+typedef struct {
+    BOOL ignore;
+} mount_options;
+
+#define VCB_TYPE_VOLUME     1
+#define VCB_TYPE_PARTITION0 2
+
 typedef struct _device_extension {
+    UINT32 type;
+    mount_options options;
+    PVPB Vpb;
     device* devices;
+    UINT64 devices_loaded;
 //     DISK_GEOMETRY geometry;
-    UINT64 length;
     superblock superblock;
 //     WCHAR label[MAX_LABEL_SIZE];
     BOOL readonly;
     BOOL removing;
+    BOOL locked;
+    PFILE_OBJECT locked_fileobj;
     fcb* volume_fcb;
     file_ref* root_fileref;
     ERESOURCE DirResource;
@@ -324,12 +455,12 @@ typedef struct _device_extension {
     ERESOURCE tree_lock;
     PNOTIFY_SYNC NotifySync;
     LIST_ENTRY DirNotifyList;
-    LONG tree_lock_counter;
     LONG open_trees;
-    ULONG write_trees;
+    BOOL need_write;
 //     ERESOURCE LogToPhysLock;
 //     UINT64 chunk_root_phys_addr;
     UINT64 root_tree_phys_addr;
+    UINT64 data_flags;
 //     log_to_phys* log_to_phys;
     LIST_ENTRY roots;
     LIST_ENTRY drop_roots;
@@ -343,11 +474,33 @@ typedef struct _device_extension {
     UINT32 max_inline;
     LIST_ENTRY sys_chunks;
     LIST_ENTRY chunks;
+    LIST_ENTRY chunks_changed;
     LIST_ENTRY trees;
+    LIST_ENTRY all_fcbs;
+    LIST_ENTRY dirty_fcbs;
+    KSPIN_LOCK dirty_fcbs_lock;
+    LIST_ENTRY dirty_filerefs;
+    KSPIN_LOCK dirty_filerefs_lock;
+    ERESOURCE checksum_lock;
+    ERESOURCE chunk_lock;
+    LIST_ENTRY sector_checksums;
+    LIST_ENTRY shared_extents;
+    KSPIN_LOCK shared_extents_lock;
     HANDLE flush_thread_handle;
+    KTIMER flush_thread_timer;
+    KEVENT flush_thread_finished;
+    drv_threads threads;
+    PFILE_OBJECT root_file;
     LIST_ENTRY list_entry;
 } device_extension;
 
+typedef struct {
+    UINT32 type;
+    PDEVICE_OBJECT devobj;
+    BTRFS_UUID uuid;
+    UNICODE_STRING name;
+} part0_device_extension;
+
 typedef struct {
     LIST_ENTRY listentry;
     PSID sid;
@@ -366,39 +519,37 @@ typedef struct {
     BOOL deleted;
 } changed_sector;
 
-enum write_tree_status {
-    WriteTreeStatus_Pending,
-    WriteTreeStatus_Success,
-    WriteTreeStatus_Error,
-    WriteTreeStatus_Cancelling,
-    WriteTreeStatus_Cancelled
+enum write_data_status {
+    WriteDataStatus_Pending,
+    WriteDataStatus_Success,
+    WriteDataStatus_Error,
+    WriteDataStatus_Cancelling,
+    WriteDataStatus_Cancelled,
+    WriteDataStatus_Ignore
 };
 
-struct write_tree_context;
+struct write_data_context;
 
 typedef struct {
-    struct write_tree_context* context;
+    struct write_data_context* context;
     UINT8* buf;
+    BOOL need_free;
     device* device;
     PIRP Irp;
     IO_STATUS_BLOCK iosb;
-    enum write_tree_status status;
+    enum write_data_status status;
     LIST_ENTRY list_entry;
-} write_tree_stripe;
+} write_data_stripe;
 
 typedef struct {
     KEVENT Event;
     LIST_ENTRY stripes;
-} write_tree_context;
+    LONG stripes_left;
+    BOOL tree;
+} write_data_context;
 
 // #pragma pack(pop)
 
-static __inline void init_tree_holder(tree_holder* th) {
-//     th->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_holder_nonpaged), ALLOC_TAG);
-//     KeInitializeSpinLock(&th->nonpaged->spin_lock);
-//     ExInitializeResourceLite(&th->nonpaged->lock); // FIXME - delete this later
-}
-
 static __inline void* map_user_buffer(PIRP Irp) {
     if (!Irp->MdlAddress) {
         return Irp->UserBuffer;
@@ -439,22 +590,28 @@ static __inline void insert_into_ordered_list(LIST_ENTRY* list, ordered_list* in
     InsertTailList(list, &ins->list_entry);
 }
 
+static __inline void get_raid0_offset(UINT64 off, UINT64 stripe_length, UINT16 num_stripes, UINT64* stripeoff, UINT16* stripe) {
+    UINT64 initoff, startoff;
+    
+    startoff = off % (num_stripes * stripe_length);
+    initoff = (off / (num_stripes * stripe_length)) * stripe_length;
+    
+    *stripe = startoff / stripe_length;
+    *stripeoff = initoff + startoff - (*stripe * stripe_length);
+}
+
 // in btrfs.c
 device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid);
-ULONG sector_align( ULONG NumberToBeAligned, ULONG Alignment );
+UINT64 sector_align( UINT64 NumberToBeAligned, UINT64 Alignment );
 int keycmp(const KEY* key1, const KEY* key2);
 ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa);
 BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen);
-NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, LIST_ENTRY* rollback);
-BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, LIST_ENTRY* rollback);
 void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line);
 void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line);
 BOOL STDCALL get_last_inode(device_extension* Vcb, root* r);
 NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback);
 NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback);
-UINT64 find_next_dir_index(device_extension* Vcb, root* subvol, UINT64 inode);
-NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback);
-NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback);
+NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback);
 fcb* create_fcb();
 file_ref* create_fileref();
 void protect_superblocks(device_extension* Vcb, chunk* c);
@@ -465,8 +622,14 @@ NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID
                            ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override, IO_STATUS_BLOCK* iosb);
 BOOL is_file_name_valid(PUNICODE_STRING us);
 void send_notification_fileref(file_ref* fileref, ULONG filter_match, ULONG action);
+void send_notification_fcb(file_ref* fileref, ULONG filter_match, ULONG action);
 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, LIST_ENTRY* rollback);
 
 #ifdef _MSC_VER
 #define funcname __FUNCTION__
@@ -533,6 +696,13 @@ void STDCALL init_fast_io_dispatch(FAST_IO_DISPATCH** fiod);
 // in crc32c.c
 UINT32 STDCALL calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen);
 
+enum rollback_type {
+    ROLLBACK_INSERT_ITEM,
+    ROLLBACK_DELETE_ITEM,
+    ROLLBACK_INSERT_EXTENT,
+    ROLLBACK_DELETE_EXTENT
+};
+
 // in treefuncs.c
 NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line);
 BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
@@ -541,22 +711,22 @@ void STDCALL free_trees(device_extension* Vcb);
 BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp, LIST_ENTRY* rollback);
 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, const char* func, const char* file, unsigned int line);
+NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, const char* func, const char* file, unsigned int line);
 NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, const char* func, const char* file, unsigned int line);
 void clear_rollback(LIST_ENTRY* rollback);
 void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback);
 void free_trees_root(device_extension* Vcb, root* r);
-NTSTATUS STDCALL read_tree(device_extension* Vcb, UINT64 addr, UINT8* buf);
+void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr);
 
 #define find_item(Vcb, r, tp, searchkey, ignore) _find_item(Vcb, r, tp, searchkey, ignore, funcname, __FILE__, __LINE__)
 #define find_next_item(Vcb, tp, next_tp, ignore) _find_next_item(Vcb, tp, next_tp, ignore, funcname, __FILE__, __LINE__)
 #define find_prev_item(Vcb, tp, prev_tp, ignore) _find_prev_item(Vcb, tp, prev_tp, ignore, funcname, __FILE__, __LINE__)
 #define free_tree(t) _free_tree(t, funcname, __FILE__, __LINE__)
-#define load_tree(t, addr, r, pt) _load_tree(t, addr, r, pt, funcname, __FILE__, __LINE__)
+#define load_tree(t, addr, r, pt, parent) _load_tree(t, addr, r, pt, parent, funcname, __FILE__, __LINE__)
 #define do_load_tree(Vcb, th, r, t, td, loaded) _do_load_tree(Vcb, th, r, t, td, loaded, funcname, __FILE__, __LINE__)  
 
 // in search.c
-void STDCALL look_for_vols(LIST_ENTRY* volumes);
+void STDCALL look_for_vols(PDRIVER_OBJECT DriverObject, LIST_ENTRY* volumes);
 
 // in cache.c
 NTSTATUS STDCALL init_cache();
@@ -565,28 +735,32 @@ extern CACHE_MANAGER_CALLBACKS* cache_callbacks;
 
 // in write.c
 NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback);
-NTSTATUS write_file(PDEVICE_OBJECT DeviceObject, PIRP Irp);
-NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache, LIST_ENTRY* rollback);
+NTSTATUS write_file(device_extension* Vcb, PIRP Irp, BOOL wait, BOOL deferred_write);
+NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache,
+                     BOOL wait, BOOL deferred_write, LIST_ENTRY* rollback);
 NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback);
-NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, LIST_ENTRY* rollback);
-NTSTATUS excise_extents_inode(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
-NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
-void update_checksum_tree(device_extension* Vcb, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
-NTSTATUS insert_sparse_extent(device_extension* Vcb, root* r, UINT64 inode, UINT64 start, UINT64 length, LIST_ENTRY* rollback);
+NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* rollback);
+void commit_checksum_changes(device_extension* Vcb, LIST_ENTRY* changed_sector_list);
+NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback);
 chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address);
-void add_to_space_list(chunk* c, UINT64 offset, UINT64 size, UINT8 type);
-NTSTATUS consider_write(device_extension* Vcb);
-BOOL insert_extent_chunk_inode(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* inode_item, chunk* c, UINT64 start_data,
-                               UINT64 length, BOOL prealloc, void* data, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
 chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback);
-NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, UINT32 length);
-NTSTATUS write_tree(device_extension* Vcb, UINT64 addr, UINT8* data, write_tree_context* wtc);
-void free_write_tree_stripes(write_tree_context* wtc);
+NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp);
+NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp);
+void free_write_data_stripes(write_data_context* wtc);
 NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollback);
+NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback);
+BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, BOOL prealloc, void* data, LIST_ENTRY* changed_sector_list,
+                         PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
+void remove_fcb_extent(extent* ext, LIST_ENTRY* rollback);
+NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset,
+                                   signed long long count, BOOL no_csum, UINT64 new_size);
 
 // in dirctrl.c
 NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type);
 
 // in security.c
 NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
@@ -595,16 +769,16 @@ void fcb_get_sd(fcb* fcb, struct _fcb* parent);
 // UINT32 STDCALL get_uid();
 void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid);
 UINT32 sid_to_uid(PSID sid);
-NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* fileref, ACCESS_STATE* as);
+NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* parfileref, ACCESS_STATE* as);
 
 // in fileinfo.c
 NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback);
-NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback);
-NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, LIST_ENTRY* rollback);
 BOOL has_open_children(file_ref* fileref);
 NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT64 end, fcb* fcb, file_ref* fileref, PFILE_OBJECT FileObject, BOOL advance_only, LIST_ENTRY* rollback);
+NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset);
+NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr);
 
 // in reparse.c
 NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen);
@@ -613,20 +787,28 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp);
 
 // in create.c
 NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, root* r, UINT64 parinode, root** subvol,
-                                         UINT64* inode, UINT8* type, PANSI_STRING utf8);
-NTSTATUS update_inode_item(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, LIST_ENTRY* rollback);
+NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr, root** subvol,
+                                             UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8);
+NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, file_ref* fr,
+                                  root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8);
 NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed);
+NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb);
+NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr, UINT32 streamhash, fcb* parent, fcb** pfcb);
+void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock);
+NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index);
 
 // in fsctl.c
 NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user);
+void do_unlock_volume(device_extension* Vcb);
 
 // in flushthread.c
 void STDCALL flush_thread(void* context);
 
 // in read.c
 NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp);
-NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr);
+NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UINT32* csum, BOOL is_tree, UINT8* buf, chunk** pc, PIRP Irp);
+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);
 
 // in pnp.c
 NTSTATUS STDCALL drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp);
@@ -636,13 +818,36 @@ NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c);
 NTSTATUS clear_free_space_cache(device_extension* Vcb);
 NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollback);
 NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback);
+NTSTATUS add_space_entry(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 offset, UINT64 size);
+void _space_list_add(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
+void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
+void _space_list_subtract(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
+void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
+
+#define space_list_add(Vcb, c, deleting, address, length, rollback) _space_list_add(Vcb, c, deleting, address, length, rollback, funcname)
+#define space_list_add2(list, list_size, address, length, rollback) _space_list_add2(list, list_size, address, length, rollback, funcname)
+#define space_list_subtract(Vcb, c, deleting, address, length, rollback) _space_list_subtract(Vcb, c, deleting, address, length, rollback, funcname)
+#define space_list_subtract2(list, list_size, address, length, rollback) _space_list_subtract2(list, list_size, address, length, rollback, funcname)
 
 // in extent-tree.c
-NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback);
-NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* changed_sector_list, LIST_ENTRY* rollback);
+NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback);
+NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback);
+NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, LIST_ENTRY* rollback);
+NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, LIST_ENTRY* rollback);
 void decrease_chunk_usage(chunk* c, UINT64 delta);
-NTSTATUS convert_shared_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback);
 NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback);
+UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset);
+
+// in worker-thread.c
+void STDCALL worker_thread(void* context);
+void do_read_job(PIRP Irp);
+void do_write_job(device_extension* Vcb, PIRP Irp);
+
+// in registry.c
+void STDCALL read_registry(PUNICODE_STRING regpath);
+NTSTATUS registry_mark_volume_mounted(BTRFS_UUID* uuid);
+NTSTATUS registry_mark_volume_unmounted(BTRFS_UUID* uuid);
+NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options);
 
 #define fast_io_possible(fcb) (!FsRtlAreThereCurrentFileLocks(&fcb->lock) && !fcb->Vcb->readonly ? FastIoIsPossible : FastIoIsQuestionable)
 
@@ -669,6 +874,22 @@ static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY*
         head->Blink = item;
 }
 
+#ifdef DEBUG_FCB_REFCOUNTS
+#ifdef DEBUG_LONG_MESSAGES
+#define increase_fileref_refcount(fileref) {\
+    LONG rc = InterlockedIncrement(&fileref->refcount);\
+    MSG(funcname, __FILE__, __LINE__, "fileref %p: refcount now %i\n", 1, fileref, rc);\
+}
+#else
+#define increase_fileref_refcount(fileref) {\
+    LONG rc = InterlockedIncrement(&fileref->refcount);\
+    MSG(funcname, "fileref %p: refcount now %i\n", 1, fileref, rc);\
+}
+#endif
+#else
+#define increase_fileref_refcount(fileref) InterlockedIncrement(&fileref->refcount)
+#endif
+
 #ifdef _MSC_VER
 // #define int3 __asm { int 3 }
 #define int3 __debugbreak()
@@ -676,34 +897,11 @@ static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY*
 #define int3 asm("int3;")
 #endif
 
-#define acquire_tree_lock(Vcb, exclusive) {\
-    LONG ref = InterlockedIncrement(&Vcb->tree_lock_counter); \
-    ref = ref; \
-    if (exclusive) { \
-        TRACE("getting tree_lock (exclusive) %u->%u\n", ref-1, ref); \
-        ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE); \
-        TRACE("open tree count = %i\n", Vcb->open_trees); \
-    } else { \
-        TRACE("getting tree_lock %u->%u\n", ref-1, ref); \
-        ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); \
-    } \
-} 
-
 // 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
 
-#define release_tree_lock(Vcb, exclusive) {\
-    LONG ref = InterlockedDecrement(&Vcb->tree_lock_counter); \
-    ref = ref; \
-    TRACE("releasing tree_lock %u->%u\n", ref+1, ref); \
-    if (exclusive) {\
-        TRACE("open tree count = %i\n", Vcb->open_trees); \
-    } \
-    ExReleaseResourceLite(&Vcb->tree_lock); \
-}
-
 // from sys/stat.h
 #define __S_IFMT        0170000 /* These bits determine file type.  */
 #define __S_IFDIR       0040000 /* Directory.  */
@@ -773,6 +971,7 @@ NTSTATUS NTAPI FsRtlRemoveDotsFromPath(PWSTR OriginalString,
                                        USHORT PathLength, USHORT *NewLength);
 NTSTATUS NTAPI FsRtlValidateReparsePointBuffer(ULONG BufferLength,
                                                PREPARSE_DATA_BUFFER ReparseBuffer);
-#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7) */
+ULONG NTAPI KeQueryActiveProcessorCount(PKAFFINITY ActiveProcessors);
+#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_VISTA) */
 
 #endif
index e428fef..39b6cf2 100644 (file)
 #include <sys/stat.h>
 #endif /* __REACTOS__ */
 #include "btrfs_drv.h"
+#ifndef __REACTOS__
+#include <winioctl.h>
+#endif
 
 extern PDEVICE_OBJECT devobj;
 
-BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, root* r,
-                                         UINT64 parinode, root** subvol, UINT64* inode, UINT8* type, PANSI_STRING utf8) {
-    DIR_ITEM* di;
+static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
+    KEY searchkey;
+    traverse_ptr tp;
+    NTSTATUS Status;
+    UINT64 index;
+    
+    searchkey.obj_id = inode;
+    searchkey.obj_type = TYPE_INODE_REF;
+    searchkey.offset = parinode;
+    
+    Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("error - find_item returned %08x\n", Status);
+        return Status;
+    }
+    
+    if (!keycmp(&tp.item->key, &searchkey)) {
+        INODE_REF* ir;
+        ULONG len;
+        
+        index = 0;
+        
+        ir = (INODE_REF*)tp.item->data;
+        len = tp.item->size;
+        
+        do {
+            ULONG itemlen;
+            
+            if (len < sizeof(INODE_REF) || len < sizeof(INODE_REF) - 1 + ir->n) {
+                ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+                break;
+            }
+            
+            itemlen = sizeof(INODE_REF) - sizeof(char) + ir->n;
+            
+            if (ir->n == utf8->Length && RtlCompareMemory(ir->name, utf8->Buffer, ir->n) == ir->n) {
+                index = ir->index;
+                break;
+            }
+            
+            if (len > itemlen) {
+                len -= itemlen;
+                ir = (INODE_REF*)&ir->name[ir->n];
+            } else
+                break;
+        } while (len > 0);
+        
+        if (index == 0)
+            return STATUS_NOT_FOUND;
+        
+        *pindex = index;
+        
+        return STATUS_SUCCESS;
+    } else
+        return STATUS_NOT_FOUND;
+}
+
+static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
+    KEY searchkey;
+    traverse_ptr tp;
+    NTSTATUS Status;
+    UINT64 index;
+    
+    searchkey.obj_id = inode;
+    searchkey.obj_type = TYPE_INODE_EXTREF;
+    searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
+    
+    Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("error - find_item returned %08x\n", Status);
+        return Status;
+    }
+    
+    if (!keycmp(&tp.item->key, &searchkey)) {
+        INODE_EXTREF* ier;
+        ULONG len;
+        
+        index = 0;
+        
+        ier = (INODE_EXTREF*)tp.item->data;
+        len = tp.item->size;
+        
+        do {
+            ULONG itemlen;
+            
+            if (len < sizeof(INODE_EXTREF) || len < sizeof(INODE_EXTREF) - 1 + ier->n) {
+                ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+                break;
+            }
+            
+            itemlen = sizeof(INODE_EXTREF) - sizeof(char) + ier->n;
+            
+            if (ier->n == utf8->Length && RtlCompareMemory(ier->name, utf8->Buffer, ier->n) == ier->n) {
+                index = ier->index;
+                break;
+            }
+            
+            if (len > itemlen) {
+                len -= itemlen;
+                ier = (INODE_EXTREF*)&ier->name[ier->n];
+            } else
+                break;
+        } while (len > 0);
+        
+        if (index == 0)
+            return STATUS_NOT_FOUND;
+        
+        *pindex = index;
+        
+        return STATUS_SUCCESS;
+    } else
+        return STATUS_NOT_FOUND;
+}
+
+static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 subvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
+    KEY searchkey;
+    traverse_ptr tp;
+    NTSTATUS Status;
+    ROOT_REF* rr;
+    
+    searchkey.obj_id = r->id;
+    searchkey.obj_type = TYPE_ROOT_REF;
+    searchkey.offset = subvolid;
+    
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("error - find_item returned %08x\n", Status);
+        return Status;
+    }
+    
+    if (keycmp(&tp.item->key, &searchkey)) {
+        ERR("couldn't find (%llx,%x,%llx) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    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 %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;
+    }
+    
+    if (rr->dir == parinode && rr->n == utf8->Length && RtlCompareMemory(utf8->Buffer, rr->name, rr->n) == rr->n) {
+        *pindex = rr->index;
+        return STATUS_SUCCESS;
+    } else
+        return STATUS_NOT_FOUND;
+}
+
+static NTSTATUS load_index_list(fcb* fcb) {
     KEY searchkey;
-    traverse_ptr tp, tp2, next_tp;
+    traverse_ptr tp, next_tp;
+    NTSTATUS Status;
     BOOL b;
+    
+    searchkey.obj_id = fcb->inode;
+    searchkey.obj_type = TYPE_DIR_INDEX;
+    searchkey.offset = 2;
+    
+    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("error - find_item returned %08x\n", Status);
+        return Status;
+    }
+
+    if (keycmp(&tp.item->key, &searchkey) == -1) {
+        if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE)) {
+            tp = next_tp;
+            
+            TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+        }
+    }
+    
+    if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_DIR_INDEX) {
+        Status = STATUS_SUCCESS;
+        goto end;
+    }
+    
+    do {
+        DIR_ITEM* di;
+        
+        TRACE("key: %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+        di = (DIR_ITEM*)tp.item->data;
+        
+        if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) {
+            WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+        } else {
+            index_entry* ie;
+            ULONG stringlen;
+            UNICODE_STRING us;
+            LIST_ENTRY* le;
+            BOOL inserted;
+            
+            ie = ExAllocatePoolWithTag(PagedPool, sizeof(index_entry), ALLOC_TAG);
+            if (!ie) {
+                ERR("out of memory\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto end;
+            }
+            
+            ie->utf8.Length = ie->utf8.MaximumLength = di->n;
+            
+            if (di->n > 0) {
+                ie->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, ie->utf8.MaximumLength, ALLOC_TAG);
+                if (!ie->utf8.Buffer) {
+                    ERR("out of memory\n");
+                    ExFreePool(ie);
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                    goto end;
+                }
+                
+                RtlCopyMemory(ie->utf8.Buffer, di->name, di->n);
+            } else
+                ie->utf8.Buffer = NULL;
+            
+            Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
+            if (!NT_SUCCESS(Status)) {
+                ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+                if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
+                ExFreePool(ie);
+                goto nextitem;
+            }
+            
+            if (stringlen == 0) {
+                ERR("UTF8 length was 0\n");
+                if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
+                ExFreePool(ie);
+                goto nextitem;
+            }
+            
+            us.Length = us.MaximumLength = stringlen;
+            us.Buffer = ExAllocatePoolWithTag(PagedPool, us.MaximumLength, ALLOC_TAG);
+            
+            if (!us.Buffer) {
+                ERR("out of memory\n");
+                if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
+                ExFreePool(ie);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+            
+            Status = RtlUTF8ToUnicodeN(us.Buffer, stringlen, &stringlen, di->name, di->n);
+            if (!NT_SUCCESS(Status)) {
+                ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+                ExFreePool(us.Buffer);
+                if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
+                ExFreePool(ie);
+                goto nextitem;
+            }
+            
+            Status = RtlUpcaseUnicodeString(&ie->filepart_uc, &us, TRUE);
+            if (!NT_SUCCESS(Status)) {
+                ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+                ExFreePool(us.Buffer);
+                if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
+                ExFreePool(ie);
+                goto nextitem;
+            }
+            
+            ie->key = di->key;
+            ie->type = di->type;
+            ie->index = tp.item->key.offset;
+            
+            ie->hash = calc_crc32c(0xfffffffe, (UINT8*)ie->filepart_uc.Buffer, (ULONG)ie->filepart_uc.Length);
+            inserted = FALSE;
+            
+            le = fcb->index_list.Flink;
+            while (le != &fcb->index_list) {
+                index_entry* ie2 = CONTAINING_RECORD(le, index_entry, list_entry);
+                
+                if (ie2->hash >= ie->hash) {
+                    InsertHeadList(le->Blink, &ie->list_entry);
+                    inserted = TRUE;
+                    break;
+                }
+                
+                le = le->Flink;
+            }
+            
+            if (!inserted)
+                InsertTailList(&fcb->index_list, &ie->list_entry);
+        }
+        
+nextitem:
+        b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+         
+        if (b) {
+            tp = next_tp;
+            
+            b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_DIR_INDEX;
+        }
+    } while (b);
+    
+    Status = STATUS_SUCCESS;
+    
+end:
+    if (!NT_SUCCESS(Status)) {
+        while (!IsListEmpty(&fcb->index_list)) {
+            LIST_ENTRY* le = RemoveHeadList(&fcb->index_list);
+            index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry);
+
+            if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
+            if (ie->filepart_uc.Buffer) ExFreePool(ie->filepart_uc.Buffer);
+            ExFreePool(ie);
+        }
+    } else
+        mark_fcb_dirty(fcb);
+    
+    return Status;
+}
+
+static NTSTATUS STDCALL find_file_in_dir_index(file_ref* fr, PUNICODE_STRING filename, root** subvol, UINT64* inode, UINT8* type,
+                                               UINT64* pindex, PANSI_STRING utf8) {
+    LIST_ENTRY* le;
+    NTSTATUS Status;
+    UNICODE_STRING us;
+    UINT32 hash;
+        
+    Status = RtlUpcaseUnicodeString(&us, filename, TRUE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+        return Status;
+    }
+    
+    hash = calc_crc32c(0xfffffffe, (UINT8*)us.Buffer, (ULONG)us.Length);
+    
+    ExAcquireResourceExclusiveLite(&fr->fcb->nonpaged->index_lock, TRUE);
+    
+    if (!fr->fcb->index_loaded) {
+        Status = load_index_list(fr->fcb);
+        if (!NT_SUCCESS(Status)) {
+            ERR("load_index_list returned %08x\n", Status);
+            goto end;
+        }
+        
+        fr->fcb->index_loaded = TRUE;
+    }
+    
+    ExConvertExclusiveToSharedLite(&fr->fcb->nonpaged->index_lock);
+    
+    le = fr->fcb->index_list.Flink;
+    while (le != &fr->fcb->index_list) {
+        index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry);
+        
+        if (ie->hash == hash && ie->filepart_uc.Length == us.Length && RtlCompareMemory(ie->filepart_uc.Buffer, us.Buffer, us.Length) == us.Length) {
+            LIST_ENTRY* le;
+            BOOL ignore_entry = FALSE;
+            
+            ExAcquireResourceSharedLite(&fr->nonpaged->children_lock, TRUE);
+
+            le = fr->children.Flink;
+            while (le != &fr->children) {
+                file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
+                
+                if (fr2->index == ie->index) {
+                    if (fr2->deleted || fr2->filepart_uc.Length != us.Length ||
+                        RtlCompareMemory(fr2->filepart_uc.Buffer, us.Buffer, us.Length) != us.Length) {
+                        ignore_entry = TRUE;
+                        break;
+                    }
+                    break;
+                } else if (fr2->index > ie->index)
+                    break;
+                
+                le = le->Flink;
+            }
+            
+            ExReleaseResourceLite(&fr->nonpaged->children_lock);
+            
+            if (ignore_entry)
+                goto nextitem;
+            
+            if (ie->key.obj_type == TYPE_ROOT_ITEM) {
+                if (subvol) {
+                    *subvol = NULL;
+                    
+                    le = fr->fcb->Vcb->roots.Flink;
+                    while (le != &fr->fcb->Vcb->roots) {
+                        root* r2 = CONTAINING_RECORD(le, root, list_entry);
+                        
+                        if (r2->id == ie->key.obj_id) {
+                            *subvol = r2;
+                            break;
+                        }
+                        
+                        le = le->Flink;
+                    }
+                }
+                
+                if (inode)
+                    *inode = SUBVOL_ROOT_INODE;
+                
+                if (type)
+                    *type = BTRFS_TYPE_DIRECTORY;
+            } else {
+                if (subvol)
+                    *subvol = fr->fcb->subvol;
+                
+                if (inode)
+                    *inode = ie->key.obj_id;
+                
+                if (type)
+                    *type = ie->type;
+            }
+            
+            if (utf8) {
+                utf8->MaximumLength = utf8->Length = ie->utf8.Length;
+                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, ie->utf8.Buffer, ie->utf8.Length);
+            }
+            
+            if (pindex)
+                *pindex = ie->index;
+            
+            Status = STATUS_SUCCESS;
+            goto end;
+        } else if (ie->hash > hash) {
+            Status = STATUS_OBJECT_NAME_NOT_FOUND;
+            goto end;
+        }
+        
+nextitem:
+        le = le->Flink;
+    }
+    
+    Status = STATUS_OBJECT_NAME_NOT_FOUND;
+    
+end:
+    ExReleaseResourceLite(&fr->fcb->nonpaged->index_lock);
+    
+    ExFreePool(us.Buffer);
+    
+    return Status;
+}
+
+NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr,
+                                             root** subvol, UINT64* inode, UINT8* type, UINT64* pindex, PANSI_STRING utf8) {
+    DIR_ITEM* di;
+    KEY searchkey;
+    traverse_ptr tp;
     NTSTATUS Status;
     ULONG stringlen;
     
-    TRACE("(%p, %.*S, %08x, %p, %llx, %p, %p, %p)\n", Vcb, filename->Length / sizeof(WCHAR), filename->Buffer, crc32, r, parinode, subvol, inode, type);
+    TRACE("(%p, %.*S, %08x, (%llx, %llx), %p, %p, %p)\n", Vcb, filename->Length / sizeof(WCHAR), filename->Buffer, crc32,
+                                                          fr->fcb->subvol->id, fr->fcb->inode, subvol, inode, type);
     
-    searchkey.obj_id = parinode;
+    searchkey.obj_id = fr->fcb->inode;
     searchkey.obj_type = TYPE_DIR_ITEM;
     searchkey.offset = crc32;
     
-    Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, fr->fcb->subvol, &tp, &searchkey, FALSE);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
-        return FALSE;
+        return Status;
     }
     
     TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
@@ -51,7 +501,8 @@ BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING
         // found by hash
         
         if (tp.item->size < sizeof(DIR_ITEM)) {
-            WARN("(%llx;%llx,%x,%llx) was %u bytes, expected at least %u\n", r->id, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
+            WARN("(%llx;%llx,%x,%llx) was %u bytes, expected at least %u\n", fr->fcb->subvol->id, tp.item->key.obj_id, tp.item->key.obj_type,
+                                                                             tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
         } else {
             di = (DIR_ITEM*)tp.item->data;
             
@@ -74,7 +525,7 @@ BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING
                     
                     if (!utf16) {
                         ERR("out of memory\n");
-                        return FALSE;
+                        return STATUS_INSUFFICIENT_RESOURCES;
                     }
                     
                     Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n);
@@ -86,6 +537,8 @@ BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING
                         us.Length = us.MaximumLength = (USHORT)stringlen;
                         
                         if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) {
+                            UINT64 index;
+                            
                             if (di->key.obj_type == TYPE_ROOT_ITEM) {
                                 LIST_ENTRY* le = Vcb->roots.Flink;
                                 
@@ -111,7 +564,7 @@ BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING
                                     *type = BTRFS_TYPE_DIRECTORY;
                             } else {
                                 if (subvol)
-                                    *subvol = r;
+                                    *subvol = fr->fcb->subvol;
                                 
                                 if (inode)
                                     *inode = di->key.obj_id;
@@ -127,7 +580,7 @@ BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING
                                 if (!utf8->Buffer) {
                                     ERR("out of memory\n");
                                     ExFreePool(utf16);
-                                    return FALSE;
+                                    return STATUS_INSUFFICIENT_RESOURCES;
                                 }
                                 
                                 RtlCopyMemory(utf8->Buffer, di->name, di->n);
@@ -135,145 +588,76 @@ BOOL STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING
                             
                             ExFreePool(utf16);
                             
-//                             TRACE("found %.*S by hash at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
+                            index = 0;
+                                
+                            if (fr->fcb->subvol != Vcb->root_root) {
+                                if (di->key.obj_type == TYPE_ROOT_ITEM) {
+                                    Status = find_subvol_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
+                                    if (!NT_SUCCESS(Status)) {
+                                        ERR("find_subvol_dir_index returned %08x\n", Status);
+                                        return Status;
+                                    }
+                                } else {
+                                    Status = find_file_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
+                                    if (!NT_SUCCESS(Status)) {
+                                        if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
+                                            Status = find_file_dir_index_extref(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
+                                            
+                                            if (!NT_SUCCESS(Status)) {
+                                                ERR("find_file_dir_index_extref returned %08x\n", Status);
+                                                return Status;
+                                            }
+                                        } else {
+                                            ERR("find_file_dir_index returned %08x\n", Status);
+                                            return Status;
+                                        }
+                                    }
+                                }
+                            }
                             
-                            return TRUE;
-                        }
-                    }
-                    
-                    ExFreePool(utf16);
-                }
-                
-                di = (DIR_ITEM*)&di->name[di->n + di->m];
-            }
-        }
-    }
-    
-    searchkey.obj_id = parinode;
-    searchkey.obj_type = TYPE_DIR_INDEX;
-    searchkey.offset = 2;
-    
-    Status = find_item(Vcb, r, &tp2, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return FALSE;
-    }
-    
-    tp = tp2;
-    
-    TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-    
-    if (keycmp(&tp.item->key, &searchkey) == -1) {
-        if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
-            tp = next_tp;
-            
-            TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-        }
-    }
-    
-    if (tp.item->key.obj_id != parinode || tp.item->key.obj_type != TYPE_DIR_INDEX)
-        return FALSE;
-    
-    b = TRUE;
-    do {
-        TRACE("key: %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-        di = (DIR_ITEM*)tp.item->data;
-        
-        if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) {
-            WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-        } else {    
-            TRACE("%.*s\n", di->n, di->name);
-            
-            Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
-            if (!NT_SUCCESS(Status)) {
-                ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-            } else {
-                WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
-                UNICODE_STRING us;
-                
-                if (!utf16) {
-                    ERR("out of memory\n");
-                    return FALSE;
-                }
-                
-                Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n);
-
-                if (!NT_SUCCESS(Status)) {
-                    ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                } else {
-                    us.Buffer = utf16;
-                    us.Length = us.MaximumLength = (USHORT)stringlen;
-                    
-                    if (FsRtlAreNamesEqual(filename, &us, TRUE, NULL)) {
-                        if (di->key.obj_type == TYPE_ROOT_ITEM) {
-                            LIST_ENTRY* le = Vcb->roots.Flink;
-
-                            if (subvol) {
-                                *subvol = NULL;
+                            if (index != 0) {
+                                LIST_ENTRY* le = fr->children.Flink;
                                 
-                                while (le != &Vcb->roots) {
-                                    root* r2 = CONTAINING_RECORD(le, root, list_entry);
+                                while (le != &fr->children) {
+                                    file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
                                     
-                                    if (r2->id == di->key.obj_id) {
-                                        *subvol = r2;
+                                    if (fr2->index == index) {
+                                        if (fr2->deleted || !FsRtlAreNamesEqual(&fr2->filepart, filename, TRUE, NULL)) {
+                                            goto byindex;
+                                        }
+                                        break;
+                                    } else if (fr2->index > index)
                                         break;
-                                    }
                                     
                                     le = le->Flink;
                                 }
                             }
                             
-                            if (inode)
-                                *inode = SUBVOL_ROOT_INODE;
-                            
-                            if (type)
-                                *type = BTRFS_TYPE_DIRECTORY;
-                        } else {
-                            if (subvol)
-                                *subvol = r;
-                            
-                            if (inode)
-                                *inode = di->key.obj_id;
-                            
-                            if (type)
-                                *type = di->type;
-                        }
-//                         TRACE("found %.*S at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
-                        
-                        if (utf8) {
-                            utf8->MaximumLength = di->n;
-                            utf8->Length = utf8->MaximumLength;
-                            utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
-                            if (!utf8->Buffer) {
-                                ERR("out of memory\n");
-                                ExFreePool(utf16);
-                                
-                                return FALSE;
-                            }
+//                             TRACE("found %.*S by hash at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
+
+                            if (pindex)
+                                *pindex = index;
                             
-                            RtlCopyMemory(utf8->Buffer, di->name, di->n);
+                            return STATUS_SUCCESS;
                         }
-                        
-                        ExFreePool(utf16);
-                        
-                        return TRUE;
                     }
+                    
+                    ExFreePool(utf16);
                 }
                 
-                ExFreePool(utf16);
+                di = (DIR_ITEM*)&di->name[di->n + di->m];
             }
         }
-        
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
-         
-        if (b) {
-            tp = next_tp;
-            
-            b = tp.item->key.obj_id == parinode && tp.item->key.obj_type == TYPE_DIR_INDEX;
-        }
-    } while (b);
+    }
     
-    return FALSE;
+byindex:
+    Status = find_file_in_dir_index(fr, filename, subvol, inode, type, pindex, utf8);
+    if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+        ERR("find_file_in_dir_index returned %08x\n", Status);
+        return Status;
+    }
+    
+    return Status;
 }
 
 fcb* create_fcb() {
@@ -315,8 +699,14 @@ fcb* create_fcb() {
     ExInitializeResourceLite(&fcb->nonpaged->resource);
     fcb->Header.Resource = &fcb->nonpaged->resource;
     
+    ExInitializeResourceLite(&fcb->nonpaged->index_lock);
+    
     FsRtlInitializeFileLock(&fcb->lock, NULL, NULL);
     
+    InitializeListHead(&fcb->extents);
+    InitializeListHead(&fcb->index_list);
+    InitializeListHead(&fcb->hardlinks);
+    
     return fcb;
 }
 
@@ -331,42 +721,50 @@ file_ref* create_fileref() {
     
     RtlZeroMemory(fr, sizeof(file_ref));
     
+    fr->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(file_ref_nonpaged), ALLOC_TAG);
+    if (!fr->nonpaged) {
+        ERR("out of memory\n");
+        ExFreePool(fr);
+        return NULL;
+    }
+    
     fr->refcount = 1;
     
 #ifdef DEBUG_FCB_REFCOUNTS
-    WARN("fileref %p: refcount now %i\n", fr, fr->refcount);
+    WARN("fileref %p: refcount now 1\n", fr);
 #endif
     
     InitializeListHead(&fr->children);
     
+    ExInitializeResourceLite(&fr->nonpaged->children_lock);
+    
     return fr;
 }
 
-static BOOL STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, root* r,
-                                     UINT64 parinode, root** subvol, UINT64* inode, UINT8* type, PANSI_STRING utf8) {
+NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, file_ref* fr,
+                                  root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8) {
     char* fn;
     UINT32 crc32;
-    BOOL ret;
     ULONG utf8len;
     NTSTATUS Status;
     
     Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, filename->Buffer, filename->Length);
     if (!NT_SUCCESS(Status)) {
         ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
-        return FALSE;
+        return Status;
     }
     
     fn = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG);
     if (!fn) {
         ERR("out of memory\n");
-        return FALSE;
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
     
     Status = RtlUnicodeToUTF8N(fn, utf8len, &utf8len, filename->Buffer, filename->Length);
     if (!NT_SUCCESS(Status)) {
         ExFreePool(fn);
         ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
-        return FALSE;
+        return Status;
     }
     
     TRACE("%.*s\n", utf8len, fn);
@@ -374,12 +772,10 @@ static BOOL STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING file
     crc32 = calc_crc32c(0xfffffffe, (UINT8*)fn, (ULONG)utf8len);
     TRACE("crc32c(%.*s) = %08x\n", utf8len, fn, crc32);
     
-    ret = find_file_in_dir_with_crc32(Vcb, filename, crc32, r, parinode, subvol, inode, type, utf8);
-    
-    return ret;
+    return find_file_in_dir_with_crc32(Vcb, filename, crc32, fr, subvol, inode, type, index, utf8);
 }
 
-static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* size, UINT32* hash, PANSI_STRING xattr) {
+static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* hash, PANSI_STRING xattr) {
     NTSTATUS Status;
     ULONG utf8len;
     char* utf8;
@@ -451,7 +847,6 @@ static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream,
                 if (RtlCompareMemory(di->name, utf8, utf8len) == utf8len) {
                     TRACE("found exact match for %s\n", utf8);
                     
-                    *size = di->m;
                     *hash = tp.item->key.offset;
                     
                     xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
@@ -532,7 +927,6 @@ static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream,
                                 TRACE("found case-insensitive match for %s\n", utf8);
                                 
                                 *newstreamname = us;
-                                *size = di->m;
                                 *hash = tp.item->key.offset;
                                 
                                 xattr->Buffer = ExAllocatePoolWithTag(PagedPool, di->n + 1, ALLOC_TAG);
@@ -695,15 +1089,24 @@ static NTSTATUS split_path(PUNICODE_STRING path, UNICODE_STRING** parts, ULONG*
 static file_ref* search_fileref_children(file_ref* dir, PUNICODE_STRING name) {
     LIST_ENTRY* le;
     file_ref *c, *deleted = NULL;
+    NTSTATUS Status;
+    UNICODE_STRING ucus;
 #ifdef DEBUG_FCB_REFCOUNTS
     ULONG rc;
 #endif
+
+    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 && FsRtlAreNamesEqual(&c->filepart, name, TRUE, NULL)) {
+        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 {
@@ -713,6 +1116,8 @@ static file_ref* search_fileref_children(file_ref* dir, PUNICODE_STRING name) {
 #else
                 InterlockedIncrement(&c->refcount);
 #endif
+                ExFreePool(ucus.Buffer);
+                
                 return c;
             }
         }
@@ -720,23 +1125,56 @@ static file_ref* search_fileref_children(file_ref* dir, PUNICODE_STRING name) {
         le = le->Flink;
     }
     
-    if (deleted) {
-#ifdef DEBUG_FCB_REFCOUNTS
-        rc = InterlockedIncrement(&deleted->refcount);
-        WARN("fileref %p: refcount now %i (%S)\n", deleted, rc, file_desc_fileref(deleted));
-#else
-        InterlockedIncrement(&deleted->refcount);
-#endif
-    }
+    if (deleted)
+        increase_fileref_refcount(deleted);
+    
+    ExFreePool(ucus.Buffer);
     
     return deleted;
 }
 
-static NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb) {
+static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size) {
+    KEY searchkey;
+    traverse_ptr tp;
+    NTSTATUS Status;
+    EXTENT_ITEM* ei;
+    
+    searchkey.obj_id = address;
+    searchkey.obj_type = TYPE_EXTENT_ITEM;
+    searchkey.offset = size;
+    
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("error - find_item returned %08x\n", Status);
+        return 0;
+    }
+    
+    if (keycmp(&searchkey, &tp.item->key)) {
+        ERR("couldn't find (%llx,%x,%llx) in extent tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+        return 0;
+    }
+    
+    if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
+        EXTENT_ITEM_V0* eiv0 = (EXTENT_ITEM_V0*)tp.item->data;
+        
+        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));
+        return 0;
+    }
+    
+    ei = (EXTENT_ITEM*)tp.item->data;
+    
+    return ei->refcount;
+}
+
+NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
     fcb* fcb;
+    BOOL b;
     
     if (!IsListEmpty(&subvol->fcbs)) {
         LIST_ENTRY* le = subvol->fcbs.Flink;
@@ -785,19 +1223,47 @@ static NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT
     }
     
     if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
-        ERR("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", inode, subvol->id);
+        WARN("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", inode, subvol->id);
         free_fcb(fcb);
-        return STATUS_INTERNAL_ERROR;
+        return STATUS_INVALID_PARAMETER;
     }
     
     if (tp.item->size > 0)
         RtlCopyMemory(&fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
     
-    fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8->Buffer[0] == '.', FALSE);
+    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)
+            fcb->type = BTRFS_TYPE_DIRECTORY;
+        else if (fcb->inode_item.st_mode & __S_IFCHR)
+            fcb->type = BTRFS_TYPE_CHARDEV;
+        else if (fcb->inode_item.st_mode & __S_IFBLK)
+            fcb->type = BTRFS_TYPE_BLOCKDEV;
+        else if (fcb->inode_item.st_mode & __S_IFIFO)
+            fcb->type = BTRFS_TYPE_FIFO;
+        else if (fcb->inode_item.st_mode & __S_IFLNK)
+            fcb->type = BTRFS_TYPE_SYMLINK;
+        else if (fcb->inode_item.st_mode & __S_IFSOCK)
+            fcb->type = BTRFS_TYPE_SOCKET;
+        else
+            fcb->type = BTRFS_TYPE_FILE;
+    }
+    
+    fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', FALSE);
     
     fcb_get_sd(fcb, parent);
     
+    if (fcb->type == BTRFS_TYPE_DIRECTORY) {
+        UINT8* xattrdata;
+        UINT16 xattrlen;
+        
+        if (get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &xattrdata, &xattrlen)) {
+            fcb->reparse_xattr.Buffer = (char*)xattrdata;
+            fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = xattrlen;
+        }
+    }
+    
     InsertTailList(&subvol->fcbs, &fcb->list_entry);
+    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
     
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
     
@@ -806,11 +1272,12 @@ static NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT
         fcb->Header.FileSize.QuadPart = 0;
         fcb->Header.ValidDataLength.QuadPart = 0;
     } else {
-        EXTENT_DATA* ed;
+        EXTENT_DATA* ed = NULL;
+        traverse_ptr next_tp;
         
         searchkey.obj_id = fcb->inode;
         searchkey.obj_type = TYPE_EXTENT_DATA;
-        searchkey.offset = 0xffffffffffffffff;
+        searchkey.offset = 0;
         
         Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
         if (!NT_SUCCESS(Status)) {
@@ -819,38 +1286,248 @@ static NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT
             return Status;
         }
         
-        if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
-            ERR("error - could not find EXTENT_DATA items for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
-            free_fcb(fcb);
-            return STATUS_INTERNAL_ERROR;
-        }
+        do {
+            if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+                extent* ext;
+                BOOL unique = FALSE;
+                
+                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,
+                        tp.item->size, sizeof(EXTENT_DATA));
+                    
+                    free_fcb(fcb);
+                    return STATUS_INTERNAL_ERROR;
+                }
+                
+                if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+                    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,
+                            tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
+                    
+                        free_fcb(fcb);
+                        return STATUS_INTERNAL_ERROR;
+                    }
+                    
+                    if (ed2->size != 0)
+                        unique = get_extent_refcount(fcb->Vcb, ed2->address, ed2->size) == 1;
+                }
+                
+                ext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
+                if (!ext) {
+                    ERR("out of memory\n");
+                    free_fcb(fcb);
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+                
+                ext->data = ExAllocatePoolWithTag(PagedPool, 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);
+                ext->datalen = tp.item->size;
+                ext->unique = unique;
+                ext->ignore = FALSE;
+                
+                InsertTailList(&fcb->extents, &ext->list_entry);
+            }
+            
+            b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+         
+            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 (ed && ed->type == EXTENT_TYPE_INLINE)
+            fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
+        else
+            fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
+        
+        fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
+        fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
+    }
+    
+    // FIXME - only do if st_nlink > 1?
+    
+    searchkey.obj_id = inode;
+    searchkey.obj_type = TYPE_INODE_REF;
+    searchkey.offset = 0;
+    
+    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("error - find_item returned %08x\n", Status);
+        free_fcb(fcb);
+        return Status;
+    }
+    
+    do {
+        traverse_ptr next_tp;
+        
+        if (tp.item->key.obj_id == searchkey.obj_id) {
+            if (tp.item->key.obj_type == TYPE_INODE_REF) {
+                ULONG len;
+                INODE_REF* ir;
+                
+                len = tp.item->size;
+                ir = (INODE_REF*)tp.item->data;
+                
+                while (len >= sizeof(INODE_REF) - 1) {
+                    hardlink* hl;
+                    ULONG stringlen;
+                    
+                    hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
+                    if (!hl) {
+                        ERR("out of memory\n");
+                        free_fcb(fcb);
+                        return STATUS_INSUFFICIENT_RESOURCES;
+                    }
+                    
+                    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;
+                    
+                    if (stringlen == 0)
+                        hl->name.Buffer = NULL;
+                    else {
+                        hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
+                        
+                        if (!hl->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);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+                            ExFreePool(hl->name.Buffer);
+                            ExFreePool(hl);
+                            free_fcb(fcb);
+                            return Status;
+                        }
+                    }
+                    
+                    InsertTailList(&fcb->hardlinks, &hl->list_entry);
+                    
+                    len -= sizeof(INODE_REF) - 1 + ir->n;
+                    ir = (INODE_REF*)&ir->name[ir->n];
+                }
+            } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
+                ULONG len;
+                INODE_EXTREF* ier;
+                
+                len = tp.item->size;
+                ier = (INODE_EXTREF*)tp.item->data;
+                
+                while (len >= sizeof(INODE_EXTREF) - 1) {
+                    hardlink* hl;
+                    ULONG stringlen;
+                    
+                    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;
+                    
+                    if (stringlen == 0)
+                        hl->name.Buffer = NULL;
+                    else {
+                        hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG);
+                        
+                        if (!hl->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);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+                            ExFreePool(hl->name.Buffer);
+                            ExFreePool(hl);
+                            free_fcb(fcb);
+                            return Status;
+                        }
+                    }
+                    
+                    InsertTailList(&fcb->hardlinks, &hl->list_entry);
+                    
+                    len -= sizeof(INODE_EXTREF) - 1 + ier->n;
+                    ier = (INODE_EXTREF*)&ier->name[ier->n];
+                }
+            }
+        }
+        
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
         
-        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,
-                tp.item->size, sizeof(EXTENT_DATA));
+        if (b) {
+            tp = next_tp;
             
-            free_fcb(fcb);
-            return STATUS_INTERNAL_ERROR;
+            if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > TYPE_INODE_EXTREF))
+                break;
         }
-        
-        ed = (EXTENT_DATA*)tp.item->data;
-        
-        if (ed->type == EXTENT_TYPE_INLINE)
-            fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
-        else
-            fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
-        
-        fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
-        fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
-    }
-    
+    } while (b);
+
     *pfcb = fcb;
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr,
-                                UINT32 streamsize, UINT32 streamhash, fcb* parent, fcb** pfcb) {
+NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr,
+                         UINT32 streamhash, fcb* parent, fcb** pfcb) {
     fcb* fcb;
+    UINT8* xattrdata;
+    UINT16 xattrlen;
     
     if (!IsListEmpty(&subvol->fcbs)) {
         LIST_ENTRY* le = subvol->fcbs.Flink;
@@ -881,6 +1558,12 @@ static NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inod
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
     }
+      
+    if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr->Buffer, streamhash, &xattrdata, &xattrlen)) {
+        ERR("get_xattr failed\n");
+        free_fcb(fcb);
+        return STATUS_INTERNAL_ERROR;
+    }
 
     fcb->Vcb = Vcb;
     
@@ -888,24 +1571,58 @@ static NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inod
     fcb->inode = parent->inode;
     fcb->type = parent->type;
     fcb->ads = TRUE;
-    fcb->adssize = streamsize;
     fcb->adshash = streamhash;
     fcb->adsxattr = *xattr;
     
+    fcb->adsdata.Buffer = (char*)xattrdata;
+    fcb->adsdata.Length = fcb->adsdata.MaximumLength = xattrlen;
+    
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
-    fcb->Header.AllocationSize.QuadPart = fcb->adssize;
-    fcb->Header.FileSize.QuadPart = fcb->adssize;
-    fcb->Header.ValidDataLength.QuadPart = fcb->adssize;
+    fcb->Header.AllocationSize.QuadPart = xattrlen;
+    fcb->Header.FileSize.QuadPart = xattrlen;
+    fcb->Header.ValidDataLength.QuadPart = xattrlen;
     
-    TRACE("stream found: size = %x, hash = %08x\n", fcb->adssize, fcb->adshash);
+    TRACE("stream found: size = %x, hash = %08x\n", xattrlen, fcb->adshash);
     
     InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
     
     *pfcb = fcb;
     
     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);
+}
+
 NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed) {
     UNICODE_STRING fnus2;
     file_ref *dir, *sf, *sf2;
@@ -916,7 +1633,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
     
     TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, unparsed);
     
-    if (Vcb->removing)
+    if (Vcb->removing || Vcb->locked)
         return STATUS_ACCESS_DENIED;
     
     fnus2 = *fnus;
@@ -927,13 +1644,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
     }
     
     if (related && fnus->Length == 0) {
-#ifdef DEBUG_FCB_REFCOUNTS
-        LONG rc = InterlockedIncrement(&related->refcount);
-        WARN("fileref %p: refcount now %i\n", related, rc);
-#else
-        InterlockedIncrement(&related->refcount);
-#endif
-        
+        increase_fileref_refcount(related);
         
         *pfr = related;
         return STATUS_SUCCESS;
@@ -948,12 +1659,17 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
         }
         
         if (fnus2.Length == sizeof(WCHAR)) {
-#ifdef DEBUG_FCB_REFCOUNTS
-            LONG rc = InterlockedIncrement(&Vcb->root_fileref->refcount);
-            WARN("fileref %p: refcount now %i (root)\n", Vcb->root_fileref, rc);
-#else
-            InterlockedIncrement(&Vcb->root_fileref->refcount);
-#endif
+            if (Vcb->root_fileref->fcb->open_count == 0) { // don't allow root to be opened on unmounted FS
+                ULONG cc;
+                IO_STATUS_BLOCK iosb;
+                
+                Status = dev_ioctl(Vcb->devices[0].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+                
+                if (!NT_SUCCESS(Status))
+                    return Status;
+            }
+            
+            increase_fileref_refcount(Vcb->root_fileref);
             *pfr = Vcb->root_fileref;
             return STATUS_SUCCESS;
         }
@@ -982,10 +1698,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
     }
     
     sf = dir;
-    dir->refcount++;
-#ifdef DEBUG_FCB_REFCOUNTS
-    WARN("fileref %p: refcount now %i (%S)\n", dir, dir->refcount, file_desc_fileref(dir));
-#endif
+    increase_fileref_refcount(dir);
     
     if (parent) {
         num_parts--;
@@ -1002,6 +1715,8 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
         goto end2;
     }
     
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    
     for (i = 0; i < num_parts; i++) {
         BOOL lastpart = (i == num_parts-1) || (i == num_parts-2 && has_stream);
         
@@ -1019,7 +1734,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
             if (has_stream && i == num_parts - 1) {
                 UNICODE_STRING streamname;
                 ANSI_STRING xattr;
-                UINT32 streamsize, streamhash;
+                UINT32 streamhash;
                 
                 streamname.Buffer = NULL;
                 streamname.Length = streamname.MaximumLength = 0;
@@ -1028,18 +1743,14 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                 
                 // FIXME - check if already opened
                 
-                if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamsize, &streamhash, &xattr)) {
+                if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamhash, &xattr)) {
                     TRACE("could not find stream %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
                     
                     Status = STATUS_OBJECT_NAME_NOT_FOUND;
                     goto end;
                 } else {
-                    ULONG fnlen;
                     fcb* fcb;
-#ifdef DEBUG_FCB_REFCOUNTS
-                    LONG rc;
-#endif
-                    
+
                     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");
@@ -1048,7 +1759,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                         goto end;
                     }
                     
-                    Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamsize, streamhash, sf->fcb, &fcb);
+                    Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb);
                     if (!NT_SUCCESS(Status)) {
                         ERR("open_fcb_stream returned %08x\n", Status);
                         goto end;
@@ -1079,57 +1790,38 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                         RtlCopyMemory(sf2->filepart.Buffer, parts[i].Buffer, parts[i].Length);
                     }
                     
-                    sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR);
-                    
-                    if (sf != Vcb->root_fileref)
-                        sf2->name_offset++;
-                    
-                    fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length;
-                    
-                    sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
-                    if (!sf2->full_filename.Buffer) {
-                        ERR("out of memory\n");
+                    Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
                         free_fileref(sf2);
-                        Status = STATUS_INSUFFICIENT_RESOURCES;
                         goto end;
                     }
                     
-                    sf2->full_filename.Length = sf2->full_filename.MaximumLength = fnlen;
-                    RtlCopyMemory(sf2->full_filename.Buffer, sf->full_filename.Buffer, sf->full_filename.Length);
-                    
-                    sf2->full_filename.Buffer[sf->full_filename.Length / sizeof(WCHAR)] = ':';
-                    
-                    RtlCopyMemory(&sf2->full_filename.Buffer[sf2->name_offset], sf2->filepart.Buffer, sf2->filepart.Length);
-                    
                     // FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM
 
                     sf2->parent = (struct _file_ref*)sf;
-                    InsertTailList(&sf->children, &sf2->list_entry);
+                    insert_fileref_child(sf, sf2, TRUE);
                     
-#ifdef DEBUG_FCB_REFCOUNTS
-                    rc = InterlockedIncrement(&sf->refcount);
-                    WARN("fileref %p: refcount now %i\n", sf, rc);
-#else
-                    InterlockedIncrement(&sf->refcount);
-#endif
+                    increase_fileref_refcount(sf);
                 }
             } else {
                 root* subvol;
-                UINT64 inode;
+                UINT64 inode, index;
                 UINT8 type;
                 ANSI_STRING utf8;
-#ifdef DEBUG_FCB_REFCOUNTS
-                LONG rc;
-#endif
                 
-                if (!find_file_in_dir(Vcb, &parts[i], sf->fcb->subvol, sf->fcb->inode, &subvol, &inode, &type, &utf8)) {
+                Status = find_file_in_dir(Vcb, &parts[i], sf, &subvol, &inode, &type, &index, &utf8);
+                if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
                     TRACE("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
 
                     Status = lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
                     goto end;
+                } else if (!NT_SUCCESS(Status)) {
+                    ERR("find_file_in_dir returned %08x\n", Status);
+                    goto end;
                 } else {
                     fcb* fcb;
-                    ULONG strlen, fnlen;
+                    ULONG strlen;
                     
                     if (type != BTRFS_TYPE_DIRECTORY && !lastpart) {
                         WARN("passed path including file as subdirectory\n");
@@ -1154,6 +1846,10 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                     
                     sf2->fcb = fcb;
                     
+                    if (type == BTRFS_TYPE_DIRECTORY)
+                        fcb->fileref = sf2;
+                    
+                    sf2->index = index;
                     sf2->utf8 = utf8;
                     
                     Status = RtlUTF8ToUnicodeN(NULL, 0, &strlen, utf8.Buffer, utf8.Length);
@@ -1179,37 +1875,18 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                         goto end;
                     }
                     
-                    sf2->name_offset = sf->full_filename.Length / sizeof(WCHAR);
-    
-                    if (sf != Vcb->root_fileref)
-                        sf2->name_offset++;
-                    
-                    fnlen = (sf2->name_offset * sizeof(WCHAR)) + sf2->filepart.Length;
-                    
-                    sf2->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
-                    if (!sf2->full_filename.Buffer) {
-                        ERR("out of memory\n");
+                    Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
                         free_fileref(sf2);
-                        Status = STATUS_INSUFFICIENT_RESOURCES;
                         goto end;
                     }
                     
-                    sf2->full_filename.Length = sf2->full_filename.MaximumLength = fnlen;
-                    RtlCopyMemory(sf2->full_filename.Buffer, sf->full_filename.Buffer, sf->full_filename.Length);
-                    
-                    if (sf != Vcb->root_fileref)
-                        sf2->full_filename.Buffer[sf->full_filename.Length / sizeof(WCHAR)] = '\\';
+                    sf2->parent = (struct _file_ref*)sf;
                     
-                    RtlCopyMemory(&sf2->full_filename.Buffer[sf2->name_offset], sf2->filepart.Buffer, sf2->filepart.Length);
+                    insert_fileref_child(sf, sf2, TRUE);
                     
-                    sf2->parent = (struct _file_ref*)sf;
-                    InsertTailList(&sf->children, &sf2->list_entry);
-#ifdef DEBUG_FCB_REFCOUNTS
-                    rc = InterlockedIncrement(&sf->refcount);
-                    WARN("fileref %p: refcount now %i\n", sf, rc);
-#else
-                    InterlockedIncrement(&sf->refcount);
-#endif
+                    increase_fileref_refcount(sf);
                 }
             }
         }
@@ -1235,6 +1912,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
     *pfr = sf2;
     
 end:
+    ExReleaseResourceLite(&Vcb->fcb_lock);
     free_fileref(sf);
     
 end2:
@@ -1246,25 +1924,64 @@ end2:
     return Status;
 }
 
+NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index) {
+    KEY searchkey;
+    traverse_ptr tp, prev_tp;
+    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);
+    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))
+            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;
+}
+
 static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_STRING fpus, file_ref* parfileref, ULONG options, file_ref** pfr, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     fcb* fcb;
     ULONG utf8len;
     char* utf8 = NULL;
-    UINT32 crc32;
     UINT64 dirpos, inode;
-    KEY searchkey;
-    traverse_ptr tp;
-    INODE_ITEM *dirii, *ii;
     UINT8 type;
-    ULONG disize;
-    DIR_ITEM *di, *di2;
     LARGE_INTEGER time;
     BTRFS_TIME now;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    ANSI_STRING utf8as;
     ULONG defda;
     file_ref* fileref;
+    hardlink* hl;
 #ifdef DEBUG_FCB_REFCOUNTS
     LONG rc;
 #endif
@@ -1287,56 +2004,28 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     
     utf8[utf8len] = 0;
     
-    crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, utf8len);
-    
-    dirpos = find_next_dir_index(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode);
-    if (dirpos == 0) {
-        Status = STATUS_INTERNAL_ERROR;
+    Status = fcb_get_last_dir_index(parfileref->fcb, &dirpos);
+    if (!NT_SUCCESS(Status)) {
+        ERR("fcb_get_last_dir_index returned %08x\n", Status);
         ExFreePool(utf8);
         return Status;
     }
     
-    TRACE("filename = %s, crc = %08x, dirpos = %llx\n", utf8, crc32, dirpos);
-    
     KeQuerySystemTime(&time);
     win_time_to_unix(time, &now);
     
-//     TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
+    TRACE("create file %.*S\n", fpus->Length / sizeof(WCHAR), fpus->Buffer);
+    ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+    TRACE("parfileref->fcb->inode_item.st_size (inode %llx) was %llx\n", parfileref->fcb->inode, parfileref->fcb->inode_item.st_size);
     parfileref->fcb->inode_item.st_size += utf8len * 2;
-//     TRACE("parfcb->inode_item.st_size was %llx\n", parfcb->inode_item.st_size);
+    TRACE("parfileref->fcb->inode_item.st_size (inode %llx) now %llx\n", parfileref->fcb->inode, parfileref->fcb->inode_item.st_size);
     parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
     parfileref->fcb->inode_item.sequence++;
     parfileref->fcb->inode_item.st_ctime = now;
     parfileref->fcb->inode_item.st_mtime = now;
+    ExReleaseResourceLite(parfileref->fcb->Header.Resource);
     
-    searchkey.obj_id = parfileref->fcb->inode;
-    searchkey.obj_type = TYPE_INODE_ITEM;
-    searchkey.offset = 0xffffffffffffffff;
-    
-    Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        ExFreePool(utf8);
-        return Status;
-    }
-    
-    if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
-        ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parfileref->fcb->inode, parfileref->fcb->subvol->id);
-        ExFreePool(utf8);
-        return STATUS_INTERNAL_ERROR;
-    }
-    
-    dirii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
-    if (!dirii) {
-        ERR("out of memory\n");
-        ExFreePool(utf8);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    RtlCopyMemory(dirii, &parfileref->fcb->inode_item, sizeof(INODE_ITEM));
-    delete_tree_item(Vcb, &tp, rollback);
-    
-    insert_tree_item(Vcb, parfileref->fcb->subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, dirii, sizeof(INODE_ITEM), NULL, rollback);
+    mark_fcb_dirty(parfileref->fcb);
     
     if (parfileref->fcb->subvol->lastinode == 0)
         get_last_inode(Vcb, parfileref->fcb->subvol);
@@ -1345,51 +2034,6 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     
     type = options & FILE_DIRECTORY_FILE ? BTRFS_TYPE_DIRECTORY : BTRFS_TYPE_FILE;
     
-    disize = sizeof(DIR_ITEM) - 1 + utf8len;
-    di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
-    if (!di) {
-        ERR("out of memory\n");
-        ExFreePool(utf8);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    di->key.obj_id = inode;
-    di->key.obj_type = TYPE_INODE_ITEM;
-    di->key.offset = 0;
-    di->transid = Vcb->superblock.generation;
-    di->m = 0;
-    di->n = (UINT16)utf8len;
-    di->type = type;
-    RtlCopyMemory(di->name, utf8, utf8len);
-    
-    insert_tree_item(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode, TYPE_DIR_INDEX, dirpos, di, disize, NULL, rollback);
-    
-    di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
-    if (!di2) {
-        ERR("out of memory\n");
-        ExFreePool(utf8);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    RtlCopyMemory(di2, di, disize);
-    
-    Status = add_dir_item(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode, crc32, di2, disize, rollback);
-    if (!NT_SUCCESS(Status)) {
-        ERR("add_dir_item returned %08x\n", Status);
-        ExFreePool(utf8);
-        return Status;
-    }
-    
-    utf8as.Buffer = utf8;
-    utf8as.Length = utf8as.MaximumLength = utf8len;
-    
-    Status = add_inode_ref(Vcb, parfileref->fcb->subvol, inode, parfileref->fcb->inode, dirpos, &utf8as, rollback);
-    if (!NT_SUCCESS(Status)) {
-        ERR("add_inode_ref returned %08x\n", Status);
-        ExFreePool(utf8);
-        return Status;
-    }
-    
     // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
     
     TRACE("requested attributes = %x\n", IrpSp->Parameters.Create.FileAttributes);
@@ -1411,19 +2055,6 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     if (IrpSp->Parameters.Create.FileAttributes == FILE_ATTRIBUTE_NORMAL)
         IrpSp->Parameters.Create.FileAttributes = defda;
     
-    if (IrpSp->Parameters.Create.FileAttributes != defda) {
-        char val[64];
-    
-        sprintf(val, "0x%x", IrpSp->Parameters.Create.FileAttributes);
-    
-        Status = set_xattr(Vcb, parfileref->fcb->subvol, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback);
-        if (!NT_SUCCESS(Status)) {
-            ERR("set_xattr returned %08x\n", Status);
-            ExFreePool(utf8);
-            return Status;
-        }
-    }
-    
     parfileref->fcb->subvol->lastinode++;
     
     fcb = create_fcb();
@@ -1473,6 +2104,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     fcb->Header.ValidDataLength.QuadPart = 0;
     
     fcb->atts = IrpSp->Parameters.Create.FileAttributes;
+    fcb->atts_changed = fcb->atts != defda;
     
 #ifdef DEBUG_FCB_REFCOUNTS
     rc = InterlockedIncrement(&parfileref->fcb->refcount);
@@ -1492,53 +2124,68 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
         return Status;
     }
     
-    fileref = create_fileref();
-    if (!fileref) {
+    fcb->sd_dirty = TRUE;
+    
+    hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
+    if (!hl) {
         ERR("out of memory\n");
         free_fcb(fcb);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    fileref->fcb = fcb;
-
-    fileref->utf8.MaximumLength = fileref->utf8.Length = utf8len;
-    fileref->utf8.Buffer = utf8;
+    hl->parent = parfileref->fcb->inode;
+    hl->index = dirpos;
     
-    fileref->filepart = *fpus;
-        
-    Status = set_xattr(Vcb, parfileref->fcb->subvol, inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), rollback);
-    if (!NT_SUCCESS(Status)) {
-        ERR("set_xattr returned %08x\n", Status);
-        free_fileref(fileref);
-        return Status;
-    }
+    hl->utf8.Length = hl->utf8.MaximumLength = utf8len;
+    hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG);
     
-    fileref->full_filename.Length = parfileref->full_filename.Length + (parfileref->full_filename.Length == sizeof(WCHAR) ? 0 : sizeof(WCHAR)) + fileref->filepart.Length;
-    fileref->full_filename.MaximumLength = fileref->full_filename.Length;
-    fileref->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->full_filename.Length, ALLOC_TAG);
-    if (!fileref->full_filename.Buffer) {
+    if (!hl->utf8.Buffer) {
         ERR("out of memory\n");
-        free_fileref(fileref);
+        ExFreePool(hl);
+        free_fcb(fcb);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
+    RtlCopyMemory(hl->utf8.Buffer, utf8, utf8len);
     
-    RtlCopyMemory(fileref->full_filename.Buffer, parfileref->full_filename.Buffer, parfileref->full_filename.Length);
+    hl->name.Length = hl->name.MaximumLength = fpus->Length;
+    hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fpus->Length, ALLOC_TAG);
+    
+    if (!hl->name.Buffer) {
+        ERR("out of memory\n");
+        ExFreePool(hl->utf8.Buffer);
+        ExFreePool(hl);
+        free_fcb(fcb);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
     
-    if (parfileref->full_filename.Length > sizeof(WCHAR))
-        fileref->full_filename.Buffer[parfileref->full_filename.Length / sizeof(WCHAR)] = '\\';
+    RtlCopyMemory(hl->name.Buffer, fpus->Buffer, fpus->Length);
     
-    RtlCopyMemory(&fileref->full_filename.Buffer[(parfileref->full_filename.Length / sizeof(WCHAR)) + (parfileref->full_filename.Length == sizeof(WCHAR) ? 0 : 1)],
-                  fileref->filepart.Buffer, fileref->filepart.Length);
+    InsertTailList(&fcb->hardlinks, &hl->list_entry);
     
-    ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
-    if (!ii) {
+    fileref = create_fileref();
+    if (!fileref) {
         ERR("out of memory\n");
+        free_fcb(fcb);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    fileref->fcb = fcb;
+    fileref->index = dirpos;
+
+    fileref->utf8.MaximumLength = fileref->utf8.Length = utf8len;
+    fileref->utf8.Buffer = utf8;
+    
+    fileref->filepart = *fpus;
+    
+    Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
         free_fileref(fileref);
-        return STATUS_INSUFFICIENT_RESOURCES;
+        return Status;
     }
-    
+        
     if (Irp->Overlay.AllocationSize.QuadPart > 0) {
-        Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, rollback);
+        Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
         
         if (!NT_SUCCESS(Status)) {
             ERR("extend_file returned %08x\n", Status);
@@ -1547,25 +2194,50 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
         }
     }
     
-    RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
-    insert_tree_item(Vcb, fcb->subvol, inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
+    fcb->created = TRUE;
+    mark_fcb_dirty(fcb);
+    
+    fileref->created = TRUE;
+    mark_fileref_dirty(fileref);
     
     fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
     fcb->subvol->root_item.ctime = now;
     
     fileref->parent = parfileref;
-    InsertTailList(&parfileref->children, &fileref->list_entry);
-#ifdef DEBUG_FCB_REFCOUNTS
-    rc = InterlockedIncrement(&parfileref->refcount);
-    WARN("fileref %p: refcount now %i\n", parfileref, rc);
-#else
-    InterlockedIncrement(&parfileref->refcount);
-#endif
+    
+    ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
+    
+    if (IsListEmpty(&parfileref->children))
+        InsertTailList(&parfileref->children, &fileref->list_entry);
+    else {
+        LIST_ENTRY* le = parfileref->children.Flink;
+        file_ref* fr1 = CONTAINING_RECORD(le, file_ref, list_entry);
+        
+        while (le != &parfileref->children) {
+            file_ref* fr2 = (le->Flink == &parfileref->children) ? NULL : CONTAINING_RECORD(le->Flink, file_ref, list_entry);
+            
+            if (fileref->index > fr1->index && (!fr2 || fr2->index > fileref->index)) {
+                InsertHeadList(&fr1->list_entry, &fileref->list_entry);
+                break;
+            }
+            
+            fr1 = fr2;
+            le = le->Flink;
+        }
+    }
+    
+    ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
+    
+    increase_fileref_refcount(parfileref);
  
     InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
     
     *pfr = fileref;
     
+    if (type == BTRFS_TYPE_DIRECTORY)
+        fileref->fcb->fileref = fileref;
+    
     TRACE("created new file %S in subvol %llx, inode %llx\n", file_desc_fileref(fileref), fcb->subvol->id, fcb->inode);
     
     return STATUS_SUCCESS;
@@ -1581,7 +2253,6 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     static WCHAR datasuf[] = {':','$','D','A','T','A',0};
     UNICODE_STRING dsus, fpus, stream;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    ULONG access;
     PACCESS_STATE access_state = IrpSp->Parameters.Create.SecurityContext->AccessState;
 #ifdef DEBUG_FCB_REFCOUNTS
     LONG oc;
@@ -1603,9 +2274,7 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     } else
         related = NULL;
     
-    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
     Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL);
-    ExReleaseResourceLite(&Vcb->fcb_lock);
     if (!NT_SUCCESS(Status))
         goto end;
     
@@ -1675,14 +2344,10 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
         file_ref* newpar;
         fcb* fcb;
         static char xapref[] = "user.";
-        ULONG xapreflen = strlen(xapref), fnlen;
+        ULONG xapreflen = strlen(xapref);
         LARGE_INTEGER time;
         BTRFS_TIME now;
-        KEY searchkey;
-        traverse_ptr tp;
-        INODE_ITEM* ii;
         ULONG utf8len;
-        UINT64 offset;
 #ifdef DEBUG_FCB_REFCOUNTS
         LONG rc;
 #endif
@@ -1690,19 +2355,11 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
         TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
         TRACE("stream = %.*S\n", stream.Length / sizeof(WCHAR), stream.Buffer);
         
-        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         Status = open_fileref(Vcb, &newpar, &fpus, parfileref, FALSE, NULL);
-        ExReleaseResourceLite(&Vcb->fcb_lock);
         
         if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
             UNICODE_STRING fpus2;
             
-            if (!SeAccessCheck(parfileref->fcb->sd, &access_state->SubjectSecurityContext, FALSE, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
-                               IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) {
-                WARN("SeAccessCheck failed, returning %08x\n", Status);
-                goto end;
-            }
-            
             if (!is_file_name_valid(&fpus))
                 return STATUS_OBJECT_NAME_INVALID;
             
@@ -1725,7 +2382,8 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
                 goto end;
             }
             
-            // FIXME - send notification
+            send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
+            send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
         } else if (!NT_SUCCESS(Status)) {
             ERR("open_fileref returned %08x\n", Status);
             goto end;
@@ -1734,12 +2392,6 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
         free_fileref(parfileref);
         parfileref = newpar;
         
-        if (!SeAccessCheck(parfileref->fcb->sd, &access_state->SubjectSecurityContext, FALSE, access_state->OriginalDesiredAccess, 0, NULL,
-                           IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) {
-            WARN("SeAccessCheck failed, returning %08x\n", Status);
-            goto end;
-        }
-        
         if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK) {
             WARN("parent not file or symlink\n");
             Status = STATUS_INVALID_PARAMETER;
@@ -1777,7 +2429,6 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
         fcb->type = parfileref->fcb->type;
         
         fcb->ads = TRUE;
-        fcb->adssize = 0;
         
         Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream.Buffer, stream.Length);
         if (!NT_SUCCESS(Status)) {
@@ -1821,10 +2472,6 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
         }
         
         fileref->fcb = fcb;
-        
-        fileref->name_offset = parfileref->full_filename.Length / sizeof(WCHAR);
-        if (parfileref != Vcb->root_fileref)
-            fileref->name_offset++;
 
         fileref->filepart.MaximumLength = fileref->filepart.Length = stream.Length;
         fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.MaximumLength, ALLOC_TAG);
@@ -1837,32 +2484,17 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
         
         RtlCopyMemory(fileref->filepart.Buffer, stream.Buffer, stream.Length);
         
-        fnlen = (fileref->name_offset * sizeof(WCHAR)) + fileref->filepart.Length;
-
-        fileref->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
-        if (!fileref->full_filename.Buffer) {
-            ERR("out of memory\n");
-            free_fileref(fileref);
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto end;
-        }
-        
-        fileref->full_filename.Length = fileref->full_filename.MaximumLength = fnlen;
-        RtlCopyMemory(fileref->full_filename.Buffer, parfileref->full_filename.Buffer, parfileref->full_filename.Length);
-
-        fileref->full_filename.Buffer[parfileref->full_filename.Length / sizeof(WCHAR)] = ':';
-
-        RtlCopyMemory(&fileref->full_filename.Buffer[fileref->name_offset], fileref->filepart.Buffer, fileref->filepart.Length);
-        TRACE("full_filename = %.*S\n", fileref->full_filename.Length / sizeof(WCHAR), fileref->full_filename.Buffer);
-        
-        Status = set_xattr(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)"", 0, rollback);
+        Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
         if (!NT_SUCCESS(Status)) {
-            ERR("set_xattr returned %08x\n", Status);
+            ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
             free_fileref(fileref);
             goto end;
         }
+       
+        mark_fcb_dirty(fcb);
         
         InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+        InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
         
         KeQuerySystemTime(&time);
         win_time_to_unix(time, &now);
@@ -1871,58 +2503,22 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
         parfileref->fcb->inode_item.sequence++;
         parfileref->fcb->inode_item.st_ctime = now;
         
-        searchkey.obj_id = parfileref->fcb->inode;
-        searchkey.obj_type = TYPE_INODE_ITEM;
-        searchkey.offset = 0xffffffffffffffff;
-        
-        Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, FALSE);
-        if (!NT_SUCCESS(Status)) {
-            ERR("error - find_item returned %08x\n", Status);
-            free_fileref(fileref);
-            goto end;
-        }
-        
-        if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
-            delete_tree_item(Vcb, &tp, rollback);
-            offset = tp.item->key.offset;
-        } else {
-            WARN("could not find INODE_ITEM for inode %llx in subvol %llx\n", searchkey.obj_id, parfileref->fcb->subvol->id);
-            offset = 0;
-        }
-        
-        ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
-        if (!ii) {
-            ERR("out of memory\n");
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            free_fileref(fileref);
-            goto end;
-        }
-    
-        RtlCopyMemory(ii, &parfileref->fcb->inode_item, sizeof(INODE_ITEM));
-        
-        insert_tree_item(Vcb, parfileref->fcb->subvol, parfileref->fcb->inode, TYPE_INODE_ITEM, offset, ii, sizeof(INODE_ITEM), NULL, rollback);
+        mark_fcb_dirty(parfileref->fcb);
         
         parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
         parfileref->fcb->subvol->root_item.ctime = now;
         
         fileref->parent = (struct _file_ref*)parfileref;
+        
+        ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
         InsertTailList(&parfileref->children, &fileref->list_entry);
-#ifdef DEBUG_FCB_REFCOUNTS
-        rc = InterlockedIncrement(&parfileref->refcount);
-        WARN("fileref %p: refcount now %i\n", parfileref, rc);
-#else
-        InterlockedIncrement(&parfileref->refcount);
-#endif
+        ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
+        
+        increase_fileref_refcount(parfileref);
         
         ExFreePool(fpus.Buffer);
         fpus.Buffer = NULL;
     } else {
-        if (!SeAccessCheck(parfileref->fcb->sd, &access_state->SubjectSecurityContext, FALSE, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
-                           IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) {
-            WARN("SeAccessCheck failed, returning %08x\n", Status);
-            goto end;
-        }
-        
         if (!is_file_name_valid(&fpus)) {
             Status = STATUS_OBJECT_NAME_INVALID;
             goto end;
@@ -1934,6 +2530,9 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
             ERR("file_create2 returned %08x\n", Status);
             goto end;
         }
+        
+        send_notification_fileref(fileref, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
+        send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
     }
     
     FileObject->FsContext = fileref->fcb;
@@ -1958,7 +2557,7 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     RtlInitUnicodeString(&ccb->query_string, NULL);
     ccb->has_wildcard = FALSE;
     ccb->specific_file = FALSE;
-    ccb->access = access;
+    ccb->access = access_state->OriginalDesiredAccess;
     
 #ifdef DEBUG_FCB_REFCOUNTS
     oc = InterlockedIncrement(&fileref->fcb->open_count);
@@ -1973,37 +2572,31 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     
 //     TRACE("returning FCB %p with parent %p\n", fcb, parfcb);
     
-    Status = consider_write(Vcb);
-    
-    if (NT_SUCCESS(Status)) {
-//         ULONG fnlen;
+//     ULONG fnlen;
 // 
-//         fcb->name_offset = fcb->par->full_filename.Length / sizeof(WCHAR);
-//                 
-//         if (fcb->par != Vcb->root_fcb)
-//             fcb->name_offset++;
-//         
-//         fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
-//         
-//         fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
-//         if (!fcb->full_filename.Buffer) {
-//             ERR("out of memory\n");
-//             Status = STATUS_INSUFFICIENT_RESOURCES;
-//             goto end;
-//         }   
-//         
-//         fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
-//         RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
-//         
-//         if (fcb->par != Vcb->root_fcb)
-//             fcb->full_filename.Buffer[fcb->par->full_filename.Length / sizeof(WCHAR)] = '\\';
-//         
-//         RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
-        
-        send_notification_fileref(fileref, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
+//     fcb->name_offset = fcb->par->full_filename.Length / sizeof(WCHAR);
+//             
+//     if (fcb->par != Vcb->root_fcb)
+//         fcb->name_offset++;
+//     
+//     fnlen = (fcb->name_offset * sizeof(WCHAR)) + fcb->filepart.Length;
+//     
+//     fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, fnlen, ALLOC_TAG);
+//     if (!fcb->full_filename.Buffer) {
+//         ERR("out of memory\n");
+//         Status = STATUS_INSUFFICIENT_RESOURCES;
+//         goto end;
+//     }   
+//     
+//     fcb->full_filename.Length = fcb->full_filename.MaximumLength = fnlen;
+//     RtlCopyMemory(fcb->full_filename.Buffer, fcb->par->full_filename.Buffer, fcb->par->full_filename.Length);
+//     
+//     if (fcb->par != Vcb->root_fcb)
+//         fcb->full_filename.Buffer[fcb->par->full_filename.Length / sizeof(WCHAR)] = '\\';
+//     
+//     RtlCopyMemory(&fcb->full_filename.Buffer[fcb->name_offset], fcb->filepart.Buffer, fcb->filepart.Length);
         
-        goto end2;
-    }
+    goto end2;
     
 end:    
     if (fpus.Buffer)
@@ -2141,44 +2734,6 @@ static __inline void debug_create_options(ULONG RequestedOptions) {
     }
 }
 
-NTSTATUS update_inode_item(device_extension* Vcb, root* subvol, UINT64 inode, INODE_ITEM* ii, LIST_ENTRY* rollback) {
-    KEY searchkey;
-    traverse_ptr tp;
-    INODE_ITEM* newii;
-    NTSTATUS Status;
-    UINT64 offset = 0;
-    
-    searchkey.obj_id = inode;
-    searchkey.obj_type = TYPE_INODE_ITEM;
-    searchkey.offset = 0xffffffffffffffff;
-    
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return Status;
-    }
-    
-    if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
-        delete_tree_item(Vcb, &tp, rollback);
-        
-        offset = tp.item->key.offset;
-    } else {
-        WARN("could not find INODE_ITEM for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id);
-    }
-    
-    newii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
-    if (!newii) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-
-    RtlCopyMemory(newii, ii, sizeof(INODE_ITEM));
-    
-    insert_tree_item(Vcb, subvol, inode, TYPE_INODE_ITEM, offset, newii, sizeof(INODE_ITEM), NULL, rollback);
-    
-    return STATUS_SUCCESS;
-}
-
 static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) {
     NTSTATUS Status;
     
@@ -2199,9 +2754,9 @@ static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) {
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
-        Status = read_file(fcb->Vcb, fcb->subvol, fcb->inode, *data, 0, size, &bytes_read);
+        Status = read_file(fcb, *data, 0, size, &bytes_read, NULL);
         if (!NT_SUCCESS(Status)) {
-            ERR("read_file returned %08x\n", Status);
+            ERR("read_file_fcb returned %08x\n", Status);
             ExFreePool(*data);
             return Status;
         }
@@ -2270,27 +2825,28 @@ static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) {
                 return Status;
             }
         }
-    } else {
-        UINT16 datalen;
-        
-        if (!get_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, data, &datalen))
-            return STATUS_INTERNAL_ERROR;
-        
-        if (!*data)
+    } else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
+        if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length == 0)
             return STATUS_INTERNAL_ERROR;
-        
-        if (datalen < sizeof(ULONG)) {
+            
+        if (fcb->reparse_xattr.Length < sizeof(ULONG)) {
             WARN("xattr was too short to be a reparse point\n");
-            ExFreePool(*data);
             return STATUS_INTERNAL_ERROR;
         }
         
-        Status = FsRtlValidateReparsePointBuffer(datalen, (REPARSE_DATA_BUFFER*)*data);
+        Status = FsRtlValidateReparsePointBuffer(fcb->reparse_xattr.Length, (REPARSE_DATA_BUFFER*)fcb->reparse_xattr.Buffer);
         if (!NT_SUCCESS(Status)) {
             ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status);
-            ExFreePool(*data);
             return Status;
         }
+        
+        *data = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.Length, ALLOC_TAG);
+        if (!*data) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlCopyMemory(*data, fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
     }
     
     return STATUS_SUCCESS;
@@ -2304,8 +2860,6 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
     ccb* ccb;
     device_extension* Vcb = DeviceObject->DeviceExtension;
     PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
-    ULONG access;
-    PACCESS_STATE access_state = Stack->Parameters.Create.SecurityContext->AccessState;
     USHORT unparsed;
     file_ref *related, *fileref;
 #ifdef DEBUG_FCB_REFCOUNTS
@@ -2322,15 +2876,16 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         Status = STATUS_INVALID_PARAMETER;
         goto exit;
     }
-    
-    if (options & FILE_OPEN_BY_FILE_ID) {
-        WARN("FILE_OPEN_BY_FILE_ID not supported\n");
-        Status = STATUS_NOT_IMPLEMENTED;
-        goto exit;
-    }
 
     FileObject = Stack->FileObject;
-
+    
+    if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) {
+        struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
+        
+        related = relatedccb->fileref;
+    } else
+        related = NULL;
+    
     debug_create_options(options);
     
     switch (RequestedDisposition) {
@@ -2374,21 +2929,42 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
     
     // FIXME - if Vcb->readonly or subvol readonly, don't allow the write ACCESS_MASK flags
     
-    if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) {
-        struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
+    if (options & FILE_OPEN_BY_FILE_ID) {
+        if (FileObject->FileName.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) {
+            UINT64 inode;
+            
+            RtlCopyMemory(&inode, FileObject->FileName.Buffer, sizeof(UINT64));
+            
+            if (related->fcb == Vcb->root_fileref->fcb && inode == 0)
+                inode = Vcb->root_fileref->fcb->inode;
+            
+            if (inode == 0) { // we use 0 to mean the parent of a subvolume
+                fileref = related->parent;
+                increase_fileref_refcount(fileref);
+                Status = STATUS_SUCCESS;
+            } else
+                Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref);
+        } else {
+            WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
+            Status = STATUS_NOT_IMPLEMENTED;
+            goto exit;
+        }
+    } else {
+        if (related && FileObject->FileName.Length != 0 && FileObject->FileName.Buffer[0] == '\\') {
+            Status = STATUS_OBJECT_NAME_INVALID;
+            goto exit;
+        }
         
-        related = relatedccb->fileref;
-    } else
-        related = NULL;
-
-    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-    Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed);
-    ExReleaseResourceLite(&Vcb->fcb_lock);
+        Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed);
+    }
     
     if (Status == STATUS_REPARSE) {
         REPARSE_DATA_BUFFER* data;
         
+        ExAcquireResourceSharedLite(fileref->fcb->Header.Resource, TRUE);
         Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
+        ExReleaseResourceLite(fileref->fcb->Header.Resource);
+        
         if (!NT_SUCCESS(Status)) {
             ERR("get_reparse_block returned %08x\n", Status);
             
@@ -2417,7 +2993,6 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         ExReleaseResourceLite(&Vcb->fcb_lock);
         
         Status = STATUS_OBJECT_NAME_NOT_FOUND;
-        goto exit; // FIXME?
     }
     
     if (NT_SUCCESS(Status)) {
@@ -2440,17 +3015,18 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
     if (NT_SUCCESS(Status)) { // file already exists
         file_ref* sf;
         
-        if (Vcb->readonly && RequestedDisposition == FILE_OVERWRITE_IF) {
-            Status = STATUS_MEDIA_WRITE_PROTECTED;
-            free_fileref(fileref);
-            goto exit;
-        }
-        
-        if (fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && (RequestedDisposition == FILE_SUPERSEDE ||
-            RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF)) {
-            Status = STATUS_ACCESS_DENIED;
-            free_fileref(fileref);
-            goto exit;
+        if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
+            if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+                Status = STATUS_ACCESS_DENIED;
+                free_fileref(fileref);
+                goto exit;
+            }
+            
+            if (Vcb->readonly) {
+                Status = STATUS_MEDIA_WRITE_PROTECTED;
+                free_fileref(fileref);
+                goto exit;
+            }
         }
         
         TRACE("deleted = %s\n", fileref->deleted ? "TRUE" : "FALSE");
@@ -2469,12 +3045,49 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             sf = sf->parent;
         }
         
-        if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF)) {
-            Status = STATUS_ACCESS_DENIED;
+        if (fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
+            ACCESS_MASK allowed = DELETE | READ_CONTROL | WRITE_OWNER | WRITE_DAC |
+                                    SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA |
+                                    FILE_READ_EA | FILE_WRITE_EA | FILE_READ_ATTRIBUTES |
+                                    FILE_WRITE_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY |
+                                    FILE_TRAVERSE;
+
+            if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY)
+                allowed |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD;
+            
+            if (Stack->Parameters.Create.SecurityContext->DesiredAccess & ~allowed) {
+                Status = STATUS_ACCESS_DENIED;
+                free_fileref(fileref);
+                goto exit;
+            }
+        }
+        
+        if (options & FILE_NON_DIRECTORY_FILE && fileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fileref(fileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
+            Status = STATUS_FILE_IS_A_DIRECTORY;
+            goto exit;
+        } else if (options & FILE_DIRECTORY_FILE && fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
+            TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref));
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fileref(fileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
+            Status = STATUS_NOT_A_DIRECTORY;
+            goto exit;
+        }
+        
+        if (options & FILE_DELETE_ON_CLOSE && (fileref == Vcb->root_fileref || Vcb->readonly ||
+            fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY || fileref->fcb->atts & FILE_ATTRIBUTE_READONLY)) {
+            Status = STATUS_CANNOT_DELETE;
             free_fileref(fileref);
             goto exit;
         }
         
+        
         if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT))  {
             UINT8* data;
             
@@ -2506,18 +3119,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             goto exit;
         }
         
-        if (!SeAccessCheck(fileref->fcb->ads ? fileref->parent->fcb->sd : fileref->fcb->sd, &access_state->SubjectSecurityContext, FALSE, access_state->OriginalDesiredAccess, 0, NULL,
-            IoGetFileObjectGenericMapping(), Stack->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode, &access, &Status)) {
-            WARN("SeAccessCheck failed, returning %08x\n", Status);
-        
-            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-            free_fileref(fileref);
-            ExReleaseResourceLite(&Vcb->fcb_lock);
-            goto exit;
-        }
-        
         if (fileref->fcb->open_count > 0) {
-            Status = IoCheckShareAccess(access, Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, TRUE);
+            Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
+                                        Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, TRUE);
             
             if (!NT_SUCCESS(Status)) {
                 WARN("IoCheckShareAccess failed, returning %08x\n", Status);
@@ -2528,10 +3132,11 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
                 goto exit;
             }
         } else {
-            IoSetShareAccess(access, Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
+            IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
+                             Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
         }
 
-        if (access & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) {
+        if (Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) {
             if (!MmFlushImageSection(&fileref->fcb->nonpaged->segment_object, MmFlushForWrite)) {
                 Status = (options & FILE_DELETE_ON_CLOSE) ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION;
                 
@@ -2543,7 +3148,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         }
         
         if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
-            ULONG defda;
+            ULONG defda, oldatts, filter;
+            LARGE_INTEGER time;
+            BTRFS_TIME now;
             
             if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
                 WARN("cannot overwrite readonly file\n");
@@ -2569,7 +3176,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             }
             
             if (Irp->Overlay.AllocationSize.QuadPart > 0) {
-                Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, rollback);
+                Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
                 
                 if (!NT_SUCCESS(Status)) {
                     ERR("extend_file returned %08x\n", Status);
@@ -2578,12 +3185,11 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
                 }
             }
             
-            Status = update_inode_item(Vcb, fileref->fcb->subvol, fileref->fcb->inode, &fileref->fcb->inode_item, rollback);
-            if (!NT_SUCCESS(Status)) {
-                ERR("update_inode_item returned %08x\n", Status);
-                free_fileref(fileref);
-                goto exit;
-            }
+            filter = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE;
+            
+            mark_fcb_dirty(fileref->fcb);
+            
+            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);
@@ -2593,48 +3199,24 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             else
                 fileref->fcb->atts |= Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
             
-            if (Stack->Parameters.Create.FileAttributes != defda) {
-                char val[64];
-            
-                sprintf(val, "0x%x", Stack->Parameters.Create.FileAttributes);
+            if (fileref->fcb->atts != oldatts) {
+                fileref->fcb->atts_changed = TRUE;
+                fileref->fcb->atts_deleted = Stack->Parameters.Create.FileAttributes == defda;
+                filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+            }
             
-                Status = set_xattr(Vcb, fileref->fcb->subvol, fileref->fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), rollback);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("set_xattr returned %08x\n", Status);
-                    free_fileref(fileref);
-                    goto exit;
-                }
-            } else
-                delete_xattr(Vcb, fileref->fcb->subvol, fileref->fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, rollback);
+            KeQuerySystemTime(&time);
+            win_time_to_unix(time, &now);
             
+            fileref->fcb->inode_item.transid = Vcb->superblock.generation;
+            fileref->fcb->inode_item.sequence++;
+            fileref->fcb->inode_item.st_ctime = now;
+            fileref->fcb->inode_item.st_mtime = now;
+
             // FIXME - truncate streams
             // FIXME - do we need to alter parent directory's times?
-            // FIXME - send notifications
-            
-            Status = consider_write(Vcb);
-            if (!NT_SUCCESS(Status)) {
-                ERR("consider_write returned %08x\n", Status);
-                free_fileref(fileref);
-                goto exit;
-            }
-        }
-    
-        if (options & FILE_NON_DIRECTORY_FILE && fileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
-            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-            free_fileref(fileref);
-            ExReleaseResourceLite(&Vcb->fcb_lock);
-            
-            Status = STATUS_FILE_IS_A_DIRECTORY;
-            goto exit;
-        } else if (options & FILE_DIRECTORY_FILE && fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
-            TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref));
-            
-            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-            free_fileref(fileref);
-            ExReleaseResourceLite(&Vcb->fcb_lock);
             
-            Status = STATUS_NOT_A_DIRECTORY;
-            goto exit;
+            send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED);
         }
     
         FileObject->FsContext = fileref->fcb;
@@ -2661,7 +3243,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         RtlInitUnicodeString(&ccb->query_string, NULL);
         ccb->has_wildcard = FALSE;
         ccb->specific_file = FALSE;
-        ccb->access = access;
+        ccb->access = Stack->Parameters.Create.SecurityContext->DesiredAccess;
         
         ccb->fileref = fileref;
         
@@ -2716,8 +3298,8 @@ exit:
 NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     NTSTATUS Status;
     PIO_STACK_LOCATION IrpSp;
-    device_extension* Vcb = NULL;
-    BOOL top_level;
+    device_extension* Vcb = DeviceObject->DeviceExtension;
+    BOOL top_level, locked = FALSE;
     LIST_ENTRY rollback;
     
     TRACE("create (flags = %x)\n", Irp->Flags);
@@ -2729,7 +3311,7 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     top_level = is_top_level(Irp);
     
     /* return success if just called for FS device object */
-    if (DeviceObject == devobj)  {
+    if (DeviceObject == devobj || (Vcb && Vcb->type == VCB_TYPE_PARTITION0))  {
         TRACE("create called for FS device object\n");
         
         Irp->IoStatus.Information = FILE_OPENED;
@@ -2740,6 +3322,7 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     
     Vcb = DeviceObject->DeviceExtension;
     ExAcquireResourceSharedLite(&Vcb->load_lock, TRUE);
+    locked = TRUE;
     
     IrpSp = IoGetCurrentIrpStackLocation(Irp);
     
@@ -2820,27 +3403,24 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
         
         IrpSp->FileObject->SectionObjectPointer = &Vcb->volume_fcb->nonpaged->segment_object;
 
+        if (!IrpSp->FileObject->Vpb)
+            IrpSp->FileObject->Vpb = DeviceObject->Vpb;
+
         Irp->IoStatus.Information = FILE_OPENED;
         Status = STATUS_SUCCESS;
     } else {
-        BOOL exclusive, skip_lock;
-        ULONG disposition;
+        BOOL skip_lock;
         
         TRACE("file name: %.*S\n", IrpSp->FileObject->FileName.Length / sizeof(WCHAR), IrpSp->FileObject->FileName.Buffer);
         
         if (IrpSp->FileObject->RelatedFileObject)
             TRACE("related file = %S\n", file_desc(IrpSp->FileObject->RelatedFileObject));
         
-        disposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
-        
-        // We acquire the lock exclusively if there's the possibility we might be writing
-        exclusive = disposition != FILE_OPEN;
-        
         // Don't lock again if we're being called from within CcCopyRead etc.
         skip_lock = ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock);
 
         if (!skip_lock)
-            acquire_tree_lock(Vcb, exclusive); 
+            ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
         
 //         ExAcquireResourceExclusiveLite(&Vpb->DirResource, TRUE);
     //     Status = NtfsCreateFile(DeviceObject,
@@ -2848,13 +3428,13 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
         Status = open_file(DeviceObject, Irp, &rollback);
 //         ExReleaseResourceLite(&Vpb->DirResource);
         
-        if (exclusive && !NT_SUCCESS(Status))
+        if (!NT_SUCCESS(Status))
             do_rollback(Vcb, &rollback);
         else
             clear_rollback(&rollback);
         
         if (!skip_lock)
-            release_tree_lock(Vcb, exclusive);
+            ExReleaseResourceLite(&Vcb->tree_lock);
         
 //         Status = STATUS_ACCESS_DENIED;
     }
@@ -2866,7 +3446,8 @@ exit:
     
     TRACE("create returning %08x\n", Status);
     
-    ExReleaseResourceLite(&Vcb->load_lock);
+    if (locked)
+        ExReleaseResourceLite(&Vcb->load_lock);
     
     if (top_level) 
         IoSetTopLevelIrp(NULL);
index 30bca36..972bc6f 100644 (file)
@@ -25,14 +25,16 @@ enum DirEntryType {
 
 typedef struct {
     KEY key;
+    BOOL name_alloc;
     char* name;
     ULONG namelen;
     UINT8 type;
     enum DirEntryType dir_entry_type;
 } dir_entry;
 
-ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type) {
-    ULONG att, tag, br;
+static ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts) {
+    fcb* fcb;
+    ULONG tag = 0, br;
     NTSTATUS Status;
     
     // FIXME - will this slow things down?
@@ -43,43 +45,41 @@ ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode,
     if (type != BTRFS_TYPE_FILE && type != BTRFS_TYPE_DIRECTORY)
         return 0;
     
-    att = get_file_attributes(Vcb, NULL, subvol, inode, type, FALSE, FALSE);
+    if (!(atts & FILE_ATTRIBUTE_REPARSE_POINT))
+        return 0;
     
-    if (!(att & FILE_ATTRIBUTE_REPARSE_POINT))
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    Status = open_fcb(Vcb, subvol, inode, type, NULL, NULL, &fcb);
+    if (!NT_SUCCESS(Status)) {
+        ERR("open_fcb returned %08x\n", Status);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
         return 0;
+    }
+    ExReleaseResourceLite(&Vcb->fcb_lock);
+    
+    ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
     
     if (type == BTRFS_TYPE_DIRECTORY) {
-        UINT8* data;
-        UINT16 datalen;
-        
-        if (!get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &data, &datalen))
-            return 0;
-        
-        if (!data)
-            return 0;
-        
-        if (datalen < sizeof(ULONG)) {
-            ExFreePool(data);
-            return 0;
-        }
-        
-        RtlCopyMemory(&tag, data, sizeof(ULONG));
+        if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length < sizeof(ULONG))
+            goto end;
         
-        ExFreePool(data);
+        RtlCopyMemory(&tag, fcb->reparse_xattr.Buffer, sizeof(ULONG));
     } else {
-        // FIXME - see if file loaded and cached, and do CcCopyRead if it is
-
-        Status = read_file(Vcb, subvol, inode, (UINT8*)&tag, 0, sizeof(ULONG), &br);
-        
+        Status = read_file(fcb, (UINT8*)&tag, 0, sizeof(ULONG), &br, NULL);
         if (!NT_SUCCESS(Status)) {
             ERR("read_file returned %08x\n", Status);
-            return 0;
+            goto end;
         }
         
         if (br < sizeof(ULONG))
-            return 0;
+            goto end;
     }
     
+end:
+    ExReleaseResourceLite(fcb->Header.Resource);
+
+    free_fcb(fcb);
+    
     return tag;
 }
 
@@ -90,7 +90,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
     INODE_ITEM ii;
     NTSTATUS Status;
     ULONG stringlen;
-    BOOL dotfile;
+    ULONG atts;
     
     IrpSp = IoGetCurrentIrpStackLocation(Irp);
     
@@ -128,24 +128,23 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
                 LIST_ENTRY* le;
                 BOOL found = FALSE;
                 
-                if (fileref) {
-                    ExAcquireResourceSharedLite(&fcb->Vcb->fcb_lock, TRUE);
-                    
-                    le = fileref->children.Flink;
-                    while (le != &fileref->children) {
-                        file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
+                ExAcquireResourceSharedLite(&fcb->Vcb->fcb_lock, TRUE);
+                if (!IsListEmpty(&r->fcbs)) {
+                    le = r->fcbs.Flink;
+                    while (le != &r->fcbs) {
+                        struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
                         
-                        if (c->fcb->subvol == r && c->fcb->inode == inode && !c->fcb->ads) {
-                            ii = c->fcb->inode_item;
+                        if (fcb2->inode == inode && !fcb2->ads) {
+                            ii = fcb2->inode_item;
+                            atts = fcb2->atts;
                             found = TRUE;
                             break;
                         }
                         
                         le = le->Flink;
                     }
-                    
-                    ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
                 }
+                ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
                 
                 if (!found) {
                     KEY searchkey;
@@ -170,6 +169,16 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
                     
                     if (tp.item->size > 0)
                         RtlCopyMemory(&ii, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
+                    
+                    if (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation ||
+                        IrpSp->Parameters.QueryDirectory.FileInformationClass == FileDirectoryInformation ||
+                        IrpSp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation ||
+                        IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation) {
+                        
+                        BOOL dotfile = de->namelen > 1 && de->name[0] == '.';
+
+                        atts = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+                    }
                 }
                 
                 break;
@@ -179,6 +188,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
                 ii = fcb->inode_item;
                 r = fcb->subvol;
                 inode = fcb->inode;
+                atts = fcb->atts;
                 break;
                 
             case DirEntryType_Parent:
@@ -186,6 +196,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
                     ii = fileref->parent->fcb->inode_item;
                     r = fileref->parent->fcb->subvol;
                     inode = fileref->parent->fcb->inode;
+                    atts = fileref->parent->fcb->atts;
                 } else {
                     ERR("no fileref\n");
                     return STATUS_INTERNAL_ERROR;
@@ -208,8 +219,6 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
         }
     }
     
-    dotfile = de->name[0] == '.' && (de->name[1] != '.' || de->name[2] != 0) && (de->name[1] != 0);
-    
     switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
         case FileBothDirectoryInformation:
         {
@@ -220,7 +229,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             needed = sizeof(FILE_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
             
             if (needed > *len) {
-                WARN("buffer overflow - %u > %u\n", needed, *len);
+                TRACE("buffer overflow - %u > %u\n", needed, *len);
                 return STATUS_BUFFER_OVERFLOW;
             }
            
@@ -232,9 +241,9 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             fbdi->ChangeTime.QuadPart = 0;
             fbdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
             fbdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
-            fbdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+            fbdi->FileAttributes = atts;
             fbdi->FileNameLength = stringlen;
-            fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type);
+            fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
             fbdi->ShortNameLength = 0;
 //             fibdi->ShortName[12];
             
@@ -259,7 +268,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             needed = sizeof(FILE_DIRECTORY_INFORMATION) - sizeof(WCHAR) + stringlen;
             
             if (needed > *len) {
-                WARN("buffer overflow - %u > %u\n", needed, *len);
+                TRACE("buffer overflow - %u > %u\n", needed, *len);
                 return STATUS_BUFFER_OVERFLOW;
             }
            
@@ -271,7 +280,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             fdi->ChangeTime.QuadPart = 0;
             fdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
             fdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
-            fdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+            fdi->FileAttributes = atts;
             fdi->FileNameLength = stringlen;
             
             Status = RtlUTF8ToUnicodeN(fdi->FileName, stringlen, &stringlen, de->name, de->namelen);
@@ -295,7 +304,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             needed = sizeof(FILE_FULL_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
             
             if (needed > *len) {
-                WARN("buffer overflow - %u > %u\n", needed, *len);
+                TRACE("buffer overflow - %u > %u\n", needed, *len);
                 return STATUS_BUFFER_OVERFLOW;
             }
            
@@ -307,9 +316,9 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             ffdi->ChangeTime.QuadPart = 0;
             ffdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
             ffdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
-            ffdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+            ffdi->FileAttributes = atts;
             ffdi->FileNameLength = stringlen;
-            ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type);
+            ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
             
             Status = RtlUTF8ToUnicodeN(ffdi->FileName, stringlen, &stringlen, de->name, de->namelen);
 
@@ -332,7 +341,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             needed = sizeof(FILE_ID_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + stringlen;
             
             if (needed > *len) {
-                WARN("buffer overflow - %u > %u\n", needed, *len);
+                TRACE("buffer overflow - %u > %u\n", needed, *len);
                 return STATUS_BUFFER_OVERFLOW;
             }
             
@@ -347,9 +356,9 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             fibdi->ChangeTime.QuadPart = 0;
             fibdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
             fibdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
-            fibdi->FileAttributes = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+            fibdi->FileAttributes = atts;
             fibdi->FileNameLength = stringlen;
-            fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type);
+            fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
             fibdi->ShortNameLength = 0;
 //             fibdi->ShortName[12];
             fibdi->FileId.QuadPart = inode;
@@ -379,7 +388,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             needed = sizeof(FILE_NAMES_INFORMATION) - sizeof(WCHAR) + stringlen;
             
             if (needed > *len) {
-                WARN("buffer overflow - %u > %u\n", needed, *len);
+                TRACE("buffer overflow - %u > %u\n", needed, *len);
                 return STATUS_BUFFER_OVERFLOW;
             }
             
@@ -419,19 +428,23 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
     return STATUS_NO_MORE_FILES;
 }
 
-static NTSTATUS STDCALL next_dir_entry(f