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

CORE-11937

svn path=/trunk/; revision=72576

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

index 1156757..7e98893 100644 (file)
@@ -1,10 +1,13 @@
+add_subdirectory(zlib)
 
 include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/drivers
+                    ${REACTOS_SOURCE_DIR}/sdk/include/reactos/libs/zlib
                     inc)
 
 list(APPEND SOURCE
     btrfs.c
     cache.c
+    compress.c
     crc32c.c
     create.c
     dirctrl.c
@@ -29,7 +32,7 @@ add_library(btrfs SHARED ${SOURCE} btrfs.rc)
 
 add_definitions(-D__KERNEL__)
 set_module_type(btrfs kernelmodedriver)
-target_link_libraries(btrfs ntoskrnl_vista ${PSEH_LIB})
+target_link_libraries(btrfs ntoskrnl_vista zlib_solo ${PSEH_LIB})
 add_importlibs(btrfs ntoskrnl hal)
 add_pch(btrfs btrfs_drv.h SOURCE)
 add_cd_file(TARGET btrfs DESTINATION reactos/system32/drivers NO_CAB FOR all)
index 190db9c..2426362 100644 (file)
@@ -35,8 +35,9 @@
 #endif
 #include <mountdev.h>
 
-#define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \
-                            BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
+#define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
+                            BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | \
+                            BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES)
 #define COMPAT_RO_SUPPORTED 0
 
 static WCHAR device_name[] = {'\\','B','t','r','f','s',0};
@@ -53,6 +54,12 @@ LIST_ENTRY volumes;
 LIST_ENTRY VcbList;
 ERESOURCE global_loading_lock;
 UINT32 debug_log_level = 0;
+UINT32 mount_compress = 0;
+UINT32 mount_compress_force = 0;
+UINT32 mount_compress_type = 0;
+UINT32 mount_zlib_level = 3;
+UINT32 mount_flush_interval = 30;
+UINT32 mount_max_inline = 2048;
 BOOL log_started = FALSE;
 UNICODE_STRING log_device, log_file, registry_path;
 
@@ -313,7 +320,7 @@ static void STDCALL DriverUnload(PDRIVER_OBJECT DriverObject) {
         ExFreePool(registry_path.Buffer);
 }
 
-BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) {
+BOOL STDCALL get_last_inode(device_extension* Vcb, root* r, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, prev_tp;
     NTSTATUS Status;
@@ -323,13 +330,13 @@ BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) {
     searchkey.obj_type = 0xff;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return FALSE;
     }
     
-    while (find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
+    while (find_prev_item(Vcb, &tp, &prev_tp, FALSE, Irp)) {
         tp = prev_tp;
         
         TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
@@ -348,7 +355,7 @@ BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) {
     return TRUE;
 }
 
-BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen) {
+BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     DIR_ITEM* xa;
@@ -361,7 +368,7 @@ BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char*
     searchkey.obj_type = TYPE_XATTR_ITEM;
     searchkey.offset = crc32;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return FALSE;
@@ -419,7 +426,7 @@ BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char*
     return FALSE;
 }
 
-NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback) {
+NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     UINT8* di2;
@@ -429,7 +436,7 @@ NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32
     searchkey.obj_type = TYPE_DIR_ITEM;
     searchkey.offset = crc32;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -456,11 +463,11 @@ NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL, rollback);
+        insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL, Irp, rollback);
         
         ExFreePool(di);
     } else {
-        insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL, rollback);
+        insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL, Irp, rollback);
     }
     
     return STATUS_SUCCESS;
@@ -704,8 +711,24 @@ static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObj
             break;
 
         case FileFsDeviceInformation:
-            FIXME("STUB: FileFsDeviceInformation\n");
+        {
+            FILE_FS_DEVICE_INFORMATION* ffdi = Irp->AssociatedIrp.SystemBuffer;
+            
+            TRACE("FileFsDeviceInformation\n");
+            
+            ffdi->DeviceType = FILE_DEVICE_DISK;
+            ffdi->Characteristics = Vcb->devices[0].devobj->Characteristics;
+            
+            if (Vcb->readonly)
+                ffdi->Characteristics |= FILE_READ_ONLY_DEVICE;
+            else
+                ffdi->Characteristics &= ~FILE_READ_ONLY_DEVICE;
+
+            BytesCopied = sizeof(FILE_FS_DEVICE_INFORMATION);
+            Status = STATUS_SUCCESS;
+            
             break;
+        }
 
         case FileFsDriverPathInformation:
             FIXME("STUB: FileFsDriverPathInformation\n");
@@ -919,7 +942,7 @@ static NTSTATUS STDCALL read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, P
 //     }
 // }
 
-NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, LIST_ENTRY* rollback) {
+NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, PIRP Irp, LIST_ENTRY* rollback) {
     root* r;
     tree* t;
     ROOT_ITEM* ri;
@@ -975,7 +998,7 @@ NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_t
     // We ask here for a traverse_ptr to the item we're inserting, so we can
     // copy some of the tree's variables
     
-    if (!insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp, rollback)) {
         ERR("insert_tree_item failed\n");
         ExFreePool(ri);
         
@@ -1159,30 +1182,46 @@ NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_t
 static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) {
     ULONG utf8len;
     NTSTATUS Status;
+    USHORT vollen, i;
     
     TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel);
     
-    Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
-    if (!NT_SUCCESS(Status))
-        goto end;
+    vollen = ffli->VolumeLabelLength;
     
-    if (utf8len > MAX_LABEL_SIZE) {
-        Status = STATUS_INVALID_VOLUME_LABEL;
-        goto end;
+    for (i = 0; i < ffli->VolumeLabelLength / sizeof(WCHAR); i++) {
+        if (ffli->VolumeLabel[i] == 0) {
+            vollen = i * sizeof(WCHAR);
+            break;
+        } else if (ffli->VolumeLabel[i] == '/' || ffli->VolumeLabel[i] == '\\') {
+            Status = STATUS_INVALID_VOLUME_LABEL;
+            goto end;
+        }
     }
     
-    // FIXME - check for '/' and '\\' and reject
-    
-//     utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
-    
-    Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE * sizeof(WCHAR), &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
-    if (!NT_SUCCESS(Status))
-        goto release;
+    if (vollen == 0) {
+        utf8len = 0;
+    } else {
+        Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, vollen);
+        if (!NT_SUCCESS(Status))
+            goto end;
+        
+        if (utf8len > MAX_LABEL_SIZE) {
+            Status = STATUS_INVALID_VOLUME_LABEL;
+            goto end;
+        }
+    }
     
     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
     
-    if (utf8len < MAX_LABEL_SIZE * sizeof(WCHAR))
-        RtlZeroMemory(Vcb->superblock.label + utf8len, (MAX_LABEL_SIZE * sizeof(WCHAR)) - utf8len);
+    if (utf8len > 0) {
+        Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE, &utf8len, ffli->VolumeLabel, vollen);
+        if (!NT_SUCCESS(Status))
+            goto release;
+    } else
+        Status = STATUS_SUCCESS;
+    
+    if (utf8len < MAX_LABEL_SIZE)
+        RtlZeroMemory(Vcb->superblock.label + utf8len, MAX_LABEL_SIZE - utf8len);
     
 //     test_tree_deletion(Vcb); // TESTING
 //     test_tree_splitting(Vcb);
@@ -1266,7 +1305,7 @@ exit:
     return Status;
 }
 
-NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -1275,7 +1314,7 @@ NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, U
     searchkey.obj_type = TYPE_DIR_ITEM;
     searchkey.offset = crc32;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -1319,7 +1358,7 @@ NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, U
                         if ((UINT8*)&di->name[di->n + di->m] - tp.item->data < tp.item->size)
                             RtlCopyMemory(dioff, &di->name[di->n + di->m], tp.item->size - ((UINT8*)&di->name[di->n + di->m] - tp.item->data));
                         
-                        insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL, rollback);
+                        insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL, Irp, rollback);
                     }
                     
                     break;
@@ -1336,7 +1375,7 @@ NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, U
     return STATUS_SUCCESS;
 }
 
-NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     BOOL changed = FALSE;
@@ -1346,7 +1385,7 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN
     searchkey.obj_type = TYPE_INODE_REF;
     searchkey.offset = parinode;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -1400,7 +1439,7 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN
                         if ((UINT8*)&ir->name[ir->n] - tp.item->data < tp.item->size)
                             RtlCopyMemory(iroff, &ir->name[ir->n], tp.item->size - ((UINT8*)&ir->name[ir->n] - tp.item->data));
                         
-                        insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, rollback);
+                        insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, Irp, rollback);
                     }
                     
                     break;
@@ -1431,7 +1470,7 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN
     searchkey.obj_type = TYPE_INODE_EXTREF;
     searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -1485,7 +1524,7 @@ NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UIN
                         if ((UINT8*)&ier->name[ier->n] - tp.item->data < tp.item->size)
                             RtlCopyMemory(ieroff, &ier->name[ier->n], tp.item->size - ((UINT8*)&ier->name[ier->n] - tp.item->data));
                         
-                        insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, rollback);
+                        insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, Irp, rollback);
                     }
                     
                     break;
@@ -1612,7 +1651,7 @@ void send_notification_fcb(file_ref* fileref, ULONG filter_match, ULONG action)
         hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
         file_ref* parfr;
         
-        Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr);
+        Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, NULL);
         
         if (!NT_SUCCESS(Status)) {
             ERR("open_fileref_by_inode returned %08x\n", Status);
@@ -1732,6 +1771,13 @@ void mark_fileref_dirty(file_ref* fileref) {
 void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) {
     LONG rc;
 
+// #ifdef DEBUG    
+//     if (!ExIsResourceAcquiredExclusiveLite(&fcb->Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&fcb->Vcb->tree_lock)) {
+//         ERR("fcb_lock not acquired exclusively\n");
+//         int3;
+//     }
+// #endif
+
     rc = InterlockedDecrement(&fcb->refcount);
     
 #ifdef DEBUG_FCB_REFCOUNTS
@@ -1746,7 +1792,7 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line)
     if (rc > 0)
         return;
     
-    ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
+//     ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
     
     if (fcb->list_entry.Flink)
         RemoveEntryList(&fcb->list_entry);
@@ -1754,7 +1800,7 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line)
     if (fcb->list_entry_all.Flink)
         RemoveEntryList(&fcb->list_entry_all);
     
-    ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
+//     ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
    
     ExDeleteResourceLite(&fcb->nonpaged->resource);
     ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
@@ -1821,6 +1867,13 @@ void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line)
 void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line) {
     LONG rc;
 
+// #ifdef DEBUG    
+//     if (!ExIsResourceAcquiredExclusiveLite(&fr->fcb->Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&fr->fcb->Vcb->tree_lock) && !fr->dirty) {
+//         ERR("fcb_lock not acquired exclusively\n");
+//         int3;
+//     }
+// #endif
+
     rc = InterlockedDecrement(&fr->refcount);
     
 #ifdef DEBUG_FCB_REFCOUNTS
@@ -1915,6 +1968,9 @@ static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObjec
     
     CcUninitializeCacheMap(FileObject, NULL, NULL);
     
+    if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
+        return STATUS_SUCCESS;
+    
     ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
     
     if (fileref)
@@ -1935,9 +1991,9 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) {
     LIST_ENTRY* le;
     LARGE_INTEGER time;
     
-#ifndef __REACTOS__
+    Vcb->removing = TRUE;
+    
     RemoveEntryList(&Vcb->list_entry);
-#endif
     
     Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
     if (!NT_SUCCESS(Status))
@@ -1948,8 +2004,8 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) {
         
         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
 
-        if (Vcb->need_write)
-            do_write(Vcb, &rollback);
+        if (Vcb->need_write && !Vcb->readonly)
+            do_write(Vcb, NULL, &rollback);
         
         free_trees(Vcb);
         
@@ -1969,8 +2025,6 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) {
     
     ExFreePool(Vcb->threads.threads);
     
-    Vcb->removing = TRUE;
-    
     time.QuadPart = 0;
     KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
     KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, FALSE, NULL);
@@ -2027,10 +2081,9 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) {
         if (c->cache)
             free_fcb(c->cache);
         
-        ExDeleteResourceLite(&c->nonpaged->lock);
-        ExDeleteResourceLite(&c->nonpaged->changed_extents_lock);
+        ExDeleteResourceLite(&c->lock);
+        ExDeleteResourceLite(&c->changed_extents_lock);
         
-        ExFreePool(c->nonpaged);
         ExFreePool(c->chunk_item);
         ExFreePool(c);
     }
@@ -2064,7 +2117,7 @@ void STDCALL uninit(device_extension* Vcb, BOOL flush) {
     ZwClose(Vcb->flush_thread_handle);
 }
 
-NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
+NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, PIRP Irp, LIST_ENTRY* rollback) {
     LARGE_INTEGER newlength, time;
     BTRFS_TIME now;
     NTSTATUS Status;
@@ -2103,7 +2156,7 @@ NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY*
                 // excise extents
                 
                 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
-                    Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), rollback);
+                    Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), Irp, rollback);
                     if (!NT_SUCCESS(Status)) {
                         ERR("excise_extents returned %08x\n", Status);
                         ExReleaseResourceLite(fileref->fcb->Header.Resource);
@@ -2265,7 +2318,7 @@ static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
                     
                     ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
                     
-                    Status = delete_fileref(fileref, FileObject, &rollback);
+                    Status = delete_fileref(fileref, FileObject, Irp, &rollback);
                     if (!NT_SUCCESS(Status)) {
                         ERR("delete_fileref returned %08x\n", Status);
                         do_rollback(Vcb, &rollback);
@@ -2319,14 +2372,14 @@ exit2:
     return Status;
 }
 
-ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa) {
+ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa, PIRP Irp) {
     ULONG att;
     char* eaval;
     UINT16 ealen;
     
     // ii can be NULL
     
-    if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen)) {
+    if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen, Irp)) {
         if (ealen > 2) {
             if (eaval[0] == '0' && eaval[1] == 'x') {
                 int i;
@@ -2388,7 +2441,7 @@ ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r
     return att;
 }
 
-NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer) {
+static NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer, BOOL override) {
     IO_STATUS_BLOCK* IoStatus;
     LARGE_INTEGER Offset;
     PIRP Irp;
@@ -2425,9 +2478,13 @@ NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, UL
         goto exit;
     }
     
+    Irp->Flags |= IRP_NOCACHE;
     IrpSp = IoGetNextIrpStackLocation(Irp);
     IrpSp->MajorFunction = IRP_MJ_READ;
     
+    if (override)
+        IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+    
     if (DeviceObject->Flags & DO_BUFFERED_IO) {
         FIXME("FIXME - buffered IO\n");
     } else if (DeviceObject->Flags & DO_DIRECT_IO) {
@@ -2461,20 +2518,10 @@ NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, UL
     
     IoSetCompletionRoutine(Irp, read_completion, context, TRUE, TRUE, TRUE);
 
-//     if (Override)
-//     {
-//         Stack = IoGetNextIrpStackLocation(Irp);
-//         Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
-//     }
-
-//     TRACE("Calling IO Driver... with irp %p\n", Irp);
     Status = IoCallDriver(DeviceObject, Irp);
 
-//     TRACE("Waiting for IO Operation for %p\n", Irp);
     if (Status == STATUS_PENDING) {
-//         TRACE("Operation pending\n");
         KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
-//         TRACE("Getting IO Status... for %p\n", Irp);
         Status = context->iosb.Status;
     }
     
@@ -2512,7 +2559,7 @@ static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT de
         if (i > 0 && superblock_addrs[i] + sizeof(superblock) > length)
             break;
         
-        Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb);
+        Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb, FALSE);
         if (!NT_SUCCESS(Status)) {
             ERR("Failed to read superblock %u: %08x\n", i, Status);
             ExFreePool(sb);
@@ -2646,7 +2693,7 @@ static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr,
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
+static NTSTATUS STDCALL look_for_roots(device_extension* Vcb, PIRP Irp) {
     traverse_ptr tp, next_tp;
     KEY searchkey;
     BOOL b;
@@ -2656,7 +2703,7 @@ static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
     searchkey.obj_type = 0;
     searchkey.offset = 0;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_tree returned %08x\n", Status);
         return Status;
@@ -2681,7 +2728,7 @@ static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
             }
         }
     
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         
         if (b)
             tp = next_tp;
@@ -2690,7 +2737,7 @@ static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
+static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
     BOOL b;
@@ -2703,7 +2750,7 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
     searchkey.obj_type = TYPE_DEV_EXTENT;
     searchkey.offset = 0;
     
-    Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_tree returned %08x\n", Status);
         return Status;
@@ -2730,7 +2777,7 @@ static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
             }
         }
     
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         
         if (b) {
             tp = next_tp;
@@ -2858,7 +2905,7 @@ static void init_device(device_extension* Vcb, device* dev, BOOL get_length) {
     }
 }
 
-static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
+static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb, PIRP Irp) {
     traverse_ptr tp, next_tp;
     KEY searchkey;
     BOOL b;
@@ -2872,7 +2919,7 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
     
     Vcb->data_flags = 0;
     
-    Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -2950,33 +2997,24 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
             if (tp.item->size < sizeof(CHUNK_ITEM)) {
                 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(CHUNK_ITEM));
             } else {            
-                c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
+                c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
                 
                 if (!c) {
                     ERR("out of memory\n");
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
                 
-                c->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk_nonpaged), ALLOC_TAG);
-                
-                if (!c->nonpaged) {
-                    ERR("out of memory\n");
-                    ExFreePool(c);
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-                
                 c->size = tp.item->size;
                 c->offset = tp.item->key.offset;
                 c->used = c->oldused = 0;
                 c->cache = NULL;
                 c->created = FALSE;
                 
-                c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
+                c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, tp.item->size, ALLOC_TAG);
                 
                 if (!c->chunk_item) {
                     ERR("out of memory\n");
                     ExFreePool(c);
-                    ExFreePool(c->nonpaged);
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
             
@@ -2988,12 +3026,11 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
                 if (c->chunk_item->num_stripes > 0) {
                     CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
                     
-                    c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
+                    c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
                     
                     if (!c->devices) {
                         ERR("out of memory\n");
                         ExFreePool(c);
-                        ExFreePool(c->nonpaged);
                         ExFreePool(c->chunk_item);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
@@ -3005,8 +3042,8 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
                 } else
                     c->devices = NULL;
                 
-                ExInitializeResourceLite(&c->nonpaged->lock);
-                ExInitializeResourceLite(&c->nonpaged->changed_extents_lock);
+                ExInitializeResourceLite(&c->lock);
+                ExInitializeResourceLite(&c->changed_extents_lock);
                 
                 InitializeListHead(&c->space);
                 InitializeListHead(&c->space_size);
@@ -3019,7 +3056,7 @@ static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
             }
         }
     
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         
         if (b)
             tp = next_tp;
@@ -3094,7 +3131,7 @@ void protect_superblocks(device_extension* Vcb, chunk* c) {
     }
 }
 
