[BTRFS] Upgrade to 1.3
authorPierre Schweitzer <pierre@reactos.org>
Tue, 11 Jun 2019 10:35:19 +0000 (12:35 +0200)
committerPierre Schweitzer <pierre@reactos.org>
Tue, 11 Jun 2019 10:35:19 +0000 (12:35 +0200)
CORE-16111

13 files changed:
dll/win32/ubtrfs/ubtrfs.rc
drivers/filesystems/btrfs/btrfs.c
drivers/filesystems/btrfs/btrfs.h
drivers/filesystems/btrfs/btrfs.rc
drivers/filesystems/btrfs/btrfs_drv.h
drivers/filesystems/btrfs/create.c
drivers/filesystems/btrfs/dirctrl.c
drivers/filesystems/btrfs/fileinfo.c
drivers/filesystems/btrfs/flushthread.c
drivers/filesystems/btrfs/free-space.c
drivers/filesystems/btrfs/fsctl.c
drivers/filesystems/btrfs/write.c
media/doc/README.FSD

index c4c486f..2fbce14 100644 (file)
@@ -25,18 +25,18 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
 // TEXTINCLUDE
 //
 
-1 TEXTINCLUDE
+1 TEXTINCLUDE 
 BEGIN
     "resource.h\0"
 END
 
-2 TEXTINCLUDE
+2 TEXTINCLUDE 
 BEGIN
     "#include ""winres.h""\r\n"
     "\0"
 END
 
-3 TEXTINCLUDE
+3 TEXTINCLUDE 
 BEGIN
     "\r\n"
     "\0"
@@ -51,8 +51,8 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,1,0,0
- PRODUCTVERSION 1,1,0,0
+ FILEVERSION 1,3,0,0
+ PRODUCTVERSION 1,3,0,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -60,20 +60,20 @@ VS_VERSION_INFO VERSIONINFO
  FILEFLAGS 0x0L
 #endif
  FILEOS 0x4L
- FILETYPE 0x0L
+ FILETYPE 0x2L
  FILESUBTYPE 0x0L
 BEGIN
     BLOCK "StringFileInfo"
     BEGIN
         BLOCK "080904b0"
         BEGIN
-            VALUE "FileDescription", "Btrfs formatting utility"
-            VALUE "FileVersion", "1.1"
-            VALUE "InternalName", "mkbtrfs"
-            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-18"
-            VALUE "OriginalFilename", "mkbtrfs.exe"
+            VALUE "FileDescription", "Btrfs utility DLL"
+            VALUE "FileVersion", "1.3"
+            VALUE "InternalName", "ubtrfs"
+            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-19"
+            VALUE "OriginalFilename", "ubtrfs.dll"
             VALUE "ProductName", "WinBtrfs"
-            VALUE "ProductVersion", "1.1"
+            VALUE "ProductVersion", "1.3"
         END
     END
     BLOCK "VarFileInfo"
index 4cd6bb1..c480d9f 100644 (file)
@@ -795,7 +795,8 @@ static NTSTATUS drv_query_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _
             data->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH |
                                          FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS | FILE_SUPPORTS_HARD_LINKS | FILE_PERSISTENT_ACLS |
                                          FILE_SUPPORTS_REPARSE_POINTS | FILE_SUPPORTS_SPARSE_FILES | FILE_SUPPORTS_OBJECT_IDS |
-                                         FILE_SUPPORTS_OPEN_BY_FILE_ID | FILE_SUPPORTS_EXTENDED_ATTRIBUTES | FILE_SUPPORTS_BLOCK_REFCOUNTING;
+                                         FILE_SUPPORTS_OPEN_BY_FILE_ID | FILE_SUPPORTS_EXTENDED_ATTRIBUTES | FILE_SUPPORTS_BLOCK_REFCOUNTING |
+                                         FILE_SUPPORTS_POSIX_UNLINK_RENAME;
             if (Vcb->readonly)
                 data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
 
@@ -1068,6 +1069,7 @@ NTSTATUS create_root(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) devi
     RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
     r->root_item.num_references = 1;
     r->fcbs_version = 0;
+    r->checked_for_orphans = TRUE;
     InitializeListHead(&r->fcbs);
     RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
 
@@ -1966,7 +1968,63 @@ void uninit(_In_ device_extension* Vcb) {
     ZwClose(Vcb->flush_thread_handle);
 }
 
-NTSTATUS delete_fileref(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback) {
+static NTSTATUS delete_fileref_fcb(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback) {
+    NTSTATUS Status;
+    LIST_ENTRY* le;
+
+    // excise extents
+
+    if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
+        Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), Irp, rollback);
+        if (!NT_SUCCESS(Status)) {
+            ERR("excise_extents returned %08x\n", Status);
+            return Status;
+        }
+    }
+
+    fileref->fcb->Header.AllocationSize.QuadPart = 0;
+    fileref->fcb->Header.FileSize.QuadPart = 0;
+    fileref->fcb->Header.ValidDataLength.QuadPart = 0;
+
+    if (FileObject) {
+        CC_FILE_SIZES ccfs;
+
+        ccfs.AllocationSize = fileref->fcb->Header.AllocationSize;
+        ccfs.FileSize = fileref->fcb->Header.FileSize;
+        ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength;
+
+        Status = STATUS_SUCCESS;
+
+        _SEH2_TRY {
+            CcSetFileSizes(FileObject, &ccfs);
+        } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+            Status = _SEH2_GetExceptionCode();
+        } _SEH2_END;
+
+        if (!NT_SUCCESS(Status)) {
+            ERR("CcSetFileSizes threw exception %08x\n", Status);
+            return Status;
+        }
+    }
+
+    fileref->fcb->deleted = TRUE;
+
+    le = fileref->children.Flink;
+    while (le != &fileref->children) {
+        file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
+
+        if (fr2->fcb->ads) {
+            fr2->fcb->deleted = TRUE;
+            mark_fcb_dirty(fr2->fcb);
+        }
+
+        le = le->Flink;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS delete_fileref(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject, _In_ BOOL make_orphan, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback) {
     LARGE_INTEGER newlength, time;
     BTRFS_TIME now;
     NTSTATUS Status;
@@ -2002,61 +2060,17 @@ NTSTATUS delete_fileref(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject
 
             fileref->fcb->inode_item_changed = TRUE;
 
-            if (fileref->fcb->inode_item.st_nlink > 1) {
+            if (fileref->fcb->inode_item.st_nlink > 1 || make_orphan) {
                 fileref->fcb->inode_item.st_nlink--;
                 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
                 fileref->fcb->inode_item.sequence++;
                 fileref->fcb->inode_item.st_ctime = now;
             } else {
-                // excise extents
-
-                if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
-                    Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), Irp, rollback);
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("excise_extents returned %08x\n", Status);
-                        ExReleaseResourceLite(fileref->fcb->Header.Resource);
-                        return Status;
-                    }
-                }
-
-                fileref->fcb->Header.AllocationSize.QuadPart = 0;
-                fileref->fcb->Header.FileSize.QuadPart = 0;
-                fileref->fcb->Header.ValidDataLength.QuadPart = 0;
-
-                if (FileObject) {
-                    CC_FILE_SIZES ccfs;
-
-                    ccfs.AllocationSize = fileref->fcb->Header.AllocationSize;
-                    ccfs.FileSize = fileref->fcb->Header.FileSize;
-                    ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength;
-
-                    Status = STATUS_SUCCESS;
-
-                    _SEH2_TRY {
-                        CcSetFileSizes(FileObject, &ccfs);
-                    } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
-                        Status = _SEH2_GetExceptionCode();
-                    } _SEH2_END;
-
-                    if (!NT_SUCCESS(Status)) {
-                        ERR("CcSetFileSizes threw exception %08x\n", Status);
-                        ExReleaseResourceLite(fileref->fcb->Header.Resource);
-                        return Status;
-                    }
-                }
-
-                fileref->fcb->deleted = TRUE;
-
-                le = fileref->children.Flink;
-                while (le != &fileref->children) {
-                    file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
-
-                    if (fr2->fcb->ads) {
-                        fr2->fcb->deleted = TRUE;
-                        mark_fcb_dirty(fr2->fcb);
-                    }
-
-                    le = le->Flink;
+                Status = delete_fileref_fcb(fileref, FileObject, Irp, rollback);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_fileref_fcb returned %08x\n", Status);
+                    ExReleaseResourceLite(fileref->fcb->Header.Resource);
+                    return Status;
                 }
             }
 
@@ -2271,9 +2285,26 @@ static NTSTATUS drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
             // FIXME - flush all of subvol's fcbs
         }
 
