[BTRFS]
[reactos.git] / reactos / drivers / filesystems / btrfs / create.c
index f389dfb..04dc8f2 100644 (file)
@@ -27,651 +27,6 @@ extern PDEVICE_OBJECT devobj;
 
 static WCHAR datastring[] = L"::$DATA";
 
-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;
-    UINT64 index;
-    
-    searchkey.obj_id = inode;
-    searchkey.obj_type = TYPE_INODE_REF;
-    searchkey.offset = parinode;
-    
-    Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return Status;
-    }
-    
-    if (!keycmp(tp.item->key, searchkey)) {
-        INODE_REF* ir;
-        ULONG len;
-        
-        index = 0;
-        
-        ir = (INODE_REF*)tp.item->data;
-        len = tp.item->size;
-        
-        do {
-            ULONG itemlen;
-            
-            if (len < sizeof(INODE_REF) || len < sizeof(INODE_REF) - 1 + ir->n) {
-                ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                break;
-            }
-            
-            itemlen = sizeof(INODE_REF) - sizeof(char) + ir->n;
-            
-            if (ir->n == utf8->Length && RtlCompareMemory(ir->name, utf8->Buffer, ir->n) == ir->n) {
-                index = ir->index;
-                break;
-            }
-            
-            if (len > itemlen) {
-                len -= itemlen;
-                ir = (INODE_REF*)&ir->name[ir->n];
-            } else
-                break;
-        } while (len > 0);
-        
-        if (index == 0)
-            return STATUS_NOT_FOUND;
-        
-        *pindex = index;
-        
-        return STATUS_SUCCESS;
-    } else
-        return STATUS_NOT_FOUND;
-}
-
-static NTSTATUS find_file_dir_index_extref(device_extension* Vcb, root* r, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
-    KEY searchkey;
-    traverse_ptr tp;
-    NTSTATUS Status;
-    UINT64 index;
-    
-    searchkey.obj_id = inode;
-    searchkey.obj_type = TYPE_INODE_EXTREF;
-    searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
-    
-    Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return Status;
-    }
-    
-    if (!keycmp(tp.item->key, searchkey)) {
-        INODE_EXTREF* ier;
-        ULONG len;
-        
-        index = 0;
-        
-        ier = (INODE_EXTREF*)tp.item->data;
-        len = tp.item->size;
-        
-        do {
-            ULONG itemlen;
-            
-            if (len < sizeof(INODE_EXTREF) || len < sizeof(INODE_EXTREF) - 1 + ier->n) {
-                ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                break;
-            }
-            
-            itemlen = sizeof(INODE_EXTREF) - sizeof(char) + ier->n;
-            
-            if (ier->n == utf8->Length && RtlCompareMemory(ier->name, utf8->Buffer, ier->n) == ier->n) {
-                index = ier->index;
-                break;
-            }
-            
-            if (len > itemlen) {
-                len -= itemlen;
-                ier = (INODE_EXTREF*)&ier->name[ier->n];
-            } else
-                break;
-        } while (len > 0);
-        
-        if (index == 0)
-            return STATUS_NOT_FOUND;
-        
-        *pindex = index;
-        
-        return STATUS_SUCCESS;
-    } else
-        return STATUS_NOT_FOUND;
-}
-
-static NTSTATUS find_subvol_dir_index(device_extension* Vcb, root* r, UINT64 subvolid, UINT64 parinode, PANSI_STRING utf8, UINT64* pindex, PIRP Irp) {
-    KEY searchkey;
-    traverse_ptr tp;
-    NTSTATUS Status;
-    ROOT_REF* rr;
-    
-    searchkey.obj_id = r->id;
-    searchkey.obj_type = TYPE_ROOT_REF;
-    searchkey.offset = subvolid;
-    
-    Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        return Status;
-    }
-    
-    if (keycmp(tp.item->key, searchkey)) {
-        ERR("couldn't find (%llx,%x,%llx) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
-        return STATUS_INTERNAL_ERROR;
-    }
-    
-    if (tp.item->size < sizeof(ROOT_REF)) {
-        ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
-            tp.item->size, sizeof(ROOT_REF));
-        return STATUS_INTERNAL_ERROR;
-    }
-    
-    rr = (ROOT_REF*)tp.item->data;
-    
-    if (tp.item->size < sizeof(ROOT_REF) - 1 + rr->n) {
-        ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
-            tp.item->size, sizeof(ROOT_REF) - 1 + rr->n);
-        return STATUS_INTERNAL_ERROR;
-    }
-    
-    if (rr->dir == parinode && rr->n == utf8->Length && RtlCompareMemory(utf8->Buffer, rr->name, rr->n) == rr->n) {
-        *pindex = rr->index;
-        return STATUS_SUCCESS;
-    } else
-        return STATUS_NOT_FOUND;
-}
-
-static NTSTATUS load_index_list(fcb* fcb, PIRP Irp) {
-    KEY searchkey;
-    traverse_ptr tp, next_tp;
-    NTSTATUS Status;
-    BOOL b;
-    
-    searchkey.obj_id = fcb->inode;
-    searchkey.obj_type = TYPE_DIR_INDEX;
-    searchkey.offset = 2;
-    
-    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, 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, 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);
-        }
-    }
-    
-    if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_DIR_INDEX) {
-        Status = STATUS_SUCCESS;
-        goto end;
-    }
-    
-    do {
-        DIR_ITEM* di;
-        
-        TRACE("key: %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-        di = (DIR_ITEM*)tp.item->data;
-        
-        if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) {
-            WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-        } else {
-            index_entry* ie;
-            ULONG stringlen;
-            UNICODE_STRING us;
-            LIST_ENTRY* le;
-            BOOL inserted;
-            
-            ie = ExAllocatePoolWithTag(PagedPool, sizeof(index_entry), ALLOC_TAG);
-            if (!ie) {
-                ERR("out of memory\n");
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto end;
-            }
-            
-            ie->utf8.Length = ie->utf8.MaximumLength = di->n;
-            
-            if (di->n > 0) {
-                ie->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, ie->utf8.MaximumLength, ALLOC_TAG);
-                if (!ie->utf8.Buffer) {
-                    ERR("out of memory\n");
-                    ExFreePool(ie);
-                    Status = STATUS_INSUFFICIENT_RESOURCES;
-                    goto end;
-                }
-                
-                RtlCopyMemory(ie->utf8.Buffer, di->name, di->n);
-            } else
-                ie->utf8.Buffer = NULL;
-            
-            Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
-            if (!NT_SUCCESS(Status)) {
-                ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-                if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
-                ExFreePool(ie);
-                goto nextitem;
-            }
-            
-            if (stringlen == 0) {
-                ERR("UTF8 length was 0\n");
-                if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
-                ExFreePool(ie);
-                goto nextitem;
-            }
-            
-            us.Length = us.MaximumLength = stringlen;
-            us.Buffer = ExAllocatePoolWithTag(PagedPool, us.MaximumLength, ALLOC_TAG);
-            
-            if (!us.Buffer) {
-                ERR("out of memory\n");
-                if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
-                ExFreePool(ie);
-                return STATUS_INSUFFICIENT_RESOURCES;
-            }
-            
-            Status = RtlUTF8ToUnicodeN(us.Buffer, stringlen, &stringlen, di->name, di->n);
-            if (!NT_SUCCESS(Status)) {
-                ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                ExFreePool(us.Buffer);
-                if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
-                ExFreePool(ie);
-                goto nextitem;
-            }
-            
-            Status = RtlUpcaseUnicodeString(&ie->filepart_uc, &us, TRUE);
-            if (!NT_SUCCESS(Status)) {
-                ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-                ExFreePool(us.Buffer);
-                if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
-                ExFreePool(ie);
-                goto nextitem;
-            }
-            
-            ie->key = di->key;
-            ie->type = di->type;
-            ie->index = tp.item->key.offset;
-            
-            ie->hash = calc_crc32c(0xfffffffe, (UINT8*)ie->filepart_uc.Buffer, (ULONG)ie->filepart_uc.Length);
-            inserted = FALSE;
-            
-            le = fcb->index_list.Flink;
-            while (le != &fcb->index_list) {
-                index_entry* ie2 = CONTAINING_RECORD(le, index_entry, list_entry);
-                
-                if (ie2->hash >= ie->hash) {
-                    InsertHeadList(le->Blink, &ie->list_entry);
-                    inserted = TRUE;
-                    break;
-                }
-                
-                le = le->Flink;
-            }
-            
-            if (!inserted)
-                InsertTailList(&fcb->index_list, &ie->list_entry);
-        }
-        
-nextitem:
-        b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp);
-         
-        if (b) {
-            tp = next_tp;
-            
-            b = tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type == TYPE_DIR_INDEX;
-        }
-    } while (b);
-    
-    Status = STATUS_SUCCESS;
-    
-end:
-    if (!NT_SUCCESS(Status)) {
-        while (!IsListEmpty(&fcb->index_list)) {
-            LIST_ENTRY* le = RemoveHeadList(&fcb->index_list);
-            index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry);
-
-            if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
-            if (ie->filepart_uc.Buffer) ExFreePool(ie->filepart_uc.Buffer);
-            ExFreePool(ie);
-        }
-    } else
-        mark_fcb_dirty(fcb); // It's not necessarily dirty, but this is an easy way of making sure
-                             // the list remains in memory until the next flush.
-    
-    return Status;
-}
-
-static NTSTATUS STDCALL find_file_in_dir_index(file_ref* fr, PUNICODE_STRING filename, root** subvol, UINT64* inode, UINT8* type,
-                                               UINT64* pindex, PANSI_STRING utf8, PIRP Irp) {
-    LIST_ENTRY* le;
-    NTSTATUS Status;
-    UNICODE_STRING us;
-    UINT32 hash;
-        
-    Status = RtlUpcaseUnicodeString(&us, filename, TRUE);
-    if (!NT_SUCCESS(Status)) {
-        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-        return Status;
-    }
-    
-    hash = calc_crc32c(0xfffffffe, (UINT8*)us.Buffer, (ULONG)us.Length);
-    
-    ExAcquireResourceExclusiveLite(&fr->fcb->nonpaged->index_lock, TRUE);
-    
-    if (!fr->fcb->index_loaded) {
-        Status = load_index_list(fr->fcb, Irp);
-        if (!NT_SUCCESS(Status)) {
-            ERR("load_index_list returned %08x\n", Status);
-            goto end;
-        }
-        
-        fr->fcb->index_loaded = TRUE;
-    }
-    
-    ExConvertExclusiveToSharedLite(&fr->fcb->nonpaged->index_lock);
-    
-    le = fr->fcb->index_list.Flink;
-    while (le != &fr->fcb->index_list) {
-        index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry);
-        
-        if (ie->hash == hash && ie->filepart_uc.Length == us.Length && RtlCompareMemory(ie->filepart_uc.Buffer, us.Buffer, us.Length) == us.Length) {
-            LIST_ENTRY* le;
-            BOOL ignore_entry = FALSE;
-            
-            ExAcquireResourceSharedLite(&fr->nonpaged->children_lock, TRUE);
-
-            le = fr->children.Flink;
-            while (le != &fr->children) {
-                file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
-                
-                if (fr2->index == ie->index) {
-                    if (fr2->deleted || fr2->filepart_uc.Length != us.Length ||
-                        RtlCompareMemory(fr2->filepart_uc.Buffer, us.Buffer, us.Length) != us.Length) {
-                        ignore_entry = TRUE;
-                        break;
-                    }
-                    break;
-                } else if (fr2->index > ie->index)
-                    break;
-                
-                le = le->Flink;
-            }
-            
-            ExReleaseResourceLite(&fr->nonpaged->children_lock);
-            
-            if (ignore_entry)
-                goto nextitem;
-            
-            if (ie->key.obj_type == TYPE_ROOT_ITEM) {
-                if (subvol) {
-                    *subvol = NULL;
-                    
-                    le = fr->fcb->Vcb->roots.Flink;
-                    while (le != &fr->fcb->Vcb->roots) {
-                        root* r2 = CONTAINING_RECORD(le, root, list_entry);
-                        
-                        if (r2->id == ie->key.obj_id) {
-                            *subvol = r2;
-                            break;
-                        }
-                        
-                        le = le->Flink;
-                    }
-                }
-                
-                if (inode)
-                    *inode = SUBVOL_ROOT_INODE;
-                
-                if (type)
-                    *type = BTRFS_TYPE_DIRECTORY;
-            } else {
-                if (subvol)
-                    *subvol = fr->fcb->subvol;
-                
-                if (inode)
-                    *inode = ie->key.obj_id;
-                
-                if (type)
-                    *type = ie->type;
-            }
-            
-            if (utf8) {
-                utf8->MaximumLength = utf8->Length = ie->utf8.Length;
-                utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
-                if (!utf8->Buffer) {
-                    ERR("out of memory\n");
-                    Status = STATUS_INSUFFICIENT_RESOURCES;
-                    goto end;
-                }
-                
-                RtlCopyMemory(utf8->Buffer, ie->utf8.Buffer, ie->utf8.Length);
-            }
-            
-            if (pindex)
-                *pindex = ie->index;
-            
-            Status = STATUS_SUCCESS;
-            goto end;
-        } else if (ie->hash > hash) {
-            Status = STATUS_OBJECT_NAME_NOT_FOUND;
-            goto end;
-        }
-        
-nextitem:
-        le = le->Flink;
-    }
-    
-    Status = STATUS_OBJECT_NAME_NOT_FOUND;
-    
-end:
-    ExReleaseResourceLite(&fr->fcb->nonpaged->index_lock);
-    
-    ExFreePool(us.Buffer);
-    
-    return Status;
-}
-
-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,
-                                                    BOOL case_sensitive, PIRP Irp) {
-    DIR_ITEM* di;
-    KEY searchkey;
-    traverse_ptr tp;
-    NTSTATUS Status;
-    ULONG stringlen;
-    
-    TRACE("(%p, %.*S, %08x, (%llx, %llx), %p, %p, %p)\n", Vcb, filename->Length / sizeof(WCHAR), filename->Buffer, crc32,
-                                                          fr->fcb->subvol->id, fr->fcb->inode, subvol, inode, type);
-    
-    searchkey.obj_id = fr->fcb->inode;
-    searchkey.obj_type = TYPE_DIR_ITEM;
-    searchkey.offset = crc32;
-    
-    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;
-    }
-    
-    TRACE("found item %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-    
-    if (!keycmp(searchkey, tp.item->key)) {
-        UINT32 size = tp.item->size;
-        
-        // found by hash
-        
-        if (tp.item->size < sizeof(DIR_ITEM)) {
-            WARN("(%llx;%llx,%x,%llx) was %u bytes, expected at least %u\n", fr->fcb->subvol->id, tp.item->key.obj_id, tp.item->key.obj_type,
-                                                                             tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
-        } else {
-            di = (DIR_ITEM*)tp.item->data;
-            
-            while (size > 0) {
-                if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + di->m + di->n)) {
-                    WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-                    break;
-                }
-                
-                size -= sizeof(DIR_ITEM) - sizeof(char);
-                size -= di->n;
-                size -= di->m;
-                
-                Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, di->name, di->n);
-                if (!NT_SUCCESS(Status)) {
-                    ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-                } else {
-                    WCHAR* utf16 = ExAllocatePoolWithTag(PagedPool, stringlen, ALLOC_TAG);
-                    UNICODE_STRING us;
-                    
-                    if (!utf16) {
-                        ERR("out of memory\n");
-                        return STATUS_INSUFFICIENT_RESOURCES;
-                    }
-                    
-                    Status = RtlUTF8ToUnicodeN(utf16, stringlen, &stringlen, di->name, di->n);
-
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                    } else {
-                        ANSI_STRING nutf8;
-                        
-                        us.Buffer = utf16;
-                        us.Length = us.MaximumLength = (USHORT)stringlen;
-                        
-                        if (FsRtlAreNamesEqual(filename, &us, !case_sensitive, NULL)) {
-                            UINT64 index;
-                            
-                            if (di->key.obj_type == TYPE_ROOT_ITEM) {
-                                LIST_ENTRY* le = Vcb->roots.Flink;
-                                
-                                if (subvol) {
-                                    *subvol = NULL;
-                                    
-                                    while (le != &Vcb->roots) {
-                                        root* r2 = CONTAINING_RECORD(le, root, list_entry);
-                                        
-                                        if (r2->id == di->key.obj_id) {
-                                            *subvol = r2;
-                                            break;
-                                        }
-                                        
-                                        le = le->Flink;
-                                    }
-                                }
-
-                                if (inode)
-                                    *inode = SUBVOL_ROOT_INODE;
-                                
-                                if (type)
-                                    *type = BTRFS_TYPE_DIRECTORY;
-                            } else {
-                                if (subvol)
-                                    *subvol = fr->fcb->subvol;
-                                
-                                if (inode)
-                                    *inode = di->key.obj_id;
-                                
-                                if (type)
-                                    *type = di->type;
-                            }
-                            
-                            if (utf8) {
-                                utf8->MaximumLength = di->n;
-                                utf8->Length = utf8->MaximumLength;
-                                utf8->Buffer = ExAllocatePoolWithTag(PagedPool, utf8->MaximumLength, ALLOC_TAG);
-                                if (!utf8->Buffer) {
-                                    ERR("out of memory\n");
-                                    ExFreePool(utf16);
-                                    return STATUS_INSUFFICIENT_RESOURCES;
-                                }
-                                
-                                RtlCopyMemory(utf8->Buffer, di->name, di->n);
-                            }
-                            
-                            ExFreePool(utf16);
-                            
-                            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, &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, &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, &nutf8, &index, Irp);
-                                            
-                                            if (!NT_SUCCESS(Status)) {
-                                                ERR("find_file_dir_index_extref returned %08x\n", Status);
-                                                return Status;
-                                            }
-                                        } else {
-                                            ERR("find_file_dir_index returned %08x\n", Status);
-                                            return Status;
-                                        }
-                                    }
-                                }
-                            }
-                            
-                            if (index != 0) {
-                                LIST_ENTRY* le = fr->children.Flink;
-                                
-                                while (le != &fr->children) {
-                                    file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
-                                    
-                                    if (fr2->index == index) {
-                                        if (fr2->deleted || !FsRtlAreNamesEqual(&fr2->filepart, filename, !case_sensitive, NULL)) {
-                                            goto byindex;
-                                        }
-                                        break;
-                                    } else if (fr2->index > index)
-                                        break;
-                                    
-                                    le = le->Flink;
-                                }
-                            }
-                            
-//                             TRACE("found %.*S by hash at (%llx,%llx)\n", filename->Length / sizeof(WCHAR), filename->Buffer, (*subvol)->id, *inode);
-
-                            if (pindex)
-                                *pindex = index;
-                            
-                            return STATUS_SUCCESS;
-                        }
-                    }
-                    
-                    ExFreePool(utf16);
-                }
-                
-                di = (DIR_ITEM*)&di->name[di->n + di->m];
-            }
-        }
-    }
-    
-byindex:
-    if (case_sensitive)
-        return STATUS_OBJECT_NAME_NOT_FOUND;
-    
-    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;
-    }
-    
-    return Status;
-}
-
 fcb* create_fcb(POOL_TYPE pool_type) {
     fcb* fcb;
     
@@ -711,14 +66,17 @@ fcb* create_fcb(POOL_TYPE pool_type) {
     ExInitializeResourceLite(&fcb->nonpaged->resource);
     fcb->Header.Resource = &fcb->nonpaged->resource;
     
-    ExInitializeResourceLite(&fcb->nonpaged->index_lock);
+    ExInitializeResourceLite(&fcb->nonpaged->dir_children_lock);
     
     FsRtlInitializeFileLock(&fcb->lock, NULL, NULL);
     
     InitializeListHead(&fcb->extents);
-    InitializeListHead(&fcb->index_list);
     InitializeListHead(&fcb->hardlinks);
     
+    InitializeListHead(&fcb->dir_children_index);
+    InitializeListHead(&fcb->dir_children_hash);
+    InitializeListHead(&fcb->dir_children_hash_uc);
+    
     return fcb;
 }
 
@@ -740,52 +98,148 @@ file_ref* create_fileref() {
         return NULL;
     }
     
-    fr->refcount = 1;
-    
-#ifdef DEBUG_FCB_REFCOUNTS
-    WARN("fileref %p: refcount now 1\n", fr);
-#endif
-    
-    InitializeListHead(&fr->children);
-    
-    ExInitializeResourceLite(&fr->nonpaged->children_lock);
-    
-    return fr;
-}
+    fr->refcount = 1;
+    
+#ifdef DEBUG_FCB_REFCOUNTS
+    WARN("fileref %p: refcount now 1\n", fr);
+#endif
+    
+    InitializeListHead(&fr->children);
+    
+    ExInitializeResourceLite(&fr->nonpaged->children_lock);
+    
+    return fr;
+}
+
+static NTSTATUS STDCALL find_file_in_dir(device_extension* Vcb, PUNICODE_STRING filename, fcb* fcb,
+                                         root** subvol, UINT64* inode, dir_child** pdc, BOOL case_sensitive, PIRP Irp) {
+    NTSTATUS Status;
+    UNICODE_STRING fnus;
+    UINT32 hash;
+    LIST_ENTRY* le;
+    UINT8 c;
+    
+    if (!case_sensitive) {
+        Status = RtlUpcaseUnicodeString(&fnus, filename, TRUE);
+        
+        if (!NT_SUCCESS(Status)) {
+            ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+            return Status;
+        }
+    } else
+        fnus = *filename;
+    
+    hash = calc_crc32c(0xffffffff, (UINT8*)fnus.Buffer, fnus.Length);
+    
+    c = hash >> 24;
+    
+    ExAcquireResourceSharedLite(&fcb->nonpaged->dir_children_lock, TRUE);
+    
+    if (case_sensitive) {
+        if (!fcb->hash_ptrs[c]) {
+            Status = STATUS_OBJECT_NAME_NOT_FOUND;
+            goto end;
+        }
+        
+        le = fcb->hash_ptrs[c];
+        while (le != &fcb->dir_children_hash) {
+            dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash);
+            
+            if (dc->hash == hash) {
+                if (dc->name.Length == fnus.Length && RtlCompareMemory(dc->name.Buffer, fnus.Buffer, fnus.Length) == fnus.Length) {
+                    if (dc->key.obj_type == TYPE_ROOT_ITEM) {
+                        LIST_ENTRY* le2;
+                        
+                        *subvol = NULL;
+                        
+                        le2 = fcb->Vcb->roots.Flink;
+                        while (le2 != &fcb->Vcb->roots) {
+                            root* r2 = CONTAINING_RECORD(le2, root, list_entry);
+                            
+                            if (r2->id == dc->key.obj_id) {
+                                *subvol = r2;
+                                break;
+                            }
+                            
+                            le2 = le2->Flink;
+                        }
+                        
+                        *inode = SUBVOL_ROOT_INODE;
+                    } else {
+                        *subvol = fcb->subvol;
+                        *inode = dc->key.obj_id;
+                    }
+                    
+                    *pdc = dc;
+
+                    Status = STATUS_SUCCESS;
+                    goto end;
+                }
+            } else if (dc->hash > hash) {
+                Status = STATUS_OBJECT_NAME_NOT_FOUND;
+                goto end;
+            }
+            
+            le = le->Flink;
+        }
+    } else {
+        if (!fcb->hash_ptrs_uc[c]) {
+            Status = STATUS_OBJECT_NAME_NOT_FOUND;
+            goto end;
+        }
+        
+        le = fcb->hash_ptrs_uc[c];
+        while (le != &fcb->dir_children_hash_uc) {
+            dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
+            
+            if (dc->hash_uc == hash) {
+                if (dc->name_uc.Length == fnus.Length && RtlCompareMemory(dc->name_uc.Buffer, fnus.Buffer, fnus.Length) == fnus.Length) {
+                    if (dc->key.obj_type == TYPE_ROOT_ITEM) {
+                        LIST_ENTRY* le2;
+                        
+                        *subvol = NULL;
+                        
+                        le2 = fcb->Vcb->roots.Flink;
+                        while (le2 != &fcb->Vcb->roots) {
+                            root* r2 = CONTAINING_RECORD(le2, root, list_entry);
+                            
+                            if (r2->id == dc->key.obj_id) {
+                                *subvol = r2;
+                                break;
+                            }
+                            
+                            le2 = le2->Flink;
+                        }
+                        
+                        *inode = SUBVOL_ROOT_INODE;
+                    } else {
+                        *subvol = fcb->subvol;
+                        *inode = dc->key.obj_id;
+                    }
+                    
+                    *pdc = dc;
+                    
+                    Status = STATUS_SUCCESS;
+                    goto end;
+                }
+            } else if (dc->hash_uc > hash) {
+                Status = STATUS_OBJECT_NAME_NOT_FOUND;
+                goto end;
+            }
+            
+            le = le->Flink;
+        }
+    }
+    
+    Status = STATUS_OBJECT_NAME_NOT_FOUND;
 