-static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) {
+static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb, PIRP Irp) {
     LIST_ENTRY* le = Vcb->chunks.Flink;
     chunk* c;
     KEY searchkey;
@@ -3113,7 +3150,7 @@ static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) {
         searchkey.obj_id = c->offset;
         searchkey.offset = c->chunk_item->size;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -3141,7 +3178,7 @@ static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) {
         // FIXME - make sure we free occasionally after doing one of these, or we
         // might use up a lot of memory with a big disk.
         
-        Status = load_free_space_cache(Vcb, c);
+        Status = load_free_space_cache(Vcb, c, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("load_free_space_cache returned %08x\n", Status);
             return Status;
@@ -3253,12 +3290,24 @@ static NTSTATUS load_sys_chunks(device_extension* Vcb) {
     return STATUS_SUCCESS;
 }
 
-static root* find_default_subvol(device_extension* Vcb) {
+static root* find_default_subvol(device_extension* Vcb, PIRP Irp) {
     LIST_ENTRY* le;
     
     static char fn[] = "default";
     static UINT32 crc32 = 0x8dbfc2d2;
     
+    if (Vcb->options.subvol_id != 0) {
+        le = Vcb->roots.Flink;
+        while (le != &Vcb->roots) {
+            root* r = CONTAINING_RECORD(le, root, list_entry);
+            
+            if (r->id == Vcb->options.subvol_id)
+                return r;
+            
+            le = le->Flink;
+        }
+    }
+    
     if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
         NTSTATUS Status;
         KEY searchkey;
@@ -3269,7 +3318,7 @@ static root* find_default_subvol(device_extension* Vcb) {
         searchkey.obj_type = TYPE_DIR_ITEM;
         searchkey.offset = crc32;
         
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             goto end;
@@ -3334,7 +3383,7 @@ static NTSTATUS create_worker_threads(PDEVICE_OBJECT DeviceObject) {
     ULONG i;
     NTSTATUS Status;
     
-    Vcb->threads.num_threads = max(3, KeQueryActiveProcessorCount(NULL)); // FIXME - number of processors?
+    Vcb->threads.num_threads = max(3, KeQueryActiveProcessorCount(NULL));
     
     Vcb->threads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_thread) * Vcb->threads.num_threads, ALLOC_TAG);
     if (!Vcb->threads.threads) {
@@ -3366,6 +3415,8 @@ static NTSTATUS create_worker_threads(PDEVICE_OBJECT DeviceObject) {
         }
     }
     
+    Vcb->threads.pending_jobs = 0;
+    
     return STATUS_SUCCESS;
 }
 
@@ -3375,6 +3426,9 @@ BOOL add_thread_job(device_extension* Vcb, PIRP Irp) {
     
     threadnum = InterlockedIncrement(&Vcb->threads.next_thread) % Vcb->threads.num_threads;
     
+    if (Vcb->threads.pending_jobs >= Vcb->threads.num_threads)
+        return FALSE;
+    
     if (Vcb->threads.threads[threadnum].quit)
         return FALSE;
     
@@ -3388,6 +3442,8 @@ BOOL add_thread_job(device_extension* Vcb, PIRP Irp) {
     
     tj->Irp = Irp;
     
+    InterlockedIncrement(&Vcb->threads.pending_jobs);
+    
     ExInterlockedInsertTailList(&Vcb->threads.threads[threadnum].jobs, &tj->list_entry, &Vcb->threads.threads[threadnum].spin_lock);
     KeSetEvent(&Vcb->threads.threads[threadnum].event, 0, FALSE);
     
@@ -3527,7 +3583,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         TRACE("btrfs magic found\n");
     }
     
-    Status = registry_load_volume_options(&Vcb->superblock.uuid, &Vcb->options);
+    Status = registry_load_volume_options(Vcb);
     if (!NT_SUCCESS(Status)) {
         ERR("registry_load_volume_options returned %08x\n", Status);
         goto exit;
@@ -3564,10 +3620,13 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         Vcb->readonly = TRUE;
     }
     
+    if (Vcb->options.readonly)
+        Vcb->readonly = TRUE;
+    
     Vcb->superblock.generation++;
     Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF;
     
-    Vcb->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG);
+    Vcb->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG);
     if (!Vcb->devices) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -3595,8 +3654,6 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     
     Vcb->log_to_phys_loaded = FALSE;
     
-    Vcb->max_inline = Vcb->superblock.node_size / 2;
-
     add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, NULL);
     
     if (!Vcb->chunk_root) {
@@ -3629,7 +3686,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
 
     FsRtlNotifyInitializeSync(&Vcb->NotifySync);
     
-    Status = load_chunk_root(Vcb);
+    Status = load_chunk_root(Vcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("load_chunk_root returned %08x\n", Status);
         goto exit;
@@ -3663,29 +3720,31 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         goto exit;
     }
     
-    Status = look_for_roots(Vcb);
+    Status = look_for_roots(Vcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("look_for_roots returned %08x\n", Status);
         goto exit;
     }
     
-    Status = find_chunk_usage(Vcb);
-    if (!NT_SUCCESS(Status)) {
-        ERR("find_chunk_usage returned %08x\n", Status);
-        goto exit;
+    if (!Vcb->readonly) {
+        Status = find_chunk_usage(Vcb, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("find_chunk_usage returned %08x\n", Status);
+            goto exit;
+        }
     }
     
     // We've already increased the generation by one
     if (!Vcb->readonly && Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation) {
         WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb->superblock.generation - 1, Vcb->superblock.cache_generation);
-        Status = clear_free_space_cache(Vcb);
+        Status = clear_free_space_cache(Vcb, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("clear_free_space_cache returned %08x\n", Status);
             goto exit;
         }
     }
     
-    Vcb->volume_fcb = create_fcb();
+    Vcb->volume_fcb = create_fcb(NonPagedPool);
     if (!Vcb->volume_fcb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -3695,7 +3754,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     Vcb->volume_fcb->Vcb = Vcb;
     Vcb->volume_fcb->sd = NULL;
     
-    root_fcb = create_fcb();
+    root_fcb = create_fcb(NonPagedPool);
     if (!root_fcb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -3711,7 +3770,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     WARN("root FCB = %p\n", root_fcb);
 #endif
     
-    root_fcb->subvol = find_default_subvol(Vcb);
+    root_fcb->subvol = find_default_subvol(Vcb, Irp);
 
     if (!root_fcb->subvol) {
         ERR("could not find top subvol\n");
@@ -3723,7 +3782,7 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     searchkey.obj_type = TYPE_INODE_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto exit;
@@ -3738,9 +3797,9 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     if (tp.item->size > 0)
         RtlCopyMemory(&root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
     
-    fcb_get_sd(root_fcb, NULL);
+    fcb_get_sd(root_fcb, NULL, Irp);
     
-    root_fcb->atts = get_file_attributes(Vcb, &root_fcb->inode_item, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE);
+    root_fcb->atts = get_file_attributes(Vcb, &root_fcb->inode_item, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE, Irp);
     
     Vcb->root_fileref = create_fileref();
     if (!Vcb->root_fileref) {
@@ -3764,18 +3823,13 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     
     Vcb->root_file = IoCreateStreamFileObject(NULL, DeviceToMount);
     Vcb->root_file->FsContext = root_fcb;
-#ifdef __REACTOS__
     Vcb->root_file->SectionObjectPointer = &root_fcb->nonpaged->segment_object;
     Vcb->root_file->Vpb = DeviceObject->Vpb;
-#endif
     
     RtlZeroMemory(root_ccb, sizeof(ccb));
     root_ccb->NodeType = BTRFS_NODE_TYPE_CCB;
     root_ccb->NodeSize = sizeof(ccb);
     
-#ifndef __REACTOS__
-    Vcb->root_file->FsContext = root_ccb;
-#else
     Vcb->root_file->FsContext2 = root_ccb;
     
     _SEH2_TRY {
@@ -3784,10 +3838,9 @@ static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         Status = _SEH2_GetExceptionCode();
         goto exit;
     } _SEH2_END;
-#endif
     
     for (i = 0; i < Vcb->superblock.num_devices; i++) {
-        Status = find_disk_holes(Vcb, &Vcb->devices[i]);
+        Status = find_disk_holes(Vcb, &Vcb->devices[i], Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("find_disk_holes returned %08x\n", Status);
             goto exit;
@@ -3867,6 +3920,92 @@ exit:
     return Status;
 }
 
+static NTSTATUS verify_volume(PDEVICE_OBJECT device) {
+    device_extension* Vcb = device->DeviceExtension;
+    ULONG cc, to_read;
+    IO_STATUS_BLOCK iosb;
+    NTSTATUS Status;
+    superblock* sb;
+    UINT32 crc32;
+    UINT64 i;
+    
+    if (Vcb->removing)
+        return STATUS_WRONG_VOLUME;
+    
+    Status = dev_ioctl(Vcb->Vpb->RealDevice, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+    
+    if (!NT_SUCCESS(Status)) {
+        ERR("dev_ioctl returned %08x\n", Status);
+        return Status;
+    }
+    
+    to_read = sector_align(sizeof(superblock), device->SectorSize);
+    
+    sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+    if (!sb) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    Status = sync_read_phys(Vcb->Vpb->RealDevice, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("Failed to read superblock: %08x\n", Status);
+        ExFreePool(sb);
+        return Status;
+    }
+    
+    if (sb->magic != BTRFS_MAGIC) {
+        ERR("not a BTRFS volume\n");
+        ExFreePool(sb);
+        return STATUS_WRONG_VOLUME;
+    }
+    
+    if (RtlCompareMemory(&sb->uuid, &Vcb->superblock.uuid, sizeof(BTRFS_UUID)) != sizeof(BTRFS_UUID)) {
+        ERR("different UUIDs\n");
+        ExFreePool(sb);
+        return STATUS_WRONG_VOLUME;
+    }
+    
+    crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+    TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
+    
+    if (crc32 != *((UINT32*)sb->checksum)) {
+        ERR("different UUIDs\n");
+        ExFreePool(sb);
+        return STATUS_WRONG_VOLUME;
+    }
+    
+    ExFreePool(sb);
+    
+    for (i = 0; i < Vcb->superblock.num_devices; i++) {
+        if (Vcb->devices[i].removable) {
+            NTSTATUS Status;
+            ULONG cc;
+            IO_STATUS_BLOCK iosb;
+            
+            Status = dev_ioctl(Vcb->devices[i].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("dev_ioctl returned %08x\n", Status);
+                return Status;
+            }
+            
+            if (iosb.Information < sizeof(ULONG)) {
+                ERR("iosb.Information was too short\n");
+                return STATUS_INTERNAL_ERROR;
+            }
+            
+            Vcb->devices[i].change_count = cc;
+        }
+        
+        Vcb->devices[i].devobj->Flags &= ~DO_VERIFY_VOLUME;
+    }
+    
+    Vcb->Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
+    
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     PIO_STACK_LOCATION IrpSp;
     NTSTATUS Status;
@@ -3910,7 +4049,15 @@ static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject,
             break;
             
         case IRP_MN_VERIFY_VOLUME:
-            FIXME("STUB: IRP_MN_VERIFY_VOLUME\n");
+            TRACE("IRP_MN_VERIFY_VOLUME\n");
+            
+            Status = verify_volume(DeviceObject);
+            
+            if (!NT_SUCCESS(Status) && Vcb->Vpb->Flags & VPB_MOUNTED) {
+                uninit(Vcb, FALSE);
+//                 Vcb->Vpb->Flags &= ~VPB_MOUNTED;
+            }
+            
             break;
            
         default:
@@ -4136,8 +4283,7 @@ static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp
     Status = STATUS_SUCCESS;
 
     while (!IsListEmpty(&VcbList)) {
-        LIST_ENTRY* le = RemoveHeadList(&VcbList);
-        Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
+        Vcb = CONTAINING_RECORD(VcbList.Flink, device_extension, list_entry);
         
         TRACE("shutting down Vcb %p\n", Vcb);
         
index 3246f5d..4d7d89e 100644 (file)
@@ -75,7 +75,7 @@ BEGIN
             VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016"
             VALUE "OriginalFilename", "btrfs.sys"
             VALUE "ProductName", "WinBtrfs"
-            VALUE "ProductVersion", "0.5"
+            VALUE "ProductVersion", "0.6"
         END
     END
     BLOCK "VarFileInfo"
index d0b62fa..1b01d92 100644 (file)
@@ -56,6 +56,7 @@
 #define BTRFS_NODE_TYPE_FCB 0x2296
 
 #define ALLOC_TAG 0x7442484D //'MHBt'
+#define ALLOC_TAG_ZLIB 0x7A42484D //'MHBz'
 
 #define STDCALL __stdcall
 
 #define EA_REPARSE "system.reparse"
 #define EA_REPARSE_HASH 0x786f6167
 
-#define READ_AHEAD_GRANULARITY 0x10000 // 64 KB
-
 #define MAX_EXTENT_SIZE 0x8000000 // 128 MB
+#define COMPRESSED_EXTENT_SIZE 0x20000 // 128 KB
+
+#define READ_AHEAD_GRANULARITY COMPRESSED_EXTENT_SIZE // really ought to be a multiple of COMPRESSED_EXTENT_SIZE
 
 #ifdef _MSC_VER
 #define try __try
@@ -87,7 +89,7 @@
 
 // #pragma pack(push, 1)
 
-struct device_extension;
+struct _device_extension;
 
 typedef struct {
     BTRFS_UUID fsuuid;
@@ -176,6 +178,7 @@ typedef struct _fcb {
     
     BOOL ads;
     UINT32 adshash;
+    ULONG adsmaxlen;
     ANSI_STRING adsxattr;
     ANSI_STRING adsdata;
     
@@ -344,11 +347,6 @@ typedef struct {
     LIST_ENTRY space;
 } device;
 
-typedef struct {
-    ERESOURCE lock;
-    ERESOURCE changed_extents_lock;
-} chunk_nonpaged;
-
 typedef struct {
     CHUNK_ITEM* chunk_item;
     UINT32 size;
@@ -361,7 +359,8 @@ typedef struct {
     LIST_ENTRY space_size;
     LIST_ENTRY deleting;
     LIST_ENTRY changed_extents;
-    chunk_nonpaged* nonpaged;
+    ERESOURCE lock;
+    ERESOURCE changed_extents_lock;
     BOOL created;
     
     LIST_ENTRY list_entry;
@@ -424,10 +423,19 @@ typedef struct {
     ULONG num_threads;
     LONG next_thread;
     drv_thread* threads;
+    LONG pending_jobs;
 } drv_threads;
 
 typedef struct {
     BOOL ignore;
+    BOOL compress;
+    BOOL compress_force;
+    UINT8 compress_type;
+    BOOL readonly;
+    UINT32 zlib_level;
+    UINT32 flush_interval;
+    UINT32 max_inline;
+    UINT64 subvol_id;
 } mount_options;
 
 #define VCB_TYPE_VOLUME     1
@@ -471,7 +479,6 @@ typedef struct _device_extension {
     root* dev_root;
     root* uuid_root;
     BOOL log_to_phys_loaded;
-    UINT32 max_inline;
     LIST_ENTRY sys_chunks;
     LIST_ENTRY chunks;
     LIST_ENTRY chunks_changed;
@@ -528,10 +535,10 @@ enum write_data_status {
     WriteDataStatus_Ignore
 };
 
-struct write_data_context;
+struct _write_data_context;
 
 typedef struct {
-    struct write_data_context* context;
+    struct _write_data_context* context;
     UINT8* buf;
     BOOL need_free;
     device* device;
@@ -541,7 +548,7 @@ typedef struct {
     LIST_ENTRY list_entry;
 } write_data_stripe;
 
-typedef struct {
+typedef struct _write_data_context {
     KEVENT Event;
     LIST_ENTRY stripes;
     LONG stripes_left;
@@ -596,7 +603,7 @@ static __inline void get_raid0_offset(UINT64 off, UINT64 stripe_length, UINT16 n
     startoff = off % (num_stripes * stripe_length);
     initoff = (off / (num_stripes * stripe_length)) * stripe_length;
     
-    *stripe = startoff / stripe_length;
+    *stripe = (UINT16)(startoff / stripe_length);
     *stripeoff = initoff + startoff - (*stripe * stripe_length);
 }
 
@@ -604,19 +611,19 @@ static __inline void get_raid0_offset(UINT64 off, UINT64 stripe_length, UINT16 n
 device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid);
 UINT64 sector_align( UINT64 NumberToBeAligned, UINT64 Alignment );
 int keycmp(const KEY* key1, const KEY* key2);
-ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa);
-BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen);
+ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa, PIRP Irp);
+BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen, PIRP Irp);
 void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line);
 void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line);
-BOOL STDCALL get_last_inode(device_extension* Vcb, root* r);
-NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback);
-NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback);
-NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback);
-fcb* create_fcb();
+BOOL STDCALL get_last_inode(device_extension* Vcb, root* r, PIRP Irp);
+NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback);
+fcb* create_fcb(POOL_TYPE pool_type);
 file_ref* create_fileref();
 void protect_superblocks(device_extension* Vcb, chunk* c);
 BOOL is_top_level(PIRP Irp);
-NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, LIST_ENTRY* rollback);
+NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, PIRP Irp, LIST_ENTRY* rollback);
 void STDCALL uninit(device_extension* Vcb, BOOL flush);
 NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer,
                            ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override, IO_STATUS_BLOCK* iosb);
@@ -629,7 +636,7 @@ BOOL add_thread_job(device_extension* Vcb, PIRP Irp);
 NTSTATUS part0_passthrough(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 void mark_fcb_dirty(fcb* fcb);
 void mark_fileref_dirty(file_ref* fileref);
-NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback);
+NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, PIRP Irp, LIST_ENTRY* rollback);
 
 #ifdef _MSC_VER
 #define funcname __FUNCTION__
@@ -641,6 +648,13 @@ NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY*
 #define free_fcb(fcb) _free_fcb(fcb, funcname, __FILE__, __LINE__)
 #define free_fileref(fileref) _free_fileref(fileref, funcname, __FILE__, __LINE__)
 
+extern UINT32 mount_compress;
+extern UINT32 mount_compress_force;
+extern UINT32 mount_compress_type;
+extern UINT32 mount_zlib_level;
+extern UINT32 mount_flush_interval;
+extern UINT32 mount_max_inline;
+
 #ifdef _DEBUG
 
 extern BOOL log_started;
@@ -696,34 +710,50 @@ void STDCALL init_fast_io_dispatch(FAST_IO_DISPATCH** fiod);
 // in crc32c.c
 UINT32 STDCALL calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen);
 
+typedef struct {
+    LIST_ENTRY* list;
+    LIST_ENTRY* list_size;
+    UINT64 address;
+    UINT64 length;
+    chunk* chunk;
+} rollback_space;
+
+typedef struct {
+    fcb* fcb;
+    extent* ext;
+} rollback_extent;
+
 enum rollback_type {
     ROLLBACK_INSERT_ITEM,
     ROLLBACK_DELETE_ITEM,
     ROLLBACK_INSERT_EXTENT,
-    ROLLBACK_DELETE_EXTENT
+    ROLLBACK_DELETE_EXTENT,
+    ROLLBACK_ADD_SPACE,
+    ROLLBACK_SUBTRACT_SPACE
 };
 
 // in treefuncs.c
-NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, const char* func, const char* file, unsigned int line);
-BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
-BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, const char* func, const char* file, unsigned int line);
+NTSTATUS STDCALL _find_item(device_extension* Vcb, root* r, traverse_ptr* tp, const KEY* searchkey, BOOL ignore, PIRP Irp, const char* func, const char* file, unsigned int line);
+BOOL STDCALL _find_next_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* next_tp, BOOL ignore, PIRP Irp, const char* func, const char* file, unsigned int line);
+BOOL STDCALL _find_prev_item(device_extension* Vcb, const traverse_ptr* tp, traverse_ptr* prev_tp, BOOL ignore, PIRP Irp, const char* func, const char* file, unsigned int line);
 void STDCALL free_trees(device_extension* Vcb);
-BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp, LIST_ENTRY* rollback);
+BOOL STDCALL insert_tree_item(device_extension* Vcb, root* r, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, UINT32 size, traverse_ptr* ptp, PIRP Irp, LIST_ENTRY* rollback);
 void STDCALL delete_tree_item(device_extension* Vcb, traverse_ptr* tp, LIST_ENTRY* rollback);
 tree* STDCALL _free_tree(tree* t, const char* func, const char* file, unsigned int line);
-NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, const char* func, const char* file, unsigned int line);
-NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, const char* func, const char* file, unsigned int line);
+NTSTATUS STDCALL _load_tree(device_extension* Vcb, UINT64 addr, root* r, tree** pt, tree* parent, PIRP Irp, const char* func, const char* file, unsigned int line);
+NTSTATUS STDCALL _do_load_tree(device_extension* Vcb, tree_holder* th, root* r, tree* t, tree_data* td, BOOL* loaded, PIRP Irp,
+                               const char* func, const char* file, unsigned int line);
 void clear_rollback(LIST_ENTRY* rollback);
 void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback);
 void free_trees_root(device_extension* Vcb, root* r);
 void add_rollback(LIST_ENTRY* rollback, enum rollback_type type, void* ptr);
 
-#define find_item(Vcb, r, tp, searchkey, ignore) _find_item(Vcb, r, tp, searchkey, ignore, funcname, __FILE__, __LINE__)
-#define find_next_item(Vcb, tp, next_tp, ignore) _find_next_item(Vcb, tp, next_tp, ignore, funcname, __FILE__, __LINE__)
-#define find_prev_item(Vcb, tp, prev_tp, ignore) _find_prev_item(Vcb, tp, prev_tp, ignore, funcname, __FILE__, __LINE__)
+#define find_item(Vcb, r, tp, searchkey, ignore, Irp) _find_item(Vcb, r, tp, searchkey, ignore, Irp, funcname, __FILE__, __LINE__)
+#define find_next_item(Vcb, tp, next_tp, ignore, Irp) _find_next_item(Vcb, tp, next_tp, ignore, Irp, funcname, __FILE__, __LINE__)
+#define find_prev_item(Vcb, tp, prev_tp, ignore, Irp) _find_prev_item(Vcb, tp, prev_tp, ignore, Irp, funcname, __FILE__, __LINE__)
 #define free_tree(t) _free_tree(t, funcname, __FILE__, __LINE__)
-#define load_tree(t, addr, r, pt, parent) _load_tree(t, addr, r, pt, parent, funcname, __FILE__, __LINE__)
-#define do_load_tree(Vcb, th, r, t, td, loaded) _do_load_tree(Vcb, th, r, t, td, loaded, funcname, __FILE__, __LINE__)  
+#define load_tree(t, addr, r, pt, parent, Irp) _load_tree(t, addr, r, pt, parent, Irp, funcname, __FILE__, __LINE__)
+#define do_load_tree(Vcb, th, r, t, td, loaded, Irp) _do_load_tree(Vcb, th, r, t, td, loaded, Irp, funcname, __FILE__, __LINE__)  
 
 // in search.c
 void STDCALL look_for_vols(PDRIVER_OBJECT DriverObject, LIST_ENTRY* volumes);
@@ -734,51 +764,52 @@ void STDCALL free_cache();
 extern CACHE_MANAGER_CALLBACKS* cache_callbacks;
 
 // in write.c
-NTSTATUS STDCALL do_write(device_extension* Vcb, LIST_ENTRY* rollback);
+NTSTATUS STDCALL do_write(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback);
 NTSTATUS write_file(device_extension* Vcb, PIRP Irp, BOOL wait, BOOL deferred_write);
 NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache,
                      BOOL wait, BOOL deferred_write, LIST_ENTRY* rollback);
-NTSTATUS truncate_file(fcb* fcb, UINT64 end, LIST_ENTRY* rollback);
+NTSTATUS truncate_file(fcb* fcb, UINT64 end, PIRP Irp, LIST_ENTRY* rollback);
 NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIRP Irp, LIST_ENTRY* rollback);
-NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, LIST_ENTRY* rollback);
+NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, PIRP Irp, LIST_ENTRY* rollback);
 void commit_checksum_changes(device_extension* Vcb, LIST_ENTRY* changed_sector_list);
-NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback);
 chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address);
-chunk* alloc_chunk(device_extension* Vcb, UINT64 flags, LIST_ENTRY* rollback);
-NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp);
-NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp);
+chunk* alloc_chunk(device_extension* Vcb, UINT64 flags);
+NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp, chunk* c);
+NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp, chunk* c);
 void free_write_data_stripes(write_data_context* wtc);
-NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, LIST_ENTRY* rollback);
+NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback);
 NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-void flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* rollback);
+void flush_fcb(fcb* fcb, BOOL cache, PIRP Irp, LIST_ENTRY* rollback);
 BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, BOOL prealloc, void* data, LIST_ENTRY* changed_sector_list,
-                         PIRP Irp, LIST_ENTRY* rollback);
-NTSTATUS do_nocow_write(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
+                         PIRP Irp, LIST_ENTRY* rollback, UINT8 compression, UINT64 decoded_size);
 NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
-void remove_fcb_extent(extent* ext, LIST_ENTRY* rollback);
 NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset,
-                                   signed long long count, BOOL no_csum, UINT64 new_size);
+                                   signed long long count, BOOL no_csum, UINT64 new_size, PIRP Irp);
+NTSTATUS do_write_file(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS write_compressed(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
 
 // in dirctrl.c
 NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
+ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts, PIRP Irp);
 
 // in security.c
 NTSTATUS STDCALL drv_query_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 NTSTATUS STDCALL drv_set_security(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-void fcb_get_sd(fcb* fcb, struct _fcb* parent);
+void fcb_get_sd(fcb* fcb, struct _fcb* parent, PIRP Irp);
 // UINT32 STDCALL get_uid();
 void add_user_mapping(WCHAR* sidstring, ULONG sidstringlength, UINT32 uid);
 UINT32 sid_to_uid(PSID sid);
+void uid_to_sid(UINT32 uid, PSID* sid);
 NTSTATUS fcb_get_new_sd(fcb* fcb, file_ref* parfileref, ACCESS_STATE* as);
 
 // in fileinfo.c
 NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 NTSTATUS STDCALL drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback);
+NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback);
 BOOL has_open_children(file_ref* fileref);
 NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT64 end, fcb* fcb, file_ref* fileref, PFILE_OBJECT FileObject, BOOL advance_only, LIST_ENTRY* rollback);
 NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset);
-NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr);
+NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp);
 
 // in reparse.c
 NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void* buffer, DWORD buflen, DWORD* retlen);
@@ -787,15 +818,14 @@ NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp);
 
 // in create.c
 NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
-NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr, root** subvol,
-                                             UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8);
 NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, file_ref* fr,
-                                  root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8);
-NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed);
-NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb);
-NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr, UINT32 streamhash, fcb* parent, fcb** pfcb);
+                                  root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8, PIRP Irp);
+NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed, ULONG* fn_offset, PIRP Irp);
+NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, PIRP Irp);
+NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr, UINT32 streamhash, fcb* parent, fcb** pfcb, PIRP Irp);
 void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock);
-NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index);
+NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index, PIRP Irp);
+NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp);
 
 // in fsctl.c
 NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP Irp, UINT32 type, BOOL user);
@@ -814,29 +844,29 @@ NTSTATUS do_read(PIRP Irp, BOOL wait, ULONG* bytes_read);
 NTSTATUS STDCALL drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp);
 
 // in free-space.c
-NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c);
-NTSTATUS clear_free_space_cache(device_extension* Vcb);
-NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollback);
-NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback);
+NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp);
+NTSTATUS clear_free_space_cache(device_extension* Vcb, PIRP Irp);
+NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS update_chunk_caches(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback);
 NTSTATUS add_space_entry(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 offset, UINT64 size);
 void _space_list_add(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
-void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
+void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func);
 void _space_list_subtract(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
-void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func);
+void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func);
 
 #define space_list_add(Vcb, c, deleting, address, length, rollback) _space_list_add(Vcb, c, deleting, address, length, rollback, funcname)
-#define space_list_add2(list, list_size, address, length, rollback) _space_list_add2(list, list_size, address, length, rollback, funcname)
+#define space_list_add2(list, list_size, address, length, rollback) _space_list_add2(list, list_size, address, length, NULL, rollback, funcname)
 #define space_list_subtract(Vcb, c, deleting, address, length, rollback) _space_list_subtract(Vcb, c, deleting, address, length, rollback, funcname)
-#define space_list_subtract2(list, list_size, address, length, rollback) _space_list_subtract2(list, list_size, address, length, rollback, funcname)
+#define space_list_subtract2(list, list_size, address, length, rollback) _space_list_subtract2(list, list_size, address, length, NULL, rollback, funcname)
 
 // in extent-tree.c
-NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback);
-NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback);
-NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, LIST_ENTRY* rollback);
-NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, LIST_ENTRY* rollback);
+NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, PIRP Irp, LIST_ENTRY* rollback);
 void decrease_chunk_usage(chunk* c, UINT64 delta);
-NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback);
-UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset);
+NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp, LIST_ENTRY* rollback);
+UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, PIRP Irp);
 
 // in worker-thread.c
 void STDCALL worker_thread(void* context);
@@ -847,7 +877,11 @@ void do_write_job(device_extension* Vcb, PIRP Irp);
 void STDCALL read_registry(PUNICODE_STRING regpath);
 NTSTATUS registry_mark_volume_mounted(BTRFS_UUID* uuid);
 NTSTATUS registry_mark_volume_unmounted(BTRFS_UUID* uuid);
-NTSTATUS registry_load_volume_options(BTRFS_UUID* uuid, mount_options* options);
+NTSTATUS registry_load_volume_options(device_extension* Vcb);
+
+// in compress.c
+NTSTATUS decompress(UINT8 type, UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen);
+NTSTATUS write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback);
 
 #define fast_io_possible(fcb) (!FsRtlAreThereCurrentFileLocks(&fcb->lock) && !fcb->Vcb->readonly ? FastIoIsPossible : FastIoIsQuestionable)
 
@@ -874,6 +908,23 @@ static __inline void InsertAfter(LIST_ENTRY* head, LIST_ENTRY* item, LIST_ENTRY*
         head->Blink = item;
 }
 
+static __inline BOOL write_fcb_compressed(fcb* fcb) {
+    // make sure we don't accidentally write the cache inodes or pagefile compressed
+    if (fcb->subvol->id == BTRFS_ROOT_ROOT || fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE)
+        return FALSE;
+    
+    if (fcb->Vcb->options.compress_force)
+        return TRUE;
+    
+    if (fcb->inode_item.flags & BTRFS_INODE_NOCOMPRESS)
+        return FALSE;
+    
+    if (fcb->inode_item.flags & BTRFS_INODE_COMPRESS || fcb->Vcb->options.compress)
+        return TRUE;
+    
+    return FALSE;
+}
+
 #ifdef DEBUG_FCB_REFCOUNTS
 #ifdef DEBUG_LONG_MESSAGES
 #define increase_fileref_refcount(fileref) {\
index 3071431..bc35ad0 100644 (file)
@@ -1,9 +1,13 @@
+// No copyright claimed in this file - do what you want with it.
+
 #ifndef BTRFSIOCTL_H_DEFINED
 #define BTRFSIOCTL_H_DEFINED
 
 #define FSCTL_BTRFS_GET_FILE_IDS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x829, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
 #define FSCTL_BTRFS_CREATE_SUBVOL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82a, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
 #define FSCTL_BTRFS_CREATE_SNAPSHOT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82b, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