-        if (fileref && oc == 0) {
+        if (fileref && (oc == 0 || (fileref->delete_on_close && fileref->posix_delete))) {
             if (!fcb->Vcb->removing) {
-                if (fileref && fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
+                if (oc == 0 && fileref->fcb->inode_item.st_nlink == 0 && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) { // last handle closed on POSIX-deleted file
+                    LIST_ENTRY rollback;
+
+                    InitializeListHead(&rollback);
+
+                    Status = delete_fileref_fcb(fileref, FileObject, Irp, &rollback);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("delete_fileref_fcb returned %08x\n", Status);
+                        do_rollback(fcb->Vcb, &rollback);
+                        ExReleaseResourceLite(fileref->fcb->Header.Resource);
+                        ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+                        goto exit;
+                    }
+
+                    clear_rollback(&rollback);
+
+                    mark_fcb_dirty(fileref->fcb);
+                } else if (fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
                     LIST_ENTRY rollback;
 
                     InitializeListHead(&rollback);
@@ -2292,7 +2323,7 @@ static NTSTATUS drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
                     // fileref_lock needs to be acquired before fcb->Header.Resource
                     ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, TRUE);
 
-                    Status = delete_fileref(fileref, FileObject, Irp, &rollback);
+                    Status = delete_fileref(fileref, FileObject, oc > 0 && fileref->posix_delete, Irp, &rollback);
                     if (!NT_SUCCESS(Status)) {
                         ERR("delete_fileref returned %08x\n", Status);
                         do_rollback(fcb->Vcb, &rollback);
@@ -2677,6 +2708,7 @@ static NTSTATUS add_root(_Inout_ device_extension* Vcb, _In_ UINT64 id, _In_ UIN
     r->parent = 0;
     r->send_ops = 0;
     r->fcbs_version = 0;
+    r->checked_for_orphans = FALSE;
     InitializeListHead(&r->fcbs);
     RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
 
@@ -3530,6 +3562,26 @@ void protect_superblocks(_Inout_ chunk* c) {
     }
 }
 
+UINT64 chunk_estimate_phys_size(device_extension* Vcb, chunk* c, UINT64 u) {
+    UINT64 nfactor, dfactor;
+
+    if (c->chunk_item->type & BLOCK_FLAG_DUPLICATE || c->chunk_item->type & BLOCK_FLAG_RAID1 || c->chunk_item->type & BLOCK_FLAG_RAID10) {
+        nfactor = 1;
+        dfactor = 2;
+    } else if (c->chunk_item->type & BLOCK_FLAG_RAID5) {
+        nfactor = Vcb->superblock.num_devices - 1;
+        dfactor = Vcb->superblock.num_devices;
+    } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
+        nfactor = Vcb->superblock.num_devices - 2;
+        dfactor = Vcb->superblock.num_devices;
+    } else {
+        nfactor = 1;
+        dfactor = 1;
+    }
+
+    return u * dfactor / nfactor;
+}
+
 NTSTATUS find_chunk_usage(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
     LIST_ENTRY* le = Vcb->chunks.Flink;
     chunk* c;
@@ -3540,6 +3592,8 @@ NTSTATUS find_chunk_usage(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_ex
 
     searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
 
+    Vcb->superblock.bytes_used = 0;
+
     while (le != &Vcb->chunks) {
         c = CONTAINING_RECORD(le, chunk, list_entry);
 
@@ -3559,6 +3613,8 @@ NTSTATUS find_chunk_usage(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_ex
                 c->used = c->oldused = bgi->used;
 
                 TRACE("chunk %llx has %llx bytes used\n", c->offset, c->used);
+
+                Vcb->superblock.bytes_used += chunk_estimate_phys_size(Vcb, c, bgi->used);
             } else {
                 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
                     Vcb->extent_root->id, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(BLOCK_GROUP_ITEM));
index eede405..d0d2c93 100644 (file)
@@ -19,6 +19,7 @@ static const UINT64 superblock_addrs[] = { 0x10000, 0x4000000, 0x4000000000, 0x4
 #define TYPE_INODE_REF         0x0C
 #define TYPE_INODE_EXTREF      0x0D
 #define TYPE_XATTR_ITEM        0x18
+#define TYPE_ORPHAN_INODE      0x30
 #define TYPE_DIR_ITEM          0x54
 #define TYPE_DIR_INDEX         0x60
 #define TYPE_EXTENT_DATA       0x6C
@@ -113,6 +114,8 @@ static const UINT64 superblock_addrs[] = { 0x10000, 0x4000000, 0x4000000000, 0x4
 
 #define BTRFS_SUPERBLOCK_FLAGS_SEEDING   0x100000000
 
+#define BTRFS_ORPHAN_INODE_OBJID         0xFFFFFFFFFFFFFFFB
+
 #pragma pack(push, 1)
 
 typedef struct {
index d10323e..da8380e 100644 (file)
@@ -51,8 +51,8 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,2,1,0
- PRODUCTVERSION 1,2,1,0
+ FILEVERSION 1,3,0,0
+ PRODUCTVERSION 1,3,0,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -68,12 +68,12 @@ BEGIN
         BLOCK "080904b0"
         BEGIN
             VALUE "FileDescription", "WinBtrfs"
-            VALUE "FileVersion", "1.2.1"
+            VALUE "FileVersion", "1.3"
             VALUE "InternalName", "btrfs"
             VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-19"
             VALUE "OriginalFilename", "btrfs.sys"
             VALUE "ProductName", "WinBtrfs"
-            VALUE "ProductVersion", "1.2.1"
+            VALUE "ProductVersion", "1.3"
         END
     END
     BLOCK "VarFileInfo"
index b41c979..b5d1cd2 100644 (file)
 #define EA_EA "user.EA"
 #define EA_EA_HASH 0x8270dd43
 
+#define EA_CASE_SENSITIVE "user.casesensitive"
+#define EA_CASE_SENSITIVE_HASH 0x1a9d97d4
+
 #define EA_PROP_COMPRESSION "btrfs.compression"
 #define EA_PROP_COMPRESSION_HASH 0x20ccdf69
 
 #define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000
 #endif
 
+#ifndef FILE_SUPPORTS_POSIX_UNLINK_RENAME
+#define FILE_SUPPORTS_POSIX_UNLINK_RENAME 0x00000400
+#endif
+
 #ifndef FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL
 #define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000
 #endif
 
+#ifndef _MSC_VER
+typedef struct _FILE_ID_128 {
+    UCHAR Identifier[16];
+} FILE_ID_128, *PFILE_ID_128;
+#endif
+
 typedef struct _DUPLICATE_EXTENTS_DATA {
     HANDLE FileHandle;
     LARGE_INTEGER SourceFileOffset;
@@ -280,6 +293,9 @@ typedef struct _fcb {
     BOOL inode_item_changed;
     enum prop_compression_type prop_compression;
     LIST_ENTRY xattrs;
+    BOOL marked_as_orphan;
+    BOOL case_sensitive;
+    BOOL case_sensitive_set;
 
     LIST_ENTRY dir_children_index;
     LIST_ENTRY dir_children_hash;
@@ -317,6 +333,7 @@ typedef struct _file_ref {
     ANSI_STRING oldutf8;
     UINT64 oldindex;
     BOOL delete_on_close;
+    BOOL posix_delete;
     BOOL deleted;
     BOOL created;
     file_ref_nonpaged* nonpaged;
@@ -434,6 +451,7 @@ typedef struct _root {
     UINT64 parent;
     LONG send_ops;
     UINT64 fcbs_version;
+    BOOL checked_for_orphans;
     LIST_ENTRY fcbs;
     LIST_ENTRY* fcbs_ptrs[256];
     LIST_ENTRY list_entry;
@@ -1123,7 +1141,7 @@ WCHAR* file_desc(_In_ PFILE_OBJECT FileObject);
 WCHAR* file_desc_fileref(_In_ file_ref* fileref);
 void mark_fcb_dirty(_In_ fcb* fcb);
 void mark_fileref_dirty(_In_ file_ref* fileref);
-NTSTATUS delete_fileref(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback);
+NTSTATUS delete_fileref(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject, _In_ BOOL make_orphan, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback);
 void chunk_lock_range(_In_ device_extension* Vcb, _In_ chunk* c, _In_ UINT64 start, _In_ UINT64 length);
 void chunk_unlock_range(_In_ device_extension* Vcb, _In_ chunk* c, _In_ UINT64 start, _In_ UINT64 length);
 void init_device(_In_ device_extension* Vcb, _Inout_ device* dev, _In_ BOOL get_nums);
@@ -1142,6 +1160,7 @@ void reap_fcb(fcb* fcb);
 void reap_fcbs(device_extension* Vcb);
 void reap_fileref(device_extension* Vcb, file_ref* fr);
 void reap_filerefs(device_extension* Vcb, file_ref* fr);
+UINT64 chunk_estimate_phys_size(device_extension* Vcb, chunk* c, UINT64 u);
 
 #ifdef _MSC_VER
 #define funcname __FUNCTION__
@@ -1412,7 +1431,7 @@ NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusiv
                       _In_ PUNICODE_STRING fnus, _In_opt_ file_ref* related, _In_ BOOL parent, _Out_opt_ USHORT* parsed, _Out_opt_ ULONG* fn_offset, _In_ POOL_TYPE pooltype,
                       _In_ BOOL case_sensitive, _In_opt_ PIRP Irp);
 NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
-                  root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, POOL_TYPE pooltype, PIRP Irp);
+                  root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, BOOL always_add_hl, fcb* parent, fcb** pfcb, POOL_TYPE pooltype, PIRP Irp);
 NTSTATUS load_csum(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length, PIRP Irp);
 NTSTATUS load_dir_children(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, fcb* fcb, BOOL ignore_size, PIRP Irp);
 NTSTATUS add_dir_child(fcb* fcb, UINT64 inode, BOOL subvol, PANSI_STRING utf8, PUNICODE_STRING name, UINT8 type, dir_child** pdc);
index 089b19c..2d996ed 100644 (file)
@@ -593,7 +593,7 @@ cont:
 }
 
 NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
-                  root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, fcb* parent, fcb** pfcb, POOL_TYPE pooltype, PIRP Irp) {
+                  root* subvol, UINT64 inode, UINT8 type, PANSI_STRING utf8, BOOL always_add_hl, fcb* parent, fcb** pfcb, POOL_TYPE pooltype, PIRP Irp) {
     KEY searchkey;
     traverse_ptr tp, next_tp;
     NTSTATUS Status;
@@ -719,7 +719,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
         if ((no_data && tp.item->key.obj_type > TYPE_XATTR_ITEM) || tp.item->key.obj_type > TYPE_EXTENT_DATA)
             break;
 
-        if (fcb->inode_item.st_nlink > 1 && tp.item->key.obj_type == TYPE_INODE_REF) {
+        if ((always_add_hl || fcb->inode_item.st_nlink > 1) && tp.item->key.obj_type == TYPE_INODE_REF) {
             ULONG len;
             INODE_REF* ir;
 
@@ -784,7 +784,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                 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) {
+        } else if ((always_add_hl || fcb->inode_item.st_nlink > 1) && tp.item->key.obj_type == TYPE_INODE_EXTREF) {
             ULONG len;
             INODE_EXTREF* ier;
 
@@ -971,6 +971,11 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                         else
                             fcb->prop_compression = PropCompression_None;
                     }
+                } else if (tp.item->key.offset == EA_CASE_SENSITIVE_HASH && di->n == sizeof(EA_CASE_SENSITIVE) - 1 && RtlCompareMemory(EA_CASE_SENSITIVE, di->name, di->n) == di->n) {
+                    if (di->m > 0) {
+                        fcb->case_sensitive = di->m == 1 && di->name[di->n] == '1';
+                        fcb->case_sensitive_set = TRUE;
+                    }
                 } else if (di->n > sizeof(xapref) - 1 && RtlCompareMemory(xapref, di->name, sizeof(xapref) - 1) == sizeof(xapref) - 1) {
                     dir_child* dc;
                     ULONG utf16len;
@@ -1165,8 +1170,8 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
 #endif
 
                             *pfcb = fcb2;
-                            release_fcb_lock(Vcb);
                             reap_fcb(fcb);
+                            release_fcb_lock(Vcb);
                             return STATUS_SUCCESS;
                         }
                     }
@@ -1174,8 +1179,8 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                     if (deleted_fcb) {
                         InterlockedIncrement(&deleted_fcb->refcount);
                         *pfcb = deleted_fcb;
-                        release_fcb_lock(Vcb);
                         reap_fcb(fcb);
+                        release_fcb_lock(Vcb);
                         return STATUS_SUCCESS;
                     }
 
@@ -1514,7 +1519,7 @@ NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_ex
 #ifdef DEBUG_STATS
                 time1 = KeQueryPerformanceCounter(NULL);
 #endif
-                Status = open_fcb(Vcb, subvol, inode, dc->type, &dc->utf8, sf->fcb, &fcb, pooltype, Irp);
+                Status = open_fcb(Vcb, subvol, inode, dc->type, &dc->utf8, FALSE, sf->fcb, &fcb, pooltype, Irp);
 #ifdef DEBUG_STATS
                 time2 = KeQueryPerformanceCounter(NULL);
                 Vcb->stats.open_fcb_calls++;
@@ -1692,7 +1697,16 @@ NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusiv
 #ifdef DEBUG_STATS
         time1 = KeQueryPerformanceCounter(NULL);
 #endif
-        Status = open_fileref_child(Vcb, sf, &nb->us, case_sensitive, lastpart, streampart, pooltype, &sf2, Irp);
+        BOOL cs = case_sensitive;
+
+        if (!cs) {
+            if (streampart)
+                cs = sf->parent->fcb->case_sensitive;
+            else
+                cs = sf->fcb->case_sensitive;
+        }
+
+        Status = open_fileref_child(Vcb, sf, &nb->us, cs, lastpart, streampart, pooltype, &sf2, Irp);
 #ifdef DEBUG_STATS
         time2 = KeQueryPerformanceCounter(NULL);
         Vcb->stats.open_fileref_child_calls++;
@@ -2529,6 +2543,7 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
     static const WCHAR DOSATTRIB[] = L"DOSATTRIB";
     static const WCHAR EA[] = L"EA";
     static const WCHAR reparse[] = L"reparse";
+    static const WCHAR casesensitive_str[] = L"casesensitive";
     LARGE_INTEGER time;
     BTRFS_TIME now;
     ULONG utf8len, overhead;
@@ -2634,7 +2649,8 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
 
     if ((stream->Length == sizeof(DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, DOSATTRIB, stream->Length) == stream->Length) ||
         (stream->Length == sizeof(EA) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, EA, stream->Length) == stream->Length) ||
-        (stream->Length == sizeof(reparse) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, reparse, stream->Length) == stream->Length)) {
+        (stream->Length == sizeof(reparse) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, reparse, stream->Length) == stream->Length) ||
+        (stream->Length == sizeof(casesensitive_str) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, casesensitive_str, stream->Length) == stream->Length)) {
         free_fileref(parfileref);
         return STATUS_OBJECT_NAME_INVALID;
     }
@@ -3774,7 +3790,7 @@ static NTSTATUS open_file2(device_extension* Vcb, ULONG RequestedDisposition, PO
                     if (dc->fileref) {
                         send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_REMOVED_STREAM, &dc->name);
 
-                        Status = delete_fileref(dc->fileref, NULL, NULL, rollback);
+                        Status = delete_fileref(dc->fileref, NULL, FALSE, NULL, rollback);
                         if (!NT_SUCCESS(Status)) {
                             ERR("delete_fileref returned %08x\n", Status);
 
@@ -3953,30 +3969,198 @@ NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock)
     UNICODE_STRING name;
     BOOL hl_alloc = FALSE;
     file_ref *parfr, *fr;
-#ifdef __REACTOS__
-    hardlink* hl;
-#endif
 
-    Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, PagedPool, Irp);
+    Status = open_fcb(Vcb, subvol, inode, 0, NULL, TRUE, NULL, &fcb, PagedPool, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
         return Status;
     }
 
+    ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
+
+    if (fcb->inode_item.st_nlink == 0 || fcb->deleted) {
+        ExReleaseResourceLite(fcb->Header.Resource);
+        free_fcb(fcb);
+        return STATUS_OBJECT_NAME_NOT_FOUND;
+    }
+
     if (fcb->fileref) {
         *pfr = fcb->fileref;
         increase_fileref_refcount(fcb->fileref);
+        free_fcb(fcb);
+        ExReleaseResourceLite(fcb->Header.Resource);
         return STATUS_SUCCESS;
     }
 
+    if (IsListEmpty(&fcb->hardlinks)) {
+        ExReleaseResourceLite(fcb->Header.Resource);
+
+        ExAcquireResourceSharedLite(&Vcb->dirty_filerefs_lock, TRUE);
+
+        if (!IsListEmpty(&Vcb->dirty_filerefs)) {
+            LIST_ENTRY* le = Vcb->dirty_filerefs.Flink;
+            while (le != &Vcb->dirty_filerefs) {
+                file_ref* fr = CONTAINING_RECORD(le, file_ref, list_entry_dirty);
+
+                if (fr->fcb == fcb) {
+                    ExReleaseResourceLite(&Vcb->dirty_filerefs_lock);
+                    increase_fileref_refcount(fr);
+                    free_fcb(fcb);
+                    *pfr = fr;
+                    return STATUS_SUCCESS;
+                }
+
+                le = le->Flink;
+            }
+        }
+
+        ExReleaseResourceLite(&Vcb->dirty_filerefs_lock);
+
+        {
+            KEY searchkey;
+            traverse_ptr tp;
+
+            searchkey.obj_id = fcb->inode;
+            searchkey.obj_type = TYPE_INODE_REF;
+            searchkey.offset = 0;
+
+            Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("find_item returned %08x\n", Status);
+                free_fcb(fcb);
+                return Status;
+            }
+
+            do {
+                traverse_ptr next_tp;
+
+                if (tp.item->key.obj_id > fcb->inode || (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type > TYPE_INODE_EXTREF))
+                    break;
+
+                if (tp.item->key.obj_id == fcb->inode) {
+                    if (tp.item->key.obj_type == TYPE_INODE_REF) {
+                        INODE_REF* ir = (INODE_REF*)tp.item->data;
+#ifdef __REACTOS__
+                        ULONG stringlen;
+#endif
+
+                        if (tp.item->size < offsetof(INODE_REF, name[0]) || tp.item->size < offsetof(INODE_REF, name[0]) + ir->n) {
+                            ERR("INODE_REF was too short\n");
+                            free_fcb(fcb);
+                            return STATUS_INTERNAL_ERROR;
+                        }
+
 #ifndef __REACTOS__
-    hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
-#else
-    hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
+                        ULONG stringlen;
 #endif
 
-    name = hl->name;
-    parent = hl->parent;
+                        Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+                            free_fcb(fcb);
+                            return Status;
+                        }
+
+                        name.Length = name.MaximumLength = (UINT16)stringlen;
+
+                        if (stringlen == 0)
+                            name.Buffer = NULL;
+                        else {
+                            name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
+
+                            if (!name.Buffer) {
+                                ERR("out of memory\n");
+                                free_fcb(fcb);
+                                return STATUS_INSUFFICIENT_RESOURCES;
+                            }
+
+                            Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ir->name, ir->n);
+                            if (!NT_SUCCESS(Status)) {
+                                ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+                                ExFreePool(name.Buffer);
+                                free_fcb(fcb);
+                                return Status;
+                            }
+
+                            hl_alloc = TRUE;
+                        }
+
+                        parent = tp.item->key.offset;
+
+                        break;
+                    } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
+                        INODE_EXTREF* ier = (INODE_EXTREF*)tp.item->data;
+#ifdef __REACTOS__
+                        ULONG stringlen;
+#endif
+
+                        if (tp.item->size < offsetof(INODE_EXTREF, name[0]) || tp.item->size < offsetof(INODE_EXTREF, name[0]) + ier->n) {
+                            ERR("INODE_EXTREF was too short\n");
+                            free_fcb(fcb);
+                            return STATUS_INTERNAL_ERROR;
+                        }
+
+#ifndef __REACTOS__
+                        ULONG stringlen;
+#endif
+
+                        Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
+                            free_fcb(fcb);
+                            return Status;
+                        }
+
+                        name.Length = name.MaximumLength = (UINT16)stringlen;
+
+                        if (stringlen == 0)
+                            name.Buffer = NULL;
+                        else {
+                            name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
+
+                            if (!name.Buffer) {
+                                ERR("out of memory\n");
+                                free_fcb(fcb);
+                                return STATUS_INSUFFICIENT_RESOURCES;
+                            }
+
+                            Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ier->name, ier->n);
+                            if (!NT_SUCCESS(Status)) {
+                                ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status);
+                                ExFreePool(name.Buffer);
+                                free_fcb(fcb);
+                                return Status;
+                            }
+
+                            hl_alloc = TRUE;
+                        }
+
+                        parent = ier->dir;
+
+                        break;
+                    }
+                }
+
+                if (find_next_item(Vcb, &tp, &next_tp, FALSE, Irp))
+                    tp = next_tp;
+                else
+                    break;
+            } while (TRUE);
+        }
+
+        if (parent == 0) {
+            WARN("trying to open inode with no references\n");
+            free_fcb(fcb);
+            return STATUS_INVALID_PARAMETER;
+        }
+    } else {
+        hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
+
+        name = hl->name;
+        parent = hl->parent;
+
+        ExReleaseResourceLite(fcb->Header.Resource);
+    }
 
     if (parent == inode) { // subvolume root
         KEY searchkey;
@@ -4048,6 +4232,9 @@ NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock)
             if (stringlen == 0)
                 name.Buffer = NULL;
             else {
+                if (hl_alloc)
+                    ExFreePool(name.Buffer);
+
                 name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
 
                 if (!name.Buffer) {
@@ -4190,9 +4377,21 @@ static NTSTATUS open_file(PDEVICE_OBJECT DeviceObject, _Requires_lock_held_(_Cur
     }
 
     if (options & FILE_OPEN_BY_FILE_ID) {
-        if (fn.Length == sizeof(UINT64) && related && RequestedDisposition == FILE_OPEN) {
+        if (RequestedDisposition != FILE_OPEN) {
+            WARN("FILE_OPEN_BY_FILE_ID not supported for anything other than FILE_OPEN\n");
+            Status = STATUS_INVALID_PARAMETER;
+            goto exit;
+        }
+
+        if (fn.Length == sizeof(UINT64)) {
             UINT64 inode;
 
+            if (!related) {
+                WARN("cannot open by short file ID unless related fileref also provided");
+                Status = STATUS_INVALID_PARAMETER;
+                goto exit;
+            }
+
             RtlCopyMemory(&inode, fn.Buffer, sizeof(UINT64));
 
             if (related->fcb == Vcb->root_fileref->fcb && inode == 0)
@@ -4205,10 +4404,38 @@ static NTSTATUS open_file(PDEVICE_OBJECT DeviceObject, _Requires_lock_held_(_Cur
             } else
                 Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref, Irp);
 
+            goto loaded;
+        } else if (fn.Length == sizeof(FILE_ID_128)) {
+            UINT64 inode, subvol_id;
+            root* subvol = NULL;
+
+            RtlCopyMemory(&inode, fn.Buffer, sizeof(UINT64));
+            RtlCopyMemory(&subvol_id, (UINT8*)fn.Buffer + sizeof(UINT64), sizeof(UINT64));
+
+            if (subvol_id == BTRFS_ROOT_FSTREE || (subvol_id >= 0x100 && subvol_id < 0x8000000000000000)) {
+                LIST_ENTRY* le = Vcb->roots.Flink;
+                while (le != &Vcb->roots) {
+                    root* r = CONTAINING_RECORD(le, root, list_entry);
+
+                    if (r->id == subvol_id) {
+                        subvol = r;
+                        break;
+                    }
+
+                    le = le->Flink;
+                }
+            }
+
+            if (!subvol) {
+                WARN("subvol %llx not found\n", subvol_id);
+                Status = STATUS_OBJECT_NAME_NOT_FOUND;
+            } else
+                Status = open_fileref_by_inode(Vcb, subvol, inode, &fileref, Irp);
+
             goto loaded;
         } else {
-            WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
-            Status = STATUS_NOT_IMPLEMENTED;
+            WARN("invalid ID size for FILE_OPEN_BY_FILE_ID\n");
+            Status = STATUS_INVALID_PARAMETER;
             goto exit;
         }
     }
index 19b261b..48e7fc2 100644 (file)
 
 #include "btrfs_drv.h"
 
+// not currently in mingw
+#ifndef _MSC_VER
+#define FileIdExtdDirectoryInformation (enum _FILE_INFORMATION_CLASS)60
+#define FileIdExtdBothDirectoryInformation (enum _FILE_INFORMATION_CLASS)63
+
+typedef struct _FILE_ID_EXTD_DIR_INFORMATION {
+    ULONG NextEntryOffset;
+    ULONG FileIndex;
+    LARGE_INTEGER CreationTime;
+    LARGE_INTEGER LastAccessTime;
+    LARGE_INTEGER LastWriteTime;
+    LARGE_INTEGER ChangeTime;
+    LARGE_INTEGER EndOfFile;
+    LARGE_INTEGER AllocationSize;
+    ULONG FileAttributes;
+    ULONG FileNameLength;
+    ULONG EaSize;
+    ULONG ReparsePointTag;
+    FILE_ID_128 FileId;
+    WCHAR FileName[1];
+} FILE_ID_EXTD_DIR_INFORMATION, *PFILE_ID_EXTD_DIR_INFORMATION;
+
+typedef struct _FILE_ID_EXTD_BOTH_DIR_INFORMATION {
+    ULONG NextEntryOffset;
+    ULONG FileIndex;
+    LARGE_INTEGER CreationTime;
+    LARGE_INTEGER LastAccessTime;
+    LARGE_INTEGER LastWriteTime;
+    LARGE_INTEGER ChangeTime;
+    LARGE_INTEGER EndOfFile;
+    LARGE_INTEGER AllocationSize;
+    ULONG FileAttributes;
+    ULONG FileNameLength;
+    ULONG EaSize;
+    ULONG ReparsePointTag;
+    FILE_ID_128 FileId;
+    CCHAR ShortNameLength;
+    WCHAR ShortName[12];
+    WCHAR FileName[1];
+} FILE_ID_EXTD_BOTH_DIR_INFORMATION, *PFILE_ID_EXTD_BOTH_DIR_INFORMATION;
+
+#endif
+
 enum DirEntryType {
     DirEntryType_File,
     DirEntryType_Self,
@@ -79,7 +122,7 @@ ULONG get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 t
     if (!(atts & FILE_ATTRIBUTE_REPARSE_POINT))
         return 0;
 
-    Status = open_fcb(Vcb, subvol, inode, type, NULL, NULL, &fcb, PagedPool, Irp);
+    Status = open_fcb(Vcb, subvol, inode, type, NULL, FALSE, NULL, &fcb, PagedPool, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
         return 0;
@@ -221,7 +264,9 @@ static NTSTATUS query_dir_item(fcb* fcb, ccb* ccb, void* buf, LONG* len, PIRP Ir
                             IrpSp->Parameters.QueryDirectory.FileInformationClass == FileDirectoryInformation ||
                             IrpSp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation ||
                             IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation ||
-                            IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdFullDirectoryInformation) {
+                            IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdFullDirectoryInformation ||
+                            IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdExtdDirectoryInformation ||
+                            IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdExtdBothDirectoryInformation) {
 
                             BOOL dotfile = de->name.Length > sizeof(WCHAR) && de->name.Buffer[0] == '.';
 
@@ -231,7 +276,9 @@ static NTSTATUS query_dir_item(fcb* fcb, ccb* ccb, void* buf, LONG* len, PIRP Ir
                         if (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation ||
                             IrpSp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation ||
                             IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation ||
-                            IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdFullDirectoryInformation) {
+                            IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdFullDirectoryInformation ||
+                            IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdExtdDirectoryInformation ||
+                            IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdExtdBothDirectoryInformation) {
                             ealen = get_ea_len(fcb->Vcb, r, inode, Irp);
                         }
                     }
@@ -465,6 +512,102 @@ static NTSTATUS query_dir_item(fcb* fcb, ccb* ccb, void* buf, LONG* len, PIRP Ir
             return STATUS_SUCCESS;
         }
 
+#ifndef _MSC_VER
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch"
+#endif
+        case FileIdExtdDirectoryInformation:
+        {
+            FILE_ID_EXTD_DIR_INFORMATION* fiedi = buf;
+
+            TRACE("FileIdExtdDirectoryInformation\n");
+
+            needed = offsetof(FILE_ID_EXTD_DIR_INFORMATION, FileName[0]) + de->name.Length;
+
+            if (needed > *len) {
+                TRACE("buffer overflow - %u > %u\n", needed, *len);
+                return STATUS_BUFFER_OVERFLOW;
+            }
+
+            fiedi->NextEntryOffset = 0;
+            fiedi->FileIndex = 0;
+            fiedi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
+            fiedi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
+            fiedi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
+            fiedi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
+            fiedi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
+
+            if (de->type == BTRFS_TYPE_SYMLINK)
+                fiedi->AllocationSize.QuadPart = 0;
+            else if (atts & FILE_ATTRIBUTE_SPARSE_FILE)
+                fiedi->AllocationSize.QuadPart = ii.st_blocks;
+            else
+                fiedi->AllocationSize.QuadPart = sector_align(ii.st_size, fcb->Vcb->superblock.sector_size);
+
+            fiedi->FileAttributes = atts;
+            fiedi->FileNameLength = de->name.Length;
+            fiedi->EaSize = ealen;
+            fiedi->ReparsePointTag = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, ccb->lxss, Irp);
+
+            RtlCopyMemory(&fiedi->FileId.Identifier[0], &fcb->inode, sizeof(UINT64));
+            RtlCopyMemory(&fiedi->FileId.Identifier[sizeof(UINT64)], &fcb->subvol->id, sizeof(UINT64));
+
+            RtlCopyMemory(fiedi->FileName, de->name.Buffer, de->name.Length);
+
+            *len -= needed;
+
+            return STATUS_SUCCESS;
+        }
+
+        case FileIdExtdBothDirectoryInformation:
+        {
+            FILE_ID_EXTD_BOTH_DIR_INFORMATION* fiebdi = buf;
+
+            TRACE("FileIdExtdBothDirectoryInformation\n");
+
+            needed = offsetof(FILE_ID_EXTD_BOTH_DIR_INFORMATION, FileName[0]) + de->name.Length;
+
+            if (needed > *len) {
+                TRACE("buffer overflow - %u > %u\n", needed, *len);
+                return STATUS_BUFFER_OVERFLOW;
+            }
+
+            fiebdi->NextEntryOffset = 0;
+            fiebdi->FileIndex = 0;
+            fiebdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
+            fiebdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
+            fiebdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
+            fiebdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
+            fiebdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
+
+            if (de->type == BTRFS_TYPE_SYMLINK)
+                fiebdi->AllocationSize.QuadPart = 0;
+            else if (atts & FILE_ATTRIBUTE_SPARSE_FILE)
+                fiebdi->AllocationSize.QuadPart = ii.st_blocks;
+            else
+                fiebdi->AllocationSize.QuadPart = sector_align(ii.st_size, fcb->Vcb->superblock.sector_size);
+
+            fiebdi->FileAttributes = atts;
+            fiebdi->FileNameLength = de->name.Length;
+            fiebdi->EaSize = ealen;
+            fiebdi->ReparsePointTag = get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, ccb->lxss, Irp);
+
+            RtlCopyMemory(&fiebdi->FileId.Identifier[0], &fcb->inode, sizeof(UINT64));
+            RtlCopyMemory(&fiebdi->FileId.Identifier[sizeof(UINT64)], &fcb->subvol->id, sizeof(UINT64));
+
+            fiebdi->ShortNameLength = 0;
+
+            RtlCopyMemory(fiebdi->FileName, de->name.Buffer, de->name.Length);
+
+            *len -= needed;
+
+            return STATUS_SUCCESS;
+        }
+
+#ifndef _MSC_VER
+#pragma GCC diagnostic pop
+#endif
+
         case FileNamesInformation:
         {
             FILE_NAMES_INFORMATION* fni = buf;
@@ -857,13 +1000,22 @@ static NTSTATUS query_directory(PIRP Irp) {
 
         while (length > 0) {
             switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
+#ifndef _MSC_VER
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch"
+#endif
                 case FileBothDirectoryInformation:
                 case FileDirectoryInformation:
                 case FileIdBothDirectoryInformation:
                 case FileFullDirectoryInformation:
                 case FileIdFullDirectoryInformation:
+                case FileIdExtdDirectoryInformation:
+                case FileIdExtdBothDirectoryInformation:
                     length -= length % 8;
                     break;
+#ifndef _MSC_VER
+#pragma GCC diagnostic pop
+#endif
 
                 case FileNamesInformation:
                     length -= length % 4;
index 2339c5d..06ae42a 100644 (file)
 // not currently in mingw - introduced with Windows 10
 #ifndef _MSC_VER
 #define FileIdInformation (enum _FILE_INFORMATION_CLASS)59
+#define FileHardLinkFullIdInformation (enum _FILE_INFORMATION_CLASS)62
+#define FileDispositionInformationEx (enum _FILE_INFORMATION_CLASS)64
+#define FileRenameInformationEx (enum _FILE_INFORMATION_CLASS)65
+#define FileStatInformation (enum _FILE_INFORMATION_CLASS)68
 #define FileStatLxInformation (enum _FILE_INFORMATION_CLASS)70
+#define FileCaseSensitiveInformation (enum _FILE_INFORMATION_CLASS)71
+#define FileLinkInformationEx (enum _FILE_INFORMATION_CLASS)72
+
+typedef struct _FILE_ID_INFORMATION {
+    ULONGLONG VolumeSerialNumber;
+    FILE_ID_128 FileId;
+} FILE_ID_INFORMATION, *PFILE_ID_INFORMATION;
+
+typedef struct _FILE_STAT_INFORMATION {
+    LARGE_INTEGER FileId;
+    LARGE_INTEGER CreationTime;
+    LARGE_INTEGER LastAccessTime;
+    LARGE_INTEGER LastWriteTime;
+    LARGE_INTEGER ChangeTime;
+    LARGE_INTEGER AllocationSize;
+    LARGE_INTEGER EndOfFile;
+    ULONG FileAttributes;
+    ULONG ReparseTag;
+    ULONG NumberOfLinks;
+    ACCESS_MASK EffectiveAccess;
+} FILE_STAT_INFORMATION, *PFILE_STAT_INFORMATION;
 
 typedef struct _FILE_STAT_LX_INFORMATION {
     LARGE_INTEGER FileId;
@@ -49,9 +74,119 @@ typedef struct _FILE_STAT_LX_INFORMATION {
 #define LX_FILE_METADATA_HAS_DEVICE_ID  0x08
 #define LX_FILE_CASE_SENSITIVE_DIR      0x10
 
+typedef struct _FILE_RENAME_INFORMATION_EX {
+    union {
+        BOOLEAN ReplaceIfExists;
+        ULONG Flags;
+    };
+    HANDLE RootDirectory;
+    ULONG FileNameLength;
+    WCHAR FileName[1];
+} FILE_RENAME_INFORMATION_EX, *PFILE_RENAME_INFORMATION_EX;
+
+typedef struct _FILE_DISPOSITION_INFORMATION_EX {
+    ULONG Flags;
+} FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
+
+typedef struct _FILE_LINK_INFORMATION_EX {
+    union {
+        BOOLEAN ReplaceIfExists;
+        ULONG Flags;
+    };
+    HANDLE RootDirectory;
+    ULONG FileNameLength;
+    WCHAR FileName[1];
+} FILE_LINK_INFORMATION_EX, *PFILE_LINK_INFORMATION_EX;
+
+typedef struct _FILE_CASE_SENSITIVE_INFORMATION {
+    ULONG Flags;
+} FILE_CASE_SENSITIVE_INFORMATION, *PFILE_CASE_SENSITIVE_INFORMATION;
+
+typedef struct _FILE_LINK_ENTRY_FULL_ID_INFORMATION {
+    ULONG NextEntryOffset;
+    FILE_ID_128 ParentFileId;
+    ULONG FileNameLength;
+    WCHAR FileName[1];
+} FILE_LINK_ENTRY_FULL_ID_INFORMATION, *PFILE_LINK_ENTRY_FULL_ID_INFORMATION;
+
+typedef struct _FILE_LINKS_FULL_ID_INFORMATION {
+    ULONG BytesNeeded;
+    ULONG EntriesReturned;
+    FILE_LINK_ENTRY_FULL_ID_INFORMATION Entry;
+} FILE_LINKS_FULL_ID_INFORMATION, *PFILE_LINKS_FULL_ID_INFORMATION;
+
+#define FILE_RENAME_REPLACE_IF_EXISTS                       0x001
+#define FILE_RENAME_POSIX_SEMANTICS                         0x002
+#define FILE_RENAME_SUPPRESS_PIN_STATE_INHERITANCE          0x004
+#define FILE_RENAME_SUPPRESS_STORAGE_RESERVE_INHERITANCE    0x008
+#define FILE_RENAME_NO_INCREASE_AVAILABLE_SPACE             0x010
+#define FILE_RENAME_NO_DECREASE_AVAILABLE_SPACE             0x020
+#define FILE_RENAME_IGNORE_READONLY_ATTRIBUTE               0x040
+#define FILE_RENAME_FORCE_RESIZE_TARGET_SR                  0x080
+#define FILE_RENAME_FORCE_RESIZE_SOURCE_SR                  0x100
+
+#define FILE_DISPOSITION_DELETE                         0x1
+#define FILE_DISPOSITION_POSIX_SEMANTICS                0x2
+#define FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK      0x4
+#define FILE_DISPOSITION_ON_CLOSE                       0x8
+
+#define FILE_LINK_REPLACE_IF_EXISTS                       0x001
+#define FILE_LINK_POSIX_SEMANTICS                         0x002
+#define FILE_LINK_SUPPRESS_STORAGE_RESERVE_INHERITANCE    0x008
+#define FILE_LINK_NO_INCREASE_AVAILABLE_SPACE             0x010
+#define FILE_LINK_NO_DECREASE_AVAILABLE_SPACE             0x020
+#define FILE_LINK_IGNORE_READONLY_ATTRIBUTE               0x040
+#define FILE_LINK_FORCE_RESIZE_TARGET_SR                  0x080
+#define FILE_LINK_FORCE_RESIZE_SOURCE_SR                  0x100
+
+#define FILE_CS_FLAG_CASE_SENSITIVE_DIR                 1
+
+#else
+
+#define FILE_RENAME_INFORMATION_EX FILE_RENAME_INFORMATION
+#define FILE_LINK_INFORMATION_EX FILE_LINK_INFORMATION
+
 #endif
 #endif
 
+#ifdef __REACTOS__
+typedef struct _FILE_RENAME_INFORMATION_EX {
+    union {
+        BOOLEAN ReplaceIfExists;
+        ULONG Flags;
+    };
+    HANDLE RootDirectory;
+    ULONG FileNameLength;
+    WCHAR FileName[1];
+} FILE_RENAME_INFORMATION_EX, *PFILE_RENAME_INFORMATION_EX;
+
+typedef struct _FILE_DISPOSITION_INFORMATION_EX {
+    ULONG Flags;
+} FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
+
+typedef struct _FILE_LINK_INFORMATION_EX {
+    union {
+        BOOLEAN ReplaceIfExists;
+        ULONG Flags;
+    };
+    HANDLE RootDirectory;
+    ULONG FileNameLength;
+    WCHAR FileName[1];
+} FILE_LINK_INFORMATION_EX, *PFILE_LINK_INFORMATION_EX;
+
+#define FILE_RENAME_REPLACE_IF_EXISTS                       0x001
+#define FILE_RENAME_POSIX_SEMANTICS                         0x002
+#define FILE_RENAME_IGNORE_READONLY_ATTRIBUTE               0x040
+
+#define FILE_DISPOSITION_DELETE                         0x1
+#define FILE_DISPOSITION_POSIX_SEMANTICS                0x2
+#define FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK      0x4
+
+#define FILE_LINK_REPLACE_IF_EXISTS                       0x001
+#define FILE_LINK_POSIX_SEMANTICS                         0x002
+#define FILE_LINK_IGNORE_READONLY_ATTRIBUTE               0x040
+#endif
+
 static NTSTATUS set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
     FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer;
     fcb* fcb = FileObject->FsContext;
@@ -219,20 +354,29 @@ end:
     return Status;
 }
 
-static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) {
-    FILE_DISPOSITION_INFORMATION* fdi = Irp->AssociatedIrp.SystemBuffer;
+static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL ex) {
     fcb* fcb = FileObject->FsContext;
     ccb* ccb = FileObject->FsContext2;
     file_ref* fileref = ccb ? ccb->fileref : NULL;
-    ULONG atts;
+    ULONG atts, flags;
     NTSTATUS Status;
 
     if (!fileref)
         return STATUS_INVALID_PARAMETER;
 
+    if (ex) {
+        FILE_DISPOSITION_INFORMATION_EX* fdi = Irp->AssociatedIrp.SystemBuffer;
+
+        flags = fdi->Flags;
+    } else {
+        FILE_DISPOSITION_INFORMATION* fdi = Irp->AssociatedIrp.SystemBuffer;
+
+        flags = fdi->DeleteFile ? FILE_DISPOSITION_DELETE : 0;
+    }
+
     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
 
-    TRACE("changing delete_on_close to %s for %S (fcb %p)\n", fdi->DeleteFile ? "TRUE" : "FALSE", file_desc(FileObject), fcb);
+    TRACE("changing delete_on_close to %s for %S (fcb %p)\n", flags & FILE_DISPOSITION_DELETE ? "TRUE" : "FALSE", file_desc(FileObject), fcb);
 
     if (fcb->ads) {
         if (fileref->parent)
@@ -261,14 +405,19 @@ static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFI
     }
 
     if (!MmFlushImageSection(&fcb->nonpaged->segment_object, MmFlushForDelete)) {
-        TRACE("trying to delete file which is being mapped as an image\n");
-        Status = STATUS_CANNOT_DELETE;
-        goto end;
+        if (!ex || flags & FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK) {
+            TRACE("trying to delete file which is being mapped as an image\n");
+            Status = STATUS_CANNOT_DELETE;
+            goto end;
+        }
     }
 
-    ccb->fileref->delete_on_close = fdi->DeleteFile;
+    ccb->fileref->delete_on_close = flags & FILE_DISPOSITION_DELETE;
+
+    FileObject->DeletePending = flags & FILE_DISPOSITION_DELETE;
 
-    FileObject->DeletePending = fdi->DeleteFile;
+    if (flags & FILE_DISPOSITION_DELETE && flags & FILE_DISPOSITION_POSIX_SEMANTICS)
+        ccb->fileref->posix_delete = TRUE;
 
     Status = STATUS_SUCCESS;
 
@@ -276,7 +425,7 @@ end:
     ExReleaseResourceLite(fcb->Header.Resource);
 
     // send notification that directory is about to be deleted
-    if (NT_SUCCESS(Status) && fdi->DeleteFile && fcb->type == BTRFS_TYPE_DIRECTORY) {
+    if (NT_SUCCESS(Status) && flags & FILE_DISPOSITION_DELETE && fcb->type == BTRFS_TYPE_DIRECTORY) {
         FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext,
                                        NULL, FALSE, FALSE, 0, NULL, NULL, NULL);
     }
@@ -1161,7 +1310,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destd
         }
 
         if (!me->dummyfileref->fcb->ads) {
-            Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
+            Status = delete_fileref(me->dummyfileref, NULL, FALSE, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_fileref returned %08x\n", Status);
                 goto end;
@@ -1217,7 +1366,7 @@ static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destd
         me = CONTAINING_RECORD(le, move_entry, list_entry);
 
         if (me->dummyfileref->fcb->ads && me->parent->dummyfileref->fcb->deleted) {
-            Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback);
+            Status = delete_fileref(me->dummyfileref, NULL, FALSE, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_fileref returned %08x\n", Status);
                 goto end;
@@ -1349,9 +1498,8 @@ void insert_dir_child_into_hash_lists(fcb* fcb, dir_child* dc) {
     }
 }
 
-static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) {
-    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer;
+static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, BOOL ex) {
+    FILE_RENAME_INFORMATION_EX* fri = Irp->AssociatedIrp.SystemBuffer;
     fcb *fcb = FileObject->FsContext;
     ccb* ccb = FileObject->FsContext2;
     file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
@@ -1366,11 +1514,17 @@ static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OB
     hardlink* hl;
     SECURITY_SUBJECT_CONTEXT subjcont;
     ACCESS_MASK access;
+    ULONG flags;
 
     InitializeListHead(&rollback);
 
+    if (ex)
+        flags = fri->Flags;
+    else
+        flags = fri->ReplaceIfExists ? FILE_RENAME_REPLACE_IF_EXISTS : 0;
+
     TRACE("tfo = %p\n", tfo);
-    TRACE("ReplaceIfExists = %u\n", IrpSp->Parameters.SetFile.ReplaceIfExists);
+    TRACE("Flags = %x\n", flags);
     TRACE("RootDirectory = %p\n", fri->RootDirectory);
     TRACE("FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName);
 
@@ -1451,16 +1605,21 @@ static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OB
         TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref));
 
         if (fileref != oldfileref && !oldfileref->deleted) {
-            if (!IrpSp->Parameters.SetFile.ReplaceIfExists) {
+            if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) {
                 Status = STATUS_OBJECT_NAME_COLLISION;
                 goto end;
-            } else if ((oldfileref->open_count >= 1 || has_open_children(oldfileref)) && !oldfileref->deleted) {
+            } else if (fileref == oldfileref) {
+                Status = STATUS_ACCESS_DENIED;
+                goto end;
+            } else if (!(flags & FILE_RENAME_POSIX_SEMANTICS) && (oldfileref->open_count > 0 || has_open_children(oldfileref)) && !oldfileref->deleted) {
                 WARN("trying to overwrite open file\n");
                 Status = STATUS_ACCESS_DENIED;
                 goto end;
-            }
-
-            if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
+            } else if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && oldfileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
+                WARN("trying to overwrite readonly file\n");
+                Status = STATUS_ACCESS_DENIED;
+                goto end;
+            } else if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
                 WARN("trying to overwrite directory\n");
                 Status = STATUS_ACCESS_DENIED;
                 goto end;
@@ -1516,7 +1675,12 @@ static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OB
 
         SeReleaseSubjectContext(&subjcont);
 
-        Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
+        if (oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS) {
+            oldfileref->delete_on_close = TRUE;
+            oldfileref->posix_delete = TRUE;
+        }
+
+        Status = delete_fileref(oldfileref, NULL, oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS, Irp, &rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("delete_fileref returned %08x\n", Status);
             goto end;
@@ -2177,8 +2341,8 @@ static NTSTATUS set_position_information(PFILE_OBJECT FileObject, PIRP Irp) {
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) {
-    FILE_LINK_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer;
+static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, BOOL ex) {
+    FILE_LINK_INFORMATION_EX* fli = Irp->AssociatedIrp.SystemBuffer;
     fcb *fcb = FileObject->FsContext, *tfofcb, *parfcb;
     ccb* ccb = FileObject->FsContext2;
     file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL;
@@ -2194,13 +2358,19 @@ static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJE
     ACCESS_MASK access;
     SECURITY_SUBJECT_CONTEXT subjcont;
     dir_child* dc = NULL;
+    ULONG flags;
 
     InitializeListHead(&rollback);
 
     // FIXME - check fli length
     // FIXME - don't ignore fli->RootDirectory
 
-    TRACE("ReplaceIfExists = %x\n", fli->ReplaceIfExists);
+    if (ex)
+        flags = fli->Flags;
+    else
+        flags = fli->ReplaceIfExists ? FILE_LINK_REPLACE_IF_EXISTS : 0;
+
+    TRACE("flags = %x\n", flags);
     TRACE("RootDirectory = %p\n", fli->RootDirectory);
     TRACE("FileNameLength = %x\n", fli->FileNameLength);
     TRACE("FileName = %.*S\n", fli->FileNameLength / sizeof(WCHAR), fli->FileName);
@@ -2292,19 +2462,21 @@ static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJE
         if (!oldfileref->deleted) {
             WARN("destination file %S already exists\n", file_desc_fileref(oldfileref));
 
-            if (!fli->ReplaceIfExists) {
+            if (!(flags & FILE_LINK_REPLACE_IF_EXISTS)) {
                 Status = STATUS_OBJECT_NAME_COLLISION;
                 goto end;
-            } else if (oldfileref->open_count >= 1 && !oldfileref->deleted) {
+            } else if (fileref == oldfileref) {
+                Status = STATUS_ACCESS_DENIED;
+                goto end;
+            } else if (!(flags & FILE_LINK_POSIX_SEMANTICS) && (oldfileref->open_count > 0 || has_open_children(oldfileref)) && !oldfileref->deleted) {
                 WARN("trying to overwrite open file\n");
                 Status = STATUS_ACCESS_DENIED;
                 goto end;
-            } else if (fileref == oldfileref) {
+            } else if (!(flags & FILE_LINK_IGNORE_READONLY_ATTRIBUTE) && oldfileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
+                WARN("trying to overwrite readonly file\n");
                 Status = STATUS_ACCESS_DENIED;
                 goto end;
-            }
-
-            if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
+            } else if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) {
                 WARN("trying to overwrite directory\n");
                 Status = STATUS_ACCESS_DENIED;
                 goto end;
@@ -2353,7 +2525,12 @@ static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJE
 
         SeReleaseSubjectContext(&subjcont);
 
-        Status = delete_fileref(oldfileref, NULL, Irp, &rollback);
+        if (oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS) {
+            oldfileref->delete_on_close = TRUE;
+            oldfileref->posix_delete = TRUE;
+        }
+
+        Status = delete_fileref(oldfileref, NULL, oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS, Irp, &rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("delete_fileref returned %08x\n", Status);
             goto end;
@@ -2608,6 +2785,40 @@ end:
     return Status;
 }
 
+#ifndef __REACTOS__
+static NTSTATUS set_case_sensitive_information(PIRP Irp) {
+    FILE_CASE_SENSITIVE_INFORMATION* fcsi = (FILE_CASE_SENSITIVE_INFORMATION*)Irp->AssociatedIrp.SystemBuffer;
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+    if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(FILE_CASE_SENSITIVE_INFORMATION))
+        return STATUS_INFO_LENGTH_MISMATCH;
+
+    PFILE_OBJECT FileObject = IrpSp->FileObject;
+
+    if (!FileObject)
+        return STATUS_INVALID_PARAMETER;
+
+    fcb* fcb = FileObject->FsContext;
+
+    if (!fcb)
+        return STATUS_INVALID_PARAMETER;
+
+    if (!(fcb->atts & FILE_ATTRIBUTE_DIRECTORY)) {
+        WARN("cannot set case-sensitive flag on anything other than directory\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
+
+    fcb->case_sensitive = fcsi->Flags & FILE_CS_FLAG_CASE_SENSITIVE_DIR;
+    mark_fcb_dirty(fcb);
+
+    ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+
+    return STATUS_SUCCESS;
+}
+#endif
+
 _Dispatch_type_(IRP_MJ_SET_INFORMATION)
 _Function_class_(DRIVER_DISPATCH)
 NTSTATUS NTAPI drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
@@ -2655,7 +2866,11 @@ NTSTATUS NTAPI drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
     }
 
     if (fcb != Vcb->dummy_fcb && is_subvol_readonly(fcb->subvol, Irp) && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation &&
+#ifndef __REACTOS__
+        (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformationEx))) {
+#else
         (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation))) {
+#endif
         Status = STATUS_ACCESS_DENIED;
         goto end;
     }
@@ -2704,7 +2919,7 @@ NTSTATUS NTAPI drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
                 break;
             }
 
-            Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject);
+            Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject, FALSE);
 
             break;
         }
@@ -2726,7 +2941,7 @@ NTSTATUS NTAPI drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
 
         case FileLinkInformation:
             TRACE("FileLinkInformation\n");
-            Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject);
+            Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, FALSE);
             break;
 
         case FilePositionInformation:
@@ -2737,7 +2952,7 @@ NTSTATUS NTAPI drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
         case FileRenameInformation:
             TRACE("FileRenameInformation\n");
             // FIXME - make this work with streams
-            Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject);
+            Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, FALSE);
             break;
 
         case FileValidDataLengthInformation:
@@ -2755,6 +2970,52 @@ NTSTATUS NTAPI drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
             break;
         }
 
