1 /* Copyright (c) Mark Harmstone 2016-17
3 * This file is part of WinBtrfs.
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
20 #endif /* __REACTOS__ */
21 #include "btrfs_drv.h"
24 extern PDEVICE_OBJECT master_devobj
;
26 static const WCHAR datastring
[] = L
"::$DATA";
29 #define ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED 0x0002
30 #define ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT 0x0100
31 #define ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET 0x0002
33 typedef struct _ATOMIC_CREATE_ECP_CONTEXT
{
37 USHORT ReparseBufferLength
;
38 PREPARSE_DATA_BUFFER ReparseBuffer
;
40 LONGLONG ValidDataLength
;
41 } ATOMIC_CREATE_ECP_CONTEXT
, *PATOMIC_CREATE_ECP_CONTEXT
;
43 static const GUID GUID_ECP_ATOMIC_CREATE
= { 0x4720bd83, 0x52ac, 0x4104, { 0xa1, 0x30, 0xd1, 0xec, 0x6a, 0x8c, 0xc8, 0xe5 } };
45 fcb
* create_fcb(device_extension
* Vcb
, POOL_TYPE pool_type
) {
48 if (pool_type
== NonPagedPool
) {
49 fcb
= ExAllocatePoolWithTag(pool_type
, sizeof(struct _fcb
), ALLOC_TAG
);
51 ERR("out of memory\n");
55 fcb
= ExAllocateFromPagedLookasideList(&Vcb
->fcb_lookaside
);
57 ERR("out of memory\n");
62 #ifdef DEBUG_FCB_REFCOUNTS
63 WARN("allocating fcb %p\n", fcb
);
65 RtlZeroMemory(fcb
, sizeof(struct _fcb
));
66 fcb
->pool_type
= pool_type
;
68 fcb
->Header
.NodeTypeCode
= BTRFS_NODE_TYPE_FCB
;
69 fcb
->Header
.NodeByteSize
= sizeof(struct _fcb
);
71 fcb
->nonpaged
= ExAllocateFromNPagedLookasideList(&Vcb
->fcb_np_lookaside
);
73 ERR("out of memory\n");
75 if (pool_type
== NonPagedPool
)
78 ExFreeToPagedLookasideList(&Vcb
->fcb_lookaside
, fcb
);
82 RtlZeroMemory(fcb
->nonpaged
, sizeof(struct _fcb_nonpaged
));
84 ExInitializeResourceLite(&fcb
->nonpaged
->paging_resource
);
85 fcb
->Header
.PagingIoResource
= &fcb
->nonpaged
->paging_resource
;
87 ExInitializeFastMutex(&fcb
->nonpaged
->HeaderMutex
);
88 FsRtlSetupAdvancedHeader(&fcb
->Header
, &fcb
->nonpaged
->HeaderMutex
);
91 #ifdef DEBUG_FCB_REFCOUNTS
92 WARN("fcb %p: refcount now %i\n", fcb
, fcb
->refcount
);
95 ExInitializeResourceLite(&fcb
->nonpaged
->resource
);
96 fcb
->Header
.Resource
= &fcb
->nonpaged
->resource
;
98 ExInitializeResourceLite(&fcb
->nonpaged
->dir_children_lock
);
100 FsRtlInitializeFileLock(&fcb
->lock
, NULL
, NULL
);
102 InitializeListHead(&fcb
->extents
);
103 InitializeListHead(&fcb
->hardlinks
);
104 InitializeListHead(&fcb
->xattrs
);
106 InitializeListHead(&fcb
->dir_children_index
);
107 InitializeListHead(&fcb
->dir_children_hash
);
108 InitializeListHead(&fcb
->dir_children_hash_uc
);
113 file_ref
* create_fileref(device_extension
* Vcb
) {
116 fr
= ExAllocateFromPagedLookasideList(&Vcb
->fileref_lookaside
);
118 ERR("out of memory\n");
122 RtlZeroMemory(fr
, sizeof(file_ref
));
124 fr
->nonpaged
= ExAllocateFromNPagedLookasideList(&Vcb
->fileref_np_lookaside
);
126 ERR("out of memory\n");
127 ExFreeToPagedLookasideList(&Vcb
->fileref_lookaside
, fr
);
133 #ifdef DEBUG_FCB_REFCOUNTS
134 WARN("fileref %p: refcount now 1\n", fr
);
137 InitializeListHead(&fr
->children
);
139 ExInitializeResourceLite(&fr
->nonpaged
->fileref_lock
);
144 NTSTATUS
find_file_in_dir(PUNICODE_STRING filename
, fcb
* fcb
, root
** subvol
, UINT64
* inode
, dir_child
** pdc
, BOOL case_sensitive
) {
152 if (!case_sensitive
) {
153 Status
= RtlUpcaseUnicodeString(&fnus
, filename
, TRUE
);
155 if (!NT_SUCCESS(Status
)) {
156 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
162 hash
= calc_crc32c(0xffffffff, (UINT8
*)fnus
.Buffer
, fnus
.Length
);
166 if (!ExIsResourceAcquiredSharedLite(&fcb
->nonpaged
->dir_children_lock
)) {
167 ExAcquireResourceSharedLite(&fcb
->nonpaged
->dir_children_lock
, TRUE
);
171 if (case_sensitive
) {
172 if (!fcb
->hash_ptrs
[c
]) {
173 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
177 le
= fcb
->hash_ptrs
[c
];
178 while (le
!= &fcb
->dir_children_hash
) {
179 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash
);
181 if (dc
->hash
== hash
) {
182 if (dc
->name
.Length
== fnus
.Length
&& RtlCompareMemory(dc
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
) == fnus
.Length
) {
183 if (dc
->key
.obj_type
== TYPE_ROOT_ITEM
) {
188 le2
= fcb
->Vcb
->roots
.Flink
;
189 while (le2
!= &fcb
->Vcb
->roots
) {
190 root
* r2
= CONTAINING_RECORD(le2
, root
, list_entry
);
192 if (r2
->id
== dc
->key
.obj_id
) {
200 *inode
= SUBVOL_ROOT_INODE
;
202 *subvol
= fcb
->subvol
;
203 *inode
= dc
->key
.obj_id
;
208 Status
= STATUS_SUCCESS
;
211 } else if (dc
->hash
> hash
) {
212 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
219 if (!fcb
->hash_ptrs_uc
[c
]) {
220 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
224 le
= fcb
->hash_ptrs_uc
[c
];
225 while (le
!= &fcb
->dir_children_hash_uc
) {
226 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash_uc
);
228 if (dc
->hash_uc
== hash
) {
229 if (dc
->name_uc
.Length
== fnus
.Length
&& RtlCompareMemory(dc
->name_uc
.Buffer
, fnus
.Buffer
, fnus
.Length
) == fnus
.Length
) {
230 if (dc
->key
.obj_type
== TYPE_ROOT_ITEM
) {
235 le2
= fcb
->Vcb
->roots
.Flink
;
236 while (le2
!= &fcb
->Vcb
->roots
) {
237 root
* r2
= CONTAINING_RECORD(le2
, root
, list_entry
);
239 if (r2
->id
== dc
->key
.obj_id
) {
247 *inode
= SUBVOL_ROOT_INODE
;
249 *subvol
= fcb
->subvol
;
250 *inode
= dc
->key
.obj_id
;
255 Status
= STATUS_SUCCESS
;
258 } else if (dc
->hash_uc
> hash
) {
259 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
267 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
271 ExReleaseResourceLite(&fcb
->nonpaged
->dir_children_lock
);
274 ExFreePool(fnus
.Buffer
);
279 static NTSTATUS
split_path(device_extension
* Vcb
, PUNICODE_STRING path
, LIST_ENTRY
* parts
, BOOL
* stream
) {
286 len
= path
->Length
/ sizeof(WCHAR
);
287 if (len
> 0 && (path
->Buffer
[len
- 1] == '/' || path
->Buffer
[len
- 1] == '\\'))
290 if (len
== 0 || (path
->Buffer
[len
- 1] == '/' || path
->Buffer
[len
- 1] == '\\')) {
291 WARN("zero-length filename part\n");
292 return STATUS_OBJECT_NAME_INVALID
;
296 for (i
= 0; i
< len
; i
++) {
297 if (path
->Buffer
[i
] == '/' || path
->Buffer
[i
] == '\\') {
299 } else if (path
->Buffer
[i
] == ':') {
306 for (i
= 0; i
< len
; i
++) {
307 if (path
->Buffer
[i
] == '/' || path
->Buffer
[i
] == '\\') {
308 if (buf
[0] == '/' || buf
[0] == '\\') {
309 WARN("zero-length filename part\n");
310 Status
= STATUS_OBJECT_NAME_INVALID
;
314 nb
= ExAllocateFromPagedLookasideList(&Vcb
->name_bit_lookaside
);
316 ERR("out of memory\n");
317 Status
= STATUS_INSUFFICIENT_RESOURCES
;
322 nb
->us
.Length
= nb
->us
.MaximumLength
= (USHORT
)(&path
->Buffer
[i
] - buf
) * sizeof(WCHAR
);
323 InsertTailList(parts
, &nb
->list_entry
);
325 buf
= &path
->Buffer
[i
+1];
329 nb
= ExAllocateFromPagedLookasideList(&Vcb
->name_bit_lookaside
);
331 ERR("out of memory\n");
332 Status
= STATUS_INSUFFICIENT_RESOURCES
;
337 nb
->us
.Length
= nb
->us
.MaximumLength
= (USHORT
)(&path
->Buffer
[i
] - buf
) * sizeof(WCHAR
);
338 InsertTailList(parts
, &nb
->list_entry
);
341 static const WCHAR datasuf
[] = {':','$','D','A','T','A',0};
344 dsus
.Buffer
= (WCHAR
*)datasuf
;
345 dsus
.Length
= dsus
.MaximumLength
= sizeof(datasuf
) - sizeof(WCHAR
);
347 for (i
= 0; i
< nb
->us
.Length
/ sizeof(WCHAR
); i
++) {
348 if (nb
->us
.Buffer
[i
] == ':') {
351 if (nb
->us
.Buffer
[i
+1] == 0) {
352 WARN("zero-length stream name\n");
353 Status
= STATUS_OBJECT_NAME_INVALID
;
357 nb2
= ExAllocateFromPagedLookasideList(&Vcb
->name_bit_lookaside
);
359 ERR("out of memory\n");
360 Status
= STATUS_INSUFFICIENT_RESOURCES
;
364 nb2
->us
.Buffer
= &nb
->us
.Buffer
[i
+1];
365 nb2
->us
.Length
= nb2
->us
.MaximumLength
= (UINT16
)(nb
->us
.Length
- (i
* sizeof(WCHAR
)) - sizeof(WCHAR
));
366 InsertTailList(parts
, &nb2
->list_entry
);
368 nb
->us
.Length
= (UINT16
)i
* sizeof(WCHAR
);
369 nb
->us
.MaximumLength
= nb
->us
.Length
;
377 // FIXME - should comparison be case-insensitive?
378 // remove :$DATA suffix
379 if (nb
->us
.Length
>= dsus
.Length
&& RtlCompareMemory(&nb
->us
.Buffer
[(nb
->us
.Length
- dsus
.Length
)/sizeof(WCHAR
)], dsus
.Buffer
, dsus
.Length
) == dsus
.Length
)
380 nb
->us
.Length
-= dsus
.Length
;
382 if (nb
->us
.Length
== 0) {
383 RemoveTailList(parts
);
384 ExFreeToPagedLookasideList(&Vcb
->name_bit_lookaside
, nb
);
390 // if path is just stream name, remove first empty item
391 if (has_stream
&& path
->Length
>= sizeof(WCHAR
) && path
->Buffer
[0] == ':') {
392 name_bit
*nb1
= CONTAINING_RECORD(RemoveHeadList(parts
), name_bit
, list_entry
);
394 ExFreeToPagedLookasideList(&Vcb
->name_bit_lookaside
, nb1
);
397 *stream
= has_stream
;
399 return STATUS_SUCCESS
;
402 while (!IsListEmpty(parts
)) {
403 nb
= CONTAINING_RECORD(RemoveHeadList(parts
), name_bit
, list_entry
);
405 ExFreeToPagedLookasideList(&Vcb
->name_bit_lookaside
, nb
);
411 NTSTATUS
load_csum(_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, UINT32
* csum
, UINT64 start
, UINT64 length
, PIRP Irp
) {
414 traverse_ptr tp
, next_tp
;
418 searchkey
.obj_id
= EXTENT_CSUM_ID
;
419 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
420 searchkey
.offset
= start
;
422 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
, Irp
);
423 if (!NT_SUCCESS(Status
)) {
424 ERR("error - find_item returned %08x\n", Status
);
430 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
433 if (start
< tp
.item
->key
.offset
)
436 j
= ((start
- tp
.item
->key
.offset
) / Vcb
->superblock
.sector_size
) + i
;
438 if (j
* sizeof(UINT32
) > tp
.item
->size
|| tp
.item
->key
.offset
> start
+ (i
* Vcb
->superblock
.sector_size
)) {
439 ERR("checksum not found for %llx\n", start
+ (i
* Vcb
->superblock
.sector_size
));
440 return STATUS_INTERNAL_ERROR
;
443 readlen
= (ULONG
)min((tp
.item
->size
/ sizeof(UINT32
)) - j
, length
- i
);
444 RtlCopyMemory(&csum
[i
], tp
.item
->data
+ (j
* sizeof(UINT32
)), readlen
* sizeof(UINT32
));
451 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
458 ERR("could not read checksums: offset %llx, length %llx sectors\n", start
, length
);
459 return STATUS_INTERNAL_ERROR
;
462 return STATUS_SUCCESS
;
465 NTSTATUS
load_dir_children(_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, fcb
* fcb
, BOOL ignore_size
, PIRP Irp
) {
467 traverse_ptr tp
, next_tp
;
469 ULONG num_children
= 0;
471 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
472 if (!fcb
->hash_ptrs
) {
473 ERR("out of memory\n");
474 return STATUS_INSUFFICIENT_RESOURCES
;
477 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
479 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
480 if (!fcb
->hash_ptrs_uc
) {
481 ERR("out of memory\n");
482 return STATUS_INSUFFICIENT_RESOURCES
;
485 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
487 if (!ignore_size
&& fcb
->inode_item
.st_size
== 0)
488 return STATUS_SUCCESS
;
490 searchkey
.obj_id
= fcb
->inode
;
491 searchkey
.obj_type
= TYPE_DIR_INDEX
;
492 searchkey
.offset
= 2;
494 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
495 if (!NT_SUCCESS(Status
)) {
496 ERR("find_item returned %08x\n", Status
);
500 if (keycmp(tp
.item
->key
, searchkey
) == -1) {
501 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
)) {
503 TRACE("moving on to %llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
507 while (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
508 DIR_ITEM
* di
= (DIR_ITEM
*)tp
.item
->data
;
512 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
513 WARN("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DIR_ITEM
));
518 WARN("(%llx,%x,%llx): DIR_ITEM name length is zero\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
522 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &utf16len
, di
->name
, di
->n
);
523 if (!NT_SUCCESS(Status
)) {
524 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
528 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
530 ERR("out of memory\n");
531 return STATUS_INSUFFICIENT_RESOURCES
;
535 dc
->index
= tp
.item
->key
.offset
;
539 dc
->utf8
.MaximumLength
= dc
->utf8
.Length
= di
->n
;
540 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, di
->n
, ALLOC_TAG
);
541 if (!dc
->utf8
.Buffer
) {
542 ERR("out of memory\n");
544 return STATUS_INSUFFICIENT_RESOURCES
;
547 RtlCopyMemory(dc
->utf8
.Buffer
, di
->name
, di
->n
);
549 dc
->name
.MaximumLength
= dc
->name
.Length
= (UINT16
)utf16len
;
550 dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dc
->name
.MaximumLength
, ALLOC_TAG
);
551 if (!dc
->name
.Buffer
) {
552 ERR("out of memory\n");
553 ExFreePool(dc
->utf8
.Buffer
);
555 return STATUS_INSUFFICIENT_RESOURCES
;
558 Status
= RtlUTF8ToUnicodeN(dc
->name
.Buffer
, utf16len
, &utf16len
, di
->name
, di
->n
);
559 if (!NT_SUCCESS(Status
)) {
560 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
561 ExFreePool(dc
->utf8
.Buffer
);
562 ExFreePool(dc
->name
.Buffer
);
567 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, &dc
->name
, TRUE
);
568 if (!NT_SUCCESS(Status
)) {
569 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
570 ExFreePool(dc
->utf8
.Buffer
);
571 ExFreePool(dc
->name
.Buffer
);
576 dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name
.Buffer
, dc
->name
.Length
);
577 dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name_uc
.Buffer
, dc
->name_uc
.Length
);
579 InsertTailList(&fcb
->dir_children_index
, &dc
->list_entry_index
);
581 insert_dir_child_into_hash_lists(fcb
, dc
);
586 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
))
592 return STATUS_SUCCESS
;
595 NTSTATUS
open_fcb(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
596 root
* subvol
, UINT64 inode
, UINT8 type
, PANSI_STRING utf8
, BOOL always_add_hl
, fcb
* parent
, fcb
** pfcb
, POOL_TYPE pooltype
, PIRP Irp
) {
598 traverse_ptr tp
, next_tp
;
600 fcb
*fcb
, *deleted_fcb
= NULL
;
601 BOOL atts_set
= FALSE
, sd_set
= FALSE
, no_data
;
602 LIST_ENTRY
* lastle
= NULL
;
603 EXTENT_DATA
* ed
= NULL
;
607 hash
= calc_crc32c(0xffffffff, (UINT8
*)&inode
, sizeof(UINT64
));
609 acquire_fcb_lock_shared(Vcb
);
611 if (subvol
->fcbs_ptrs
[hash
>> 24]) {
612 LIST_ENTRY
* le
= subvol
->fcbs_ptrs
[hash
>> 24];
614 while (le
!= &subvol
->fcbs
) {
615 fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
617 if (fcb
->inode
== inode
) {
622 #ifdef DEBUG_FCB_REFCOUNTS
623 LONG rc
= InterlockedIncrement(&fcb
->refcount
);
625 WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb
, rc
, fcb
->subvol
->id
, fcb
->inode
);
627 InterlockedIncrement(&fcb
->refcount
);
631 release_fcb_lock(Vcb
);
632 return STATUS_SUCCESS
;
635 } else if (fcb
->hash
> hash
) {
637 InterlockedIncrement(&deleted_fcb
->refcount
);
639 release_fcb_lock(Vcb
);
640 return STATUS_SUCCESS
;
644 fcbs_version
= subvol
->fcbs_version
;
653 release_fcb_lock(Vcb
);
656 InterlockedIncrement(&deleted_fcb
->refcount
);
658 return STATUS_SUCCESS
;
661 fcb
= create_fcb(Vcb
, pooltype
);
663 ERR("out of memory\n");
664 return STATUS_INSUFFICIENT_RESOURCES
;
669 fcb
->subvol
= subvol
;
674 searchkey
.obj_id
= inode
;
675 searchkey
.obj_type
= TYPE_INODE_ITEM
;
676 searchkey
.offset
= 0xffffffffffffffff;
678 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
679 if (!NT_SUCCESS(Status
)) {
680 ERR("error - find_item returned %08x\n", Status
);
685 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
686 WARN("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", inode
, subvol
->id
);
688 return STATUS_INVALID_PARAMETER
;
691 if (tp
.item
->size
> 0)
692 RtlCopyMemory(&fcb
->inode_item
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
694 if (fcb
->type
== 0) { // guess the type from the inode mode, if the caller doesn't know already
695 if ((fcb
->inode_item
.st_mode
& __S_IFDIR
) == __S_IFDIR
)
696 fcb
->type
= BTRFS_TYPE_DIRECTORY
;
697 else if ((fcb
->inode_item
.st_mode
& __S_IFCHR
) == __S_IFCHR
)
698 fcb
->type
= BTRFS_TYPE_CHARDEV
;
699 else if ((fcb
->inode_item
.st_mode
& __S_IFBLK
) == __S_IFBLK
)
700 fcb
->type
= BTRFS_TYPE_BLOCKDEV
;
701 else if ((fcb
->inode_item
.st_mode
& __S_IFIFO
) == __S_IFIFO
)
702 fcb
->type
= BTRFS_TYPE_FIFO
;
703 else if ((fcb
->inode_item
.st_mode
& __S_IFLNK
) == __S_IFLNK
)
704 fcb
->type
= BTRFS_TYPE_SYMLINK
;
705 else if ((fcb
->inode_item
.st_mode
& __S_IFSOCK
) == __S_IFSOCK
)
706 fcb
->type
= BTRFS_TYPE_SOCKET
;
708 fcb
->type
= BTRFS_TYPE_FILE
;
711 no_data
= fcb
->inode_item
.st_size
== 0 || (fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
);
713 while (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
)) {
716 if (tp
.item
->key
.obj_id
> inode
)
719 if ((no_data
&& tp
.item
->key
.obj_type
> TYPE_XATTR_ITEM
) || tp
.item
->key
.obj_type
> TYPE_EXTENT_DATA
)
722 if ((always_add_hl
|| fcb
->inode_item
.st_nlink
> 1) && tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
727 ir
= (INODE_REF
*)tp
.item
->data
;
729 while (len
>= sizeof(INODE_REF
) - 1) {
733 hl
= ExAllocatePoolWithTag(pooltype
, sizeof(hardlink
), ALLOC_TAG
);
735 ERR("out of memory\n");
737 return STATUS_INSUFFICIENT_RESOURCES
;
740 hl
->parent
= tp
.item
->key
.offset
;
741 hl
->index
= ir
->index
;
743 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= ir
->n
;
745 if (hl
->utf8
.Length
> 0) {
746 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(pooltype
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
747 RtlCopyMemory(hl
->utf8
.Buffer
, ir
->name
, ir
->n
);
750 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ir
->name
, ir
->n
);
751 if (!NT_SUCCESS(Status
)) {
752 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
758 hl
->name
.Length
= hl
->name
.MaximumLength
= (UINT16
)stringlen
;
761 hl
->name
.Buffer
= NULL
;
763 hl
->name
.Buffer
= ExAllocatePoolWithTag(pooltype
, hl
->name
.MaximumLength
, ALLOC_TAG
);
765 if (!hl
->name
.Buffer
) {
766 ERR("out of memory\n");
769 return STATUS_INSUFFICIENT_RESOURCES
;
772 Status
= RtlUTF8ToUnicodeN(hl
->name
.Buffer
, stringlen
, &stringlen
, ir
->name
, ir
->n
);
773 if (!NT_SUCCESS(Status
)) {
774 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
775 ExFreePool(hl
->name
.Buffer
);
782 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
784 len
-= sizeof(INODE_REF
) - 1 + ir
->n
;
785 ir
= (INODE_REF
*)&ir
->name
[ir
->n
];
787 } else if ((always_add_hl
|| fcb
->inode_item
.st_nlink
> 1) && tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
792 ier
= (INODE_EXTREF
*)tp
.item
->data
;
794 while (len
>= sizeof(INODE_EXTREF
) - 1) {
798 hl
= ExAllocatePoolWithTag(pooltype
, sizeof(hardlink
), ALLOC_TAG
);
800 ERR("out of memory\n");
802 return STATUS_INSUFFICIENT_RESOURCES
;
805 hl
->parent
= ier
->dir
;
806 hl
->index
= ier
->index
;
808 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= ier
->n
;
810 if (hl
->utf8
.Length
> 0) {
811 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(pooltype
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
812 RtlCopyMemory(hl
->utf8
.Buffer
, ier
->name
, ier
->n
);
815 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ier
->name
, ier
->n
);
816 if (!NT_SUCCESS(Status
)) {
817 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
823 hl
->name
.Length
= hl
->name
.MaximumLength
= (UINT16
)stringlen
;
826 hl
->name
.Buffer
= NULL
;
828 hl
->name
.Buffer
= ExAllocatePoolWithTag(pooltype
, hl
->name
.MaximumLength
, ALLOC_TAG
);
830 if (!hl
->name
.Buffer
) {
831 ERR("out of memory\n");
834 return STATUS_INSUFFICIENT_RESOURCES
;
837 Status
= RtlUTF8ToUnicodeN(hl
->name
.Buffer
, stringlen
, &stringlen
, ier
->name
, ier
->n
);
838 if (!NT_SUCCESS(Status
)) {
839 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
840 ExFreePool(hl
->name
.Buffer
);
847 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
849 len
-= sizeof(INODE_EXTREF
) - 1 + ier
->n
;
850 ier
= (INODE_EXTREF
*)&ier
->name
[ier
->n
];
852 } else if (tp
.item
->key
.obj_type
== TYPE_XATTR_ITEM
) {
856 static const char xapref
[] = "user.";
858 if (tp
.item
->size
< offsetof(DIR_ITEM
, name
[0])) {
859 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, offsetof(DIR_ITEM
, name
[0]));
864 di
= (DIR_ITEM
*)tp
.item
->data
;
867 if (len
< offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
)
870 if (tp
.item
->key
.offset
== EA_REPARSE_HASH
&& di
->n
== sizeof(EA_REPARSE
) - 1 && RtlCompareMemory(EA_REPARSE
, di
->name
, di
->n
) == di
->n
) {
872 fcb
->reparse_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, di
->m
, ALLOC_TAG
);
873 if (!fcb
->reparse_xattr
.Buffer
) {
874 ERR("out of memory\n");
876 return STATUS_INSUFFICIENT_RESOURCES
;
879 RtlCopyMemory(fcb
->reparse_xattr
.Buffer
, &di
->name
[di
->n
], di
->m
);
881 fcb
->reparse_xattr
.Buffer
= NULL
;
883 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= di
->m
;
884 } else if (tp
.item
->key
.offset
== EA_EA_HASH
&& di
->n
== sizeof(EA_EA
) - 1 && RtlCompareMemory(EA_EA
, di
->name
, di
->n
) == di
->n
) {
888 Status
= IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION
*)&di
->name
[di
->n
], di
->m
, &offset
);
890 if (!NT_SUCCESS(Status
))
891 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
893 FILE_FULL_EA_INFORMATION
* eainfo
;
895 fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, di
->m
, ALLOC_TAG
);
896 if (!fcb
->ea_xattr
.Buffer
) {
897 ERR("out of memory\n");
899 return STATUS_INSUFFICIENT_RESOURCES
;
902 RtlCopyMemory(fcb
->ea_xattr
.Buffer
, &di
->name
[di
->n
], di
->m
);
904 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= di
->m
;
909 eainfo
= (FILE_FULL_EA_INFORMATION
*)&di
->name
[di
->n
];
911 fcb
->ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
913 if (eainfo
->NextEntryOffset
== 0)
916 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
920 } else if (tp
.item
->key
.offset
== EA_DOSATTRIB_HASH
&& di
->n
== sizeof(EA_DOSATTRIB
) - 1 && RtlCompareMemory(EA_DOSATTRIB
, di
->name
, di
->n
) == di
->n
) {
922 if (get_file_attributes_from_xattr(&di
->name
[di
->n
], di
->m
, &fcb
->atts
)) {
925 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
)
926 fcb
->atts
|= FILE_ATTRIBUTE_DIRECTORY
;
927 else if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
928 fcb
->atts
|= FILE_ATTRIBUTE_REPARSE_POINT
;
930 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
)
931 fcb
->atts
&= ~FILE_ATTRIBUTE_DIRECTORY
;
933 if (inode
== SUBVOL_ROOT_INODE
) {
934 if (subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
935 fcb
->atts
|= FILE_ATTRIBUTE_READONLY
;
937 fcb
->atts
&= ~FILE_ATTRIBUTE_READONLY
;
941 } else if (tp
.item
->key
.offset
== EA_NTACL_HASH
&& di
->n
== sizeof(EA_NTACL
) - 1 && RtlCompareMemory(EA_NTACL
, di
->name
, di
->n
) == di
->n
) {
943 fcb
->sd
= ExAllocatePoolWithTag(PagedPool
, di
->m
, ALLOC_TAG
);
945 ERR("out of memory\n");
947 return STATUS_INSUFFICIENT_RESOURCES
;
950 RtlCopyMemory(fcb
->sd
, &di
->name
[di
->n
], di
->m
);
952 // We have to test against our copy rather than the source, as RtlValidRelativeSecurityDescriptor
953 // will fail if the ACLs aren't 32-bit aligned.
954 if (!RtlValidRelativeSecurityDescriptor(fcb
->sd
, di
->m
, 0))
959 } else if (tp
.item
->key
.offset
== EA_PROP_COMPRESSION_HASH
&& di
->n
== sizeof(EA_PROP_COMPRESSION
) - 1 && RtlCompareMemory(EA_PROP_COMPRESSION
, di
->name
, di
->n
) == di
->n
) {
961 static const char lzo
[] = "lzo";
962 static const char zlib
[] = "zlib";
963 static const char zstd
[] = "zstd";
965 if (di
->m
== sizeof(lzo
) - 1 && RtlCompareMemory(&di
->name
[di
->n
], lzo
, di
->m
) == di
->m
)
966 fcb
->prop_compression
= PropCompression_LZO
;
967 else if (di
->m
== sizeof(zlib
) - 1 && RtlCompareMemory(&di
->name
[di
->n
], zlib
, di
->m
) == di
->m
)
968 fcb
->prop_compression
= PropCompression_Zlib
;
969 else if (di
->m
== sizeof(zstd
) - 1 && RtlCompareMemory(&di
->name
[di
->n
], zstd
, di
->m
) == di
->m
)
970 fcb
->prop_compression
= PropCompression_ZSTD
;
972 fcb
->prop_compression
= PropCompression_None
;
974 } 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
) {
976 fcb
->case_sensitive
= di
->m
== 1 && di
->name
[di
->n
] == '1';
977 fcb
->case_sensitive_set
= TRUE
;
979 } else if (di
->n
> sizeof(xapref
) - 1 && RtlCompareMemory(xapref
, di
->name
, sizeof(xapref
) - 1) == sizeof(xapref
) - 1) {
983 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &utf16len
, &di
->name
[sizeof(xapref
) - 1], di
->n
+ 1 - sizeof(xapref
));
984 if (!NT_SUCCESS(Status
)) {
985 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
990 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
992 ERR("out of memory\n");
994 return STATUS_INSUFFICIENT_RESOURCES
;
997 RtlZeroMemory(dc
, sizeof(dir_child
));
999 dc
->utf8
.MaximumLength
= dc
->utf8
.Length
= di
->n
+ 1 - sizeof(xapref
);
1000 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dc
->utf8
.MaximumLength
, ALLOC_TAG
);
1001 if (!dc
->utf8
.Buffer
) {
1002 ERR("out of memory\n");
1005 return STATUS_INSUFFICIENT_RESOURCES
;
1008 RtlCopyMemory(dc
->utf8
.Buffer
, &di
->name
[sizeof(xapref
) - 1], dc
->utf8
.Length
);
1010 dc
->name
.MaximumLength
= dc
->name
.Length
= (UINT16
)utf16len
;
1011 dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dc
->name
.MaximumLength
, ALLOC_TAG
);
1012 if (!dc
->name
.Buffer
) {
1013 ERR("out of memory\n");
1014 ExFreePool(dc
->utf8
.Buffer
);
1017 return STATUS_INSUFFICIENT_RESOURCES
;
1020 Status
= RtlUTF8ToUnicodeN(dc
->name
.Buffer
, utf16len
, &utf16len
, dc
->utf8
.Buffer
, dc
->utf8
.Length
);
1021 if (!NT_SUCCESS(Status
)) {
1022 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
1023 ExFreePool(dc
->utf8
.Buffer
);
1024 ExFreePool(dc
->name
.Buffer
);
1030 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, &dc
->name
, TRUE
);
1031 if (!NT_SUCCESS(Status
)) {
1032 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1033 ExFreePool(dc
->utf8
.Buffer
);
1034 ExFreePool(dc
->name
.Buffer
);
1042 InsertTailList(&fcb
->dir_children_index
, &dc
->list_entry_index
);
1046 xa
= ExAllocatePoolWithTag(PagedPool
, offsetof(xattr
, data
[0]) + di
->m
+ di
->n
, ALLOC_TAG
);
1048 ERR("out of memory\n");
1050 return STATUS_INSUFFICIENT_RESOURCES
;
1053 xa
->namelen
= di
->n
;
1054 xa
->valuelen
= di
->m
;
1056 RtlCopyMemory(xa
->data
, di
->name
, di
->m
+ di
->n
);
1058 InsertTailList(&fcb
->xattrs
, &xa
->list_entry
);
1061 len
-= (ULONG
)offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
;
1063 if (len
< offsetof(DIR_ITEM
, name
[0]))
1066 di
= (DIR_ITEM
*)&di
->name
[di
->m
+ di
->n
];
1068 } else if (tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
) {
1070 BOOL unique
= FALSE
;
1072 ed
= (EXTENT_DATA
*)tp
.item
->data
;
1074 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
1075 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
,
1076 tp
.item
->size
, sizeof(EXTENT_DATA
));
1079 return STATUS_INTERNAL_ERROR
;
1082 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
1083 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)&ed
->data
[0];
1085 if (tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
1086 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
,
1087 tp
.item
->size
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
));
1090 return STATUS_INTERNAL_ERROR
;
1093 if (ed2
->address
== 0 || ed2
->size
== 0) // sparse
1096 if (ed2
->size
!= 0 && is_tree_unique(Vcb
, tp
.tree
, Irp
))
1097 unique
= is_extent_unique(Vcb
, ed2
->address
, ed2
->size
, Irp
);
1100 ext
= ExAllocatePoolWithTag(pooltype
, offsetof(extent
, extent_data
) + tp
.item
->size
, ALLOC_TAG
);
1102 ERR("out of memory\n");
1104 return STATUS_INSUFFICIENT_RESOURCES
;
1107 ext
->offset
= tp
.item
->key
.offset
;
1108 RtlCopyMemory(&ext
->extent_data
, tp
.item
->data
, tp
.item
->size
);
1109 ext
->datalen
= tp
.item
->size
;
1110 ext
->unique
= unique
;
1111 ext
->ignore
= FALSE
;
1112 ext
->inserted
= FALSE
;
1115 InsertTailList(&fcb
->extents
, &ext
->list_entry
);
1119 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
1120 Status
= load_dir_children(Vcb
, fcb
, FALSE
, Irp
);
1121 if (!NT_SUCCESS(Status
)) {
1122 ERR("load_dir_children returned %08x\n", Status
);
1129 fcb
->Header
.AllocationSize
.QuadPart
= 0;
1130 fcb
->Header
.FileSize
.QuadPart
= 0;
1131 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
1133 if (ed
&& ed
->type
== EXTENT_TYPE_INLINE
)
1134 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->inode_item
.st_size
;
1136 fcb
->Header
.AllocationSize
.QuadPart
= sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
1138 fcb
->Header
.FileSize
.QuadPart
= fcb
->inode_item
.st_size
;
1139 fcb
->Header
.ValidDataLength
.QuadPart
= fcb
->inode_item
.st_size
;
1143 fcb
->atts
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
, utf8
&& utf8
->Buffer
[0] == '.', TRUE
, Irp
);
1146 fcb_get_sd(fcb
, parent
, FALSE
, Irp
);
1148 acquire_fcb_lock_exclusive(Vcb
);
1150 if (lastle
&& subvol
->fcbs_version
== fcbs_version
)
1151 InsertHeadList(lastle
, &fcb
->list_entry
);
1153 if (subvol
->fcbs_ptrs
[hash
>> 24]) {
1154 LIST_ENTRY
* le
= subvol
->fcbs_ptrs
[hash
>> 24];
1156 while (le
!= &subvol
->fcbs
) {
1157 struct _fcb
* fcb2
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
1159 if (fcb2
->inode
== inode
) {
1164 #ifdef DEBUG_FCB_REFCOUNTS
1165 LONG rc
= InterlockedIncrement(&fcb2
->refcount
);
1167 WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb2
, rc
, fcb2
->subvol
->id
, fcb2
->inode
);
1169 InterlockedIncrement(&fcb2
->refcount
);
1174 release_fcb_lock(Vcb
);
1175 return STATUS_SUCCESS
;
1178 } else if (fcb2
->hash
> hash
) {
1180 InterlockedIncrement(&deleted_fcb
->refcount
);
1181 *pfcb
= deleted_fcb
;
1183 release_fcb_lock(Vcb
);
1184 return STATUS_SUCCESS
;
1195 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
&& fcb
->reparse_xattr
.Length
== 0) {
1196 fcb
->atts
&= ~FILE_ATTRIBUTE_REPARSE_POINT
;
1198 if (!Vcb
->readonly
&& !is_subvol_readonly(subvol
, Irp
)) {
1199 fcb
->atts_changed
= TRUE
;
1200 mark_fcb_dirty(fcb
);
1205 UINT8 c
= hash
>> 24;
1211 if (subvol
->fcbs_ptrs
[d
]) {
1212 lastle
= subvol
->fcbs_ptrs
[d
]->Blink
;
1222 InsertHeadList(lastle
, &fcb
->list_entry
);
1224 if (lastle
== &subvol
->fcbs
|| (CONTAINING_RECORD(lastle
, struct _fcb
, list_entry
)->hash
>> 24) != (hash
>> 24))
1225 subvol
->fcbs_ptrs
[hash
>> 24] = &fcb
->list_entry
;
1227 InsertTailList(&subvol
->fcbs
, &fcb
->list_entry
);
1229 if (fcb
->list_entry
.Blink
== &subvol
->fcbs
|| (CONTAINING_RECORD(fcb
->list_entry
.Blink
, struct _fcb
, list_entry
)->hash
>> 24) != (hash
>> 24))
1230 subvol
->fcbs_ptrs
[hash
>> 24] = &fcb
->list_entry
;
1234 subvol
->fcbs_version
++;
1236 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
1238 release_fcb_lock(Vcb
);
1240 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
1244 return STATUS_SUCCESS
;
1247 static NTSTATUS
open_fcb_stream(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
1248 dir_child
* dc
, fcb
* parent
, fcb
** pfcb
, PIRP Irp
) {
1251 UINT16 xattrlen
, overhead
;
1255 static const char xapref
[] = "user.";
1259 xattr
.Length
= sizeof(xapref
) - 1 + dc
->utf8
.Length
;
1260 xattr
.MaximumLength
= xattr
.Length
+ 1;
1261 xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, xattr
.MaximumLength
, ALLOC_TAG
);
1262 if (!xattr
.Buffer
) {
1263 ERR("out of memory\n");
1264 return STATUS_INSUFFICIENT_RESOURCES
;
1267 RtlCopyMemory(xattr
.Buffer
, xapref
, sizeof(xapref
) - 1);
1268 RtlCopyMemory(&xattr
.Buffer
[sizeof(xapref
) - 1], dc
->utf8
.Buffer
, dc
->utf8
.Length
);
1269 xattr
.Buffer
[xattr
.Length
] = 0;
1271 fcb
= create_fcb(Vcb
, PagedPool
);
1273 ERR("out of memory\n");
1274 ExFreePool(xattr
.Buffer
);
1275 return STATUS_INSUFFICIENT_RESOURCES
;
1280 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)xattr
.Buffer
, xattr
.Length
);
1282 if (!get_xattr(Vcb
, parent
->subvol
, parent
->inode
, xattr
.Buffer
, crc32
, &xattrdata
, &xattrlen
, Irp
)) {
1283 ERR("get_xattr failed\n");
1285 ExFreePool(xattr
.Buffer
);
1286 return STATUS_INTERNAL_ERROR
;
1289 fcb
->subvol
= parent
->subvol
;
1290 fcb
->inode
= parent
->inode
;
1291 fcb
->type
= parent
->type
;
1293 fcb
->adshash
= crc32
;
1294 fcb
->adsxattr
= xattr
;
1296 // find XATTR_ITEM overhead and hence calculate maximum length
1298 searchkey
.obj_id
= parent
->inode
;
1299 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
1300 searchkey
.offset
= crc32
;
1302 Status
= find_item(Vcb
, parent
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
1303 if (!NT_SUCCESS(Status
)) {
1304 ERR("find_item returned %08x\n", Status
);
1309 if (keycmp(tp
.item
->key
, searchkey
)) {
1310 ERR("error - could not find key for xattr\n");
1312 return STATUS_INTERNAL_ERROR
;
1315 if (tp
.item
->size
< xattrlen
) {
1316 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, xattrlen
);
1318 return STATUS_INTERNAL_ERROR
;
1321 overhead
= tp
.item
->size
- xattrlen
;
1323 fcb
->adsmaxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
) - overhead
;
1325 fcb
->adsdata
.Buffer
= (char*)xattrdata
;
1326 fcb
->adsdata
.Length
= fcb
->adsdata
.MaximumLength
= xattrlen
;
1328 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
1329 fcb
->Header
.AllocationSize
.QuadPart
= xattrlen
;
1330 fcb
->Header
.FileSize
.QuadPart
= xattrlen
;
1331 fcb
->Header
.ValidDataLength
.QuadPart
= xattrlen
;
1333 TRACE("stream found: size = %x, hash = %08x\n", xattrlen
, fcb
->adshash
);
1337 return STATUS_SUCCESS
;
1340 NTSTATUS
open_fileref_child(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) _In_ device_extension
* Vcb
,
1341 _In_ file_ref
* sf
, _In_ PUNICODE_STRING name
, _In_ BOOL case_sensitive
, _In_ BOOL lastpart
, _In_ BOOL streampart
,
1342 _In_ POOL_TYPE pooltype
, _Out_ file_ref
** psf2
, _In_opt_ PIRP Irp
) {
1346 if (sf
->fcb
== Vcb
->dummy_fcb
)
1347 return STATUS_OBJECT_NAME_NOT_FOUND
;
1350 BOOL locked
= FALSE
;
1352 UNICODE_STRING name_uc
;
1353 dir_child
* dc
= NULL
;
1355 struct _fcb
* duff_fcb
= NULL
;
1356 file_ref
* duff_fr
= NULL
;
1358 if (!case_sensitive
) {
1359 Status
= RtlUpcaseUnicodeString(&name_uc
, name
, TRUE
);
1360 if (!NT_SUCCESS(Status
)) {
1361 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1366 if (!ExIsResourceAcquiredSharedLite(&sf
->fcb
->nonpaged
->dir_children_lock
)) {
1367 ExAcquireResourceSharedLite(&sf
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1371 le
= sf
->fcb
->dir_children_index
.Flink
;
1372 while (le
!= &sf
->fcb
->dir_children_index
) {
1373 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
1375 if (dc2
->index
== 0) {
1376 if ((case_sensitive
&& dc2
->name
.Length
== name
->Length
&& RtlCompareMemory(dc2
->name
.Buffer
, name
->Buffer
, dc2
->name
.Length
) == dc2
->name
.Length
) ||
1377 (!case_sensitive
&& dc2
->name_uc
.Length
== name_uc
.Length
&& RtlCompareMemory(dc2
->name_uc
.Buffer
, name_uc
.Buffer
, dc2
->name_uc
.Length
) == dc2
->name_uc
.Length
)
1390 ExReleaseResourceLite(&sf
->fcb
->nonpaged
->dir_children_lock
);
1392 if (!case_sensitive
)
1393 ExFreePool(name_uc
.Buffer
);
1395 return STATUS_OBJECT_NAME_NOT_FOUND
;
1400 ExReleaseResourceLite(&sf
->fcb
->nonpaged
->dir_children_lock
);
1402 if (!case_sensitive
)
1403 ExFreePool(name_uc
.Buffer
);
1405 increase_fileref_refcount(dc
->fileref
);
1406 *psf2
= dc
->fileref
;
1407 return STATUS_SUCCESS
;
1411 ExReleaseResourceLite(&sf
->fcb
->nonpaged
->dir_children_lock
);
1413 if (!case_sensitive
)
1414 ExFreePool(name_uc
.Buffer
);
1416 Status
= open_fcb_stream(Vcb
, dc
, sf
->fcb
, &fcb
, Irp
);
1417 if (!NT_SUCCESS(Status
)) {
1418 ERR("open_fcb_stream returned %08x\n", Status
);
1422 fcb
->hash
= sf
->fcb
->hash
;
1424 acquire_fcb_lock_exclusive(Vcb
);
1426 if (sf
->fcb
->subvol
->fcbs_ptrs
[fcb
->hash
>> 24]) {
1427 LIST_ENTRY
* le
= sf
->fcb
->subvol
->fcbs_ptrs
[fcb
->hash
>> 24];
1429 while (le
!= &sf
->fcb
->subvol
->fcbs
) {
1430 struct _fcb
* fcb2
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
1432 if (fcb2
->inode
== fcb
->inode
) {
1433 if (fcb2
->ads
&& fcb2
->adshash
== fcb
->adshash
) { // FIXME - handle hash collisions
1438 } else if (fcb2
->hash
> fcb
->hash
)
1446 InsertHeadList(&sf
->fcb
->list_entry
, &fcb
->list_entry
);
1447 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
1448 fcb
->subvol
->fcbs_version
++;
1451 release_fcb_lock(Vcb
);
1455 InterlockedIncrement(&fcb
->refcount
);
1458 sf2
= create_fileref(Vcb
);
1460 ERR("out of memory\n");
1462 return STATUS_INSUFFICIENT_RESOURCES
;
1465 ExAcquireResourceExclusiveLite(&sf
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1470 increase_fileref_refcount(sf2
);
1473 sf2
->parent
= (struct _file_ref
*)sf
;
1476 increase_fileref_refcount(sf
);
1477 InsertTailList(&sf
->children
, &sf2
->list_entry
);
1480 ExReleaseResourceLite(&sf
->fcb
->nonpaged
->dir_children_lock
);
1483 reap_fileref(Vcb
, duff_fr
);
1489 Status
= find_file_in_dir(name
, sf
->fcb
, &subvol
, &inode
, &dc
, case_sensitive
);
1490 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
1491 TRACE("could not find %.*S\n", name
->Length
/ sizeof(WCHAR
), name
->Buffer
);
1493 return lastpart
? STATUS_OBJECT_NAME_NOT_FOUND
: STATUS_OBJECT_PATH_NOT_FOUND
;
1494 } else if (!NT_SUCCESS(Status
)) {
1495 ERR("find_file_in_dir returned %08x\n", Status
);
1499 file_ref
* duff_fr
= NULL
;
1501 LARGE_INTEGER time1
, time2
;
1505 if (!lastpart
&& dc
->type
!= BTRFS_TYPE_DIRECTORY
) {
1506 TRACE("passed path including file as subdirectory\n");
1507 return STATUS_OBJECT_PATH_NOT_FOUND
;
1510 InterlockedIncrement(&dc
->fileref
->refcount
);
1511 *psf2
= dc
->fileref
;
1512 return STATUS_SUCCESS
;
1515 if (!subvol
|| (subvol
!= Vcb
->root_fileref
->fcb
->subvol
&& inode
== SUBVOL_ROOT_INODE
&& subvol
->parent
!= sf
->fcb
->subvol
->id
)) {
1516 fcb
= Vcb
->dummy_fcb
;
1517 InterlockedIncrement(&fcb
->refcount
);
1520 time1
= KeQueryPerformanceCounter(NULL
);
1522 Status
= open_fcb(Vcb
, subvol
, inode
, dc
->type
, &dc
->utf8
, FALSE
, sf
->fcb
, &fcb
, pooltype
, Irp
);
1524 time2
= KeQueryPerformanceCounter(NULL
);
1525 Vcb
->stats
.open_fcb_calls
++;
1526 Vcb
->stats
.open_fcb_time
+= time2
.QuadPart
- time1
.QuadPart
;
1529 if (!NT_SUCCESS(Status
)) {
1530 ERR("open_fcb returned %08x\n", Status
);
1535 if (dc
->type
!= BTRFS_TYPE_DIRECTORY
&& !lastpart
&& !(fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
)) {
1536 TRACE("passed path including file as subdirectory\n");
1538 return STATUS_OBJECT_PATH_NOT_FOUND
;
1541 sf2
= create_fileref(Vcb
);
1543 ERR("out of memory\n");
1545 return STATUS_INSUFFICIENT_RESOURCES
;
1550 if (dc
->type
== BTRFS_TYPE_DIRECTORY
)
1553 ExAcquireResourceExclusiveLite(&sf
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1556 sf2
->parent
= (struct _file_ref
*)sf
;
1559 InsertTailList(&sf
->children
, &sf2
->list_entry
);
1560 increase_fileref_refcount(sf
);
1564 increase_fileref_refcount(sf2
);
1567 ExReleaseResourceLite(&sf
->fcb
->nonpaged
->dir_children_lock
);
1570 reap_fileref(Vcb
, duff_fr
);
1576 return STATUS_SUCCESS
;
1579 NTSTATUS
open_fileref(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) _In_ device_extension
* Vcb
, _Out_ file_ref
** pfr
,
1580 _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
,
1581 _In_ BOOL case_sensitive
, _In_opt_ PIRP Irp
) {
1582 UNICODE_STRING fnus2
;
1583 file_ref
*dir
, *sf
, *sf2
;
1589 TRACE("(%p, %p, %p, %u, %p)\n", Vcb
, pfr
, related
, parent
, parsed
);
1591 if (Vcb
->removing
|| Vcb
->locked
)
1592 return STATUS_ACCESS_DENIED
;
1596 if (fnus2
.Length
< sizeof(WCHAR
) && !related
) {
1597 ERR("error - fnus was too short\n");
1598 return STATUS_INTERNAL_ERROR
;
1601 if (related
&& fnus
->Length
== 0) {
1602 increase_fileref_refcount(related
);
1605 return STATUS_SUCCESS
;
1611 if (fnus2
.Buffer
[0] != '\\') {
1612 ERR("error - filename %.*S did not begin with \\\n", fnus2
.Length
/ sizeof(WCHAR
), fnus2
.Buffer
);
1613 return STATUS_OBJECT_PATH_NOT_FOUND
;
1616 // if path starts with two backslashes, ignore one of them
1617 if (fnus2
.Length
>= 2 * sizeof(WCHAR
) && fnus2
.Buffer
[1] == '\\') {
1619 fnus2
.Length
-= sizeof(WCHAR
);
1620 fnus2
.MaximumLength
-= sizeof(WCHAR
);
1623 if (fnus2
.Length
== sizeof(WCHAR
)) {
1624 if (Vcb
->root_fileref
->open_count
== 0 && !(Vcb
->Vpb
->Flags
& VPB_MOUNTED
)) // don't allow root to be opened on unmounted FS
1625 return STATUS_DEVICE_NOT_READY
;
1627 increase_fileref_refcount(Vcb
->root_fileref
);
1628 *pfr
= Vcb
->root_fileref
;
1633 return STATUS_SUCCESS
;
1634 } else if (fnus2
.Length
>= 2 * sizeof(WCHAR
) && fnus2
.Buffer
[1] == '\\')
1635 return STATUS_OBJECT_NAME_INVALID
;
1637 dir
= Vcb
->root_fileref
;
1640 fnus2
.Length
-= sizeof(WCHAR
);
1641 fnus2
.MaximumLength
-= sizeof(WCHAR
);
1644 if (dir
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& (fnus
->Length
< sizeof(WCHAR
) || fnus
->Buffer
[0] != ':')) {
1645 WARN("passed related fileref which isn't a directory (%S) (fnus = %.*S)\n",
1646 file_desc_fileref(related
), fnus
->Length
/ sizeof(WCHAR
), fnus
->Buffer
);
1647 return STATUS_OBJECT_PATH_NOT_FOUND
;
1650 InitializeListHead(&parts
);
1652 if (fnus
->Length
!= 0 &&
1653 (fnus
->Length
!= sizeof(datastring
) - sizeof(WCHAR
) || RtlCompareMemory(fnus
->Buffer
, datastring
, sizeof(datastring
) - sizeof(WCHAR
)) != sizeof(datastring
) - sizeof(WCHAR
))) {
1654 Status
= split_path(Vcb
, &fnus2
, &parts
, &has_stream
);
1655 if (!NT_SUCCESS(Status
)) {
1656 ERR("split_path returned %08x\n", Status
);
1662 increase_fileref_refcount(dir
);
1664 if (parent
&& !IsListEmpty(&parts
)) {
1667 nb
= CONTAINING_RECORD(RemoveTailList(&parts
), name_bit
, list_entry
);
1670 if (has_stream
&& !IsListEmpty(&parts
)) {
1671 nb
= CONTAINING_RECORD(RemoveTailList(&parts
), name_bit
, list_entry
);
1678 if (IsListEmpty(&parts
)) {
1679 Status
= STATUS_SUCCESS
;
1690 name_bit
* nb
= CONTAINING_RECORD(le
, name_bit
, list_entry
);
1691 BOOL lastpart
= le
->Flink
== &parts
|| (has_stream
&& le
->Flink
->Flink
== &parts
);
1692 BOOL streampart
= has_stream
&& le
->Flink
== &parts
;
1694 LARGE_INTEGER time1
, time2
;
1698 time1
= KeQueryPerformanceCounter(NULL
);
1700 BOOL cs
= case_sensitive
;
1704 cs
= sf
->parent
->fcb
->case_sensitive
;
1706 cs
= sf
->fcb
->case_sensitive
;
1709 Status
= open_fileref_child(Vcb
, sf
, &nb
->us
, cs
, lastpart
, streampart
, pooltype
, &sf2
, Irp
);
1711 time2
= KeQueryPerformanceCounter(NULL
);
1712 Vcb
->stats
.open_fileref_child_calls
++;
1713 Vcb
->stats
.open_fileref_child_time
+= time2
.QuadPart
- time1
.QuadPart
;
1715 if (!NT_SUCCESS(Status
)) {
1716 if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
|| Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1717 TRACE("open_fileref_child returned %08x\n", Status
);
1719 ERR("open_fileref_child returned %08x\n", Status
);
1724 if (le
->Flink
== &parts
) { // last entry
1727 nb
= CONTAINING_RECORD(le
->Blink
, name_bit
, list_entry
);
1729 *fn_offset
= (ULONG
)(nb
->us
.Buffer
- fnus
->Buffer
);
1735 if (sf2
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
) {
1736 Status
= STATUS_REPARSE
;
1739 name_bit
* nb2
= CONTAINING_RECORD(le
->Flink
, name_bit
, list_entry
);
1741 *parsed
= (USHORT
)(nb2
->us
.Buffer
- fnus
->Buffer
- 1) * sizeof(WCHAR
);
1751 } while (le
!= &parts
);
1753 if (Status
!= STATUS_REPARSE
)
1754 Status
= STATUS_SUCCESS
;
1760 while (!IsListEmpty(&parts
)) {
1761 name_bit
* nb
= CONTAINING_RECORD(RemoveHeadList(&parts
), name_bit
, list_entry
);
1762 ExFreeToPagedLookasideList(&Vcb
->name_bit_lookaside
, nb
);
1766 TRACE("returning %08x\n", Status
);
1771 NTSTATUS
add_dir_child(fcb
* fcb
, UINT64 inode
, BOOL subvol
, PANSI_STRING utf8
, PUNICODE_STRING name
, UINT8 type
, dir_child
** pdc
) {
1776 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
1778 ERR("out of memory\n");
1779 return STATUS_INSUFFICIENT_RESOURCES
;
1782 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
->Length
, ALLOC_TAG
);
1783 if (!dc
->utf8
.Buffer
) {
1784 ERR("out of memory\n");
1786 return STATUS_INSUFFICIENT_RESOURCES
;
1789 dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, name
->Length
, ALLOC_TAG
);
1790 if (!dc
->name
.Buffer
) {
1791 ERR("out of memory\n");
1792 ExFreePool(dc
->utf8
.Buffer
);
1794 return STATUS_INSUFFICIENT_RESOURCES
;
1797 dc
->key
.obj_id
= inode
;
1798 dc
->key
.obj_type
= subvol
? TYPE_ROOT_ITEM
: TYPE_INODE_ITEM
;
1799 dc
->key
.offset
= subvol
? 0xffffffffffffffff : 0;
1803 dc
->utf8
.Length
= dc
->utf8
.MaximumLength
= utf8
->Length
;
1804 RtlCopyMemory(dc
->utf8
.Buffer
, utf8
->Buffer
, utf8
->Length
);
1806 dc
->name
.Length
= dc
->name
.MaximumLength
= name
->Length
;
1807 RtlCopyMemory(dc
->name
.Buffer
, name
->Buffer
, name
->Length
);
1809 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, name
, TRUE
);
1810 if (!NT_SUCCESS(Status
)) {
1811 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1812 ExFreePool(dc
->utf8
.Buffer
);
1813 ExFreePool(dc
->name
.Buffer
);
1818 dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name
.Buffer
, dc
->name
.Length
);
1819 dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name_uc
.Buffer
, dc
->name_uc
.Length
);
1821 locked
= ExIsResourceAcquiredExclusive(&fcb
->nonpaged
->dir_children_lock
);
1824 ExAcquireResourceExclusiveLite(&fcb
->nonpaged
->dir_children_lock
, TRUE
);
1826 if (IsListEmpty(&fcb
->dir_children_index
))
1829 dir_child
* dc2
= CONTAINING_RECORD(fcb
->dir_children_index
.Blink
, dir_child
, list_entry_index
);
1831 dc
->index
= max(2, dc2
->index
+ 1);
1834 InsertTailList(&fcb
->dir_children_index
, &dc
->list_entry_index
);
1836 insert_dir_child_into_hash_lists(fcb
, dc
);
1839 ExReleaseResourceLite(&fcb
->nonpaged
->dir_children_lock
);
1843 return STATUS_SUCCESS
;
1846 UINT32
inherit_mode(fcb
* parfcb
, BOOL is_dir
) {
1852 mode
= parfcb
->inode_item
.st_mode
& ~S_IFDIR
;
1853 mode
&= ~S_ISVTX
; // clear sticky bit
1854 mode
&= ~S_ISUID
; // clear setuid bit
1857 mode
&= ~S_ISGID
; // if not directory, clear setgid bit
1862 static NTSTATUS
file_create_parse_ea(fcb
* fcb
, FILE_FULL_EA_INFORMATION
* ea
) {
1864 LIST_ENTRY ealist
, *le
;
1868 InitializeListHead(&ealist
);
1874 s
.Length
= s
.MaximumLength
= ea
->EaNameLength
;
1875 s
.Buffer
= ea
->EaName
;
1877 RtlUpperString(&s
, &s
);
1880 while (le
!= &ealist
) {
1881 ea_item
* item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
1883 if (item
->name
.Length
== s
.Length
&& RtlCompareMemory(item
->name
.Buffer
, s
.Buffer
, s
.Length
) == s
.Length
) {
1884 item
->flags
= ea
->Flags
;
1885 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
1886 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
1895 ea_item
* item
= ExAllocatePoolWithTag(PagedPool
, sizeof(ea_item
), ALLOC_TAG
);
1897 ERR("out of memory\n");
1898 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1902 item
->name
.Length
= item
->name
.MaximumLength
= ea
->EaNameLength
;
1903 item
->name
.Buffer
= ea
->EaName
;
1905 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
1906 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
1908 item
->flags
= ea
->Flags
;
1910 InsertTailList(&ealist
, &item
->list_entry
);
1913 if (ea
->NextEntryOffset
== 0)
1916 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
1919 // handle LXSS values
1921 while (le
!= &ealist
) {
1922 LIST_ENTRY
* le2
= le
->Flink
;
1923 ea_item
* item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
1925 if (item
->name
.Length
== sizeof(lxuid
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxuid
, item
->name
.Length
) == item
->name
.Length
) {
1926 if (item
->value
.Length
< sizeof(UINT32
)) {
1927 ERR("uid value was shorter than expected\n");
1928 Status
= STATUS_INVALID_PARAMETER
;
1932 RtlCopyMemory(&fcb
->inode_item
.st_uid
, item
->value
.Buffer
, sizeof(UINT32
));
1933 fcb
->sd_dirty
= TRUE
;
1934 fcb
->sd_deleted
= FALSE
;
1936 RemoveEntryList(&item
->list_entry
);
1938 } else if (item
->name
.Length
== sizeof(lxgid
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxgid
, item
->name
.Length
) == item
->name
.Length
) {
1939 if (item
->value
.Length
< sizeof(UINT32
)) {
1940 ERR("gid value was shorter than expected\n");
1941 Status
= STATUS_INVALID_PARAMETER
;
1945 RtlCopyMemory(&fcb
->inode_item
.st_gid
, item
->value
.Buffer
, sizeof(UINT32
));
1947 RemoveEntryList(&item
->list_entry
);
1949 } else if (item
->name
.Length
== sizeof(lxmod
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxmod
, item
->name
.Length
) == item
->name
.Length
) {
1950 UINT32 allowed
= S_IRUSR
| S_IWUSR
| S_IXUSR
| S_IRGRP
| S_IWGRP
| S_IXGRP
| S_IROTH
| S_IWOTH
| S_IXOTH
| S_ISGID
| S_ISVTX
| S_ISUID
;
1953 if (item
->value
.Length
< sizeof(UINT32
)) {
1954 ERR("mode value was shorter than expected\n");
1955 Status
= STATUS_INVALID_PARAMETER
;
1959 RtlCopyMemory(&val
, item
->value
.Buffer
, sizeof(UINT32
));
1961 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
)
1962 allowed
|= __S_IFIFO
| __S_IFCHR
| __S_IFBLK
| __S_IFSOCK
;
1964 fcb
->inode_item
.st_mode
&= ~allowed
;
1965 fcb
->inode_item
.st_mode
|= val
& allowed
;
1967 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
1968 if ((fcb
->inode_item
.st_mode
& __S_IFCHR
) == __S_IFCHR
)
1969 fcb
->type
= BTRFS_TYPE_CHARDEV
;
1970 else if ((fcb
->inode_item
.st_mode
& __S_IFBLK
) == __S_IFBLK
)
1971 fcb
->type
= BTRFS_TYPE_BLOCKDEV
;
1972 else if ((fcb
->inode_item
.st_mode
& __S_IFIFO
) == __S_IFIFO
)
1973 fcb
->type
= BTRFS_TYPE_FIFO
;
1974 else if ((fcb
->inode_item
.st_mode
& __S_IFSOCK
) == __S_IFSOCK
)
1975 fcb
->type
= BTRFS_TYPE_SOCKET
;
1978 RemoveEntryList(&item
->list_entry
);
1980 } else if (item
->name
.Length
== sizeof(lxdev
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxdev
, item
->name
.Length
) == item
->name
.Length
) {
1981 UINT32 major
, minor
;
1983 if (item
->value
.Length
< sizeof(UINT64
)) {
1984 ERR("dev value was shorter than expected\n");
1985 Status
= STATUS_INVALID_PARAMETER
;
1989 major
= *(UINT32
*)item
->value
.Buffer
;
1990 minor
= *(UINT32
*)&item
->value
.Buffer
[sizeof(UINT32
)];
1992 fcb
->inode_item
.st_rdev
= (minor
& 0xFFFFF) | ((major
& 0xFFFFFFFFFFF) << 20);
1994 RemoveEntryList(&item
->list_entry
);
2001 if (fcb
->type
!= BTRFS_TYPE_CHARDEV
&& fcb
->type
!= BTRFS_TYPE_BLOCKDEV
)
2002 fcb
->inode_item
.st_rdev
= 0;
2004 if (IsListEmpty(&ealist
))
2005 return STATUS_SUCCESS
;
2008 while (le
!= &ealist
) {
2009 ea_item
* item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
2012 size
+= 4 - (size
% 4);
2014 size
+= (UINT16
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + item
->name
.Length
+ 1 + item
->value
.Length
;
2019 buf
= ExAllocatePoolWithTag(PagedPool
, size
, ALLOC_TAG
);
2021 ERR("out of memory\n");
2022 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2026 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= size
;
2027 fcb
->ea_xattr
.Buffer
= buf
;
2033 while (le
!= &ealist
) {
2034 ea_item
* item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
2037 ea
->NextEntryOffset
= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ ea
->EaValueLength
;
2039 if (ea
->NextEntryOffset
% 4 > 0)
2040 ea
->NextEntryOffset
+= 4 - (ea
->NextEntryOffset
% 4);
2042 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
2044 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
2046 ea
->NextEntryOffset
= 0;
2047 ea
->Flags
= item
->flags
;
2048 ea
->EaNameLength
= (UCHAR
)item
->name
.Length
;
2049 ea
->EaValueLength
= item
->value
.Length
;
2051 RtlCopyMemory(ea
->EaName
, item
->name
.Buffer
, item
->name
.Length
);
2052 ea
->EaName
[item
->name
.Length
] = 0;
2053 RtlCopyMemory(&ea
->EaName
[item
->name
.Length
+ 1], item
->value
.Buffer
, item
->value
.Length
);
2055 fcb
->ealen
+= 5 + item
->name
.Length
+ item
->value
.Length
;
2060 fcb
->ea_changed
= TRUE
;
2062 Status
= STATUS_SUCCESS
;
2065 while (!IsListEmpty(&ealist
)) {
2066 ea_item
* item
= CONTAINING_RECORD(RemoveHeadList(&ealist
), ea_item
, list_entry
);
2074 static NTSTATUS
file_create2(_In_ PIRP Irp
, _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) _In_ device_extension
* Vcb
, _In_ PUNICODE_STRING fpus
,
2075 _In_ file_ref
* parfileref
, _In_ ULONG options
, _In_reads_bytes_opt_(ealen
) FILE_FULL_EA_INFORMATION
* ea
, _In_ ULONG ealen
,
2076 _Out_ file_ref
** pfr
, BOOL case_sensitive
, _In_ LIST_ENTRY
* rollback
) {
2085 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2086 POOL_TYPE pool_type
= IrpSp
->Flags
& SL_OPEN_PAGING_FILE
? NonPagedPool
: PagedPool
;
2091 LIST_ENTRY
* lastle
= NULL
;
2092 file_ref
* existing_fileref
= NULL
;
2093 #ifdef DEBUG_FCB_REFCOUNTS
2097 if (parfileref
->fcb
== Vcb
->dummy_fcb
)
2098 return STATUS_ACCESS_DENIED
;
2100 if (options
& FILE_DIRECTORY_FILE
&& IrpSp
->Parameters
.Create
.FileAttributes
& FILE_ATTRIBUTE_TEMPORARY
)
2101 return STATUS_INVALID_PARAMETER
;
2103 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, fpus
->Buffer
, fpus
->Length
);
2104 if (!NT_SUCCESS(Status
)) {
2105 ERR("RtlUnicodeToUTF8N returned %08x\n", Status
);
2109 utf8
= ExAllocatePoolWithTag(pool_type
, utf8len
+ 1, ALLOC_TAG
);
2111 ERR("out of memory\n");
2112 return STATUS_INSUFFICIENT_RESOURCES
;
2115 Status
= RtlUnicodeToUTF8N(utf8
, utf8len
, &utf8len
, fpus
->Buffer
, fpus
->Length
);
2116 if (!NT_SUCCESS(Status
)) {
2117 ERR("RtlUnicodeToUTF8N returned %08x\n", Status
);
2124 KeQuerySystemTime(&time
);
2125 win_time_to_unix(time
, &now
);
2127 TRACE("create file %.*S\n", fpus
->Length
/ sizeof(WCHAR
), fpus
->Buffer
);
2128 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2129 TRACE("parfileref->fcb->inode_item.st_size (inode %llx) was %llx\n", parfileref
->fcb
->inode
, parfileref
->fcb
->inode_item
.st_size
);
2130 parfileref
->fcb
->inode_item
.st_size
+= utf8len
* 2;
2131 TRACE("parfileref->fcb->inode_item.st_size (inode %llx) now %llx\n", parfileref
->fcb
->inode
, parfileref
->fcb
->inode_item
.st_size
);
2132 parfileref
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2133 parfileref
->fcb
->inode_item
.sequence
++;
2134 parfileref
->fcb
->inode_item
.st_ctime
= now
;
2135 parfileref
->fcb
->inode_item
.st_mtime
= now
;
2136 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2138 parfileref
->fcb
->inode_item_changed
= TRUE
;
2139 mark_fcb_dirty(parfileref
->fcb
);
2141 inode
= InterlockedIncrement64(&parfileref
->fcb
->subvol
->lastinode
);
2143 type
= options
& FILE_DIRECTORY_FILE
? BTRFS_TYPE_DIRECTORY
: BTRFS_TYPE_FILE
;
2145 // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
2147 TRACE("requested attributes = %x\n", IrpSp
->Parameters
.Create
.FileAttributes
);
2152 defda
|= FILE_ATTRIBUTE_HIDDEN
;
2154 if (options
& FILE_DIRECTORY_FILE
) {
2155 defda
|= FILE_ATTRIBUTE_DIRECTORY
;
2156 IrpSp
->Parameters
.Create
.FileAttributes
|= FILE_ATTRIBUTE_DIRECTORY
;
2158 IrpSp
->Parameters
.Create
.FileAttributes
&= ~FILE_ATTRIBUTE_DIRECTORY
;
2160 if (!(IrpSp
->Parameters
.Create
.FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)) {
2161 IrpSp
->Parameters
.Create
.FileAttributes
|= FILE_ATTRIBUTE_ARCHIVE
;
2162 defda
|= FILE_ATTRIBUTE_ARCHIVE
;
2165 TRACE("defda = %x\n", defda
);
2167 if (IrpSp
->Parameters
.Create
.FileAttributes
== FILE_ATTRIBUTE_NORMAL
)
2168 IrpSp
->Parameters
.Create
.FileAttributes
= defda
;
2170 fcb
= create_fcb(Vcb
, pool_type
);
2172 ERR("out of memory\n");
2175 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2176 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2177 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2179 return STATUS_INSUFFICIENT_RESOURCES
;
2184 if (IrpSp
->Flags
& SL_OPEN_PAGING_FILE
) {
2185 fcb
->Header
.Flags2
|= FSRTL_FLAG2_IS_PAGING_FILE
;
2186 Vcb
->disallow_dismount
= TRUE
;
2189 fcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
2190 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2191 fcb
->inode_item
.st_size
= 0;
2192 fcb
->inode_item
.st_blocks
= 0;
2193 fcb
->inode_item
.block_group
= 0;
2194 fcb
->inode_item
.st_nlink
= 1;
2195 fcb
->inode_item
.st_gid
= GID_NOBODY
; // FIXME?
2196 fcb
->inode_item
.st_mode
= inherit_mode(parfileref
->fcb
, type
== BTRFS_TYPE_DIRECTORY
); // use parent's permissions by default
2197 fcb
->inode_item
.st_rdev
= 0;
2198 fcb
->inode_item
.flags
= 0;
2199 fcb
->inode_item
.sequence
= 1;
2200 fcb
->inode_item
.st_atime
= now
;
2201 fcb
->inode_item
.st_ctime
= now
;
2202 fcb
->inode_item
.st_mtime
= now
;
2203 fcb
->inode_item
.otime
= now
;
2205 if (type
== BTRFS_TYPE_DIRECTORY
)
2206 fcb
->inode_item
.st_mode
|= S_IFDIR
;
2208 fcb
->inode_item
.st_mode
|= S_IFREG
;
2209 fcb
->inode_item
.st_mode
&= ~(S_IXUSR
| S_IXGRP
| S_IXOTH
); // remove executable bit if not directory
2212 if (IrpSp
->Flags
& SL_OPEN_PAGING_FILE
) {
2213 fcb
->inode_item
.flags
= BTRFS_INODE_NODATACOW
| BTRFS_INODE_NODATASUM
| BTRFS_INODE_NOCOMPRESS
;
2215 // inherit nodatacow flag from parent directory
2216 if (parfileref
->fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
) {
2217 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATACOW
;
2219 if (type
!= BTRFS_TYPE_DIRECTORY
)
2220 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATASUM
;
2223 if (parfileref
->fcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
2224 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
2227 fcb
->prop_compression
= parfileref
->fcb
->prop_compression
;
2228 fcb
->prop_compression_changed
= fcb
->prop_compression
!= PropCompression_None
;
2230 fcb
->inode_item_changed
= TRUE
;
2232 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
2233 fcb
->Header
.AllocationSize
.QuadPart
= 0;
2234 fcb
->Header
.FileSize
.QuadPart
= 0;
2235 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
2237 fcb
->atts
= IrpSp
->Parameters
.Create
.FileAttributes
& ~FILE_ATTRIBUTE_NORMAL
;
2238 fcb
->atts_changed
= fcb
->atts
!= defda
;
2240 #ifdef DEBUG_FCB_REFCOUNTS
2241 rc
= InterlockedIncrement(&parfileref
->fcb
->refcount
);
2242 WARN("fcb %p: refcount now %i (%S)\n", parfileref
->fcb
, rc
, file_desc_fileref(parfileref
));
2244 InterlockedIncrement(&parfileref
->fcb
->refcount
);
2246 fcb
->subvol
= parfileref
->fcb
->subvol
;
2249 fcb
->created
= TRUE
;
2250 fcb
->deleted
= TRUE
;
2252 fcb
->hash
= calc_crc32c(0xffffffff, (UINT8
*)&inode
, sizeof(UINT64
));
2254 acquire_fcb_lock_exclusive(Vcb
);
2256 if (fcb
->subvol
->fcbs_ptrs
[fcb
->hash
>> 24]) {
2257 LIST_ENTRY
* le
= fcb
->subvol
->fcbs_ptrs
[fcb
->hash
>> 24];
2259 while (le
!= &fcb
->subvol
->fcbs
) {
2260 struct _fcb
* fcb2
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
2262 if (fcb2
->hash
> fcb
->hash
) {
2272 UINT8 c
= fcb
->hash
>> 24;
2278 if (fcb
->subvol
->fcbs_ptrs
[d
]) {
2279 lastle
= fcb
->subvol
->fcbs_ptrs
[d
]->Blink
;
2289 InsertHeadList(lastle
, &fcb
->list_entry
);
2291 if (lastle
== &fcb
->subvol
->fcbs
|| (CONTAINING_RECORD(lastle
, struct _fcb
, list_entry
)->hash
>> 24) != (fcb
->hash
>> 24))
2292 fcb
->subvol
->fcbs_ptrs
[fcb
->hash
>> 24] = &fcb
->list_entry
;
2294 InsertTailList(&fcb
->subvol
->fcbs
, &fcb
->list_entry
);
2296 if (fcb
->list_entry
.Blink
== &fcb
->subvol
->fcbs
|| (CONTAINING_RECORD(fcb
->list_entry
.Blink
, struct _fcb
, list_entry
)->hash
>> 24) != (fcb
->hash
>> 24))
2297 fcb
->subvol
->fcbs_ptrs
[fcb
->hash
>> 24] = &fcb
->list_entry
;
2300 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
2302 fcb
->subvol
->fcbs_version
++;
2304 release_fcb_lock(Vcb
);
2306 mark_fcb_dirty(fcb
);
2308 Status
= fcb_get_new_sd(fcb
, parfileref
, IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
);
2310 if (!NT_SUCCESS(Status
)) {
2311 ERR("fcb_get_new_sd returned %08x\n", Status
);
2314 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2315 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2316 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2323 fcb
->sd_dirty
= TRUE
;
2325 if (ea
&& ealen
> 0) {
2326 Status
= file_create_parse_ea(fcb
, ea
);
2327 if (!NT_SUCCESS(Status
)) {
2328 ERR("file_create_parse_ea returned %08x\n", Status
);
2331 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2332 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2333 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2341 fileref
= create_fileref(Vcb
);
2343 ERR("out of memory\n");
2346 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2347 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2348 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2352 return STATUS_INSUFFICIENT_RESOURCES
;
2357 if (Irp
->Overlay
.AllocationSize
.QuadPart
> 0 && !write_fcb_compressed(fcb
)) {
2358 Status
= extend_file(fcb
, fileref
, Irp
->Overlay
.AllocationSize
.QuadPart
, TRUE
, NULL
, rollback
);
2360 if (!NT_SUCCESS(Status
)) {
2361 ERR("extend_file returned %08x\n", Status
);
2362 reap_fileref(Vcb
, fileref
);
2364 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2365 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2366 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2374 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
2375 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
2376 if (!fcb
->hash_ptrs
) {
2377 ERR("out of memory\n");
2378 reap_fileref(Vcb
, fileref
);
2380 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2381 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2382 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2386 return STATUS_INSUFFICIENT_RESOURCES
;
2389 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
2391 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
2392 if (!fcb
->hash_ptrs_uc
) {
2393 ERR("out of memory\n");
2394 reap_fileref(Vcb
, fileref
);
2396 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2397 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2398 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2402 return STATUS_INSUFFICIENT_RESOURCES
;
2405 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
2408 fcb
->deleted
= FALSE
;
2410 fileref
->created
= TRUE
;
2412 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
2413 fcb
->subvol
->root_item
.ctime
= now
;
2415 utf8as
.Buffer
= utf8
;
2416 utf8as
.Length
= utf8as
.MaximumLength
= (UINT16
)utf8len
;
2418 ExAcquireResourceExclusiveLite(&parfileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
2420 // check again doesn't already exist
2421 if (case_sensitive
) {
2422 UINT32 dc_hash
= calc_crc32c(0xffffffff, (UINT8
*)fpus
->Buffer
, fpus
->Length
);
2424 if (parfileref
->fcb
->hash_ptrs
[dc_hash
>> 24]) {
2425 LIST_ENTRY
* le
= parfileref
->fcb
->hash_ptrs
[dc_hash
>> 24];
2426 while (le
!= &parfileref
->fcb
->dir_children_hash
) {
2427 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash
);
2429 if (dc
->hash
== dc_hash
&& dc
->name
.Length
== fpus
->Length
&& RtlCompareMemory(dc
->name
.Buffer
, fpus
->Buffer
, fpus
->Length
) == fpus
->Length
) {
2430 existing_fileref
= dc
->fileref
;
2432 } else if (dc
->hash
> dc_hash
)
2439 UNICODE_STRING fpusuc
;
2444 Status
= RtlUpcaseUnicodeString(&fpusuc
, fpus
, TRUE
);
2445 if (!NT_SUCCESS(Status
)) {
2446 ExReleaseResourceLite(&parfileref
->fcb
->nonpaged
->dir_children_lock
);
2447 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
2448 reap_fileref(Vcb
, fileref
);
2450 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2451 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2452 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2460 UINT32 dc_hash
= calc_crc32c(0xffffffff, (UINT8
*)fpusuc
.Buffer
, fpusuc
.Length
);
2462 dc_hash
= calc_crc32c(0xffffffff, (UINT8
*)fpusuc
.Buffer
, fpusuc
.Length
);
2465 if (parfileref
->fcb
->hash_ptrs_uc
[dc_hash
>> 24]) {
2466 LIST_ENTRY
* le
= parfileref
->fcb
->hash_ptrs_uc
[dc_hash
>> 24];
2467 while (le
!= &parfileref
->fcb
->dir_children_hash_uc
) {
2468 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash_uc
);
2470 if (dc
->hash_uc
== dc_hash
&& dc
->name
.Length
== fpusuc
.Length
&& RtlCompareMemory(dc
->name
.Buffer
, fpusuc
.Buffer
, fpusuc
.Length
) == fpusuc
.Length
) {
2471 existing_fileref
= dc
->fileref
;
2473 } else if (dc
->hash_uc
> dc_hash
)
2480 ExFreePool(fpusuc
.Buffer
);
2483 if (existing_fileref
) {
2484 ExReleaseResourceLite(&parfileref
->fcb
->nonpaged
->dir_children_lock
);
2485 reap_fileref(Vcb
, fileref
);
2487 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2488 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2489 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2493 increase_fileref_refcount(existing_fileref
);
2494 *pfr
= existing_fileref
;
2496 return STATUS_OBJECT_NAME_COLLISION
;
2499 Status
= add_dir_child(parfileref
->fcb
, fcb
->inode
, FALSE
, &utf8as
, fpus
, fcb
->type
, &dc
);
2500 if (!NT_SUCCESS(Status
)) {
2501 ExReleaseResourceLite(&parfileref
->fcb
->nonpaged
->dir_children_lock
);
2502 ERR("add_dir_child returned %08x\n", Status
);
2503 reap_fileref(Vcb
, fileref
);
2505 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2506 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2507 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2514 fileref
->parent
= parfileref
;
2516 dc
->fileref
= fileref
;
2518 if (type
== BTRFS_TYPE_DIRECTORY
)
2519 fileref
->fcb
->fileref
= fileref
;
2521 InsertTailList(&parfileref
->children
, &fileref
->list_entry
);
2522 ExReleaseResourceLite(&parfileref
->fcb
->nonpaged
->dir_children_lock
);
2526 mark_fileref_dirty(fileref
);
2527 increase_fileref_refcount(parfileref
);
2531 TRACE("created new file %S in subvol %llx, inode %llx\n", file_desc_fileref(fileref
), fcb
->subvol
->id
, fcb
->inode
);
2533 return STATUS_SUCCESS
;
2536 static NTSTATUS
create_stream(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
2537 file_ref
** pfileref
, file_ref
** pparfileref
, PUNICODE_STRING fpus
, PUNICODE_STRING stream
, PIRP Irp
,
2538 ULONG options
, POOL_TYPE pool_type
, BOOL case_sensitive
, LIST_ENTRY
* rollback
) {
2539 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2540 file_ref
*fileref
, *newpar
, *parfileref
;
2542 static const char xapref
[] = "user.";
2543 static const WCHAR DOSATTRIB
[] = L
"DOSATTRIB";
2544 static const WCHAR EA
[] = L
"EA";
2545 static const WCHAR reparse
[] = L
"reparse";
2546 static const WCHAR casesensitive_str
[] = L
"casesensitive";
2549 ULONG utf8len
, overhead
;
2554 dir_child
* existing_dc
= NULL
;
2555 ACCESS_MASK granted_access
;
2556 #ifdef DEBUG_FCB_REFCOUNTS
2563 TRACE("fpus = %.*S\n", fpus
->Length
/ sizeof(WCHAR
), fpus
->Buffer
);
2564 TRACE("stream = %.*S\n", stream
->Length
/ sizeof(WCHAR
), stream
->Buffer
);
2566 parfileref
= *pparfileref
;
2568 if (parfileref
->fcb
== Vcb
->dummy_fcb
)
2569 return STATUS_ACCESS_DENIED
;
2571 Status
= open_fileref(Vcb
, &newpar
, fpus
, parfileref
, FALSE
, NULL
, NULL
, PagedPool
, case_sensitive
, Irp
);
2573 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
2574 UNICODE_STRING fpus2
;
2576 if (!is_file_name_valid(fpus
, FALSE
))
2577 return STATUS_OBJECT_NAME_INVALID
;
2579 fpus2
.Length
= fpus2
.MaximumLength
= fpus
->Length
;
2580 fpus2
.Buffer
= ExAllocatePoolWithTag(pool_type
, fpus2
.MaximumLength
, ALLOC_TAG
);
2582 if (!fpus2
.Buffer
) {
2583 ERR("out of memory\n");
2584 return STATUS_INSUFFICIENT_RESOURCES
;
2587 RtlCopyMemory(fpus2
.Buffer
, fpus
->Buffer
, fpus2
.Length
);
2589 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2591 if (!SeAccessCheck(parfileref
->fcb
->sd
, &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
2592 TRUE
, options
& FILE_DIRECTORY_FILE
? FILE_ADD_SUBDIRECTORY
: FILE_ADD_FILE
, 0, NULL
,
2593 IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
2594 &granted_access
, &Status
)) {
2595 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2599 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2601 Status
= file_create2(Irp
, Vcb
, &fpus2
, parfileref
, options
, NULL
, 0, &newpar
, case_sensitive
, rollback
);
2603 if (!NT_SUCCESS(Status
)) {
2604 ERR("file_create2 returned %08x\n", Status
);
2605 ExFreePool(fpus2
.Buffer
);
2607 } else if (Status
!= STATUS_OBJECT_NAME_COLLISION
) {
2608 send_notification_fileref(newpar
, options
& FILE_DIRECTORY_FILE
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
2609 send_notification_fcb(newpar
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2612 ExFreePool(fpus2
.Buffer
);
2613 } else if (!NT_SUCCESS(Status
)) {
2614 ERR("open_fileref returned %08x\n", Status
);
2618 parfileref
= newpar
;
2619 *pparfileref
= parfileref
;
2621 if (parfileref
->fcb
->type
!= BTRFS_TYPE_FILE
&& parfileref
->fcb
->type
!= BTRFS_TYPE_SYMLINK
&& parfileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
2622 WARN("parent not file, directory, or symlink\n");
2623 free_fileref(parfileref
);
2624 return STATUS_INVALID_PARAMETER
;
2627 if (options
& FILE_DIRECTORY_FILE
) {
2628 WARN("tried to create directory as stream\n");
2629 free_fileref(parfileref
);
2630 return STATUS_INVALID_PARAMETER
;
2633 if (parfileref
->fcb
->atts
& FILE_ATTRIBUTE_READONLY
) {
2634 free_fileref(parfileref
);
2635 return STATUS_ACCESS_DENIED
;
2638 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2640 if (!SeAccessCheck(parfileref
->fcb
->sd
, &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
2641 TRUE
, FILE_WRITE_DATA
, 0, NULL
, IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
2642 &granted_access
, &Status
)) {
2643 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2644 free_fileref(parfileref
);
2648 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2650 if ((stream
->Length
== sizeof(DOSATTRIB
) - sizeof(WCHAR
) && RtlCompareMemory(stream
->Buffer
, DOSATTRIB
, stream
->Length
) == stream
->Length
) ||
2651 (stream
->Length
== sizeof(EA
) - sizeof(WCHAR
) && RtlCompareMemory(stream
->Buffer
, EA
, stream
->Length
) == stream
->Length
) ||
2652 (stream
->Length
== sizeof(reparse
) - sizeof(WCHAR
) && RtlCompareMemory(stream
->Buffer
, reparse
, stream
->Length
) == stream
->Length
) ||
2653 (stream
->Length
== sizeof(casesensitive_str
) - sizeof(WCHAR
) && RtlCompareMemory(stream
->Buffer
, casesensitive_str
, stream
->Length
) == stream
->Length
)) {
2654 free_fileref(parfileref
);
2655 return STATUS_OBJECT_NAME_INVALID
;
2658 fcb
= create_fcb(Vcb
, pool_type
);
2660 ERR("out of memory\n");
2661 free_fileref(parfileref
);
2662 return STATUS_INSUFFICIENT_RESOURCES
;
2667 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
2668 fcb
->Header
.AllocationSize
.QuadPart
= 0;
2669 fcb
->Header
.FileSize
.QuadPart
= 0;
2670 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
2672 #ifdef DEBUG_FCB_REFCOUNTS
2673 rc
= InterlockedIncrement(&parfileref
->fcb
->refcount
);
2674 WARN("fcb %p: refcount now %i (%S)\n", parfileref
->fcb
, rc
, file_desc_fileref(parfileref
));
2676 InterlockedIncrement(&parfileref
->fcb
->refcount
);
2678 fcb
->subvol
= parfileref
->fcb
->subvol
;
2679 fcb
->inode
= parfileref
->fcb
->inode
;
2680 fcb
->type
= parfileref
->fcb
->type
;
2684 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, stream
->Buffer
, stream
->Length
);
2685 if (!NT_SUCCESS(Status
)) {
2686 ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status
);
2688 free_fileref(parfileref
);
2692 fcb
->adsxattr
.Length
= (UINT16
)utf8len
+ sizeof(xapref
) - 1;
2693 fcb
->adsxattr
.MaximumLength
= fcb
->adsxattr
.Length
+ 1;
2694 fcb
->adsxattr
.Buffer
= ExAllocatePoolWithTag(pool_type
, fcb
->adsxattr
.MaximumLength
, ALLOC_TAG
);
2695 if (!fcb
->adsxattr
.Buffer
) {
2696 ERR("out of memory\n");
2698 free_fileref(parfileref
);
2699 return STATUS_INSUFFICIENT_RESOURCES
;
2702 RtlCopyMemory(fcb
->adsxattr
.Buffer
, xapref
, sizeof(xapref
) - 1);
2704 Status
= RtlUnicodeToUTF8N(&fcb
->adsxattr
.Buffer
[sizeof(xapref
) - 1], utf8len
, &utf8len
, stream
->Buffer
, stream
->Length
);
2705 if (!NT_SUCCESS(Status
)) {
2706 ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status
);
2708 free_fileref(parfileref
);
2712 fcb
->adsxattr
.Buffer
[fcb
->adsxattr
.Length
] = 0;
2714 TRACE("adsxattr = %s\n", fcb
->adsxattr
.Buffer
);
2716 fcb
->adshash
= calc_crc32c(0xfffffffe, (UINT8
*)fcb
->adsxattr
.Buffer
, fcb
->adsxattr
.Length
);
2717 TRACE("adshash = %08x\n", fcb
->adshash
);
2719 searchkey
.obj_id
= parfileref
->fcb
->inode
;
2720 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
2721 searchkey
.offset
= fcb
->adshash
;
2723 Status
= find_item(Vcb
, parfileref
->fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
2724 if (!NT_SUCCESS(Status
)) {
2725 ERR("find_item returned %08x\n", Status
);
2727 free_fileref(parfileref
);
2731 if (!keycmp(tp
.item
->key
, searchkey
))
2732 overhead
= tp
.item
->size
;
2736 fcb
->adsmaxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
) - (sizeof(DIR_ITEM
) - 1);
2738 if (utf8len
+ sizeof(xapref
) - 1 + overhead
> fcb
->adsmaxlen
) {
2739 WARN("not enough room for new DIR_ITEM (%u + %u > %u)", utf8len
+ sizeof(xapref
) - 1, overhead
, fcb
->adsmaxlen
);
2741 free_fileref(parfileref
);
2742 return STATUS_DISK_FULL
;
2744 fcb
->adsmaxlen
-= overhead
+ utf8len
+ sizeof(xapref
) - 1;
2746 fcb
->created
= TRUE
;
2747 fcb
->deleted
= TRUE
;
2749 acquire_fcb_lock_exclusive(Vcb
);
2750 InsertHeadList(&parfileref
->fcb
->list_entry
, &fcb
->list_entry
); // insert in list after parent fcb
2751 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
2752 parfileref
->fcb
->subvol
->fcbs_version
++;
2753 release_fcb_lock(Vcb
);
2755 mark_fcb_dirty(fcb
);
2757 fileref
= create_fileref(Vcb
);
2759 ERR("out of memory\n");
2761 free_fileref(parfileref
);
2762 return STATUS_INSUFFICIENT_RESOURCES
;
2767 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
2769 ERR("out of memory\n");
2770 reap_fileref(Vcb
, fileref
);
2771 free_fileref(parfileref
);
2772 return STATUS_INSUFFICIENT_RESOURCES
;
2775 RtlZeroMemory(dc
, sizeof(dir_child
));
2777 dc
->utf8
.MaximumLength
= dc
->utf8
.Length
= fcb
->adsxattr
.Length
+ 1 - sizeof(xapref
);
2778 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dc
->utf8
.MaximumLength
, ALLOC_TAG
);
2779 if (!dc
->utf8
.Buffer
) {
2780 ERR("out of memory\n");
2782 reap_fileref(Vcb
, fileref
);
2783 free_fileref(parfileref
);
2784 return STATUS_INSUFFICIENT_RESOURCES
;
2787 RtlCopyMemory(dc
->utf8
.Buffer
, &fcb
->adsxattr
.Buffer
[sizeof(xapref
) - 1], fcb
->adsxattr
.Length
+ 1 - sizeof(xapref
));
2789 dc
->name
.MaximumLength
= dc
->name
.Length
= stream
->Length
;
2790 dc
->name
.Buffer
= ExAllocatePoolWithTag(pool_type
, dc
->name
.MaximumLength
, ALLOC_TAG
);
2791 if (!dc
->name
.Buffer
) {
2792 ERR("out of memory\n");
2793 ExFreePool(dc
->utf8
.Buffer
);
2795 reap_fileref(Vcb
, fileref
);
2796 free_fileref(parfileref
);
2797 return STATUS_INSUFFICIENT_RESOURCES
;
2800 RtlCopyMemory(dc
->name
.Buffer
, stream
->Buffer
, stream
->Length
);
2802 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, &dc
->name
, TRUE
);
2803 if (!NT_SUCCESS(Status
)) {
2804 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
2805 ExFreePool(dc
->utf8
.Buffer
);
2806 ExFreePool(dc
->name
.Buffer
);
2808 reap_fileref(Vcb
, fileref
);
2809 free_fileref(parfileref
);
2813 KeQuerySystemTime(&time
);
2814 win_time_to_unix(time
, &now
);
2816 ExAcquireResourceExclusiveLite(&parfileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
2819 LIST_ENTRY
* le
= parfileref
->fcb
->dir_children_index
.Flink
;
2821 le
= parfileref
->fcb
->dir_children_index
.Flink
;
2823 while (le
!= &parfileref
->fcb
->dir_children_index
) {
2824 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
2826 if (dc2
->index
== 0) {
2827 if ((case_sensitive
&& dc2
->name
.Length
== dc
->name
.Length
&& RtlCompareMemory(dc2
->name
.Buffer
, dc
->name
.Buffer
, dc2
->name
.Length
) == dc2
->name
.Length
) ||
2828 (!case_sensitive
&& dc2
->name_uc
.Length
== dc
->name_uc
.Length
&& RtlCompareMemory(dc2
->name_uc
.Buffer
, dc
->name_uc
.Buffer
, dc2
->name_uc
.Length
) == dc2
->name_uc
.Length
)
2840 ExFreePool(dc
->utf8
.Buffer
);
2841 ExFreePool(dc
->name
.Buffer
);
2843 reap_fileref(Vcb
, fileref
);
2844 free_fileref(parfileref
);
2846 increase_fileref_refcount(existing_dc
->fileref
);
2847 *pfileref
= existing_dc
->fileref
;
2849 return STATUS_OBJECT_NAME_COLLISION
;
2852 dc
->fileref
= fileref
;
2854 fileref
->parent
= (struct _file_ref
*)parfileref
;
2855 fcb
->deleted
= FALSE
;
2857 InsertHeadList(&parfileref
->fcb
->dir_children_index
, &dc
->list_entry_index
);
2859 InsertTailList(&parfileref
->children
, &fileref
->list_entry
);
2861 ExReleaseResourceLite(&parfileref
->fcb
->nonpaged
->dir_children_lock
);
2863 mark_fileref_dirty(fileref
);
2865 parfileref
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2866 parfileref
->fcb
->inode_item
.sequence
++;
2867 parfileref
->fcb
->inode_item
.st_ctime
= now
;
2868 parfileref
->fcb
->inode_item_changed
= TRUE
;
2870 mark_fcb_dirty(parfileref
->fcb
);
2872 parfileref
->fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
2873 parfileref
->fcb
->subvol
->root_item
.ctime
= now
;
2875 increase_fileref_refcount(parfileref
);
2877 *pfileref
= fileref
;
2879 send_notification_fileref(parfileref
, FILE_NOTIFY_CHANGE_STREAM_NAME
, FILE_ACTION_ADDED_STREAM
, &fileref
->dc
->name
);
2881 return STATUS_SUCCESS
;
2884 // LXSS programs can be distinguished by the fact they have a NULL PEB.
2890 ZwQueryInformationProcess (
2891 _In_ HANDLE ProcessHandle
,
2892 _In_ PROCESSINFOCLASS ProcessInformationClass
,
2893 _Out_ PVOID ProcessInformation
,
2894 _In_ ULONG ProcessInformationLength
,
2895 _Out_opt_ PULONG ReturnLength
2898 static __inline BOOL
called_from_lxss() {
2900 PROCESS_BASIC_INFORMATION pbi
;
2903 Status
= ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation
, &pbi
, sizeof(pbi
), &retlen
);
2905 if (!NT_SUCCESS(Status
)) {
2906 ERR("ZwQueryInformationProcess returned %08x\n", Status
);
2910 return !pbi
.PebBaseAddress
;
2913 #define called_from_lxss() FALSE
2916 static NTSTATUS
file_create(PIRP Irp
, _Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
2917 PFILE_OBJECT FileObject
, file_ref
* related
, BOOL loaded_related
, PUNICODE_STRING fnus
, ULONG disposition
, ULONG options
,
2918 file_ref
** existing_fileref
, LIST_ENTRY
* rollback
) {
2920 file_ref
*fileref
, *parfileref
= NULL
;
2923 static const WCHAR datasuf
[] = {':','$','D','A','T','A',0};
2924 UNICODE_STRING dsus
, fpus
, stream
;
2925 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2926 POOL_TYPE pool_type
= IrpSp
->Flags
& SL_OPEN_PAGING_FILE
? NonPagedPool
: PagedPool
;
2928 ATOMIC_CREATE_ECP_CONTEXT
* acec
= NULL
;
2929 #ifdef DEBUG_FCB_REFCOUNTS
2933 TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp
, Vcb
, FileObject
, fnus
->Length
/ sizeof(WCHAR
), fnus
->Buffer
, disposition
, options
);
2936 return STATUS_MEDIA_WRITE_PROTECTED
;
2938 if (options
& FILE_DELETE_ON_CLOSE
&& IrpSp
->Parameters
.Create
.FileAttributes
& FILE_ATTRIBUTE_READONLY
)
2939 return STATUS_CANNOT_DELETE
;
2941 if (NT_SUCCESS(FsRtlGetEcpListFromIrp(Irp
, &ecp_list
)) && ecp_list
) {
2947 Status
= FsRtlGetNextExtraCreateParameter(ecp_list
, ctx
, &type
, &ctx
, &ctxsize
);
2949 if (NT_SUCCESS(Status
)) {
2950 if (RtlCompareMemory(&type
, &GUID_ECP_ATOMIC_CREATE
, sizeof(GUID
)) == sizeof(GUID
) && ctxsize
>= sizeof(ATOMIC_CREATE_ECP_CONTEXT
)) {
2955 } while (NT_SUCCESS(Status
));
2958 dsus
.Buffer
= (WCHAR
*)datasuf
;
2959 dsus
.Length
= dsus
.MaximumLength
= sizeof(datasuf
) - sizeof(WCHAR
);
2962 if (!loaded_related
) {
2963 Status
= open_fileref(Vcb
, &parfileref
, fnus
, related
, TRUE
, NULL
, NULL
, pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
2965 if (!NT_SUCCESS(Status
))
2968 parfileref
= related
;
2970 if (parfileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& (fnus
->Length
< sizeof(WCHAR
) || fnus
->Buffer
[0] != ':')) {
2971 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
2975 if (is_subvol_readonly(parfileref
->fcb
->subvol
, Irp
)) {
2976 Status
= STATUS_ACCESS_DENIED
;
2980 i
= (fnus
->Length
/ sizeof(WCHAR
))-1;
2981 while ((fnus
->Buffer
[i
] == '\\' || fnus
->Buffer
[i
] == '/') && i
> 0) { i
--; }
2985 while (i
> 0 && fnus
->Buffer
[i
-1] != '\\' && fnus
->Buffer
[i
-1] != '/') { i
--; }
2987 fpus
.MaximumLength
= (USHORT
)((j
- i
+ 2) * sizeof(WCHAR
));
2988 fpus
.Buffer
= ExAllocatePoolWithTag(pool_type
, fpus
.MaximumLength
, ALLOC_TAG
);
2990 ERR("out of memory\n");
2991 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2995 fpus
.Length
= (USHORT
)((j
- i
+ 1) * sizeof(WCHAR
));
2997 RtlCopyMemory(fpus
.Buffer
, &fnus
->Buffer
[i
], (j
- i
+ 1) * sizeof(WCHAR
));
2998 fpus
.Buffer
[j
- i
+ 1] = 0;
3000 if (fpus
.Length
> dsus
.Length
) { // check for :$DATA suffix
3003 lb
.Buffer
= &fpus
.Buffer
[(fpus
.Length
- dsus
.Length
)/sizeof(WCHAR
)];
3004 lb
.Length
= lb
.MaximumLength
= dsus
.Length
;
3006 TRACE("lb = %.*S\n", lb
.Length
/sizeof(WCHAR
), lb
.Buffer
);
3008 if (FsRtlAreNamesEqual(&dsus
, &lb
, TRUE
, NULL
)) {
3009 TRACE("ignoring :$DATA suffix\n");
3011 fpus
.Length
-= lb
.Length
;
3013 if (fpus
.Length
> sizeof(WCHAR
) && fpus
.Buffer
[(fpus
.Length
-1)/sizeof(WCHAR
)] == ':')
3014 fpus
.Length
-= sizeof(WCHAR
);
3016 TRACE("fpus = %.*S\n", fpus
.Length
/ sizeof(WCHAR
), fpus
.Buffer
);
3022 for (i
= 0; i
< fpus
.Length
/ sizeof(WCHAR
); i
++) {
3023 if (fpus
.Buffer
[i
] == ':') {
3024 stream
.Length
= (USHORT
)(fpus
.Length
- (i
* sizeof(WCHAR
)) - sizeof(WCHAR
));
3025 stream
.Buffer
= &fpus
.Buffer
[i
+1];
3027 fpus
.Length
= (USHORT
)(i
* sizeof(WCHAR
));
3032 if (stream
.Length
> 0) {
3033 Status
= create_stream(Vcb
, &fileref
, &parfileref
, &fpus
, &stream
, Irp
, options
, pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, rollback
);
3034 if (!NT_SUCCESS(Status
)) {
3035 ERR("create_stream returned %08x\n", Status
);
3039 IoSetShareAccess(IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
, IrpSp
->Parameters
.Create
.ShareAccess
,
3040 FileObject
, &fileref
->fcb
->share_access
);
3042 ACCESS_MASK granted_access
;
3044 if (!is_file_name_valid(&fpus
, FALSE
)) {
3045 Status
= STATUS_OBJECT_NAME_INVALID
;
3049 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3051 if (!SeAccessCheck(parfileref
->fcb
->sd
, &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
3052 TRUE
, options
& FILE_DIRECTORY_FILE
? FILE_ADD_SUBDIRECTORY
: FILE_ADD_FILE
, 0, NULL
,
3053 IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
3054 &granted_access
, &Status
)) {
3055 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3059 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3061 if (Irp
->AssociatedIrp
.SystemBuffer
&& IrpSp
->Parameters
.Create
.EaLength
> 0) {
3064 Status
= IoCheckEaBufferValidity(Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.Create
.EaLength
, &offset
);
3065 if (!NT_SUCCESS(Status
)) {
3066 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
3071 Status
= file_create2(Irp
, Vcb
, &fpus
, parfileref
, options
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.Create
.EaLength
,
3072 &fileref
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, rollback
);
3074 if (Status
== STATUS_OBJECT_NAME_COLLISION
) {
3075 *existing_fileref
= fileref
;
3077 } else if (!NT_SUCCESS(Status
)) {
3078 ERR("file_create2 returned %08x\n", Status
);
3082 IoSetShareAccess(IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
, IrpSp
->Parameters
.Create
.ShareAccess
, FileObject
, &fileref
->fcb
->share_access
);
3084 send_notification_fileref(fileref
, options
& FILE_DIRECTORY_FILE
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
3085 send_notification_fcb(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
3088 FileObject
->FsContext
= fileref
->fcb
;
3090 ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ccb
), ALLOC_TAG
);
3092 ERR("out of memory\n");
3093 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3094 fileref
->deleted
= TRUE
;
3095 fileref
->fcb
->deleted
= TRUE
;
3097 if (stream
.Length
== 0) {
3098 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
3099 parfileref
->fcb
->inode_item
.st_size
-= fileref
->dc
->utf8
.Length
* 2;
3100 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
3103 free_fileref(fileref
);
3107 RtlZeroMemory(ccb
, sizeof(*ccb
));
3109 ccb
->fileref
= fileref
;
3111 ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
3112 ccb
->NodeSize
= sizeof(*ccb
);
3113 ccb
->disposition
= disposition
;
3114 ccb
->options
= options
;
3115 ccb
->query_dir_offset
= 0;
3116 RtlInitUnicodeString(&ccb
->query_string
, NULL
);
3117 ccb
->has_wildcard
= FALSE
;
3118 ccb
->specific_file
= FALSE
;
3119 ccb
->access
= IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
;
3120 ccb
->case_sensitive
= IrpSp
->Flags
& SL_CASE_SENSITIVE
;
3121 ccb
->reserving
= FALSE
;
3122 ccb
->lxss
= called_from_lxss();
3124 #ifdef DEBUG_FCB_REFCOUNTS
3125 oc
= InterlockedIncrement(&fileref
->open_count
);
3126 ERR("fileref %p: open_count now %i\n", fileref
, oc
);
3128 InterlockedIncrement(&fileref
->open_count
);
3130 InterlockedIncrement(&Vcb
->open_files
);
3132 FileObject
->FsContext2
= ccb
;
3134 FileObject
->SectionObjectPointer
= &fileref
->fcb
->nonpaged
->segment_object
;
3136 // FIXME - ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT
3137 if (acec
&& acec
->InFlags
& ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED
) {
3138 if (acec
->ReparseBufferLength
> sizeof(UINT32
) && *(UINT32
*)acec
->ReparseBuffer
== IO_REPARSE_TAG_SYMLINK
) {
3139 fileref
->fcb
->inode_item
.st_mode
&= ~(__S_IFIFO
| __S_IFCHR
| __S_IFBLK
| __S_IFSOCK
);
3140 fileref
->fcb
->type
= BTRFS_TYPE_FILE
;
3143 if (fileref
->fcb
->type
== BTRFS_TYPE_SOCKET
|| fileref
->fcb
->type
== BTRFS_TYPE_FIFO
||
3144 fileref
->fcb
->type
== BTRFS_TYPE_CHARDEV
|| fileref
->fcb
->type
== BTRFS_TYPE_BLOCKDEV
) {
3145 // NOP. If called from LXSS, humour it - we hardcode the values elsewhere.
3147 Status
= set_reparse_point2(fileref
->fcb
, acec
->ReparseBuffer
, acec
->ReparseBufferLength
, NULL
, NULL
, Irp
, rollback
);
3148 if (!NT_SUCCESS(Status
)) {
3149 ERR("set_reparse_point2 returned %08x\n", Status
);
3150 fileref
->deleted
= TRUE
;
3151 fileref
->fcb
->deleted
= TRUE
;
3153 if (stream
.Length
== 0) {
3154 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
3155 parfileref
->fcb
->inode_item
.st_size
-= fileref
->dc
->utf8
.Length
* 2;
3156 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
3159 free_fileref(fileref
);
3164 acec
->OutFlags
|= ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET
;
3167 fileref
->dc
->type
= fileref
->fcb
->type
;
3173 ExFreePool(fpus
.Buffer
);
3176 if (parfileref
&& !loaded_related
)
3177 free_fileref(parfileref
);
3182 static __inline
void debug_create_options(ULONG RequestedOptions
) {
3183 if (RequestedOptions
!= 0) {
3184 ULONG options
= RequestedOptions
;
3186 TRACE("requested options:\n");
3188 if (options
& FILE_DIRECTORY_FILE
) {
3189 TRACE(" FILE_DIRECTORY_FILE\n");
3190 options
&= ~FILE_DIRECTORY_FILE
;
3193 if (options
& FILE_WRITE_THROUGH
) {
3194 TRACE(" FILE_WRITE_THROUGH\n");
3195 options
&= ~FILE_WRITE_THROUGH
;
3198 if (options
& FILE_SEQUENTIAL_ONLY
) {
3199 TRACE(" FILE_SEQUENTIAL_ONLY\n");
3200 options
&= ~FILE_SEQUENTIAL_ONLY
;
3203 if (options
& FILE_NO_INTERMEDIATE_BUFFERING
) {
3204 TRACE(" FILE_NO_INTERMEDIATE_BUFFERING\n");
3205 options
&= ~FILE_NO_INTERMEDIATE_BUFFERING
;
3208 if (options
& FILE_SYNCHRONOUS_IO_ALERT
) {
3209 TRACE(" FILE_SYNCHRONOUS_IO_ALERT\n");
3210 options
&= ~FILE_SYNCHRONOUS_IO_ALERT
;
3213 if (options
& FILE_SYNCHRONOUS_IO_NONALERT
) {
3214 TRACE(" FILE_SYNCHRONOUS_IO_NONALERT\n");
3215 options
&= ~FILE_SYNCHRONOUS_IO_NONALERT
;
3218 if (options
& FILE_NON_DIRECTORY_FILE
) {
3219 TRACE(" FILE_NON_DIRECTORY_FILE\n");
3220 options
&= ~FILE_NON_DIRECTORY_FILE
;
3223 if (options
& FILE_CREATE_TREE_CONNECTION
) {
3224 TRACE(" FILE_CREATE_TREE_CONNECTION\n");
3225 options
&= ~FILE_CREATE_TREE_CONNECTION
;
3228 if (options
& FILE_COMPLETE_IF_OPLOCKED
) {
3229 TRACE(" FILE_COMPLETE_IF_OPLOCKED\n");
3230 options
&= ~FILE_COMPLETE_IF_OPLOCKED
;
3233 if (options
& FILE_NO_EA_KNOWLEDGE
) {
3234 TRACE(" FILE_NO_EA_KNOWLEDGE\n");
3235 options
&= ~FILE_NO_EA_KNOWLEDGE
;
3238 if (options
& FILE_OPEN_REMOTE_INSTANCE
) {
3239 TRACE(" FILE_OPEN_REMOTE_INSTANCE\n");
3240 options
&= ~FILE_OPEN_REMOTE_INSTANCE
;
3243 if (options
& FILE_RANDOM_ACCESS
) {
3244 TRACE(" FILE_RANDOM_ACCESS\n");
3245 options
&= ~FILE_RANDOM_ACCESS
;
3248 if (options
& FILE_DELETE_ON_CLOSE
) {
3249 TRACE(" FILE_DELETE_ON_CLOSE\n");
3250 options
&= ~FILE_DELETE_ON_CLOSE
;
3253 if (options
& FILE_OPEN_BY_FILE_ID
) {
3254 TRACE(" FILE_OPEN_BY_FILE_ID\n");
3255 options
&= ~FILE_OPEN_BY_FILE_ID
;
3258 if (options
& FILE_OPEN_FOR_BACKUP_INTENT
) {
3259 TRACE(" FILE_OPEN_FOR_BACKUP_INTENT\n");
3260 options
&= ~FILE_OPEN_FOR_BACKUP_INTENT
;
3263 if (options
& FILE_NO_COMPRESSION
) {
3264 TRACE(" FILE_NO_COMPRESSION\n");
3265 options
&= ~FILE_NO_COMPRESSION
;
3268 #if NTDDI_VERSION >= NTDDI_WIN7
3269 if (options
& FILE_OPEN_REQUIRING_OPLOCK
) {
3270 TRACE(" FILE_OPEN_REQUIRING_OPLOCK\n");
3271 options
&= ~FILE_OPEN_REQUIRING_OPLOCK
;
3274 if (options
& FILE_DISALLOW_EXCLUSIVE
) {
3275 TRACE(" FILE_DISALLOW_EXCLUSIVE\n");
3276 options
&= ~FILE_DISALLOW_EXCLUSIVE
;
3280 if (options
& FILE_RESERVE_OPFILTER
) {
3281 TRACE(" FILE_RESERVE_OPFILTER\n");
3282 options
&= ~FILE_RESERVE_OPFILTER
;
3285 if (options
& FILE_OPEN_REPARSE_POINT
) {
3286 TRACE(" FILE_OPEN_REPARSE_POINT\n");
3287 options
&= ~FILE_OPEN_REPARSE_POINT
;
3290 if (options
& FILE_OPEN_NO_RECALL
) {
3291 TRACE(" FILE_OPEN_NO_RECALL\n");
3292 options
&= ~FILE_OPEN_NO_RECALL
;
3295 if (options
& FILE_OPEN_FOR_FREE_SPACE_QUERY
) {
3296 TRACE(" FILE_OPEN_FOR_FREE_SPACE_QUERY\n");
3297 options
&= ~FILE_OPEN_FOR_FREE_SPACE_QUERY
;
3301 TRACE(" unknown options: %x\n", options
);
3303 TRACE("requested options: (none)\n");
3307 static NTSTATUS
get_reparse_block(fcb
* fcb
, UINT8
** data
) {
3310 if (fcb
->type
== BTRFS_TYPE_FILE
|| fcb
->type
== BTRFS_TYPE_SYMLINK
) {
3311 ULONG size
, bytes_read
, i
;
3313 if (fcb
->type
== BTRFS_TYPE_FILE
&& fcb
->inode_item
.st_size
< sizeof(ULONG
)) {
3314 WARN("file was too short to be a reparse point\n");
3315 return STATUS_INVALID_PARAMETER
;
3318 // 0x10007 = 0xffff (maximum length of data buffer) + 8 bytes header
3319 size
= (ULONG
)min(0x10007, fcb
->inode_item
.st_size
);
3322 return STATUS_INVALID_PARAMETER
;
3324 *data
= ExAllocatePoolWithTag(PagedPool
, size
, ALLOC_TAG
);
3326 ERR("out of memory\n");
3327 return STATUS_INSUFFICIENT_RESOURCES
;
3330 Status
= read_file(fcb
, *data
, 0, size
, &bytes_read
, NULL
);
3331 if (!NT_SUCCESS(Status
)) {
3332 ERR("read_file_fcb returned %08x\n", Status
);
3337 if (fcb
->type
== BTRFS_TYPE_SYMLINK
) {
3338 ULONG stringlen
, reqlen
;
3339 UINT16 subnamelen
, printnamelen
;
3340 REPARSE_DATA_BUFFER
* rdb
;
3342 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, (char*)*data
, bytes_read
);
3343 if (!NT_SUCCESS(Status
)) {
3344 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
3349 subnamelen
= printnamelen
= (USHORT
)stringlen
;
3351 reqlen
= offsetof(REPARSE_DATA_BUFFER
, SymbolicLinkReparseBuffer
.PathBuffer
) + subnamelen
+ printnamelen
;
3353 rdb
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
3356 ERR("out of memory\n");
3358 return STATUS_INSUFFICIENT_RESOURCES
;
3361 rdb
->ReparseTag
= IO_REPARSE_TAG_SYMLINK
;
3362 rdb
->ReparseDataLength
= (USHORT
)(reqlen
- offsetof(REPARSE_DATA_BUFFER
, SymbolicLinkReparseBuffer
));
3365 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
= 0;
3366 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameLength
= subnamelen
;
3367 rdb
->SymbolicLinkReparseBuffer
.PrintNameOffset
= subnamelen
;
3368 rdb
->SymbolicLinkReparseBuffer
.PrintNameLength
= printnamelen
;
3369 rdb
->SymbolicLinkReparseBuffer
.Flags
= SYMLINK_FLAG_RELATIVE
;
3371 Status
= RtlUTF8ToUnicodeN(&rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)],
3372 stringlen
, &stringlen
, (char*)*data
, size
);
3374 if (!NT_SUCCESS(Status
)) {
3375 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
3381 for (i
= 0; i
< stringlen
/ sizeof(WCHAR
); i
++) {
3382 if (rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[(rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)) + i
] == '/')
3383 rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[(rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)) + i
] = '\\';
3386 RtlCopyMemory(&rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.PrintNameOffset
/ sizeof(WCHAR
)],
3387 &rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)],
3388 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameLength
);
3392 *data
= (UINT8
*)rdb
;
3394 Status
= FsRtlValidateReparsePointBuffer(bytes_read
, (REPARSE_DATA_BUFFER
*)*data
);
3395 if (!NT_SUCCESS(Status
)) {
3396 ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status
);
3401 } else if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
3402 if (!fcb
->reparse_xattr
.Buffer
|| fcb
->reparse_xattr
.Length
== 0)
3403 return STATUS_INTERNAL_ERROR
;
3405 if (fcb
->reparse_xattr
.Length
< sizeof(ULONG
)) {
3406 WARN("xattr was too short to be a reparse point\n");
3407 return STATUS_INTERNAL_ERROR
;
3410 Status
= FsRtlValidateReparsePointBuffer(fcb
->reparse_xattr
.Length
, (REPARSE_DATA_BUFFER
*)fcb
->reparse_xattr
.Buffer
);
3411 if (!NT_SUCCESS(Status
)) {
3412 ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status
);
3416 *data
= ExAllocatePoolWithTag(PagedPool
, fcb
->reparse_xattr
.Length
, ALLOC_TAG
);
3418 ERR("out of memory\n");
3419 return STATUS_INSUFFICIENT_RESOURCES
;
3422 RtlCopyMemory(*data
, fcb
->reparse_xattr
.Buffer
, fcb
->reparse_xattr
.Length
);
3424 return STATUS_INVALID_PARAMETER
;
3426 return STATUS_SUCCESS
;
3429 static void fcb_load_csums(_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, fcb
* fcb
, PIRP Irp
) {
3433 if (fcb
->csum_loaded
)
3436 if (IsListEmpty(&fcb
->extents
) || fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)
3439 le
= fcb
->extents
.Flink
;
3440 while (le
!= &fcb
->extents
) {
3441 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3443 if (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
) {
3444 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)&ext
->extent_data
.data
[0];
3447 len
= (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
? ed2
->num_bytes
: ed2
->size
) / Vcb
->superblock
.sector_size
;
3449 ext
->csum
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)(len
* sizeof(UINT32
)), ALLOC_TAG
);
3451 ERR("out of memory\n");
3455 Status
= load_csum(Vcb
, ext
->csum
, ed2
->address
+ (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
? ed2
->offset
: 0), len
, Irp
);
3457 if (!NT_SUCCESS(Status
)) {
3458 ERR("load_csum returned %08x\n", Status
);
3467 fcb
->csum_loaded
= TRUE
;
3470 static NTSTATUS
open_file2(device_extension
* Vcb
, ULONG RequestedDisposition
, POOL_TYPE pool_type
, file_ref
* fileref
, ACCESS_MASK
* granted_access
,
3471 PFILE_OBJECT FileObject
, UNICODE_STRING
* fn
, ULONG options
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3476 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3478 if (RequestedDisposition
== FILE_SUPERSEDE
|| RequestedDisposition
== FILE_OVERWRITE
|| RequestedDisposition
== FILE_OVERWRITE_IF
) {
3484 if (fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
|| is_subvol_readonly(fileref
->fcb
->subvol
, Irp
)) {
3485 free_fileref(fileref
);
3487 return STATUS_ACCESS_DENIED
;
3490 if (Vcb
->readonly
) {
3491 free_fileref(fileref
);
3493 return STATUS_MEDIA_WRITE_PROTECTED
;
3497 if (!MmCanFileBeTruncated(&fileref
->fcb
->nonpaged
->segment_object
, &zero
)) {
3498 free_fileref(fileref
);
3500 return STATUS_USER_MAPPED_FILE
;
3504 if (IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
!= 0) {
3505 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3507 if (!SeAccessCheck((fileref
->fcb
->ads
|| fileref
->fcb
== Vcb
->dummy_fcb
) ? fileref
->parent
->fcb
->sd
: fileref
->fcb
->sd
,
3508 &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
3509 TRUE
, IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
, 0, NULL
,
3510 IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
3511 granted_access
, &Status
)) {
3512 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3513 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
3515 free_fileref(fileref
);
3520 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3522 *granted_access
= 0;
3524 TRACE("deleted = %s\n", fileref
->deleted
? "TRUE" : "FALSE");
3528 if (sf
->delete_on_close
) {
3529 TRACE("could not open as deletion pending\n");
3531 free_fileref(fileref
);
3533 return STATUS_DELETE_PENDING
;
3538 readonly
= (!fileref
->fcb
->ads
&& fileref
->fcb
->atts
& FILE_ATTRIBUTE_READONLY
) || (fileref
->fcb
->ads
&& fileref
->parent
->fcb
->atts
& FILE_ATTRIBUTE_READONLY
) ||
3539 is_subvol_readonly(fileref
->fcb
->subvol
, Irp
) || fileref
->fcb
== Vcb
->dummy_fcb
|| Vcb
->readonly
;
3541 if (options
& FILE_DELETE_ON_CLOSE
&& (fileref
== Vcb
->root_fileref
|| readonly
)) {
3542 free_fileref(fileref
);
3544 return STATUS_CANNOT_DELETE
;
3548 ACCESS_MASK allowed
;
3550 allowed
= READ_CONTROL
| SYNCHRONIZE
| ACCESS_SYSTEM_SECURITY
| FILE_READ_DATA
|
3551 FILE_READ_EA
| FILE_READ_ATTRIBUTES
| FILE_EXECUTE
| FILE_LIST_DIRECTORY
|
3554 if (!Vcb
->readonly
&& (fileref
->fcb
== Vcb
->dummy_fcb
|| fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
))
3557 if (fileref
->fcb
!= Vcb
->dummy_fcb
&& !is_subvol_readonly(fileref
->fcb
->subvol
, Irp
) && !Vcb
->readonly
) {
3558 allowed
|= DELETE
| WRITE_OWNER
| WRITE_DAC
| FILE_WRITE_EA
| FILE_WRITE_ATTRIBUTES
;
3560 if (!fileref
->fcb
->ads
&& fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
3561 allowed
|= FILE_ADD_SUBDIRECTORY
| FILE_ADD_FILE
| FILE_DELETE_CHILD
;
3562 } else if (fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
&& is_subvol_readonly(fileref
->fcb
->subvol
, Irp
) && !Vcb
->readonly
) {
3563 // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
3565 allowed
|= FILE_WRITE_ATTRIBUTES
;
3568 if (IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
& MAXIMUM_ALLOWED
) {
3569 *granted_access
&= allowed
;
3570 IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->PreviouslyGrantedAccess
&= allowed
;
3571 } else if (*granted_access
& ~allowed
) {
3572 free_fileref(fileref
);
3574 return Vcb
->readonly
? STATUS_MEDIA_WRITE_PROTECTED
: STATUS_ACCESS_DENIED
;
3578 if ((fileref
->fcb
->type
== BTRFS_TYPE_SYMLINK
|| fileref
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
) && !(options
& FILE_OPEN_REPARSE_POINT
)) {
3579 REPARSE_DATA_BUFFER
* data
;
3581 /* How reparse points work from the point of view of the filesystem appears to
3582 * undocumented. When returning STATUS_REPARSE, MSDN encourages us to return
3583 * IO_REPARSE in Irp->IoStatus.Information, but that means we have to do our own
3584 * translation. If we instead return the reparse tag in Information, and store
3585 * a pointer to the reparse data buffer in Irp->Tail.Overlay.AuxiliaryBuffer,
3586 * IopSymlinkProcessReparse will do the translation for us. */
3588 Status
= get_reparse_block(fileref
->fcb
, (UINT8
**)&data
);
3589 if (!NT_SUCCESS(Status
)) {
3590 ERR("get_reparse_block returned %08x\n", Status
);
3591 Status
= STATUS_SUCCESS
;
3593 Irp
->IoStatus
.Information
= data
->ReparseTag
;
3595 if (fn
->Buffer
[(fn
->Length
/ sizeof(WCHAR
)) - 1] == '\\')
3596 data
->Reserved
= sizeof(WCHAR
);
3598 Irp
->Tail
.Overlay
.AuxiliaryBuffer
= (void*)data
;
3600 free_fileref(fileref
);
3602 return STATUS_REPARSE
;
3606 if (fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
&& !fileref
->fcb
->ads
) {
3607 if (options
& FILE_NON_DIRECTORY_FILE
&& !(fileref
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
)) {
3608 free_fileref(fileref
);
3610 return STATUS_FILE_IS_A_DIRECTORY
;
3612 } else if (options
& FILE_DIRECTORY_FILE
) {
3613 TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref
->fcb
->type
, file_desc_fileref(fileref
));
3615 free_fileref(fileref
);
3617 return STATUS_NOT_A_DIRECTORY
;
3620 if (fileref
->open_count
> 0) {
3621 Status
= IoCheckShareAccess(*granted_access
, IrpSp
->Parameters
.Create
.ShareAccess
, FileObject
, &fileref
->fcb
->share_access
, FALSE
);
3623 if (!NT_SUCCESS(Status
)) {
3624 if (Status
== STATUS_SHARING_VIOLATION
)
3625 TRACE("IoCheckShareAccess failed, returning %08x\n", Status
);
3627 WARN("IoCheckShareAccess failed, returning %08x\n", Status
);
3629 free_fileref(fileref
);
3634 IoUpdateShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3636 IoSetShareAccess(*granted_access
, IrpSp
->Parameters
.Create
.ShareAccess
, FileObject
, &fileref
->fcb
->share_access
);
3638 if (*granted_access
& FILE_WRITE_DATA
|| options
& FILE_DELETE_ON_CLOSE
) {
3639 if (!MmFlushImageSection(&fileref
->fcb
->nonpaged
->segment_object
, MmFlushForWrite
)) {
3641 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3643 free_fileref(fileref
);
3645 return (options
& FILE_DELETE_ON_CLOSE
) ? STATUS_CANNOT_DELETE
: STATUS_SHARING_VIOLATION
;
3649 if (RequestedDisposition
== FILE_OVERWRITE
|| RequestedDisposition
== FILE_OVERWRITE_IF
|| RequestedDisposition
== FILE_SUPERSEDE
) {
3650 ULONG defda
, oldatts
, filter
;
3654 if ((RequestedDisposition
== FILE_OVERWRITE
|| RequestedDisposition
== FILE_OVERWRITE_IF
) && readonly
) {
3655 WARN("cannot overwrite readonly file\n");
3657 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3659 free_fileref(fileref
);
3661 return STATUS_ACCESS_DENIED
;
3664 if (!fileref
->fcb
->ads
&& (IrpSp
->Parameters
.Create
.FileAttributes
& (FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
)) != ((fileref
->fcb
->atts
& (FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
)))) {
3665 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3667 free_fileref(fileref
);
3669 return STATUS_ACCESS_DENIED
;
3672 if (fileref
->fcb
->ads
) {
3673 Status
= stream_set_end_of_file_information(Vcb
, 0, fileref
->fcb
, fileref
, FALSE
);
3674 if (!NT_SUCCESS(Status
)) {
3675 ERR("stream_set_end_of_file_information returned %08x\n", Status
);
3677 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3679 free_fileref(fileref
);
3684 Status
= truncate_file(fileref
->fcb
, 0, Irp
, rollback
);
3685 if (!NT_SUCCESS(Status
)) {
3686 ERR("truncate_file returned %08x\n", Status
);
3688 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3690 free_fileref(fileref
);
3696 if (Irp
->Overlay
.AllocationSize
.QuadPart
> 0) {
3697 Status
= extend_file(fileref
->fcb
, fileref
, Irp
->Overlay
.AllocationSize
.QuadPart
, TRUE
, NULL
, rollback
);
3699 if (!NT_SUCCESS(Status
)) {
3700 ERR("extend_file returned %08x\n", Status
);
3702 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3704 free_fileref(fileref
);
3710 if (!fileref
->fcb
->ads
) {
3713 if (Irp
->AssociatedIrp
.SystemBuffer
&& IrpSp
->Parameters
.Create
.EaLength
> 0) {
3715 FILE_FULL_EA_INFORMATION
* eainfo
;
3717 Status
= IoCheckEaBufferValidity(Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.Create
.EaLength
, &offset
);
3718 if (!NT_SUCCESS(Status
)) {
3719 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
3721 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3723 free_fileref(fileref
);
3728 fileref
->fcb
->ealen
= 4;
3730 // capitalize EA name
3731 eainfo
= Irp
->AssociatedIrp
.SystemBuffer
;
3735 s
.Length
= s
.MaximumLength
= eainfo
->EaNameLength
;
3736 s
.Buffer
= eainfo
->EaName
;
3738 RtlUpperString(&s
, &s
);
3740 fileref
->fcb
->ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
3742 if (eainfo
->NextEntryOffset
== 0)
3745 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
3748 if (fileref
->fcb
->ea_xattr
.Buffer
)
3749 ExFreePool(fileref
->fcb
->ea_xattr
.Buffer
);
3751 fileref
->fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(pool_type
, IrpSp
->Parameters
.Create
.EaLength
, ALLOC_TAG
);
3752 if (!fileref
->fcb
->ea_xattr
.Buffer
) {
3753 ERR("out of memory\n");
3755 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3757 free_fileref(fileref
);
3759 return STATUS_INSUFFICIENT_RESOURCES
;
3762 fileref
->fcb
->ea_xattr
.Length
= fileref
->fcb
->ea_xattr
.MaximumLength
= (USHORT
)IrpSp
->Parameters
.Create
.EaLength
;
3763 RtlCopyMemory(fileref
->fcb
->ea_xattr
.Buffer
, Irp
->AssociatedIrp
.SystemBuffer
, fileref
->fcb
->ea_xattr
.Length
);
3765 if (fileref
->fcb
->ea_xattr
.Length
> 0) {
3766 ExFreePool(fileref
->fcb
->ea_xattr
.Buffer
);
3767 fileref
->fcb
->ea_xattr
.Buffer
= NULL
;
3768 fileref
->fcb
->ea_xattr
.Length
= fileref
->fcb
->ea_xattr
.MaximumLength
= 0;
3770 fileref
->fcb
->ea_changed
= TRUE
;
3771 fileref
->fcb
->ealen
= 0;
3775 // remove streams and send notifications
3776 le
= fileref
->fcb
->dir_children_index
.Flink
;
3777 while (le
!= &fileref
->fcb
->dir_children_index
) {
3778 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
3779 LIST_ENTRY
* le2
= le
->Flink
;
3781 if (dc
->index
== 0) {
3785 Status
= open_fileref_child(Vcb
, fileref
, &dc
->name
, TRUE
, TRUE
, TRUE
, PagedPool
, &fr2
, NULL
);
3786 if (!NT_SUCCESS(Status
))
3787 WARN("open_fileref_child returned %08x\n", Status
);
3791 send_notification_fcb(fileref
, FILE_NOTIFY_CHANGE_STREAM_NAME
, FILE_ACTION_REMOVED_STREAM
, &dc
->name
);
3793 Status
= delete_fileref(dc
->fileref
, NULL
, FALSE
, NULL
, rollback
);
3794 if (!NT_SUCCESS(Status
)) {
3795 ERR("delete_fileref returned %08x\n", Status
);
3797 free_fileref(fileref
);
3809 KeQuerySystemTime(&time
);
3810 win_time_to_unix(time
, &now
);
3812 filter
= FILE_NOTIFY_CHANGE_SIZE
| FILE_NOTIFY_CHANGE_LAST_WRITE
;
3814 if (fileref
->fcb
->ads
) {
3815 fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
3816 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
3817 mark_fcb_dirty(fileref
->parent
->fcb
);
3819 send_notification_fcb(fileref
->parent
, filter
, FILE_ACTION_MODIFIED
, &fileref
->dc
->name
);
3821 mark_fcb_dirty(fileref
->fcb
);
3823 oldatts
= fileref
->fcb
->atts
;
3825 defda
= get_file_attributes(Vcb
, fileref
->fcb
->subvol
, fileref
->fcb
->inode
, fileref
->fcb
->type
,
3826 fileref
->dc
&& fileref
->dc
->name
.Length
>= sizeof(WCHAR
) && fileref
->dc
->name
.Buffer
[0] == '.', TRUE
, Irp
);
3828 if (RequestedDisposition
== FILE_SUPERSEDE
)
3829 fileref
->fcb
->atts
= IrpSp
->Parameters
.Create
.FileAttributes
| FILE_ATTRIBUTE_ARCHIVE
;
3831 fileref
->fcb
->atts
|= IrpSp
->Parameters
.Create
.FileAttributes
| FILE_ATTRIBUTE_ARCHIVE
;
3833 if (fileref
->fcb
->atts
!= oldatts
) {
3834 fileref
->fcb
->atts_changed
= TRUE
;
3835 fileref
->fcb
->atts_deleted
= IrpSp
->Parameters
.Create
.FileAttributes
== defda
;
3836 filter
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
3839 fileref
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
3840 fileref
->fcb
->inode_item
.sequence
++;
3841 fileref
->fcb
->inode_item
.st_ctime
= now
;
3842 fileref
->fcb
->inode_item
.st_mtime
= now
;
3843 fileref
->fcb
->inode_item_changed
= TRUE
;
3845 send_notification_fcb(fileref
, filter
, FILE_ACTION_MODIFIED
, NULL
);
3848 if (options
& FILE_NO_EA_KNOWLEDGE
&& fileref
->fcb
->ea_xattr
.Length
> 0) {
3849 FILE_FULL_EA_INFORMATION
* ffei
= (FILE_FULL_EA_INFORMATION
*)fileref
->fcb
->ea_xattr
.Buffer
;
3852 if (ffei
->Flags
& FILE_NEED_EA
) {
3853 WARN("returning STATUS_ACCESS_DENIED as no EA knowledge\n");
3855 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3857 free_fileref(fileref
);
3859 return STATUS_ACCESS_DENIED
;
3862 if (ffei
->NextEntryOffset
== 0)
3865 ffei
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ffei
) + ffei
->NextEntryOffset
);
3870 FileObject
->FsContext
= fileref
->fcb
;
3872 ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ccb
), ALLOC_TAG
);
3874 ERR("out of memory\n");
3876 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3878 free_fileref(fileref
);
3880 return STATUS_INSUFFICIENT_RESOURCES
;
3883 RtlZeroMemory(ccb
, sizeof(*ccb
));
3885 ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
3886 ccb
->NodeSize
= sizeof(*ccb
);
3887 ccb
->disposition
= RequestedDisposition
;
3888 ccb
->options
= options
;
3889 ccb
->query_dir_offset
= 0;
3890 RtlInitUnicodeString(&ccb
->query_string
, NULL
);
3891 ccb
->has_wildcard
= FALSE
;
3892 ccb
->specific_file
= FALSE
;
3893 ccb
->access
= *granted_access
;
3894 ccb
->case_sensitive
= IrpSp
->Flags
& SL_CASE_SENSITIVE
;
3895 ccb
->reserving
= FALSE
;
3896 ccb
->lxss
= called_from_lxss();
3898 ccb
->fileref
= fileref
;
3900 FileObject
->FsContext2
= ccb
;
3901 FileObject
->SectionObjectPointer
= &fileref
->fcb
->nonpaged
->segment_object
;
3903 if (NT_SUCCESS(Status
)) {
3904 switch (RequestedDisposition
) {
3905 case FILE_SUPERSEDE
:
3906 Irp
->IoStatus
.Information
= FILE_SUPERSEDED
;
3911 Irp
->IoStatus
.Information
= FILE_OPENED
;
3914 case FILE_OVERWRITE
:
3915 case FILE_OVERWRITE_IF
:
3916 Irp
->IoStatus
.Information
= FILE_OVERWRITTEN
;
3921 // Make sure paging files don't have any extents marked as being prealloc,
3922 // as this would mean we'd have to lock exclusively when writing.
3923 if (IrpSp
->Flags
& SL_OPEN_PAGING_FILE
) {
3925 BOOL changed
= FALSE
;
3927 ExAcquireResourceExclusiveLite(fileref
->fcb
->Header
.Resource
, TRUE
);
3929 le
= fileref
->fcb
->extents
.Flink
;
3931 while (le
!= &fileref
->fcb
->extents
) {
3932 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3934 if (ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) {
3935 ext
->extent_data
.type
= EXTENT_TYPE_REGULAR
;
3942 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
3945 fileref
->fcb
->extents_changed
= TRUE
;
3946 mark_fcb_dirty(fileref
->fcb
);
3949 fileref
->fcb
->Header
.Flags2
|= FSRTL_FLAG2_IS_PAGING_FILE
;
3950 Vcb
->disallow_dismount
= TRUE
;
3953 #ifdef DEBUG_FCB_REFCOUNTS
3954 LONG oc
= InterlockedIncrement(&fileref
->open_count
);
3955 ERR("fileref %p: open_count now %i\n", fileref
, oc
);
3957 InterlockedIncrement(&fileref
->open_count
);
3959 InterlockedIncrement(&Vcb
->open_files
);
3961 return STATUS_SUCCESS
;
3964 NTSTATUS
open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
3965 root
* subvol
, UINT64 inode
, file_ref
** pfr
, PIRP Irp
) {
3969 UNICODE_STRING name
;
3970 BOOL hl_alloc
= FALSE
;
3971 file_ref
*parfr
, *fr
;
3973 Status
= open_fcb(Vcb
, subvol
, inode
, 0, NULL
, TRUE
, NULL
, &fcb
, PagedPool
, Irp
);
3974 if (!NT_SUCCESS(Status
)) {
3975 ERR("open_fcb returned %08x\n", Status
);
3979 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
3981 if (fcb
->inode_item
.st_nlink
== 0 || fcb
->deleted
) {
3982 ExReleaseResourceLite(fcb
->Header
.Resource
);
3984 return STATUS_OBJECT_NAME_NOT_FOUND
;
3988 *pfr
= fcb
->fileref
;
3989 increase_fileref_refcount(fcb
->fileref
);
3991 ExReleaseResourceLite(fcb
->Header
.Resource
);
3992 return STATUS_SUCCESS
;
3995 if (IsListEmpty(&fcb
->hardlinks
)) {
3996 ExReleaseResourceLite(fcb
->Header
.Resource
);
3998 ExAcquireResourceSharedLite(&Vcb
->dirty_filerefs_lock
, TRUE
);
4000 if (!IsListEmpty(&Vcb
->dirty_filerefs
)) {
4001 LIST_ENTRY
* le
= Vcb
->dirty_filerefs
.Flink
;
4002 while (le
!= &Vcb
->dirty_filerefs
) {
4003 file_ref
* fr
= CONTAINING_RECORD(le
, file_ref
, list_entry_dirty
);
4005 if (fr
->fcb
== fcb
) {
4006 ExReleaseResourceLite(&Vcb
->dirty_filerefs_lock
);
4007 increase_fileref_refcount(fr
);
4010 return STATUS_SUCCESS
;
4017 ExReleaseResourceLite(&Vcb
->dirty_filerefs_lock
);
4023 searchkey
.obj_id
= fcb
->inode
;
4024 searchkey
.obj_type
= TYPE_INODE_REF
;
4025 searchkey
.offset
= 0;
4027 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4028 if (!NT_SUCCESS(Status
)) {
4029 ERR("find_item returned %08x\n", Status
);
4035 traverse_ptr next_tp
;
4037 if (tp
.item
->key
.obj_id
> fcb
->inode
|| (tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
> TYPE_INODE_EXTREF
))
4040 if (tp
.item
->key
.obj_id
== fcb
->inode
) {
4041 if (tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
4042 INODE_REF
* ir
= (INODE_REF
*)tp
.item
->data
;
4047 if (tp
.item
->size
< offsetof(INODE_REF
, name
[0]) || tp
.item
->size
< offsetof(INODE_REF
, name
[0]) + ir
->n
) {
4048 ERR("INODE_REF was too short\n");
4050 return STATUS_INTERNAL_ERROR
;
4057 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ir
->name
, ir
->n
);
4058 if (!NT_SUCCESS(Status
)) {
4059 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
4064 name
.Length
= name
.MaximumLength
= (UINT16
)stringlen
;
4069 name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.MaximumLength
, ALLOC_TAG
);
4072 ERR("out of memory\n");
4074 return STATUS_INSUFFICIENT_RESOURCES
;
4077 Status
= RtlUTF8ToUnicodeN(name
.Buffer
, stringlen
, &stringlen
, ir
->name
, ir
->n
);
4078 if (!NT_SUCCESS(Status
)) {
4079 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
4080 ExFreePool(name
.Buffer
);
4088 parent
= tp
.item
->key
.offset
;
4091 } else if (tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
4092 INODE_EXTREF
* ier
= (INODE_EXTREF
*)tp
.item
->data
;
4097 if (tp
.item
->size
< offsetof(INODE_EXTREF
, name
[0]) || tp
.item
->size
< offsetof(INODE_EXTREF
, name
[0]) + ier
->n
) {
4098 ERR("INODE_EXTREF was too short\n");
4100 return STATUS_INTERNAL_ERROR
;
4107 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ier
->name
, ier
->n
);
4108 if (!NT_SUCCESS(Status
)) {
4109 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
4114 name
.Length
= name
.MaximumLength
= (UINT16
)stringlen
;
4119 name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.MaximumLength
, ALLOC_TAG
);
4122 ERR("out of memory\n");
4124 return STATUS_INSUFFICIENT_RESOURCES
;
4127 Status
= RtlUTF8ToUnicodeN(name
.Buffer
, stringlen
, &stringlen
, ier
->name
, ier
->n
);
4128 if (!NT_SUCCESS(Status
)) {
4129 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
4130 ExFreePool(name
.Buffer
);
4144 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
))
4152 WARN("trying to open inode with no references\n");
4154 return STATUS_INVALID_PARAMETER
;
4157 hardlink
* hl
= CONTAINING_RECORD(fcb
->hardlinks
.Flink
, hardlink
, list_entry
);
4160 parent
= hl
->parent
;
4162 ExReleaseResourceLite(fcb
->Header
.Resource
);
4165 if (parent
== inode
) { // subvolume root
4169 searchkey
.obj_id
= subvol
->id
;
4170 searchkey
.obj_type
= TYPE_ROOT_BACKREF
;
4171 searchkey
.offset
= 0xffffffffffffffff;
4173 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
4174 if (!NT_SUCCESS(Status
)) {
4175 ERR("find_item returned %08x\n", Status
);
4180 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
4181 ROOT_REF
* rr
= (ROOT_REF
*)tp
.item
->data
;
4186 if (tp
.item
->size
< sizeof(ROOT_REF
)) {
4187 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(ROOT_REF
));
4189 return STATUS_INTERNAL_ERROR
;
4192 if (tp
.item
->size
< offsetof(ROOT_REF
, name
[0]) + rr
->n
) {
4193 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, offsetof(ROOT_REF
, name
[0]) + rr
->n
);
4195 return STATUS_INTERNAL_ERROR
;
4198 le
= Vcb
->roots
.Flink
;
4199 while (le
!= &Vcb
->roots
) {
4200 root
* r2
= CONTAINING_RECORD(le
, root
, list_entry
);
4202 if (r2
->id
== tp
.item
->key
.offset
) {
4211 ERR("couldn't find subvol %llx\n", tp
.item
->key
.offset
);
4213 return STATUS_INTERNAL_ERROR
;
4216 Status
= open_fileref_by_inode(Vcb
, r
, rr
->dir
, &parfr
, Irp
);
4217 if (!NT_SUCCESS(Status
)) {
4218 ERR("open_fileref_by_inode returned %08x\n", Status
);
4223 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, rr
->name
, rr
->n
);
4224 if (!NT_SUCCESS(Status
)) {
4225 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
4230 name
.Length
= name
.MaximumLength
= (UINT16
)stringlen
;
4236 ExFreePool(name
.Buffer
);
4238 name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, name
.MaximumLength
, ALLOC_TAG
);
4241 ERR("out of memory\n");
4243 return STATUS_INSUFFICIENT_RESOURCES
;
4246 Status
= RtlUTF8ToUnicodeN(name
.Buffer
, stringlen
, &stringlen
, rr
->name
, rr
->n
);
4247 if (!NT_SUCCESS(Status
)) {
4248 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
4249 ExFreePool(name
.Buffer
);
4257 ERR("couldn't find parent for subvol %llx\n", subvol
->id
);
4259 return STATUS_INTERNAL_ERROR
;
4262 Status
= open_fileref_by_inode(Vcb
, subvol
, parent
, &parfr
, Irp
);
4263 if (!NT_SUCCESS(Status
)) {
4264 ERR("open_fileref_by_inode returned %08x\n", Status
);
4270 Status
= open_fileref_child(Vcb
, parfr
, &name
, TRUE
, TRUE
, FALSE
, PagedPool
, &fr
, Irp
);
4273 ExFreePool(name
.Buffer
);
4275 if (!NT_SUCCESS(Status
)) {
4276 ERR("open_fileref_child returned %08x\n", Status
);
4279 free_fileref(parfr
);
4287 free_fileref(parfr
);
4289 return STATUS_SUCCESS
;
4292 static NTSTATUS
open_file(PDEVICE_OBJECT DeviceObject
, _Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4293 PFILE_OBJECT FileObject
= NULL
;
4294 ULONG RequestedDisposition
;
4297 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4299 ULONG fn_offset
= 0;
4300 file_ref
*related
, *fileref
= NULL
;
4301 POOL_TYPE pool_type
= IrpSp
->Flags
& SL_OPEN_PAGING_FILE
? NonPagedPool
: PagedPool
;
4302 ACCESS_MASK granted_access
;
4303 BOOL loaded_related
= FALSE
;
4306 LARGE_INTEGER time1
, time2
;
4307 UINT8 open_type
= 0;
4309 time1
= KeQueryPerformanceCounter(NULL
);
4312 Irp
->IoStatus
.Information
= 0;
4314 RequestedDisposition
= ((IrpSp
->Parameters
.Create
.Options
>> 24) & 0xff);
4315 options
= IrpSp
->Parameters
.Create
.Options
& FILE_VALID_OPTION_FLAGS
;
4317 if (options
& FILE_DIRECTORY_FILE
&& RequestedDisposition
== FILE_SUPERSEDE
) {
4318 WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
4319 return STATUS_INVALID_PARAMETER
;
4322 FileObject
= IrpSp
->FileObject
;
4325 ERR("FileObject was NULL\n");
4326 return STATUS_INVALID_PARAMETER
;
4329 if (FileObject
->RelatedFileObject
&& FileObject
->RelatedFileObject
->FsContext2
) {
4330 struct _ccb
* relatedccb
= FileObject
->RelatedFileObject
->FsContext2
;
4332 related
= relatedccb
->fileref
;
4336 debug_create_options(options
);
4338 switch (RequestedDisposition
) {
4339 case FILE_SUPERSEDE
:
4340 TRACE("requested disposition: FILE_SUPERSEDE\n");
4344 TRACE("requested disposition: FILE_CREATE\n");
4348 TRACE("requested disposition: FILE_OPEN\n");
4352 TRACE("requested disposition: FILE_OPEN_IF\n");
4355 case FILE_OVERWRITE
:
4356 TRACE("requested disposition: FILE_OVERWRITE\n");
4359 case FILE_OVERWRITE_IF
:
4360 TRACE("requested disposition: FILE_OVERWRITE_IF\n");
4364 ERR("unknown disposition: %x\n", RequestedDisposition
);
4365 Status
= STATUS_NOT_IMPLEMENTED
;
4369 fn
= FileObject
->FileName
;
4371 TRACE("(%.*S)\n", fn
.Length
/ sizeof(WCHAR
), fn
.Buffer
);
4372 TRACE("FileObject = %p\n", FileObject
);
4374 if (Vcb
->readonly
&& (RequestedDisposition
== FILE_SUPERSEDE
|| RequestedDisposition
== FILE_CREATE
|| RequestedDisposition
== FILE_OVERWRITE
)) {
4375 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
4379 if (options
& FILE_OPEN_BY_FILE_ID
) {
4380 if (RequestedDisposition
!= FILE_OPEN
) {
4381 WARN("FILE_OPEN_BY_FILE_ID not supported for anything other than FILE_OPEN\n");
4382 Status
= STATUS_INVALID_PARAMETER
;
4386 if (fn
.Length
== sizeof(UINT64
)) {
4390 WARN("cannot open by short file ID unless related fileref also provided");
4391 Status
= STATUS_INVALID_PARAMETER
;
4395 RtlCopyMemory(&inode
, fn
.Buffer
, sizeof(UINT64
));
4397 if (related
->fcb
== Vcb
->root_fileref
->fcb
&& inode
== 0)
4398 inode
= Vcb
->root_fileref
->fcb
->inode
;
4400 if (inode
== 0) { // we use 0 to mean the parent of a subvolume
4401 fileref
= related
->parent
;
4402 increase_fileref_refcount(fileref
);
4403 Status
= STATUS_SUCCESS
;
4405 Status
= open_fileref_by_inode(Vcb
, related
->fcb
->subvol
, inode
, &fileref
, Irp
);
4408 } else if (fn
.Length
== sizeof(FILE_ID_128
)) {
4409 UINT64 inode
, subvol_id
;
4410 root
* subvol
= NULL
;
4412 RtlCopyMemory(&inode
, fn
.Buffer
, sizeof(UINT64
));
4413 RtlCopyMemory(&subvol_id
, (UINT8
*)fn
.Buffer
+ sizeof(UINT64
), sizeof(UINT64
));
4415 if (subvol_id
== BTRFS_ROOT_FSTREE
|| (subvol_id
>= 0x100 && subvol_id
< 0x8000000000000000)) {
4416 LIST_ENTRY
* le
= Vcb
->roots
.Flink
;
4417 while (le
!= &Vcb
->roots
) {
4418 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
4420 if (r
->id
== subvol_id
) {
4430 WARN("subvol %llx not found\n", subvol_id
);
4431 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
4433 Status
= open_fileref_by_inode(Vcb
, subvol
, inode
, &fileref
, Irp
);
4437 WARN("invalid ID size for FILE_OPEN_BY_FILE_ID\n");
4438 Status
= STATUS_INVALID_PARAMETER
;
4443 if (related
&& fn
.Length
!= 0 && fn
.Buffer
[0] == '\\') {
4444 Status
= STATUS_INVALID_PARAMETER
;
4448 if (!related
&& RequestedDisposition
!= FILE_OPEN
&& !(IrpSp
->Flags
& SL_OPEN_TARGET_DIRECTORY
)) {
4451 Status
= open_fileref(Vcb
, &related
, &fn
, NULL
, TRUE
, &parsed
, &fnoff
,
4452 pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
4454 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
4455 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
4456 else if (Status
== STATUS_REPARSE
)
4458 else if (NT_SUCCESS(Status
)) {
4459 fnoff
*= sizeof(WCHAR
);
4460 fnoff
+= (related
->dc
? related
->dc
->name
.Length
: 0) + sizeof(WCHAR
);
4462 if (related
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
) {
4463 Status
= STATUS_REPARSE
;
4465 parsed
= (USHORT
)fnoff
- sizeof(WCHAR
);
4467 fn
.Buffer
= &fn
.Buffer
[fnoff
/ sizeof(WCHAR
)];
4468 fn
.Length
-= (USHORT
)fnoff
;
4470 Status
= open_fileref(Vcb
, &fileref
, &fn
, related
, IrpSp
->Flags
& SL_OPEN_TARGET_DIRECTORY
, &parsed
, &fn_offset
,
4471 pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
4473 loaded_related
= TRUE
;
4477 Status
= open_fileref(Vcb
, &fileref
, &fn
, related
, IrpSp
->Flags
& SL_OPEN_TARGET_DIRECTORY
, &parsed
, &fn_offset
,
4478 pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
4482 if (Status
== STATUS_REPARSE
) {
4483 REPARSE_DATA_BUFFER
* data
;
4485 ExAcquireResourceSharedLite(fileref
->fcb
->Header
.Resource
, TRUE
);
4486 Status
= get_reparse_block(fileref
->fcb
, (UINT8
**)&data
);
4487 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
4489 if (!NT_SUCCESS(Status
)) {
4490 ERR("get_reparse_block returned %08x\n", Status
);
4492 Status
= STATUS_SUCCESS
;
4494 Status
= STATUS_REPARSE
;
4495 RtlCopyMemory(&Irp
->IoStatus
.Information
, data
, sizeof(ULONG
));
4497 data
->Reserved
= FileObject
->FileName
.Length
- parsed
;
4499 Irp
->Tail
.Overlay
.AuxiliaryBuffer
= (void*)data
;
4501 free_fileref(fileref
);
4507 if (NT_SUCCESS(Status
) && fileref
->deleted
)
4508 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
4510 if (NT_SUCCESS(Status
)) {
4511 if (RequestedDisposition
== FILE_CREATE
) {
4512 TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref
));
4513 Status
= STATUS_OBJECT_NAME_COLLISION
;
4515 free_fileref(fileref
);
4519 } else if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
4520 if (RequestedDisposition
== FILE_OPEN
|| RequestedDisposition
== FILE_OVERWRITE
) {
4521 TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
4524 } else if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
) {
4525 TRACE("open_fileref returned %08x\n", Status
);
4528 ERR("open_fileref returned %08x\n", Status
);
4532 if (NT_SUCCESS(Status
)) { // file already exists
4533 Status
= open_file2(Vcb
, RequestedDisposition
, pool_type
, fileref
, &granted_access
, FileObject
, &fn
, options
, Irp
, rollback
);
4534 if (!NT_SUCCESS(Status
))
4537 file_ref
* existing_file
;
4542 Status
= file_create(Irp
, Vcb
, FileObject
, related
, loaded_related
, &fn
, RequestedDisposition
, options
, &existing_file
, rollback
);
4544 if (Status
== STATUS_OBJECT_NAME_COLLISION
) { // already exists
4545 fileref
= existing_file
;
4547 Status
= open_file2(Vcb
, RequestedDisposition
, pool_type
, fileref
, &granted_access
, FileObject
, &fn
, options
, Irp
, rollback
);
4548 if (!NT_SUCCESS(Status
))
4551 Irp
->IoStatus
.Information
= NT_SUCCESS(Status
) ? FILE_CREATED
: 0;
4552 granted_access
= IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
;
4556 if (NT_SUCCESS(Status
) && !(options
& FILE_NO_INTERMEDIATE_BUFFERING
))
4557 FileObject
->Flags
|= FO_CACHE_SUPPORTED
;
4561 free_fileref(related
);
4563 if (Status
== STATUS_SUCCESS
) {
4566 IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->PreviouslyGrantedAccess
|= granted_access
;
4567 IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->RemainingDesiredAccess
&= ~(granted_access
| MAXIMUM_ALLOWED
);
4569 if (!FileObject
->Vpb
)
4570 FileObject
->Vpb
= DeviceObject
->Vpb
;
4572 fcb2
= FileObject
->FsContext
;
4575 struct _ccb
* ccb2
= FileObject
->FsContext2
;
4577 fcb2
= ccb2
->fileref
->parent
->fcb
;
4580 ExAcquireResourceExclusiveLite(fcb2
->Header
.Resource
, TRUE
);
4581 fcb_load_csums(Vcb
, fcb2
, Irp
);
4582 ExReleaseResourceLite(fcb2
->Header
.Resource
);
4583 } else if (Status
!= STATUS_REPARSE
&& Status
!= STATUS_OBJECT_NAME_NOT_FOUND
&& Status
!= STATUS_OBJECT_PATH_NOT_FOUND
)
4584 TRACE("returning %08x\n", Status
);
4587 time2
= KeQueryPerformanceCounter(NULL
);
4589 if (open_type
== 0) {
4590 Vcb
->stats
.open_total_time
+= time2
.QuadPart
- time1
.QuadPart
;
4591 Vcb
->stats
.num_opens
++;
4592 } else if (open_type
== 1) {
4593 Vcb
->stats
.overwrite_total_time
+= time2
.QuadPart
- time1
.QuadPart
;
4594 Vcb
->stats
.num_overwrites
++;
4595 } else if (open_type
== 2) {
4596 Vcb
->stats
.create_total_time
+= time2
.QuadPart
- time1
.QuadPart
;
4597 Vcb
->stats
.num_creates
++;
4604 static NTSTATUS
verify_vcb(device_extension
* Vcb
, PIRP Irp
) {
4607 BOOL need_verify
= FALSE
;
4609 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
4611 le
= Vcb
->devices
.Flink
;
4612 while (le
!= &Vcb
->devices
) {
4613 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
4615 if (dev
->devobj
&& dev
->removable
) {
4617 IO_STATUS_BLOCK iosb
;
4619 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
4621 if (IoIsErrorUserInduced(Status
)) {
4622 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status
);
4624 } else if (!NT_SUCCESS(Status
)) {
4625 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status
);
4627 } else if (iosb
.Information
< sizeof(ULONG
)) {
4628 ERR("iosb.Information was too short\n");
4629 Status
= STATUS_INTERNAL_ERROR
;
4630 } else if (cc
!= dev
->change_count
) {
4631 dev
->devobj
->Flags
|= DO_VERIFY_VOLUME
;
4639 Status
= STATUS_SUCCESS
;
4642 ExReleaseResourceLite(&Vcb
->tree_lock
);
4645 PDEVICE_OBJECT devobj
;
4647 devobj
= IoGetDeviceToVerify(Irp
->Tail
.Overlay
.Thread
);
4648 IoSetDeviceToVerify(Irp
->Tail
.Overlay
.Thread
, NULL
);
4651 devobj
= IoGetDeviceToVerify(PsGetCurrentThread());
4652 IoSetDeviceToVerify(PsGetCurrentThread(), NULL
);
4655 devobj
= Vcb
->Vpb
? Vcb
->Vpb
->RealDevice
: NULL
;
4658 Status
= IoVerifyVolume(devobj
, FALSE
);
4660 Status
= STATUS_VERIFY_REQUIRED
;
4666 static BOOL
has_manage_volume_privilege(ACCESS_STATE
* access_state
, KPROCESSOR_MODE processor_mode
) {
4667 PRIVILEGE_SET privset
;
4669 privset
.PrivilegeCount
= 1;
4670 privset
.Control
= PRIVILEGE_SET_ALL_NECESSARY
;
4671 privset
.Privilege
[0].Luid
= RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
);
4672 privset
.Privilege
[0].Attributes
= 0;
4674 return SePrivilegeCheck(&privset
, &access_state
->SubjectSecurityContext
, processor_mode
) ? TRUE
: FALSE
;
4677 _Dispatch_type_(IRP_MJ_CREATE
)
4678 _Function_class_(DRIVER_DISPATCH
)
4679 NTSTATUS NTAPI
drv_create(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4681 PIO_STACK_LOCATION IrpSp
;
4682 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4683 BOOL top_level
, locked
= FALSE
;
4685 FsRtlEnterFileSystem();
4687 TRACE("create (flags = %x)\n", Irp
->Flags
);
4689 top_level
= is_top_level(Irp
);
4691 /* return success if just called for FS device object */
4692 if (DeviceObject
== master_devobj
) {
4693 TRACE("create called for FS device object\n");
4695 Irp
->IoStatus
.Information
= FILE_OPENED
;
4696 Status
= STATUS_SUCCESS
;
4699 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4700 Status
= vol_create(DeviceObject
, Irp
);
4702 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
4703 Status
= STATUS_INVALID_PARAMETER
;
4707 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
)) {
4708 Status
= STATUS_DEVICE_NOT_READY
;
4712 if (Vcb
->removing
) {
4713 Status
= STATUS_ACCESS_DENIED
;
4717 Status
= verify_vcb(Vcb
, Irp
);
4718 if (!NT_SUCCESS(Status
)) {
4719 ERR("verify_vcb returned %08x\n", Status
);
4723 ExAcquireResourceSharedLite(&Vcb
->load_lock
, TRUE
);
4726 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4728 if (IrpSp
->Flags
!= 0) {
4729 UINT32 flags
= IrpSp
->Flags
;
4733 if (flags
& SL_CASE_SENSITIVE
) {
4734 TRACE("SL_CASE_SENSITIVE\n");
4735 flags
&= ~SL_CASE_SENSITIVE
;
4738 if (flags
& SL_FORCE_ACCESS_CHECK
) {
4739 TRACE("SL_FORCE_ACCESS_CHECK\n");
4740 flags
&= ~SL_FORCE_ACCESS_CHECK
;
4743 if (flags
& SL_OPEN_PAGING_FILE
) {
4744 TRACE("SL_OPEN_PAGING_FILE\n");
4745 flags
&= ~SL_OPEN_PAGING_FILE
;
4748 if (flags
& SL_OPEN_TARGET_DIRECTORY
) {
4749 TRACE("SL_OPEN_TARGET_DIRECTORY\n");
4750 flags
&= ~SL_OPEN_TARGET_DIRECTORY
;
4753 if (flags
& SL_STOP_ON_SYMLINK
) {
4754 TRACE("SL_STOP_ON_SYMLINK\n");
4755 flags
&= ~SL_STOP_ON_SYMLINK
;
4759 WARN("unknown flags: %x\n", flags
);
4761 TRACE("flags: (none)\n");
4764 if (!IrpSp
->FileObject
) {
4765 ERR("FileObject was NULL\n");
4766 Status
= STATUS_INVALID_PARAMETER
;
4770 if (IrpSp
->FileObject
->RelatedFileObject
) {
4771 fcb
* relatedfcb
= IrpSp
->FileObject
->RelatedFileObject
->FsContext
;
4773 if (relatedfcb
&& relatedfcb
->Vcb
!= Vcb
) {
4774 WARN("RelatedFileObject was for different device\n");
4775 Status
= STATUS_INVALID_PARAMETER
;
4781 if (IrpSp
->FileObject
->FileName
.Length
== 0 && !IrpSp
->FileObject
->RelatedFileObject
) {
4782 ULONG RequestedDisposition
= ((IrpSp
->Parameters
.Create
.Options
>> 24) & 0xff);
4783 ULONG RequestedOptions
= IrpSp
->Parameters
.Create
.Options
& FILE_VALID_OPTION_FLAGS
;
4784 #ifdef DEBUG_FCB_REFCOUNTS
4789 TRACE("open operation for volume\n");
4791 if (RequestedDisposition
!= FILE_OPEN
&& RequestedDisposition
!= FILE_OPEN_IF
) {
4792 Status
= STATUS_ACCESS_DENIED
;
4796 if (RequestedOptions
& FILE_DIRECTORY_FILE
) {
4797 Status
= STATUS_NOT_A_DIRECTORY
;
4801 ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ccb
), ALLOC_TAG
);
4803 ERR("out of memory\n");
4804 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4808 RtlZeroMemory(ccb
, sizeof(*ccb
));
4810 ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
4811 ccb
->NodeSize
= sizeof(*ccb
);
4812 ccb
->disposition
= RequestedDisposition
;
4813 ccb
->options
= RequestedOptions
;
4814 ccb
->access
= IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->PreviouslyGrantedAccess
;
4815 ccb
->manage_volume_privilege
= has_manage_volume_privilege(IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
,
4816 IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
);
4817 ccb
->reserving
= FALSE
;
4818 ccb
->lxss
= called_from_lxss();
4820 #ifdef DEBUG_FCB_REFCOUNTS
4821 rc
= InterlockedIncrement(&Vcb
->volume_fcb
->refcount
);
4822 WARN("fcb %p: refcount now %i (volume)\n", Vcb
->volume_fcb
, rc
);
4824 InterlockedIncrement(&Vcb
->volume_fcb
->refcount
);
4826 IrpSp
->FileObject
->FsContext
= Vcb
->volume_fcb
;
4827 IrpSp
->FileObject
->FsContext2
= ccb
;
4829 IrpSp
->FileObject
->SectionObjectPointer
= &Vcb
->volume_fcb
->nonpaged
->segment_object
;
4831 if (!IrpSp
->FileObject
->Vpb
)
4832 IrpSp
->FileObject
->Vpb
= DeviceObject
->Vpb
;
4834 InterlockedIncrement(&Vcb
->open_files
);
4836 Irp
->IoStatus
.Information
= FILE_OPENED
;
4837 Status
= STATUS_SUCCESS
;
4839 LIST_ENTRY rollback
;
4842 InitializeListHead(&rollback
);
4844 TRACE("file name: %.*S\n", IrpSp
->FileObject
->FileName
.Length
/ sizeof(WCHAR
), IrpSp
->FileObject
->FileName
.Buffer
);
4846 if (IrpSp
->FileObject
->RelatedFileObject
)
4847 TRACE("related file = %S\n", file_desc(IrpSp
->FileObject
->RelatedFileObject
));
4849 // Don't lock again if we're being called from within CcCopyRead etc.
4850 skip_lock
= ExIsResourceAcquiredExclusiveLite(&Vcb
->tree_lock
);
4853 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
4855 ExAcquireResourceSharedLite(&Vcb
->fileref_lock
, TRUE
);
4857 Status
= open_file(DeviceObject
, Vcb
, Irp
, &rollback
);
4859 if (!NT_SUCCESS(Status
))
4860 do_rollback(Vcb
, &rollback
);
4862 clear_rollback(&rollback
);
4864 ExReleaseResourceLite(&Vcb
->fileref_lock
);
4867 ExReleaseResourceLite(&Vcb
->tree_lock
);
4871 Irp
->IoStatus
.Status
= Status
;
4872 IoCompleteRequest( Irp
, NT_SUCCESS(Status
) ? IO_DISK_INCREMENT
: IO_NO_INCREMENT
);
4874 TRACE("create returning %08x\n", Status
);
4877 ExReleaseResourceLite(&Vcb
->load_lock
);
4880 IoSetTopLevelIrp(NULL
);
4882 FsRtlExitFileSystem();