-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,
-                                  BOOL case_sensitive, PIRP Irp) {
-    char* fn;
-    UINT32 crc32;
-    ULONG utf8len;
-    NTSTATUS Status;
-    
-    Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, filename->Buffer, filename->Length);
-    if (!NT_SUCCESS(Status)) {
-        ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status);
-        return Status;
-    }
-    
-    fn = ExAllocatePoolWithTag(PagedPool, utf8len, ALLOC_TAG);
-    if (!fn) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    Status = RtlUnicodeToUTF8N(fn, utf8len, &utf8len, filename->Buffer, filename->Length);
-    if (!NT_SUCCESS(Status)) {
-        ExFreePool(fn);
-        ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
-        return Status;
-    }
-    
-    TRACE("%.*s\n", utf8len, fn);
+end:
+    ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
     
-    crc32 = calc_crc32c(0xfffffffe, (UINT8*)fn, (ULONG)utf8len);
-    TRACE("crc32c(%.*s) = %08x\n", utf8len, fn, crc32);
+    if (!case_sensitive)
+        ExFreePool(fnus.Buffer);
     
-    return find_file_in_dir_with_crc32(Vcb, filename, crc32, fr, subvol, inode, type, index, utf8, case_sensitive, Irp);
+    return Status;
 }
 
 static BOOL find_stream(device_extension* Vcb, fcb* fcb, PUNICODE_STRING stream, PUNICODE_STRING newstreamname, UINT32* hash, PANSI_STRING xattr, PIRP Irp) {
@@ -1178,14 +632,195 @@ end:
     return deleted;
 }
 