+#ifndef _MSC_VER
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch"
+#endif
+#ifndef __REACTOS__
+        case FileDispositionInformationEx:
+        {
+            TRACE("FileDispositionInformationEx\n");
+
+            if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) {
+                WARN("insufficient privileges\n");
+                Status = STATUS_ACCESS_DENIED;
+                break;
+            }
+
+            Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject, TRUE);
+
+            break;
+        }
+
+        case FileRenameInformationEx:
+            TRACE("FileRenameInformationEx\n");
+            Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, TRUE);
+            break;
+
+        case FileLinkInformationEx:
+            TRACE("FileLinkInformationEx\n");
+            Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, TRUE);
+            break;
+
+        case FileCaseSensitiveInformation:
+            TRACE("FileCaseSensitiveInformation\n");
+
+            if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) {
+                WARN("insufficient privileges\n");
+                Status = STATUS_ACCESS_DENIED;
+                break;
+            }
+
+            Status = set_case_sensitive_information(Irp);
+            break;
+#endif
+#ifndef _MSC_VER
+#pragma GCC diagnostic pop
+#endif
+
         default:
             WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass);
     }
@@ -3362,16 +3623,176 @@ static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_
 #endif /* __REACTOS__ */
 
 #if (NTDDI_VERSION >= NTDDI_WIN10)