+#define FSCTL_BTRFS_GET_INODE_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82c, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+#define FSCTL_BTRFS_SET_INODE_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x82d, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
 
 typedef struct {
     UINT64 subvol;
@@ -17,4 +21,29 @@ typedef struct {
     WCHAR name[1];
 } btrfs_create_snapshot;
 
+typedef struct {
+    UINT64 subvol;
+    UINT64 inode;
+    BOOL top;
+    UINT8 type;
+    UINT32 st_uid;
+    UINT32 st_gid;
+    UINT32 st_mode;
+    UINT64 st_rdev;
+    UINT64 flags;
+    UINT32 inline_length;
+    UINT64 disk_size[3];
+} btrfs_inode_info;
+
+typedef struct {
+    UINT64 flags;
+    BOOL flags_changed;
+    UINT32 st_uid;
+    BOOL uid_changed;
+    UINT32 st_gid;
+    BOOL gid_changed;
+    UINT32 st_mode;
+    BOOL mode_changed;
+} btrfs_set_inode_info;
+
 #endif
diff --git a/reactos/drivers/filesystems/btrfs/compress.c b/reactos/drivers/filesystems/btrfs/compress.c
new file mode 100755 (executable)
index 0000000..7b7a00a
--- /dev/null
@@ -0,0 +1,898 @@
+/* Copyright (c) Mark Harmstone 2016
+ * Copyright (c) Reimar Doeffinger 2006
+ * Copyright (c) Markus Oberhumer 1996
+ * 
+ * This file is part of WinBtrfs.
+ * 
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ * 
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public Licence for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
+// Portion of the LZO decompression code here were cribbed from code in
+// libavcodec, also under the LGPL. Thank you, Reimar Doeffinger.
+
+// The LZO compression code comes from v0.22 of lzo, written way back in
+// 1996, and available here:
+// https://www.ibiblio.org/pub/historic-linux/ftp-archives/sunsite.unc.edu/Sep-29-1996/libs/lzo-0.22.tar.gz
+// Modern versions of lzo are licensed under the GPL, but the very oldest
+// versions are under the LGPL and hence okay to use here.
+
+#include "btrfs_drv.h"
+
+#define Z_SOLO
+#define ZLIB_INTERNAL
+
+#ifndef __REACTOS__
+#include "zlib/zlib.h"
+#include "zlib/inftrees.h"
+#include "zlib/inflate.h"
+#else
+#include <zlib.h>
+#endif
+
+#define LINUX_PAGE_SIZE 4096
+
+typedef struct {
+    UINT8* in;
+    UINT32 inlen;
+    UINT32 inpos;
+    UINT8* out;
+    UINT32 outlen;
+    UINT32 outpos;
+    BOOL error;
+    void* wrkmem;
+} lzo_stream;
+
+#define LZO1X_MEM_COMPRESS ((UINT32) (16384L * sizeof(UINT8*)))
+
+#define M1_MAX_OFFSET 0x0400
+#define M2_MAX_OFFSET 0x0800
+#define M3_MAX_OFFSET 0x4000
+#define M4_MAX_OFFSET 0xbfff
+
+#define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET)
+
+#define M1_MARKER 0
+#define M2_MARKER 64
+#define M3_MARKER 32
+#define M4_MARKER 16
+
+#define _DV2(p, shift1, shift2) (((( (UINT32)(p[2]) << shift1) ^ p[1]) << shift2) ^ p[0])
+#define DVAL_NEXT(dv, p) dv ^= p[-1]; dv = (((dv) >> 5) ^ ((UINT32)(p[2]) << (2*5)))
+#define _DV(p, shift) _DV2(p, shift, shift)
+#define DVAL_FIRST(dv, p) dv = _DV((p), 5)
+#define _DINDEX(dv, p) ((40799u * (dv)) >> 5)
+#define DINDEX(dv, p) (((_DINDEX(dv, p)) & 0x3fff) << 0)
+#define UPDATE_D(dict, cycle, dv, p) dict[DINDEX(dv, p)] = (p)
+#define UPDATE_I(dict, cycle, index, p) dict[index] = (p)
+
+#define LZO_CHECK_MPOS_NON_DET(m_pos, m_off, in, ip, max_offset) \
+    ((void*) m_pos < (void*) in || \
+    (m_off = (UINT8*) ip - (UINT8*) m_pos) <= 0 || \
+    m_off > max_offset)
+        
+#define LZO_BYTE(x) ((unsigned char) (x))
+
+static UINT8 lzo_nextbyte(lzo_stream* stream) {
+    UINT8 c;
+    
+    if (stream->inpos >= stream->inlen) {
+        stream->error = TRUE;
+        return 0;
+    }
+    
+    c = stream->in[stream->inpos];
+    stream->inpos++;
+    
+    return c;
+}
+
+static int lzo_len(lzo_stream* stream, int byte, int mask) {
+    int len = byte & mask;
+    
+    if (len == 0) {
+        while (!(byte = lzo_nextbyte(stream))) {
+            if (stream->error) return 0;
+            
+            len += 255;
+        }
+        
+        len += mask + byte;
+    }
+    
+    return len;
+}
+
+static void lzo_copy(lzo_stream* stream, int len) {
+    if (stream->inpos + len > stream->inlen) {
+        stream->error = TRUE;
+        return;
+    }
+    
+    if (stream->outpos + len > stream->outlen) {
+        stream->error = TRUE;
+        return;
+    }
+    
+    do {
+        stream->out[stream->outpos] = stream->in[stream->inpos];
+        stream->inpos++;
+        stream->outpos++;
+        len--;
+    } while (len > 0);
+}
+
+static void lzo_copyback(lzo_stream* stream, int back, int len) {
+    if (stream->outpos < back) {
+        stream->error = TRUE;
+        return;
+    }
+    
+    if (stream->outpos + len > stream->outlen) {
+        stream->error = TRUE;
+        return;
+    }
+    
+    do {
+        stream->out[stream->outpos] = stream->out[stream->outpos - back];
+        stream->outpos++;
+        len--;
+    } while (len > 0);
+}
+
+static NTSTATUS do_lzo_decompress(lzo_stream* stream) {
+    UINT8 byte;
+    UINT32 len, back;
+    BOOL backcopy = FALSE;
+    
+    stream->error = FALSE;
+    
+    byte = lzo_nextbyte(stream);
+    if (stream->error) return STATUS_INTERNAL_ERROR;
+    
+    if (byte > 17) {
+        lzo_copy(stream, byte - 17);
+        if (stream->error) return STATUS_INTERNAL_ERROR;
+        
+        byte = lzo_nextbyte(stream);
+        if (stream->error) return STATUS_INTERNAL_ERROR;
+        
+        if (byte < 16) return STATUS_INTERNAL_ERROR;
+    }
+    
+    while (1) {
+        if (byte >> 4) {
+            backcopy = TRUE;
+            if (byte >> 6) {
+                len = (byte >> 5) - 1;
+                back = (lzo_nextbyte(stream) << 3) + ((byte >> 2) & 7) + 1;
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+            } else if (byte >> 5) {
+                len = lzo_len(stream, byte, 31);
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+                
+                byte = lzo_nextbyte(stream);
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+                
+                back = (lzo_nextbyte(stream) << 6) + (byte >> 2) + 1;
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+            } else {
+                len = lzo_len(stream, byte, 7);
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+                
+                back = (1 << 14) + ((byte & 8) << 11);
+                
+                byte = lzo_nextbyte(stream);
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+                
+                back += (lzo_nextbyte(stream) << 6) + (byte >> 2);
+                if (stream->error) return STATUS_INTERNAL_ERROR;
+                
+                if (back == (1 << 14)) {
+                    if (len != 1)
+                        return STATUS_INTERNAL_ERROR;
+                    break;
+                }
+            }
+        } else if (backcopy) {
+            len = 0;
+            back = (lzo_nextbyte(stream) << 2) + (byte >> 2) + 1;
+            if (stream->error) return STATUS_INTERNAL_ERROR;
+        } else {
+            len = lzo_len(stream, byte, 15);
+            if (stream->error) return STATUS_INTERNAL_ERROR;
+            
+            lzo_copy(stream, len + 3);
+            if (stream->error) return STATUS_INTERNAL_ERROR;
+            
+            byte = lzo_nextbyte(stream);
+            if (stream->error) return STATUS_INTERNAL_ERROR;
+            
+            if (byte >> 4)
+                continue;
+            
+            len = 1;
+            back = (1 << 11) + (lzo_nextbyte(stream) << 2) + (byte >> 2) + 1;
+            if (stream->error) return STATUS_INTERNAL_ERROR;
+            
+            break;
+        }
+        
+        lzo_copyback(stream, back, len + 2);
+        if (stream->error) return STATUS_INTERNAL_ERROR;
+        
+        len = byte & 3;
+        
+        if (len) {
+            lzo_copy(stream, len);
+            if (stream->error) return STATUS_INTERNAL_ERROR;
+        } else
+            backcopy = !backcopy;
+        
+        byte = lzo_nextbyte(stream);
+        if (stream->error) return STATUS_INTERNAL_ERROR;
+    }
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS lzo_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) {
+    NTSTATUS Status;
+    UINT32 extlen, partlen, inoff, outoff;
+    lzo_stream stream;
+    
+    extlen = *((UINT32*)inbuf);
+    if (inlen < extlen) {
+        ERR("compressed extent was %llx, should have been at least %x\n", inlen, extlen);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    inoff = sizeof(UINT32);
+    outoff = 0;
+    
+    do {
+        partlen = *(UINT32*)&inbuf[inoff];
+        
+        if (partlen + inoff > inlen) {
+            ERR("overflow: %x + %x > %llx\n", partlen, inoff, inlen);
+            return STATUS_INTERNAL_ERROR;
+        }
+        
+        inoff += sizeof(UINT32);
+    
+        stream.in = &inbuf[inoff];
+        stream.inlen = partlen;
+        stream.inpos = 0;
+        stream.out = &outbuf[outoff];
+        stream.outlen = LINUX_PAGE_SIZE;
+        stream.outpos = 0;
+        
+        Status = do_lzo_decompress(&stream);
+        if (!NT_SUCCESS(Status)) {
+            ERR("do_lzo_decompress returned %08x\n", Status);
+            return Status;
+        }
+        
+        if (stream.outpos < stream.outlen)
+            RtlZeroMemory(&stream.out[stream.outpos], stream.outlen - stream.outpos);
+        
+        inoff += partlen;
+        outoff += stream.outlen;
+        
+        if (LINUX_PAGE_SIZE - (inoff % LINUX_PAGE_SIZE) < sizeof(UINT32))
+            inoff = ((inoff / LINUX_PAGE_SIZE) + 1) * LINUX_PAGE_SIZE;
+    } while (inoff < extlen);
+    
+    return STATUS_SUCCESS;
+}
+
+static void* zlib_alloc(void* opaque, unsigned int items, unsigned int size) {
+    return ExAllocatePoolWithTag(PagedPool, items * size, ALLOC_TAG_ZLIB);
+}
+
+static void zlib_free(void* opaque, void* ptr) {
+    ExFreePool(ptr);
+}
+
+static NTSTATUS zlib_decompress(UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) {
+    z_stream c_stream;
+    int ret;
+
+    c_stream.zalloc = zlib_alloc;
+    c_stream.zfree = zlib_free;
+    c_stream.opaque = (voidpf)0;
+
+    ret = inflateInit(&c_stream);
+    
+    if (ret != Z_OK) {
+        ERR("inflateInit returned %08x\n", ret);
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    c_stream.next_in = inbuf;
+    c_stream.avail_in = inlen;
+    
+    c_stream.next_out = outbuf;
+    c_stream.avail_out = outlen;
+    
+    do {
+        ret = inflate(&c_stream, Z_NO_FLUSH);
+        
+        if (ret != Z_OK && ret != Z_STREAM_END) {
+            ERR("inflate returned %08x\n", ret);
+            inflateEnd(&c_stream);
+            return STATUS_INTERNAL_ERROR;
+        }
+    } while (ret != Z_STREAM_END);
+
+    ret = inflateEnd(&c_stream);
+    
+    if (ret != Z_OK) {
+        ERR("inflateEnd returned %08x\n", ret);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    // FIXME - if we're short, should we zero the end of outbuf so we don't leak information into userspace?
+    
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS decompress(UINT8 type, UINT8* inbuf, UINT64 inlen, UINT8* outbuf, UINT64 outlen) {
+    if (type == BTRFS_COMPRESSION_ZLIB)
+        return zlib_decompress(inbuf, inlen, outbuf, outlen);
+    else if (type == BTRFS_COMPRESSION_LZO)
+        return lzo_decompress(inbuf, inlen, outbuf, outlen);
+    else {
+        ERR("unsupported compression type %x\n", type);
+        return STATUS_NOT_SUPPORTED;
+    }
+}
+
+static NTSTATUS zlib_write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
+    UINT8 compression;
+    UINT64 comp_length;
+    UINT8* comp_data;
+    UINT32 out_left;
+    LIST_ENTRY* le;
+    chunk* c;
+    z_stream c_stream;
+    int ret;
+    
+    comp_data = ExAllocatePoolWithTag(PagedPool, end_data - start_data, ALLOC_TAG);
+    if (!comp_data) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
+    if (!NT_SUCCESS(Status)) {
+        ERR("excise_extents returned %08x\n", Status);
+        ExFreePool(comp_data);
+        return Status;
+    }
+    
+    c_stream.zalloc = zlib_alloc;
+    c_stream.zfree = zlib_free;
+    c_stream.opaque = (voidpf)0;
+
+    ret = deflateInit(&c_stream, fcb->Vcb->options.zlib_level);
+    
+    if (ret != Z_OK) {
+        ERR("deflateInit returned %08x\n", ret);
+        ExFreePool(comp_data);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    c_stream.avail_in = end_data - start_data;
+    c_stream.next_in = data;
+    c_stream.avail_out = end_data - start_data;
+    c_stream.next_out = comp_data;
+    
+    do {
+        ret = deflate(&c_stream, Z_FINISH);
+        
+        if (ret == Z_STREAM_ERROR) {
+            ERR("deflate returned %x\n", ret);
+            ExFreePool(comp_data);
+            return STATUS_INTERNAL_ERROR;
+        }
+    } while (c_stream.avail_in > 0 && c_stream.avail_out > 0);
+    
+    out_left = c_stream.avail_out;
+    
+    ret = deflateEnd(&c_stream);
+    
+    if (ret != Z_OK) {
+        ERR("deflateEnd returned %08x\n", ret);
+        ExFreePool(comp_data);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    if (out_left < fcb->Vcb->superblock.sector_size) { // compressed extent would be larger than or same size as uncompressed extent
+        ExFreePool(comp_data);
+        
+        comp_length = end_data - start_data;
+        comp_data = data;
+        compression = BTRFS_COMPRESSION_NONE;
+        
+        *compressed = FALSE;
+    } else {
+        UINT32 cl;
+        
+        compression = BTRFS_COMPRESSION_ZLIB;
+        cl = end_data - start_data - out_left;
+        comp_length = sector_align(cl, fcb->Vcb->superblock.sector_size);
+        
+        RtlZeroMemory(comp_data + cl, comp_length - cl);
+        
+        *compressed = TRUE;
+    }
+    
+    ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
+    
+    le = fcb->Vcb->chunks.Flink;
+    while (le != &fcb->Vcb->chunks) {
+        c = CONTAINING_RECORD(le, chunk, list_entry);
+        
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        
+        if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) {
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) {
+                ExReleaseResourceLite(&c->lock);
+                ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+                
+                if (compression != BTRFS_COMPRESSION_NONE)
+                    ExFreePool(comp_data);
+                
+                return STATUS_SUCCESS;
+            }
+        }
+        
+        ExReleaseResourceLite(&c->lock);
+
+        le = le->Flink;
+    }
+    
+    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+    
+    ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+    
+    if ((c = alloc_chunk(fcb->Vcb, fcb->Vcb->data_flags))) {
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+        
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        
+        if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) {
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) {
+                ExReleaseResourceLite(&c->lock);
+                
+                if (compression != BTRFS_COMPRESSION_NONE)
+                    ExFreePool(comp_data);
+                
+                return STATUS_SUCCESS;
+            }
+        }
+        
+        ExReleaseResourceLite(&c->lock);
+    } else
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+    
+    WARN("couldn't find any data chunks with %llx bytes free\n", comp_length);
+
+    return STATUS_DISK_FULL;
+}
+
+static NTSTATUS lzo_do_compress(const UINT8* in, UINT32 in_len, UINT8* out, UINT32* out_len, void* wrkmem) {
+    const UINT8* ip;
+    UINT32 dv;
+    UINT8* op;
+    const UINT8* in_end = in + in_len;
+    const UINT8* ip_end = in + in_len - 9 - 4;
+    const UINT8* ii;
+    const UINT8** dict = (const UINT8**)wrkmem;
+
+    op = out;
+    ip = in;
+    ii = ip;
+
+    DVAL_FIRST(dv, ip); UPDATE_D(dict, cycle, dv, ip); ip++;
+    DVAL_NEXT(dv, ip);  UPDATE_D(dict, cycle, dv, ip); ip++;
+    DVAL_NEXT(dv, ip);  UPDATE_D(dict, cycle, dv, ip); ip++;
+    DVAL_NEXT(dv, ip);  UPDATE_D(dict, cycle, dv, ip); ip++;
+
+    while (1) {
+        const UINT8* m_pos;
+        UINT32 m_len;
+        ptrdiff_t m_off;
+        UINT32 lit, dindex;
+
+        dindex = DINDEX(dv, ip);
+        m_pos = dict[dindex];
+        UPDATE_I(dict, cycle, dindex, ip);
+
+        if (!LZO_CHECK_MPOS_NON_DET(m_pos, m_off, in, ip, M4_MAX_OFFSET) && m_pos[0] == ip[0] && m_pos[1] == ip[1] && m_pos[2] == ip[2]) {
+            lit = ip - ii;
+            m_pos += 3;
+            if (m_off <= M2_MAX_OFFSET)
+                goto match;
+
+            if (lit == 3) { /* better compression, but slower */
+                if (op - 2 <= out)
+                    return STATUS_INTERNAL_ERROR;
+                
+                op[-2] |= LZO_BYTE(3);
+                *op++ = *ii++; *op++ = *ii++; *op++ = *ii++;
+                goto code_match;
+            }
+
+            if (*m_pos == ip[3])
+                goto match;
+        }
+
+        /* a literal */
+        ++ip;
+        if (ip >= ip_end)
+            break;
+        DVAL_NEXT(dv, ip);
+        continue;
+
+        /* a match */
+match:
+        /* store current literal run */
+        if (lit > 0) {
+            UINT32 t = lit;
+
+            if (t <= 3) {
+                if (op - 2 <= out)
+                    return STATUS_INTERNAL_ERROR;
+
+                op[-2] |= LZO_BYTE(t);
+            } else if (t <= 18)
+                *op++ = LZO_BYTE(t - 3);
+            else {
+                UINT32 tt = t - 18;
+
+                *op++ = 0;
+                while (tt > 255) {
+                    tt -= 255;
+                    *op++ = 0;
+                }
+                
+                if (tt <= 0)
+                    return STATUS_INTERNAL_ERROR;
+
+                *op++ = LZO_BYTE(tt);
+            }
+            
+            do {
+                *op++ = *ii++;
+            } while (--t > 0);
+        }
+
+
+        /* code the match */
+code_match:
+        if (ii != ip)
+            return STATUS_INTERNAL_ERROR;
+        
+        ip += 3;
+        if (*m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++ ||
+            *m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++) {
+            --ip;
+            m_len = ip - ii;
+        
+            if (m_len < 3 || m_len > 8)
+                return STATUS_INTERNAL_ERROR;
+
+            if (m_off <= M2_MAX_OFFSET) {
+                m_off -= 1;
+                *op++ = LZO_BYTE(((m_len - 1) << 5) | ((m_off & 7) << 2));
+                *op++ = LZO_BYTE(m_off >> 3);
+            } else if (m_off <= M3_MAX_OFFSET) {
+                m_off -= 1;
+                *op++ = LZO_BYTE(M3_MARKER | (m_len - 2));
+                goto m3_m4_offset;
+            } else {
+                m_off -= 0x4000;
+                
+                if (m_off <= 0 || m_off > 0x7fff)
+                    return STATUS_INTERNAL_ERROR;
+                    
+                *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2));
+                goto m3_m4_offset;
+            }
+        } else {
+            const UINT8* end;
+            end = in_end;
+            while (ip < end && *m_pos == *ip)
+                m_pos++, ip++;
+            m_len = (ip - ii);
+            
+            if (m_len < 3)
+                return STATUS_INTERNAL_ERROR;
+
+            if (m_off <= M3_MAX_OFFSET) {
+                m_off -= 1;
+                if (m_len <= 33)
+                    *op++ = LZO_BYTE(M3_MARKER | (m_len - 2));
+                else {
+                    m_len -= 33;
+                    *op++ = M3_MARKER | 0;
+                    goto m3_m4_len;
+                }
+            } else {
+                m_off -= 0x4000;
+                
+                if (m_off <= 0 || m_off > 0x7fff)
+                    return STATUS_INTERNAL_ERROR;
+
+                if (m_len <= 9)
+                    *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2));
+                else {
+                    m_len -= 9;
+                    *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11));
+m3_m4_len:
+                    while (m_len > 255) {
+                        m_len -= 255;
+                        *op++ = 0;
+                    }
+                    
+                    if (m_len <= 0)
+                        return STATUS_INTERNAL_ERROR;
+                    
+                    *op++ = LZO_BYTE(m_len);
+                }
+            }
+
+m3_m4_offset:
+            *op++ = LZO_BYTE((m_off & 63) << 2);
+            *op++ = LZO_BYTE(m_off >> 6);
+        }
+
+        ii = ip;
+        if (ip >= ip_end)
+            break;
+        DVAL_FIRST(dv, ip);
+    }
+
+    /* store final literal run */
+    if (in_end - ii > 0) {
+        UINT32 t = in_end - ii;
+
+        if (op == out && t <= 238)
+            *op++ = LZO_BYTE(17 + t);
+        else if (t <= 3)
+            op[-2] |= LZO_BYTE(t);
+        else if (t <= 18)
+            *op++ = LZO_BYTE(t - 3);
+        else {
+            UINT32 tt = t - 18;
+
+            *op++ = 0;
+            while (tt > 255) {
+                tt -= 255;
+                *op++ = 0;
+            }
+            
+            if (tt <= 0)
+                return STATUS_INTERNAL_ERROR;
+
+            *op++ = LZO_BYTE(tt);
+        }
+        
+        do {
+            *op++ = *ii++;
+        } while (--t > 0);
+    }
+
+    *out_len = op - out;
+    
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS lzo1x_1_compress(lzo_stream* stream) {
+    UINT8 *op = stream->out;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    if (stream->inlen <= 0)
+        stream->outlen = 0;
+    else if (stream->inlen <= 9 + 4) {
+        *op++ = LZO_BYTE(17 + stream->inlen);
+        
+        stream->inpos = 0;
+        do {
+            *op++ = stream->in[stream->inpos];
+            stream->inpos++;
+        } while (stream->inlen < stream->inpos);
+        stream->outlen = op - stream->out;
+    } else
+        Status = lzo_do_compress(stream->in, stream->inlen, stream->out, &stream->outlen, stream->wrkmem);
+    
+    if (Status == STATUS_SUCCESS) {
+        op = stream->out + stream->outlen;
+        *op++ = M4_MARKER | 1;
+        *op++ = 0;
+        *op++ = 0;
+        stream->outlen += 3;
+    }
+
+    return Status;
+}
+
+static __inline UINT32 lzo_max_outlen(UINT32 inlen) {
+    return inlen + (inlen / 16) + 64 + 3; // formula comes from LZO.FAQ
+}
+
+static NTSTATUS lzo_write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
+    UINT8 compression;
+    UINT64 comp_length;
+    ULONG comp_data_len, num_pages, i;
+    UINT8* comp_data;
+    BOOL skip_compression = FALSE;
+    lzo_stream stream;
+    UINT32* out_size;
+    LIST_ENTRY* le;
+    chunk* c;
+    
+    num_pages = (sector_align(end_data - start_data, LINUX_PAGE_SIZE)) / LINUX_PAGE_SIZE;
+    
+    // Four-byte overall header
+    // Another four-byte header page
+    // Each page has a maximum size of lzo_max_outlen(LINUX_PAGE_SIZE)
+    // Plus another four bytes for possible padding
+    comp_data_len = sizeof(UINT32) + ((lzo_max_outlen(LINUX_PAGE_SIZE) + (2 * sizeof(UINT32))) * num_pages);
+    
+    comp_data = ExAllocatePoolWithTag(PagedPool, comp_data_len, ALLOC_TAG);
+    if (!comp_data) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    stream.wrkmem = ExAllocatePoolWithTag(PagedPool, LZO1X_MEM_COMPRESS, ALLOC_TAG);
+    if (!stream.wrkmem) {
+        ERR("out of memory\n");
+        ExFreePool(comp_data);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
+    if (!NT_SUCCESS(Status)) {
+        ERR("excise_extents returned %08x\n", Status);
+        ExFreePool(comp_data);
+        ExFreePool(stream.wrkmem);
+        return Status;
+    }
+    
+    out_size = (UINT32*)comp_data;
+    *out_size = sizeof(UINT32);
+    
+    stream.in = data;
+    stream.out = comp_data + (2 * sizeof(UINT32));
+    
+    for (i = 0; i < num_pages; i++) {
+        UINT32* pagelen = (UINT32*)(stream.out - sizeof(UINT32));
+        
+        stream.inlen = min(LINUX_PAGE_SIZE, end_data - start_data - (i * LINUX_PAGE_SIZE));
+        
+        Status = lzo1x_1_compress(&stream);
+        if (!NT_SUCCESS(Status)) {
+            ERR("lzo1x_1_compress returned %08x\n", Status);
+            skip_compression = TRUE;
+            break;
+        }
+        
+        *pagelen = stream.outlen;
+        *out_size += stream.outlen + sizeof(UINT32);
+        
+        stream.in += LINUX_PAGE_SIZE;
+        stream.out += stream.outlen + sizeof(UINT32);
+        
+        if (LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE) < sizeof(UINT32)) {
+            RtlZeroMemory(stream.out, LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE));
+            stream.out += LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE);
+            *out_size += LINUX_PAGE_SIZE - (*out_size % LINUX_PAGE_SIZE);
+        }
+    }
+    
+    ExFreePool(stream.wrkmem);
+    
+    if (skip_compression || *out_size >= end_data - start_data - fcb->Vcb->superblock.sector_size) { // compressed extent would be larger than or same size as uncompressed extent
+        ExFreePool(comp_data);
+        
+        comp_length = end_data - start_data;
+        comp_data = data;
+        compression = BTRFS_COMPRESSION_NONE;
+        
+        *compressed = FALSE;
+    } else {
+        compression = BTRFS_COMPRESSION_LZO;
+        comp_length = sector_align(*out_size, fcb->Vcb->superblock.sector_size);
+        
+        RtlZeroMemory(comp_data + *out_size, comp_length - *out_size);
+        
+        *compressed = TRUE;
+    }
+    
+    ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
+    
+    le = fcb->Vcb->chunks.Flink;
+    while (le != &fcb->Vcb->chunks) {
+        c = CONTAINING_RECORD(le, chunk, list_entry);
+        
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        
+        if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) {
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) {
+                ExReleaseResourceLite(&c->lock);
+                ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+                
+                if (compression != BTRFS_COMPRESSION_NONE)
+                    ExFreePool(comp_data);
+                
+                return STATUS_SUCCESS;
+            }
+        }
+        
+        ExReleaseResourceLite(&c->lock);
+
+        le = le->Flink;
+    }
+    
+    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+    ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+    
+    if ((c = alloc_chunk(fcb->Vcb, fcb->Vcb->data_flags))) {
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+        
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        
+        if (c->chunk_item->type == fcb->Vcb->data_flags && (c->chunk_item->size - c->used) >= comp_length) {
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE, comp_data, changed_sector_list, Irp, rollback, compression, end_data - start_data)) {
+                ExReleaseResourceLite(&c->lock);
+                
+                if (compression != BTRFS_COMPRESSION_NONE)
+                    ExFreePool(comp_data);
+                
+                return STATUS_SUCCESS;
+            }
+        }
+        
+        ExReleaseResourceLite(&c->lock);
+    } else
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+    
+    WARN("couldn't find any data chunks with %llx bytes free\n", comp_length);
+
+    return STATUS_DISK_FULL;
+}
+
+NTSTATUS write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, BOOL* compressed, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
+    UINT8 type;
+
+    if (fcb->Vcb->options.compress_type != 0)
+        type = fcb->Vcb->options.compress_type;
+    else {
+        if (fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO)
+            type = BTRFS_COMPRESSION_LZO;
+        else
+            type = BTRFS_COMPRESSION_ZLIB;
+    }
+    
+    if (type == BTRFS_COMPRESSION_LZO) {
+        fcb->Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO;
+        return lzo_write_compressed_bit(fcb, start_data, end_data, data, compressed, changed_sector_list, Irp, rollback);
+    } else
+        return zlib_write_compressed_bit(fcb, start_data, end_data, data, compressed, changed_sector_list, Irp, rollback);
+}
index 39b6cf2..5576833 100644 (file)
@@ -25,7 +25,7 @@
 
 extern PDEVICE_OBJECT devobj;
 