+NTSTATUS load_csum(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length, PIRP Irp) {
+    NTSTATUS Status;
+    KEY searchkey;
+    traverse_ptr tp, next_tp;
+    UINT64 i, j;
+    BOOL b;
+    
+    searchkey.obj_id = EXTENT_CSUM_ID;
+    searchkey.obj_type = TYPE_EXTENT_CSUM;
+    searchkey.offset = start;
+    
+    Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("error - find_item returned %08x\n", Status);
+        return Status;
+    }
+    
+    i = 0;
+    do {
+        if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+            ULONG readlen;
+            
+            if (start < tp.item->key.offset)
+                j = 0;
+            else
+                j = ((start - tp.item->key.offset) / Vcb->superblock.sector_size) + i;
+            
+            if (j * sizeof(UINT32) > tp.item->size || tp.item->key.offset > start + (i * Vcb->superblock.sector_size)) {
+                ERR("checksum not found for %llx\n", start + (i * Vcb->superblock.sector_size));
+                return STATUS_INTERNAL_ERROR;
+            }
+            
+            readlen = min((tp.item->size / sizeof(UINT32)) - j, length - i);
+            RtlCopyMemory(&csum[i], tp.item->data + (j * sizeof(UINT32)), readlen * sizeof(UINT32));
+            i += readlen;
+            
+            if (i == length)
+                break;
+        }
+        
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
+        
+        if (b)
+            tp = next_tp;
+    } while (b);
+    
+    if (i < length) {
+        ERR("could not read checksums: offset %llx, length %llx sectors\n", start, length);
+        return STATUS_INTERNAL_ERROR;
+    }
+    
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS load_dir_children(fcb* fcb, BOOL ignore_size, PIRP Irp) {
+    KEY searchkey;
+    traverse_ptr tp, next_tp;
+    NTSTATUS Status;
+    
+    fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
+    if (!fcb->hash_ptrs) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
+    
+    fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
+    if (!fcb->hash_ptrs_uc) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
+    
+    if (!ignore_size && fcb->inode_item.st_size == 0)
+        return STATUS_SUCCESS;
+    
+    searchkey.obj_id = fcb->inode;
+    searchkey.obj_type = TYPE_DIR_INDEX;
+    searchkey.offset = 2;
+    
+    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("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, 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);
+        }
+    }
+    
+    while (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+        DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
+        dir_child* dc;
+        ULONG utf16len;
+        
+        if (tp.item->size < sizeof(DIR_ITEM)) {
+            WARN("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
+            goto cont;
+        }
+        
+        if (di->n == 0) {
+            WARN("(%llx,%x,%llx): DIR_ITEM name length is zero\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+            goto cont;
+        }
+        
+        Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, di->name, di->n);
+        if (!NT_SUCCESS(Status)) {
+            ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+            goto cont;
+        }
+
+        dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
+        if (!dc) {
+            ERR("out of memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        dc->key = di->key;
+        dc->index = tp.item->key.offset;
+        dc->type = di->type;
+        dc->fileref = NULL;
+        
+        dc->utf8.MaximumLength = dc->utf8.Length = di->n;
+        dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, di->n, ALLOC_TAG);
+        if (!dc->utf8.Buffer) {
+            ERR("out of memory\n");
+            ExFreePool(dc);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlCopyMemory(dc->utf8.Buffer, di->name, di->n);
+        
+        dc->name.MaximumLength = dc->name.Length = utf16len;
+        dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.MaximumLength, ALLOC_TAG);
+        if (!dc->name.Buffer) {
+            ERR("out of memory\n");
+            ExFreePool(dc->utf8.Buffer);
+            ExFreePool(dc);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        Status = RtlUTF8ToUnicodeN(dc->name.Buffer, utf16len, &utf16len, di->name, di->n);
+        if (!NT_SUCCESS(Status)) {
+            ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+            ExFreePool(dc->utf8.Buffer);
+            ExFreePool(dc->name.Buffer);
+            ExFreePool(dc);
+            goto cont;
+        }
+        
+        Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, TRUE);
+        if (!NT_SUCCESS(Status)) {
+            ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+            ExFreePool(dc->utf8.Buffer);
+            ExFreePool(dc->name.Buffer);
+            ExFreePool(dc);
+            goto cont;
+        }
+        
+        dc->hash = calc_crc32c(0xffffffff, (UINT8*)dc->name.Buffer, dc->name.Length);
+        dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)dc->name_uc.Buffer, dc->name_uc.Length);
+        
+        InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
+        
+        insert_dir_child_into_hash_lists(fcb, dc);
+        
+cont:
+        if (find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp))
+            tp = next_tp;
+        else
+            break;
+    }
+    
+    return STATUS_SUCCESS;
+}
+
 NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, POOL_TYPE pooltype, PIRP Irp) {
     KEY searchkey;
-    traverse_ptr tp;
+    traverse_ptr tp, next_tp;
     NTSTATUS Status;
     fcb* fcb;
-    BOOL b;
-    UINT8* eadata;
-    UINT16 ealen;
+    BOOL atts_set = FALSE, sd_set = FALSE, no_data;
+    LIST_ENTRY* lastle = NULL;
+    EXTENT_DATA* ed = NULL;
     
     if (!IsListEmpty(&subvol->fcbs)) {
         LIST_ENTRY* le = subvol->fcbs.Flink;
@@ -1193,17 +828,22 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
         while (le != &subvol->fcbs) {
             fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
             
-            if (fcb->inode == inode && !fcb->ads) {
+            if (fcb->inode == inode) {
+                if (!fcb->ads) {
 #ifdef DEBUG_FCB_REFCOUNTS
-                LONG rc = InterlockedIncrement(&fcb->refcount);
-                
-                WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol->id, fcb->inode);
+                    LONG rc = InterlockedIncrement(&fcb->refcount);
+
+                    WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol->id, fcb->inode);
 #else
-                InterlockedIncrement(&fcb->refcount);
+                    InterlockedIncrement(&fcb->refcount);
 #endif
 
-                *pfcb = fcb;
-                return STATUS_SUCCESS;
+                    *pfcb = fcb;
+                    return STATUS_SUCCESS;
+                }
+            } else if (fcb->inode > inode) {
+                lastle = le->Blink;
+                break;
             }
             
             le = le->Flink;
@@ -1250,326 +890,356 @@ NTSTATUS open_fcb(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
         else if (fcb->inode_item.st_mode & __S_IFBLK)
             fcb->type = BTRFS_TYPE_BLOCKDEV;
         else if (fcb->inode_item.st_mode & __S_IFIFO)
-            fcb->type = BTRFS_TYPE_FIFO;
-        else if (fcb->inode_item.st_mode & __S_IFLNK)
-            fcb->type = BTRFS_TYPE_SYMLINK;
-        else if (fcb->inode_item.st_mode & __S_IFSOCK)
-            fcb->type = BTRFS_TYPE_SOCKET;
-        else
-            fcb->type = BTRFS_TYPE_FILE;
-    }
-    
-    fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', FALSE, Irp);
-    
-    fcb_get_sd(fcb, parent, Irp);
-    
-    if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
-        UINT8* xattrdata;
-        UINT16 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;
-        } else {
-            fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
-            
-            if (!Vcb->readonly && !(subvol->root_item.flags & BTRFS_SUBVOL_READONLY)) {
-                fcb->atts_changed = TRUE;
-                mark_fcb_dirty(fcb);
-            }
-        }
-    }
-    
-    fcb->ealen = 0;
-    
-    if (get_xattr(Vcb, subvol, inode, EA_EA, EA_EA_HASH, &eadata, &ealen, Irp)) {
-        ULONG offset;
-        
-        Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)eadata, ealen, &offset);
-        
-        if (!NT_SUCCESS(Status)) {
-            WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
-            ExFreePool(eadata);
-        } else {
-            FILE_FULL_EA_INFORMATION* eainfo;
-            fcb->ea_xattr.Buffer = (char*)eadata;
-            fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = ealen;
-            
-            fcb->ealen = 4;
-            
-            // calculate ealen
-            eainfo = (FILE_FULL_EA_INFORMATION*)eadata;
-            do {
-                fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
-                
-                if (eainfo->NextEntryOffset == 0)
-                    break;
-                
-                eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
-            } while (TRUE);
-        }
-    }
-    
-    InsertTailList(&subvol->fcbs, &fcb->list_entry);
-    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
-    
-    fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
-    
-    if (fcb->inode_item.st_size == 0 || (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK)) {
-        fcb->Header.AllocationSize.QuadPart = 0;
-        fcb->Header.FileSize.QuadPart = 0;
-        fcb->Header.ValidDataLength.QuadPart = 0;
-    } else {
-        EXTENT_DATA* ed = NULL;
-        traverse_ptr next_tp;
-        
-        searchkey.obj_id = fcb->inode;
-        searchkey.obj_type = TYPE_EXTENT_DATA;
-        searchkey.offset = 0;
-        
-        Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
-        if (!NT_SUCCESS(Status)) {
-            ERR("error - find_item returned %08x\n", Status);
-            free_fcb(fcb);
-            return Status;
-        }
-        
-        do {
-            if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
-                extent* ext;
-                BOOL unique = FALSE;
-                
-                ed = (EXTENT_DATA*)tp.item->data;
-                
-                if (tp.item->size < sizeof(EXTENT_DATA)) {
-                    ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
-                        tp.item->size, sizeof(EXTENT_DATA));
-                    
-                    free_fcb(fcb);
-                    return STATUS_INTERNAL_ERROR;
-                }
-                
-                if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
-                    EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
-                    
-                    if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
-                        ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
-                            tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
-                    
-                        free_fcb(fcb);
-                        return STATUS_INTERNAL_ERROR;
-                    }
-                    
-                    if (ed2->address == 0 && ed2->size == 0) // sparse
-                        goto nextitem;
-                    
-                    if (ed2->size != 0 && is_tree_unique(Vcb, tp.tree, Irp))
-                        unique = is_extent_unique(Vcb, ed2->address, ed2->size, Irp);
-                }
-                
-                ext = ExAllocatePoolWithTag(pooltype, sizeof(extent), ALLOC_TAG);
-                if (!ext) {
-                    ERR("out of memory\n");
-                    free_fcb(fcb);
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-                
-                ext->data = ExAllocatePoolWithTag(pooltype, tp.item->size, ALLOC_TAG);
-                if (!ext->data) {
-                    ERR("out of memory\n");
-                    ExFreePool(ext);
-                    free_fcb(fcb);
-                    return STATUS_INSUFFICIENT_RESOURCES;
-                }
-                
-                ext->offset = tp.item->key.offset;
-                RtlCopyMemory(ext->data, tp.item->data, tp.item->size);
-                ext->datalen = tp.item->size;
-                ext->unique = unique;
-                ext->ignore = FALSE;
-                
-                InsertTailList(&fcb->extents, &ext->list_entry);
-            }
-            
-nextitem:
-            b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
-         
-            if (b) {
-                tp = next_tp;
-                
-                if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type))
-                    break;
-            }
-        } while (b);
-        
-        if (ed && ed->type == EXTENT_TYPE_INLINE)
-            fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
-        else
-            fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
-        
-        fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
-        fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
-    }
-    
-    // FIXME - only do if st_nlink > 1?
-    
-    searchkey.obj_id = inode;
-    searchkey.obj_type = TYPE_INODE_REF;
-    searchkey.offset = 0;
-    
-    Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
-    if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
-        free_fcb(fcb);
-        return Status;
+            fcb->type = BTRFS_TYPE_FIFO;
+        else if (fcb->inode_item.st_mode & __S_IFLNK)
+            fcb->type = BTRFS_TYPE_SYMLINK;
+        else if (fcb->inode_item.st_mode & __S_IFSOCK)
+            fcb->type = BTRFS_TYPE_SOCKET;
+        else
+            fcb->type = BTRFS_TYPE_FILE;
     }
     