-#ifdef __MINGW32__
-typedef struct _FILE_ID_128 {
-    UCHAR Identifier[16];
-} FILE_ID_128, *PFILE_ID_128;
+static NTSTATUS fill_in_hard_link_full_id_information(FILE_LINKS_FULL_ID_INFORMATION* flfii, file_ref* fileref, PIRP Irp, LONG* length) {
+    NTSTATUS Status;
+    LIST_ENTRY* le;
+    LONG bytes_needed;
+    FILE_LINK_ENTRY_FULL_ID_INFORMATION* flefii;
+    BOOL overflow = FALSE;
+    fcb* fcb = fileref->fcb;
+    ULONG len;
 
-typedef struct _FILE_ID_INFORMATION {
-    ULONGLONG VolumeSerialNumber;
-    FILE_ID_128 FileId;
-} FILE_ID_INFORMATION, *PFILE_ID_INFORMATION;
-#endif
+    if (fcb->ads)
+        return STATUS_INVALID_PARAMETER;
+
+    if (*length < (LONG)offsetof(FILE_LINKS_FULL_ID_INFORMATION, Entry))
+        return STATUS_INVALID_PARAMETER;
+
+    RtlZeroMemory(flfii, *length);
+
+    bytes_needed = offsetof(FILE_LINKS_FULL_ID_INFORMATION, Entry);
+    len = bytes_needed;
+    flefii = NULL;
+
+    ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
+
+    if (fcb->inode == SUBVOL_ROOT_INODE) {
+        ULONG namelen;
+
+        if (fcb == fcb->Vcb->root_fileref->fcb)
+            namelen = sizeof(WCHAR);
+        else
+            namelen = fileref->dc->name.Length;
+
+        bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + namelen;
+
+        if (bytes_needed > *length)
+            overflow = TRUE;
+
+        if (!overflow) {
+            flefii = &flfii->Entry;
+
+            flefii->NextEntryOffset = 0;
+
+            if (fcb == fcb->Vcb->root_fileref->fcb) {
+                RtlZeroMemory(&flefii->ParentFileId.Identifier[0], sizeof(FILE_ID_128));
+                flefii->FileNameLength = 1;
+                flefii->FileName[0] = '.';
+            } else {
+                RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &fileref->parent->fcb->inode, sizeof(UINT64));
+                RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(UINT64)], &fileref->parent->fcb->subvol->id, sizeof(UINT64));
+
+                flefii->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
+                RtlCopyMemory(flefii->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
+            }
+
+            flfii->EntriesReturned++;
+
+            len = bytes_needed;
+        }
+    } else {
+        ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, TRUE);
+
+        if (IsListEmpty(&fcb->hardlinks)) {
+            bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + fileref->dc->name.Length;
+
+            if (bytes_needed > *length)
+                overflow = TRUE;
+
+            if (!overflow) {
+                flefii = &flfii->Entry;
+
+                flefii->NextEntryOffset = 0;
+
+                RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &fileref->parent->fcb->inode, sizeof(UINT64));
+                RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(UINT64)], &fileref->parent->fcb->subvol->id, sizeof(UINT64));
+
+                flefii->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR);
+                RtlCopyMemory(flefii->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length);
+
+                flfii->EntriesReturned++;
+
+                len = bytes_needed;
+            }
+        } else {
+            le = fcb->hardlinks.Flink;
+            while (le != &fcb->hardlinks) {
+                hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
+                file_ref* parfr;
+
+                TRACE("parent %llx, index %llx, name %.*S\n", hl->parent, hl->index, hl->name.Length / sizeof(WCHAR), hl->name.Buffer);
+
+                Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp);
+
+                if (!NT_SUCCESS(Status)) {
+                    ERR("open_fileref_by_inode returned %08x\n", Status);
+                } else if (!parfr->deleted) {
+                    LIST_ENTRY* le2;
+                    BOOL found = FALSE, deleted = FALSE;
+                    UNICODE_STRING* fn = NULL;
+
+                    le2 = parfr->children.Flink;
+                    while (le2 != &parfr->children) {
+                        file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
+
+                        if (fr2->dc->index == hl->index) {
+                            found = TRUE;
+                            deleted = fr2->deleted;
+
+                            if (!deleted)
+                                fn = &fr2->dc->name;
+
+                            break;
+                        }
+
+                        le2 = le2->Flink;
+                    }
+
+                    if (!found)
+                        fn = &hl->name;
+
+                    if (!deleted) {
+                        TRACE("fn = %.*S (found = %u)\n", fn->Length / sizeof(WCHAR), fn->Buffer, found);
+
+                        if (flefii)
+                            bytes_needed = (LONG)sector_align(bytes_needed, 8);
+
+                        bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + fn->Length;
+
+                        if (bytes_needed > *length)
+                            overflow = TRUE;
+
+                        if (!overflow) {
+                            if (flefii) {
+                                flefii->NextEntryOffset = (ULONG)sector_align(offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + (flefii->FileNameLength * sizeof(WCHAR)), 8);
+                                flefii = (FILE_LINK_ENTRY_FULL_ID_INFORMATION*)((UINT8*)flefii + flefii->NextEntryOffset);
+                            } else
+                                flefii = &flfii->Entry;
+
+                            flefii->NextEntryOffset = 0;
+
+                            RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &parfr->fcb->inode, sizeof(UINT64));
+                            RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(UINT64)], &parfr->fcb->subvol->id, sizeof(UINT64));
+
+                            flefii->FileNameLength = fn->Length / sizeof(WCHAR);
+                            RtlCopyMemory(flefii->FileName, fn->Buffer, fn->Length);
+
+                            flfii->EntriesReturned++;
+
+                            len = bytes_needed;
+                        }
+                    }
+
+                    free_fileref(parfr);
+                }
+
+                le = le->Flink;
+            }
+        }
+
+        ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
+    }
+
+    flfii->BytesNeeded = bytes_needed;
+
+    *length -= len;
+
+    Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
+
+    ExReleaseResourceLite(fcb->Header.Resource);
+
+    return Status;
+}
 
 static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION* fii, fcb* fcb, LONG* length) {
     RtlCopyMemory(&fii->VolumeSerialNumber, &fcb->Vcb->superblock.uuid.uuid[8], sizeof(UINT64));
@@ -3385,6 +3806,66 @@ static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION* fii, fcb* fcb,
 #endif
 
 #ifndef __REACTOS__
+static NTSTATUS fill_in_file_stat_information(FILE_STAT_INFORMATION* fsi, fcb* fcb, ccb* ccb, LONG* length) {
+    INODE_ITEM* ii;
+
+    fsi->FileId.LowPart = (UINT32)fcb->inode;
+    fsi->FileId.HighPart = (UINT32)fcb->subvol->id;
+
+    if (fcb->ads)
+        ii = &ccb->fileref->parent->fcb->inode_item;
+    else
+        ii = &fcb->inode_item;
+
+    if (fcb == fcb->Vcb->dummy_fcb) {
+        LARGE_INTEGER time;
+
+        KeQuerySystemTime(&time);
+        fsi->CreationTime = fsi->LastAccessTime = fsi->LastWriteTime = fsi->ChangeTime = time;
+    } else {
+        fsi->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
+        fsi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
+        fsi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
+        fsi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
+    }
+
+    if (fcb->ads) {
+        fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length;
+        fsi->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts;
+    } else {
+        fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb);
+        fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size;
+        fsi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts;
+    }
+
+    if (fcb->type == BTRFS_TYPE_SOCKET)
+        fsi->ReparseTag = IO_REPARSE_TAG_LXSS_SOCKET;
+    else if (fcb->type == BTRFS_TYPE_FIFO)
+        fsi->ReparseTag = IO_REPARSE_TAG_LXSS_FIFO;
+    else if (fcb->type == BTRFS_TYPE_CHARDEV)
+        fsi->ReparseTag = IO_REPARSE_TAG_LXSS_CHARDEV;
+    else if (fcb->type == BTRFS_TYPE_BLOCKDEV)
+        fsi->ReparseTag = IO_REPARSE_TAG_LXSS_BLOCKDEV;
+    else if (!(fsi->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+        fsi->ReparseTag = 0;
+    else
+        fsi->ReparseTag = get_reparse_tag_fcb(fcb);
+
+    if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV)
+        fsi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
+
+    if (fcb->ads)
+        fsi->NumberOfLinks = ccb->fileref->parent->fcb->inode_item.st_nlink;
+    else
+        fsi->NumberOfLinks = fcb->inode_item.st_nlink;
+
+    fsi->EffectiveAccess = ccb->access;
+
+    *length -= sizeof(FILE_STAT_INFORMATION);
+
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION* fsli, fcb* fcb, ccb* ccb, LONG* length) {
     INODE_ITEM* ii;
 
@@ -3439,7 +3920,11 @@ static NTSTATUS fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION* fsli,
         fsli->NumberOfLinks = fcb->inode_item.st_nlink;
 
     fsli->EffectiveAccess = ccb->access;
-    fsli->LxFlags = LX_FILE_METADATA_HAS_UID | LX_FILE_METADATA_HAS_GID | LX_FILE_METADATA_HAS_MODE | LX_FILE_METADATA_HAS_DEVICE_ID; // FIXME - LX_FILE_CASE_SENSITIVE_DIR
+    fsli->LxFlags = LX_FILE_METADATA_HAS_UID | LX_FILE_METADATA_HAS_GID | LX_FILE_METADATA_HAS_MODE | LX_FILE_METADATA_HAS_DEVICE_ID;
+
+    if (fcb->case_sensitive)
+        fsli->LxFlags |= LX_FILE_CASE_SENSITIVE_DIR;
+
     fsli->LxUid = ii->st_uid;
     fsli->LxGid = ii->st_gid;
     fsli->LxMode = ii->st_mode;
@@ -3456,6 +3941,14 @@ static NTSTATUS fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION* fsli,
 
     return STATUS_SUCCESS;
 }
+
+static NTSTATUS fill_in_file_case_sensitive_information(FILE_CASE_SENSITIVE_INFORMATION* fcsi, fcb* fcb, LONG* length) {
+    fcsi->Flags = fcb->case_sensitive ? FILE_CS_FLAG_CASE_SENSITIVE_DIR : 0;
+
+    *length -= sizeof(FILE_CASE_SENSITIVE_INFORMATION);
+
+    return STATUS_SUCCESS;
+}
 #endif
 
 static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
@@ -3746,6 +4239,23 @@ static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP
             break;
         }
 
