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
);
140 ExInitializeResourceLite(&fr
->nonpaged
->children_lock
);
145 NTSTATUS
find_file_in_dir(PUNICODE_STRING filename
, fcb
* fcb
, root
** subvol
, UINT64
* inode
, dir_child
** pdc
, BOOL case_sensitive
) {
153 if (!case_sensitive
) {
154 Status
= RtlUpcaseUnicodeString(&fnus
, filename
, TRUE
);
156 if (!NT_SUCCESS(Status
)) {
157 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
163 hash
= calc_crc32c(0xffffffff, (UINT8
*)fnus
.Buffer
, fnus
.Length
);
167 if (!ExIsResourceAcquiredSharedLite(&fcb
->nonpaged
->dir_children_lock
)) {
168 ExAcquireResourceSharedLite(&fcb
->nonpaged
->dir_children_lock
, TRUE
);
172 if (case_sensitive
) {
173 if (!fcb
->hash_ptrs
[c
]) {
174 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
178 le
= fcb
->hash_ptrs
[c
];
179 while (le
!= &fcb
->dir_children_hash
) {
180 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash
);
182 if (dc
->hash
== hash
) {
183 if (dc
->name
.Length
== fnus
.Length
&& RtlCompareMemory(dc
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
) == fnus
.Length
) {
184 if (dc
->key
.obj_type
== TYPE_ROOT_ITEM
) {
189 le2
= fcb
->Vcb
->roots
.Flink
;
190 while (le2
!= &fcb
->Vcb
->roots
) {
191 root
* r2
= CONTAINING_RECORD(le2
, root
, list_entry
);
193 if (r2
->id
== dc
->key
.obj_id
) {
201 *inode
= SUBVOL_ROOT_INODE
;
203 *subvol
= fcb
->subvol
;
204 *inode
= dc
->key
.obj_id
;
209 Status
= STATUS_SUCCESS
;
212 } else if (dc
->hash
> hash
) {
213 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
220 if (!fcb
->hash_ptrs_uc
[c
]) {
221 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
225 le
= fcb
->hash_ptrs_uc
[c
];
226 while (le
!= &fcb
->dir_children_hash_uc
) {
227 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash_uc
);
229 if (dc
->hash_uc
== hash
) {
230 if (dc
->name_uc
.Length
== fnus
.Length
&& RtlCompareMemory(dc
->name_uc
.Buffer
, fnus
.Buffer
, fnus
.Length
) == fnus
.Length
) {
231 if (dc
->key
.obj_type
== TYPE_ROOT_ITEM
) {
236 le2
= fcb
->Vcb
->roots
.Flink
;
237 while (le2
!= &fcb
->Vcb
->roots
) {
238 root
* r2
= CONTAINING_RECORD(le2
, root
, list_entry
);
240 if (r2
->id
== dc
->key
.obj_id
) {
248 *inode
= SUBVOL_ROOT_INODE
;
250 *subvol
= fcb
->subvol
;
251 *inode
= dc
->key
.obj_id
;
256 Status
= STATUS_SUCCESS
;
259 } else if (dc
->hash_uc
> hash
) {
260 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
268 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
272 ExReleaseResourceLite(&fcb
->nonpaged
->dir_children_lock
);
275 ExFreePool(fnus
.Buffer
);
280 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] == '\\'))
291 for (i
= 0; i
< len
; i
++) {
292 if (path
->Buffer
[i
] == '/' || path
->Buffer
[i
] == '\\') {
294 } else if (path
->Buffer
[i
] == ':') {
301 for (i
= 0; i
< len
; i
++) {
302 if (path
->Buffer
[i
] == '/' || path
->Buffer
[i
] == '\\') {
303 nb
= ExAllocateFromPagedLookasideList(&Vcb
->name_bit_lookaside
);
305 ERR("out of memory\n");
306 return STATUS_INSUFFICIENT_RESOURCES
;
310 nb
->us
.Length
= nb
->us
.MaximumLength
= (USHORT
)(&path
->Buffer
[i
] - buf
) * sizeof(WCHAR
);
311 InsertTailList(parts
, &nb
->list_entry
);
313 buf
= &path
->Buffer
[i
+1];
317 nb
= ExAllocateFromPagedLookasideList(&Vcb
->name_bit_lookaside
);
319 ERR("out of memory\n");
320 return STATUS_INSUFFICIENT_RESOURCES
;
324 nb
->us
.Length
= nb
->us
.MaximumLength
= (USHORT
)(&path
->Buffer
[i
] - buf
) * sizeof(WCHAR
);
325 InsertTailList(parts
, &nb
->list_entry
);
328 static const WCHAR datasuf
[] = {':','$','D','A','T','A',0};
331 dsus
.Buffer
= (WCHAR
*)datasuf
;
332 dsus
.Length
= dsus
.MaximumLength
= sizeof(datasuf
) - sizeof(WCHAR
);
334 for (i
= 0; i
< nb
->us
.Length
/ sizeof(WCHAR
); i
++) {
335 if (nb
->us
.Buffer
[i
] == ':') {
338 nb2
= ExAllocateFromPagedLookasideList(&Vcb
->name_bit_lookaside
);
340 ERR("out of memory\n");
341 return STATUS_INSUFFICIENT_RESOURCES
;
344 nb2
->us
.Buffer
= &nb
->us
.Buffer
[i
+1];
345 nb2
->us
.Length
= nb2
->us
.MaximumLength
= (UINT16
)(nb
->us
.Length
- (i
* sizeof(WCHAR
)) - sizeof(WCHAR
));
346 InsertTailList(parts
, &nb2
->list_entry
);
348 nb
->us
.Length
= (UINT16
)i
* sizeof(WCHAR
);
349 nb
->us
.MaximumLength
= nb
->us
.Length
;
357 // FIXME - should comparison be case-insensitive?
358 // remove :$DATA suffix
359 if (nb
->us
.Length
>= dsus
.Length
&& RtlCompareMemory(&nb
->us
.Buffer
[(nb
->us
.Length
- dsus
.Length
)/sizeof(WCHAR
)], dsus
.Buffer
, dsus
.Length
) == dsus
.Length
)
360 nb
->us
.Length
-= dsus
.Length
;
362 if (nb
->us
.Length
== 0) {
363 RemoveTailList(parts
);
364 ExFreeToPagedLookasideList(&Vcb
->name_bit_lookaside
, nb
);
370 // if path is just stream name, remove first empty item
371 if (has_stream
&& path
->Length
>= sizeof(WCHAR
) && path
->Buffer
[0] == ':') {
372 name_bit
*nb1
= CONTAINING_RECORD(RemoveHeadList(parts
), name_bit
, list_entry
);
374 ExFreeToPagedLookasideList(&Vcb
->name_bit_lookaside
, nb1
);
377 *stream
= has_stream
;
379 return STATUS_SUCCESS
;
382 NTSTATUS
load_csum(_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, UINT32
* csum
, UINT64 start
, UINT64 length
, PIRP Irp
) {
385 traverse_ptr tp
, next_tp
;
389 searchkey
.obj_id
= EXTENT_CSUM_ID
;
390 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
391 searchkey
.offset
= start
;
393 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
, Irp
);
394 if (!NT_SUCCESS(Status
)) {
395 ERR("error - find_item returned %08x\n", Status
);
401 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
404 if (start
< tp
.item
->key
.offset
)
407 j
= ((start
- tp
.item
->key
.offset
) / Vcb
->superblock
.sector_size
) + i
;
409 if (j
* sizeof(UINT32
) > tp
.item
->size
|| tp
.item
->key
.offset
> start
+ (i
* Vcb
->superblock
.sector_size
)) {
410 ERR("checksum not found for %llx\n", start
+ (i
* Vcb
->superblock
.sector_size
));
411 return STATUS_INTERNAL_ERROR
;
414 readlen
= (ULONG
)min((tp
.item
->size
/ sizeof(UINT32
)) - j
, length
- i
);
415 RtlCopyMemory(&csum
[i
], tp
.item
->data
+ (j
* sizeof(UINT32
)), readlen
* sizeof(UINT32
));
422 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
429 ERR("could not read checksums: offset %llx, length %llx sectors\n", start
, length
);
430 return STATUS_INTERNAL_ERROR
;
433 return STATUS_SUCCESS
;
436 NTSTATUS
load_dir_children(_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, fcb
* fcb
, BOOL ignore_size
, PIRP Irp
) {
438 traverse_ptr tp
, next_tp
;
440 ULONG num_children
= 0;
442 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
443 if (!fcb
->hash_ptrs
) {
444 ERR("out of memory\n");
445 return STATUS_INSUFFICIENT_RESOURCES
;
448 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
450 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
451 if (!fcb
->hash_ptrs_uc
) {
452 ERR("out of memory\n");
453 return STATUS_INSUFFICIENT_RESOURCES
;
456 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
458 if (!ignore_size
&& fcb
->inode_item
.st_size
== 0)
459 return STATUS_SUCCESS
;
461 searchkey
.obj_id
= fcb
->inode
;
462 searchkey
.obj_type
= TYPE_DIR_INDEX
;
463 searchkey
.offset
= 2;
465 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
466 if (!NT_SUCCESS(Status
)) {
467 ERR("find_item returned %08x\n", Status
);
471 if (keycmp(tp
.item
->key
, searchkey
) == -1) {
472 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
)) {
474 TRACE("moving on to %llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
478 while (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
479 DIR_ITEM
* di
= (DIR_ITEM
*)tp
.item
->data
;
483 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
484 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
));
489 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
);
493 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &utf16len
, di
->name
, di
->n
);
494 if (!NT_SUCCESS(Status
)) {
495 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
499 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
501 ERR("out of memory\n");
502 return STATUS_INSUFFICIENT_RESOURCES
;
506 dc
->index
= tp
.item
->key
.offset
;
510 dc
->utf8
.MaximumLength
= dc
->utf8
.Length
= di
->n
;
511 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, di
->n
, ALLOC_TAG
);
512 if (!dc
->utf8
.Buffer
) {
513 ERR("out of memory\n");
515 return STATUS_INSUFFICIENT_RESOURCES
;
518 RtlCopyMemory(dc
->utf8
.Buffer
, di
->name
, di
->n
);
520 dc
->name
.MaximumLength
= dc
->name
.Length
= (UINT16
)utf16len
;
521 dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dc
->name
.MaximumLength
, ALLOC_TAG
);
522 if (!dc
->name
.Buffer
) {
523 ERR("out of memory\n");
524 ExFreePool(dc
->utf8
.Buffer
);
526 return STATUS_INSUFFICIENT_RESOURCES
;
529 Status
= RtlUTF8ToUnicodeN(dc
->name
.Buffer
, utf16len
, &utf16len
, di
->name
, di
->n
);
530 if (!NT_SUCCESS(Status
)) {
531 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
532 ExFreePool(dc
->utf8
.Buffer
);
533 ExFreePool(dc
->name
.Buffer
);
538 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, &dc
->name
, TRUE
);
539 if (!NT_SUCCESS(Status
)) {
540 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
541 ExFreePool(dc
->utf8
.Buffer
);
542 ExFreePool(dc
->name
.Buffer
);
547 dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name
.Buffer
, dc
->name
.Length
);
548 dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name_uc
.Buffer
, dc
->name_uc
.Length
);
550 InsertTailList(&fcb
->dir_children_index
, &dc
->list_entry_index
);
552 insert_dir_child_into_hash_lists(fcb
, dc
);
557 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
))
563 // If a directory has a lot of files, force it to stick around until the next flush
564 // so we aren't constantly re-reading.
565 if (num_children
>= 100)
568 return STATUS_SUCCESS
;
571 NTSTATUS
open_fcb(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
572 root
* subvol
, UINT64 inode
, UINT8 type
, PANSI_STRING utf8
, fcb
* parent
, fcb
** pfcb
, POOL_TYPE pooltype
, PIRP Irp
) {
574 traverse_ptr tp
, next_tp
;
576 fcb
*fcb
, *deleted_fcb
= NULL
;
577 BOOL atts_set
= FALSE
, sd_set
= FALSE
, no_data
;
578 LIST_ENTRY
* lastle
= NULL
;
579 EXTENT_DATA
* ed
= NULL
;
581 if (!IsListEmpty(&subvol
->fcbs
)) {
582 LIST_ENTRY
* le
= subvol
->fcbs
.Flink
;
584 while (le
!= &subvol
->fcbs
) {
585 fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
587 if (fcb
->inode
== inode
) {
592 #ifdef DEBUG_FCB_REFCOUNTS
593 LONG rc
= InterlockedIncrement(&fcb
->refcount
);
595 WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb
, rc
, fcb
->subvol
->id
, fcb
->inode
);
597 InterlockedIncrement(&fcb
->refcount
);
601 return STATUS_SUCCESS
;
604 } else if (fcb
->inode
> inode
) {
606 InterlockedIncrement(&deleted_fcb
->refcount
);
608 return STATUS_SUCCESS
;
620 InterlockedIncrement(&deleted_fcb
->refcount
);
622 return STATUS_SUCCESS
;
625 fcb
= create_fcb(Vcb
, pooltype
);
627 ERR("out of memory\n");
628 return STATUS_INSUFFICIENT_RESOURCES
;
633 fcb
->subvol
= subvol
;
637 searchkey
.obj_id
= inode
;
638 searchkey
.obj_type
= TYPE_INODE_ITEM
;
639 searchkey
.offset
= 0xffffffffffffffff;
641 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
642 if (!NT_SUCCESS(Status
)) {
643 ERR("error - find_item returned %08x\n", Status
);
648 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
649 WARN("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", inode
, subvol
->id
);
651 return STATUS_INVALID_PARAMETER
;
654 if (tp
.item
->size
> 0)
655 RtlCopyMemory(&fcb
->inode_item
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
657 if (fcb
->type
== 0) { // guess the type from the inode mode, if the caller doesn't know already
658 if ((fcb
->inode_item
.st_mode
& __S_IFDIR
) == __S_IFDIR
)
659 fcb
->type
= BTRFS_TYPE_DIRECTORY
;
660 else if ((fcb
->inode_item
.st_mode
& __S_IFCHR
) == __S_IFCHR
)
661 fcb
->type
= BTRFS_TYPE_CHARDEV
;
662 else if ((fcb
->inode_item
.st_mode
& __S_IFBLK
) == __S_IFBLK
)
663 fcb
->type
= BTRFS_TYPE_BLOCKDEV
;
664 else if ((fcb
->inode_item
.st_mode
& __S_IFIFO
) == __S_IFIFO
)
665 fcb
->type
= BTRFS_TYPE_FIFO
;
666 else if ((fcb
->inode_item
.st_mode
& __S_IFLNK
) == __S_IFLNK
)
667 fcb
->type
= BTRFS_TYPE_SYMLINK
;
668 else if ((fcb
->inode_item
.st_mode
& __S_IFSOCK
) == __S_IFSOCK
)
669 fcb
->type
= BTRFS_TYPE_SOCKET
;
671 fcb
->type
= BTRFS_TYPE_FILE
;
674 no_data
= fcb
->inode_item
.st_size
== 0 || (fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
);
676 while (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
)) {
679 if (tp
.item
->key
.obj_id
> inode
)
682 if ((no_data
&& tp
.item
->key
.obj_type
> TYPE_XATTR_ITEM
) || tp
.item
->key
.obj_type
> TYPE_EXTENT_DATA
)
685 if (fcb
->inode_item
.st_nlink
> 1 && tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
690 ir
= (INODE_REF
*)tp
.item
->data
;
692 while (len
>= sizeof(INODE_REF
) - 1) {
696 hl
= ExAllocatePoolWithTag(pooltype
, sizeof(hardlink
), ALLOC_TAG
);
698 ERR("out of memory\n");
700 return STATUS_INSUFFICIENT_RESOURCES
;
703 hl
->parent
= tp
.item
->key
.offset
;
704 hl
->index
= ir
->index
;
706 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= ir
->n
;
708 if (hl
->utf8
.Length
> 0) {
709 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(pooltype
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
710 RtlCopyMemory(hl
->utf8
.Buffer
, ir
->name
, ir
->n
);
713 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ir
->name
, ir
->n
);
714 if (!NT_SUCCESS(Status
)) {
715 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
721 hl
->name
.Length
= hl
->name
.MaximumLength
= (UINT16
)stringlen
;
724 hl
->name
.Buffer
= NULL
;
726 hl
->name
.Buffer
= ExAllocatePoolWithTag(pooltype
, hl
->name
.MaximumLength
, ALLOC_TAG
);
728 if (!hl
->name
.Buffer
) {
729 ERR("out of memory\n");
732 return STATUS_INSUFFICIENT_RESOURCES
;
735 Status
= RtlUTF8ToUnicodeN(hl
->name
.Buffer
, stringlen
, &stringlen
, ir
->name
, ir
->n
);
736 if (!NT_SUCCESS(Status
)) {
737 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
738 ExFreePool(hl
->name
.Buffer
);
745 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
747 len
-= sizeof(INODE_REF
) - 1 + ir
->n
;
748 ir
= (INODE_REF
*)&ir
->name
[ir
->n
];
750 } else if (fcb
->inode_item
.st_nlink
> 1 && tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
755 ier
= (INODE_EXTREF
*)tp
.item
->data
;
757 while (len
>= sizeof(INODE_EXTREF
) - 1) {
761 hl
= ExAllocatePoolWithTag(pooltype
, sizeof(hardlink
), ALLOC_TAG
);
763 ERR("out of memory\n");
765 return STATUS_INSUFFICIENT_RESOURCES
;
768 hl
->parent
= ier
->dir
;
769 hl
->index
= ier
->index
;
771 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= ier
->n
;
773 if (hl
->utf8
.Length
> 0) {
774 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(pooltype
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
775 RtlCopyMemory(hl
->utf8
.Buffer
, ier
->name
, ier
->n
);
778 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ier
->name
, ier
->n
);
779 if (!NT_SUCCESS(Status
)) {
780 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
786 hl
->name
.Length
= hl
->name
.MaximumLength
= (UINT16
)stringlen
;
789 hl
->name
.Buffer
= NULL
;
791 hl
->name
.Buffer
= ExAllocatePoolWithTag(pooltype
, hl
->name
.MaximumLength
, ALLOC_TAG
);
793 if (!hl
->name
.Buffer
) {
794 ERR("out of memory\n");
797 return STATUS_INSUFFICIENT_RESOURCES
;
800 Status
= RtlUTF8ToUnicodeN(hl
->name
.Buffer
, stringlen
, &stringlen
, ier
->name
, ier
->n
);
801 if (!NT_SUCCESS(Status
)) {
802 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
803 ExFreePool(hl
->name
.Buffer
);
810 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
812 len
-= sizeof(INODE_EXTREF
) - 1 + ier
->n
;
813 ier
= (INODE_EXTREF
*)&ier
->name
[ier
->n
];
815 } else if (tp
.item
->key
.obj_type
== TYPE_XATTR_ITEM
) {
819 static const char xapref
[] = "user.";
821 if (tp
.item
->size
< offsetof(DIR_ITEM
, name
[0])) {
822 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]));
827 di
= (DIR_ITEM
*)tp
.item
->data
;
830 if (len
< offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
)
833 if (tp
.item
->key
.offset
== EA_REPARSE_HASH
&& di
->n
== sizeof(EA_REPARSE
) - 1 && RtlCompareMemory(EA_REPARSE
, di
->name
, di
->n
) == di
->n
) {
835 fcb
->reparse_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, di
->m
, ALLOC_TAG
);
836 if (!fcb
->reparse_xattr
.Buffer
) {
837 ERR("out of memory\n");
839 return STATUS_INSUFFICIENT_RESOURCES
;
842 RtlCopyMemory(fcb
->reparse_xattr
.Buffer
, &di
->name
[di
->n
], di
->m
);
844 fcb
->reparse_xattr
.Buffer
= NULL
;
846 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= di
->m
;
847 } else if (tp
.item
->key
.offset
== EA_EA_HASH
&& di
->n
== sizeof(EA_EA
) - 1 && RtlCompareMemory(EA_EA
, di
->name
, di
->n
) == di
->n
) {
851 Status
= IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION
*)&di
->name
[di
->n
], di
->m
, &offset
);
853 if (!NT_SUCCESS(Status
))
854 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
856 FILE_FULL_EA_INFORMATION
* eainfo
;
858 fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, di
->m
, ALLOC_TAG
);
859 if (!fcb
->ea_xattr
.Buffer
) {
860 ERR("out of memory\n");
862 return STATUS_INSUFFICIENT_RESOURCES
;
865 RtlCopyMemory(fcb
->ea_xattr
.Buffer
, &di
->name
[di
->n
], di
->m
);
867 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= di
->m
;
872 eainfo
= (FILE_FULL_EA_INFORMATION
*)&di
->name
[di
->n
];
874 fcb
->ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
876 if (eainfo
->NextEntryOffset
== 0)
879 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
883 } else if (tp
.item
->key
.offset
== EA_DOSATTRIB_HASH
&& di
->n
== sizeof(EA_DOSATTRIB
) - 1 && RtlCompareMemory(EA_DOSATTRIB
, di
->name
, di
->n
) == di
->n
) {
885 if (get_file_attributes_from_xattr(&di
->name
[di
->n
], di
->m
, &fcb
->atts
)) {
888 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
)
889 fcb
->atts
|= FILE_ATTRIBUTE_DIRECTORY
;
890 else if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
891 fcb
->atts
|= FILE_ATTRIBUTE_REPARSE_POINT
;
893 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
)
894 fcb
->atts
&= ~FILE_ATTRIBUTE_DIRECTORY
;
896 if (inode
== SUBVOL_ROOT_INODE
) {
897 if (subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
898 fcb
->atts
|= FILE_ATTRIBUTE_READONLY
;
900 fcb
->atts
&= ~FILE_ATTRIBUTE_READONLY
;
904 } else if (tp
.item
->key
.offset
== EA_NTACL_HASH
&& di
->n
== sizeof(EA_NTACL
) - 1 && RtlCompareMemory(EA_NTACL
, di
->name
, di
->n
) == di
->n
) {
906 fcb
->sd
= ExAllocatePoolWithTag(PagedPool
, di
->m
, ALLOC_TAG
);
908 ERR("out of memory\n");
910 return STATUS_INSUFFICIENT_RESOURCES
;
913 RtlCopyMemory(fcb
->sd
, &di
->name
[di
->n
], di
->m
);
915 // We have to test against our copy rather than the source, as RtlValidRelativeSecurityDescriptor
916 // will fail if the ACLs aren't 32-bit aligned.
917 if (!RtlValidRelativeSecurityDescriptor(fcb
->sd
, di
->m
, 0))
922 } 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
) {
924 static const char lzo
[] = "lzo";
925 static const char zlib
[] = "zlib";
926 static const char zstd
[] = "zstd";
928 if (di
->m
== sizeof(lzo
) - 1 && RtlCompareMemory(&di
->name
[di
->n
], lzo
, di
->m
) == di
->m
)
929 fcb
->prop_compression
= PropCompression_LZO
;
930 else if (di
->m
== sizeof(zlib
) - 1 && RtlCompareMemory(&di
->name
[di
->n
], zlib
, di
->m
) == di
->m
)
931 fcb
->prop_compression
= PropCompression_Zlib
;
932 else if (di
->m
== sizeof(zstd
) - 1 && RtlCompareMemory(&di
->name
[di
->n
], zstd
, di
->m
) == di
->m
)
933 fcb
->prop_compression
= PropCompression_ZSTD
;
935 fcb
->prop_compression
= PropCompression_None
;
937 } else if (di
->n
> sizeof(xapref
) - 1 && RtlCompareMemory(xapref
, di
->name
, sizeof(xapref
) - 1) == sizeof(xapref
) - 1) {
941 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &utf16len
, &di
->name
[sizeof(xapref
) - 1], di
->n
+ 1 - sizeof(xapref
));
942 if (!NT_SUCCESS(Status
)) {
943 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
948 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
950 ERR("out of memory\n");
952 return STATUS_INSUFFICIENT_RESOURCES
;
955 RtlZeroMemory(dc
, sizeof(dir_child
));
957 dc
->utf8
.MaximumLength
= dc
->utf8
.Length
= di
->n
+ 1 - sizeof(xapref
);
958 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dc
->utf8
.MaximumLength
, ALLOC_TAG
);
959 if (!dc
->utf8
.Buffer
) {
960 ERR("out of memory\n");
963 return STATUS_INSUFFICIENT_RESOURCES
;
966 RtlCopyMemory(dc
->utf8
.Buffer
, &di
->name
[sizeof(xapref
) - 1], dc
->utf8
.Length
);
968 dc
->name
.MaximumLength
= dc
->name
.Length
= (UINT16
)utf16len
;
969 dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dc
->name
.MaximumLength
, ALLOC_TAG
);
970 if (!dc
->name
.Buffer
) {
971 ERR("out of memory\n");
972 ExFreePool(dc
->utf8
.Buffer
);
975 return STATUS_INSUFFICIENT_RESOURCES
;
978 Status
= RtlUTF8ToUnicodeN(dc
->name
.Buffer
, utf16len
, &utf16len
, dc
->utf8
.Buffer
, dc
->utf8
.Length
);
979 if (!NT_SUCCESS(Status
)) {
980 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
981 ExFreePool(dc
->utf8
.Buffer
);
982 ExFreePool(dc
->name
.Buffer
);
988 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, &dc
->name
, TRUE
);
989 if (!NT_SUCCESS(Status
)) {
990 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
991 ExFreePool(dc
->utf8
.Buffer
);
992 ExFreePool(dc
->name
.Buffer
);
1000 InsertTailList(&fcb
->dir_children_index
, &dc
->list_entry_index
);
1004 xa
= ExAllocatePoolWithTag(PagedPool
, offsetof(xattr
, data
[0]) + di
->m
+ di
->n
, ALLOC_TAG
);
1006 ERR("out of memory\n");
1008 return STATUS_INSUFFICIENT_RESOURCES
;
1011 xa
->namelen
= di
->n
;
1012 xa
->valuelen
= di
->m
;
1014 RtlCopyMemory(xa
->data
, di
->name
, di
->m
+ di
->n
);
1016 InsertTailList(&fcb
->xattrs
, &xa
->list_entry
);
1019 len
-= (ULONG
)offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
;
1021 if (len
< offsetof(DIR_ITEM
, name
[0]))
1024 di
= (DIR_ITEM
*)&di
->name
[di
->m
+ di
->n
];
1026 } else if (tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
) {
1028 BOOL unique
= FALSE
;
1030 ed
= (EXTENT_DATA
*)tp
.item
->data
;
1032 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
1033 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
,
1034 tp
.item
->size
, sizeof(EXTENT_DATA
));
1037 return STATUS_INTERNAL_ERROR
;
1040 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
1041 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)&ed
->data
[0];
1043 if (tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
1044 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
,
1045 tp
.item
->size
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
));
1048 return STATUS_INTERNAL_ERROR
;
1051 if (ed2
->address
== 0 || ed2
->size
== 0) // sparse
1054 if (ed2
->size
!= 0 && is_tree_unique(Vcb
, tp
.tree
, Irp
))
1055 unique
= is_extent_unique(Vcb
, ed2
->address
, ed2
->size
, Irp
);
1058 ext
= ExAllocatePoolWithTag(pooltype
, offsetof(extent
, extent_data
) + tp
.item
->size
, ALLOC_TAG
);
1060 ERR("out of memory\n");
1062 return STATUS_INSUFFICIENT_RESOURCES
;
1065 ext
->offset
= tp
.item
->key
.offset
;
1066 RtlCopyMemory(&ext
->extent_data
, tp
.item
->data
, tp
.item
->size
);
1067 ext
->datalen
= tp
.item
->size
;
1068 ext
->unique
= unique
;
1069 ext
->ignore
= FALSE
;
1070 ext
->inserted
= FALSE
;
1073 InsertTailList(&fcb
->extents
, &ext
->list_entry
);
1077 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
1078 Status
= load_dir_children(Vcb
, fcb
, FALSE
, Irp
);
1079 if (!NT_SUCCESS(Status
)) {
1080 ERR("load_dir_children returned %08x\n", Status
);
1087 fcb
->Header
.AllocationSize
.QuadPart
= 0;
1088 fcb
->Header
.FileSize
.QuadPart
= 0;
1089 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
1091 if (ed
&& ed
->type
== EXTENT_TYPE_INLINE
)
1092 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->inode_item
.st_size
;
1094 fcb
->Header
.AllocationSize
.QuadPart
= sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
1096 fcb
->Header
.FileSize
.QuadPart
= fcb
->inode_item
.st_size
;
1097 fcb
->Header
.ValidDataLength
.QuadPart
= fcb
->inode_item
.st_size
;
1101 fcb
->atts
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
, utf8
&& utf8
->Buffer
[0] == '.', TRUE
, Irp
);
1104 fcb_get_sd(fcb
, parent
, FALSE
, Irp
);
1106 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
&& fcb
->reparse_xattr
.Length
== 0) {
1107 fcb
->atts
&= ~FILE_ATTRIBUTE_REPARSE_POINT
;
1109 if (!Vcb
->readonly
&& !is_subvol_readonly(subvol
, Irp
)) {
1110 fcb
->atts_changed
= TRUE
;
1111 mark_fcb_dirty(fcb
);
1116 InsertHeadList(lastle
, &fcb
->list_entry
);
1118 InsertTailList(&subvol
->fcbs
, &fcb
->list_entry
);
1120 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
1122 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
1125 return STATUS_SUCCESS
;
1128 static NTSTATUS
open_fcb_stream(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
1129 dir_child
* dc
, fcb
* parent
, fcb
** pfcb
, PIRP Irp
) {
1132 UINT16 xattrlen
, overhead
;
1136 static const char xapref
[] = "user.";
1140 xattr
.Length
= sizeof(xapref
) - 1 + dc
->utf8
.Length
;
1141 xattr
.MaximumLength
= xattr
.Length
+ 1;
1142 xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, xattr
.MaximumLength
, ALLOC_TAG
);
1143 if (!xattr
.Buffer
) {
1144 ERR("out of memory\n");
1145 return STATUS_INSUFFICIENT_RESOURCES
;
1148 RtlCopyMemory(xattr
.Buffer
, xapref
, sizeof(xapref
) - 1);
1149 RtlCopyMemory(&xattr
.Buffer
[sizeof(xapref
) - 1], dc
->utf8
.Buffer
, dc
->utf8
.Length
);
1150 xattr
.Buffer
[xattr
.Length
] = 0;
1152 fcb
= create_fcb(Vcb
, PagedPool
);
1154 ERR("out of memory\n");
1155 ExFreePool(xattr
.Buffer
);
1156 return STATUS_INSUFFICIENT_RESOURCES
;
1161 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)xattr
.Buffer
, xattr
.Length
);
1163 if (!get_xattr(Vcb
, parent
->subvol
, parent
->inode
, xattr
.Buffer
, crc32
, &xattrdata
, &xattrlen
, Irp
)) {
1164 ERR("get_xattr failed\n");
1166 ExFreePool(xattr
.Buffer
);
1167 return STATUS_INTERNAL_ERROR
;
1170 fcb
->subvol
= parent
->subvol
;
1171 fcb
->inode
= parent
->inode
;
1172 fcb
->type
= parent
->type
;
1174 fcb
->adshash
= crc32
;
1175 fcb
->adsxattr
= xattr
;
1177 // find XATTR_ITEM overhead and hence calculate maximum length
1179 searchkey
.obj_id
= parent
->inode
;
1180 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
1181 searchkey
.offset
= crc32
;
1183 Status
= find_item(Vcb
, parent
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
1184 if (!NT_SUCCESS(Status
)) {
1185 ERR("find_item returned %08x\n", Status
);
1190 if (keycmp(tp
.item
->key
, searchkey
)) {
1191 ERR("error - could not find key for xattr\n");
1193 return STATUS_INTERNAL_ERROR
;
1196 if (tp
.item
->size
< xattrlen
) {
1197 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
);
1199 return STATUS_INTERNAL_ERROR
;
1202 overhead
= tp
.item
->size
- xattrlen
;
1204 fcb
->adsmaxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
) - overhead
;
1206 fcb
->adsdata
.Buffer
= (char*)xattrdata
;
1207 fcb
->adsdata
.Length
= fcb
->adsdata
.MaximumLength
= xattrlen
;
1209 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
1210 fcb
->Header
.AllocationSize
.QuadPart
= xattrlen
;
1211 fcb
->Header
.FileSize
.QuadPart
= xattrlen
;
1212 fcb
->Header
.ValidDataLength
.QuadPart
= xattrlen
;
1214 TRACE("stream found: size = %x, hash = %08x\n", xattrlen
, fcb
->adshash
);
1216 InsertHeadList(&parent
->list_entry
, &fcb
->list_entry
);
1218 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
1222 return STATUS_SUCCESS
;
1225 NTSTATUS
open_fileref_child(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) _In_ device_extension
* Vcb
,
1226 _In_ file_ref
* sf
, _In_ PUNICODE_STRING name
, _In_ BOOL case_sensitive
, _In_ BOOL lastpart
, _In_ BOOL streampart
,
1227 _In_ POOL_TYPE pooltype
, _Out_ file_ref
** psf2
, _In_opt_ PIRP Irp
) {
1231 if (sf
->fcb
== Vcb
->dummy_fcb
)
1232 return STATUS_OBJECT_NAME_NOT_FOUND
;
1235 BOOL locked
= FALSE
;
1237 UNICODE_STRING name_uc
;
1238 dir_child
* dc
= NULL
;
1241 if (!case_sensitive
) {
1242 Status
= RtlUpcaseUnicodeString(&name_uc
, name
, TRUE
);
1243 if (!NT_SUCCESS(Status
)) {
1244 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1249 if (!ExIsResourceAcquiredSharedLite(&sf
->fcb
->nonpaged
->dir_children_lock
)) {
1250 ExAcquireResourceSharedLite(&sf
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1254 le
= sf
->fcb
->dir_children_index
.Flink
;
1255 while (le
!= &sf
->fcb
->dir_children_index
) {
1256 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
1258 if (dc2
->index
== 0) {
1259 if ((case_sensitive
&& dc2
->name
.Length
== name
->Length
&& RtlCompareMemory(dc2
->name
.Buffer
, name
->Buffer
, dc2
->name
.Length
) == dc2
->name
.Length
) ||
1260 (!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
)
1271 if (!case_sensitive
)
1272 ExFreePool(name_uc
.Buffer
);
1275 ExReleaseResourceLite(&sf
->fcb
->nonpaged
->dir_children_lock
);
1278 return STATUS_OBJECT_NAME_NOT_FOUND
;
1281 increase_fileref_refcount(dc
->fileref
);
1282 *psf2
= dc
->fileref
;
1283 return STATUS_SUCCESS
;
1286 Status
= open_fcb_stream(Vcb
, dc
, sf
->fcb
, &fcb
, Irp
);
1287 if (!NT_SUCCESS(Status
)) {
1288 ERR("open_fcb_stream returned %08x\n", Status
);
1292 sf2
= create_fileref(Vcb
);
1294 ERR("out of memory\n");
1296 return STATUS_INSUFFICIENT_RESOURCES
;
1301 sf2
->parent
= (struct _file_ref
*)sf
;
1306 ExAcquireResourceExclusiveLite(&sf
->nonpaged
->children_lock
, TRUE
);
1307 InsertTailList(&sf
->children
, &sf2
->list_entry
);
1308 ExReleaseResourceLite(&sf
->nonpaged
->children_lock
);
1310 increase_fileref_refcount(sf
);
1316 Status
= find_file_in_dir(name
, sf
->fcb
, &subvol
, &inode
, &dc
, case_sensitive
);
1317 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
1318 TRACE("could not find %.*S\n", name
->Length
/ sizeof(WCHAR
), name
->Buffer
);
1320 return lastpart
? STATUS_OBJECT_NAME_NOT_FOUND
: STATUS_OBJECT_PATH_NOT_FOUND
;
1321 } else if (!NT_SUCCESS(Status
)) {
1322 ERR("find_file_in_dir returned %08x\n", Status
);
1327 LARGE_INTEGER time1
, time2
;
1331 if (!lastpart
&& dc
->type
!= BTRFS_TYPE_DIRECTORY
) {
1332 TRACE("passed path including file as subdirectory\n");
1333 return STATUS_OBJECT_PATH_NOT_FOUND
;
1336 InterlockedIncrement(&dc
->fileref
->refcount
);
1337 *psf2
= dc
->fileref
;
1338 return STATUS_SUCCESS
;
1341 if (!subvol
|| (subvol
!= Vcb
->root_fileref
->fcb
->subvol
&& inode
== SUBVOL_ROOT_INODE
&& subvol
->parent
!= sf
->fcb
->subvol
->id
)) {
1342 fcb
= Vcb
->dummy_fcb
;
1343 InterlockedIncrement(&fcb
->refcount
);
1346 time1
= KeQueryPerformanceCounter(NULL
);
1348 Status
= open_fcb(Vcb
, subvol
, inode
, dc
->type
, &dc
->utf8
, sf
->fcb
, &fcb
, pooltype
, Irp
);
1350 time2
= KeQueryPerformanceCounter(NULL
);
1351 Vcb
->stats
.open_fcb_calls
++;
1352 Vcb
->stats
.open_fcb_time
+= time2
.QuadPart
- time1
.QuadPart
;
1355 if (!NT_SUCCESS(Status
)) {
1356 ERR("open_fcb returned %08x\n", Status
);
1361 if (dc
->type
!= BTRFS_TYPE_DIRECTORY
&& !lastpart
&& !(fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
)) {
1362 TRACE("passed path including file as subdirectory\n");
1364 return STATUS_OBJECT_PATH_NOT_FOUND
;
1367 sf2
= create_fileref(Vcb
);
1369 ERR("out of memory\n");
1371 return STATUS_INSUFFICIENT_RESOURCES
;
1376 if (dc
->type
== BTRFS_TYPE_DIRECTORY
)
1382 sf2
->parent
= (struct _file_ref
*)sf
;
1384 ExAcquireResourceExclusiveLite(&sf
->nonpaged
->children_lock
, TRUE
);
1385 InsertTailList(&sf
->children
, &sf2
->list_entry
);
1386 ExReleaseResourceLite(&sf
->nonpaged
->children_lock
);
1388 increase_fileref_refcount(sf
);
1394 return STATUS_SUCCESS
;
1397 NTSTATUS
open_fileref(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) _In_ device_extension
* Vcb
, _Out_ file_ref
** pfr
,
1398 _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
,
1399 _In_ BOOL case_sensitive
, _In_opt_ PIRP Irp
) {
1400 UNICODE_STRING fnus2
;
1401 file_ref
*dir
, *sf
, *sf2
;
1407 TRACE("(%p, %p, %p, %u, %p)\n", Vcb
, pfr
, related
, parent
, parsed
);
1410 if (!ExIsResourceAcquiredExclusiveLite(&Vcb
->fcb_lock
) && !ExIsResourceAcquiredExclusiveLite(&Vcb
->tree_lock
)) {
1411 ERR("fcb_lock not acquired exclusively\n");
1416 if (Vcb
->removing
|| Vcb
->locked
)
1417 return STATUS_ACCESS_DENIED
;
1421 if (fnus2
.Length
< sizeof(WCHAR
) && !related
) {
1422 ERR("error - fnus was too short\n");
1423 return STATUS_INTERNAL_ERROR
;
1426 if (related
&& fnus
->Length
== 0) {
1427 increase_fileref_refcount(related
);
1430 return STATUS_SUCCESS
;
1436 if (fnus2
.Buffer
[0] != '\\') {
1437 ERR("error - filename %.*S did not begin with \\\n", fnus2
.Length
/ sizeof(WCHAR
), fnus2
.Buffer
);
1438 return STATUS_OBJECT_PATH_NOT_FOUND
;
1441 // if path starts with two backslashes, ignore one of them
1442 if (fnus2
.Length
>= 2 * sizeof(WCHAR
) && fnus2
.Buffer
[1] == '\\') {
1444 fnus2
.Length
-= sizeof(WCHAR
);
1445 fnus2
.MaximumLength
-= sizeof(WCHAR
);
1448 if (fnus2
.Length
== sizeof(WCHAR
)) {
1449 if (Vcb
->root_fileref
->open_count
== 0 && !(Vcb
->Vpb
->Flags
& VPB_MOUNTED
)) // don't allow root to be opened on unmounted FS
1450 return STATUS_DEVICE_NOT_READY
;
1452 increase_fileref_refcount(Vcb
->root_fileref
);
1453 *pfr
= Vcb
->root_fileref
;
1458 return STATUS_SUCCESS
;
1461 dir
= Vcb
->root_fileref
;
1464 fnus2
.Length
-= sizeof(WCHAR
);
1465 fnus2
.MaximumLength
-= sizeof(WCHAR
);
1468 if (dir
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& (fnus
->Length
< sizeof(WCHAR
) || fnus
->Buffer
[0] != ':')) {
1469 WARN("passed related fileref which isn't a directory (%S) (fnus = %.*S)\n",
1470 file_desc_fileref(related
), fnus
->Length
/ sizeof(WCHAR
), fnus
->Buffer
);
1471 return STATUS_OBJECT_PATH_NOT_FOUND
;
1474 InitializeListHead(&parts
);
1476 if (fnus
->Length
!= 0 &&
1477 (fnus
->Length
!= sizeof(datastring
) - sizeof(WCHAR
) || RtlCompareMemory(fnus
->Buffer
, datastring
, sizeof(datastring
) - sizeof(WCHAR
)) != sizeof(datastring
) - sizeof(WCHAR
))) {
1478 Status
= split_path(Vcb
, &fnus2
, &parts
, &has_stream
);
1479 if (!NT_SUCCESS(Status
)) {
1480 ERR("split_path returned %08x\n", Status
);
1486 increase_fileref_refcount(dir
);
1488 if (parent
&& !IsListEmpty(&parts
)) {
1491 nb
= CONTAINING_RECORD(RemoveTailList(&parts
), name_bit
, list_entry
);
1494 if (has_stream
&& !IsListEmpty(&parts
)) {
1495 nb
= CONTAINING_RECORD(RemoveTailList(&parts
), name_bit
, list_entry
);
1502 if (IsListEmpty(&parts
)) {
1503 Status
= STATUS_SUCCESS
;
1514 name_bit
* nb
= CONTAINING_RECORD(le
, name_bit
, list_entry
);
1515 BOOL lastpart
= le
->Flink
== &parts
|| (has_stream
&& le
->Flink
->Flink
== &parts
);
1516 BOOL streampart
= has_stream
&& le
->Flink
== &parts
;
1518 LARGE_INTEGER time1
, time2
;
1522 time1
= KeQueryPerformanceCounter(NULL
);
1524 Status
= open_fileref_child(Vcb
, sf
, &nb
->us
, case_sensitive
, lastpart
, streampart
, pooltype
, &sf2
, Irp
);
1526 time2
= KeQueryPerformanceCounter(NULL
);
1527 Vcb
->stats
.open_fileref_child_calls
++;
1528 Vcb
->stats
.open_fileref_child_time
+= time2
.QuadPart
- time1
.QuadPart
;
1530 if (!NT_SUCCESS(Status
)) {
1531 if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
|| Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1532 TRACE("open_fileref_child returned %08x\n", Status
);
1534 ERR("open_fileref_child returned %08x\n", Status
);
1539 if (le
->Flink
== &parts
) { // last entry
1542 nb
= CONTAINING_RECORD(le
->Blink
, name_bit
, list_entry
);
1544 *fn_offset
= (ULONG
)(nb
->us
.Buffer
- fnus
->Buffer
);
1550 if (sf2
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
) {
1551 Status
= STATUS_REPARSE
;
1554 name_bit
* nb2
= CONTAINING_RECORD(le
->Flink
, name_bit
, list_entry
);
1556 *parsed
= (USHORT
)(nb2
->us
.Buffer
- fnus
->Buffer
- 1) * sizeof(WCHAR
);
1562 free_fileref(Vcb
, sf
);
1566 } while (le
!= &parts
);
1568 if (Status
!= STATUS_REPARSE
)
1569 Status
= STATUS_SUCCESS
;
1573 free_fileref(Vcb
, sf
);
1575 while (!IsListEmpty(&parts
)) {
1576 name_bit
* nb
= CONTAINING_RECORD(RemoveHeadList(&parts
), name_bit
, list_entry
);
1577 ExFreeToPagedLookasideList(&Vcb
->name_bit_lookaside
, nb
);
1581 TRACE("returning %08x\n", Status
);
1586 NTSTATUS
add_dir_child(fcb
* fcb
, UINT64 inode
, BOOL subvol
, PANSI_STRING utf8
, PUNICODE_STRING name
, UINT8 type
, dir_child
** pdc
) {
1590 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
1592 ERR("out of memory\n");
1593 return STATUS_INSUFFICIENT_RESOURCES
;
1596 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
->Length
, ALLOC_TAG
);
1597 if (!dc
->utf8
.Buffer
) {
1598 ERR("out of memory\n");
1600 return STATUS_INSUFFICIENT_RESOURCES
;
1603 dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, name
->Length
, ALLOC_TAG
);
1604 if (!dc
->name
.Buffer
) {
1605 ERR("out of memory\n");
1606 ExFreePool(dc
->utf8
.Buffer
);
1608 return STATUS_INSUFFICIENT_RESOURCES
;
1611 dc
->key
.obj_id
= inode
;
1612 dc
->key
.obj_type
= subvol
? TYPE_ROOT_ITEM
: TYPE_INODE_ITEM
;
1613 dc
->key
.offset
= subvol
? 0xffffffffffffffff : 0;
1617 dc
->utf8
.Length
= dc
->utf8
.MaximumLength
= utf8
->Length
;
1618 RtlCopyMemory(dc
->utf8
.Buffer
, utf8
->Buffer
, utf8
->Length
);
1620 dc
->name
.Length
= dc
->name
.MaximumLength
= name
->Length
;
1621 RtlCopyMemory(dc
->name
.Buffer
, name
->Buffer
, name
->Length
);
1623 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, name
, TRUE
);
1624 if (!NT_SUCCESS(Status
)) {
1625 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1626 ExFreePool(dc
->utf8
.Buffer
);
1627 ExFreePool(dc
->name
.Buffer
);
1632 dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name
.Buffer
, dc
->name
.Length
);
1633 dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name_uc
.Buffer
, dc
->name_uc
.Length
);
1635 ExAcquireResourceExclusiveLite(&fcb
->nonpaged
->dir_children_lock
, TRUE
);
1637 if (IsListEmpty(&fcb
->dir_children_index
))
1640 dir_child
* dc2
= CONTAINING_RECORD(fcb
->dir_children_index
.Blink
, dir_child
, list_entry_index
);
1642 dc
->index
= max(2, dc2
->index
+ 1);
1645 InsertTailList(&fcb
->dir_children_index
, &dc
->list_entry_index
);
1647 insert_dir_child_into_hash_lists(fcb
, dc
);
1649 ExReleaseResourceLite(&fcb
->nonpaged
->dir_children_lock
);
1653 return STATUS_SUCCESS
;
1656 UINT32
inherit_mode(fcb
* parfcb
, BOOL is_dir
) {
1662 mode
= parfcb
->inode_item
.st_mode
& ~S_IFDIR
;
1663 mode
&= ~S_ISVTX
; // clear sticky bit
1664 mode
&= ~S_ISUID
; // clear setuid bit
1667 mode
&= ~S_ISGID
; // if not directory, clear setgid bit
1672 static NTSTATUS
file_create_parse_ea(fcb
* fcb
, FILE_FULL_EA_INFORMATION
* ea
) {
1674 LIST_ENTRY ealist
, *le
;
1678 InitializeListHead(&ealist
);
1684 s
.Length
= s
.MaximumLength
= ea
->EaNameLength
;
1685 s
.Buffer
= ea
->EaName
;
1687 RtlUpperString(&s
, &s
);
1690 while (le
!= &ealist
) {
1691 ea_item
* item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
1693 if (item
->name
.Length
== s
.Length
&& RtlCompareMemory(item
->name
.Buffer
, s
.Buffer
, s
.Length
) == s
.Length
) {
1694 item
->flags
= ea
->Flags
;
1695 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
1696 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
1705 ea_item
* item
= ExAllocatePoolWithTag(PagedPool
, sizeof(ea_item
), ALLOC_TAG
);
1707 ERR("out of memory\n");
1708 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1712 item
->name
.Length
= item
->name
.MaximumLength
= ea
->EaNameLength
;
1713 item
->name
.Buffer
= ea
->EaName
;
1715 item
->value
.Length
= item
->value
.MaximumLength
= ea
->EaValueLength
;
1716 item
->value
.Buffer
= &ea
->EaName
[ea
->EaNameLength
+ 1];
1718 item
->flags
= ea
->Flags
;
1720 InsertTailList(&ealist
, &item
->list_entry
);
1723 if (ea
->NextEntryOffset
== 0)
1726 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
1729 // handle LXSS values
1731 while (le
!= &ealist
) {
1732 LIST_ENTRY
* le2
= le
->Flink
;
1733 ea_item
* item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
1735 if (item
->name
.Length
== sizeof(lxuid
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxuid
, item
->name
.Length
) == item
->name
.Length
) {
1736 if (item
->value
.Length
< sizeof(UINT32
)) {
1737 ERR("uid value was shorter than expected\n");
1738 Status
= STATUS_INVALID_PARAMETER
;
1742 RtlCopyMemory(&fcb
->inode_item
.st_uid
, item
->value
.Buffer
, sizeof(UINT32
));
1743 fcb
->sd_dirty
= TRUE
;
1744 fcb
->sd_deleted
= FALSE
;
1746 RemoveEntryList(&item
->list_entry
);
1748 } else if (item
->name
.Length
== sizeof(lxgid
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxgid
, item
->name
.Length
) == item
->name
.Length
) {
1749 if (item
->value
.Length
< sizeof(UINT32
)) {
1750 ERR("gid value was shorter than expected\n");
1751 Status
= STATUS_INVALID_PARAMETER
;
1755 RtlCopyMemory(&fcb
->inode_item
.st_gid
, item
->value
.Buffer
, sizeof(UINT32
));
1757 RemoveEntryList(&item
->list_entry
);
1759 } else if (item
->name
.Length
== sizeof(lxmod
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxmod
, item
->name
.Length
) == item
->name
.Length
) {
1760 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
;
1763 if (item
->value
.Length
< sizeof(UINT32
)) {
1764 ERR("mode value was shorter than expected\n");
1765 Status
= STATUS_INVALID_PARAMETER
;
1769 RtlCopyMemory(&val
, item
->value
.Buffer
, sizeof(UINT32
));
1771 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
)
1772 allowed
|= __S_IFIFO
| __S_IFCHR
| __S_IFBLK
| __S_IFSOCK
;
1774 fcb
->inode_item
.st_mode
&= ~allowed
;
1775 fcb
->inode_item
.st_mode
|= val
& allowed
;
1777 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
1778 if ((fcb
->inode_item
.st_mode
& __S_IFCHR
) == __S_IFCHR
)
1779 fcb
->type
= BTRFS_TYPE_CHARDEV
;
1780 else if ((fcb
->inode_item
.st_mode
& __S_IFBLK
) == __S_IFBLK
)
1781 fcb
->type
= BTRFS_TYPE_BLOCKDEV
;
1782 else if ((fcb
->inode_item
.st_mode
& __S_IFIFO
) == __S_IFIFO
)
1783 fcb
->type
= BTRFS_TYPE_FIFO
;
1784 else if ((fcb
->inode_item
.st_mode
& __S_IFSOCK
) == __S_IFSOCK
)
1785 fcb
->type
= BTRFS_TYPE_SOCKET
;
1788 RemoveEntryList(&item
->list_entry
);
1790 } else if (item
->name
.Length
== sizeof(lxdev
) - 1 && RtlCompareMemory(item
->name
.Buffer
, lxdev
, item
->name
.Length
) == item
->name
.Length
) {
1791 UINT32 major
, minor
;
1793 if (item
->value
.Length
< sizeof(UINT64
)) {
1794 ERR("dev value was shorter than expected\n");
1795 Status
= STATUS_INVALID_PARAMETER
;
1799 major
= *(UINT32
*)item
->value
.Buffer
;
1800 minor
= *(UINT32
*)&item
->value
.Buffer
[sizeof(UINT32
)];
1802 fcb
->inode_item
.st_rdev
= (minor
& 0xFFFFF) | ((major
& 0xFFFFFFFFFFF) << 20);
1804 RemoveEntryList(&item
->list_entry
);
1811 if (fcb
->type
!= BTRFS_TYPE_CHARDEV
&& fcb
->type
!= BTRFS_TYPE_BLOCKDEV
)
1812 fcb
->inode_item
.st_rdev
= 0;
1814 if (IsListEmpty(&ealist
))
1815 return STATUS_SUCCESS
;
1818 while (le
!= &ealist
) {
1819 ea_item
* item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
1822 size
+= 4 - (size
% 4);
1824 size
+= (UINT16
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + item
->name
.Length
+ 1 + item
->value
.Length
;
1829 buf
= ExAllocatePoolWithTag(PagedPool
, size
, ALLOC_TAG
);
1831 ERR("out of memory\n");
1832 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1836 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= size
;
1837 fcb
->ea_xattr
.Buffer
= buf
;
1843 while (le
!= &ealist
) {
1844 ea_item
* item
= CONTAINING_RECORD(le
, ea_item
, list_entry
);
1847 ea
->NextEntryOffset
= (ULONG
)offsetof(FILE_FULL_EA_INFORMATION
, EaName
[0]) + ea
->EaNameLength
+ ea
->EaValueLength
;
1849 if (ea
->NextEntryOffset
% 4 > 0)
1850 ea
->NextEntryOffset
+= 4 - (ea
->NextEntryOffset
% 4);
1852 ea
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ea
) + ea
->NextEntryOffset
);
1854 ea
= (FILE_FULL_EA_INFORMATION
*)fcb
->ea_xattr
.Buffer
;
1856 ea
->NextEntryOffset
= 0;
1857 ea
->Flags
= item
->flags
;
1858 ea
->EaNameLength
= (UCHAR
)item
->name
.Length
;
1859 ea
->EaValueLength
= item
->value
.Length
;
1861 RtlCopyMemory(ea
->EaName
, item
->name
.Buffer
, item
->name
.Length
);
1862 ea
->EaName
[item
->name
.Length
] = 0;
1863 RtlCopyMemory(&ea
->EaName
[item
->name
.Length
+ 1], item
->value
.Buffer
, item
->value
.Length
);
1865 fcb
->ealen
+= 5 + item
->name
.Length
+ item
->value
.Length
;
1870 fcb
->ea_changed
= TRUE
;
1872 Status
= STATUS_SUCCESS
;
1875 while (!IsListEmpty(&ealist
)) {
1876 ea_item
* item
= CONTAINING_RECORD(RemoveHeadList(&ealist
), ea_item
, list_entry
);
1884 static NTSTATUS
file_create2(_In_ PIRP Irp
, _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) _In_ device_extension
* Vcb
, _In_ PUNICODE_STRING fpus
,
1885 _In_ file_ref
* parfileref
, _In_ ULONG options
, _In_reads_bytes_opt_(ealen
) FILE_FULL_EA_INFORMATION
* ea
, _In_ ULONG ealen
,
1886 _Out_ file_ref
** pfr
, _In_ LIST_ENTRY
* rollback
) {
1895 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1896 POOL_TYPE pool_type
= IrpSp
->Flags
& SL_OPEN_PAGING_FILE
? NonPagedPool
: PagedPool
;
1901 #ifdef DEBUG_FCB_REFCOUNTS
1905 if (parfileref
->fcb
== Vcb
->dummy_fcb
)
1906 return STATUS_ACCESS_DENIED
;
1908 if (options
& FILE_DIRECTORY_FILE
&& IrpSp
->Parameters
.Create
.FileAttributes
& FILE_ATTRIBUTE_TEMPORARY
)
1909 return STATUS_INVALID_PARAMETER
;
1911 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, fpus
->Buffer
, fpus
->Length
);
1912 if (!NT_SUCCESS(Status
)) {
1913 ERR("RtlUnicodeToUTF8N returned %08x\n", Status
);
1917 utf8
= ExAllocatePoolWithTag(pool_type
, utf8len
+ 1, ALLOC_TAG
);
1919 ERR("out of memory\n");
1920 return STATUS_INSUFFICIENT_RESOURCES
;
1923 Status
= RtlUnicodeToUTF8N(utf8
, utf8len
, &utf8len
, fpus
->Buffer
, fpus
->Length
);
1924 if (!NT_SUCCESS(Status
)) {
1925 ERR("RtlUnicodeToUTF8N returned %08x\n", Status
);
1932 KeQuerySystemTime(&time
);
1933 win_time_to_unix(time
, &now
);
1935 TRACE("create file %.*S\n", fpus
->Length
/ sizeof(WCHAR
), fpus
->Buffer
);
1936 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1937 TRACE("parfileref->fcb->inode_item.st_size (inode %llx) was %llx\n", parfileref
->fcb
->inode
, parfileref
->fcb
->inode_item
.st_size
);
1938 parfileref
->fcb
->inode_item
.st_size
+= utf8len
* 2;
1939 TRACE("parfileref->fcb->inode_item.st_size (inode %llx) now %llx\n", parfileref
->fcb
->inode
, parfileref
->fcb
->inode_item
.st_size
);
1940 parfileref
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1941 parfileref
->fcb
->inode_item
.sequence
++;
1942 parfileref
->fcb
->inode_item
.st_ctime
= now
;
1943 parfileref
->fcb
->inode_item
.st_mtime
= now
;
1944 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1946 parfileref
->fcb
->inode_item_changed
= TRUE
;
1947 mark_fcb_dirty(parfileref
->fcb
);
1949 inode
= InterlockedIncrement64(&parfileref
->fcb
->subvol
->lastinode
);
1951 type
= options
& FILE_DIRECTORY_FILE
? BTRFS_TYPE_DIRECTORY
: BTRFS_TYPE_FILE
;
1953 // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
1955 TRACE("requested attributes = %x\n", IrpSp
->Parameters
.Create
.FileAttributes
);
1960 defda
|= FILE_ATTRIBUTE_HIDDEN
;
1962 if (options
& FILE_DIRECTORY_FILE
) {
1963 defda
|= FILE_ATTRIBUTE_DIRECTORY
;
1964 IrpSp
->Parameters
.Create
.FileAttributes
|= FILE_ATTRIBUTE_DIRECTORY
;
1966 IrpSp
->Parameters
.Create
.FileAttributes
&= ~FILE_ATTRIBUTE_DIRECTORY
;
1968 if (!(IrpSp
->Parameters
.Create
.FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)) {
1969 IrpSp
->Parameters
.Create
.FileAttributes
|= FILE_ATTRIBUTE_ARCHIVE
;
1970 defda
|= FILE_ATTRIBUTE_ARCHIVE
;
1973 TRACE("defda = %x\n", defda
);
1975 if (IrpSp
->Parameters
.Create
.FileAttributes
== FILE_ATTRIBUTE_NORMAL
)
1976 IrpSp
->Parameters
.Create
.FileAttributes
= defda
;
1978 fcb
= create_fcb(Vcb
, pool_type
);
1980 ERR("out of memory\n");
1983 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1984 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1985 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1987 return STATUS_INSUFFICIENT_RESOURCES
;
1992 if (IrpSp
->Flags
& SL_OPEN_PAGING_FILE
) {
1993 fcb
->Header
.Flags2
|= FSRTL_FLAG2_IS_PAGING_FILE
;
1994 Vcb
->disallow_dismount
= TRUE
;
1997 fcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
1998 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1999 fcb
->inode_item
.st_size
= 0;
2000 fcb
->inode_item
.st_blocks
= 0;
2001 fcb
->inode_item
.block_group
= 0;
2002 fcb
->inode_item
.st_nlink
= 1;
2003 fcb
->inode_item
.st_gid
= GID_NOBODY
; // FIXME?
2004 fcb
->inode_item
.st_mode
= inherit_mode(parfileref
->fcb
, type
== BTRFS_TYPE_DIRECTORY
); // use parent's permissions by default
2005 fcb
->inode_item
.st_rdev
= 0;
2006 fcb
->inode_item
.flags
= 0;
2007 fcb
->inode_item
.sequence
= 1;
2008 fcb
->inode_item
.st_atime
= now
;
2009 fcb
->inode_item
.st_ctime
= now
;
2010 fcb
->inode_item
.st_mtime
= now
;
2011 fcb
->inode_item
.otime
= now
;
2013 if (type
== BTRFS_TYPE_DIRECTORY
)
2014 fcb
->inode_item
.st_mode
|= S_IFDIR
;
2016 fcb
->inode_item
.st_mode
|= S_IFREG
;
2017 fcb
->inode_item
.st_mode
&= ~(S_IXUSR
| S_IXGRP
| S_IXOTH
); // remove executable bit if not directory
2020 if (IrpSp
->Flags
& SL_OPEN_PAGING_FILE
) {
2021 fcb
->inode_item
.flags
= BTRFS_INODE_NODATACOW
| BTRFS_INODE_NODATASUM
| BTRFS_INODE_NOCOMPRESS
;
2023 // inherit nodatacow flag from parent directory
2024 if (parfileref
->fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
) {
2025 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATACOW
;
2027 if (type
!= BTRFS_TYPE_DIRECTORY
)
2028 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATASUM
;
2031 if (parfileref
->fcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
2032 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
2035 fcb
->prop_compression
= parfileref
->fcb
->prop_compression
;
2036 fcb
->prop_compression_changed
= fcb
->prop_compression
!= PropCompression_None
;
2038 fcb
->inode_item_changed
= TRUE
;
2040 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
2041 fcb
->Header
.AllocationSize
.QuadPart
= 0;
2042 fcb
->Header
.FileSize
.QuadPart
= 0;
2043 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
2045 fcb
->atts
= IrpSp
->Parameters
.Create
.FileAttributes
& ~FILE_ATTRIBUTE_NORMAL
;
2046 fcb
->atts_changed
= fcb
->atts
!= defda
;
2048 #ifdef DEBUG_FCB_REFCOUNTS
2049 rc
= InterlockedIncrement(&parfileref
->fcb
->refcount
);
2050 WARN("fcb %p: refcount now %i (%S)\n", parfileref
->fcb
, rc
, file_desc_fileref(parfileref
));
2052 InterlockedIncrement(&parfileref
->fcb
->refcount
);
2054 fcb
->subvol
= parfileref
->fcb
->subvol
;
2057 fcb
->created
= TRUE
;
2058 fcb
->deleted
= TRUE
;
2060 mark_fcb_dirty(fcb
);
2062 Status
= fcb_get_new_sd(fcb
, parfileref
, IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
);
2064 if (!NT_SUCCESS(Status
)) {
2065 ERR("fcb_get_new_sd returned %08x\n", Status
);
2068 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2069 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2070 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2075 fcb
->sd_dirty
= TRUE
;
2077 if (ea
&& ealen
> 0) {
2078 Status
= file_create_parse_ea(fcb
, ea
);
2079 if (!NT_SUCCESS(Status
)) {
2080 ERR("file_create_parse_ea returned %08x\n", Status
);
2083 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2084 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2085 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2091 fileref
= create_fileref(Vcb
);
2093 ERR("out of memory\n");
2096 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2097 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2098 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2100 return STATUS_INSUFFICIENT_RESOURCES
;
2105 if (Irp
->Overlay
.AllocationSize
.QuadPart
> 0 && !write_fcb_compressed(fcb
)) {
2106 Status
= extend_file(fcb
, fileref
, Irp
->Overlay
.AllocationSize
.QuadPart
, TRUE
, NULL
, rollback
);
2108 if (!NT_SUCCESS(Status
)) {
2109 ERR("extend_file returned %08x\n", Status
);
2110 free_fileref(Vcb
, fileref
);
2112 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2113 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2114 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2120 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
2121 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
2122 if (!fcb
->hash_ptrs
) {
2123 ERR("out of memory\n");
2124 free_fileref(Vcb
, fileref
);
2126 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2127 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2128 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2130 return STATUS_INSUFFICIENT_RESOURCES
;
2133 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
2135 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
2136 if (!fcb
->hash_ptrs_uc
) {
2137 ERR("out of memory\n");
2138 free_fileref(Vcb
, fileref
);
2140 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2141 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
2142 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2144 return STATUS_INSUFFICIENT_RESOURCES
;
2147 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
2150 fcb
->deleted
= FALSE
;
2152 fileref
->created
= TRUE
;
2153 mark_fileref_dirty(fileref
);
2155 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
2156 fcb
->subvol
->root_item
.ctime
= now
;
2158 fileref
->parent
= parfileref
;
2160 utf8as
.Buffer
= utf8
;
2161 utf8as
.Length
= utf8as
.MaximumLength
= (UINT16
)utf8len
;
2163 Status
= add_dir_child(fileref
->parent
->fcb
, fcb
->inode
, FALSE
, &utf8as
, fpus
, fcb
->type
, &dc
);
2164 if (!NT_SUCCESS(Status
))
2165 WARN("add_dir_child returned %08x\n", Status
);
2170 dc
->fileref
= fileref
;
2172 ExAcquireResourceExclusiveLite(&parfileref
->nonpaged
->children_lock
, TRUE
);
2173 InsertTailList(&parfileref
->children
, &fileref
->list_entry
);
2174 ExReleaseResourceLite(&parfileref
->nonpaged
->children_lock
);
2176 increase_fileref_refcount(parfileref
);
2178 InsertTailList(&fcb
->subvol
->fcbs
, &fcb
->list_entry
);
2179 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
2183 if (type
== BTRFS_TYPE_DIRECTORY
)
2184 fileref
->fcb
->fileref
= fileref
;
2186 TRACE("created new file %S in subvol %llx, inode %llx\n", file_desc_fileref(fileref
), fcb
->subvol
->id
, fcb
->inode
);
2188 return STATUS_SUCCESS
;
2191 static NTSTATUS
create_stream(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
2192 file_ref
** pfileref
, file_ref
** pparfileref
, PUNICODE_STRING fpus
, PUNICODE_STRING stream
, PIRP Irp
,
2193 ULONG options
, POOL_TYPE pool_type
, BOOL case_sensitive
, LIST_ENTRY
* rollback
) {
2194 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2195 file_ref
*fileref
, *newpar
, *parfileref
;
2197 static const char xapref
[] = "user.";
2198 static const WCHAR DOSATTRIB
[] = L
"DOSATTRIB";
2199 static const WCHAR EA
[] = L
"EA";
2200 static const WCHAR reparse
[] = L
"reparse";
2203 ULONG utf8len
, overhead
;
2208 ACCESS_MASK granted_access
;
2209 #ifdef DEBUG_FCB_REFCOUNTS
2213 TRACE("fpus = %.*S\n", fpus
->Length
/ sizeof(WCHAR
), fpus
->Buffer
);
2214 TRACE("stream = %.*S\n", stream
->Length
/ sizeof(WCHAR
), stream
->Buffer
);
2216 parfileref
= *pparfileref
;
2218 if (parfileref
->fcb
== Vcb
->dummy_fcb
)
2219 return STATUS_ACCESS_DENIED
;
2221 Status
= open_fileref(Vcb
, &newpar
, fpus
, parfileref
, FALSE
, NULL
, NULL
, PagedPool
, case_sensitive
, Irp
);
2223 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
2224 UNICODE_STRING fpus2
;
2226 if (!is_file_name_valid(fpus
, FALSE
))
2227 return STATUS_OBJECT_NAME_INVALID
;
2229 fpus2
.Length
= fpus2
.MaximumLength
= fpus
->Length
;
2230 fpus2
.Buffer
= ExAllocatePoolWithTag(pool_type
, fpus2
.MaximumLength
, ALLOC_TAG
);
2232 if (!fpus2
.Buffer
) {
2233 ERR("out of memory\n");
2234 return STATUS_INSUFFICIENT_RESOURCES
;
2237 RtlCopyMemory(fpus2
.Buffer
, fpus
->Buffer
, fpus2
.Length
);
2239 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2241 if (!SeAccessCheck(parfileref
->fcb
->sd
, &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
2242 TRUE
, options
& FILE_DIRECTORY_FILE
? FILE_ADD_SUBDIRECTORY
: FILE_ADD_FILE
, 0, NULL
,
2243 IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
2244 &granted_access
, &Status
)) {
2245 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2249 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2251 Status
= file_create2(Irp
, Vcb
, &fpus2
, parfileref
, options
, NULL
, 0, &newpar
, rollback
);
2253 if (!NT_SUCCESS(Status
)) {
2254 ERR("file_create2 returned %08x\n", Status
);
2255 ExFreePool(fpus2
.Buffer
);
2259 send_notification_fileref(newpar
, options
& FILE_DIRECTORY_FILE
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
2260 send_notification_fcb(newpar
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2261 } else if (!NT_SUCCESS(Status
)) {
2262 ERR("open_fileref returned %08x\n", Status
);
2266 parfileref
= newpar
;
2267 *pparfileref
= parfileref
;
2269 if (parfileref
->fcb
->type
!= BTRFS_TYPE_FILE
&& parfileref
->fcb
->type
!= BTRFS_TYPE_SYMLINK
&& parfileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
2270 WARN("parent not file, directory, or symlink\n");
2271 return STATUS_INVALID_PARAMETER
;
2274 if (options
& FILE_DIRECTORY_FILE
) {
2275 WARN("tried to create directory as stream\n");
2276 return STATUS_INVALID_PARAMETER
;
2279 if (parfileref
->fcb
->atts
& FILE_ATTRIBUTE_READONLY
)
2280 return STATUS_ACCESS_DENIED
;
2282 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2284 if (!SeAccessCheck(parfileref
->fcb
->sd
, &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
2285 TRUE
, FILE_WRITE_DATA
, 0, NULL
, IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
2286 &granted_access
, &Status
)) {
2287 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2291 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2293 if ((stream
->Length
== sizeof(DOSATTRIB
) - sizeof(WCHAR
) && RtlCompareMemory(stream
->Buffer
, DOSATTRIB
, stream
->Length
) == stream
->Length
) ||
2294 (stream
->Length
== sizeof(EA
) - sizeof(WCHAR
) && RtlCompareMemory(stream
->Buffer
, EA
, stream
->Length
) == stream
->Length
) ||
2295 (stream
->Length
== sizeof(reparse
) - sizeof(WCHAR
) && RtlCompareMemory(stream
->Buffer
, reparse
, stream
->Length
) == stream
->Length
)) {
2296 return STATUS_OBJECT_NAME_INVALID
;
2299 fcb
= create_fcb(Vcb
, pool_type
);
2301 ERR("out of memory\n");
2302 return STATUS_INSUFFICIENT_RESOURCES
;
2307 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
2308 fcb
->Header
.AllocationSize
.QuadPart
= 0;
2309 fcb
->Header
.FileSize
.QuadPart
= 0;
2310 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
2312 #ifdef DEBUG_FCB_REFCOUNTS
2313 rc
= InterlockedIncrement(&parfileref
->fcb
->refcount
);
2314 WARN("fcb %p: refcount now %i (%S)\n", parfileref
->fcb
, rc
, file_desc_fileref(parfileref
));
2316 InterlockedIncrement(&parfileref
->fcb
->refcount
);
2318 fcb
->subvol
= parfileref
->fcb
->subvol
;
2319 fcb
->inode
= parfileref
->fcb
->inode
;
2320 fcb
->type
= parfileref
->fcb
->type
;
2324 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, stream
->Buffer
, stream
->Length
);
2325 if (!NT_SUCCESS(Status
)) {
2326 ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status
);
2331 fcb
->adsxattr
.Length
= (UINT16
)utf8len
+ sizeof(xapref
) - 1;
2332 fcb
->adsxattr
.MaximumLength
= fcb
->adsxattr
.Length
+ 1;
2333 fcb
->adsxattr
.Buffer
= ExAllocatePoolWithTag(pool_type
, fcb
->adsxattr
.MaximumLength
, ALLOC_TAG
);
2334 if (!fcb
->adsxattr
.Buffer
) {
2335 ERR("out of memory\n");
2337 return STATUS_INSUFFICIENT_RESOURCES
;
2340 RtlCopyMemory(fcb
->adsxattr
.Buffer
, xapref
, sizeof(xapref
) - 1);
2342 Status
= RtlUnicodeToUTF8N(&fcb
->adsxattr
.Buffer
[sizeof(xapref
) - 1], utf8len
, &utf8len
, stream
->Buffer
, stream
->Length
);
2343 if (!NT_SUCCESS(Status
)) {
2344 ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status
);
2349 fcb
->adsxattr
.Buffer
[fcb
->adsxattr
.Length
] = 0;
2351 TRACE("adsxattr = %s\n", fcb
->adsxattr
.Buffer
);
2353 fcb
->adshash
= calc_crc32c(0xfffffffe, (UINT8
*)fcb
->adsxattr
.Buffer
, fcb
->adsxattr
.Length
);
2354 TRACE("adshash = %08x\n", fcb
->adshash
);
2356 searchkey
.obj_id
= parfileref
->fcb
->inode
;
2357 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
2358 searchkey
.offset
= fcb
->adshash
;
2360 Status
= find_item(Vcb
, parfileref
->fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
2361 if (!NT_SUCCESS(Status
)) {
2362 ERR("find_item returned %08x\n", Status
);
2367 if (!keycmp(tp
.item
->key
, searchkey
))
2368 overhead
= tp
.item
->size
;
2372 fcb
->adsmaxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
) - (sizeof(DIR_ITEM
) - 1);
2374 if (utf8len
+ sizeof(xapref
) - 1 + overhead
> fcb
->adsmaxlen
) {
2375 WARN("not enough room for new DIR_ITEM (%u + %u > %u)", utf8len
+ sizeof(xapref
) - 1, overhead
, fcb
->adsmaxlen
);
2377 return STATUS_DISK_FULL
;
2379 fcb
->adsmaxlen
-= overhead
+ utf8len
+ sizeof(xapref
) - 1;
2381 fileref
= create_fileref(Vcb
);
2383 ERR("out of memory\n");
2385 return STATUS_INSUFFICIENT_RESOURCES
;
2390 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
2392 ERR("out of memory\n");
2393 free_fileref(Vcb
, fileref
);
2394 return STATUS_INSUFFICIENT_RESOURCES
;
2397 RtlZeroMemory(dc
, sizeof(dir_child
));
2399 dc
->utf8
.MaximumLength
= dc
->utf8
.Length
= fcb
->adsxattr
.Length
+ 1 - sizeof(xapref
);
2400 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dc
->utf8
.MaximumLength
, ALLOC_TAG
);
2401 if (!dc
->utf8
.Buffer
) {
2402 ERR("out of memory\n");
2404 free_fileref(Vcb
, fileref
);
2405 return STATUS_INSUFFICIENT_RESOURCES
;
2408 RtlCopyMemory(dc
->utf8
.Buffer
, &fcb
->adsxattr
.Buffer
[sizeof(xapref
) - 1], fcb
->adsxattr
.Length
+ 1 - sizeof(xapref
));
2410 dc
->name
.MaximumLength
= dc
->name
.Length
= stream
->Length
;
2411 dc
->name
.Buffer
= ExAllocatePoolWithTag(pool_type
, dc
->name
.MaximumLength
, ALLOC_TAG
);
2412 if (!dc
->name
.Buffer
) {
2413 ERR("out of memory\n");
2414 ExFreePool(dc
->utf8
.Buffer
);
2416 free_fileref(Vcb
, fileref
);
2417 return STATUS_INSUFFICIENT_RESOURCES
;
2420 RtlCopyMemory(dc
->name
.Buffer
, stream
->Buffer
, stream
->Length
);
2422 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, &dc
->name
, TRUE
);
2423 if (!NT_SUCCESS(Status
)) {
2424 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
2425 ExFreePool(dc
->utf8
.Buffer
);
2426 ExFreePool(dc
->name
.Buffer
);
2428 free_fileref(Vcb
, fileref
);
2432 dc
->fileref
= fileref
;
2435 InsertHeadList(&parfileref
->fcb
->dir_children_index
, &dc
->list_entry_index
);
2437 mark_fcb_dirty(fcb
);
2438 mark_fileref_dirty(fileref
);
2440 InsertHeadList(&parfileref
->fcb
->list_entry
, &fcb
->list_entry
); // insert in list after parent fcb
2441 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
2443 KeQuerySystemTime(&time
);
2444 win_time_to_unix(time
, &now
);
2446 parfileref
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2447 parfileref
->fcb
->inode_item
.sequence
++;
2448 parfileref
->fcb
->inode_item
.st_ctime
= now
;
2449 parfileref
->fcb
->inode_item_changed
= TRUE
;
2451 mark_fcb_dirty(parfileref
->fcb
);
2453 parfileref
->fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
2454 parfileref
->fcb
->subvol
->root_item
.ctime
= now
;
2456 fileref
->parent
= (struct _file_ref
*)parfileref
;
2458 ExAcquireResourceExclusiveLite(&parfileref
->nonpaged
->children_lock
, TRUE
);
2459 InsertTailList(&parfileref
->children
, &fileref
->list_entry
);
2460 ExReleaseResourceLite(&parfileref
->nonpaged
->children_lock
);
2462 increase_fileref_refcount(parfileref
);
2464 *pfileref
= fileref
;
2466 send_notification_fileref(parfileref
, FILE_NOTIFY_CHANGE_STREAM_NAME
, FILE_ACTION_ADDED_STREAM
, &fileref
->dc
->name
);
2468 return STATUS_SUCCESS
;
2471 // LXSS programs can be distinguished by the fact they have a NULL PEB.
2477 ZwQueryInformationProcess (
2478 _In_ HANDLE ProcessHandle
,
2479 _In_ PROCESSINFOCLASS ProcessInformationClass
,
2480 _Out_ PVOID ProcessInformation
,
2481 _In_ ULONG ProcessInformationLength
,
2482 _Out_opt_ PULONG ReturnLength
2485 static __inline BOOL
called_from_lxss() {
2487 PROCESS_BASIC_INFORMATION pbi
;
2490 Status
= ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation
, &pbi
, sizeof(pbi
), &retlen
);
2492 if (!NT_SUCCESS(Status
)) {
2493 ERR("ZwQueryInformationProcess returned %08x\n", Status
);
2497 return !pbi
.PebBaseAddress
;
2500 #define called_from_lxss() FALSE
2503 static NTSTATUS
file_create(PIRP Irp
, _Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
2504 PFILE_OBJECT FileObject
, file_ref
* related
, BOOL loaded_related
, PUNICODE_STRING fnus
, ULONG disposition
, ULONG options
, LIST_ENTRY
* rollback
) {
2506 file_ref
*fileref
, *parfileref
= NULL
;
2509 static const WCHAR datasuf
[] = {':','$','D','A','T','A',0};
2510 UNICODE_STRING dsus
, fpus
, stream
;
2511 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2512 POOL_TYPE pool_type
= IrpSp
->Flags
& SL_OPEN_PAGING_FILE
? NonPagedPool
: PagedPool
;
2514 ATOMIC_CREATE_ECP_CONTEXT
* acec
= NULL
;
2515 #ifdef DEBUG_FCB_REFCOUNTS
2519 TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp
, Vcb
, FileObject
, fnus
->Length
/ sizeof(WCHAR
), fnus
->Buffer
, disposition
, options
);
2522 return STATUS_MEDIA_WRITE_PROTECTED
;
2524 if (options
& FILE_DELETE_ON_CLOSE
&& IrpSp
->Parameters
.Create
.FileAttributes
& FILE_ATTRIBUTE_READONLY
)
2525 return STATUS_CANNOT_DELETE
;
2527 if (NT_SUCCESS(FsRtlGetEcpListFromIrp(Irp
, &ecp_list
)) && ecp_list
) {
2533 Status
= FsRtlGetNextExtraCreateParameter(ecp_list
, ctx
, &type
, &ctx
, &ctxsize
);
2535 if (NT_SUCCESS(Status
)) {
2536 if (RtlCompareMemory(&type
, &GUID_ECP_ATOMIC_CREATE
, sizeof(GUID
)) == sizeof(GUID
) && ctxsize
>= sizeof(ATOMIC_CREATE_ECP_CONTEXT
)) {
2541 } while (NT_SUCCESS(Status
));
2544 dsus
.Buffer
= (WCHAR
*)datasuf
;
2545 dsus
.Length
= dsus
.MaximumLength
= sizeof(datasuf
) - sizeof(WCHAR
);
2548 if (!loaded_related
) {
2549 Status
= open_fileref(Vcb
, &parfileref
, fnus
, related
, TRUE
, NULL
, NULL
, pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
2551 if (!NT_SUCCESS(Status
))
2554 parfileref
= related
;
2556 if (parfileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& (fnus
->Length
< sizeof(WCHAR
) || fnus
->Buffer
[0] != ':')) {
2557 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
2561 if (is_subvol_readonly(parfileref
->fcb
->subvol
, Irp
)) {
2562 Status
= STATUS_ACCESS_DENIED
;
2566 i
= (fnus
->Length
/ sizeof(WCHAR
))-1;
2567 while ((fnus
->Buffer
[i
] == '\\' || fnus
->Buffer
[i
] == '/') && i
> 0) { i
--; }
2571 while (i
> 0 && fnus
->Buffer
[i
-1] != '\\' && fnus
->Buffer
[i
-1] != '/') { i
--; }
2573 fpus
.MaximumLength
= (USHORT
)((j
- i
+ 2) * sizeof(WCHAR
));
2574 fpus
.Buffer
= ExAllocatePoolWithTag(pool_type
, fpus
.MaximumLength
, ALLOC_TAG
);
2576 ERR("out of memory\n");
2577 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2581 fpus
.Length
= (USHORT
)((j
- i
+ 1) * sizeof(WCHAR
));
2583 RtlCopyMemory(fpus
.Buffer
, &fnus
->Buffer
[i
], (j
- i
+ 1) * sizeof(WCHAR
));
2584 fpus
.Buffer
[j
- i
+ 1] = 0;
2586 if (fpus
.Length
> dsus
.Length
) { // check for :$DATA suffix
2589 lb
.Buffer
= &fpus
.Buffer
[(fpus
.Length
- dsus
.Length
)/sizeof(WCHAR
)];
2590 lb
.Length
= lb
.MaximumLength
= dsus
.Length
;
2592 TRACE("lb = %.*S\n", lb
.Length
/sizeof(WCHAR
), lb
.Buffer
);
2594 if (FsRtlAreNamesEqual(&dsus
, &lb
, TRUE
, NULL
)) {
2595 TRACE("ignoring :$DATA suffix\n");
2597 fpus
.Length
-= lb
.Length
;
2599 if (fpus
.Length
> sizeof(WCHAR
) && fpus
.Buffer
[(fpus
.Length
-1)/sizeof(WCHAR
)] == ':')
2600 fpus
.Length
-= sizeof(WCHAR
);
2602 TRACE("fpus = %.*S\n", fpus
.Length
/ sizeof(WCHAR
), fpus
.Buffer
);
2608 for (i
= 0; i
< fpus
.Length
/ sizeof(WCHAR
); i
++) {
2609 if (fpus
.Buffer
[i
] == ':') {
2610 stream
.Length
= (USHORT
)(fpus
.Length
- (i
* sizeof(WCHAR
)) - sizeof(WCHAR
));
2611 stream
.Buffer
= &fpus
.Buffer
[i
+1];
2613 fpus
.Length
= (USHORT
)(i
* sizeof(WCHAR
));
2618 if (stream
.Length
> 0) {
2619 Status
= create_stream(Vcb
, &fileref
, &parfileref
, &fpus
, &stream
, Irp
, options
, pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, rollback
);
2620 if (!NT_SUCCESS(Status
)) {
2621 ERR("create_stream returned %08x\n", Status
);
2625 IoSetShareAccess(IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
, IrpSp
->Parameters
.Create
.ShareAccess
,
2626 FileObject
, &fileref
->fcb
->share_access
);
2628 ACCESS_MASK granted_access
;
2630 if (!is_file_name_valid(&fpus
, FALSE
)) {
2631 Status
= STATUS_OBJECT_NAME_INVALID
;
2635 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2637 if (!SeAccessCheck(parfileref
->fcb
->sd
, &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
2638 TRUE
, options
& FILE_DIRECTORY_FILE
? FILE_ADD_SUBDIRECTORY
: FILE_ADD_FILE
, 0, NULL
,
2639 IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
2640 &granted_access
, &Status
)) {
2641 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2645 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2647 if (Irp
->AssociatedIrp
.SystemBuffer
&& IrpSp
->Parameters
.Create
.EaLength
> 0) {
2650 Status
= IoCheckEaBufferValidity(Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.Create
.EaLength
, &offset
);
2651 if (!NT_SUCCESS(Status
)) {
2652 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
2657 Status
= file_create2(Irp
, Vcb
, &fpus
, parfileref
, options
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.Create
.EaLength
,
2658 &fileref
, rollback
);
2660 if (!NT_SUCCESS(Status
)) {
2661 ERR("file_create2 returned %08x\n", Status
);
2665 IoSetShareAccess(IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
, IrpSp
->Parameters
.Create
.ShareAccess
, FileObject
, &fileref
->fcb
->share_access
);
2667 send_notification_fileref(fileref
, options
& FILE_DIRECTORY_FILE
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
2668 send_notification_fcb(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2671 FileObject
->FsContext
= fileref
->fcb
;
2673 ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ccb
), ALLOC_TAG
);
2675 ERR("out of memory\n");
2676 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2677 fileref
->deleted
= TRUE
;
2678 fileref
->fcb
->deleted
= TRUE
;
2680 if (stream
.Length
== 0) {
2681 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2682 parfileref
->fcb
->inode_item
.st_size
-= fileref
->dc
->utf8
.Length
* 2;
2683 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2686 free_fileref(Vcb
, fileref
);
2690 RtlZeroMemory(ccb
, sizeof(*ccb
));
2692 ccb
->fileref
= fileref
;
2694 ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
2695 ccb
->NodeSize
= sizeof(*ccb
);
2696 ccb
->disposition
= disposition
;
2697 ccb
->options
= options
;
2698 ccb
->query_dir_offset
= 0;
2699 RtlInitUnicodeString(&ccb
->query_string
, NULL
);
2700 ccb
->has_wildcard
= FALSE
;
2701 ccb
->specific_file
= FALSE
;
2702 ccb
->access
= IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
;
2703 ccb
->case_sensitive
= IrpSp
->Flags
& SL_CASE_SENSITIVE
;
2704 ccb
->reserving
= FALSE
;
2705 ccb
->lxss
= called_from_lxss();
2707 #ifdef DEBUG_FCB_REFCOUNTS
2708 oc
= InterlockedIncrement(&fileref
->open_count
);
2709 ERR("fileref %p: open_count now %i\n", fileref
, oc
);
2711 InterlockedIncrement(&fileref
->open_count
);
2713 InterlockedIncrement(&Vcb
->open_files
);
2715 FileObject
->FsContext2
= ccb
;
2717 FileObject
->SectionObjectPointer
= &fileref
->fcb
->nonpaged
->segment_object
;
2719 // FIXME - ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT
2720 if (acec
&& acec
->InFlags
& ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED
) {
2721 if (acec
->ReparseBufferLength
> sizeof(UINT32
) && *(UINT32
*)acec
->ReparseBuffer
== IO_REPARSE_TAG_SYMLINK
) {
2722 fileref
->fcb
->inode_item
.st_mode
&= ~(__S_IFIFO
| __S_IFCHR
| __S_IFBLK
| __S_IFSOCK
);
2723 fileref
->fcb
->type
= BTRFS_TYPE_FILE
;
2726 if (fileref
->fcb
->type
== BTRFS_TYPE_SOCKET
|| fileref
->fcb
->type
== BTRFS_TYPE_FIFO
||
2727 fileref
->fcb
->type
== BTRFS_TYPE_CHARDEV
|| fileref
->fcb
->type
== BTRFS_TYPE_BLOCKDEV
) {
2728 // NOP. If called from LXSS, humour it - we hardcode the values elsewhere.
2730 Status
= set_reparse_point2(fileref
->fcb
, acec
->ReparseBuffer
, acec
->ReparseBufferLength
, NULL
, NULL
, Irp
, rollback
);
2731 if (!NT_SUCCESS(Status
)) {
2732 ERR("set_reparse_point2 returned %08x\n", Status
);
2733 fileref
->deleted
= TRUE
;
2734 fileref
->fcb
->deleted
= TRUE
;
2736 if (stream
.Length
== 0) {
2737 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
2738 parfileref
->fcb
->inode_item
.st_size
-= fileref
->dc
->utf8
.Length
* 2;
2739 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
2742 free_fileref(Vcb
, fileref
);
2747 acec
->OutFlags
|= ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET
;
2750 fileref
->dc
->type
= fileref
->fcb
->type
;
2756 ExFreePool(fpus
.Buffer
);
2759 if (parfileref
&& !loaded_related
)
2760 free_fileref(Vcb
, parfileref
);
2765 static __inline
void debug_create_options(ULONG RequestedOptions
) {
2766 if (RequestedOptions
!= 0) {
2767 ULONG options
= RequestedOptions
;
2769 TRACE("requested options:\n");
2771 if (options
& FILE_DIRECTORY_FILE
) {
2772 TRACE(" FILE_DIRECTORY_FILE\n");
2773 options
&= ~FILE_DIRECTORY_FILE
;
2776 if (options
& FILE_WRITE_THROUGH
) {
2777 TRACE(" FILE_WRITE_THROUGH\n");
2778 options
&= ~FILE_WRITE_THROUGH
;
2781 if (options
& FILE_SEQUENTIAL_ONLY
) {
2782 TRACE(" FILE_SEQUENTIAL_ONLY\n");
2783 options
&= ~FILE_SEQUENTIAL_ONLY
;
2786 if (options
& FILE_NO_INTERMEDIATE_BUFFERING
) {
2787 TRACE(" FILE_NO_INTERMEDIATE_BUFFERING\n");
2788 options
&= ~FILE_NO_INTERMEDIATE_BUFFERING
;
2791 if (options
& FILE_SYNCHRONOUS_IO_ALERT
) {
2792 TRACE(" FILE_SYNCHRONOUS_IO_ALERT\n");
2793 options
&= ~FILE_SYNCHRONOUS_IO_ALERT
;
2796 if (options
& FILE_SYNCHRONOUS_IO_NONALERT
) {
2797 TRACE(" FILE_SYNCHRONOUS_IO_NONALERT\n");
2798 options
&= ~FILE_SYNCHRONOUS_IO_NONALERT
;
2801 if (options
& FILE_NON_DIRECTORY_FILE
) {
2802 TRACE(" FILE_NON_DIRECTORY_FILE\n");
2803 options
&= ~FILE_NON_DIRECTORY_FILE
;
2806 if (options
& FILE_CREATE_TREE_CONNECTION
) {
2807 TRACE(" FILE_CREATE_TREE_CONNECTION\n");
2808 options
&= ~FILE_CREATE_TREE_CONNECTION
;
2811 if (options
& FILE_COMPLETE_IF_OPLOCKED
) {
2812 TRACE(" FILE_COMPLETE_IF_OPLOCKED\n");
2813 options
&= ~FILE_COMPLETE_IF_OPLOCKED
;
2816 if (options
& FILE_NO_EA_KNOWLEDGE
) {
2817 TRACE(" FILE_NO_EA_KNOWLEDGE\n");
2818 options
&= ~FILE_NO_EA_KNOWLEDGE
;
2821 if (options
& FILE_OPEN_REMOTE_INSTANCE
) {
2822 TRACE(" FILE_OPEN_REMOTE_INSTANCE\n");
2823 options
&= ~FILE_OPEN_REMOTE_INSTANCE
;
2826 if (options
& FILE_RANDOM_ACCESS
) {
2827 TRACE(" FILE_RANDOM_ACCESS\n");
2828 options
&= ~FILE_RANDOM_ACCESS
;
2831 if (options
& FILE_DELETE_ON_CLOSE
) {
2832 TRACE(" FILE_DELETE_ON_CLOSE\n");
2833 options
&= ~FILE_DELETE_ON_CLOSE
;
2836 if (options
& FILE_OPEN_BY_FILE_ID
) {
2837 TRACE(" FILE_OPEN_BY_FILE_ID\n");
2838 options
&= ~FILE_OPEN_BY_FILE_ID
;
2841 if (options
& FILE_OPEN_FOR_BACKUP_INTENT
) {
2842 TRACE(" FILE_OPEN_FOR_BACKUP_INTENT\n");
2843 options
&= ~FILE_OPEN_FOR_BACKUP_INTENT
;
2846 if (options
& FILE_NO_COMPRESSION
) {
2847 TRACE(" FILE_NO_COMPRESSION\n");
2848 options
&= ~FILE_NO_COMPRESSION
;
2851 #if NTDDI_VERSION >= NTDDI_WIN7
2852 if (options
& FILE_OPEN_REQUIRING_OPLOCK
) {
2853 TRACE(" FILE_OPEN_REQUIRING_OPLOCK\n");
2854 options
&= ~FILE_OPEN_REQUIRING_OPLOCK
;
2857 if (options
& FILE_DISALLOW_EXCLUSIVE
) {
2858 TRACE(" FILE_DISALLOW_EXCLUSIVE\n");
2859 options
&= ~FILE_DISALLOW_EXCLUSIVE
;
2863 if (options
& FILE_RESERVE_OPFILTER
) {
2864 TRACE(" FILE_RESERVE_OPFILTER\n");
2865 options
&= ~FILE_RESERVE_OPFILTER
;
2868 if (options
& FILE_OPEN_REPARSE_POINT
) {
2869 TRACE(" FILE_OPEN_REPARSE_POINT\n");
2870 options
&= ~FILE_OPEN_REPARSE_POINT
;
2873 if (options
& FILE_OPEN_NO_RECALL
) {
2874 TRACE(" FILE_OPEN_NO_RECALL\n");
2875 options
&= ~FILE_OPEN_NO_RECALL
;
2878 if (options
& FILE_OPEN_FOR_FREE_SPACE_QUERY
) {
2879 TRACE(" FILE_OPEN_FOR_FREE_SPACE_QUERY\n");
2880 options
&= ~FILE_OPEN_FOR_FREE_SPACE_QUERY
;
2884 TRACE(" unknown options: %x\n", options
);
2886 TRACE("requested options: (none)\n");
2890 static NTSTATUS
get_reparse_block(fcb
* fcb
, UINT8
** data
) {
2893 if (fcb
->type
== BTRFS_TYPE_FILE
|| fcb
->type
== BTRFS_TYPE_SYMLINK
) {
2894 ULONG size
, bytes_read
, i
;
2896 if (fcb
->type
== BTRFS_TYPE_FILE
&& fcb
->inode_item
.st_size
< sizeof(ULONG
)) {
2897 WARN("file was too short to be a reparse point\n");
2898 return STATUS_INVALID_PARAMETER
;
2901 // 0x10007 = 0xffff (maximum length of data buffer) + 8 bytes header
2902 size
= (ULONG
)min(0x10007, fcb
->inode_item
.st_size
);
2905 return STATUS_INVALID_PARAMETER
;
2907 *data
= ExAllocatePoolWithTag(PagedPool
, size
, ALLOC_TAG
);
2909 ERR("out of memory\n");
2910 return STATUS_INSUFFICIENT_RESOURCES
;
2913 Status
= read_file(fcb
, *data
, 0, size
, &bytes_read
, NULL
);
2914 if (!NT_SUCCESS(Status
)) {
2915 ERR("read_file_fcb returned %08x\n", Status
);
2920 if (fcb
->type
== BTRFS_TYPE_SYMLINK
) {
2921 ULONG stringlen
, reqlen
;
2922 UINT16 subnamelen
, printnamelen
;
2923 REPARSE_DATA_BUFFER
* rdb
;
2925 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, (char*)*data
, bytes_read
);
2926 if (!NT_SUCCESS(Status
)) {
2927 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
2932 subnamelen
= printnamelen
= (USHORT
)stringlen
;
2934 reqlen
= offsetof(REPARSE_DATA_BUFFER
, SymbolicLinkReparseBuffer
.PathBuffer
) + subnamelen
+ printnamelen
;
2936 rdb
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
2939 ERR("out of memory\n");
2941 return STATUS_INSUFFICIENT_RESOURCES
;
2944 rdb
->ReparseTag
= IO_REPARSE_TAG_SYMLINK
;
2945 rdb
->ReparseDataLength
= (USHORT
)(reqlen
- offsetof(REPARSE_DATA_BUFFER
, SymbolicLinkReparseBuffer
));
2948 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
= 0;
2949 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameLength
= subnamelen
;
2950 rdb
->SymbolicLinkReparseBuffer
.PrintNameOffset
= subnamelen
;
2951 rdb
->SymbolicLinkReparseBuffer
.PrintNameLength
= printnamelen
;
2952 rdb
->SymbolicLinkReparseBuffer
.Flags
= SYMLINK_FLAG_RELATIVE
;
2954 Status
= RtlUTF8ToUnicodeN(&rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)],
2955 stringlen
, &stringlen
, (char*)*data
, size
);
2957 if (!NT_SUCCESS(Status
)) {
2958 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
2964 for (i
= 0; i
< stringlen
/ sizeof(WCHAR
); i
++) {
2965 if (rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[(rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)) + i
] == '/')
2966 rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[(rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)) + i
] = '\\';
2969 RtlCopyMemory(&rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.PrintNameOffset
/ sizeof(WCHAR
)],
2970 &rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)],
2971 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameLength
);
2975 *data
= (UINT8
*)rdb
;
2977 Status
= FsRtlValidateReparsePointBuffer(bytes_read
, (REPARSE_DATA_BUFFER
*)*data
);
2978 if (!NT_SUCCESS(Status
)) {
2979 ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status
);
2984 } else if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
2985 if (!fcb
->reparse_xattr
.Buffer
|| fcb
->reparse_xattr
.Length
== 0)
2986 return STATUS_INTERNAL_ERROR
;
2988 if (fcb
->reparse_xattr
.Length
< sizeof(ULONG
)) {
2989 WARN("xattr was too short to be a reparse point\n");
2990 return STATUS_INTERNAL_ERROR
;
2993 Status
= FsRtlValidateReparsePointBuffer(fcb
->reparse_xattr
.Length
, (REPARSE_DATA_BUFFER
*)fcb
->reparse_xattr
.Buffer
);
2994 if (!NT_SUCCESS(Status
)) {
2995 ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status
);
2999 *data
= ExAllocatePoolWithTag(PagedPool
, fcb
->reparse_xattr
.Length
, ALLOC_TAG
);
3001 ERR("out of memory\n");
3002 return STATUS_INSUFFICIENT_RESOURCES
;
3005 RtlCopyMemory(*data
, fcb
->reparse_xattr
.Buffer
, fcb
->reparse_xattr
.Length
);
3007 return STATUS_INVALID_PARAMETER
;
3009 return STATUS_SUCCESS
;
3012 static void fcb_load_csums(_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, fcb
* fcb
, PIRP Irp
) {
3016 if (fcb
->csum_loaded
)
3019 if (IsListEmpty(&fcb
->extents
) || fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)
3022 le
= fcb
->extents
.Flink
;
3023 while (le
!= &fcb
->extents
) {
3024 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3026 if (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
) {
3027 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)&ext
->extent_data
.data
[0];
3030 len
= (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
? ed2
->num_bytes
: ed2
->size
) / Vcb
->superblock
.sector_size
;
3032 ext
->csum
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)(len
* sizeof(UINT32
)), ALLOC_TAG
);
3034 ERR("out of memory\n");
3038 Status
= load_csum(Vcb
, ext
->csum
, ed2
->address
+ (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
? ed2
->offset
: 0), len
, Irp
);
3040 if (!NT_SUCCESS(Status
)) {
3041 ERR("load_csum returned %08x\n", Status
);
3050 fcb
->csum_loaded
= TRUE
;
3053 static NTSTATUS
open_file(PDEVICE_OBJECT DeviceObject
, _Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3054 PFILE_OBJECT FileObject
= NULL
;
3055 ULONG RequestedDisposition
;
3059 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3061 ULONG fn_offset
= 0;
3062 file_ref
*related
, *fileref
= NULL
;
3063 POOL_TYPE pool_type
= IrpSp
->Flags
& SL_OPEN_PAGING_FILE
? NonPagedPool
: PagedPool
;
3064 ACCESS_MASK granted_access
;
3065 BOOL loaded_related
= FALSE
;
3067 #ifdef DEBUG_FCB_REFCOUNTS
3071 LARGE_INTEGER time1
, time2
;
3072 UINT8 open_type
= 0;
3074 time1
= KeQueryPerformanceCounter(NULL
);
3077 Irp
->IoStatus
.Information
= 0;
3079 RequestedDisposition
= ((IrpSp
->Parameters
.Create
.Options
>> 24) & 0xff);
3080 options
= IrpSp
->Parameters
.Create
.Options
& FILE_VALID_OPTION_FLAGS
;
3082 if (options
& FILE_DIRECTORY_FILE
&& RequestedDisposition
== FILE_SUPERSEDE
) {
3083 WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
3084 return STATUS_INVALID_PARAMETER
;
3087 FileObject
= IrpSp
->FileObject
;
3090 ERR("FileObject was NULL\n");
3091 return STATUS_INVALID_PARAMETER
;
3094 if (FileObject
->RelatedFileObject
&& FileObject
->RelatedFileObject
->FsContext2
) {
3095 struct _ccb
* relatedccb
= FileObject
->RelatedFileObject
->FsContext2
;
3097 related
= relatedccb
->fileref
;
3101 debug_create_options(options
);
3103 switch (RequestedDisposition
) {
3104 case FILE_SUPERSEDE
:
3105 TRACE("requested disposition: FILE_SUPERSEDE\n");
3109 TRACE("requested disposition: FILE_CREATE\n");
3113 TRACE("requested disposition: FILE_OPEN\n");
3117 TRACE("requested disposition: FILE_OPEN_IF\n");
3120 case FILE_OVERWRITE
:
3121 TRACE("requested disposition: FILE_OVERWRITE\n");
3124 case FILE_OVERWRITE_IF
:
3125 TRACE("requested disposition: FILE_OVERWRITE_IF\n");
3129 ERR("unknown disposition: %x\n", RequestedDisposition
);
3130 Status
= STATUS_NOT_IMPLEMENTED
;
3134 fn
= FileObject
->FileName
;
3136 TRACE("(%.*S)\n", fn
.Length
/ sizeof(WCHAR
), fn
.Buffer
);
3137 TRACE("FileObject = %p\n", FileObject
);
3139 if (Vcb
->readonly
&& (RequestedDisposition
== FILE_SUPERSEDE
|| RequestedDisposition
== FILE_CREATE
|| RequestedDisposition
== FILE_OVERWRITE
)) {
3140 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
3144 acquire_fcb_lock_exclusive(Vcb
);
3146 if (options
& FILE_OPEN_BY_FILE_ID
) {
3147 if (fn
.Length
== sizeof(UINT64
) && related
&& RequestedDisposition
== FILE_OPEN
) {
3150 RtlCopyMemory(&inode
, fn
.Buffer
, sizeof(UINT64
));
3152 if (related
->fcb
== Vcb
->root_fileref
->fcb
&& inode
== 0)
3153 inode
= Vcb
->root_fileref
->fcb
->inode
;
3155 if (inode
== 0) { // we use 0 to mean the parent of a subvolume
3156 fileref
= related
->parent
;
3157 increase_fileref_refcount(fileref
);
3158 Status
= STATUS_SUCCESS
;
3160 Status
= open_fileref_by_inode(Vcb
, related
->fcb
->subvol
, inode
, &fileref
, Irp
);
3163 WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
3164 Status
= STATUS_NOT_IMPLEMENTED
;
3165 release_fcb_lock(Vcb
);
3169 if (related
&& fn
.Length
!= 0 && fn
.Buffer
[0] == '\\') {
3170 Status
= STATUS_INVALID_PARAMETER
;
3171 release_fcb_lock(Vcb
);
3175 if (!related
&& RequestedDisposition
!= FILE_OPEN
&& !(IrpSp
->Flags
& SL_OPEN_TARGET_DIRECTORY
)) {
3178 Status
= open_fileref(Vcb
, &related
, &fn
, NULL
, TRUE
, &parsed
, &fnoff
,
3179 pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
3181 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
3182 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
3183 else if (Status
== STATUS_REPARSE
)
3185 else if (NT_SUCCESS(Status
)) {
3186 fnoff
*= sizeof(WCHAR
);
3187 fnoff
+= (related
->dc
? related
->dc
->name
.Length
: 0) + sizeof(WCHAR
);
3189 if (related
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
) {
3190 Status
= STATUS_REPARSE
;
3192 parsed
= (USHORT
)fnoff
- sizeof(WCHAR
);
3194 fn
.Buffer
= &fn
.Buffer
[fnoff
/ sizeof(WCHAR
)];
3195 fn
.Length
-= (USHORT
)fnoff
;
3197 Status
= open_fileref(Vcb
, &fileref
, &fn
, related
, IrpSp
->Flags
& SL_OPEN_TARGET_DIRECTORY
, &parsed
, &fn_offset
,
3198 pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
3200 loaded_related
= TRUE
;
3205 Status
= open_fileref(Vcb
, &fileref
, &fn
, related
, IrpSp
->Flags
& SL_OPEN_TARGET_DIRECTORY
, &parsed
, &fn_offset
,
3206 pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
3210 if (Status
== STATUS_REPARSE
) {
3211 REPARSE_DATA_BUFFER
* data
;
3213 ExAcquireResourceSharedLite(fileref
->fcb
->Header
.Resource
, TRUE
);
3214 Status
= get_reparse_block(fileref
->fcb
, (UINT8
**)&data
);
3215 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
3217 if (!NT_SUCCESS(Status
)) {
3218 ERR("get_reparse_block returned %08x\n", Status
);
3220 Status
= STATUS_SUCCESS
;
3222 Status
= STATUS_REPARSE
;
3223 RtlCopyMemory(&Irp
->IoStatus
.Information
, data
, sizeof(ULONG
));
3225 data
->Reserved
= FileObject
->FileName
.Length
- parsed
;
3227 Irp
->Tail
.Overlay
.AuxiliaryBuffer
= (void*)data
;
3229 free_fileref(Vcb
, fileref
);
3230 release_fcb_lock(Vcb
);
3236 if (NT_SUCCESS(Status
) && fileref
->deleted
)
3237 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
3239 if (NT_SUCCESS(Status
)) {
3240 if (RequestedDisposition
== FILE_CREATE
) {
3241 TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref
));
3242 Status
= STATUS_OBJECT_NAME_COLLISION
;
3244 free_fileref(Vcb
, fileref
);
3245 release_fcb_lock(Vcb
);
3249 } else if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
3250 if (RequestedDisposition
== FILE_OPEN
|| RequestedDisposition
== FILE_OVERWRITE
) {
3251 TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
3252 release_fcb_lock(Vcb
);
3255 } else if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
) {
3256 TRACE("open_fileref returned %08x\n", Status
);
3257 release_fcb_lock(Vcb
);
3260 ERR("open_fileref returned %08x\n", Status
);
3261 release_fcb_lock(Vcb
);
3265 if (NT_SUCCESS(Status
)) { // file already exists
3269 release_fcb_lock(Vcb
);
3271 if (RequestedDisposition
== FILE_SUPERSEDE
|| RequestedDisposition
== FILE_OVERWRITE
|| RequestedDisposition
== FILE_OVERWRITE_IF
) {
3277 if (fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
|| is_subvol_readonly(fileref
->fcb
->subvol
, Irp
)) {
3278 Status
= STATUS_ACCESS_DENIED
;
3280 acquire_fcb_lock_exclusive(Vcb
);
3281 free_fileref(Vcb
, fileref
);
3282 release_fcb_lock(Vcb
);
3287 if (Vcb
->readonly
) {
3288 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
3290 acquire_fcb_lock_exclusive(Vcb
);
3291 free_fileref(Vcb
, fileref
);
3292 release_fcb_lock(Vcb
);
3298 if (!MmCanFileBeTruncated(&fileref
->fcb
->nonpaged
->segment_object
, &zero
)) {
3299 Status
= STATUS_USER_MAPPED_FILE
;
3301 acquire_fcb_lock_exclusive(Vcb
);
3302 free_fileref(Vcb
, fileref
);
3303 release_fcb_lock(Vcb
);
3309 if (IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
!= 0) {
3310 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3312 if (!SeAccessCheck((fileref
->fcb
->ads
|| fileref
->fcb
== Vcb
->dummy_fcb
) ? fileref
->parent
->fcb
->sd
: fileref
->fcb
->sd
,
3313 &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
3314 TRUE
, IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
, 0, NULL
,
3315 IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
3316 &granted_access
, &Status
)) {
3317 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3318 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
3320 acquire_fcb_lock_exclusive(Vcb
);
3321 free_fileref(Vcb
, fileref
);
3322 release_fcb_lock(Vcb
);
3327 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3331 TRACE("deleted = %s\n", fileref
->deleted
? "TRUE" : "FALSE");
3335 if (sf
->delete_on_close
) {
3336 TRACE("could not open as deletion pending\n");
3337 Status
= STATUS_DELETE_PENDING
;
3339 acquire_fcb_lock_exclusive(Vcb
);
3340 free_fileref(Vcb
, fileref
);
3341 release_fcb_lock(Vcb
);
3348 readonly
= (!fileref
->fcb
->ads
&& fileref
->fcb
->atts
& FILE_ATTRIBUTE_READONLY
) || (fileref
->fcb
->ads
&& fileref
->parent
->fcb
->atts
& FILE_ATTRIBUTE_READONLY
) ||
3349 is_subvol_readonly(fileref
->fcb
->subvol
, Irp
) || fileref
->fcb
== Vcb
->dummy_fcb
|| Vcb
->readonly
;
3351 if (options
& FILE_DELETE_ON_CLOSE
&& (fileref
== Vcb
->root_fileref
|| readonly
)) {
3352 Status
= STATUS_CANNOT_DELETE
;
3354 acquire_fcb_lock_exclusive(Vcb
);
3355 free_fileref(Vcb
, fileref
);
3356 release_fcb_lock(Vcb
);
3362 ACCESS_MASK allowed
;
3364 allowed
= READ_CONTROL
| SYNCHRONIZE
| ACCESS_SYSTEM_SECURITY
| FILE_READ_DATA
|
3365 FILE_READ_EA
| FILE_READ_ATTRIBUTES
| FILE_EXECUTE
| FILE_LIST_DIRECTORY
|
3368 if (!Vcb
->readonly
&& (fileref
->fcb
== Vcb
->dummy_fcb
|| fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
))
3371 if (fileref
->fcb
!= Vcb
->dummy_fcb
&& !is_subvol_readonly(fileref
->fcb
->subvol
, Irp
) && !Vcb
->readonly
) {
3372 allowed
|= DELETE
| WRITE_OWNER
| WRITE_DAC
| FILE_WRITE_EA
| FILE_WRITE_ATTRIBUTES
;
3374 if (!fileref
->fcb
->ads
&& fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
3375 allowed
|= FILE_ADD_SUBDIRECTORY
| FILE_ADD_FILE
| FILE_DELETE_CHILD
;
3376 } else if (fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
&& is_subvol_readonly(fileref
->fcb
->subvol
, Irp
) && !Vcb
->readonly
) {
3377 // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
3379 allowed
|= FILE_WRITE_ATTRIBUTES
;
3382 if (IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
& MAXIMUM_ALLOWED
) {
3383 granted_access
&= allowed
;
3384 IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->PreviouslyGrantedAccess
&= allowed
;
3385 } else if (granted_access
& ~allowed
) {
3386 Status
= Vcb
->readonly
? STATUS_MEDIA_WRITE_PROTECTED
: STATUS_ACCESS_DENIED
;
3388 acquire_fcb_lock_exclusive(Vcb
);
3389 free_fileref(Vcb
, fileref
);
3390 release_fcb_lock(Vcb
);
3396 if ((fileref
->fcb
->type
== BTRFS_TYPE_SYMLINK
|| fileref
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
) && !(options
& FILE_OPEN_REPARSE_POINT
)) {
3397 REPARSE_DATA_BUFFER
* data
;
3399 /* How reparse points work from the point of view of the filesystem appears to
3400 * undocumented. When returning STATUS_REPARSE, MSDN encourages us to return
3401 * IO_REPARSE in Irp->IoStatus.Information, but that means we have to do our own
3402 * translation. If we instead return the reparse tag in Information, and store
3403 * a pointer to the reparse data buffer in Irp->Tail.Overlay.AuxiliaryBuffer,
3404 * IopSymlinkProcessReparse will do the translation for us. */
3406 Status
= get_reparse_block(fileref
->fcb
, (UINT8
**)&data
);
3407 if (!NT_SUCCESS(Status
)) {
3408 ERR("get_reparse_block returned %08x\n", Status
);
3409 Status
= STATUS_SUCCESS
;
3411 Status
= STATUS_REPARSE
;
3412 Irp
->IoStatus
.Information
= data
->ReparseTag
;
3414 if (fn
.Buffer
[(fn
.Length
/ sizeof(WCHAR
)) - 1] == '\\')
3415 data
->Reserved
= sizeof(WCHAR
);
3417 Irp
->Tail
.Overlay
.AuxiliaryBuffer
= (void*)data
;
3419 acquire_fcb_lock_exclusive(Vcb
);
3420 free_fileref(Vcb
, fileref
);
3421 release_fcb_lock(Vcb
);
3427 if (fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
&& !fileref
->fcb
->ads
) {
3428 if (options
& FILE_NON_DIRECTORY_FILE
&& !(fileref
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
)) {
3429 Status
= STATUS_FILE_IS_A_DIRECTORY
;
3431 acquire_fcb_lock_exclusive(Vcb
);
3432 free_fileref(Vcb
, fileref
);
3433 release_fcb_lock(Vcb
);
3437 } else if (options
& FILE_DIRECTORY_FILE
) {
3438 TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref
->fcb
->type
, file_desc_fileref(fileref
));
3439 Status
= STATUS_NOT_A_DIRECTORY
;
3441 acquire_fcb_lock_exclusive(Vcb
);
3442 free_fileref(Vcb
, fileref
);
3443 release_fcb_lock(Vcb
);
3448 if (fileref
->open_count
> 0) {
3449 Status
= IoCheckShareAccess(granted_access
, IrpSp
->Parameters
.Create
.ShareAccess
, FileObject
, &fileref
->fcb
->share_access
, FALSE
);
3451 if (!NT_SUCCESS(Status
)) {
3452 if (Status
== STATUS_SHARING_VIOLATION
)
3453 TRACE("IoCheckShareAccess failed, returning %08x\n", Status
);
3455 WARN("IoCheckShareAccess failed, returning %08x\n", Status
);
3457 acquire_fcb_lock_exclusive(Vcb
);
3458 free_fileref(Vcb
, fileref
);
3459 release_fcb_lock(Vcb
);
3464 IoUpdateShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3466 IoSetShareAccess(granted_access
, IrpSp
->Parameters
.Create
.ShareAccess
, FileObject
, &fileref
->fcb
->share_access
);
3468 if (granted_access
& FILE_WRITE_DATA
|| options
& FILE_DELETE_ON_CLOSE
) {
3469 if (!MmFlushImageSection(&fileref
->fcb
->nonpaged
->segment_object
, MmFlushForWrite
)) {
3470 Status
= (options
& FILE_DELETE_ON_CLOSE
) ? STATUS_CANNOT_DELETE
: STATUS_SHARING_VIOLATION
;
3472 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3474 acquire_fcb_lock_exclusive(Vcb
);
3475 free_fileref(Vcb
, fileref
);
3476 release_fcb_lock(Vcb
);
3482 if (RequestedDisposition
== FILE_OVERWRITE
|| RequestedDisposition
== FILE_OVERWRITE_IF
|| RequestedDisposition
== FILE_SUPERSEDE
) {
3483 ULONG defda
, oldatts
, filter
;
3487 if ((RequestedDisposition
== FILE_OVERWRITE
|| RequestedDisposition
== FILE_OVERWRITE_IF
) && readonly
) {
3488 WARN("cannot overwrite readonly file\n");
3489 Status
= STATUS_ACCESS_DENIED
;
3491 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3493 acquire_fcb_lock_exclusive(Vcb
);
3494 free_fileref(Vcb
, fileref
);
3495 release_fcb_lock(Vcb
);
3500 if (!fileref
->fcb
->ads
&& (IrpSp
->Parameters
.Create
.FileAttributes
& (FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
)) != ((fileref
->fcb
->atts
& (FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
)))) {
3501 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3503 acquire_fcb_lock_exclusive(Vcb
);
3504 free_fileref(Vcb
, fileref
);
3505 release_fcb_lock(Vcb
);
3507 Status
= STATUS_ACCESS_DENIED
;
3511 if (fileref
->fcb
->ads
) {
3512 Status
= stream_set_end_of_file_information(Vcb
, 0, fileref
->fcb
, fileref
, FALSE
);
3513 if (!NT_SUCCESS(Status
)) {
3514 ERR("stream_set_end_of_file_information returned %08x\n", Status
);
3516 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3518 acquire_fcb_lock_exclusive(Vcb
);
3519 free_fileref(Vcb
, fileref
);
3520 release_fcb_lock(Vcb
);
3525 Status
= truncate_file(fileref
->fcb
, 0, Irp
, rollback
);
3526 if (!NT_SUCCESS(Status
)) {
3527 ERR("truncate_file returned %08x\n", Status
);
3529 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3531 acquire_fcb_lock_exclusive(Vcb
);
3532 free_fileref(Vcb
, fileref
);
3533 release_fcb_lock(Vcb
);
3539 if (Irp
->Overlay
.AllocationSize
.QuadPart
> 0) {
3540 Status
= extend_file(fileref
->fcb
, fileref
, Irp
->Overlay
.AllocationSize
.QuadPart
, TRUE
, NULL
, rollback
);
3542 if (!NT_SUCCESS(Status
)) {
3543 ERR("extend_file returned %08x\n", Status
);
3545 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3547 acquire_fcb_lock_exclusive(Vcb
);
3548 free_fileref(Vcb
, fileref
);
3549 release_fcb_lock(Vcb
);
3555 if (!fileref
->fcb
->ads
) {
3558 if (Irp
->AssociatedIrp
.SystemBuffer
&& IrpSp
->Parameters
.Create
.EaLength
> 0) {
3560 FILE_FULL_EA_INFORMATION
* eainfo
;
3562 Status
= IoCheckEaBufferValidity(Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.Create
.EaLength
, &offset
);
3563 if (!NT_SUCCESS(Status
)) {
3564 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
3566 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3568 acquire_fcb_lock_exclusive(Vcb
);
3569 free_fileref(Vcb
, fileref
);
3570 release_fcb_lock(Vcb
);
3575 fileref
->fcb
->ealen
= 4;
3577 // capitalize EA name
3578 eainfo
= Irp
->AssociatedIrp
.SystemBuffer
;
3582 s
.Length
= s
.MaximumLength
= eainfo
->EaNameLength
;
3583 s
.Buffer
= eainfo
->EaName
;
3585 RtlUpperString(&s
, &s
);
3587 fileref
->fcb
->ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
3589 if (eainfo
->NextEntryOffset
== 0)
3592 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
3595 if (fileref
->fcb
->ea_xattr
.Buffer
)
3596 ExFreePool(fileref
->fcb
->ea_xattr
.Buffer
);
3598 fileref
->fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(pool_type
, IrpSp
->Parameters
.Create
.EaLength
, ALLOC_TAG
);
3599 if (!fileref
->fcb
->ea_xattr
.Buffer
) {
3600 ERR("out of memory\n");
3601 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3603 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3605 acquire_fcb_lock_exclusive(Vcb
);
3606 free_fileref(Vcb
, fileref
);
3607 release_fcb_lock(Vcb
);
3612 fileref
->fcb
->ea_xattr
.Length
= fileref
->fcb
->ea_xattr
.MaximumLength
= (USHORT
)IrpSp
->Parameters
.Create
.EaLength
;
3613 RtlCopyMemory(fileref
->fcb
->ea_xattr
.Buffer
, Irp
->AssociatedIrp
.SystemBuffer
, fileref
->fcb
->ea_xattr
.Length
);
3615 if (fileref
->fcb
->ea_xattr
.Length
> 0) {
3616 ExFreePool(fileref
->fcb
->ea_xattr
.Buffer
);
3617 fileref
->fcb
->ea_xattr
.Buffer
= NULL
;
3618 fileref
->fcb
->ea_xattr
.Length
= fileref
->fcb
->ea_xattr
.MaximumLength
= 0;
3620 fileref
->fcb
->ea_changed
= TRUE
;
3621 fileref
->fcb
->ealen
= 0;
3625 // remove streams and send notifications
3626 le
= fileref
->fcb
->dir_children_index
.Flink
;
3627 while (le
!= &fileref
->fcb
->dir_children_index
) {
3628 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
3629 LIST_ENTRY
* le2
= le
->Flink
;
3631 if (dc
->index
== 0) {
3635 Status
= open_fileref_child(Vcb
, fileref
, &dc
->name
, TRUE
, TRUE
, TRUE
, PagedPool
, &fr2
, NULL
);
3636 if (!NT_SUCCESS(Status
))
3637 WARN("open_fileref_child returned %08x\n", Status
);
3641 send_notification_fcb(fileref
, FILE_NOTIFY_CHANGE_STREAM_NAME
, FILE_ACTION_REMOVED_STREAM
, &dc
->name
);
3643 Status
= delete_fileref(dc
->fileref
, NULL
, NULL
, rollback
);
3644 if (!NT_SUCCESS(Status
)) {
3645 ERR("delete_fileref returned %08x\n", Status
);
3647 acquire_fcb_lock_exclusive(Vcb
);
3648 free_fileref(Vcb
, fileref
);
3649 release_fcb_lock(Vcb
);
3661 KeQuerySystemTime(&time
);
3662 win_time_to_unix(time
, &now
);
3664 filter
= FILE_NOTIFY_CHANGE_SIZE
| FILE_NOTIFY_CHANGE_LAST_WRITE
;
3666 if (fileref
->fcb
->ads
) {
3667 fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
3668 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
3669 mark_fcb_dirty(fileref
->parent
->fcb
);
3671 send_notification_fcb(fileref
->parent
, filter
, FILE_ACTION_MODIFIED
, &fileref
->dc
->name
);
3673 mark_fcb_dirty(fileref
->fcb
);
3675 oldatts
= fileref
->fcb
->atts
;
3677 defda
= get_file_attributes(Vcb
, fileref
->fcb
->subvol
, fileref
->fcb
->inode
, fileref
->fcb
->type
,
3678 fileref
->dc
&& fileref
->dc
->name
.Length
>= sizeof(WCHAR
) && fileref
->dc
->name
.Buffer
[0] == '.', TRUE
, Irp
);
3680 if (RequestedDisposition
== FILE_SUPERSEDE
)
3681 fileref
->fcb
->atts
= IrpSp
->Parameters
.Create
.FileAttributes
| FILE_ATTRIBUTE_ARCHIVE
;
3683 fileref
->fcb
->atts
|= IrpSp
->Parameters
.Create
.FileAttributes
| FILE_ATTRIBUTE_ARCHIVE
;
3685 if (fileref
->fcb
->atts
!= oldatts
) {
3686 fileref
->fcb
->atts_changed
= TRUE
;
3687 fileref
->fcb
->atts_deleted
= IrpSp
->Parameters
.Create
.FileAttributes
== defda
;
3688 filter
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
3691 fileref
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
3692 fileref
->fcb
->inode_item
.sequence
++;
3693 fileref
->fcb
->inode_item
.st_ctime
= now
;
3694 fileref
->fcb
->inode_item
.st_mtime
= now
;
3695 fileref
->fcb
->inode_item_changed
= TRUE
;
3697 send_notification_fcb(fileref
, filter
, FILE_ACTION_MODIFIED
, NULL
);
3700 if (options
& FILE_NO_EA_KNOWLEDGE
&& fileref
->fcb
->ea_xattr
.Length
> 0) {
3701 FILE_FULL_EA_INFORMATION
* ffei
= (FILE_FULL_EA_INFORMATION
*)fileref
->fcb
->ea_xattr
.Buffer
;
3704 if (ffei
->Flags
& FILE_NEED_EA
) {
3705 WARN("returning STATUS_ACCESS_DENIED as no EA knowledge\n");
3706 Status
= STATUS_ACCESS_DENIED
;
3708 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3710 acquire_fcb_lock_exclusive(Vcb
);
3711 free_fileref(Vcb
, fileref
);
3712 release_fcb_lock(Vcb
);
3717 if (ffei
->NextEntryOffset
== 0)
3720 ffei
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ffei
) + ffei
->NextEntryOffset
);
3725 FileObject
->FsContext
= fileref
->fcb
;
3727 ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ccb
), ALLOC_TAG
);
3729 ERR("out of memory\n");
3730 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3732 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3734 acquire_fcb_lock_exclusive(Vcb
);
3735 free_fileref(Vcb
, fileref
);
3736 release_fcb_lock(Vcb
);
3741 RtlZeroMemory(ccb
, sizeof(*ccb
));
3743 ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
3744 ccb
->NodeSize
= sizeof(*ccb
);
3745 ccb
->disposition
= RequestedDisposition
;
3746 ccb
->options
= options
;
3747 ccb
->query_dir_offset
= 0;
3748 RtlInitUnicodeString(&ccb
->query_string
, NULL
);
3749 ccb
->has_wildcard
= FALSE
;
3750 ccb
->specific_file
= FALSE
;
3751 ccb
->access
= granted_access
;
3752 ccb
->case_sensitive
= IrpSp
->Flags
& SL_CASE_SENSITIVE
;
3753 ccb
->reserving
= FALSE
;
3754 ccb
->lxss
= called_from_lxss();
3756 ccb
->fileref
= fileref
;
3758 FileObject
->FsContext2
= ccb
;
3759 FileObject
->SectionObjectPointer
= &fileref
->fcb
->nonpaged
->segment_object
;
3761 if (NT_SUCCESS(Status
)) {
3762 switch (RequestedDisposition
) {
3763 case FILE_SUPERSEDE
:
3764 Irp
->IoStatus
.Information
= FILE_SUPERSEDED
;
3769 Irp
->IoStatus
.Information
= FILE_OPENED
;
3772 case FILE_OVERWRITE
:
3773 case FILE_OVERWRITE_IF
:
3774 Irp
->IoStatus
.Information
= FILE_OVERWRITTEN
;
3779 // Make sure paging files don't have any extents marked as being prealloc,
3780 // as this would mean we'd have to lock exclusively when writing.
3781 if (IrpSp
->Flags
& SL_OPEN_PAGING_FILE
) {
3783 BOOL changed
= FALSE
;
3785 ExAcquireResourceExclusiveLite(fileref
->fcb
->Header
.Resource
, TRUE
);
3787 le
= fileref
->fcb
->extents
.Flink
;
3789 while (le
!= &fileref
->fcb
->extents
) {
3790 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3792 if (ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) {
3793 ext
->extent_data
.type
= EXTENT_TYPE_REGULAR
;
3800 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
3803 fileref
->fcb
->extents_changed
= TRUE
;
3804 mark_fcb_dirty(fileref
->fcb
);
3807 fileref
->fcb
->Header
.Flags2
|= FSRTL_FLAG2_IS_PAGING_FILE
;
3808 Vcb
->disallow_dismount
= TRUE
;
3811 #ifdef DEBUG_FCB_REFCOUNTS
3812 oc
= InterlockedIncrement(&fileref
->open_count
);
3813 ERR("fileref %p: open_count now %i\n", fileref
, oc
);
3815 InterlockedIncrement(&fileref
->open_count
);
3817 InterlockedIncrement(&Vcb
->open_files
);
3822 Status
= file_create(Irp
, Vcb
, FileObject
, related
, loaded_related
, &fn
, RequestedDisposition
, options
, rollback
);
3823 release_fcb_lock(Vcb
);
3825 Irp
->IoStatus
.Information
= NT_SUCCESS(Status
) ? FILE_CREATED
: 0;
3826 granted_access
= IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
;
3829 if (NT_SUCCESS(Status
) && !(options
& FILE_NO_INTERMEDIATE_BUFFERING
))
3830 FileObject
->Flags
|= FO_CACHE_SUPPORTED
;
3833 if (loaded_related
) {
3834 acquire_fcb_lock_exclusive(Vcb
);
3835 free_fileref(Vcb
, related
);
3836 release_fcb_lock(Vcb
);
3839 if (Status
== STATUS_SUCCESS
) {
3842 IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->PreviouslyGrantedAccess
|= granted_access
;
3843 IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->RemainingDesiredAccess
&= ~(granted_access
| MAXIMUM_ALLOWED
);
3845 if (!FileObject
->Vpb
)
3846 FileObject
->Vpb
= DeviceObject
->Vpb
;
3848 fcb2
= FileObject
->FsContext
;
3851 struct _ccb
* ccb2
= FileObject
->FsContext2
;
3853 fcb2
= ccb2
->fileref
->parent
->fcb
;
3856 ExAcquireResourceExclusiveLite(fcb2
->Header
.Resource
, TRUE
);
3857 fcb_load_csums(Vcb
, fcb2
, Irp
);
3858 ExReleaseResourceLite(fcb2
->Header
.Resource
);
3859 } else if (Status
!= STATUS_REPARSE
&& Status
!= STATUS_OBJECT_NAME_NOT_FOUND
&& Status
!= STATUS_OBJECT_PATH_NOT_FOUND
)
3860 TRACE("returning %08x\n", Status
);
3863 time2
= KeQueryPerformanceCounter(NULL
);
3865 if (open_type
== 0) {
3866 Vcb
->stats
.open_total_time
+= time2
.QuadPart
- time1
.QuadPart
;
3867 Vcb
->stats
.num_opens
++;
3868 } else if (open_type
== 1) {
3869 Vcb
->stats
.overwrite_total_time
+= time2
.QuadPart
- time1
.QuadPart
;
3870 Vcb
->stats
.num_overwrites
++;
3871 } else if (open_type
== 2) {
3872 Vcb
->stats
.create_total_time
+= time2
.QuadPart
- time1
.QuadPart
;
3873 Vcb
->stats
.num_creates
++;
3880 static NTSTATUS
verify_vcb(device_extension
* Vcb
, PIRP Irp
) {
3883 BOOL need_verify
= FALSE
;
3885 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3887 le
= Vcb
->devices
.Flink
;
3888 while (le
!= &Vcb
->devices
) {
3889 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3891 if (dev
->devobj
&& dev
->removable
) {
3893 IO_STATUS_BLOCK iosb
;
3895 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
3897 if (IoIsErrorUserInduced(Status
)) {
3898 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status
);
3900 } else if (!NT_SUCCESS(Status
)) {
3901 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status
);
3903 } else if (iosb
.Information
< sizeof(ULONG
)) {
3904 ERR("iosb.Information was too short\n");
3905 Status
= STATUS_INTERNAL_ERROR
;
3906 } else if (cc
!= dev
->change_count
) {
3907 dev
->devobj
->Flags
|= DO_VERIFY_VOLUME
;
3915 Status
= STATUS_SUCCESS
;
3918 ExReleaseResourceLite(&Vcb
->tree_lock
);
3921 PDEVICE_OBJECT devobj
;
3923 devobj
= IoGetDeviceToVerify(Irp
->Tail
.Overlay
.Thread
);
3924 IoSetDeviceToVerify(Irp
->Tail
.Overlay
.Thread
, NULL
);
3927 devobj
= IoGetDeviceToVerify(PsGetCurrentThread());
3928 IoSetDeviceToVerify(PsGetCurrentThread(), NULL
);
3931 devobj
= Vcb
->Vpb
? Vcb
->Vpb
->RealDevice
: NULL
;
3934 Status
= IoVerifyVolume(devobj
, FALSE
);
3936 Status
= STATUS_VERIFY_REQUIRED
;
3942 static BOOL
has_manage_volume_privilege(ACCESS_STATE
* access_state
, KPROCESSOR_MODE processor_mode
) {
3943 PRIVILEGE_SET privset
;
3945 privset
.PrivilegeCount
= 1;
3946 privset
.Control
= PRIVILEGE_SET_ALL_NECESSARY
;
3947 privset
.Privilege
[0].Luid
= RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
);
3948 privset
.Privilege
[0].Attributes
= 0;
3950 return SePrivilegeCheck(&privset
, &access_state
->SubjectSecurityContext
, processor_mode
) ? TRUE
: FALSE
;
3953 _Dispatch_type_(IRP_MJ_CREATE
)
3954 _Function_class_(DRIVER_DISPATCH
)
3955 NTSTATUS NTAPI
drv_create(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3957 PIO_STACK_LOCATION IrpSp
;
3958 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3959 BOOL top_level
, locked
= FALSE
;
3961 FsRtlEnterFileSystem();
3963 TRACE("create (flags = %x)\n", Irp
->Flags
);
3965 top_level
= is_top_level(Irp
);
3967 /* return success if just called for FS device object */
3968 if (DeviceObject
== master_devobj
) {
3969 TRACE("create called for FS device object\n");
3971 Irp
->IoStatus
.Information
= FILE_OPENED
;
3972 Status
= STATUS_SUCCESS
;
3975 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
3976 Status
= vol_create(DeviceObject
, Irp
);
3978 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
3979 Status
= STATUS_INVALID_PARAMETER
;
3983 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
)) {
3984 Status
= STATUS_DEVICE_NOT_READY
;
3988 if (Vcb
->removing
) {
3989 Status
= STATUS_ACCESS_DENIED
;
3993 Status
= verify_vcb(Vcb
, Irp
);
3994 if (!NT_SUCCESS(Status
)) {
3995 ERR("verify_vcb returned %08x\n", Status
);
3999 ExAcquireResourceSharedLite(&Vcb
->load_lock
, TRUE
);
4002 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4004 if (IrpSp
->Flags
!= 0) {
4005 UINT32 flags
= IrpSp
->Flags
;
4009 if (flags
& SL_CASE_SENSITIVE
) {
4010 TRACE("SL_CASE_SENSITIVE\n");
4011 flags
&= ~SL_CASE_SENSITIVE
;
4014 if (flags
& SL_FORCE_ACCESS_CHECK
) {
4015 TRACE("SL_FORCE_ACCESS_CHECK\n");
4016 flags
&= ~SL_FORCE_ACCESS_CHECK
;
4019 if (flags
& SL_OPEN_PAGING_FILE
) {
4020 TRACE("SL_OPEN_PAGING_FILE\n");
4021 flags
&= ~SL_OPEN_PAGING_FILE
;
4024 if (flags
& SL_OPEN_TARGET_DIRECTORY
) {
4025 TRACE("SL_OPEN_TARGET_DIRECTORY\n");
4026 flags
&= ~SL_OPEN_TARGET_DIRECTORY
;
4029 if (flags
& SL_STOP_ON_SYMLINK
) {
4030 TRACE("SL_STOP_ON_SYMLINK\n");
4031 flags
&= ~SL_STOP_ON_SYMLINK
;
4035 WARN("unknown flags: %x\n", flags
);
4037 TRACE("flags: (none)\n");
4040 if (!IrpSp
->FileObject
) {
4041 ERR("FileObject was NULL\n");
4042 Status
= STATUS_INVALID_PARAMETER
;
4046 if (IrpSp
->FileObject
->RelatedFileObject
) {
4047 fcb
* relatedfcb
= IrpSp
->FileObject
->RelatedFileObject
->FsContext
;
4049 if (relatedfcb
&& relatedfcb
->Vcb
!= Vcb
) {
4050 WARN("RelatedFileObject was for different device\n");
4051 Status
= STATUS_INVALID_PARAMETER
;
4057 if (IrpSp
->FileObject
->FileName
.Length
== 0 && !IrpSp
->FileObject
->RelatedFileObject
) {
4058 ULONG RequestedDisposition
= ((IrpSp
->Parameters
.Create
.Options
>> 24) & 0xff);
4059 ULONG RequestedOptions
= IrpSp
->Parameters
.Create
.Options
& FILE_VALID_OPTION_FLAGS
;
4060 #ifdef DEBUG_FCB_REFCOUNTS
4065 TRACE("open operation for volume\n");
4067 if (RequestedDisposition
!= FILE_OPEN
&& RequestedDisposition
!= FILE_OPEN_IF
) {
4068 Status
= STATUS_ACCESS_DENIED
;
4072 if (RequestedOptions
& FILE_DIRECTORY_FILE
) {
4073 Status
= STATUS_NOT_A_DIRECTORY
;
4077 ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ccb
), ALLOC_TAG
);
4079 ERR("out of memory\n");
4080 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4084 RtlZeroMemory(ccb
, sizeof(*ccb
));
4086 ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
4087 ccb
->NodeSize
= sizeof(*ccb
);
4088 ccb
->disposition
= RequestedDisposition
;
4089 ccb
->options
= RequestedOptions
;
4090 ccb
->access
= IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->PreviouslyGrantedAccess
;
4091 ccb
->manage_volume_privilege
= has_manage_volume_privilege(IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
,
4092 IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
);
4093 ccb
->reserving
= FALSE
;
4094 ccb
->lxss
= called_from_lxss();
4096 #ifdef DEBUG_FCB_REFCOUNTS
4097 rc
= InterlockedIncrement(&Vcb
->volume_fcb
->refcount
);
4098 WARN("fcb %p: refcount now %i (volume)\n", Vcb
->volume_fcb
, rc
);
4100 InterlockedIncrement(&Vcb
->volume_fcb
->refcount
);
4102 IrpSp
->FileObject
->FsContext
= Vcb
->volume_fcb
;
4103 IrpSp
->FileObject
->FsContext2
= ccb
;
4105 IrpSp
->FileObject
->SectionObjectPointer
= &Vcb
->volume_fcb
->nonpaged
->segment_object
;
4107 if (!IrpSp
->FileObject
->Vpb
)
4108 IrpSp
->FileObject
->Vpb
= DeviceObject
->Vpb
;
4110 InterlockedIncrement(&Vcb
->open_files
);
4112 Irp
->IoStatus
.Information
= FILE_OPENED
;
4113 Status
= STATUS_SUCCESS
;
4115 LIST_ENTRY rollback
;
4118 InitializeListHead(&rollback
);
4120 TRACE("file name: %.*S\n", IrpSp
->FileObject
->FileName
.Length
/ sizeof(WCHAR
), IrpSp
->FileObject
->FileName
.Buffer
);
4122 if (IrpSp
->FileObject
->RelatedFileObject
)
4123 TRACE("related file = %S\n", file_desc(IrpSp
->FileObject
->RelatedFileObject
));
4125 // Don't lock again if we're being called from within CcCopyRead etc.
4126 skip_lock
= ExIsResourceAcquiredExclusiveLite(&Vcb
->tree_lock
);
4129 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
4131 Status
= open_file(DeviceObject
, Vcb
, Irp
, &rollback
);
4133 if (!NT_SUCCESS(Status
))
4134 do_rollback(Vcb
, &rollback
);
4136 clear_rollback(&rollback
);
4139 ExReleaseResourceLite(&Vcb
->tree_lock
);
4143 Irp
->IoStatus
.Status
= Status
;
4144 IoCompleteRequest( Irp
, NT_SUCCESS(Status
) ? IO_DISK_INCREMENT
: IO_NO_INCREMENT
);
4146 TRACE("create returning %08x\n", Status
);
4149 ExReleaseResourceLite(&Vcb
->load_lock
);
4152 IoSetTopLevelIrp(NULL
);
4154 FsRtlExitFileSystem();