-    do {
-        traverse_ptr next_tp;
+    no_data = fcb->inode_item.st_size == 0 || (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK);
+    
+    while (find_next_item(Vcb, &tp, &next_tp, FALSE, Irp)) {
+        tp = next_tp;
+        
+        if (tp.item->key.obj_id > inode)
+            break;
+        
+        if ((no_data && tp.item->key.obj_type > TYPE_XATTR_ITEM) || tp.item->key.obj_type > TYPE_EXTENT_DATA)
+            break;
         
-        if (tp.item->key.obj_id == searchkey.obj_id) {
-            if (tp.item->key.obj_type == TYPE_INODE_REF) {
-                ULONG len;
-                INODE_REF* ir;
+        if (fcb->inode_item.st_nlink > 1 && tp.item->key.obj_type == TYPE_INODE_REF) {
+            ULONG len;
+            INODE_REF* ir;
+            
+            len = tp.item->size;
+            ir = (INODE_REF*)tp.item->data;
+            
+            while (len >= sizeof(INODE_REF) - 1) {
+                hardlink* hl;
+                ULONG stringlen;
+                
+                hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
+                if (!hl) {
+                    ERR("out of memory\n");
+                    free_fcb(fcb);
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+                
+                hl->parent = tp.item->key.offset;
+                hl->index = ir->index;
+                
+                hl->utf8.Length = hl->utf8.MaximumLength = ir->n;
+                
+                if (hl->utf8.Length > 0) {
+                    hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
+                    RtlCopyMemory(hl->utf8.Buffer, ir->name, ir->n);
+                }
                 
-                len = tp.item->size;
-                ir = (INODE_REF*)tp.item->data;
+                Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+                    ExFreePool(hl);
+                    free_fcb(fcb);
+                    return Status;
+                }
+                
+                hl->name.Length = hl->name.MaximumLength = stringlen;
                 
-                while (len >= sizeof(INODE_REF) - 1) {
-                    hardlink* hl;
-                    ULONG stringlen;
+                if (stringlen == 0)
+                    hl->name.Buffer = NULL;
+                else {
+                    hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
                     
-                    hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
-                    if (!hl) {
+                    if (!hl->name.Buffer) {
                         ERR("out of memory\n");
+                        ExFreePool(hl);
                         free_fcb(fcb);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
                     
-                    hl->parent = tp.item->key.offset;
-                    hl->index = ir->index;
-                    
-                    hl->utf8.Length = hl->utf8.MaximumLength = ir->n;
-                    
-                    if (hl->utf8.Length > 0) {
-                        hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
-                        RtlCopyMemory(hl->utf8.Buffer, ir->name, ir->n);
-                    }
-                    
-                    Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n);
+                    Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ir->name, ir->n);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+                        ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+                        ExFreePool(hl->name.Buffer);
                         ExFreePool(hl);
                         free_fcb(fcb);
                         return Status;
                     }
-                    
-                    hl->name.Length = hl->name.MaximumLength = stringlen;
-                    
-                    if (stringlen == 0)
-                        hl->name.Buffer = NULL;
-                    else {
-                        hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
-                        
-                        if (!hl->name.Buffer) {
-                            ERR("out of memory\n");
-                            ExFreePool(hl);
-                            free_fcb(fcb);
-                            return STATUS_INSUFFICIENT_RESOURCES;
-                        }
-                        
-                        Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ir->name, ir->n);
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                            ExFreePool(hl->name.Buffer);
-                            ExFreePool(hl);
-                            free_fcb(fcb);
-                            return Status;
-                        }
-                    }
-                    
-                    InsertTailList(&fcb->hardlinks, &hl->list_entry);
-                    
-                    len -= sizeof(INODE_REF) - 1 + ir->n;
-                    ir = (INODE_REF*)&ir->name[ir->n];
                 }
-            } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
-                ULONG len;
-                INODE_EXTREF* ier;
                 
-                len = tp.item->size;
-                ier = (INODE_EXTREF*)tp.item->data;
+                InsertTailList(&fcb->hardlinks, &hl->list_entry);
+                
+                len -= sizeof(INODE_REF) - 1 + ir->n;
+                ir = (INODE_REF*)&ir->name[ir->n];
+            }
+        } else if (fcb->inode_item.st_nlink > 1 && tp.item->key.obj_type == TYPE_INODE_EXTREF) {
+            ULONG len;
+            INODE_EXTREF* ier;
+            
+            len = tp.item->size;
+            ier = (INODE_EXTREF*)tp.item->data;
+            
+            while (len >= sizeof(INODE_EXTREF) - 1) {
+                hardlink* hl;
+                ULONG stringlen;
+                
+                hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
+                if (!hl) {
+                    ERR("out of memory\n");
+                    free_fcb(fcb);
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+                
+                hl->parent = ier->dir;
+                hl->index = ier->index;
+                
+                hl->utf8.Length = hl->utf8.MaximumLength = ier->n;
+                
+                if (hl->utf8.Length > 0) {
+                    hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
+                    RtlCopyMemory(hl->utf8.Buffer, ier->name, ier->n);
+                }
+                
+                Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+                    ExFreePool(hl);
+                    free_fcb(fcb);
+                    return Status;
+                }
+                
+                hl->name.Length = hl->name.MaximumLength = stringlen;
                 
-                while (len >= sizeof(INODE_EXTREF) - 1) {
-                    hardlink* hl;
-                    ULONG stringlen;
+                if (stringlen == 0)
+                    hl->name.Buffer = NULL;
+                else {
+                    hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
                     
-                    hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
-                    if (!hl) {
+                    if (!hl->name.Buffer) {
                         ERR("out of memory\n");
+                        ExFreePool(hl);
                         free_fcb(fcb);
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
                     
-                    hl->parent = ier->dir;
-                    hl->index = ier->index;
-                    
-                    hl->utf8.Length = hl->utf8.MaximumLength = ier->n;
-                    
-                    if (hl->utf8.Length > 0) {
-                        hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
-                        RtlCopyMemory(hl->utf8.Buffer, ier->name, ier->n);
-                    }
-                    
-                    Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n);
+                    Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ier->name, ier->n);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+                        ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+                        ExFreePool(hl->name.Buffer);
                         ExFreePool(hl);
                         free_fcb(fcb);
                         return Status;
                     }
+                }
+                
+                InsertTailList(&fcb->hardlinks, &hl->list_entry);
+                
+                len -= sizeof(INODE_EXTREF) - 1 + ier->n;
+                ier = (INODE_EXTREF*)&ier->name[ier->n];
+            }
+        } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
+            if (tp.item->size < sizeof(DIR_ITEM)) {
+                ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
+                continue;
+            }
+            
+            if (tp.item->key.offset == EA_REPARSE_HASH) {
+                UINT8* xattrdata;
+                UINT16 xattrlen;
+                
+                if (extract_xattr(tp.item->data, tp.item->size, EA_REPARSE, &xattrdata, &xattrlen)) {
+                    fcb->reparse_xattr.Buffer = (char*)xattrdata;
+                    fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = xattrlen;
+                }
+            } else if (tp.item->key.offset == EA_EA_HASH) {
+                UINT8* eadata;
+                UINT16 ealen;
+                
+                if (extract_xattr(tp.item->data, tp.item->size, EA_EA, &eadata, &ealen)) {
+                    ULONG offset;
                     
-                    hl->name.Length = hl->name.MaximumLength = stringlen;
+                    Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)eadata, ealen, &offset);
                     
-                    if (stringlen == 0)
-                        hl->name.Buffer = NULL;
-                    else {
-                        hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
+                    if (!NT_SUCCESS(Status)) {
+                        WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
+                        ExFreePool(eadata);
+                    } else {
+                        FILE_FULL_EA_INFORMATION* eainfo;
+                        fcb->ea_xattr.Buffer = (char*)eadata;
+                        fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = ealen;
                         
-                        if (!hl->name.Buffer) {
-                            ERR("out of memory\n");
-                            ExFreePool(hl);
-                            free_fcb(fcb);
-                            return STATUS_INSUFFICIENT_RESOURCES;
-                        }
+                        fcb->ealen = 4;
                         
-                        Status = RtlUTF8ToUnicodeN(hl->name.Buffer, stringlen, &stringlen, ier->name, ier->n);
-                        if (!NT_SUCCESS(Status)) {
-                            ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                            ExFreePool(hl->name.Buffer);
-                            ExFreePool(hl);
-                            free_fcb(fcb);
-                            return Status;
-                        }
+                        // calculate ealen
+                        eainfo = (FILE_FULL_EA_INFORMATION*)eadata;
+                        do {
+                            fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
+                            
+                            if (eainfo->NextEntryOffset == 0)
+                                break;
+                            
+                            eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
+                        } while (TRUE);
+                    }
+                }
+            } else if (tp.item->key.offset == EA_DOSATTRIB_HASH) {
+                UINT8* xattrdata;
+                UINT16 xattrlen;
+                
+                if (extract_xattr(tp.item->data, tp.item->size, EA_DOSATTRIB, &xattrdata, &xattrlen)) {
+                    if (get_file_attributes_from_xattr((char*)xattrdata, xattrlen, &fcb->atts)) {
+                        atts_set = TRUE;
+                        
+                        if (fcb->type == BTRFS_TYPE_DIRECTORY)
+                            fcb->atts |= FILE_ATTRIBUTE_DIRECTORY;
+                        else if (fcb->type == BTRFS_TYPE_SYMLINK)
+                            fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
                     }
                     
-                    InsertTailList(&fcb->hardlinks, &hl->list_entry);
-                    
-                    len -= sizeof(INODE_EXTREF) - 1 + ier->n;
-                    ier = (INODE_EXTREF*)&ier->name[ier->n];
+                    ExFreePool(xattrdata);
+                }
+            } else if (tp.item->key.offset == EA_NTACL_HASH) {
+                UINT16 buflen;
+                
+                if (extract_xattr(tp.item->data, tp.item->size, EA_NTACL, (UINT8**)&fcb->sd, &buflen)) {
+                    if (get_sd_from_xattr(fcb, buflen)) {
+                        sd_set = TRUE;
+                    } else
+                        ExFreePool(fcb->sd);
+                }
+            }
+        } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) {
+            extent* ext;
+            BOOL unique = FALSE;
+            
+            ed = (EXTENT_DATA*)tp.item->data;
+            
+            if (tp.item->size < sizeof(EXTENT_DATA)) {
+                ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+                    tp.item->size, sizeof(EXTENT_DATA));
+                
+                free_fcb(fcb);
+                return STATUS_INTERNAL_ERROR;
+            }
+            
+            if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
+                EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
+                
+                if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
+                    ERR("(%llx,%x,%llx) was %llx bytes, expected at least %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+                        tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
+                
+                    free_fcb(fcb);
+                    return STATUS_INTERNAL_ERROR;
                 }