+        case FileStatInformation:
+        {
+            FILE_STAT_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer;
+
+            if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_LX_INFORMATION)) {
+                WARN("overflow\n");
+                Status = STATUS_BUFFER_OVERFLOW;
+                goto exit;
+            }
+
+            TRACE("FileStatInformation\n");
+
+            Status = fill_in_file_stat_information(fsi, fcb, ccb, &length);
+
+            break;
+        }
+
         case FileStatLxInformation:
         {
             FILE_STAT_LX_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
@@ -3762,6 +4272,36 @@ static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP
 
             break;
         }
+
+        case FileCaseSensitiveInformation:
+        {
+            FILE_CASE_SENSITIVE_INFORMATION* fcsi = Irp->AssociatedIrp.SystemBuffer;
+
+            if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_CASE_SENSITIVE_INFORMATION)) {
+                WARN("overflow\n");
+                Status = STATUS_BUFFER_OVERFLOW;
+                goto exit;
+            }
+
+            TRACE("FileCaseSensitiveInformation\n");
+
+            Status = fill_in_file_case_sensitive_information(fcsi, fcb, &length);
+
+            break;
+        }
+
+        case FileHardLinkFullIdInformation:
+        {
+            FILE_LINKS_FULL_ID_INFORMATION* flfii = Irp->AssociatedIrp.SystemBuffer;
+
+            TRACE("FileHardLinkFullIdInformation\n");
+
+            ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
+            Status = fill_in_hard_link_full_id_information(flfii, fileref, Irp, &length);
+            ExReleaseResourceLite(&Vcb->tree_lock);
+
+            break;
+        }
 #ifndef _MSC_VER
 #pragma GCC diagnostic pop
 #endif