-static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
+static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -35,7 +35,7 @@ static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode
     searchkey.obj_type = TYPE_INODE_REF;
     searchkey.offset = parinode;
     
-    Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -82,7 +82,7 @@ static NTSTATUS find_file_dir_index(device_extension* Vcb, root* r, UINT64 inode
         return STATUS_NOT_FOUND;
 }
 
-static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
+static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -92,7 +92,7 @@ static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT6
     searchkey.obj_type = TYPE_INODE_EXTREF;
     searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
     
-    Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -139,7 +139,7 @@ static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT6
         return STATUS_NOT_FOUND;
 }
 
-static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 subvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex) {
+static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 subvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -149,7 +149,7 @@ static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 sub
     searchkey.obj_type = TYPE_ROOT_REF;
     searchkey.offset = subvolid;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -181,7 +181,7 @@ static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 sub
         return STATUS_NOT_FOUND;
 }
 
-static NTSTATUS load_index_list(fcb* fcb) {
+static NTSTATUS load_index_list(fcb* fcb, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
     NTSTATUS Status;
@@ -191,14 +191,14 @@ static NTSTATUS load_index_list(fcb* fcb) {
     searchkey.obj_type = TYPE_DIR_INDEX;
     searchkey.offset = 2;
     
-    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
     }
 
     if (keycmp(&tp.item->key, &searchkey) == -1) {
-        if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE)) {
+        if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp)) {
             tp = next_tp;
             
             TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
@@ -315,7 +315,7 @@ static NTSTATUS load_index_list(fcb* fcb) {
         }
         
 nextitem:
-        b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp);
          
         if (b) {
             tp = next_tp;
@@ -343,7 +343,7 @@ end:
 }
 
 static NTSTATUS STDCALL find_file_in_dir_index(file_ref* fr, PUNICODE_STRING filename, root** subvol, UINT64* inode, UINT8* type,
-                                               UINT64* pindex, PANSI_STRING utf8) {
+                                               UINT64* pindex, PANSI_STRING utf8, PIRP Irp) {
     LIST_ENTRY* le;
     NTSTATUS Status;
     UNICODE_STRING us;
@@ -360,7 +360,7 @@ static NTSTATUS STDCALL find_file_in_dir_index(file_ref* fr, PUNICODE_STRING fil
     ExAcquireResourceExclusiveLite(&fr->fcb->nonpaged->index_lock, TRUE);
     
     if (!fr->fcb->index_loaded) {
-        Status = load_index_list(fr->fcb);
+        Status = load_index_list(fr->fcb, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("load_index_list returned %08x\n", Status);
             goto end;
@@ -472,8 +472,8 @@ end:
     return Status;
 }
 
-NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr,
-                                             root** subvol, UINT64* inode, UINT8* type, UINT64* pindex, PANSI_STRING utf8) {
+static NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STRING filename, UINT32 crc32, file_ref* fr,
+                                                    root** subvol, UINT64* inode, UINT8* type, UINT64* pindex, PANSI_STRING utf8, PIRP Irp) {
     DIR_ITEM* di;
     KEY searchkey;
     traverse_ptr tp;
@@ -487,7 +487,7 @@ NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STR
     searchkey.obj_type = TYPE_DIR_ITEM;
     searchkey.offset = crc32;
     
-    Status = find_item(Vcb, fr->fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, fr->fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -533,6 +533,8 @@ NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STR
                     if (!NT_SUCCESS(Status)) {
                         ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
                     } else {
+                        ANSI_STRING nutf8;
+                        
                         us.Buffer = utf16;
                         us.Length = us.MaximumLength = (USHORT)stringlen;
                         
@@ -591,17 +593,20 @@ NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STR
                             index = 0;
                                 
                             if (fr->fcb->subvol != Vcb->root_root) {
+                                nutf8.Buffer = di->name;
+                                nutf8.Length = nutf8.MaximumLength = di->n;
+                                
                                 if (di->key.obj_type == TYPE_ROOT_ITEM) {
-                                    Status = find_subvol_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
+                                    Status = find_subvol_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp);
                                     if (!NT_SUCCESS(Status)) {
                                         ERR("find_subvol_dir_index returned %08x\n", Status);
                                         return Status;
                                     }
                                 } else {
-                                    Status = find_file_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
+                                    Status = find_file_dir_index(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp);
                                     if (!NT_SUCCESS(Status)) {
                                         if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
-                                            Status = find_file_dir_index_extref(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, utf8, &index);
+                                            Status = find_file_dir_index_extref(Vcb, fr->fcb->subvol, di->key.obj_id, fr->fcb->inode, &nutf8, &index, Irp);
                                             
                                             if (!NT_SUCCESS(Status)) {
                                                 ERR("find_file_dir_index_extref returned %08x\n", Status);
@@ -651,7 +656,7 @@ NTSTATUS STDCALL find_file_in_dir_with_crc32(device_extension* Vcb, PUNICODE_STR
     }
     
 byindex:
-    Status = find_file_in_dir_index(fr, filename, subvol, inode, type, pindex, utf8);
+    Status = find_file_in_dir_index(fr, filename, subvol, inode, type, pindex, utf8, Irp);
     if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
         ERR("find_file_in_dir_index returned %08x\n", Status);
         return Status;
@@ -660,7 +665,7 @@ byindex:
     return Status;
 }
 
-fcb* create_fcb() {
+fcb* create_fcb(POOL_TYPE pool_type) {
     fcb* fcb;
     
     fcb = ExAllocatePoolWithTag(PagedPool, sizeof(struct _fcb), ALLOC_TAG);
@@ -742,7 +747,7 @@ file_ref* create_fileref() {
 }
 
 NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, file_ref* fr,
-                                  root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8) {
+                                  root** subvol, UINT64* inode, UINT8* type, UINT64* index, PANSI_STRING utf8, PIRP Irp) {
     char* fn;
     UINT32 crc32;
     ULONG utf8len;
@@ -772,10 +777,10 @@ NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filenam
     crc32 = calc_crc32c(0xfffffffe, (UINT8*)fn, (ULONG)utf8len);
     TRACE("crc32c(%.*s) = %08x\n", utf8len, fn, crc32);
     
-    return find_file_in_dir_with_crc32(Vcb, filename, crc32, fr, subvol, inode, type, index, utf8);
+    return find_file_in_dir_with_crc32(Vcb, filename, crc32, fr, subvol, inode, type, index, utf8, Irp);
 }
 
-static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* hash, PANSI_STRING xattr) {
+static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* hash, PANSI_STRING xattr, PIRP Irp) {
     NTSTATUS Status;
     ULONG utf8len;
     char* utf8;
@@ -823,7 +828,7 @@ static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream,
     searchkey.obj_type = TYPE_XATTR_ITEM;
     searchkey.offset = crc32;
     
-    Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto end;
@@ -876,7 +881,7 @@ static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream,
     
     searchkey.offset = 0;
     
-    Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto end;
@@ -959,7 +964,7 @@ static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream,
             }
         }
         
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         if (b) {
             tp = next_tp;
             
@@ -1133,7 +1138,7 @@ static file_ref* search_fileref_children(file_ref* dir, PUNICODE_STRING name) {
     return deleted;
 }
 
-static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size) {
+static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -1143,7 +1148,7 @@ static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64
     searchkey.obj_type = TYPE_EXTENT_ITEM;
     searchkey.offset = size;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return 0;
@@ -1169,7 +1174,7 @@ static UINT64 get_extent_refcount(device_extension* Vcb, UINT64 address, UINT64
     return ei->refcount;
 }
 
-NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb) {
+NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -1199,7 +1204,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
         }
     }
     
-    fcb = create_fcb();
+    fcb = create_fcb(PagedPool);
     if (!fcb) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
@@ -1215,7 +1220,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
     searchkey.obj_type = TYPE_INODE_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         free_fcb(fcb);
@@ -1248,15 +1253,15 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
             fcb->type = BTRFS_TYPE_FILE;
     }
     
-    fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', FALSE);
+    fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', FALSE, Irp);
     
-    fcb_get_sd(fcb, parent);
+    fcb_get_sd(fcb, parent, Irp);
     
     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
         UINT8* xattrdata;
         UINT16 xattrlen;
         
-        if (get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &xattrdata, &xattrlen)) {
+        if (get_xattr(Vcb, subvol, inode, EA_REPARSE, EA_REPARSE_HASH, &xattrdata, &xattrlen, Irp)) {
             fcb->reparse_xattr.Buffer = (char*)xattrdata;
             fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = xattrlen;
         }
@@ -1279,7 +1284,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
         searchkey.obj_type = TYPE_EXTENT_DATA;
         searchkey.offset = 0;
         
-        Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+        Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             free_fcb(fcb);
@@ -1312,8 +1317,11 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
                         return STATUS_INTERNAL_ERROR;
                     }
                     
+                    if (ed2->address == 0 && ed2->size == 0) // sparse
+                        goto nextitem;
+                    
                     if (ed2->size != 0)
-                        unique = get_extent_refcount(fcb->Vcb, ed2->address, ed2->size) == 1;
+                        unique = get_extent_refcount(fcb->Vcb, ed2->address, ed2->size, Irp) == 1;
                 }
                 
                 ext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
@@ -1340,7 +1348,8 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
                 InsertTailList(&fcb->extents, &ext->list_entry);
             }
             
-            b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+nextitem:
+            b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
          
             if (b) {
                 tp = next_tp;
@@ -1365,7 +1374,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
     searchkey.obj_type = TYPE_INODE_REF;
     searchkey.offset = 0;
     
-    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         free_fcb(fcb);
@@ -1509,7 +1518,7 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
             }
         }
         
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         
         if (b) {
             tp = next_tp;
@@ -1524,10 +1533,13 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
 }
 
 NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI_STRING* xattr,
-                         UINT32 streamhash, fcb* parent, fcb** pfcb) {
+                         UINT32 streamhash, fcb* parent, fcb** pfcb, PIRP Irp) {
     fcb* fcb;
     UINT8* xattrdata;
-    UINT16 xattrlen;
+    UINT16 xattrlen, overhead;
+    NTSTATUS Status;
+    KEY searchkey;
+    traverse_ptr tp;
     
     if (!IsListEmpty(&subvol->fcbs)) {
         LIST_ENTRY* le = subvol->fcbs.Flink;
@@ -1553,13 +1565,13 @@ NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI
         }
     }
     
-    fcb = create_fcb();
+    fcb = create_fcb(PagedPool);
     if (!fcb) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
     }
       
-    if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr->Buffer, streamhash, &xattrdata, &xattrlen)) {
+    if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr->Buffer, streamhash, &xattrdata, &xattrlen, Irp)) {
         ERR("get_xattr failed\n");
         free_fcb(fcb);
         return STATUS_INTERNAL_ERROR;
@@ -1574,6 +1586,35 @@ NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI
     fcb->adshash = streamhash;
     fcb->adsxattr = *xattr;
     
+    // find XATTR_ITEM overhead and hence calculate maximum length
+    
+    searchkey.obj_id = parent->inode;
+    searchkey.obj_type = TYPE_XATTR_ITEM;
+    searchkey.offset = streamhash;
+
+    Status = find_item(Vcb, parent->subvol, &tp, &searchkey, FALSE, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("find_item returned %08x\n", Status);
+        free_fcb(fcb);
+        return Status;
+    }
+    
+    if (keycmp(&tp.item->key, &searchkey)) {
+        ERR("error - could not find key for xattr\n");
+        free_fcb(fcb);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    if (tp.item->size < xattrlen) {
+        ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, xattrlen);
+        free_fcb(fcb);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    overhead = tp.item->size - xattrlen;
+    
+    fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - overhead;
+    
     fcb->adsdata.Buffer = (char*)xattrdata;
     fcb->adsdata.Length = fcb->adsdata.MaximumLength = xattrlen;
     
@@ -1623,7 +1664,7 @@ void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock) {
         ExReleaseResourceLite(&parent->nonpaged->children_lock);
 }
 
-NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed) {
+NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed, ULONG* fn_offset, PIRP Irp) {
     UNICODE_STRING fnus2;
     file_ref *dir, *sf, *sf2;
     ULONG i, num_parts;
@@ -1632,6 +1673,13 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
     NTSTATUS Status;
     
     TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, unparsed);
+
+#ifdef DEBUG    
+    if (!ExIsResourceAcquiredExclusiveLite(&Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
+        ERR("fcb_lock not acquired exclusively\n");
+        int3;
+    }
+#endif
     
     if (Vcb->removing || Vcb->locked)
         return STATUS_ACCESS_DENIED;
@@ -1743,7 +1791,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                 
                 // FIXME - check if already opened
                 
-                if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamhash, &xattr)) {
+                if (!find_stream(Vcb, sf->fcb, &parts[i], &streamname, &streamhash, &xattr, Irp)) {
                     TRACE("could not find stream %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
                     
                     Status = STATUS_OBJECT_NAME_NOT_FOUND;
@@ -1759,7 +1807,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                         goto end;
                     }
                     
-                    Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb);
+                    Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb, Irp);
                     if (!NT_SUCCESS(Status)) {
                         ERR("open_fcb_stream returned %08x\n", Status);
                         goto end;
@@ -1810,7 +1858,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                 UINT8 type;
                 ANSI_STRING utf8;
                 
-                Status = find_file_in_dir(Vcb, &parts[i], sf, &subvol, &inode, &type, &index, &utf8);
+                Status = find_file_in_dir(Vcb, &parts[i], sf, &subvol, &inode, &type, &index, &utf8, Irp);
                 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
                     TRACE("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
 
@@ -1823,19 +1871,19 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                     fcb* fcb;
                     ULONG strlen;
                     
-                    if (type != BTRFS_TYPE_DIRECTORY && !lastpart) {
-                        WARN("passed path including file as subdirectory\n");
-                        
-                        Status = STATUS_OBJECT_PATH_NOT_FOUND;
-                        goto end;
-                    }
-                    
-                    Status = open_fcb(Vcb, subvol, inode, type, &utf8, sf->fcb, &fcb);
+                    Status = open_fcb(Vcb, subvol, inode, type, &utf8, sf->fcb, &fcb, Irp);
                     if (!NT_SUCCESS(Status)) {
                         ERR("open_fcb returned %08x\n", Status);
                         goto end;
                     }
                     
+                    if (type != BTRFS_TYPE_DIRECTORY && !lastpart && !(fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
+                        WARN("passed path including file as subdirectory\n");
+                        free_fcb(fcb);
+                        Status = STATUS_OBJECT_PATH_NOT_FOUND;
+                        goto end;
+                    }
+
                     sf2 = create_fileref();
                     if (!sf2) {
                         ERR("out of memory\n");
@@ -1891,8 +1939,12 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
             }
         }
         
-        if (i == num_parts - 1)
+        if (i == num_parts - 1) {
+            if (fn_offset)
+                *fn_offset = parts[has_stream ? (num_parts - 2) : (num_parts - 1)].Buffer - fnus->Buffer;
+            
             break;
+        }
         
         if (sf2->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
             Status = STATUS_REPARSE;
@@ -1912,8 +1964,8 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
     *pfr = sf2;
     
 end:
-    ExReleaseResourceLite(&Vcb->fcb_lock);
     free_fileref(sf);
+    ExReleaseResourceLite(&Vcb->fcb_lock);
     
 end2:
     if (parts)
@@ -1924,7 +1976,7 @@ end2:
     return Status;
 }
 
-NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index) {
+NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, prev_tp;
     NTSTATUS Status;
@@ -1942,14 +1994,14 @@ NTSTATUS fcb_get_last_dir_index(fcb* fcb, UINT64* index) {
     searchkey.obj_type = TYPE_DIR_INDEX + 1;
     searchkey.offset = 0;
     
-    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto end;
     }
     
     if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type >= searchkey.obj_type)) {
-        if (find_prev_item(fcb->Vcb, &tp, &prev_tp, FALSE))
+        if (find_prev_item(fcb->Vcb, &tp, &prev_tp, FALSE, Irp))
             tp = prev_tp;
     }
     
@@ -1979,6 +2031,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     LARGE_INTEGER time;
     BTRFS_TIME now;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
     ULONG defda;
     file_ref* fileref;
     hardlink* hl;
@@ -1990,7 +2043,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     if (!NT_SUCCESS(Status))
         return Status;
     
-    utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
+    utf8 = ExAllocatePoolWithTag(pool_type, utf8len + 1, ALLOC_TAG);
     if (!utf8) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
@@ -2004,7 +2057,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     
     utf8[utf8len] = 0;
     
-    Status = fcb_get_last_dir_index(parfileref->fcb, &dirpos);
+    Status = fcb_get_last_dir_index(parfileref->fcb, &dirpos, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("fcb_get_last_dir_index returned %08x\n", Status);
         ExFreePool(utf8);
@@ -2028,7 +2081,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     mark_fcb_dirty(parfileref->fcb);
     
     if (parfileref->fcb->subvol->lastinode == 0)
-        get_last_inode(Vcb, parfileref->fcb->subvol);
+        get_last_inode(Vcb, parfileref->fcb->subvol, Irp);
     
     inode = parfileref->fcb->subvol->lastinode + 1;
     
@@ -2057,15 +2110,18 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     
     parfileref->fcb->subvol->lastinode++;
     
-    fcb = create_fcb();
+    fcb = create_fcb(pool_type);
     if (!fcb) {
         ERR("out of memory\n");
         ExFreePool(utf8);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-        
+
     fcb->Vcb = Vcb;
-    
+
+    if (IrpSp->Flags & SL_OPEN_PAGING_FILE)
+        fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
+
     fcb->inode_item.generation = Vcb->superblock.generation;
     fcb->inode_item.transid = Vcb->superblock.generation;
     fcb->inode_item.st_size = 0;
@@ -2090,12 +2146,19 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
         fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
     }
     
-    // inherit nodatacow flag from parent directory
-    if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
-        fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
+    if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
+        fcb->inode_item.flags = BTRFS_INODE_NODATACOW | BTRFS_INODE_NODATASUM | BTRFS_INODE_NOCOMPRESS;
+    } else {
+        // inherit nodatacow flag from parent directory
+        if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
+            fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
+            
+            if (type != BTRFS_TYPE_DIRECTORY)
+                fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
+        }
         
-        if (type != BTRFS_TYPE_DIRECTORY)
-            fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
+        if (parfileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
+            fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
     }
     
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
@@ -2120,16 +2183,24 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     
     if (!NT_SUCCESS(Status)) {
         ERR("fcb_get_new_sd returned %08x\n", Status);
+        
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        
         return Status;
     }
     
     fcb->sd_dirty = TRUE;
     