+                
+                if (ed2->address == 0 && ed2->size == 0) // sparse
+                    continue;
+                
+                if (ed2->size != 0 && is_tree_unique(Vcb, tp.tree, Irp))
+                    unique = is_extent_unique(Vcb, ed2->address, ed2->size, Irp);
+            }
+            
+            ext = ExAllocatePoolWithTag(pooltype, sizeof(extent), ALLOC_TAG);
+            if (!ext) {
+                ERR("out of memory\n");
+                free_fcb(fcb);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+            
+            ext->data = ExAllocatePoolWithTag(pooltype, tp.item->size, ALLOC_TAG);
+            if (!ext->data) {
+                ERR("out of memory\n");
+                ExFreePool(ext);
+                free_fcb(fcb);
+                return STATUS_INSUFFICIENT_RESOURCES;
             }
+            
+            ext->offset = tp.item->key.offset;
+            RtlCopyMemory(ext->data, tp.item->data, tp.item->size);
+            ext->datalen = tp.item->size;
+            ext->unique = unique;
+            ext->ignore = FALSE;
+            ext->inserted = FALSE;
+            
+            if (ed->type == EXTENT_TYPE_REGULAR && !(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
+                EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
+                UINT64 len;
+                
+                len = (ed->compression == BTRFS_COMPRESSION_NONE ? ed2->num_bytes : ed2->size) / Vcb->superblock.sector_size;
+                
+                ext->csum = ExAllocatePoolWithTag(NonPagedPool, len * sizeof(UINT32), ALLOC_TAG);
+                if (!ext->csum) {
+                    ERR("out of memory\n");
+                    ExFreePool(ext);
+                    free_fcb(fcb);
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+                
+                Status = load_csum(Vcb, ext->csum, ed2->address + (ed->compression == BTRFS_COMPRESSION_NONE ? ed2->offset : 0), len, Irp);
+                
+                if (!NT_SUCCESS(Status)) {
+                    ERR("load_csum returned %08x\n", Status);
+                    ExFreePool(ext);
+                    free_fcb(fcb);
+                    return Status;
+                }
+            } else
+                ext->csum = NULL;
+            
+            InsertTailList(&fcb->extents, &ext->list_entry);
+        }
+    }
+    
+    if (fcb->type == BTRFS_TYPE_DIRECTORY) {
+        Status = load_dir_children(fcb, FALSE, Irp);
+        if (!NT_SUCCESS(Status)) {
+            ERR("load_dir_children returned %08x\n", Status);
+            free_fcb(fcb);
+            return Status;
         }
+    }
+    
+    if (no_data) {
+        fcb->Header.AllocationSize.QuadPart = 0;
+        fcb->Header.FileSize.QuadPart = 0;
+        fcb->Header.ValidDataLength.QuadPart = 0;
+    } else {
+        if (ed && ed->type == EXTENT_TYPE_INLINE)
+            fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
+        else
+            fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
         
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
+        fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
+        fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
+    }
+    
+    if (!atts_set)
+        fcb->atts = get_file_attributes(Vcb, &fcb->inode_item, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', TRUE, Irp);
+    
+    if (!sd_set)
+        fcb_get_sd(fcb, parent, FALSE, Irp);
+    
+    if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT && fcb->reparse_xattr.Length == 0) {
+        fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
         
-        if (b) {
-            tp = next_tp;
-            
-            if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > TYPE_INODE_EXTREF))
-                break;
+        if (!Vcb->readonly && !(subvol->root_item.flags & BTRFS_SUBVOL_READONLY)) {
+            fcb->atts_changed = TRUE;
+            mark_fcb_dirty(fcb);
         }
-    } while (b);
+    }
+    
+    if (lastle)
+        InsertHeadList(lastle, &fcb->list_entry);
+    else
+        InsertTailList(&subvol->fcbs, &fcb->list_entry);
+    
+    InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
+    
+    fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
 
     *pfcb = fcb;
     return STATUS_SUCCESS;