index 6784de5..5801ff3 100644 (file)
@@ -515,6 +515,9 @@ static NTSTATUS add_parents(device_extension* Vcb, PIRP Irp) {
                     KEY searchkey;
                     traverse_ptr tp;
                     NTSTATUS Status;
+#ifdef __REACTOS__
+                    tree* t2;
+#endif
 
                     searchkey.obj_id = t->root->id;
                     searchkey.obj_type = TYPE_ROOT_ITEM;
@@ -555,6 +558,17 @@ static NTSTATUS add_parents(device_extension* Vcb, PIRP Irp) {
                             return Status;
                         }
                     }
+
+#ifndef __REACTOS__
+                    tree* t2 = tp.tree;
+#else
+                    t2 = tp.tree;
+#endif
+                    while (t2) {
+                        t2->write = TRUE;
+
+                        t2 = t2->parent;
+                    }
                 }
             }
 
@@ -2713,6 +2727,9 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp, LIST_ENTRY*
         }
 
         if (c->used != c->oldused) {
+#ifdef __REACTOS__
+            UINT64 old_phys_used, phys_used;
+#endif
             searchkey.obj_id = c->offset;
             searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
             searchkey.offset = c->chunk_item->size;
@@ -2767,11 +2784,18 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp, LIST_ENTRY*
                 goto end;
             }
 