-    hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG);
+    hl = ExAllocatePoolWithTag(pool_type, sizeof(hardlink), ALLOC_TAG);
     if (!hl) {
         ERR("out of memory\n");
+        
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
@@ -2137,24 +2208,32 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     hl->index = dirpos;
     
     hl->utf8.Length = hl->utf8.MaximumLength = utf8len;
-    hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG);
+    hl->utf8.Buffer = ExAllocatePoolWithTag(pool_type, utf8len, ALLOC_TAG);
     
     if (!hl->utf8.Buffer) {
         ERR("out of memory\n");
         ExFreePool(hl);
+        
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     RtlCopyMemory(hl->utf8.Buffer, utf8, utf8len);
     
     hl->name.Length = hl->name.MaximumLength = fpus->Length;
-    hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fpus->Length, ALLOC_TAG);
+    hl->name.Buffer = ExAllocatePoolWithTag(pool_type, fpus->Length, ALLOC_TAG);
     
     if (!hl->name.Buffer) {
         ERR("out of memory\n");
         ExFreePool(hl->utf8.Buffer);
         ExFreePool(hl);
+        
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
@@ -2165,7 +2244,11 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     fileref = create_fileref();
     if (!fileref) {
         ERR("out of memory\n");
+        
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
@@ -2175,21 +2258,43 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     fileref->utf8.MaximumLength = fileref->utf8.Length = utf8len;
     fileref->utf8.Buffer = utf8;
     
-    fileref->filepart = *fpus;
+    fileref->filepart.Length = fileref->filepart.MaximumLength = fpus->Length;
+    
+    if (fileref->filepart.Length == 0)
+        fileref->filepart.Buffer = NULL;
+    else {
+        fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.Length, ALLOC_TAG);
+        
+        if (!fileref->filepart.Buffer) {
+            ERR("out of memory\n");
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fcb(fcb);
+            ExReleaseResource(&Vcb->fcb_lock);
+            
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlCopyMemory(fileref->filepart.Buffer, fpus->Buffer, fpus->Length);
+    }
     
     Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
     if (!NT_SUCCESS(Status)) {
         ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(fileref);
+        ExReleaseResource(&Vcb->fcb_lock);
         return Status;
     }
         
-    if (Irp->Overlay.AllocationSize.QuadPart > 0) {
+    if (Irp->Overlay.AllocationSize.QuadPart > 0 && !write_fcb_compressed(fcb)) {
         Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
         
         if (!NT_SUCCESS(Status)) {
             ERR("extend_file returned %08x\n", Status);
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fileref(fileref);
+            ExReleaseResource(&Vcb->fcb_lock);
             return Status;
         }
     }
@@ -2204,29 +2309,8 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     fcb->subvol->root_item.ctime = now;
     
     fileref->parent = parfileref;
-    
-    ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
-    
-    if (IsListEmpty(&parfileref->children))
-        InsertTailList(&parfileref->children, &fileref->list_entry);
-    else {
-        LIST_ENTRY* le = parfileref->children.Flink;
-        file_ref* fr1 = CONTAINING_RECORD(le, file_ref, list_entry);
-        
-        while (le != &parfileref->children) {
-            file_ref* fr2 = (le->Flink == &parfileref->children) ? NULL : CONTAINING_RECORD(le->Flink, file_ref, list_entry);
-            
-            if (fileref->index > fr1->index && (!fr2 || fr2->index > fileref->index)) {
-                InsertHeadList(&fr1->list_entry, &fileref->list_entry);
-                break;
-            }
-            
-            fr1 = fr2;
-            le = le->Flink;
-        }
-    }
-    
-    ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
+
+    insert_fileref_child(parfileref, fileref, TRUE);
     
     increase_fileref_refcount(parfileref);
  
@@ -2243,21 +2327,250 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     return STATUS_SUCCESS;
 }
 
+static NTSTATUS create_stream(device_extension* Vcb, file_ref** pfileref, file_ref** pparfileref, PUNICODE_STRING fpus, PUNICODE_STRING stream,
+                              PIRP Irp, ULONG options, POOL_TYPE pool_type, LIST_ENTRY* rollback) {
+    file_ref *fileref, *newpar, *parfileref;
+    fcb* fcb;
+    static char xapref[] = "user.";
+    ULONG xapreflen = strlen(xapref), overhead;
+    LARGE_INTEGER time;
+    BTRFS_TIME now;
+    ULONG utf8len;
+    NTSTATUS Status;
+    KEY searchkey;
+    traverse_ptr tp;
+#ifdef DEBUG_FCB_REFCOUNTS
+    LONG rc;
+#endif
+    
+    TRACE("fpus = %.*S\n", fpus->Length / sizeof(WCHAR), fpus->Buffer);
+    TRACE("stream = %.*S\n", stream->Length / sizeof(WCHAR), stream->Buffer);
+    
+    parfileref = *pparfileref;
+    
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    Status = open_fileref(Vcb, &newpar, fpus, parfileref, FALSE, NULL, NULL, Irp);
+    ExReleaseResource(&Vcb->fcb_lock);
+    
+    if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+        UNICODE_STRING fpus2;
+        
+        if (!is_file_name_valid(fpus))
+            return STATUS_OBJECT_NAME_INVALID;
+        
+        fpus2.Length = fpus2.MaximumLength = fpus->Length;
+        fpus2.Buffer = ExAllocatePoolWithTag(pool_type, fpus2.MaximumLength, ALLOC_TAG);
+        
+        if (!fpus2.Buffer) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlCopyMemory(fpus2.Buffer, fpus->Buffer, fpus2.Length);
+        
+        Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, &newpar, rollback);
+    
+        if (!NT_SUCCESS(Status)) {
+            ERR("file_create2 returned %08x\n", Status);
+            ExFreePool(fpus2.Buffer);
+            return Status;
+        }
+        
+        send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
+        send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
+    } else if (!NT_SUCCESS(Status)) {
+        ERR("open_fileref returned %08x\n", Status);
+        return Status;
+    }
+    
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    free_fileref(parfileref);
+    ExReleaseResource(&Vcb->fcb_lock);
+    
+    parfileref = newpar;
+    *pparfileref = parfileref;
+    
+    if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK) {
+        WARN("parent not file or symlink\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+    
+    if (options & FILE_DIRECTORY_FILE) {
+        WARN("tried to create directory as stream\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+        
+    fcb = create_fcb(pool_type);
+    if (!fcb) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    fcb->Vcb = Vcb;
+    
+    fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
+    fcb->Header.AllocationSize.QuadPart = 0;
+    fcb->Header.FileSize.QuadPart = 0;
+    fcb->Header.ValidDataLength.QuadPart = 0;
+    
+#ifdef DEBUG_FCB_REFCOUNTS
+    rc = InterlockedIncrement(&parfileref->fcb->refcount);
+    WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref));
+#else
+    InterlockedIncrement(&parfileref->fcb->refcount);
+#endif
+    fcb->subvol = parfileref->fcb->subvol;
+    fcb->inode = parfileref->fcb->inode;
+    fcb->type = parfileref->fcb->type;
+    
+    fcb->ads = TRUE;
+    
+    Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream->Buffer, stream->Length);
+    if (!NT_SUCCESS(Status)) {
+        ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return Status;
+    }
+    
+    fcb->adsxattr.Length = utf8len + xapreflen;
+    fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
+    fcb->adsxattr.Buffer = ExAllocatePoolWithTag(pool_type, fcb->adsxattr.MaximumLength, ALLOC_TAG);
+    if (!fcb->adsxattr.Buffer) {
+        ERR("out of memory\n");
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen);
+    
+    Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream->Buffer, stream->Length);
+    if (!NT_SUCCESS(Status)) {
+        ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return Status;
+    }
+    
+    fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
+    
+    TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
+    
+    fcb->adshash = calc_crc32c(0xfffffffe, (UINT8*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
+    TRACE("adshash = %08x\n", fcb->adshash);
+    
+    searchkey.obj_id = parfileref->fcb->inode;
+    searchkey.obj_type = TYPE_XATTR_ITEM;
+    searchkey.offset = fcb->adshash;
+    
+    Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("find_item returned %08x\n", Status);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return Status;
+    }
+    
+    if (!keycmp(&tp.item->key, &searchkey))
+        overhead = tp.item->size;
+    else
+        overhead = 0;
+    
+    fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - (sizeof(DIR_ITEM) - 1);
+    
+    if (utf8len + xapreflen + overhead > fcb->adsmaxlen) {
+        WARN("not enough room for new DIR_ITEM (%u + %u > %u)", utf8len + xapreflen, overhead, fcb->adsmaxlen);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return STATUS_DISK_FULL;
+    } else
+        fcb->adsmaxlen -= overhead + utf8len + xapreflen;
+    
+    fileref = create_fileref();
+    if (!fileref) {
+        ERR("out of memory\n");
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fcb(fcb);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    fileref->fcb = fcb;
+
+    fileref->filepart.MaximumLength = fileref->filepart.Length = stream->Length;
+    fileref->filepart.Buffer = ExAllocatePoolWithTag(pool_type, fileref->filepart.MaximumLength, ALLOC_TAG);
+    if (!fileref->filepart.Buffer) {
+        ERR("out of memory\n");
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fileref(fileref);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlCopyMemory(fileref->filepart.Buffer, stream->Buffer, stream->Length);
+    
+    Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
+    if (!NT_SUCCESS(Status)) {
+        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        free_fileref(fileref);
+        ExReleaseResource(&Vcb->fcb_lock);
+        return Status;
+    }
+    
+    mark_fcb_dirty(fcb);
+    mark_fileref_dirty(fileref);
+    
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+    ExReleaseResource(&Vcb->fcb_lock);
+    
+    KeQuerySystemTime(&time);
+    win_time_to_unix(time, &now);
+    
+    parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
+    parfileref->fcb->inode_item.sequence++;
+    parfileref->fcb->inode_item.st_ctime = now;
+    
+    mark_fcb_dirty(parfileref->fcb);
+    
+    parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
+    parfileref->fcb->subvol->root_item.ctime = now;
+    
+    fileref->parent = (struct _file_ref*)parfileref;
+    
+    insert_fileref_child(parfileref, fileref, TRUE);
+    
+    increase_fileref_refcount(parfileref);
+    
+    *pfileref = fileref;
+    
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJECT FileObject, PUNICODE_STRING fnus, ULONG disposition, ULONG options, LIST_ENTRY* rollback) {
     NTSTATUS Status;
 //     fcb *fcb, *parfcb = NULL;
     file_ref *fileref, *parfileref = NULL, *related;
-    ULONG i, j;
+    ULONG i, j, fn_offset;
 //     ULONG utf8len;
     ccb* ccb;
     static WCHAR datasuf[] = {':','$','D','A','T','A',0};
     UNICODE_STRING dsus, fpus, stream;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
     PACCESS_STATE access_state = IrpSp->Parameters.Create.SecurityContext->AccessState;
 #ifdef DEBUG_FCB_REFCOUNTS
     LONG oc;
 #endif
-            
+
     TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp, Vcb, FileObject, fnus->Length / sizeof(WCHAR), fnus->Buffer, disposition, options);
     
     if (Vcb->readonly)
@@ -2274,7 +2587,10 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     } else
         related = NULL;
     
-    Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL);
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL, NULL, Irp);
+    ExReleaseResource(&Vcb->fcb_lock);
+    
     if (!NT_SUCCESS(Status))
         goto end;
     
@@ -2296,7 +2612,7 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     while (i > 0 && fnus->Buffer[i-1] != '\\' && fnus->Buffer[i-1] != '/') { i--; }
     
     fpus.MaximumLength = (j - i + 2) * sizeof(WCHAR);
-    fpus.Buffer = ExAllocatePoolWithTag(PagedPool, fpus.MaximumLength, ALLOC_TAG);
+    fpus.Buffer = ExAllocatePoolWithTag(pool_type, fpus.MaximumLength, ALLOC_TAG);
     if (!fpus.Buffer) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -2308,6 +2624,8 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR));
     fpus.Buffer[j - i + 1] = 0;
     
+    fn_offset = i;
+    
     if (fpus.Length > dsus.Length) { // check for :$DATA suffix
         UNICODE_STRING lb;
                 
@@ -2341,183 +2659,11 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     }
     
     if (stream.Length > 0) {
-        file_ref* newpar;
-        fcb* fcb;
-        static char xapref[] = "user.";
-        ULONG xapreflen = strlen(xapref);
-        LARGE_INTEGER time;
-        BTRFS_TIME now;
-        ULONG utf8len;
-#ifdef DEBUG_FCB_REFCOUNTS
-        LONG rc;
-#endif
-        
-        TRACE("fpus = %.*S\n", fpus.Length / sizeof(WCHAR), fpus.Buffer);
-        TRACE("stream = %.*S\n", stream.Length / sizeof(WCHAR), stream.Buffer);
-        
-        Status = open_fileref(Vcb, &newpar, &fpus, parfileref, FALSE, NULL);
-        
-        if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
-            UNICODE_STRING fpus2;
-            
-            if (!is_file_name_valid(&fpus))
-                return STATUS_OBJECT_NAME_INVALID;
-            
-            fpus2.Length = fpus2.MaximumLength = fpus.Length;
-            fpus2.Buffer = ExAllocatePoolWithTag(PagedPool, fpus2.MaximumLength, ALLOC_TAG);
-            
-            if (!fpus2.Buffer) {
-                ERR("out of memory\n");
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto end;
-            }
-            
-            RtlCopyMemory(fpus2.Buffer, fpus.Buffer, fpus2.Length);
-            
-            Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, &newpar, rollback);
-        
-            if (!NT_SUCCESS(Status)) {
-                ERR("file_create2 returned %08x\n", Status);
-                ExFreePool(fpus2.Buffer);
-                goto end;
-            }
-            
-            send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
-            send_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
-        } else if (!NT_SUCCESS(Status)) {
-            ERR("open_fileref returned %08x\n", Status);
-            goto end;
-        }
-        
-        free_fileref(parfileref);
-        parfileref = newpar;
-        
-        if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK) {
-            WARN("parent not file or symlink\n");
-            Status = STATUS_INVALID_PARAMETER;
-            goto end;
-        }
-        
-        if (options & FILE_DIRECTORY_FILE) {
-            WARN("tried to create directory as stream\n");
-            Status = STATUS_INVALID_PARAMETER;
-            goto end;
-        }
-            
-        fcb = create_fcb();
-        if (!fcb) {
-            ERR("out of memory\n");
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto end;
-        }
-        
-        fcb->Vcb = Vcb;
-        
-        fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
-        fcb->Header.AllocationSize.QuadPart = 0;
-        fcb->Header.FileSize.QuadPart = 0;
-        fcb->Header.ValidDataLength.QuadPart = 0;
-        
-#ifdef DEBUG_FCB_REFCOUNTS
-        rc = InterlockedIncrement(&parfileref->fcb->refcount);
-        WARN("fcb %p: refcount now %i (%S)\n", parfileref->fcb, rc, file_desc_fileref(parfileref));
-#else
-        InterlockedIncrement(&parfileref->fcb->refcount);
-#endif
-        fcb->subvol = parfileref->fcb->subvol;
-        fcb->inode = parfileref->fcb->inode;
-        fcb->type = parfileref->fcb->type;
-        
-        fcb->ads = TRUE;
-        
-        Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, stream.Buffer, stream.Length);
+        Status = create_stream(Vcb, &fileref, &parfileref, &fpus, &stream, Irp, options, pool_type, rollback);
         if (!NT_SUCCESS(Status)) {
-            ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
-            free_fcb(fcb);
+            ERR("create_stream returned %08x\n", Status);
             goto end;
         }
-        
-        fcb->adsxattr.Length = utf8len + xapreflen;
-        fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
-        fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
-        if (!fcb->adsxattr.Buffer) {
-            ERR("out of memory\n");
-            free_fcb(fcb);
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto end;
-        }
-        
-        RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen);
-        
-        Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len, &utf8len, stream.Buffer, stream.Length);
-        if (!NT_SUCCESS(Status)) {
-            ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
-            free_fcb(fcb);
-            goto end;
-        }
-        
-        fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
-        
-        TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
-        
-        fcb->adshash = calc_crc32c(0xfffffffe, (UINT8*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
-        TRACE("adshash = %08x\n", fcb->adshash);
-
-        fileref = create_fileref();
-        if (!fileref) {
-            ERR("out of memory\n");
-            free_fcb(fcb);
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto end;
-        }
-        
-        fileref->fcb = fcb;
-
-        fileref->filepart.MaximumLength = fileref->filepart.Length = stream.Length;
-        fileref->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->filepart.MaximumLength, ALLOC_TAG);
-        if (!fileref->filepart.Buffer) {
-            ERR("out of memory\n");
-            free_fileref(fileref);
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto end;
-        }
-        
-        RtlCopyMemory(fileref->filepart.Buffer, stream.Buffer, stream.Length);
-        
-        Status = RtlUpcaseUnicodeString(&fileref->filepart_uc, &fileref->filepart, TRUE);
-        if (!NT_SUCCESS(Status)) {
-            ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-            free_fileref(fileref);
-            goto end;
-        }
-       
-        mark_fcb_dirty(fcb);
-        
-        InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
-        InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
-        
-        KeQuerySystemTime(&time);
-        win_time_to_unix(time, &now);
-        
-        parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
-        parfileref->fcb->inode_item.sequence++;
-        parfileref->fcb->inode_item.st_ctime = now;
-        
-        mark_fcb_dirty(parfileref->fcb);
-        
-        parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
-        parfileref->fcb->subvol->root_item.ctime = now;
-        
-        fileref->parent = (struct _file_ref*)parfileref;
-        
-        ExAcquireResourceExclusiveLite(&parfileref->nonpaged->children_lock, TRUE);
-        InsertTailList(&parfileref->children, &fileref->list_entry);
-        ExReleaseResourceLite(&parfileref->nonpaged->children_lock);
-        
-        increase_fileref_refcount(parfileref);
-        
-        ExFreePool(fpus.Buffer);
-        fpus.Buffer = NULL;
     } else {
         if (!is_file_name_valid(&fpus)) {
             Status = STATUS_OBJECT_NAME_INVALID;
@@ -2541,7 +2687,9 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     if (!ccb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(fileref);
+        ExReleaseResource(&Vcb->fcb_lock);
         goto end;
     }
     
@@ -2567,6 +2715,11 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
 #endif
     
     FileObject->FsContext2 = ccb;
+    
+    if (fn_offset > 0) {
+        FileObject->FileName.Length -= fn_offset * sizeof(WCHAR);
+        RtlMoveMemory(&FileObject->FileName.Buffer[0], &FileObject->FileName.Buffer[fn_offset], FileObject->FileName.Length);
+    }
 
     FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
     
@@ -2603,8 +2756,11 @@ end:
         ExFreePool(fpus.Buffer);
     
 end2:
-    if (parfileref)
+    if (parfileref) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(parfileref);
+        ExReleaseResource(&Vcb->fcb_lock);
+    }
     
     return Status;
 }
@@ -2740,7 +2896,7 @@ static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) {
     if (fcb->type == BTRFS_TYPE_FILE || fcb->type == BTRFS_TYPE_SYMLINK) {
         ULONG size, bytes_read, i;
         
-        if (fcb->inode_item.st_size < sizeof(ULONG)) {
+        if (fcb->type == BTRFS_TYPE_FILE && fcb->inode_item.st_size < sizeof(ULONG)) {
             WARN("file was too short to be a reparse point\n");
             return STATUS_INVALID_PARAMETER;
         }
@@ -2861,6 +3017,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
     device_extension* Vcb = DeviceObject->DeviceExtension;
     PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
     USHORT unparsed;
+    ULONG fn_offset = 0;
     file_ref *related, *fileref;
 #ifdef DEBUG_FCB_REFCOUNTS
     LONG oc;
@@ -2927,8 +3084,12 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         goto exit;
     }
     
-    // FIXME - if Vcb->readonly or subvol readonly, don't allow the write ACCESS_MASK flags
-    
+    if (Vcb->readonly && Stack->Parameters.Create.SecurityContext->DesiredAccess &
+        (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC)) {
+        Status = STATUS_MEDIA_WRITE_PROTECTED;
+        goto exit;
+    }
+
     if (options & FILE_OPEN_BY_FILE_ID) {
         if (FileObject->FileName.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) {
             UINT64 inode;
@@ -2942,8 +3103,11 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
                 fileref = related->parent;
                 increase_fileref_refcount(fileref);
                 Status = STATUS_SUCCESS;
-            } else
-                Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref);
+            } else {
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref, Irp);
+                ExReleaseResource(&Vcb->fcb_lock);
+            }
         } else {
             WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
             Status = STATUS_NOT_IMPLEMENTED;
@@ -2955,7 +3119,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             goto exit;
         }
         
-        Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed, &fn_offset, Irp);
+        ExReleaseResource(&Vcb->fcb_lock);
     }
     
     if (Status == STATUS_REPARSE) {
@@ -2999,7 +3165,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         if (RequestedDisposition == FILE_CREATE) {
             TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref));
             Status = STATUS_OBJECT_NAME_COLLISION;
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fileref(fileref);
+            ExReleaseResource(&Vcb->fcb_lock);
             goto exit;
         }
     } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
@@ -3007,9 +3175,12 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
             goto exit;
         }
-    } else {
+    } else if (Status == STATUS_OBJECT_PATH_NOT_FOUND) {
         TRACE("open_fileref returned %08x\n", Status);
         goto exit;
+    } else {
+        ERR("open_fileref returned %08x\n", Status);
+        goto exit;
     }
     
     if (NT_SUCCESS(Status)) { // file already exists
@@ -3018,17 +3189,30 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
             if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
                 Status = STATUS_ACCESS_DENIED;
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fileref(fileref);
+                ExReleaseResource(&Vcb->fcb_lock);
                 goto exit;
             }
             
             if (Vcb->readonly) {
                 Status = STATUS_MEDIA_WRITE_PROTECTED;
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fileref(fileref);
+                ExReleaseResource(&Vcb->fcb_lock);
                 goto exit;
             }
         }
         
+        if (fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && Stack->Parameters.Create.SecurityContext->DesiredAccess &
+            (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC)) {
+            Status = STATUS_ACCESS_DENIED;
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fileref(fileref);
+            ExReleaseResource(&Vcb->fcb_lock);
+            goto exit;
+        }
+        
         TRACE("deleted = %s\n", fileref->deleted ? "TRUE" : "FALSE");
         
         sf = fileref;
@@ -3057,39 +3241,24 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             
             if (Stack->Parameters.Create.SecurityContext->DesiredAccess & ~allowed) {
                 Status = STATUS_ACCESS_DENIED;
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fileref(fileref);
+                ExReleaseResource(&Vcb->fcb_lock);
                 goto exit;
             }
         }
         
-        if (options & FILE_NON_DIRECTORY_FILE && fileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
-            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-            free_fileref(fileref);
-            ExReleaseResourceLite(&Vcb->fcb_lock);
-            
-            Status = STATUS_FILE_IS_A_DIRECTORY;
-            goto exit;
-        } else if (options & FILE_DIRECTORY_FILE && fileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
-            TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref));
-            
-            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-            free_fileref(fileref);
-            ExReleaseResourceLite(&Vcb->fcb_lock);
-            
-            Status = STATUS_NOT_A_DIRECTORY;
-            goto exit;
-        }
-        
         if (options & FILE_DELETE_ON_CLOSE && (fileref == Vcb->root_fileref || Vcb->readonly ||
             fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY || fileref->fcb->atts & FILE_ATTRIBUTE_READONLY)) {
             Status = STATUS_CANNOT_DELETE;
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fileref(fileref);
+            ExReleaseResource(&Vcb->fcb_lock);
             goto exit;
         }
         
-        
         if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT))  {
-            UINT8* data;
+            REPARSE_DATA_BUFFER* data;
             
             /* How reparse points work from the point of view of the filesystem appears to
              * undocumented. When returning STATUS_REPARSE, MSDN encourages us to return
@@ -3098,7 +3267,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
              * a pointer to the reparse data buffer in Irp->Tail.Overlay.AuxiliaryBuffer,
              * IopSymlinkProcessReparse will do the translation for us. */
             
-            Status = get_reparse_block(fileref->fcb, &data);
+            Status = get_reparse_block(fileref->fcb, (UINT8**)&data);
             if (!NT_SUCCESS(Status)) {
                 ERR("get_reparse_block returned %08x\n", Status);
                 
@@ -3109,7 +3278,10 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             }
             
             Status = STATUS_REPARSE;
-            RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
+            Irp->IoStatus.Information = data->ReparseTag;
+            
+            if (FileObject->FileName.Buffer[(FileObject->FileName.Length / sizeof(WCHAR)) - 1] == '\\')
+                data->Reserved = sizeof(WCHAR);
             
             Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
             
@@ -3119,6 +3291,26 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             goto exit;
         }
         
+        if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
+            if (options & FILE_NON_DIRECTORY_FILE && !(fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+                free_fileref(fileref);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
+                Status = STATUS_FILE_IS_A_DIRECTORY;
+                goto exit;
+            }
+        } else if (options & FILE_DIRECTORY_FILE) {
+            TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref->fcb->type, file_desc_fileref(fileref));
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+            free_fileref(fileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
+            Status = STATUS_NOT_A_DIRECTORY;
+            goto exit;
+        }
+        
         if (fileref->fcb->open_count > 0) {
             Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
                                         Stack->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, TRUE);
@@ -3155,7 +3347,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
                 WARN("cannot overwrite readonly file\n");
                 Status = STATUS_ACCESS_DENIED;
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fileref(fileref);
+                ExReleaseResource(&Vcb->fcb_lock);
                 goto exit;
             }
     
@@ -3168,10 +3362,12 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
 //             }
             
             // FIXME - make sure not ADS!
-            Status = truncate_file(fileref->fcb, fileref->fcb->inode_item.st_size, rollback);
+            Status = truncate_file(fileref->fcb, 0, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("truncate_file returned %08x\n", Status);
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fileref(fileref);
+                ExReleaseResource(&Vcb->fcb_lock);
                 goto exit;
             }
             
@@ -3180,7 +3376,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
                 
                 if (!NT_SUCCESS(Status)) {
                     ERR("extend_file returned %08x\n", Status);
+                    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                     free_fileref(fileref);
+                    ExReleaseResource(&Vcb->fcb_lock);
                     goto exit;
                 }
             }
@@ -3192,7 +3390,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             oldatts = fileref->fcb->atts;
             
             defda = get_file_attributes(Vcb, &fileref->fcb->inode_item, fileref->fcb->subvol, fileref->fcb->inode, fileref->fcb->type,
-                                        fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE);
+                                        fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE, Irp);
             
             if (RequestedDisposition == FILE_SUPERSEDE)
                 fileref->fcb->atts = Stack->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
@@ -3249,6 +3447,11 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         
         FileObject->FsContext2 = ccb;
         
+        if (fn_offset > 0) {
+            FileObject->FileName.Length -= fn_offset * sizeof(WCHAR);
+            RtlMoveMemory(&FileObject->FileName.Buffer[0], &FileObject->FileName.Buffer[fn_offset], FileObject->FileName.Length);
+        }
+        
         FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
         
         if (NT_SUCCESS(Status)) {
@@ -3295,6 +3498,53 @@ exit:
     return Status;
 }
 
+NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp) {
+    UINT64 i;
+    
+    for (i = 0; i < Vcb->superblock.num_devices; i++) {
+        if (Vcb->devices[i].removable) {
+            NTSTATUS Status;
+            ULONG cc;
+            IO_STATUS_BLOCK iosb;
+            
+            Status = dev_ioctl(Vcb->devices[i].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+            
+            if (!NT_SUCCESS(Status)) {
+                ERR("dev_ioctl returned %08x\n", Status);
+                return Status;
+            }
+            
+            if (iosb.Information < sizeof(ULONG)) {
+                ERR("iosb.Information was too short\n");
+                return STATUS_INTERNAL_ERROR;
+            }
+            
+            if (cc != Vcb->devices[i].change_count) {
+                PDEVICE_OBJECT dev;
+                
+                Vcb->devices[i].devobj->Flags |= DO_VERIFY_VOLUME;
+                
+                dev = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
+                IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL);
+                
+                if (!dev) {
+                    dev = IoGetDeviceToVerify(PsGetCurrentThread());
+                    IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
+                }
+                
+                dev = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
+                
+                if (dev)
+                    IoVerifyVolume(dev, FALSE);
+                
+                return STATUS_VERIFY_REQUIRED;
+            }
+        }
+    }
+    
+    return STATUS_SUCCESS;
+}
+
 NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     NTSTATUS Status;
     PIO_STACK_LOCATION IrpSp;
@@ -3321,6 +3571,13 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     }
     
     Vcb = DeviceObject->DeviceExtension;
+    
+    Status = verify_vcb(Vcb, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("verify_vcb returned %08x\n", Status);
+        goto exit;
+    }
+    
     ExAcquireResourceSharedLite(&Vcb->load_lock, TRUE);
     locked = TRUE;
     
index 972bc6f..5cc54e9 100644 (file)
@@ -32,7 +32,7 @@ typedef struct {
     enum DirEntryType dir_entry_type;
 } dir_entry;
 