@@ -1583,6 +1253,7 @@ NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
+    LIST_ENTRY* lastle = NULL;
     
     if (!IsListEmpty(&subvol->fcbs)) {
         LIST_ENTRY* le = subvol->fcbs.Flink;
@@ -1590,18 +1261,23 @@ NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI
         while (le != &subvol->fcbs) {
             fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
             
-            if (fcb->inode == inode && fcb->ads && fcb->adsxattr.Length == xattr->Length &&
-                RtlCompareMemory(fcb->adsxattr.Buffer, xattr->Buffer, fcb->adsxattr.Length) == fcb->adsxattr.Length) {
+            if (fcb->inode == inode) {
+                if (fcb->ads && fcb->adsxattr.Length == xattr->Length &&
+                    RtlCompareMemory(fcb->adsxattr.Buffer, xattr->Buffer, fcb->adsxattr.Length) == fcb->adsxattr.Length) {
 #ifdef DEBUG_FCB_REFCOUNTS
-                LONG rc = InterlockedIncrement(&fcb->refcount);
-                
-                WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol->id, fcb->inode);
+                    LONG rc = InterlockedIncrement(&fcb->refcount);
+
+                    WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol->id, fcb->inode);
 #else
-                InterlockedIncrement(&fcb->refcount);
+                    InterlockedIncrement(&fcb->refcount);
 #endif
 
-                *pfcb = fcb;
-                return STATUS_SUCCESS;
+                    *pfcb = fcb;
+                    return STATUS_SUCCESS;
+                }
+            } else if (fcb->inode > inode) {
+                lastle = le->Blink;
+                break;
             }
                             
             le = le->Flink;
@@ -1668,7 +1344,11 @@ NTSTATUS open_fcb_stream(device_extension* Vcb, root* subvol, UINT64 inode, ANSI
     
     TRACE("stream found: size = %x, hash = %08x\n", xattrlen, fcb->adshash);
     
-    InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+    if (lastle)
+        InsertHeadList(lastle, &fcb->list_entry);
+    else
+        InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+    
     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
     
     *pfcb = fcb;
@@ -1703,11 +1383,197 @@ void insert_fileref_child(file_ref* parent, file_ref* child, BOOL do_lock) {
         }
     }
     
-    if (do_lock)
-        ExReleaseResourceLite(&parent->nonpaged->children_lock);
+    if (do_lock)
+        ExReleaseResourceLite(&parent->nonpaged->children_lock);
+}
+
+static NTSTATUS open_fileref_child(device_extension* Vcb, file_ref* sf, PUNICODE_STRING name, BOOL case_sensitive, BOOL lastpart, BOOL streampart,
+                                   POOL_TYPE pooltype, file_ref** psf2, PIRP Irp) {
+    NTSTATUS Status;
+    file_ref* sf2;
+        
+    if (streampart) {
+        UNICODE_STRING streamname;
+        ANSI_STRING xattr;
+        UINT32 streamhash;
+        
+        sf2 = search_fileref_children(sf, name, case_sensitive);
+        
+        if (sf2) {
+            if (sf2->deleted) {
+                TRACE("element in path has been deleted\n");
+                free_fileref(sf2);
+                return lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
+            }
+            
+            *psf2 = sf2;
+    
+            return STATUS_SUCCESS;
+        }
+        
+        streamname.Buffer = NULL;
+        streamname.Length = streamname.MaximumLength = 0;
+        xattr.Buffer = NULL;
+        xattr.Length = xattr.MaximumLength = 0;
+        
+        // FIXME - check if already opened
+        
+        if (!find_stream(Vcb, sf->fcb, name, &streamname, &streamhash, &xattr, Irp)) {
+            TRACE("could not find stream %.*S\n", name->Length / sizeof(WCHAR), name->Buffer);
+            
+            return STATUS_OBJECT_NAME_NOT_FOUND;
+        } else {
+            fcb* fcb;
+
+            if (streamhash == EA_DOSATTRIB_HASH && xattr.Length == strlen(EA_DOSATTRIB) &&
+                RtlCompareMemory(xattr.Buffer, EA_DOSATTRIB, xattr.Length) == xattr.Length) {
+                WARN("not allowing user.DOSATTRIB to be opened as stream\n");
+            
+                return STATUS_OBJECT_NAME_NOT_FOUND;
+            }
+            
+            Status = open_fcb_stream(Vcb, sf->fcb->subvol, sf->fcb->inode, &xattr, streamhash, sf->fcb, &fcb, Irp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("open_fcb_stream returned %08x\n", Status);
+                return Status;
+            }
+            
+            sf2 = create_fileref();
+            if (!sf2) {
+                ERR("out of memory\n");
+                free_fcb(fcb);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+            
+            sf2->fcb = fcb;
+
+            if (streamname.Buffer) // case has changed
+                sf2->filepart = streamname;
+            else {
+                sf2->filepart.MaximumLength = sf2->filepart.Length = name->Length;
+                sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
+                if (!sf2->filepart.Buffer) {
+                    ERR("out of memory\n");
+                    free_fileref(sf2);
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+                
+                RtlCopyMemory(sf2->filepart.Buffer, name->Buffer, name->Length);
+            }
+            
+            Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE);
+            if (!NT_SUCCESS(Status)) {
+                ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+                free_fileref(sf2);
+                return Status;
+            }
+            
+            // FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM
+
+            sf2->parent = (struct _file_ref*)sf;
+            insert_fileref_child(sf, sf2, TRUE);
+            
+            increase_fileref_refcount(sf);
+        }
+    } else {
+        root* subvol;
+        UINT64 inode;
+        dir_child* dc;
+        
+        Status = find_file_in_dir(Vcb, name, sf->fcb, &subvol, &inode, &dc, case_sensitive, Irp);
+        if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+            TRACE("could not find %.*S\n", name->Length / sizeof(WCHAR), name->Buffer);
+
+            return lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
+        } else if (!NT_SUCCESS(Status)) {
+            ERR("find_file_in_dir returned %08x\n", Status);
+            return Status;
+        } else {
+            fcb* fcb;
+            
+            if (dc->fileref) {
+                if (!lastpart && dc->type != BTRFS_TYPE_DIRECTORY) {
+                    WARN("passed path including file as subdirectory\n");
+                    return STATUS_OBJECT_PATH_NOT_FOUND;
+                }
+                
+                InterlockedIncrement(&dc->fileref->refcount);
+                *psf2 = dc->fileref;
+                return STATUS_SUCCESS;
+            }
+            
+            Status = open_fcb(Vcb, subvol, inode, dc->type, &dc->utf8, sf->fcb, &fcb, pooltype, Irp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("open_fcb returned %08x\n", Status);
+                return Status;
+            }
+            
+            if (dc->type != BTRFS_TYPE_DIRECTORY && !lastpart && !(fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
+                WARN("passed path including file as subdirectory\n");
+                free_fcb(fcb);
+                return STATUS_OBJECT_PATH_NOT_FOUND;
+            }
+
+            sf2 = create_fileref();
+            if (!sf2) {
+                ERR("out of memory\n");
+                free_fcb(fcb);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+            
+            sf2->fcb = fcb;
+            
+            if (dc->type == BTRFS_TYPE_DIRECTORY)
+                fcb->fileref = sf2;
+            
+            sf2->index = dc->index;
+            sf2->dc = dc;
+            dc->fileref = sf2;
+            
+            sf2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.Length, ALLOC_TAG);
+            if (!sf2->utf8.Buffer) {
+                ERR("out of memory\n");
+                free_fileref(sf2);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+            
+            sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.Length, ALLOC_TAG);
+            if (!sf2->filepart.Buffer) {
+                ERR("out of memory\n");
+                free_fileref(sf2);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+            
+            sf2->filepart_uc.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name_uc.Length, ALLOC_TAG);
+            if (!sf2->filepart_uc.Buffer) {
+                ERR("out of memory\n");
+                free_fileref(sf2);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+            
+            sf2->utf8.Length = sf2->utf8.MaximumLength = dc->utf8.Length;
+            RtlCopyMemory(sf2->utf8.Buffer, dc->utf8.Buffer, dc->utf8.Length);
+            
+            sf2->filepart.Length = sf2->filepart.MaximumLength = dc->name.Length;
+            RtlCopyMemory(sf2->filepart.Buffer, dc->name.Buffer, dc->name.Length);
+            
+            sf2->filepart_uc.Length = sf2->filepart_uc.MaximumLength = dc->name_uc.Length;
+            RtlCopyMemory(sf2->filepart_uc.Buffer, dc->name_uc.Buffer, dc->name_uc.Length);
+            
+            sf2->parent = (struct _file_ref*)sf;
+            
+            insert_fileref_child(sf, sf2, TRUE);
+            
+            increase_fileref_refcount(sf);
+        }
+    }
+    
+    *psf2 = sf2;
+    
+    return STATUS_SUCCESS;
 }
 
-NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* unparsed, ULONG* fn_offset,
+NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnus, file_ref* related, BOOL parent, USHORT* parsed, ULONG* fn_offset,
                       POOL_TYPE pooltype, BOOL case_sensitive, PIRP Irp) {
     UNICODE_STRING fnus2;
     file_ref *dir, *sf, *sf2;
@@ -1716,7 +1582,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
     BOOL has_stream;
     NTSTATUS Status;
     
-    TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, unparsed);
+    TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, parsed);
 
 #ifdef DEBUG    
     if (!ExIsResourceAcquiredExclusiveLite(&Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
@@ -1755,7 +1621,7 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
                 ULONG cc;
                 IO_STATUS_BLOCK iosb;
                 
-                Status = dev_ioctl(Vcb->devices[0].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+                Status = dev_ioctl(first_device(Vcb)->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
                 
                 if (!NT_SUCCESS(Status))
                     return Status;
@@ -1763,6 +1629,10 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
             
             increase_fileref_refcount(Vcb->root_fileref);
             *pfr = Vcb->root_fileref;
+            
+            if (fn_offset)
+                *fn_offset = 0;
+            
             return STATUS_SUCCESS;
         }
         
@@ -1807,190 +1677,26 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
     if (num_parts == 0) {
         Status = STATUS_SUCCESS;
         *pfr = dir;
+        
+        if (fn_offset)
+            *fn_offset = 0;
+            
         goto end2;
     }
     
     for (i = 0; i < num_parts; i++) {
         BOOL lastpart = (i == num_parts-1) || (i == num_parts-2 && has_stream);
         
-        sf2 = search_fileref_children(sf, &parts[i], case_sensitive);
-        
-        if (sf2 && sf2->fcb->type != BTRFS_TYPE_DIRECTORY && !lastpart) {
-            WARN("passed path including file as subdirectory\n");
-            free_fileref(sf2);
+        Status = open_fileref_child(Vcb, sf, &parts[i], case_sensitive, lastpart, has_stream && i == num_parts - 1, pooltype, &sf2, Irp);
+        if (!NT_SUCCESS(Status)) {
+            if (Status == STATUS_OBJECT_PATH_NOT_FOUND || Status == STATUS_OBJECT_NAME_NOT_FOUND)
+                TRACE("open_fileref_child returned %08x\n", Status);
+            else
+                ERR("open_fileref_child returned %08x\n", Status);
             
-            Status = STATUS_OBJECT_PATH_NOT_FOUND;
-            goto end;
-        }
-        
-        if (sf2 && sf2->deleted) {
-            TRACE("element in path has been deleted\n");
-            free_fileref(sf2);
-            Status = lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
             goto end;
         }
         
-        if (!sf2) {
-            if (has_stream && i == num_parts - 1) {
-                UNICODE_STRING streamname;
-                ANSI_STRING xattr;
-                UINT32 streamhash;
-                
-                streamname.Buffer = NULL;
-                streamname.Length = streamname.MaximumLength = 0;
-                xattr.Buffer = NULL;
-                xattr.Length = xattr.MaximumLength = 0;
-                
-                // FIXME - check if already opened
-                
-                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;
-                    goto end;
-                } else {
-                    fcb* fcb;
-
-                    if (streamhash == EA_DOSATTRIB_HASH && xattr.Length == strlen(EA_DOSATTRIB) &&
-                        RtlCompareMemory(xattr.Buffer, EA_DOSATTRIB, xattr.Length) == xattr.Length) {
-                        WARN("not allowing user.DOSATTRIB to be opened as stream\n");
-                    
-                        Status = STATUS_OBJECT_NAME_NOT_FOUND;
-                        goto end;
-                    }
-                    
-                    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;
-                    }
-                    
-                    sf2 = create_fileref();
-                    if (!sf2) {
-                        ERR("out of memory\n");
-                        free_fcb(fcb);
-                        Status = STATUS_INSUFFICIENT_RESOURCES;
-                        goto end;
-                    }
-                    
-                    sf2->fcb = fcb;
-        
-                    if (streamname.Buffer) // case has changed
-                        sf2->filepart = streamname;
-                    else {
-                        sf2->filepart.MaximumLength = sf2->filepart.Length = parts[i].Length;
-                        sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
-                        if (!sf2->filepart.Buffer) {
-                            ERR("out of memory\n");
-                            free_fileref(sf2);
-                            Status = STATUS_INSUFFICIENT_RESOURCES;
-                            goto end;
-                        }   
-                        
-                        RtlCopyMemory(sf2->filepart.Buffer, parts[i].Buffer, parts[i].Length);
-                    }
-                    
-                    Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-                        free_fileref(sf2);
-                        goto end;
-                    }
-                    
-                    // FIXME - make sure all functions know that ADS FCBs won't have a valid SD or INODE_ITEM
-
-                    sf2->parent = (struct _file_ref*)sf;
-                    insert_fileref_child(sf, sf2, TRUE);
-                    
-                    increase_fileref_refcount(sf);
-                }
-            } else {
-                root* subvol;
-                UINT64 inode, index;
-                UINT8 type;
-                ANSI_STRING utf8;
-                
-                Status = find_file_in_dir(Vcb, &parts[i], sf, &subvol, &inode, &type, &index, &utf8, case_sensitive, Irp);
-                if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
-                    TRACE("could not find %.*S\n", parts[i].Length / sizeof(WCHAR), parts[i].Buffer);
-
-                    Status = lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
-                    goto end;
-                } else if (!NT_SUCCESS(Status)) {
-                    ERR("find_file_in_dir returned %08x\n", Status);
-                    goto end;
-                } else {
-                    fcb* fcb;
-                    ULONG strlen;
-                    
-                    Status = open_fcb(Vcb, subvol, inode, type, &utf8, sf->fcb, &fcb, pooltype, 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");
-                        free_fcb(fcb);
-                        Status = STATUS_INSUFFICIENT_RESOURCES;
-                        goto end;
-                    }
-                    
-                    sf2->fcb = fcb;
-                    
-                    if (type == BTRFS_TYPE_DIRECTORY)
-                        fcb->fileref = sf2;
-                    
-                    sf2->index = index;
-                    sf2->utf8 = utf8;
-                    
-                    Status = RtlUTF8ToUnicodeN(NULL, 0, &strlen, utf8.Buffer, utf8.Length);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
-                        free_fileref(sf2);
-                        goto end;
-                    }
-                    
-                    sf2->filepart.MaximumLength = sf2->filepart.Length = strlen;
-                    sf2->filepart.Buffer = ExAllocatePoolWithTag(PagedPool, sf2->filepart.MaximumLength, ALLOC_TAG);
-                    if (!sf2->filepart.Buffer) {
-                        ERR("out of memory\n");
-                        free_fileref(sf2);
-                        Status = STATUS_INSUFFICIENT_RESOURCES;
-                        goto end;
-                    }
-                    
-                    Status = RtlUTF8ToUnicodeN(sf2->filepart.Buffer, strlen, &strlen, utf8.Buffer, utf8.Length);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
-                        free_fileref(sf2);
-                        goto end;
-                    }
-                    
-                    Status = RtlUpcaseUnicodeString(&sf2->filepart_uc, &sf2->filepart, TRUE);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
-                        free_fileref(sf2);
-                        goto end;
-                    }
-                    
-                    sf2->parent = (struct _file_ref*)sf;
-                    
-                    insert_fileref_child(sf, sf2, TRUE);
-                    
-                    increase_fileref_refcount(sf);
-                }
-            }
-        }
-        
         if (i == num_parts - 1) {
             if (fn_offset)
                 *fn_offset = parts[has_stream ? (num_parts - 2) : (num_parts - 1)].Buffer - fnus->Buffer;
@@ -2001,8 +1707,8 @@ NTSTATUS open_fileref(device_extension* Vcb, file_ref** pfr, PUNICODE_STRING fnu
         if (sf2->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
             Status = STATUS_REPARSE;
             
-            if (unparsed)
-                *unparsed = fnus->Length - ((parts[i+1].Buffer - fnus->Buffer - 1) * sizeof(WCHAR));
+            if (parsed)
+                *parsed = (parts[i+1].Buffer - fnus->Buffer - 1) * sizeof(WCHAR);
             
             break;
         }
@@ -2072,6 +1778,71 @@ end:
     return Status;
 }
 
+NTSTATUS add_dir_child(fcb* fcb, UINT64 inode, BOOL subvol, UINT64 index, PANSI_STRING utf8, PUNICODE_STRING name, PUNICODE_STRING name_uc, UINT8 type, dir_child** pdc) {
+    dir_child* dc;
+    
+    dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
+    if (!dc) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8->Length, ALLOC_TAG);
+    if (!dc->utf8.Buffer) {
+        ERR("out of memory\n");
+        ExFreePool(dc);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, name->Length, ALLOC_TAG);
+    if (!dc->name.Buffer) {
+        ERR("out of memory\n");
+        ExFreePool(dc->utf8.Buffer);
+        ExFreePool(dc);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    dc->name_uc.Buffer = ExAllocatePoolWithTag(PagedPool, name_uc->Length, ALLOC_TAG);
+    if (!dc->name_uc.Buffer) {
+        ERR("out of memory\n");
+        ExFreePool(dc->utf8.Buffer);
+        ExFreePool(dc->name.Buffer);
+        ExFreePool(dc);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    
+    dc->key.obj_id = inode;
+    dc->key.obj_type = subvol ? TYPE_ROOT_ITEM : TYPE_INODE_ITEM;
+    dc->key.offset = 0;
+    dc->index = index;
+    dc->type = type;
+    dc->fileref = NULL;
+    
+    dc->utf8.Length = dc->utf8.MaximumLength = utf8->Length;
+    RtlCopyMemory(dc->utf8.Buffer, utf8->Buffer, utf8->Length);
+    
+    dc->name.Length = dc->name.MaximumLength = name->Length;
+    RtlCopyMemory(dc->name.Buffer, name->Buffer, name->Length);
+    
+    dc->name_uc.Length = dc->name_uc.MaximumLength = name_uc->Length;
+    RtlCopyMemory(dc->name_uc.Buffer, name_uc->Buffer, name_uc->Length);
+    
+    dc->hash = calc_crc32c(0xffffffff, (UINT8*)dc->name.Buffer, dc->name.Length);
+    dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)dc->name_uc.Buffer, dc->name_uc.Length);
+    
+    ExAcquireResourceExclusiveLite(&fcb->nonpaged->dir_children_lock, TRUE);
+    
+    InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
+    
+    insert_dir_child_into_hash_lists(fcb, dc);
+    
+    ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
+    
+    *pdc = dc;
+    
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_STRING fpus, file_ref* parfileref, ULONG options,
                                      FILE_FULL_EA_INFORMATION* ea, ULONG ealen, file_ref** pfr, LIST_ENTRY* rollback) {
     NTSTATUS Status;
@@ -2086,7 +1857,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
     POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
     ULONG defda;
     file_ref* fileref;
-    hardlink* hl;
+    dir_child* dc;
 #ifdef DEBUG_FCB_REFCOUNTS
     LONG rc;
 #endif
@@ -2277,42 +2048,6 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
         fcb->ea_changed = TRUE;
     }
     