-            TRACE("bytes_used = %llx\n", Vcb->superblock.bytes_used);
-
-            Vcb->superblock.bytes_used += c->used - c->oldused;
+#ifndef __REACTOS__
+            UINT64 old_phys_used = chunk_estimate_phys_size(Vcb, c, c->oldused);
+            UINT64 phys_used = chunk_estimate_phys_size(Vcb, c, c->used);
+#else
+            old_phys_used = chunk_estimate_phys_size(Vcb, c, c->oldused);
+            phys_used = chunk_estimate_phys_size(Vcb, c, c->used);
+#endif
 
-            TRACE("bytes_used = %llx\n", Vcb->superblock.bytes_used);
+            if (Vcb->superblock.bytes_used + phys_used > old_phys_used)
+                Vcb->superblock.bytes_used += phys_used - old_phys_used;
+            else
+                Vcb->superblock.bytes_used = 0;
 
             c->oldused = c->used;
         }
@@ -4118,6 +4142,8 @@ static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, PIRP Irp) {
     c->created = FALSE;
     c->oldused = c->used;
 
+    Vcb->superblock.bytes_used += chunk_estimate_phys_size(Vcb, c, c->used);
+
     return STATUS_SUCCESS;
 }
 
@@ -4652,6 +4678,15 @@ NTSTATUS flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* batchlist, PIRP Irp) {
             }
         }
 