-static ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts) {
+ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts, PIRP Irp) {
     fcb* fcb;
     ULONG tag = 0, br;
     NTSTATUS Status;
@@ -49,7 +49,7 @@ static ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64
         return 0;
     
     ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
-    Status = open_fcb(Vcb, subvol, inode, type, NULL, NULL, &fcb);
+    Status = open_fcb(Vcb, subvol, inode, type, NULL, NULL, &fcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
         ExReleaseResourceLite(&Vcb->fcb_lock);
@@ -78,7 +78,9 @@ static ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64
 end:
     ExReleaseResourceLite(fcb->Header.Resource);
 
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
     free_fcb(fcb);
+    ExReleaseResourceLite(&Vcb->fcb_lock);
     
     return tag;
 }
@@ -154,7 +156,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
                     searchkey.obj_type = TYPE_INODE_ITEM;
                     searchkey.offset = 0xffffffffffffffff;
                     
-                    Status = find_item(fcb->Vcb, r, &tp, &searchkey, FALSE);
+                    Status = find_item(fcb->Vcb, r, &tp, &searchkey, FALSE, Irp);
                     if (!NT_SUCCESS(Status)) {
                         ERR("error - find_item returned %08x\n", Status);
                         return Status;
@@ -177,7 +179,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
                         
                         BOOL dotfile = de->namelen > 1 && de->name[0] == '.';
 
-                        atts = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE);
+                        atts = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE, Irp);
                     }
                 }
                 
@@ -238,12 +240,12 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             fbdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
             fbdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
             fbdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
-            fbdi->ChangeTime.QuadPart = 0;
+            fbdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
             fbdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
             fbdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
             fbdi->FileAttributes = atts;
             fbdi->FileNameLength = stringlen;
-            fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
+            fbdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp);
             fbdi->ShortNameLength = 0;
 //             fibdi->ShortName[12];
             
@@ -277,7 +279,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             fdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
             fdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
             fdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
-            fdi->ChangeTime.QuadPart = 0;
+            fdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
             fdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
             fdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
             fdi->FileAttributes = atts;
@@ -313,12 +315,12 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             ffdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
             ffdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
             ffdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
-            ffdi->ChangeTime.QuadPart = 0;
+            ffdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
             ffdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
             ffdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
             ffdi->FileAttributes = atts;
             ffdi->FileNameLength = stringlen;
-            ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
+            ffdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp);
             
             Status = RtlUTF8ToUnicodeN(ffdi->FileName, stringlen, &stringlen, de->name, de->namelen);
 
@@ -353,12 +355,12 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
             fibdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
             fibdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
             fibdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
-            fibdi->ChangeTime.QuadPart = 0;
+            fibdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
             fibdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
             fibdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
             fibdi->FileAttributes = atts;
             fibdi->FileNameLength = stringlen;
-            fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts);
+            fibdi->EaSize = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp);
             fibdi->ShortNameLength = 0;
 //             fibdi->ShortName[12];
             fibdi->FileId.QuadPart = inode;
@@ -428,7 +430,7 @@ static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, L
     return STATUS_NO_MORE_FILES;
 }
 
-static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_entry* de) {
+static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_entry* de, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
     DIR_ITEM* di;
@@ -520,14 +522,14 @@ static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_en
         searchkey.obj_type = TYPE_DIR_INDEX;
         searchkey.offset = *offset;
         
-        Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE);
+        Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             goto end;
         }
         
         if (keycmp(&tp.item->key, &searchkey) == -1) {
-            if (find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE))
+            if (find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp))
                 tp = next_tp;
         }
         
@@ -766,7 +768,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     }
     
     newoffset = ccb->query_dir_offset;
-    Status = next_dir_entry(fileref, &newoffset, &de);
+    Status = next_dir_entry(fileref, &newoffset, &de, Irp);
     
     if (!NT_SUCCESS(Status)) {
         if (Status == STATUS_NO_MORE_FILES && initial)
@@ -845,7 +847,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
             ExFreePool(us.Buffer);
         
         if (!found) {
-            Status = find_file_in_dir(fcb->Vcb, &ccb->query_string, fileref, &found_subvol, &found_inode, &found_type, &found_index, &utf8);
+            Status = find_file_in_dir(fcb->Vcb, &ccb->query_string, fileref, &found_subvol, &found_inode, &found_type, &found_index, &utf8, Irp);
             
             if (!NT_SUCCESS(Status)) {
                 Status = STATUS_NO_SUCH_FILE;
@@ -901,7 +903,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
                 ExFreePool(de.name);
             
             newoffset = ccb->query_dir_offset;
-            Status = next_dir_entry(fileref, &newoffset, &de);
+            Status = next_dir_entry(fileref, &newoffset, &de, Irp);
             
             ExFreePool(uni_fn);
             if (NT_SUCCESS(Status)) {
@@ -976,7 +978,7 @@ static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
                 UNICODE_STRING di_uni_fn;
                 
                 newoffset = ccb->query_dir_offset;
-                Status = next_dir_entry(fileref, &newoffset, &de);
+                Status = next_dir_entry(fileref, &newoffset, &de, Irp);
                 if (NT_SUCCESS(Status)) {
                     if (has_wildcard) {
                         ULONG stringlen;
index 2bc760e..992b33d 100644 (file)
@@ -91,7 +91,7 @@ static UINT64 get_extent_hash(UINT8 type, void* data) {
     }
 }
 
-static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem, UINT8 level, LIST_ENTRY* rollback) {
+static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
@@ -113,7 +113,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
     searchkey.obj_type = TYPE_EXTENT_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -153,7 +153,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
         *ptr = type;
         RtlCopyMemory(ptr + 1, data, datalen);
         
-        if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -184,13 +184,13 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), NULL, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), NULL, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             ExFreePool(ei);
             return STATUS_INTERNAL_ERROR;
         }
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -265,7 +265,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
                     
                     delete_tree_item(Vcb, &tp, rollback);
                     
-                    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+                    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
                         ERR("insert_tree_item failed\n");
                         return STATUS_INTERNAL_ERROR;
                     }
@@ -335,7 +335,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size + sizeof(UINT8) + datalen, NULL, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size + sizeof(UINT8) + datalen, NULL, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -352,7 +352,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
         searchkey.obj_type = type;
         searchkey.offset = offset;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -381,7 +381,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
             
             delete_tree_item(Vcb, &tp2, rollback);
             
-            if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, data2, tp2.item->size, NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, data2, tp2.item->size, NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 return STATUS_INTERNAL_ERROR;
             }
@@ -394,7 +394,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
             
             delete_tree_item(Vcb, &tp, rollback);
             
-            if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 return STATUS_INTERNAL_ERROR;
             }
@@ -408,7 +408,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
     data2 = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG);
     RtlCopyMemory(data2, data, datalen);
     
-    if (!insert_tree_item(Vcb, Vcb->extent_root, address, type, offset, data2, datalen, NULL, rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->extent_root, address, type, offset, data2, datalen, NULL, Irp, rollback)) {
         ERR("insert_tree_item failed\n");
         return STATUS_INTERNAL_ERROR;
     }
@@ -421,7 +421,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
     
     delete_tree_item(Vcb, &tp, rollback);
     
-    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
         ERR("insert_tree_item failed\n");
         return STATUS_INTERNAL_ERROR;
     }
@@ -429,7 +429,7 @@ static NTSTATUS increase_extent_refcount(device_extension* Vcb, UINT64 address,
     return STATUS_SUCCESS;
 }
 
-NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback) {
+NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode, UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback) {
     EXTENT_DATA_REF edr;
     
     edr.root = root;
@@ -437,7 +437,7 @@ NTSTATUS increase_extent_refcount_data(device_extension* Vcb, UINT64 address, UI
     edr.offset = offset;
     edr.count = refcount;
     
-    return increase_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, rollback);
+    return increase_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, Irp, rollback);
 }
 
 void decrease_chunk_usage(chunk* c, UINT64 delta) {
@@ -447,7 +447,7 @@ void decrease_chunk_usage(chunk* c, UINT64 delta) {
 }
 
 static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT8 type, void* data, KEY* firstitem,
-                                         UINT8 level, UINT64 parent, LIST_ENTRY* rollback) {
+                                         UINT8 level, UINT64 parent, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     NTSTATUS Status;
     traverse_ptr tp, tp2;
@@ -464,7 +464,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
     searchkey.obj_type = TYPE_EXTENT_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -498,13 +498,13 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), &tp, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ei, sizeof(EXTENT_ITEM), &tp, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             ExFreePool(ei);
             return STATUS_INTERNAL_ERROR;
         }
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -604,7 +604,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
                     
                     delete_tree_item(Vcb, &tp, rollback);
                     
-                    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, rollback)) {
+                    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, Irp, rollback)) {
                         ERR("insert_tree_item failed\n");
                         return STATUS_INTERNAL_ERROR;
                     }
@@ -643,7 +643,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
                     
                     delete_tree_item(Vcb, &tp, rollback);
                     
-                    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, rollback)) {
+                    if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, Irp, rollback)) {
                         ERR("insert_tree_item failed\n");
                         return STATUS_INTERNAL_ERROR;
                     }
@@ -670,7 +670,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
     searchkey.obj_type = type;
     searchkey.offset = (type == TYPE_SHARED_DATA_REF || type == TYPE_EXTENT_REF_V0) ? parent : get_extent_hash(type, data);
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -717,7 +717,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
                 
                 newedr->count -= edr->count;
                 
-                if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, newedr, tp2.item->size, NULL, rollback)) {
+                if (!insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, newedr, tp2.item->size, NULL, Irp, rollback)) {
                     ERR("insert_tree_item failed\n");
                     return STATUS_INTERNAL_ERROR;
                 }
@@ -736,7 +736,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
             
             delete_tree_item(Vcb, &tp, rollback);
             
-            if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 return STATUS_INTERNAL_ERROR;
             }
@@ -775,7 +775,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
             
             delete_tree_item(Vcb, &tp, rollback);
             
-            if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 return STATUS_INTERNAL_ERROR;
             }
@@ -810,7 +810,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -823,7 +823,7 @@ static NTSTATUS decrease_extent_refcount(device_extension* Vcb, UINT64 address,
 }
 
 NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 inode,
-                                       UINT64 offset, UINT32 refcount, LIST_ENTRY* rollback) {
+                                       UINT64 offset, UINT32 refcount, PIRP Irp, LIST_ENTRY* rollback) {
     EXTENT_DATA_REF edr;
     
     edr.root = root;
@@ -831,20 +831,20 @@ NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, UINT64 address, UI
     edr.offset = offset;
     edr.count = refcount;
     
-    return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, 0, rollback);
+    return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, 0, Irp, rollback);
 }
 
-NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, LIST_ENTRY* rollback) {
+NTSTATUS decrease_extent_refcount_shared_data(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, UINT64 parent, PIRP Irp, LIST_ENTRY* rollback) {
     SHARED_DATA_REF sdr;
 
     sdr.offset = treeaddr;
     sdr.count = 1;
     
-    return decrease_extent_refcount(Vcb, address, size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, parent, rollback);
+    return decrease_extent_refcount(Vcb, address, size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, parent, Irp, rollback);
 }
 
-NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, LIST_ENTRY* rollback) {
-    return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_REF_V0, NULL, NULL, 0, treeaddr, rollback);
+NTSTATUS decrease_extent_refcount_old(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 treeaddr, PIRP Irp, LIST_ENTRY* rollback) {
+    return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_REF_V0, NULL, NULL, 0, treeaddr, Irp, rollback);
 }
 
 typedef struct {
@@ -918,7 +918,7 @@ static NTSTATUS add_data_extent_ref(LIST_ENTRY* extent_refs, UINT64 tree_id, UIN
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 flags, LIST_ENTRY* extent_refs, LIST_ENTRY* rollback) {
+static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 flags, LIST_ENTRY* extent_refs, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY *le, *next_le;
     UINT64 refcount;
     ULONG inline_len;
@@ -1005,7 +1005,7 @@ static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UIN
         le = le->Flink;
     }
     
-    if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, inline_len, NULL, rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, inline_len, NULL, Irp, rollback)) {
         ERR("error - failed to insert item\n");
         ExFreePool(ei);
         return STATUS_INTERNAL_ERROR;
@@ -1017,7 +1017,7 @@ static NTSTATUS construct_extent_item(device_extension* Vcb, UINT64 address, UIN
         while (le != extent_refs) {
             extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
             
-            if (!insert_tree_item(Vcb, Vcb->extent_root, address, er->type, er->hash, er->data, get_extent_data_len(er->type), NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->extent_root, address, er->type, er->hash, er->data, get_extent_data_len(er->type), NULL, Irp, rollback)) {
                 ERR("error - failed to insert item\n");
                 return STATUS_INTERNAL_ERROR;
             }
@@ -1081,7 +1081,7 @@ static NTSTATUS populate_extent_refs_from_tree(device_extension* Vcb, UINT64 tre
     return STATUS_SUCCESS;
 }
 
-NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, LIST_ENTRY* rollback) {
+NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 size, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
     BOOL b;
@@ -1092,7 +1092,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s
     searchkey.obj_type = TYPE_EXTENT_ITEM;
     searchkey.offset = size;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -1114,7 +1114,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s
     searchkey.obj_type = TYPE_EXTENT_REF_V0;
     searchkey.offset = 0;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -1123,7 +1123,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s
     InitializeListHead(&extent_refs);
     
     do {
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         
         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
             Status = populate_extent_refs_from_tree(Vcb, tp.item->key.offset, address, &extent_refs);
@@ -1143,7 +1143,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s
         }
     } while (b);
     
-    Status = construct_extent_item(Vcb, address, size, EXTENT_ITEM_DATA, &extent_refs, rollback);
+    Status = construct_extent_item(Vcb, address, size, EXTENT_ITEM_DATA, &extent_refs, Irp, rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("construct_extent_item returned %08x\n", Status);
         free_extent_refs(&extent_refs);
@@ -1155,7 +1155,7 @@ NTSTATUS convert_old_data_extent(device_extension* Vcb, UINT64 address, UINT64 s
     return STATUS_SUCCESS;
 }
 
-UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset) {
+UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, PIRP Irp) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
@@ -1166,14 +1166,14 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s
     searchkey.obj_type = TYPE_EXTENT_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return 0;
     }
     
     if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
-        ERR("could not find address %llx in extent tree\n", address);
+        TRACE("could not find address %llx in extent tree\n", address);
         return 0;
     }
     
@@ -1249,7 +1249,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s
     searchkey.obj_type = TYPE_EXTENT_DATA_REF;
     searchkey.offset = get_extent_data_ref_hash2(root, objid, offset);
     
-    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return 0;
@@ -1272,7 +1272,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s
         searchkey.obj_type = TYPE_EXTENT_REF_V0;
         searchkey.offset = 0;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return 0;