-    hl = ExAllocatePoolWithTag(pool_type, sizeof(hardlink), ALLOC_TAG);
-    if (!hl) {
-        ERR("out of memory\n");
-        free_fcb(fcb);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    hl->parent = parfileref->fcb->inode;
-    hl->index = dirpos;
-    
-    hl->utf8.Length = hl->utf8.MaximumLength = utf8len;
-    hl->utf8.Buffer = ExAllocatePoolWithTag(pool_type, utf8len, ALLOC_TAG);
-    
-    if (!hl->utf8.Buffer) {
-        ERR("out of memory\n");
-        ExFreePool(hl);
-        free_fcb(fcb);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    RtlCopyMemory(hl->utf8.Buffer, utf8, utf8len);
-    
-    hl->name.Length = hl->name.MaximumLength = fpus->Length;
-    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);
-        free_fcb(fcb);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    
-    RtlCopyMemory(hl->name.Buffer, fpus->Buffer, fpus->Length);
-    
-    InsertTailList(&fcb->hardlinks, &hl->list_entry);
-    
     fileref = create_fileref();
     if (!fileref) {
         ERR("out of memory\n");
@@ -2348,7 +2083,7 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
         free_fileref(fileref);
         return Status;
     }
-        
+
     if (Irp->Overlay.AllocationSize.QuadPart > 0 && !write_fcb_compressed(fcb)) {
         Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, TRUE, NULL, rollback);
         
@@ -2372,7 +2107,34 @@ static NTSTATUS STDCALL file_create2(PIRP Irp, device_extension* Vcb, PUNICODE_S
 
     insert_fileref_child(parfileref, fileref, TRUE);
     
+    Status = add_dir_child(fileref->parent->fcb, fcb->inode, FALSE, fileref->index, &fileref->utf8, &fileref->filepart, &fileref->filepart_uc, fcb->type, &dc);
+    if (!NT_SUCCESS(Status))
+        WARN("add_dir_child returned %08x\n", Status);
+    
+    fileref->dc = dc;
+    dc->fileref = fileref;
+    
     increase_fileref_refcount(parfileref);
+    
+    if (fcb->type == BTRFS_TYPE_DIRECTORY) {
+        fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
+        if (!fcb->hash_ptrs) {
+            ERR("out of memory\n");
+            free_fileref(fileref);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
+        
+        fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
+        if (!fcb->hash_ptrs_uc) {
+            ERR("out of memory\n");
+            free_fileref(fileref);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        
+        RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
+    }
  
     InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
@@ -2574,7 +2336,7 @@ static NTSTATUS create_stream(device_extension* Vcb, file_ref** pfileref, file_r
     mark_fcb_dirty(fcb);
     mark_fileref_dirty(fileref);
     
-    InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
+    InsertHeadList(&parfileref->fcb->list_entry, &fcb->list_entry); // insert in list after parent fcb
     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
     
     KeQuerySystemTime(&time);
@@ -2601,10 +2363,11 @@ static NTSTATUS create_stream(device_extension* Vcb, file_ref** pfileref, file_r
     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) {
+static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJECT FileObject, file_ref* related, BOOL loaded_related,
+                                    PUNICODE_STRING fnus, ULONG disposition, ULONG options, LIST_ENTRY* rollback) {
     NTSTATUS Status;
 //     fcb *fcb, *parfcb = NULL;
-    file_ref *fileref, *parfileref = NULL, *related;
+    file_ref *fileref, *parfileref = NULL;
     ULONG i, j, fn_offset;
 //     ULONG utf8len;
     ccb* ccb;
@@ -2626,17 +2389,13 @@ static NTSTATUS STDCALL file_create(PIRP Irp, device_extension* Vcb, PFILE_OBJEC
     dsus.Length = dsus.MaximumLength = wcslen(datasuf) * sizeof(WCHAR);
     fpus.Buffer = NULL;
     
-    if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) {
-        struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
+    if (!loaded_related) {
+        Status = open_fileref(Vcb, &parfileref, fnus, related, TRUE, NULL, NULL, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
         
-        related = relatedccb->fileref;
+        if (!NT_SUCCESS(Status))
+            goto end;
     } else
-        related = NULL;
-    
-    Status = open_fileref(Vcb, &parfileref, &FileObject->FileName, related, TRUE, NULL, NULL, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
-    
-    if (!NT_SUCCESS(Status))
-        goto end;
+        parfileref = related;
     
     if (parfileref->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
         Status = STATUS_OBJECT_PATH_NOT_FOUND;
@@ -2811,7 +2570,7 @@ end:
         ExFreePool(fpus.Buffer);
     
 end2:
-    if (parfileref)
+    if (parfileref && !loaded_related)
         free_fileref(parfileref);
     
     return Status;
@@ -2962,7 +2721,7 @@ static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) {
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         
-        Status = read_file(fcb, *data, 0, size, &bytes_read, NULL);
+        Status = read_file(fcb, *data, 0, size, &bytes_read, NULL, TRUE);
         if (!NT_SUCCESS(Status)) {
             ERR("read_file_fcb returned %08x\n", Status);
             ExFreePool(*data);
@@ -3068,14 +2827,22 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
     ccb* ccb;
     device_extension* Vcb = DeviceObject->DeviceExtension;
     PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
-    USHORT unparsed;
+    USHORT parsed;
     ULONG fn_offset = 0;
     file_ref *related, *fileref;
     POOL_TYPE pool_type = Stack->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
     ACCESS_MASK granted_access;
+    BOOL loaded_related = FALSE;
+    UNICODE_STRING fn;
 #ifdef DEBUG_FCB_REFCOUNTS
     LONG oc;
 #endif
+#ifdef DEBUG_STATS
+    LARGE_INTEGER time1, time2;
+    UINT8 open_type = 0;
+    
+    time1 = KeQueryPerformanceCounter(NULL);
+#endif
     
     Irp->IoStatus.Information = 0;
     
@@ -3130,7 +2897,9 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             goto exit;
     }
     
-    TRACE("(%.*S)\n", FileObject->FileName.Length / sizeof(WCHAR), FileObject->FileName.Buffer);
+    fn = FileObject->FileName;
+    
+    TRACE("(%.*S)\n", fn.Length / sizeof(WCHAR), fn.Buffer);
     TRACE("FileObject = %p\n", FileObject);
     
     if (Vcb->readonly && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE)) {
@@ -3147,10 +2916,10 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
     ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
 
     if (options & FILE_OPEN_BY_FILE_ID) {
-        if (FileObject->FileName.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) {
+        if (fn.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) {
             UINT64 inode;
             
-            RtlCopyMemory(&inode, FileObject->FileName.Buffer, sizeof(UINT64));
+            RtlCopyMemory(&inode, fn.Buffer, sizeof(UINT64));
             
             if (related->fcb == Vcb->root_fileref->fcb && inode == 0)
                 inode = Vcb->root_fileref->fcb->inode;
@@ -3168,13 +2937,44 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             goto exit;
         }
     } else {
-        if (related && FileObject->FileName.Length != 0 && FileObject->FileName.Buffer[0] == '\\') {
+        if (related && fn.Length != 0 && fn.Buffer[0] == '\\') {
             Status = STATUS_OBJECT_NAME_INVALID;
             goto exit;
         }
         
-        Status = open_fileref(Vcb, &fileref, &FileObject->FileName, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &unparsed, &fn_offset,
-                              pool_type, Stack->Flags & SL_CASE_SENSITIVE, Irp);
+        if (!related && RequestedDisposition != FILE_OPEN && !(Stack->Flags & SL_OPEN_TARGET_DIRECTORY)) {
+            ULONG fnoff;
+            
+            Status = open_fileref(Vcb, &related, &fn, NULL, TRUE, &parsed, &fnoff,
+                                  pool_type, Stack->Flags & SL_CASE_SENSITIVE, Irp);
+            
+            if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+                Status = STATUS_OBJECT_PATH_NOT_FOUND;
+            else if (Status == STATUS_REPARSE)
+                fileref = related;
+            else if (NT_SUCCESS(Status)) {
+                fnoff *= sizeof(WCHAR);
+                fnoff += related->filepart.Length + sizeof(WCHAR);
+                
+                if (related->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
+                    Status = STATUS_REPARSE;
+                    fileref = related;
+                    parsed = fnoff - sizeof(WCHAR);
+                } else {
+                    fn.Buffer = &fn.Buffer[fnoff / sizeof(WCHAR)];
+                    fn.Length -= fnoff;
+
+                    Status = open_fileref(Vcb, &fileref, &fn, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
+                                        pool_type, Stack->Flags & SL_CASE_SENSITIVE, Irp);
+                    
+                    loaded_related = TRUE;
+                }
+                
+            }
+        } else {
+            Status = open_fileref(Vcb, &fileref, &fn, related, Stack->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
+                                  pool_type, Stack->Flags & SL_CASE_SENSITIVE, Irp);
+        }
     }
     
     if (Status == STATUS_REPARSE) {
@@ -3194,7 +2994,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         Status = STATUS_REPARSE;
         RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
         
-        data->Reserved = unparsed;
+        data->Reserved = FileObject->FileName.Length - parsed;
         
         Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
         
@@ -3232,6 +3032,11 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         file_ref* sf;
         
         if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
+            LARGE_INTEGER zero;
+            
+#ifdef DEBUG_STATS
+            open_type = 1;
+#endif
             if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
                 Status = STATUS_ACCESS_DENIED;
                 free_fileref(fileref);
@@ -3243,6 +3048,13 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
                 free_fileref(fileref);
                 goto exit;
             }
+            
+            zero.QuadPart = 0;
+            if (!MmCanFileBeTruncated(&fileref->fcb->nonpaged->segment_object, &zero)) {
+                Status = STATUS_USER_MAPPED_FILE;
+                free_fileref(fileref);
+                goto exit;
+            }
         }
         
         SeLockSubjectContext(&Stack->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
@@ -3259,8 +3071,10 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
         
         SeUnlockSubjectContext(&Stack->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
         
+        // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
         if (fileref->fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && granted_access &
-            (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC)) {
+            (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_OWNER | WRITE_DAC) &&
+            fileref->fcb->inode != SUBVOL_ROOT_INODE) {
             Status = STATUS_ACCESS_DENIED;
             free_fileref(fileref);
             goto exit;
@@ -3324,7 +3138,7 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
             Status = STATUS_REPARSE;
             Irp->IoStatus.Information = data->ReparseTag;
             
-            if (FileObject->FileName.Buffer[(FileObject->FileName.Length / sizeof(WCHAR)) - 1] == '\\')
+            if (fn.Buffer[(fn.Length / sizeof(WCHAR)) - 1] == '\\')
                 data->Reserved = sizeof(WCHAR);
             
             Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
@@ -3543,8 +3357,8 @@ 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);
+            fn.Length -= fn_offset * sizeof(WCHAR);
+            RtlMoveMemory(&fn.Buffer[0], &fn.Buffer[fn_offset], fn.Length);
         }
         
         FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
@@ -3607,7 +3421,10 @@ static NTSTATUS STDCALL open_file(PDEVICE_OBJECT DeviceObject, PIRP Irp, LIST_EN
 #endif
         InterlockedIncrement(&Vcb->open_files);
     } else {
-        Status = file_create(Irp, DeviceObject->DeviceExtension, FileObject, &FileObject->FileName, RequestedDisposition, options, rollback);
+#ifdef DEBUG_STATS
+        open_type = 2;
+#endif
+        Status = file_create(Irp, DeviceObject->DeviceExtension, FileObject, related, loaded_related, &fn, RequestedDisposition, options, rollback);
         Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
     }
     
@@ -3618,6 +3435,9 @@ exit:
     ExReleaseResourceLite(&Vcb->fcb_lock);
     
 exit2:
+    if (loaded_related)
+        free_fileref(related);
+    
     if (NT_SUCCESS(Status)) {
         if (!FileObject->Vpb)
             FileObject->Vpb = DeviceObject->Vpb;
@@ -3626,54 +3446,94 @@ exit2:
             TRACE("returning %08x\n", Status);
     }
     
+#ifdef DEBUG_STATS
+    time2 = KeQueryPerformanceCounter(NULL);
+    
+    if (open_type == 0) {
+        Vcb->stats.open_total_time += time2.QuadPart - time1.QuadPart;
+        Vcb->stats.num_opens++;
+    } else if (open_type == 1) {
+        Vcb->stats.overwrite_total_time += time2.QuadPart - time1.QuadPart;
+        Vcb->stats.num_overwrites++;
+    } else if (open_type == 2) {
+        Vcb->stats.create_total_time += time2.QuadPart - time1.QuadPart;
+        Vcb->stats.num_creates++;
+    }
+#endif
+    
     return Status;
 }
 
 NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp) {
-    UINT64 i;
+    NTSTATUS Status;
+    LIST_ENTRY* le;
     
-    for (i = 0; i < Vcb->devices_loaded; i++) {
-        if (Vcb->devices[i].removable) {
-            NTSTATUS Status;
+    ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
+    
+    le = Vcb->devices.Flink;
+    while (le != &Vcb->devices) {
+        device* dev = CONTAINING_RECORD(le, device, list_entry);
+        
+        if (dev->removable) {
             ULONG cc;
             IO_STATUS_BLOCK iosb;
             
-            Status = dev_ioctl(Vcb->devices[i].devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+            Status = dev_ioctl(dev->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;
+                goto end;
             }
             
             if (iosb.Information < sizeof(ULONG)) {
                 ERR("iosb.Information was too short\n");
-                return STATUS_INTERNAL_ERROR;
+                Status = STATUS_INTERNAL_ERROR;
+                goto end;
             }
             
-            if (cc != Vcb->devices[i].change_count) {
-                PDEVICE_OBJECT dev;
+            if (cc != dev->change_count) {
+                PDEVICE_OBJECT devobj;
                 
-                Vcb->devices[i].devobj->Flags |= DO_VERIFY_VOLUME;
+                dev->devobj->Flags |= DO_VERIFY_VOLUME;
                 
-                dev = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
+                devobj = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
                 IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL);
                 
-                if (!dev) {
-                    dev = IoGetDeviceToVerify(PsGetCurrentThread());
+                if (!devobj) {
+                    devobj = IoGetDeviceToVerify(PsGetCurrentThread());
                     IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
                 }
                 
-                dev = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
+                devobj = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
                 
-                if (dev)
-                    IoVerifyVolume(dev, FALSE);
+                if (devobj)
+                    IoVerifyVolume(devobj, FALSE);
                 
-                return STATUS_VERIFY_REQUIRED;
+                Status = STATUS_VERIFY_REQUIRED;
+                goto end;
             }
         }
+        
+        le = le->Flink;
     }
     
-    return STATUS_SUCCESS;
+    Status = STATUS_SUCCESS;
+    
+end:
+    ExReleaseResourceLite(&Vcb->tree_lock);
+    
+    return Status;
+}
+
+static BOOL has_manage_volume_privilege(ACCESS_STATE* access_state, KPROCESSOR_MODE processor_mode) {
+    PRIVILEGE_SET privset;
+
+    privset.PrivilegeCount = 1;
+    privset.Control = PRIVILEGE_SET_ALL_NECESSARY;
+    privset.Privilege[0].Luid = RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE);
+    privset.Privilege[0].Attributes = 0;
+
+    return SePrivilegeCheck(&privset, &access_state->SubjectSecurityContext, processor_mode) ? TRUE : FALSE;
 }
 
 NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
@@ -3760,18 +3620,16 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
 #ifdef DEBUG_FCB_REFCOUNTS
         LONG rc, oc;
 #endif
+        ccb* ccb;
         
         TRACE("open operation for volume\n");
 
-        if (RequestedDisposition != FILE_OPEN &&
-            RequestedDisposition != FILE_OPEN_IF)
-        {
+        if (RequestedDisposition != FILE_OPEN && RequestedDisposition != FILE_OPEN_IF) {
             Status = STATUS_ACCESS_DENIED;
             goto exit;
         }
 
-        if (RequestedOptions & FILE_DIRECTORY_FILE)
-        {
+        if (RequestedOptions & FILE_DIRECTORY_FILE) {
             Status = STATUS_NOT_A_DIRECTORY;
             goto exit;
         }
@@ -3780,6 +3638,23 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
             Status = STATUS_ACCESS_DENIED;
             goto exit;
         }
+        
+        ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
+        if (!ccb) {
+            ERR("out of memory\n");
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
+        
+        RtlZeroMemory(ccb, sizeof(*ccb));
+        
+        ccb->NodeType = BTRFS_NODE_TYPE_CCB;
+        ccb->NodeSize = sizeof(ccb);
+        ccb->disposition = RequestedDisposition;
+        ccb->options = RequestedOptions;
+        ccb->access = IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess;
+        ccb->manage_volume_privilege = has_manage_volume_privilege(IrpSp->Parameters.Create.SecurityContext->AccessState,
+                                                                   IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode);
 
 #ifdef DEBUG_FCB_REFCOUNTS
         rc = InterlockedIncrement(&Vcb->volume_fcb->refcount);
@@ -3788,6 +3663,7 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
         InterlockedIncrement(&Vcb->volume_fcb->refcount);
 #endif
         IrpSp->FileObject->FsContext = Vcb->volume_fcb;
+        IrpSp->FileObject->FsContext2 = ccb;
         
         IrpSp->FileObject->SectionObjectPointer = &Vcb->volume_fcb->nonpaged->segment_object;
 
@@ -3812,11 +3688,7 @@ NTSTATUS STDCALL drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
         if (!skip_lock)
             ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
         
-//         ExAcquireResourceExclusiveLite(&Vpb->DirResource, TRUE);
-    //     Status = NtfsCreateFile(DeviceObject,
-    //                             Irp);
         Status = open_file(DeviceObject, Irp, &rollback);
-//         ExReleaseResourceLite(&Vpb->DirResource);
         
         if (!NT_SUCCESS(Status))
             do_rollback(Vcb, &rollback);