+        if (fcb->marked_as_orphan) {
+            Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, BTRFS_ORPHAN_INODE_OBJID, TYPE_ORPHAN_INODE,
+                                            fcb->inode, NULL, 0, Batch_Delete);
+            if (!NT_SUCCESS(Status)) {
+                ERR("insert_tree_item_batch returned %08x\n", Status);
+                goto end;
+            }
+        }
+
         Status = STATUS_SUCCESS;
         goto end;
     }
@@ -5146,6 +5181,37 @@ NTSTATUS flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* batchlist, PIRP Irp) {
         fcb->xattrs_changed = FALSE;
     }
 
+    if ((fcb->case_sensitive_set && !fcb->case_sensitive)) {
+        Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_CASE_SENSITIVE,
+                              sizeof(EA_CASE_SENSITIVE) - 1, EA_CASE_SENSITIVE_HASH);
+        if (!NT_SUCCESS(Status)) {
+            ERR("delete_xattr returned %08x\n", Status);
+            goto end;
+        }
+
+        fcb->case_sensitive_set = FALSE;
+    } else if ((!fcb->case_sensitive_set && fcb->case_sensitive)) {
+        Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_CASE_SENSITIVE,
+                           sizeof(EA_CASE_SENSITIVE) - 1, EA_CASE_SENSITIVE_HASH, (UINT8*)"1", 1);
+        if (!NT_SUCCESS(Status)) {
+            ERR("set_xattr returned %08x\n", Status);
+            goto end;
+        }
+
+        fcb->case_sensitive_set = TRUE;
+    }
+
+    if (fcb->inode_item.st_nlink == 0 && !fcb->marked_as_orphan) { // mark as orphan
+        Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, BTRFS_ORPHAN_INODE_OBJID, TYPE_ORPHAN_INODE,
+                                        fcb->inode, NULL, 0, Batch_Insert);
+        if (!NT_SUCCESS(Status)) {
+            ERR("insert_tree_item_batch returned %08x\n", Status);
+            goto end;
+        }
+
+        fcb->marked_as_orphan = TRUE;
+    }
+
     Status = STATUS_SUCCESS;
 
 end:
@@ -5197,6 +5263,9 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* batchlis
     KEY searchkey;
     traverse_ptr tp;
     UINT64 i, factor;
+#ifdef __REACTOS__
+    UINT64 phys_used;
+#endif
     CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];;
 
     TRACE("dropping chunk %llx\n", c->offset);
@@ -5441,7 +5510,16 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* batchlis
             Vcb->superblock.incompat_flags &= ~BTRFS_INCOMPAT_FLAGS_RAID56;
     }
 
-    Vcb->superblock.bytes_used -= c->oldused;
+#ifndef __REACTOS__
+    UINT64 phys_used = chunk_estimate_phys_size(Vcb, c, c->oldused);
+#else
+    phys_used = chunk_estimate_phys_size(Vcb, c, c->oldused);
+#endif
+
+    if (phys_used < Vcb->superblock.bytes_used)
+        Vcb->superblock.bytes_used -= phys_used;
+    else
+        Vcb->superblock.bytes_used = 0;
 
     ExFreePool(c->chunk_item);
     ExFreePool(c->devices);
@@ -6943,6 +7021,108 @@ static NTSTATUS test_not_full(device_extension* Vcb) {
     return STATUS_DISK_FULL;
 }
 
+static NTSTATUS check_for_orphans_root(device_extension* Vcb, root* r, PIRP Irp) {
+    NTSTATUS Status;
+    KEY searchkey;
+    traverse_ptr tp;
+    LIST_ENTRY rollback;
+
+    TRACE("(%p, %p)\n", Vcb, r);
+
+    InitializeListHead(&rollback);
+
+    searchkey.obj_id = BTRFS_ORPHAN_INODE_OBJID;
+    searchkey.obj_type = TYPE_ORPHAN_INODE;
+    searchkey.offset = 0;
+
+    Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("find_item returned %08x\n", Status);
+        return Status;
+    }
+
+    do {
+        traverse_ptr 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;
+
+        if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
+            fcb* fcb;
+
+            TRACE("removing orphaned inode %llx\n", tp.item->key.offset);
+
+            Status = open_fcb(Vcb, r, tp.item->key.offset, 0, NULL, FALSE, NULL, &fcb, PagedPool, Irp);
+            if (!NT_SUCCESS(Status))
+                ERR("open_fcb returned %08x\n", Status);
+            else {
+                if (fcb->inode_item.st_nlink == 0) {
+                    if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) {
+                        Status = excise_extents(Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size), Irp, &rollback);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("excise_extents returned %08x\n", Status);
+                            goto end;
+                        }
+                    }
+
+                    fcb->deleted = TRUE;
+
+                    mark_fcb_dirty(fcb);
+                }
+
+                free_fcb(fcb);
+
+                Status = delete_tree_item(Vcb, &tp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_tree_item returned %08x\n", Status);
+                    goto end;
+                }
+            }
+        }
+
+        if (find_next_item(Vcb, &tp, &next_tp, FALSE, Irp))
+            tp = next_tp;
+        else
+            break;
+    } while (TRUE);
+
+    Status = STATUS_SUCCESS;
+
+    clear_rollback(&rollback);
+
+end:
+    do_rollback(Vcb, &rollback);
+
+    return Status;
+}
+
+static NTSTATUS check_for_orphans(device_extension* Vcb, PIRP Irp) {
+    NTSTATUS Status;
+    LIST_ENTRY* le;
+
+    if (IsListEmpty(&Vcb->dirty_filerefs))
+        return STATUS_SUCCESS;
+
+    le = Vcb->dirty_filerefs.Flink;
+    while (le != &Vcb->dirty_filerefs) {
+        file_ref* fr = CONTAINING_RECORD(le, file_ref, list_entry_dirty);
+
+        if (!fr->fcb->subvol->checked_for_orphans) {
+            Status = check_for_orphans_root(Vcb, fr->fcb->subvol, Irp);
+            if (!NT_SUCCESS(Status)) {
+                ERR("check_for_orphans_root returned %08x\n", Status);
+                return Status;
+            }
+
+            fr->fcb->subvol->checked_for_orphans = TRUE;
+        }
+
+        le = le->Flink;
+    }
+
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS do_write2(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     LIST_ENTRY *le, batchlist;
@@ -6965,6 +7145,12 @@ static NTSTATUS do_write2(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback)
     time1 = KeQueryPerformanceCounter(&freq);
 #endif
 
+    Status = check_for_orphans(Vcb, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("check_for_orphans returned %08x\n", Status);
+        return Status;
+    }
+
     ExAcquireResourceExclusiveLite(&Vcb->dirty_filerefs_lock, TRUE);
 
     while (!IsListEmpty(&Vcb->dirty_filerefs)) {
index cd3de36..e74a4a5 100644 (file)
@@ -25,7 +25,7 @@ static NTSTATUS remove_free_space_inode(device_extension* Vcb, UINT64 inode, LIS
     NTSTATUS Status;
     fcb* fcb;
 
-    Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &fcb, PagedPool, Irp);
+    Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, FALSE, NULL, &fcb, PagedPool, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
         return Status;
@@ -497,7 +497,7 @@ NTSTATUS load_stored_free_space_cache(device_extension* Vcb, chunk* c, BOOL load
     num_entries = fsi->num_entries;
     num_bitmaps = fsi->num_bitmaps;
 
-    Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, NULL, &c->cache, PagedPool, Irp);
+    Status = open_fcb(Vcb, Vcb->root_root, inode, BTRFS_TYPE_FILE, NULL, FALSE, NULL, &c->cache, PagedPool, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
         return STATUS_NOT_FOUND;
index 3845965..9062250 100644 (file)
@@ -432,7 +432,7 @@ static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, f
         goto end;
     }
 
-    Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, fcb, &fr->fcb, PagedPool, Irp);
+    Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, FALSE, fcb, &fr->fcb, PagedPool, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("open_fcb returned %08x\n", Status);
         free_fileref(fr);
@@ -4342,6 +4342,14 @@ static NTSTATUS fsctl_set_xattr(device_extension* Vcb, PFILE_OBJECT FileObject,
         fcb->ea_changed = TRUE;
         mark_fcb_dirty(fcb);
 
+        Status = STATUS_SUCCESS;
+        goto end;
+    } else if (bsxa->namelen == sizeof(EA_CASE_SENSITIVE) - 1 && RtlCompareMemory(bsxa->data, EA_CASE_SENSITIVE, sizeof(EA_CASE_SENSITIVE) - 1) == sizeof(EA_CASE_SENSITIVE) - 1) {
+        if (bsxa->valuelen > 0 && bsxa->data[bsxa->namelen] == '1') {
+            fcb->case_sensitive = TRUE;
+            mark_fcb_dirty(fcb);
+        }
+
         Status = STATUS_SUCCESS;
         goto end;
     } else if (bsxa->namelen == sizeof(EA_PROP_COMPRESSION) - 1 && RtlCompareMemory(bsxa->data, EA_PROP_COMPRESSION, sizeof(EA_PROP_COMPRESSION) - 1) == sizeof(EA_PROP_COMPRESSION) - 1) {
index ed1df4c..a38ea7f 100644 (file)
@@ -4498,7 +4498,7 @@ NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void
             ExFreePool(data);
         } else {
             if (write_irp && Irp->MdlAddress && no_buf) {
-                BOOL locked = Irp->MdlAddress->MdlFlags & MDL_PAGES_LOCKED;
+               BOOL locked = Irp->MdlAddress->MdlFlags & (MDL_PAGES_LOCKED | MDL_PARTIAL);
 
                 if (!locked) {
                     Status = STATUS_SUCCESS;
index f70f2a5..d86c5bd 100644 (file)
@@ -3,9 +3,9 @@
 
 The following FSD are shared with: https://github.com/maharmstone/btrfs.
 
-reactos/drivers/filesystems/btrfs           # Synced to 1.2.1
+reactos/drivers/filesystems/btrfs           # Synced to 1.3
 reactos/dll/shellext/shellbtrfs             # Synced to 1.1
-reactos/sdk/lib/fslib/btrfslib              # Synced to 1.2.1
+reactos/sdk/lib/fslib/btrfslib              # Synced to 1.3
 
 The following FSD are shared with: http://www.ext2fsd.com/