@@ -1281,7 +1281,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s
         do {
             traverse_ptr next_tp;
             
-            b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+            b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
             
             if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
                 if (tp.item->size >= sizeof(EXTENT_REF_V0)) {
@@ -1335,7 +1335,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s
         searchkey.obj_type = TYPE_SHARED_DATA_REF;
         searchkey.offset = 0;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return 0;
@@ -1344,7 +1344,7 @@ UINT64 find_extent_data_refcount(device_extension* Vcb, UINT64 address, UINT64 s
         do {
             traverse_ptr next_tp;
             
-            b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+            b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
             
             if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
                 if (tp.item->size >= sizeof(SHARED_DATA_REF)) {
index 682489a..2b0608d 100644 (file)
@@ -67,7 +67,7 @@ static BOOLEAN STDCALL fast_query_basic_info(PFILE_OBJECT FileObject, BOOLEAN wa
     fbi->CreationTime.QuadPart = unix_time_to_win(&fcb->inode_item.otime);
     fbi->LastAccessTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_atime);
     fbi->LastWriteTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_mtime);
-    fbi->ChangeTime.QuadPart = 0;
+    fbi->ChangeTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_ctime);
     fbi->FileAttributes = fcb->atts;
 
     IoStatus->Status = STATUS_SUCCESS;
@@ -212,9 +212,22 @@ static BOOLEAN STDCALL fast_io_query_network_open_info(PFILE_OBJECT FileObject,
     return FALSE;
 }
 
-static NTSTATUS STDCALL fast_io_acquire_for_mod_write(PFILE_OBJECT FileObject, PLARGE_INTEGER EndingOffset, struct _ERESOURCE **ResourceToRelease, PDEVICE_OBJECT DeviceObject){
-    TRACE("STUB: fast_io_acquire_for_mod_write\n");
-    return STATUS_NOT_IMPLEMENTED;
+static NTSTATUS STDCALL fast_io_acquire_for_mod_write(PFILE_OBJECT FileObject, PLARGE_INTEGER EndingOffset, struct _ERESOURCE **ResourceToRelease, PDEVICE_OBJECT DeviceObject) {
+    fcb* fcb;
+    
+    TRACE("(%p, %llx, %p, %p)\n", FileObject, EndingOffset->QuadPart, ResourceToRelease, DeviceObject);
+    
+    fcb = FileObject->FsContext;
+    
+    if (!fcb)
+        return STATUS_INVALID_PARAMETER;
+    
+    *ResourceToRelease = fcb->Header.PagingIoResource;
+    
+    if (!ExAcquireResourceSharedLite(*ResourceToRelease, FALSE))
+        return STATUS_CANT_WAIT;
+    
+    return STATUS_SUCCESS;
 }
 
 static BOOLEAN STDCALL fast_io_read_compressed(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, ULONG LockKey, PVOID Buffer, PMDL *MdlChain, PIO_STATUS_BLOCK IoStatus, struct _COMPRESSED_DATA_INFO *CompressedDataInfo, ULONG CompressedDataInfoLength, PDEVICE_OBJECT DeviceObject){
index 4d0e323..092ed28 100644 (file)
@@ -24,7 +24,7 @@
 #endif
 #endif
 
-static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us);
+static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us, PIRP Irp);
 
 static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
     FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
@@ -65,7 +65,7 @@ static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, P
         LARGE_INTEGER time;
         BTRFS_TIME now;
         
-        defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE);
+        defda = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, fileref->filepart.Length > 0 && fileref->filepart.Buffer[0] == '.', TRUE, Irp);
         
         if (fcb->type == BTRFS_TYPE_DIRECTORY)
             fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
@@ -91,10 +91,37 @@ static NTSTATUS STDCALL set_basic_information(device_extension* Vcb, PIRP Irp, P
         filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
     }
     
-//     FIXME - CreationTime
-//     FIXME - LastAccessTime
-//     FIXME - LastWriteTime
-//     FIXME - ChangeTime
+    if (fbi->CreationTime.QuadPart == -1) {
+        FIXME("FIXME - support CreationTime == -1\n"); // FIXME - set ccb flag
+    } else if (fbi->CreationTime.QuadPart != 0) {
+        win_time_to_unix(fbi->CreationTime, &fcb->inode_item.otime);
+        inode_item_changed = TRUE;
+        filter |= FILE_NOTIFY_CHANGE_CREATION;
+    }
+    
+    if (fbi->LastAccessTime.QuadPart == -1) {
+        FIXME("FIXME - support LastAccessTime == -1\n"); // FIXME - set ccb flag
+    } else if (fbi->LastAccessTime.QuadPart != 0) {
+        win_time_to_unix(fbi->LastAccessTime, &fcb->inode_item.st_atime);
+        inode_item_changed = TRUE;
+        filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
+    }
+    
+    if (fbi->LastWriteTime.QuadPart == -1) {
+        FIXME("FIXME - support LastWriteTime == -1\n"); // FIXME - set ccb flag
+    } else if (fbi->LastWriteTime.QuadPart != 0) {
+        win_time_to_unix(fbi->LastWriteTime, &fcb->inode_item.st_mtime);
+        inode_item_changed = TRUE;
+        filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+    }
+    
+    if (fbi->ChangeTime.QuadPart == -1) {
+        FIXME("FIXME - support ChangeTime == -1\n"); // FIXME - set ccb flag
+    } else if (fbi->ChangeTime.QuadPart != 0) {
+        win_time_to_unix(fbi->ChangeTime, &fcb->inode_item.st_ctime);
+        inode_item_changed = TRUE;
+        // no filter for this
+    }
 
     if (inode_item_changed) {
         fcb->inode_item.transid = Vcb->superblock.generation;
@@ -171,7 +198,7 @@ end:
     return Status;
 }
 
-static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     INODE_EXTREF* ier;
@@ -181,7 +208,7 @@ static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 ino
     searchkey.obj_type = TYPE_INODE_EXTREF;
     searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
 
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -214,7 +241,7 @@ static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 ino
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier2, iersize, NULL, rollback)) {
+        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier2, iersize, NULL, Irp, rollback)) {
             ERR("error - failed to insert item\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -230,7 +257,7 @@ static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 ino
         ier->n = utf8->Length;
         RtlCopyMemory(ier->name, utf8->Buffer, utf8->Length);
     
-        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier, sizeof(INODE_EXTREF) - 1 + utf8->Length, NULL, rollback)) {
+        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ier, sizeof(INODE_EXTREF) - 1 + utf8->Length, NULL, Irp, rollback)) {
             ERR("error - failed to insert item\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -239,7 +266,7 @@ static NTSTATUS add_inode_extref(device_extension* Vcb, root* subvol, UINT64 ino
     return STATUS_SUCCESS;
 }
 
-NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, LIST_ENTRY* rollback) {
+NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, UINT64 index, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
     KEY searchkey;
     traverse_ptr tp;
     INODE_REF* ir;
@@ -249,7 +276,7 @@ NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64
     searchkey.obj_type = TYPE_INODE_REF;
     searchkey.offset = parinode;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -263,7 +290,7 @@ NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64
         if (irsize > maxlen) {
             if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
                 TRACE("INODE_REF too long, creating INODE_EXTREF\n");
-                return add_inode_extref(Vcb, subvol, inode, parinode, index, utf8, rollback);
+                return add_inode_extref(Vcb, subvol, inode, parinode, index, utf8, Irp, rollback);
             } else {
                 ERR("item would be too long (%u > %u)\n", irsize, maxlen);
                 return STATUS_INTERNAL_ERROR;
@@ -286,7 +313,7 @@ NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64
         
         delete_tree_item(Vcb, &tp, rollback);
         
-        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir2, irsize, NULL, rollback)) {
+        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir2, irsize, NULL, Irp, rollback)) {
             ERR("error - failed to insert item\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -301,7 +328,7 @@ NTSTATUS add_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64
         ir->n = utf8->Length;
         RtlCopyMemory(ir->name, utf8->Buffer, utf8->Length);
     
-        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir, sizeof(INODE_REF) - 1 + ir->n, NULL, rollback)) {
+        if (!insert_tree_item(Vcb, subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ir, sizeof(INODE_REF) - 1 + ir->n, NULL, Irp, rollback)) {
             ERR("error - failed to insert item\n");
             return STATUS_INTERNAL_ERROR;
         }
@@ -332,18 +359,19 @@ BOOL has_open_children(file_ref* fileref) {
 }
 
 static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
+    device_extension* Vcb = oldfcb->Vcb;
     fcb* fcb;
     LIST_ENTRY* le;
     
     // FIXME - we can skip a lot of this if the inode is about to be deleted
     
-    fcb = create_fcb();
+    fcb = create_fcb(PagedPool); // FIXME - what if we duplicate the paging file?
     if (!fcb) {
         ERR("out of memory\n");
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    fcb->Vcb = oldfcb->Vcb;
+    fcb->Vcb = Vcb;
 
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
     fcb->Header.AllocationSize = oldfcb->Header.AllocationSize;
@@ -355,6 +383,7 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
     if (oldfcb->ads) {
         fcb->ads = TRUE;
         fcb->adshash = oldfcb->adshash;
+        fcb->adsmaxlen = oldfcb->adsmaxlen;
         
         if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) {
             fcb->adsxattr.Length = oldfcb->adsxattr.Length;
@@ -363,7 +392,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
             
             if (!fcb->adsxattr.Buffer) {
                 ERR("out of memory\n");
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fcb(fcb);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
             
@@ -377,7 +410,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
             
             if (!fcb->adsdata.Buffer) {
                 ERR("out of memory\n");
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fcb(fcb);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
             
@@ -393,7 +430,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
         fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG);
         if (!fcb->sd) {
             ERR("out of memory\n");
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fcb(fcb);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
@@ -411,7 +452,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
             
             if (!ext2) {
                 ERR("out of memory\n");
+                
+                ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                 free_fcb(fcb);
+                ExReleaseResourceLite(&Vcb->fcb_lock);
+                
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
             
@@ -423,7 +468,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
                 
                 if (!ext2->data) {
                     ERR("out of memory\n");
+                    
+                    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
                     free_fcb(fcb);
+                    ExReleaseResourceLite(&Vcb->fcb_lock);
+                    
                     return STATUS_INSUFFICIENT_RESOURCES;
                 }
                 
@@ -448,7 +497,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
         
         if (!hl2) {
             ERR("out of memory\n");
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fcb(fcb);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
@@ -461,7 +514,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
         if (!hl2->name.Buffer) {
             ERR("out of memory\n");
             ExFreePool(hl2);
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fcb(fcb);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
@@ -474,7 +531,11 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
             ERR("out of memory\n");
             ExFreePool(hl2->name.Buffer);
             ExFreePool(hl2);
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fcb(fcb);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
@@ -493,41 +554,16 @@ static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) {
         fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG);
         if (!fcb->reparse_xattr.Buffer) {
             ERR("out of memory\n");
+            
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fcb(fcb);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+            
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
         RtlCopyMemory(fcb->reparse_xattr.Buffer, oldfcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
     }
-    
-    if (oldfcb->ads) {
-        fcb->ads = TRUE;
-        fcb->adshash = oldfcb->adshash;
-        
-        if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) {
-            fcb->adsxattr.Length = fcb->adsxattr.MaximumLength = oldfcb->adsxattr.Length;
-            fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG);
-            if (!fcb->adsxattr.Buffer) {
-                ERR("out of memory\n");
-                free_fcb(fcb);
-                return STATUS_INSUFFICIENT_RESOURCES;
-            }
-            
-            RtlCopyMemory(fcb->adsxattr.Buffer, oldfcb->adsxattr.Buffer, fcb->adsxattr.Length);
-        }
-        
-        if (oldfcb->adsdata.Buffer && oldfcb->adsdata.Length > 0) {
-            fcb->adsdata.Length = fcb->adsdata.MaximumLength = oldfcb->adsdata.Length;
-            fcb->adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsdata.MaximumLength, ALLOC_TAG);
-            if (!fcb->adsdata.Buffer) {
-                ERR("out of memory\n");
-                free_fcb(fcb);
-                return STATUS_INSUFFICIENT_RESOURCES;
-            }
-            
-            RtlCopyMemory(fcb->adsdata.Buffer, oldfcb->adsdata.Buffer, fcb->adsdata.Length);
-        }
-    }
 
 end:
     *pfcb = fcb;
@@ -543,7 +579,7 @@ typedef struct _move_entry {
     LIST_ENTRY list_entry;
 } move_entry;
 
-static NTSTATUS add_children_to_move_list(move_entry* me) {
+static NTSTATUS add_children_to_move_list(move_entry* me, PIRP Irp) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
@@ -586,7 +622,7 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
     searchkey.obj_type = TYPE_XATTR_ITEM;
     searchkey.offset = 0;
     
-    Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto end;
@@ -652,8 +688,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         RtlCopyMemory(xattr.Buffer, xa->name, xa->n);
                         xattr.Buffer[xa->n] = 0;
                         
+                        ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                         Status = open_fcb_stream(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, me->fileref->fcb->inode, &xattr,
-                                                 tp.item->key.offset, me->fileref->fcb, &fcb);
+                                                 tp.item->key.offset, me->fileref->fcb, &fcb, Irp);
+                        ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                        
                         if (!NT_SUCCESS(Status)) {
                             ERR("open_fcb_stream returned %08x\n", Status);
                             ExFreePool(xattr.Buffer);
@@ -663,7 +702,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         fr = create_fileref();
                         if (!fr) {
                             ERR("out of memory\n");
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fcb(fcb);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             Status = STATUS_INSUFFICIENT_RESOURCES;
                             goto end;
                         }
@@ -673,7 +716,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, &xa->name[xapreflen], xa->n - xapreflen);
                         if (!NT_SUCCESS(Status)) {
                             ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -681,14 +728,22 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         if (!fr->filepart.Buffer) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
                         Status = RtlUTF8ToUnicodeN(fr->filepart.Buffer, stringlen, &stringlen, &xa->name[xapreflen], xa->n - xapreflen);
                         if (!NT_SUCCESS(Status)) {
                             ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -697,7 +752,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         Status = RtlUpcaseUnicodeString(&fr->filepart_uc, &fr->filepart, TRUE);
                         if (!NT_SUCCESS(Status)) {
                             ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
 
@@ -710,7 +769,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         if (!me) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -730,7 +793,7 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
             } while (len > 0);
         }
         
-        b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp);
         if (b) {
             tp = next_tp;
             
@@ -744,7 +807,7 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
         searchkey.obj_type = TYPE_DIR_INDEX;
         searchkey.offset = 2;
         
-        Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE);
+        Status = find_item(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             goto end;
@@ -842,7 +905,9 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                             inode = di->key.obj_id;
                         }
                         
-                        Status = open_fcb(me->fileref->fcb->Vcb, subvol, inode, di->type, &utf8, me->fileref->fcb, &fcb);
+                        ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
+                        Status = open_fcb(me->fileref->fcb->Vcb, subvol, inode, di->type, &utf8, me->fileref->fcb, &fcb, Irp);
+                        ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
                         
                         if (!NT_SUCCESS(Status)) {
                             ERR("open_fcb returned %08x\n", Status);
@@ -855,7 +920,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
                             ExFreePool(utf8.Buffer);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fcb(fcb);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -865,7 +934,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, utf8.Buffer, utf8.Length);
                         if (!NT_SUCCESS(Status)) {
                             ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -873,7 +946,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         if (!fr->filepart.Buffer) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -881,7 +958,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         
                         if (!NT_SUCCESS(Status)) {
                             ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -891,7 +972,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         
                         if (!NT_SUCCESS(Status)) {
                             ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -909,7 +994,11 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                         if (!me) {
                             ERR("out of memory\n");
                             Status = STATUS_INSUFFICIENT_RESOURCES;
+                            
+                            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
                             free_fileref(fr);
+                            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
+                            
                             goto end;
                         }
                         
@@ -927,7 +1016,7 @@ static NTSTATUS add_children_to_move_list(move_entry* me) {
                 }
             }
             
-            b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE);
+            b = find_next_item(me->fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp);
             if (b) {
                 tp = next_tp;
                 
@@ -945,7 +1034,7 @@ end:
     return Status;
 }
 
-static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, LIST_ENTRY* rollback) {
+static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     LIST_ENTRY move_list, *le;
     move_entry* me;
@@ -980,7 +1069,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
         ExAcquireResourceSharedLite(me->fileref->fcb->Header.Resource, TRUE);
         
         if (!me->fileref->fcb->ads && me->fileref->fcb->subvol == origparent->fcb->subvol) {
-            Status = add_children_to_move_list(me);
+            Status = add_children_to_move_list(me, Irp);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("add_children_to_move_list returned %08x\n", Status);
@@ -1031,14 +1120,14 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
                     LIST_ENTRY* le2;
                     
                     if (destdir->fcb->subvol->lastinode == 0)
-                        get_last_inode(destdir->fcb->Vcb, destdir->fcb->subvol);
+                        get_last_inode(destdir->fcb->Vcb, destdir->fcb->subvol, Irp);
 
                     me->fileref->fcb->subvol = destdir->fcb->subvol;
                     me->fileref->fcb->inode = ++destdir->fcb->subvol->lastinode; // FIXME - do proper function for this
                     me->fileref->fcb->inode_item.st_nlink = 1;
                     
                     defda = get_file_attributes(me->fileref->fcb->Vcb, &me->fileref->fcb->inode_item, me->fileref->fcb->subvol, me->fileref->fcb->inode,
-                                                me->fileref->fcb->type, me->fileref->filepart.Length > 0 && me->fileref->filepart.Buffer[0] == '.', TRUE);
+                                                me->fileref->fcb->type, me->fileref->filepart.Length > 0 && me->fileref->filepart.Buffer[0] == '.', TRUE, Irp);
                     
                     me->fileref->fcb->sd_dirty = !!me->fileref->fcb->sd;
                     me->fileref->fcb->atts_changed = defda != me->fileref->fcb->atts;
@@ -1060,7 +1149,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
                                     ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
                                 } else {
                                     Status = update_changed_extent_ref(me->fileref->fcb->Vcb, c, ed2->address, ed2->size, me->fileref->fcb->subvol->id, me->fileref->fcb->inode,
-                                                                       ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size);
+                                                                       ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
                                     
                                     if (!NT_SUCCESS(Status)) {
                                         ERR("update_changed_extent_ref returned %08x\n", Status);
@@ -1221,13 +1310,15 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
         if (!me->parent) {
             RemoveEntryList(&me->fileref->list_entry);
             
+            ExAcquireResourceExclusiveLite(&me->fileref->fcb->Vcb->fcb_lock, TRUE);
             free_fileref(me->fileref->parent);
+            ExReleaseResourceLite(&me->fileref->fcb->Vcb->fcb_lock);
             
             me->fileref->parent = destdir;
             
             increase_fileref_refcount(destdir);
             
-            Status = fcb_get_last_dir_index(me->fileref->parent->fcb, &me->fileref->index);
+            Status = fcb_get_last_dir_index(me->fileref->parent->fcb, &me->fileref->index, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("fcb_get_last_dir_index returned %08x\n", Status);
                 goto end;
@@ -1249,7 +1340,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
             me->fileref->fcb->subvol->root_item.num_references++;
 
         if (!me->dummyfileref->fcb->ads) {
-            Status = delete_fileref(me->dummyfileref, NULL, rollback);
+            Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_fileref returned %08x\n", Status);
                 goto end;
@@ -1303,7 +1394,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
         me = CONTAINING_RECORD(le, move_entry, list_entry);
         
         if (me->dummyfileref->fcb->ads && me->parent->dummyfileref->fcb->deleted) {
-            Status = delete_fileref(me->dummyfileref, NULL, rollback);
+            Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_fileref returned %08x\n", Status);
                 goto end;
@@ -1326,23 +1417,36 @@ static NTSTATUS move_across_subvols(file_ref* fileref, file_ref* destdir, PANSI_
     
 end:
     while (!IsListEmpty(&move_list)) {
+        device_extension* Vcb;
+        
         le = RemoveHeadList(&move_list);
         me = CONTAINING_RECORD(le, move_entry, list_entry);
+        Vcb = me->fileref->fcb->Vcb;
         
-        if (me->dummyfcb)
+        if (me->dummyfcb) {
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fcb(me->dummyfcb);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+        }
         
-        if (me->dummyfileref)
+        if (me->dummyfileref) {
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fileref(me->dummyfileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
+        }
         
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(me->fileref);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+        
         ExFreePool(me);
     }
     
     return Status;
 }
 
-static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, BOOL ReplaceIfExists) {
+static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer;
     fcb *fcb = FileObject->FsContext;
     ccb* ccb = FileObject->FsContext2;
@@ -1360,11 +1464,8 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     
     InitializeListHead(&rollback);
     
-    // FIXME - check fri length
-    // FIXME - don't ignore fri->RootDirectory
-    
     TRACE("tfo = %p\n", tfo);
-    TRACE("ReplaceIfExists = %u\n", ReplaceIfExists);
+    TRACE("ReplaceIfExists = %u\n", IrpSp->Parameters.SetFile.ReplaceIfExists);
     TRACE("RootDirectory = %p\n", fri->RootDirectory);
     TRACE("FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName);
     
@@ -1379,6 +1480,12 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     } else {
         LONG i;
         
+        while (fnlen > 0 && (fri->FileName[fnlen - 1] == '/' || fri->FileName[fnlen - 1] == '\\'))
+            fnlen--;
+        
+        if (fnlen == 0)
+            return STATUS_INVALID_PARAMETER;
+        
         for (i = fnlen - 1; i >= 0; i--) {
             if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') {
                 fn = &fri->FileName[i+1];
@@ -1425,13 +1532,15 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
         increase_fileref_refcount(related);
     }
 
-    Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL);
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, Irp);
+    ExReleaseResourceLite(&Vcb->fcb_lock);
 
     if (NT_SUCCESS(Status)) {
         TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref));
         
         if (fileref != oldfileref && !oldfileref->deleted) {
-            if (!ReplaceIfExists) {
+            if (!IrpSp->Parameters.SetFile.ReplaceIfExists) {
                 Status = STATUS_OBJECT_NAME_COLLISION;
                 goto end;
             } else if ((oldfileref->fcb->open_count >= 1 || has_open_children(oldfileref)) && !oldfileref->deleted) {
@@ -1447,14 +1556,18 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
             }
         }
         
-        if (fileref == oldfileref || !oldfileref->deleted) {
+        if (fileref == oldfileref || oldfileref->deleted) {
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fileref(oldfileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
             oldfileref = NULL;
         }
     }
     
     if (!related) {
-        Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, Irp);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
 
         if (!NT_SUCCESS(Status)) {
             ERR("open_fileref returned %08x\n", Status);
@@ -1469,8 +1582,21 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     }
     
     if (oldfileref) {
-        // FIXME - check we have permissions for this
-        Status = delete_fileref(oldfileref, NULL, &rollback);
+        ACCESS_MASK access;
+        SECURITY_SUBJECT_CONTEXT subjcont;
+        
+        SeCaptureSubjectContext(&subjcont);
+
+        if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL,
+                           IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) {
+            SeReleaseSubjectContext(&subjcont);
+            WARN("SeAccessCheck failed, returning %08x\n", Status);
+            goto end;
+        }
+
+        SeReleaseSubjectContext(&subjcont);
+        
+        Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("delete_fileref returned %08x\n", Status);
             goto end;
@@ -1478,7 +1604,7 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     }
     
     if (fileref->parent->fcb->subvol != related->fcb->subvol && fileref->fcb->subvol == fileref->parent->fcb->subvol) {
-        Status = move_across_subvols(fileref, related, &utf8, &fnus, &rollback);
+        Status = move_across_subvols(fileref, related, &utf8, &fnus, Irp, &rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("move_across_subvols returned %08x\n", Status);
         }
@@ -1593,7 +1719,7 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     if (fr2->fcb->type == BTRFS_TYPE_DIRECTORY)
         fr2->fcb->fileref = fr2;
 
-    Status = fcb_get_last_dir_index(related->fcb, &index);
+    Status = fcb_get_last_dir_index(related->fcb, &index, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("fcb_get_last_dir_index returned %08x\n", Status);
         goto end;
@@ -1726,7 +1852,9 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     fr2->parent->fcb->inode_item.st_ctime = now;
     fr2->parent->fcb->inode_item.st_mtime = now;
     
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
     free_fileref(fr2);
+    ExReleaseResourceLite(&Vcb->fcb_lock);
     
     mark_fcb_dirty(fr2->parent->fcb);
     
@@ -1738,14 +1866,23 @@ static NTSTATUS STDCALL set_rename_information(device_extension* Vcb, PIRP Irp,
     Status = STATUS_SUCCESS;
     
 end:
-    if (oldfileref)
+    if (oldfileref) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(oldfileref);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
     
-    if (!NT_SUCCESS(Status) && related)
+    if (!NT_SUCCESS(Status) && related) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(related);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
     
-    if (!NT_SUCCESS(Status) && fr2)
+    if (!NT_SUCCESS(Status) && fr2) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(fr2);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
     
     if (NT_SUCCESS(Status))
         clear_rollback(&rollback);
@@ -1778,39 +1915,12 @@ NTSTATUS STDCALL stream_set_end_of_file_information(device_extension* Vcb, UINT6
         
         fcb->adsdata.Length = end;
     } else if (end > fcb->adsdata.Length) {
-//         UINT16 maxlen;
-        
         TRACE("extending stream to %llx bytes\n", end);
-//         
-//         // find maximum length of xattr
-//         maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
-//         
-//         searchkey.obj_id = fcb->inode;
-//         searchkey.obj_type = TYPE_XATTR_ITEM;
-//         searchkey.offset = fcb->adshash;
-// 
-//         Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
-//         if (!NT_SUCCESS(Status)) {
-//             ERR("error - find_item returned %08x\n", Status);
-//             return Status;
-//         }
-//         
-//         if (keycmp(&tp.item->key, &searchkey)) {
-//             ERR("error - could not find key for xattr\n");
-//             return STATUS_INTERNAL_ERROR;
-//         }
-//         
-//         if (!get_xattr(Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, &data, &datalen)) {
-//             ERR("get_xattr failed\n");
-//             return STATUS_INTERNAL_ERROR;
-//         }
-//         
-//         maxlen -= tp.item->size - datalen; // subtract XATTR_ITEM overhead
-//         
-//         if (end > maxlen) {
-//             ERR("error - xattr too long (%llu > %u)\n", end, maxlen);
-//             return STATUS_DISK_FULL;
-//         }
+
+        if (end > fcb->adsmaxlen) {
+            ERR("error - xattr too long (%llu > %u)\n", end, fcb->adsmaxlen);
+            return STATUS_DISK_FULL;
+        }
 
         if (end > fcb->adsdata.MaximumLength) {
             char* data = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG);
@@ -1913,7 +2023,7 @@ static NTSTATUS STDCALL set_end_of_file_information(device_extension* Vcb, PIRP
         
         TRACE("truncating file to %llx bytes\n", feofi->EndOfFile.QuadPart);
         
-        Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, &rollback);
+        Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, Irp, &rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("error - truncate_file failed\n");
             goto end;
@@ -2031,6 +2141,12 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF
         tfofcb = tfo->FsContext;
         parfcb = tfofcb;
         
+        while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\'))
+            fnlen--;
+        
+        if (fnlen == 0)
+            return STATUS_INVALID_PARAMETER;
+        
         for (i = fnlen - 1; i >= 0; i--) {
             if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') {
                 fn = &fli->FileName[i+1];
@@ -2083,7 +2199,9 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF
         increase_fileref_refcount(related);
     }
 
-    Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL);
+    ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+    Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, Irp);
+    ExReleaseResourceLite(&Vcb->fcb_lock);
 
     if (NT_SUCCESS(Status)) {
         if (!oldfileref->deleted) {
@@ -2107,13 +2225,17 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF
                 goto end;
             }
         } else {
+            ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
             free_fileref(oldfileref);
+            ExReleaseResourceLite(&Vcb->fcb_lock);
             oldfileref = NULL;
         }
     }
     
     if (!related) {
-        Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL);
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
+        Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, Irp);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
 
         if (!NT_SUCCESS(Status)) {
             ERR("open_fileref returned %08x\n", Status);
@@ -2150,14 +2272,14 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF
 
         SeReleaseSubjectContext(&subjcont);
         
-        Status = delete_fileref(oldfileref, NULL, &rollback);
+        Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("delete_fileref returned %08x\n", Status);
             goto end;
         }
     }
     
-    Status = fcb_get_last_dir_index(related->fcb, &index);
+    Status = fcb_get_last_dir_index(related->fcb, &index, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("fcb_get_last_dir_index returned %08x\n", Status);
         goto end;
@@ -2259,14 +2381,23 @@ static NTSTATUS STDCALL set_link_information(device_extension* Vcb, PIRP Irp, PF
     Status = STATUS_SUCCESS;
     
 end:
-    if (oldfileref)
+    if (oldfileref) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(oldfileref);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
     
-    if (!NT_SUCCESS(Status) && related)
+    if (!NT_SUCCESS(Status) && related) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(related);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
     
-    if (!NT_SUCCESS(Status) && fr2)
+    if (!NT_SUCCESS(Status) && fr2) {
+        ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
         free_fileref(fr2);
+        ExReleaseResourceLite(&Vcb->fcb_lock);
+    }
     
     if (NT_SUCCESS(Status))
         clear_rollback(&rollback);
@@ -2296,7 +2427,12 @@ NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp
         goto exit;
     }
     
-    if (Vcb->readonly) {
+    if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
+        Status = STATUS_ACCESS_DENIED;
+        goto end;
+    }
+    
+    if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) {
         Status = STATUS_MEDIA_WRITE_PROTECTED;
         goto end;
     }
@@ -2313,7 +2449,7 @@ NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp
         goto end;
     }
     
-    if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
+    if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) {
         Status = STATUS_ACCESS_DENIED;
         goto end;
     }
@@ -2408,7 +2544,7 @@ NTSTATUS STDCALL drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp
         case FileRenameInformation:
             TRACE("FileRenameInformation\n");
             // FIXME - make this work with streams
-            Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, IrpSp->Parameters.SetFile.ReplaceIfExists);
+            Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject);
             break;
 
         case FileValidDataLengthInformation:
@@ -2457,7 +2593,7 @@ static NTSTATUS STDCALL fill_in_file_basic_information(FILE_BASIC_INFORMATION* f
     fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
     fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
     fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
-    fbi->ChangeTime.QuadPart = 0;
+    fbi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
     
     if (fcb->ads) {
         if (!fileref || !fileref->parent) {
@@ -2496,7 +2632,7 @@ static NTSTATUS STDCALL fill_in_file_network_open_information(FILE_NETWORK_OPEN_
     fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
     fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
     fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
-    fnoi->ChangeTime.QuadPart = 0;
+    fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
     
     if (fcb->ads) {
         fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length;
@@ -2776,7 +2912,7 @@ static NTSTATUS STDCALL fill_in_file_name_information(FILE_NAME_INFORMATION* fni
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, file_ref* fileref, LONG* length) {
+static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, file_ref* fileref, PIRP Irp, LONG* length) {
     *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
     
     if (fcb->ads) {
@@ -2789,7 +2925,10 @@ static NTSTATUS STDCALL fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_IN
     } else
         ati->FileAttributes = fcb->atts;
     
-    ati->ReparseTag = 0; // FIXME
+    if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+        ati->ReparseTag = 0;
+    else
+        ati->ReparseTag = get_reparse_tag(fcb->Vcb, fcb->subvol, fcb->inode, fcb->type, fcb->atts, Irp);
     
     return STATUS_SUCCESS;
 }
@@ -2801,7 +2940,7 @@ typedef struct {
     LIST_ENTRY list_entry;
 } stream_info;
 
-static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) {
+static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, PIRP Irp, LONG* length) {
     ULONG reqsize;
     LIST_ENTRY streamlist, *le;
     FILE_STREAM_INFORMATION *entry, *lastentry;
@@ -2832,7 +2971,7 @@ static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION*
     searchkey.obj_type = TYPE_XATTR_ITEM;
     searchkey.offset = 0;
 
-    Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE);
+    Status = find_item(fileref->fcb->Vcb, fileref->fcb->subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto end;
@@ -2916,7 +3055,7 @@ static NTSTATUS STDCALL fill_in_file_stream_information(FILE_STREAM_INFORMATION*
             }
         }
         
-        b = find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(fileref->fcb->Vcb, &tp, &next_tp, FALSE, Irp);
         if (b) {
             tp = next_tp;
             
@@ -3086,7 +3225,7 @@ typedef struct {
     LIST_ENTRY list_entry;
 } name_bit;
 
-static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol) {
+static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     NTSTATUS Status;
@@ -3109,7 +3248,7 @@ static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol) {
     searchkey.obj_type = TYPE_ROOT_BACKREF;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -3156,7 +3295,7 @@ static NTSTATUS get_subvol_path(device_extension* Vcb, root* subvol) {
     
     // FIXME - recursion
 
-    Status = get_inode_dir_path(Vcb, parsubvol, rr->dir, &dirpath);
+    Status = get_inode_dir_path(Vcb, parsubvol, rr->dir, &dirpath, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("get_inode_dir_path returned %08x\n", Status);
         return Status;
@@ -3206,7 +3345,7 @@ end:
     return Status;
 }
 
-static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us) {
+static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 inode, PUNICODE_STRING us, PIRP Irp) {
     KEY searchkey;
     NTSTATUS Status;
     UINT64 in;
@@ -3222,7 +3361,7 @@ static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 i
     
     // FIXME - start with subvol prefix
     if (!subvol->path.Buffer) {
-        Status = get_subvol_path(Vcb, subvol);
+        Status = get_subvol_path(Vcb, subvol, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("get_subvol_path returned %08x\n", Status);
             return Status;
@@ -3234,7 +3373,7 @@ static NTSTATUS get_inode_dir_path(device_extension* Vcb, root* subvol, UINT64 i
         searchkey.obj_type = TYPE_INODE_EXTREF;
         searchkey.offset = 0xffffffffffffffff;
         
-        Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             goto end;
@@ -3359,13 +3498,13 @@ end:
     return Status;
 }
 
-NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr) {
+NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp) {
     NTSTATUS Status;
     fcb* fcb;
     hardlink* hl;
     file_ref *parfr, *fr;
     
-    Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb);
+    Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
         return Status;
@@ -3390,7 +3529,7 @@ NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode
     if (hl->parent == inode) // root of subvol
         parfr = NULL;
     else {
-        Status = open_fileref_by_inode(Vcb, subvol, hl->parent, &parfr);
+        Status = open_fileref_by_inode(Vcb, subvol, hl->parent, &parfr, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("open_fileref_by_inode returned %08x\n", Status);
             free_fcb(fcb);
@@ -3453,7 +3592,7 @@ NTSTATUS open_fileref_by_inode(device_extension* Vcb, root* subvol, UINT64 inode
 }
 
 #ifndef __REACTOS__
-static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, LONG* length) {
+static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) {
     NTSTATUS Status;
     LIST_ENTRY* le;
     ULONG bytes_needed;
@@ -3517,7 +3656,7 @@ static NTSTATUS STDCALL fill_in_hard_link_information(FILE_LINKS_INFORMATION* fl
             
             TRACE("parent %llx, index %llx, name %.*S\n", hl->parent, hl->index, hl->name.Length / sizeof(WCHAR), hl->name.Buffer);
             
-            Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr);
+            Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("open_fileref_by_inode returned %08x\n", Status);
@@ -3707,7 +3846,7 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec
                 goto exit;
             }
             
-            Status = fill_in_file_attribute_information(ati, fcb, fileref, &length);
+            Status = fill_in_file_attribute_information(ati, fcb, fileref, Irp, &length);
             
             break;
         }
@@ -3841,7 +3980,7 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec
             
             TRACE("FileStreamInformation\n");
             
-            Status = fill_in_file_stream_information(fsi, fileref, &length);
+            Status = fill_in_file_stream_information(fsi, fileref, Irp, &length);
 
             break;
         }
@@ -3853,7 +3992,7 @@ static NTSTATUS STDCALL query_info(device_extension* Vcb, PFILE_OBJECT FileObjec
             
             TRACE("FileHardLinkInformation\n");
             
-            Status = fill_in_hard_link_information(fli, fileref, &length);
+            Status = fill_in_hard_link_information(fli, fileref, Irp, &length);
             
             break;
         }
index 16795eb..48eabad 100644 (file)
@@ -17,8 +17,6 @@
 
 #include "btrfs_drv.h"
 
-#define INTERVAL 15000 // in milliseconds
-
 static void do_flush(device_extension* Vcb) {
     LIST_ENTRY rollback;
     
@@ -28,8 +26,8 @@ static void do_flush(device_extension* Vcb) {
 
     ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
 
-    if (Vcb->need_write)
-        do_write(Vcb, &rollback);
+    if (Vcb->need_write && !Vcb->readonly)
+        do_write(Vcb, NULL, &rollback);
     
     free_trees(Vcb);
     
@@ -49,7 +47,7 @@ void STDCALL flush_thread(void* context) {
     
     KeInitializeTimer(&Vcb->flush_thread_timer);
     
-    due_time.QuadPart = -INTERVAL * 10000;
+    due_time.QuadPart = (UINT64)Vcb->options.flush_interval * -10000000;
     
     KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL);
     
index 5f583c1..6982ae0 100644 (file)
 
 // #define DEBUG_SPACE_LISTS
 
-static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIST_ENTRY* rollback) {
+static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     fcb* fcb;
     
-    Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &fcb);
+    Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &fcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
         return Status;
@@ -36,7 +36,7 @@ static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIS
     fcb->dirty = TRUE;
     
     if (fcb->inode_item.st_size > 0) {
-        Status = excise_extents(fcb->Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), rollback);
+        Status = excise_extents(fcb->Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), Irp, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("excise_extents returned %08x\n", Status);
             return Status;
@@ -45,14 +45,14 @@ static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIS
     
     fcb->deleted = TRUE;
     
-    flush_fcb(fcb, FALSE, rollback);
+    flush_fcb(fcb, FALSE, Irp, rollback);
     
     free_fcb(fcb);
 
     return STATUS_SUCCESS;
 }
 
-NTSTATUS clear_free_space_cache(device_extension* Vcb) {
+NTSTATUS clear_free_space_cache(device_extension* Vcb, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
     NTSTATUS Status;
@@ -65,7 +65,7 @@ NTSTATUS clear_free_space_cache(device_extension* Vcb) {
     searchkey.obj_type = 0;
     searchkey.offset = 0;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -86,7 +86,7 @@ NTSTATUS clear_free_space_cache(device_extension* Vcb) {
                 else {
                     LIST_ENTRY* le;
                     
-                    Status = remove_free_space_inode(Vcb, fsi->key.obj_id, &rollback);
+                    Status = remove_free_space_inode(Vcb, fsi->key.obj_id, Irp, &rollback);
                     
                     if (!NT_SUCCESS(Status)) {
                         ERR("remove_free_space_inode for (%llx,%x,%llx) returned %08x\n", fsi->key.obj_id, fsi->key.obj_type, fsi->key.offset, Status);
@@ -109,7 +109,7 @@ NTSTATUS clear_free_space_cache(device_extension* Vcb) {
                 WARN("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(FREE_SPACE_ITEM));
         }
         
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         if (b)
             tp = next_tp;
     } while (b);
@@ -245,7 +245,7 @@ static void order_space_entry(space* s, LIST_ENTRY* list_size) {
     InsertTailList(list_size, &s->list_entry_size);
 }
 
-static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
+static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp;
     FREE_SPACE_ITEM* fsi;
@@ -266,7 +266,7 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
     searchkey.obj_type = 0;
     searchkey.offset = c->offset;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -293,10 +293,10 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
     num_entries = fsi->num_entries;
     num_bitmaps = fsi->num_bitmaps;
     
-    Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &c->cache);
+    Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &c->cache, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
-        return Status;
+        return STATUS_NOT_FOUND;
     }
     
     if (c->cache->inode_item.st_size == 0) {
@@ -329,7 +329,7 @@ static NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
         
         free_fcb(c->cache);
         c->cache = NULL;
-        return Status;
+        return STATUS_NOT_FOUND;
     }
     
     if (size > c->cache->inode_item.st_size)
@@ -446,7 +446,7 @@ clearcache:
     
     InitializeListHead(&rollback);
     
-    Status = excise_extents(Vcb, c->cache, 0, c->cache->inode_item.st_size, &rollback);
+    Status = excise_extents(Vcb, c->cache, 0, c->cache->inode_item.st_size, Irp, &rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("excise_extents returned %08x\n", Status);
         do_rollback(Vcb, &rollback);
@@ -463,7 +463,7 @@ clearcache:
     return STATUS_NOT_FOUND;
 }
 
-NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) {
+NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c, PIRP Irp) {
     traverse_ptr tp, next_tp;
     KEY searchkey;
     UINT64 lastaddr;
@@ -473,7 +473,7 @@ NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) {
 //     LIST_ENTRY* le;
     
     if (Vcb->superblock.generation - 1 == Vcb->superblock.cache_generation) {
-        Status = load_stored_free_space_cache(Vcb, c);
+        Status = load_stored_free_space_cache(Vcb, c, Irp);
         
         if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) {
             ERR("load_stored_free_space_cache returned %08x\n", Status);
@@ -489,7 +489,7 @@ NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) {
         searchkey.obj_type = TYPE_EXTENT_ITEM;
         searchkey.offset = 0;
         
-        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -525,7 +525,7 @@ NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) {
                     lastaddr = tp.item->key.obj_id + tp.item->key.offset;
             }
             
-            b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+            b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
             if (b)
                 tp = next_tp;
         } while (b);
@@ -568,48 +568,52 @@ static NTSTATUS insert_cache_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_
     
     flags = fcb->Vcb->data_flags;
     
-    ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+    ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
     
     while (le != &fcb->Vcb->chunks) {
         c = CONTAINING_RECORD(le, chunk, list_entry);
         
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
         if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
-            if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback)) {
-                ExReleaseResourceLite(&c->nonpaged->lock);
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, length)) {
+                ExReleaseResourceLite(&c->lock);
                 ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
                 return STATUS_SUCCESS;
             }
         }
         
-        ExReleaseResourceLite(&c->nonpaged->lock);
+        ExReleaseResourceLite(&c->lock);
         
         le = le->Flink;
     }
     
-    if ((c = alloc_chunk(fcb->Vcb, flags, rollback))) {
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
+    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+    ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+    
+    if ((c = alloc_chunk(fcb->Vcb, flags))) {
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+        
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
         
         if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
-            if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback)) {
-                ExReleaseResourceLite(&c->nonpaged->lock);
-                ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, length)) {
+                ExReleaseResourceLite(&c->lock);
                 return STATUS_SUCCESS;
             }
         }
         
-        ExReleaseResourceLite(&c->nonpaged->lock);
-    }
-    
-    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+        ExReleaseResourceLite(&c->lock);
+    } else   
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
     
     WARN("couldn't find any data chunks with %llx bytes free\n", length);
 
     return STATUS_DISK_FULL;
 }
 
-static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* changed, LIST_ENTRY* rollback) {
+static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* changed, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY* le;
     NTSTATUS Status;
     UINT64 num_entries, new_cache_size, i;
@@ -669,7 +673,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         
         // create new inode
         
-        c->cache = create_fcb();
+        c->cache = create_fcb(PagedPool);
         if (!c->cache) {
             ERR("out of memory\n");
             return STATUS_INSUFFICIENT_RESOURCES;
@@ -691,7 +695,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         c->cache->subvol = Vcb->root_root;
         
         if (Vcb->root_root->lastinode == 0)
-            get_last_inode(Vcb, Vcb->root_root);
+            get_last_inode(Vcb, Vcb->root_root, Irp);
         
         c->cache->inode = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
         
@@ -712,9 +716,12 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         searchkey.obj_type = 0;
         searchkey.offset = c->offset;
         
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
+            ExFreePool(fsi);
+            free_fcb(c->cache);
+            c->cache = NULL;
             return Status;
         }
         
@@ -725,7 +732,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         fsi->key.obj_type = TYPE_INODE_ITEM;
         fsi->key.offset = 0;
         
-        if (!insert_tree_item(Vcb, Vcb->root_root, FREE_SPACE_CACHE_ID, 0, c->offset, fsi, sizeof(FREE_SPACE_ITEM), NULL, rollback)) {
+        if (!insert_tree_item(Vcb, Vcb->root_root, FREE_SPACE_CACHE_ID, 0, c->offset, fsi, sizeof(FREE_SPACE_ITEM), NULL, Irp, rollback)) {
             ERR("insert_tree_item failed\n");
             free_fcb(c->cache);
             c->cache = NULL;
@@ -746,7 +753,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         
         Vcb->root_root->lastinode = c->cache->inode;
         
-        flush_fcb(c->cache, TRUE, rollback);
+        flush_fcb(c->cache, TRUE, Irp, rollback);
         
         *changed = TRUE;
     } else if (new_cache_size > c->cache->inode_item.st_size) {
@@ -764,7 +771,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         searchkey.obj_type = 0;
         searchkey.offset = c->offset;
         
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -795,7 +802,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         c->cache->inode_item.st_size = new_cache_size;
         c->cache->inode_item.st_blocks = new_cache_size;
         
-        flush_fcb(c->cache, TRUE, rollback);
+        flush_fcb(c->cache, TRUE, Irp, rollback);
     
         *changed = TRUE;
     } else {
@@ -808,7 +815,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         searchkey.obj_type = TYPE_INODE_ITEM;
         searchkey.offset = 0;
         
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -820,10 +827,12 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
             ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
             RtlCopyMemory(ii, &c->cache->inode_item, sizeof(INODE_ITEM));
             
-            if (!insert_tree_item(Vcb, Vcb->root_root, c->cache->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, rollback)) {
+            if (!insert_tree_item(Vcb, Vcb->root_root, c->cache->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp, rollback)) {
                 ERR("insert_tree_item failed\n");
                 return STATUS_INTERNAL_ERROR;
             }
+            
+            *changed = TRUE;
         } else {        
             if (tp.item->size < sizeof(INODE_ITEM)) {
                 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(INODE_ITEM));
@@ -837,7 +846,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
         searchkey.obj_type = 0;
         searchkey.offset = c->offset;
         
-        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("error - find_item returned %08x\n", Status);
             return Status;
@@ -861,7 +870,7 @@ static NTSTATUS allocate_cache_chunk(device_extension* Vcb, chunk* c, BOOL* chan
     return STATUS_SUCCESS;
 }
 
-NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollback) {
+NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY* le = Vcb->chunks_changed.Flink;
     NTSTATUS Status;
 
@@ -871,9 +880,9 @@ NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollba
         BOOL b;
         chunk* c = CONTAINING_RECORD(le, chunk, list_entry_changed);
 
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
-        Status = allocate_cache_chunk(Vcb, c, &b, rollback);
-        ExReleaseResourceLite(&c->nonpaged->lock);
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        Status = allocate_cache_chunk(Vcb, c, &b, Irp, rollback);
+        ExReleaseResourceLite(&c->lock);
         
         if (b)
             *changed = TRUE;
@@ -889,7 +898,25 @@ NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, LIST_ENTRY* rollba
     return STATUS_SUCCESS;
 }
 
-void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) {
+static void add_rollback_space(LIST_ENTRY* rollback, BOOL add, LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c) {
+    rollback_space* rs;
+    
+    rs = ExAllocatePoolWithTag(PagedPool, sizeof(rollback_space), ALLOC_TAG);
+    if (!rs) {
+        ERR("out of memory\n");
+        return;
+    }
+    
+    rs->list = list;
+    rs->list_size = list_size;
+    rs->address = address;
+    rs->length = length;
+    rs->chunk = c;
+    
+    add_rollback(rollback, add ? ROLLBACK_ADD_SPACE : ROLLBACK_SUBTRACT_SPACE, rs);
+}
+
+void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func) {
     LIST_ENTRY* le;
     space *s, *s2;
     
@@ -912,7 +939,8 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
         if (list_size)
             InsertTailList(list_size, &s->list_entry_size);
         
-        // FIXME - insert rollback entry
+        if (rollback)
+            add_rollback_space(rollback, TRUE, list, list_size, address, length, c);
         
         return;
     }
@@ -928,9 +956,11 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
         // new entry envelops old one completely
         if (address <= s2->address && address + length >= s2->address + s2->size) {
             if (address < s2->address) {
+                if (rollback)
+                    add_rollback_space(rollback, TRUE, list, list_size, address, s2->address - address, c);
+                
                 s2->size += s2->address - address;
                 s2->address = address;
-                // FIXME - insert rollback
                 
                 while (s2->list_entry.Blink != list) {
                     space* s3 = CONTAINING_RECORD(s2->list_entry.Blink, space, list_entry);
@@ -951,8 +981,10 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
             }
             
             if (length > s2->size) {
+                if (rollback)
+                    add_rollback_space(rollback, TRUE, list, list_size, s2->address + s2->size, address + length - s2->address - s2->size, c);
+                
                 s2->size = length;
-                // FIXME - insert rollback
                 
                 while (s2->list_entry.Flink != list) {
                     space* s3 = CONTAINING_RECORD(s2->list_entry.Flink, space, list_entry);
@@ -981,9 +1013,11 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
         
         // new entry overlaps start of old one
         if (address < s2->address && address + length >= s2->address) {
+            if (rollback)
+                add_rollback_space(rollback, TRUE, list, list_size, address, s2->address - address, c);
+            
             s2->size += s2->address - address;
             s2->address = address;
-            // FIXME - insert rollback
             
             while (s2->list_entry.Blink != list) {
                 space* s3 = CONTAINING_RECORD(s2->list_entry.Blink, space, list_entry);
@@ -1012,8 +1046,10 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
         
         // new entry overlaps end of old one
         if (address <= s2->address + s2->size && address + length > s2->address + s2->size) {
+            if (rollback)
+                add_rollback_space(rollback, TRUE, list, list_size, address, s2->address + s2->size - address, c);
+            
             s2->size = address + length - s2->address;
-            // FIXME - insert rollback
             
             while (s2->list_entry.Flink != list) {
                 space* s3 = CONTAINING_RECORD(s2->list_entry.Flink, space, list_entry);
@@ -1048,7 +1084,9 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
                 return;
             }
             
-            // FIXME - insert rollback
+            if (rollback)
+                add_rollback_space(rollback, TRUE, list, list_size, address, length, c);
+            
             s->address = address;
             s->size = length;
             InsertHeadList(s2->list_entry.Blink, &s->list_entry);
@@ -1065,7 +1103,6 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
     // check if contiguous with last entry
     if (s2->address + s2->size == address) {
         s2->size += length;
-        // FIXME - insert rollback
         
         if (list_size) {
             RemoveEntryList(&s2->list_entry_size);
@@ -1090,7 +1127,8 @@ void _space_list_add2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, U
     if (list_size)
         order_space_entry(s, list_size);
     
-    // FIXME - insert rollback
+    if (rollback)
+        add_rollback_space(rollback, TRUE, list, list_size, address, length, c);
 }
 
 static void space_list_merge(LIST_ENTRY* spacelist, LIST_ENTRY* spacelist_size, LIST_ENTRY* deleting) {
@@ -1108,7 +1146,7 @@ static void space_list_merge(LIST_ENTRY* spacelist, LIST_ENTRY* spacelist_size,
     }
 }
 
-static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* now, LIST_ENTRY* rollback) {
+static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME* now, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
@@ -1159,7 +1197,7 @@ static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME*
     c->cache->inode_item.sequence++;
     c->cache->inode_item.st_ctime = *now;
     
-    flush_fcb(c->cache, TRUE, rollback);
+    flush_fcb(c->cache, TRUE, Irp, rollback);
     
     // update free_space item
     
@@ -1167,7 +1205,7 @@ static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME*
     searchkey.obj_type = 0;
     searchkey.offset = c->offset;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -1211,9 +1249,9 @@ static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME*
     
     // write cache
     
-    Status = do_nocow_write(Vcb, c->cache, 0, c->cache->inode_item.st_size, data, NULL, NULL, rollback);
+    Status = do_write_file(c->cache, 0, c->cache->inode_item.st_size, data, NULL, NULL, rollback);
     if (!NT_SUCCESS(Status)) {
-        ERR("do_nocow_write returned %08x\n", Status);
+        ERR("do_write_file returned %08x\n", Status);
         return Status;
     }
 
@@ -1222,7 +1260,7 @@ static NTSTATUS update_chunk_cache(device_extension* Vcb, chunk* c, BTRFS_TIME*
     return STATUS_SUCCESS;
 }
 
-NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback) {
+NTSTATUS update_chunk_caches(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
     LIST_ENTRY* le = Vcb->chunks_changed.Flink;
     NTSTATUS Status;
     chunk* c;
@@ -1235,9 +1273,9 @@ NTSTATUS update_chunk_caches(device_extension* Vcb, LIST_ENTRY* rollback) {
     while (le != &Vcb->chunks_changed) {
         c = CONTAINING_RECORD(le, chunk, list_entry_changed);
         
-        ExAcquireResourceExclusiveLite(&c->nonpaged->lock, TRUE);
-        Status = update_chunk_cache(Vcb, c, &now, rollback);
-        ExReleaseResourceLite(&c->nonpaged->lock);
+        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        Status = update_chunk_cache(Vcb, c, &now, Irp, rollback);
+        ExReleaseResourceLite(&c->lock);
 
         if (!NT_SUCCESS(Status)) {
             ERR("update_chunk_cache(%llx) returned %08x\n", c->offset, Status);
@@ -1260,10 +1298,10 @@ void _space_list_add(device_extension* Vcb, chunk* c, BOOL deleting, UINT64 addr
     if (!c->list_entry_changed.Flink)
         InsertTailList(&Vcb->chunks_changed, &c->list_entry_changed);
     
-    _space_list_add2(list, deleting ? NULL : &c->space_size, address, length, rollback, func);
+    _space_list_add2(list, deleting ? NULL : &c->space_size, address, length, c, rollback, func);
 }
 
-void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, LIST_ENTRY* rollback, const char* func) {
+void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 address, UINT64 length, chunk* c, LIST_ENTRY* rollback, const char* func) {
     LIST_ENTRY *le, *le2;
     space *s, *s2;
     
@@ -1283,7 +1321,9 @@ void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 addre
             return;
         
         if (s2->address >= address && s2->address + s2->size <= address + length) { // remove entry entirely
-            // FIXME - insert rollback
+            if (rollback)
+                add_rollback_space(rollback, FALSE, list, list_size, s2->address, s2->size, c);
+            
             RemoveEntryList(&s2->list_entry);
             
             if (list_size)
@@ -1292,7 +1332,8 @@ void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 addre
             ExFreePool(s2);
         } else if (address + length > s2->address && address + length < s2->address + s2->size) {
             if (address > s2->address) { // cut out hole
-                // FIXME - insert rollback
+                if (rollback)
+                    add_rollback_space(rollback, FALSE, list, list_size, address, length, c);
                 
                 s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
 
@@ -1316,9 +1357,11 @@ void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 addre
                 
                 return;
             } else { // remove start of entry
+                if (rollback)
+                    add_rollback_space(rollback, FALSE, list, list_size, s2->address, address + length - s2->address, c);
+                
                 s2->size -= address + length - s2->address;
                 s2->address = address + length;
-                // FIXME - insert rollback
                 
                 if (list_size) {
                     RemoveEntryList(&s2->list_entry_size);
@@ -1326,7 +1369,9 @@ void _space_list_subtract2(LIST_ENTRY* list, LIST_ENTRY* list_size, UINT64 addre
                 }
             }
         } else if (address > s2->address && address < s2->address + s2->size) { // remove end of entry
-            // FIXME - insert rollback
+            if (rollback)
+                add_rollback_space(rollback, FALSE, list, list_size, address, s2->address + s2->size - address, c);
+            
             s2->size = address - s2->address;
             
             if (list_size) {
@@ -1347,5 +1392,5 @@ void _space_list_subtract(device_extension* Vcb, chunk* c, BOOL deleting, UINT64
     if (!c->list_entry_changed.Flink)
         InsertTailList(&Vcb->chunks_changed, &c->list_entry_changed);
     
-    _space_list_subtract2(list, deleting ? NULL : &c->space_size, address, length, rollback, func);
+    _space_list_subtract2(list, deleting ? NULL : &c->space_size, address, length, c, rollback, func);
 }
index 27c1644..aa42414 100644 (file)
@@ -27,6 +27,8 @@
 
 #define DOTDOT ".."
 
+#define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?)
+
 extern LIST_ENTRY VcbList;
 extern ERESOURCE global_loading_lock;
 
@@ -68,7 +70,7 @@ static void get_uuid(BTRFS_UUID* uuid) {
     }
 }
 
-static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* subvol, UINT64 dupflags, UINT64* newaddr, LIST_ENTRY* rollback) {
+static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* subvol, UINT64 dupflags, UINT64* newaddr, PIRP Irp, LIST_ENTRY* rollback) {
     UINT8* buf;
     NTSTATUS Status;
     write_data_context* wtc;
@@ -90,7 +92,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub
         return STATUS_INSUFFICIENT_RESOURCES;
     }
     
-    Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, NULL);
+    Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, TRUE, buf, NULL, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("read_data returned %08x\n", Status);
         goto end;
@@ -104,7 +106,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub
     t.header.level = th->level;
     t.header.tree_id = t.root->id;
     
-    Status = get_tree_new_address(Vcb, &t, rollback);
+    Status = get_tree_new_address(Vcb, &t, Irp, rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("get_tree_new_address returned %08x\n", Status);
         goto end;
@@ -143,7 +145,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub
                     EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
                     
                     if (ed2->size != 0) { // not sparse
-                        Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol->id, ln[i].key.obj_id, ln[i].key.offset - ed2->offset, 1, rollback);
+                        Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol->id, ln[i].key.obj_id, ln[i].key.offset - ed2->offset, 1, Irp, rollback);
                         
                         if (!NT_SUCCESS(Status)) {
                             ERR("increase_extent_refcount_data returned %08x\n", Status);
@@ -159,7 +161,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub
         internal_node* in = (internal_node*)&th[1];
         
         for (i = 0; i < th->num_items; i++) {
-            Status = snapshot_tree_copy(Vcb, in[i].address, subvol, dupflags, &newaddr, rollback);
+            Status = snapshot_tree_copy(Vcb, in[i].address, subvol, dupflags, &newaddr, Irp, rollback);
             
             if (!NT_SUCCESS(Status)) {
                 ERR("snapshot_tree_copy returned %08x\n", Status);
@@ -178,7 +180,7 @@ static NTSTATUS snapshot_tree_copy(device_extension* Vcb, UINT64 addr, root* sub
     wtc->tree = TRUE;
     wtc->stripes_left = 0;
     
-    Status = write_data(Vcb, t.new_address, buf, FALSE, Vcb->superblock.node_size, wtc, NULL);
+    Status = write_data(Vcb, t.new_address, buf, FALSE, Vcb->superblock.node_size, wtc, NULL, NULL);
     if (!NT_SUCCESS(Status)) {
         ERR("write_data returned %08x\n", Status);
         goto end;
@@ -243,7 +245,7 @@ static void flush_subvol_fcbs(root* subvol, LIST_ENTRY* rollback) {
     }
 }
 
-static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, UINT32 crc32, PANSI_STRING utf8, PUNICODE_STRING name) {
+static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, PANSI_STRING utf8, PUNICODE_STRING name, PIRP Irp) {
     LIST_ENTRY rollback;
     UINT64 id;
     NTSTATUS Status;
@@ -272,8 +274,6 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     
     InitializeListHead(&rollback);
     
-    ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
-    
     // flush open files on this subvol
     
     flush_subvol_fcbs(subvol, &rollback);
@@ -281,7 +281,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     // flush metadata
     
     if (Vcb->need_write)
-        do_write(Vcb, &rollback);
+        do_write(Vcb, Irp, &rollback);
     
     free_trees(Vcb);
     
@@ -292,10 +292,10 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     // create new root
     
     if (Vcb->root_root->lastinode == 0)
-        get_last_inode(Vcb, Vcb->root_root);
+        get_last_inode(Vcb, Vcb->root_root, Irp);
     
     id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
-    Status = create_root(Vcb, id, &r, TRUE, Vcb->superblock.generation, &rollback);
+    Status = create_root(Vcb, id, &r, TRUE, Vcb->superblock.generation, Irp, &rollback);
     
     if (!NT_SUCCESS(Status)) {
         ERR("create_root returned %08x\n", Status);
@@ -307,7 +307,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
         
         TRACE("uuid root doesn't exist, creating it\n");
         
-        Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, &rollback);
+        Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, FALSE, 0, Irp, &rollback);
         
         if (!NT_SUCCESS(Status)) {
             ERR("create_root returned %08x\n", Status);
@@ -333,12 +333,12 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
         searchkey.obj_type = TYPE_SUBVOL_UUID;
         RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
         
-        Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE);
+        Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp);
     } while (NT_SUCCESS(Status) && !keycmp(&searchkey, &tp.item->key));
     
     *root_num = r->id;
     
-    if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, &rollback)) {
+    if (!insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(UINT64), NULL, Irp, &rollback)) {
         ERR("insert_tree_item failed\n");
         ExFreePool(root_num);
         Status = STATUS_INTERNAL_ERROR;
@@ -349,13 +349,13 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     searchkey.obj_type = TYPE_ROOT_ITEM;
     searchkey.offset = 0xffffffffffffffff;
     
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto end;
     }
     
-    Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, tp.tree->flags, &address, &rollback);
+    Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, tp.tree->flags, &address, Irp, &rollback);
     if (!NT_SUCCESS(Status)) {
         ERR("snapshot_tree_copy returned %08x\n", Status);
         goto end;
@@ -405,7 +405,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     searchkey.obj_type = 0;
     searchkey.offset = 0;
     
-    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         goto end;
@@ -433,14 +433,14 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
     
     RtlCopyMemory(fr->utf8.Buffer, utf8->Buffer, utf8->Length);
     
-    Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, fcb, &fr->fcb);
+    Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, fcb, &fr->fcb, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
         free_fileref(fr);
         goto end;
     }
     
-    Status = fcb_get_last_dir_index(fcb, &dirpos);
+    Status = fcb_get_last_dir_index(fcb, &dirpos, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("fcb_get_last_dir_index returned %08x\n", Status);
         free_fileref(fr);
@@ -514,7 +514,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f