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 WCHAR datastring
[] = L
"::$DATA";
28 fcb
* create_fcb(device_extension
* Vcb
, POOL_TYPE pool_type
) {
31 if (pool_type
== NonPagedPool
) {
32 fcb
= ExAllocatePoolWithTag(pool_type
, sizeof(struct _fcb
), ALLOC_TAG
);
34 ERR("out of memory\n");
38 fcb
= ExAllocateFromPagedLookasideList(&Vcb
->fcb_lookaside
);
40 ERR("out of memory\n");
45 #ifdef DEBUG_FCB_REFCOUNTS
46 WARN("allocating fcb %p\n", fcb
);
48 RtlZeroMemory(fcb
, sizeof(struct _fcb
));
49 fcb
->pool_type
= pool_type
;
51 fcb
->Header
.NodeTypeCode
= BTRFS_NODE_TYPE_FCB
;
52 fcb
->Header
.NodeByteSize
= sizeof(struct _fcb
);
54 fcb
->nonpaged
= ExAllocateFromNPagedLookasideList(&Vcb
->fcb_np_lookaside
);
56 ERR("out of memory\n");
58 if (pool_type
== NonPagedPool
)
61 ExFreeToPagedLookasideList(&Vcb
->fcb_lookaside
, fcb
);
65 RtlZeroMemory(fcb
->nonpaged
, sizeof(struct _fcb_nonpaged
));
67 ExInitializeResourceLite(&fcb
->nonpaged
->paging_resource
);
68 fcb
->Header
.PagingIoResource
= &fcb
->nonpaged
->paging_resource
;
70 ExInitializeFastMutex(&fcb
->nonpaged
->HeaderMutex
);
71 FsRtlSetupAdvancedHeader(&fcb
->Header
, &fcb
->nonpaged
->HeaderMutex
);
74 #ifdef DEBUG_FCB_REFCOUNTS
75 WARN("fcb %p: refcount now %i\n", fcb
, fcb
->refcount
);
78 ExInitializeResourceLite(&fcb
->nonpaged
->resource
);
79 fcb
->Header
.Resource
= &fcb
->nonpaged
->resource
;
81 ExInitializeResourceLite(&fcb
->nonpaged
->dir_children_lock
);
83 FsRtlInitializeFileLock(&fcb
->lock
, NULL
, NULL
);
85 InitializeListHead(&fcb
->extents
);
86 InitializeListHead(&fcb
->hardlinks
);
87 InitializeListHead(&fcb
->xattrs
);
89 InitializeListHead(&fcb
->dir_children_index
);
90 InitializeListHead(&fcb
->dir_children_hash
);
91 InitializeListHead(&fcb
->dir_children_hash_uc
);
96 file_ref
* create_fileref(device_extension
* Vcb
) {
99 fr
= ExAllocateFromPagedLookasideList(&Vcb
->fileref_lookaside
);
101 ERR("out of memory\n");
105 RtlZeroMemory(fr
, sizeof(file_ref
));
107 fr
->nonpaged
= ExAllocateFromNPagedLookasideList(&Vcb
->fileref_np_lookaside
);
109 ERR("out of memory\n");
110 ExFreeToPagedLookasideList(&Vcb
->fileref_lookaside
, fr
);
116 #ifdef DEBUG_FCB_REFCOUNTS
117 WARN("fileref %p: refcount now 1\n", fr
);
120 InitializeListHead(&fr
->children
);
122 ExInitializeResourceLite(&fr
->nonpaged
->fileref_lock
);
123 ExInitializeResourceLite(&fr
->nonpaged
->children_lock
);
128 NTSTATUS
find_file_in_dir(PUNICODE_STRING filename
, fcb
* fcb
, root
** subvol
, UINT64
* inode
, dir_child
** pdc
, BOOL case_sensitive
) {
136 if (!case_sensitive
) {
137 Status
= RtlUpcaseUnicodeString(&fnus
, filename
, TRUE
);
139 if (!NT_SUCCESS(Status
)) {
140 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
146 hash
= calc_crc32c(0xffffffff, (UINT8
*)fnus
.Buffer
, fnus
.Length
);
150 if (!ExIsResourceAcquiredSharedLite(&fcb
->nonpaged
->dir_children_lock
)) {
151 ExAcquireResourceSharedLite(&fcb
->nonpaged
->dir_children_lock
, TRUE
);
155 if (case_sensitive
) {
156 if (!fcb
->hash_ptrs
[c
]) {
157 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
161 le
= fcb
->hash_ptrs
[c
];
162 while (le
!= &fcb
->dir_children_hash
) {
163 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash
);
165 if (dc
->hash
== hash
) {
166 if (dc
->name
.Length
== fnus
.Length
&& RtlCompareMemory(dc
->name
.Buffer
, fnus
.Buffer
, fnus
.Length
) == fnus
.Length
) {
167 if (dc
->key
.obj_type
== TYPE_ROOT_ITEM
) {
172 le2
= fcb
->Vcb
->roots
.Flink
;
173 while (le2
!= &fcb
->Vcb
->roots
) {
174 root
* r2
= CONTAINING_RECORD(le2
, root
, list_entry
);
176 if (r2
->id
== dc
->key
.obj_id
) {
184 *inode
= SUBVOL_ROOT_INODE
;
186 *subvol
= fcb
->subvol
;
187 *inode
= dc
->key
.obj_id
;
192 Status
= STATUS_SUCCESS
;
195 } else if (dc
->hash
> hash
) {
196 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
203 if (!fcb
->hash_ptrs_uc
[c
]) {
204 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
208 le
= fcb
->hash_ptrs_uc
[c
];
209 while (le
!= &fcb
->dir_children_hash_uc
) {
210 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash_uc
);
212 if (dc
->hash_uc
== hash
) {
213 if (dc
->name_uc
.Length
== fnus
.Length
&& RtlCompareMemory(dc
->name_uc
.Buffer
, fnus
.Buffer
, fnus
.Length
) == fnus
.Length
) {
214 if (dc
->key
.obj_type
== TYPE_ROOT_ITEM
) {
219 le2
= fcb
->Vcb
->roots
.Flink
;
220 while (le2
!= &fcb
->Vcb
->roots
) {
221 root
* r2
= CONTAINING_RECORD(le2
, root
, list_entry
);
223 if (r2
->id
== dc
->key
.obj_id
) {
231 *inode
= SUBVOL_ROOT_INODE
;
233 *subvol
= fcb
->subvol
;
234 *inode
= dc
->key
.obj_id
;
239 Status
= STATUS_SUCCESS
;
242 } else if (dc
->hash_uc
> hash
) {
243 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
251 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
255 ExReleaseResourceLite(&fcb
->nonpaged
->dir_children_lock
);
258 ExFreePool(fnus
.Buffer
);
263 static NTSTATUS
split_path(device_extension
* Vcb
, PUNICODE_STRING path
, LIST_ENTRY
* parts
, BOOL
* stream
) {
269 len
= path
->Length
/ sizeof(WCHAR
);
270 if (len
> 0 && (path
->Buffer
[len
- 1] == '/' || path
->Buffer
[len
- 1] == '\\'))
274 for (i
= 0; i
< len
; i
++) {
275 if (path
->Buffer
[i
] == '/' || path
->Buffer
[i
] == '\\') {
277 } else if (path
->Buffer
[i
] == ':') {
284 for (i
= 0; i
< len
; i
++) {
285 if (path
->Buffer
[i
] == '/' || path
->Buffer
[i
] == '\\') {
286 nb
= ExAllocateFromPagedLookasideList(&Vcb
->name_bit_lookaside
);
288 ERR("out of memory\n");
289 return STATUS_INSUFFICIENT_RESOURCES
;
293 nb
->us
.Length
= nb
->us
.MaximumLength
= (USHORT
)(&path
->Buffer
[i
] - buf
) * sizeof(WCHAR
);
294 InsertTailList(parts
, &nb
->list_entry
);
296 buf
= &path
->Buffer
[i
+1];
300 nb
= ExAllocateFromPagedLookasideList(&Vcb
->name_bit_lookaside
);
302 ERR("out of memory\n");
303 return STATUS_INSUFFICIENT_RESOURCES
;
307 nb
->us
.Length
= nb
->us
.MaximumLength
= (USHORT
)(&path
->Buffer
[i
] - buf
) * sizeof(WCHAR
);
308 InsertTailList(parts
, &nb
->list_entry
);
311 static WCHAR datasuf
[] = {':','$','D','A','T','A',0};
314 dsus
.Buffer
= datasuf
;
315 dsus
.Length
= dsus
.MaximumLength
= (UINT16
)wcslen(datasuf
) * sizeof(WCHAR
);
317 for (i
= 0; i
< nb
->us
.Length
/ sizeof(WCHAR
); i
++) {
318 if (nb
->us
.Buffer
[i
] == ':') {
321 nb2
= ExAllocateFromPagedLookasideList(&Vcb
->name_bit_lookaside
);
323 ERR("out of memory\n");
324 return STATUS_INSUFFICIENT_RESOURCES
;
327 nb2
->us
.Buffer
= &nb
->us
.Buffer
[i
+1];
328 nb2
->us
.Length
= nb2
->us
.MaximumLength
= (UINT16
)(nb
->us
.Length
- (i
* sizeof(WCHAR
)) - sizeof(WCHAR
));
329 InsertTailList(parts
, &nb2
->list_entry
);
331 nb
->us
.Length
= (UINT16
)i
* sizeof(WCHAR
);
332 nb
->us
.MaximumLength
= nb
->us
.Length
;
340 // FIXME - should comparison be case-insensitive?
341 // remove :$DATA suffix
342 if (nb
->us
.Length
>= dsus
.Length
&& RtlCompareMemory(&nb
->us
.Buffer
[(nb
->us
.Length
- dsus
.Length
)/sizeof(WCHAR
)], dsus
.Buffer
, dsus
.Length
) == dsus
.Length
)
343 nb
->us
.Length
-= dsus
.Length
;
345 if (nb
->us
.Length
== 0) {
346 RemoveTailList(parts
);
347 ExFreeToPagedLookasideList(&Vcb
->name_bit_lookaside
, nb
);
353 // if path is just stream name, remove first empty item
354 if (has_stream
&& path
->Length
>= sizeof(WCHAR
) && path
->Buffer
[0] == ':') {
355 name_bit
*nb1
= CONTAINING_RECORD(RemoveHeadList(parts
), name_bit
, list_entry
);
357 ExFreeToPagedLookasideList(&Vcb
->name_bit_lookaside
, nb1
);
360 *stream
= has_stream
;
362 return STATUS_SUCCESS
;
365 NTSTATUS
load_csum(_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, UINT32
* csum
, UINT64 start
, UINT64 length
, PIRP Irp
) {
368 traverse_ptr tp
, next_tp
;
372 searchkey
.obj_id
= EXTENT_CSUM_ID
;
373 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
374 searchkey
.offset
= start
;
376 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
, Irp
);
377 if (!NT_SUCCESS(Status
)) {
378 ERR("error - find_item returned %08x\n", Status
);
384 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
387 if (start
< tp
.item
->key
.offset
)
390 j
= ((start
- tp
.item
->key
.offset
) / Vcb
->superblock
.sector_size
) + i
;
392 if (j
* sizeof(UINT32
) > tp
.item
->size
|| tp
.item
->key
.offset
> start
+ (i
* Vcb
->superblock
.sector_size
)) {
393 ERR("checksum not found for %llx\n", start
+ (i
* Vcb
->superblock
.sector_size
));
394 return STATUS_INTERNAL_ERROR
;
397 readlen
= (ULONG
)min((tp
.item
->size
/ sizeof(UINT32
)) - j
, length
- i
);
398 RtlCopyMemory(&csum
[i
], tp
.item
->data
+ (j
* sizeof(UINT32
)), readlen
* sizeof(UINT32
));
405 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
412 ERR("could not read checksums: offset %llx, length %llx sectors\n", start
, length
);
413 return STATUS_INTERNAL_ERROR
;
416 return STATUS_SUCCESS
;
419 NTSTATUS
load_dir_children(_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, fcb
* fcb
, BOOL ignore_size
, PIRP Irp
) {
421 traverse_ptr tp
, next_tp
;
423 ULONG num_children
= 0;
425 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
426 if (!fcb
->hash_ptrs
) {
427 ERR("out of memory\n");
428 return STATUS_INSUFFICIENT_RESOURCES
;
431 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
433 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
434 if (!fcb
->hash_ptrs_uc
) {
435 ERR("out of memory\n");
436 return STATUS_INSUFFICIENT_RESOURCES
;
439 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
441 if (!ignore_size
&& fcb
->inode_item
.st_size
== 0)
442 return STATUS_SUCCESS
;
444 searchkey
.obj_id
= fcb
->inode
;
445 searchkey
.obj_type
= TYPE_DIR_INDEX
;
446 searchkey
.offset
= 2;
448 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
449 if (!NT_SUCCESS(Status
)) {
450 ERR("find_item returned %08x\n", Status
);
454 if (keycmp(tp
.item
->key
, searchkey
) == -1) {
455 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
)) {
457 TRACE("moving on to %llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
461 while (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
462 DIR_ITEM
* di
= (DIR_ITEM
*)tp
.item
->data
;
466 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
467 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
));
472 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
);
476 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &utf16len
, di
->name
, di
->n
);
477 if (!NT_SUCCESS(Status
)) {
478 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
482 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
484 ERR("out of memory\n");
485 return STATUS_INSUFFICIENT_RESOURCES
;
489 dc
->index
= tp
.item
->key
.offset
;
493 dc
->utf8
.MaximumLength
= dc
->utf8
.Length
= di
->n
;
494 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, di
->n
, ALLOC_TAG
);
495 if (!dc
->utf8
.Buffer
) {
496 ERR("out of memory\n");
498 return STATUS_INSUFFICIENT_RESOURCES
;
501 RtlCopyMemory(dc
->utf8
.Buffer
, di
->name
, di
->n
);
503 dc
->name
.MaximumLength
= dc
->name
.Length
= (UINT16
)utf16len
;
504 dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dc
->name
.MaximumLength
, ALLOC_TAG
);
505 if (!dc
->name
.Buffer
) {
506 ERR("out of memory\n");
507 ExFreePool(dc
->utf8
.Buffer
);
509 return STATUS_INSUFFICIENT_RESOURCES
;
512 Status
= RtlUTF8ToUnicodeN(dc
->name
.Buffer
, utf16len
, &utf16len
, di
->name
, di
->n
);
513 if (!NT_SUCCESS(Status
)) {
514 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
515 ExFreePool(dc
->utf8
.Buffer
);
516 ExFreePool(dc
->name
.Buffer
);
521 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, &dc
->name
, TRUE
);
522 if (!NT_SUCCESS(Status
)) {
523 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
524 ExFreePool(dc
->utf8
.Buffer
);
525 ExFreePool(dc
->name
.Buffer
);
530 dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name
.Buffer
, dc
->name
.Length
);
531 dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name_uc
.Buffer
, dc
->name_uc
.Length
);
533 InsertTailList(&fcb
->dir_children_index
, &dc
->list_entry_index
);
535 insert_dir_child_into_hash_lists(fcb
, dc
);
540 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
))
546 // If a directory has a lot of files, force it to stick around until the next flush
547 // so we aren't constantly re-reading.
548 if (num_children
>= 100)
551 return STATUS_SUCCESS
;
554 NTSTATUS
open_fcb(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
555 root
* subvol
, UINT64 inode
, UINT8 type
, PANSI_STRING utf8
, fcb
* parent
, fcb
** pfcb
, POOL_TYPE pooltype
, PIRP Irp
) {
557 traverse_ptr tp
, next_tp
;
559 fcb
*fcb
, *deleted_fcb
= NULL
;
560 BOOL atts_set
= FALSE
, sd_set
= FALSE
, no_data
;
561 LIST_ENTRY
* lastle
= NULL
;
562 EXTENT_DATA
* ed
= NULL
;
564 if (!IsListEmpty(&subvol
->fcbs
)) {
565 LIST_ENTRY
* le
= subvol
->fcbs
.Flink
;
567 while (le
!= &subvol
->fcbs
) {
568 fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
570 if (fcb
->inode
== inode
) {
575 #ifdef DEBUG_FCB_REFCOUNTS
576 LONG rc
= InterlockedIncrement(&fcb
->refcount
);
578 WARN("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb
, rc
, fcb
->subvol
->id
, fcb
->inode
);
580 InterlockedIncrement(&fcb
->refcount
);
584 return STATUS_SUCCESS
;
587 } else if (fcb
->inode
> inode
) {
589 InterlockedIncrement(&deleted_fcb
->refcount
);
591 return STATUS_SUCCESS
;
603 InterlockedIncrement(&deleted_fcb
->refcount
);
605 return STATUS_SUCCESS
;
608 fcb
= create_fcb(Vcb
, pooltype
);
610 ERR("out of memory\n");
611 return STATUS_INSUFFICIENT_RESOURCES
;
616 fcb
->subvol
= subvol
;
620 searchkey
.obj_id
= inode
;
621 searchkey
.obj_type
= TYPE_INODE_ITEM
;
622 searchkey
.offset
= 0xffffffffffffffff;
624 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
625 if (!NT_SUCCESS(Status
)) {
626 ERR("error - find_item returned %08x\n", Status
);
631 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
632 WARN("couldn't find INODE_ITEM for inode %llx in subvol %llx\n", inode
, subvol
->id
);
634 return STATUS_INVALID_PARAMETER
;
637 if (tp
.item
->size
> 0)
638 RtlCopyMemory(&fcb
->inode_item
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
640 if (fcb
->type
== 0) { // guess the type from the inode mode, if the caller doesn't know already
641 if ((fcb
->inode_item
.st_mode
& __S_IFDIR
) == __S_IFDIR
)
642 fcb
->type
= BTRFS_TYPE_DIRECTORY
;
643 else if ((fcb
->inode_item
.st_mode
& __S_IFCHR
) == __S_IFCHR
)
644 fcb
->type
= BTRFS_TYPE_CHARDEV
;
645 else if ((fcb
->inode_item
.st_mode
& __S_IFBLK
) == __S_IFBLK
)
646 fcb
->type
= BTRFS_TYPE_BLOCKDEV
;
647 else if ((fcb
->inode_item
.st_mode
& __S_IFIFO
) == __S_IFIFO
)
648 fcb
->type
= BTRFS_TYPE_FIFO
;
649 else if ((fcb
->inode_item
.st_mode
& __S_IFLNK
) == __S_IFLNK
)
650 fcb
->type
= BTRFS_TYPE_SYMLINK
;
651 else if ((fcb
->inode_item
.st_mode
& __S_IFSOCK
) == __S_IFSOCK
)
652 fcb
->type
= BTRFS_TYPE_SOCKET
;
654 fcb
->type
= BTRFS_TYPE_FILE
;
657 no_data
= fcb
->inode_item
.st_size
== 0 || (fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
);
659 while (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
)) {
662 if (tp
.item
->key
.obj_id
> inode
)
665 if ((no_data
&& tp
.item
->key
.obj_type
> TYPE_XATTR_ITEM
) || tp
.item
->key
.obj_type
> TYPE_EXTENT_DATA
)
668 if (fcb
->inode_item
.st_nlink
> 1 && tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
673 ir
= (INODE_REF
*)tp
.item
->data
;
675 while (len
>= sizeof(INODE_REF
) - 1) {
679 hl
= ExAllocatePoolWithTag(pooltype
, sizeof(hardlink
), ALLOC_TAG
);
681 ERR("out of memory\n");
683 return STATUS_INSUFFICIENT_RESOURCES
;
686 hl
->parent
= tp
.item
->key
.offset
;
687 hl
->index
= ir
->index
;
689 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= ir
->n
;
691 if (hl
->utf8
.Length
> 0) {
692 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(pooltype
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
693 RtlCopyMemory(hl
->utf8
.Buffer
, ir
->name
, ir
->n
);
696 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ir
->name
, ir
->n
);
697 if (!NT_SUCCESS(Status
)) {
698 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
704 hl
->name
.Length
= hl
->name
.MaximumLength
= (UINT16
)stringlen
;
707 hl
->name
.Buffer
= NULL
;
709 hl
->name
.Buffer
= ExAllocatePoolWithTag(pooltype
, hl
->name
.MaximumLength
, ALLOC_TAG
);
711 if (!hl
->name
.Buffer
) {
712 ERR("out of memory\n");
715 return STATUS_INSUFFICIENT_RESOURCES
;
718 Status
= RtlUTF8ToUnicodeN(hl
->name
.Buffer
, stringlen
, &stringlen
, ir
->name
, ir
->n
);
719 if (!NT_SUCCESS(Status
)) {
720 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
721 ExFreePool(hl
->name
.Buffer
);
728 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
730 len
-= sizeof(INODE_REF
) - 1 + ir
->n
;
731 ir
= (INODE_REF
*)&ir
->name
[ir
->n
];
733 } else if (fcb
->inode_item
.st_nlink
> 1 && tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
738 ier
= (INODE_EXTREF
*)tp
.item
->data
;
740 while (len
>= sizeof(INODE_EXTREF
) - 1) {
744 hl
= ExAllocatePoolWithTag(pooltype
, sizeof(hardlink
), ALLOC_TAG
);
746 ERR("out of memory\n");
748 return STATUS_INSUFFICIENT_RESOURCES
;
751 hl
->parent
= ier
->dir
;
752 hl
->index
= ier
->index
;
754 hl
->utf8
.Length
= hl
->utf8
.MaximumLength
= ier
->n
;
756 if (hl
->utf8
.Length
> 0) {
757 hl
->utf8
.Buffer
= ExAllocatePoolWithTag(pooltype
, hl
->utf8
.MaximumLength
, ALLOC_TAG
);
758 RtlCopyMemory(hl
->utf8
.Buffer
, ier
->name
, ier
->n
);
761 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, ier
->name
, ier
->n
);
762 if (!NT_SUCCESS(Status
)) {
763 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
769 hl
->name
.Length
= hl
->name
.MaximumLength
= (UINT16
)stringlen
;
772 hl
->name
.Buffer
= NULL
;
774 hl
->name
.Buffer
= ExAllocatePoolWithTag(pooltype
, hl
->name
.MaximumLength
, ALLOC_TAG
);
776 if (!hl
->name
.Buffer
) {
777 ERR("out of memory\n");
780 return STATUS_INSUFFICIENT_RESOURCES
;
783 Status
= RtlUTF8ToUnicodeN(hl
->name
.Buffer
, stringlen
, &stringlen
, ier
->name
, ier
->n
);
784 if (!NT_SUCCESS(Status
)) {
785 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
786 ExFreePool(hl
->name
.Buffer
);
793 InsertTailList(&fcb
->hardlinks
, &hl
->list_entry
);
795 len
-= sizeof(INODE_EXTREF
) - 1 + ier
->n
;
796 ier
= (INODE_EXTREF
*)&ier
->name
[ier
->n
];
798 } else if (tp
.item
->key
.obj_type
== TYPE_XATTR_ITEM
) {
802 static char xapref
[] = "user.";
804 if (tp
.item
->size
< offsetof(DIR_ITEM
, name
[0])) {
805 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]));
810 di
= (DIR_ITEM
*)tp
.item
->data
;
813 if (len
< offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
)
816 if (tp
.item
->key
.offset
== EA_REPARSE_HASH
&& di
->n
== strlen(EA_REPARSE
) && RtlCompareMemory(EA_REPARSE
, di
->name
, di
->n
) == di
->n
) {
818 fcb
->reparse_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, di
->m
, ALLOC_TAG
);
819 if (!fcb
->reparse_xattr
.Buffer
) {
820 ERR("out of memory\n");
822 return STATUS_INSUFFICIENT_RESOURCES
;
825 RtlCopyMemory(fcb
->reparse_xattr
.Buffer
, &di
->name
[di
->n
], di
->m
);
827 fcb
->reparse_xattr
.Buffer
= NULL
;
829 fcb
->reparse_xattr
.Length
= fcb
->reparse_xattr
.MaximumLength
= di
->m
;
830 } else if (tp
.item
->key
.offset
== EA_EA_HASH
&& di
->n
== strlen(EA_EA
) && RtlCompareMemory(EA_EA
, di
->name
, di
->n
) == di
->n
) {
834 Status
= IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION
*)&di
->name
[di
->n
], di
->m
, &offset
);
836 if (!NT_SUCCESS(Status
))
837 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
839 FILE_FULL_EA_INFORMATION
* eainfo
;
841 fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, di
->m
, ALLOC_TAG
);
842 if (!fcb
->ea_xattr
.Buffer
) {
843 ERR("out of memory\n");
845 return STATUS_INSUFFICIENT_RESOURCES
;
848 RtlCopyMemory(fcb
->ea_xattr
.Buffer
, &di
->name
[di
->n
], di
->m
);
850 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= di
->m
;
855 eainfo
= (FILE_FULL_EA_INFORMATION
*)&di
->name
[di
->n
];
857 fcb
->ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
859 if (eainfo
->NextEntryOffset
== 0)
862 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
866 } else if (tp
.item
->key
.offset
== EA_DOSATTRIB_HASH
&& di
->n
== strlen(EA_DOSATTRIB
) && RtlCompareMemory(EA_DOSATTRIB
, di
->name
, di
->n
) == di
->n
) {
868 if (get_file_attributes_from_xattr(&di
->name
[di
->n
], di
->m
, &fcb
->atts
)) {
871 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
)
872 fcb
->atts
|= FILE_ATTRIBUTE_DIRECTORY
;
873 else if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
874 fcb
->atts
|= FILE_ATTRIBUTE_REPARSE_POINT
;
876 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
)
877 fcb
->atts
&= ~FILE_ATTRIBUTE_DIRECTORY
;
879 if (inode
== SUBVOL_ROOT_INODE
) {
880 if (subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
881 fcb
->atts
|= FILE_ATTRIBUTE_READONLY
;
883 fcb
->atts
&= ~FILE_ATTRIBUTE_READONLY
;
887 } else if (tp
.item
->key
.offset
== EA_NTACL_HASH
&& di
->n
== strlen(EA_NTACL
) && RtlCompareMemory(EA_NTACL
, di
->name
, di
->n
) == di
->n
) {
889 fcb
->sd
= ExAllocatePoolWithTag(PagedPool
, di
->m
, ALLOC_TAG
);
891 ERR("out of memory\n");
893 return STATUS_INSUFFICIENT_RESOURCES
;
896 RtlCopyMemory(fcb
->sd
, &di
->name
[di
->n
], di
->m
);
898 // We have to test against our copy rather than the source, as RtlValidRelativeSecurityDescriptor
899 // will fail if the ACLs aren't 32-bit aligned.
900 if (!RtlValidRelativeSecurityDescriptor(fcb
->sd
, di
->m
, 0))
905 } else if (tp
.item
->key
.offset
== EA_PROP_COMPRESSION_HASH
&& di
->n
== strlen(EA_PROP_COMPRESSION
) && RtlCompareMemory(EA_PROP_COMPRESSION
, di
->name
, di
->n
) == di
->n
) {
907 const char lzo
[] = "lzo";
908 const char zlib
[] = "zlib";
910 if (di
->m
== strlen(lzo
) && RtlCompareMemory(&di
->name
[di
->n
], lzo
, di
->m
) == di
->m
)
911 fcb
->prop_compression
= PropCompression_LZO
;
912 else if (di
->m
== strlen(zlib
) && RtlCompareMemory(&di
->name
[di
->n
], zlib
, di
->m
) == di
->m
)
913 fcb
->prop_compression
= PropCompression_Zlib
;
915 fcb
->prop_compression
= PropCompression_None
;
917 } else if (di
->n
> strlen(xapref
) && RtlCompareMemory(xapref
, di
->name
, strlen(xapref
)) == strlen(xapref
)) {
921 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &utf16len
, &di
->name
[strlen(xapref
)], di
->n
- (ULONG
)strlen(xapref
));
922 if (!NT_SUCCESS(Status
)) {
923 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
928 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
930 ERR("out of memory\n");
932 return STATUS_INSUFFICIENT_RESOURCES
;
935 RtlZeroMemory(dc
, sizeof(dir_child
));
937 dc
->utf8
.MaximumLength
= dc
->utf8
.Length
= di
->n
- (UINT16
)strlen(xapref
);
938 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dc
->utf8
.MaximumLength
, ALLOC_TAG
);
939 if (!dc
->utf8
.Buffer
) {
940 ERR("out of memory\n");
943 return STATUS_INSUFFICIENT_RESOURCES
;
946 RtlCopyMemory(dc
->utf8
.Buffer
, &di
->name
[strlen(xapref
)], dc
->utf8
.Length
);
948 dc
->name
.MaximumLength
= dc
->name
.Length
= (UINT16
)utf16len
;
949 dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dc
->name
.MaximumLength
, ALLOC_TAG
);
950 if (!dc
->name
.Buffer
) {
951 ERR("out of memory\n");
952 ExFreePool(dc
->utf8
.Buffer
);
955 return STATUS_INSUFFICIENT_RESOURCES
;
958 Status
= RtlUTF8ToUnicodeN(dc
->name
.Buffer
, utf16len
, &utf16len
, dc
->utf8
.Buffer
, dc
->utf8
.Length
);
959 if (!NT_SUCCESS(Status
)) {
960 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
961 ExFreePool(dc
->utf8
.Buffer
);
962 ExFreePool(dc
->name
.Buffer
);
968 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, &dc
->name
, TRUE
);
969 if (!NT_SUCCESS(Status
)) {
970 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
971 ExFreePool(dc
->utf8
.Buffer
);
972 ExFreePool(dc
->name
.Buffer
);
980 InsertTailList(&fcb
->dir_children_index
, &dc
->list_entry_index
);
984 xa
= ExAllocatePoolWithTag(PagedPool
, offsetof(xattr
, data
[0]) + di
->m
+ di
->n
, ALLOC_TAG
);
986 ERR("out of memory\n");
988 return STATUS_INSUFFICIENT_RESOURCES
;
992 xa
->valuelen
= di
->m
;
994 RtlCopyMemory(xa
->data
, di
->name
, di
->m
+ di
->n
);
996 InsertTailList(&fcb
->xattrs
, &xa
->list_entry
);
999 len
-= (ULONG
)offsetof(DIR_ITEM
, name
[0]) + di
->m
+ di
->n
;
1001 if (len
< offsetof(DIR_ITEM
, name
[0]))
1004 di
= (DIR_ITEM
*)&di
->name
[di
->m
+ di
->n
];
1006 } else if (tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
) {
1008 BOOL unique
= FALSE
;
1010 ed
= (EXTENT_DATA
*)tp
.item
->data
;
1012 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
1013 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
,
1014 tp
.item
->size
, sizeof(EXTENT_DATA
));
1017 return STATUS_INTERNAL_ERROR
;
1020 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
1021 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)&ed
->data
[0];
1023 if (tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
1024 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
,
1025 tp
.item
->size
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
));
1028 return STATUS_INTERNAL_ERROR
;
1031 if (ed2
->address
== 0 || ed2
->size
== 0) // sparse
1034 if (ed2
->size
!= 0 && is_tree_unique(Vcb
, tp
.tree
, Irp
))
1035 unique
= is_extent_unique(Vcb
, ed2
->address
, ed2
->size
, Irp
);
1038 ext
= ExAllocatePoolWithTag(pooltype
, offsetof(extent
, extent_data
) + tp
.item
->size
, ALLOC_TAG
);
1040 ERR("out of memory\n");
1042 return STATUS_INSUFFICIENT_RESOURCES
;
1045 ext
->offset
= tp
.item
->key
.offset
;
1046 RtlCopyMemory(&ext
->extent_data
, tp
.item
->data
, tp
.item
->size
);
1047 ext
->datalen
= tp
.item
->size
;
1048 ext
->unique
= unique
;
1049 ext
->ignore
= FALSE
;
1050 ext
->inserted
= FALSE
;
1053 InsertTailList(&fcb
->extents
, &ext
->list_entry
);
1057 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
1058 Status
= load_dir_children(Vcb
, fcb
, FALSE
, Irp
);
1059 if (!NT_SUCCESS(Status
)) {
1060 ERR("load_dir_children returned %08x\n", Status
);
1067 fcb
->Header
.AllocationSize
.QuadPart
= 0;
1068 fcb
->Header
.FileSize
.QuadPart
= 0;
1069 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
1071 if (ed
&& ed
->type
== EXTENT_TYPE_INLINE
)
1072 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->inode_item
.st_size
;
1074 fcb
->Header
.AllocationSize
.QuadPart
= sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
1076 fcb
->Header
.FileSize
.QuadPart
= fcb
->inode_item
.st_size
;
1077 fcb
->Header
.ValidDataLength
.QuadPart
= fcb
->inode_item
.st_size
;
1081 fcb
->atts
= get_file_attributes(Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->type
, utf8
&& utf8
->Buffer
[0] == '.', TRUE
, Irp
);
1084 fcb_get_sd(fcb
, parent
, FALSE
, Irp
);
1086 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
&& fcb
->reparse_xattr
.Length
== 0) {
1087 fcb
->atts
&= ~FILE_ATTRIBUTE_REPARSE_POINT
;
1089 if (!Vcb
->readonly
&& !is_subvol_readonly(subvol
, Irp
)) {
1090 fcb
->atts_changed
= TRUE
;
1091 mark_fcb_dirty(fcb
);
1096 InsertHeadList(lastle
, &fcb
->list_entry
);
1098 InsertTailList(&subvol
->fcbs
, &fcb
->list_entry
);
1100 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
1102 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
1105 return STATUS_SUCCESS
;
1108 static NTSTATUS
open_fcb_stream(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
1109 dir_child
* dc
, fcb
* parent
, fcb
** pfcb
, PIRP Irp
) {
1112 UINT16 xattrlen
, overhead
;
1116 static char xapref
[] = "user.";
1120 xattr
.Length
= (UINT16
)strlen(xapref
) + dc
->utf8
.Length
;
1121 xattr
.MaximumLength
= xattr
.Length
+ 1;
1122 xattr
.Buffer
= ExAllocatePoolWithTag(PagedPool
, xattr
.MaximumLength
, ALLOC_TAG
);
1123 if (!xattr
.Buffer
) {
1124 ERR("out of memory\n");
1125 return STATUS_INSUFFICIENT_RESOURCES
;
1128 RtlCopyMemory(xattr
.Buffer
, xapref
, strlen(xapref
));
1129 RtlCopyMemory(&xattr
.Buffer
[strlen(xapref
)], dc
->utf8
.Buffer
, dc
->utf8
.Length
);
1130 xattr
.Buffer
[xattr
.Length
] = 0;
1132 fcb
= create_fcb(Vcb
, PagedPool
);
1134 ERR("out of memory\n");
1135 ExFreePool(xattr
.Buffer
);
1136 return STATUS_INSUFFICIENT_RESOURCES
;
1141 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)xattr
.Buffer
, xattr
.Length
);
1143 if (!get_xattr(Vcb
, parent
->subvol
, parent
->inode
, xattr
.Buffer
, crc32
, &xattrdata
, &xattrlen
, Irp
)) {
1144 ERR("get_xattr failed\n");
1146 ExFreePool(xattr
.Buffer
);
1147 return STATUS_INTERNAL_ERROR
;
1150 fcb
->subvol
= parent
->subvol
;
1151 fcb
->inode
= parent
->inode
;
1152 fcb
->type
= parent
->type
;
1154 fcb
->adshash
= crc32
;
1155 fcb
->adsxattr
= xattr
;
1157 // find XATTR_ITEM overhead and hence calculate maximum length
1159 searchkey
.obj_id
= parent
->inode
;
1160 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
1161 searchkey
.offset
= crc32
;
1163 Status
= find_item(Vcb
, parent
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
1164 if (!NT_SUCCESS(Status
)) {
1165 ERR("find_item returned %08x\n", Status
);
1170 if (keycmp(tp
.item
->key
, searchkey
)) {
1171 ERR("error - could not find key for xattr\n");
1173 return STATUS_INTERNAL_ERROR
;
1176 if (tp
.item
->size
< xattrlen
) {
1177 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
);
1179 return STATUS_INTERNAL_ERROR
;
1182 overhead
= tp
.item
->size
- xattrlen
;
1184 fcb
->adsmaxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
) - overhead
;
1186 fcb
->adsdata
.Buffer
= (char*)xattrdata
;
1187 fcb
->adsdata
.Length
= fcb
->adsdata
.MaximumLength
= xattrlen
;
1189 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
1190 fcb
->Header
.AllocationSize
.QuadPart
= xattrlen
;
1191 fcb
->Header
.FileSize
.QuadPart
= xattrlen
;
1192 fcb
->Header
.ValidDataLength
.QuadPart
= xattrlen
;
1194 TRACE("stream found: size = %x, hash = %08x\n", xattrlen
, fcb
->adshash
);
1196 InsertHeadList(&parent
->list_entry
, &fcb
->list_entry
);
1198 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
1202 return STATUS_SUCCESS
;
1205 NTSTATUS
open_fileref_child(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) _In_ device_extension
* Vcb
,
1206 _In_ file_ref
* sf
, _In_ PUNICODE_STRING name
, _In_ BOOL case_sensitive
, _In_ BOOL lastpart
, _In_ BOOL streampart
,
1207 _In_ POOL_TYPE pooltype
, _Out_ file_ref
** psf2
, _In_opt_ PIRP Irp
) {
1211 if (sf
->fcb
== Vcb
->dummy_fcb
)
1212 return STATUS_OBJECT_NAME_NOT_FOUND
;
1215 BOOL locked
= FALSE
;
1217 UNICODE_STRING name_uc
;
1218 dir_child
* dc
= NULL
;
1221 if (!case_sensitive
) {
1222 Status
= RtlUpcaseUnicodeString(&name_uc
, name
, TRUE
);
1223 if (!NT_SUCCESS(Status
)) {
1224 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1229 if (!ExIsResourceAcquiredSharedLite(&sf
->fcb
->nonpaged
->dir_children_lock
)) {
1230 ExAcquireResourceSharedLite(&sf
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
1234 le
= sf
->fcb
->dir_children_index
.Flink
;
1235 while (le
!= &sf
->fcb
->dir_children_index
) {
1236 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
1238 if (dc2
->index
== 0) {
1239 if ((case_sensitive
&& dc2
->name
.Length
== name
->Length
&& RtlCompareMemory(dc2
->name
.Buffer
, name
->Buffer
, dc2
->name
.Length
) == dc2
->name
.Length
) ||
1240 (!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
)
1251 if (!case_sensitive
)
1252 ExFreePool(name_uc
.Buffer
);
1255 ExReleaseResourceLite(&sf
->fcb
->nonpaged
->dir_children_lock
);
1258 return STATUS_OBJECT_NAME_NOT_FOUND
;
1261 increase_fileref_refcount(dc
->fileref
);
1262 *psf2
= dc
->fileref
;
1263 return STATUS_SUCCESS
;
1266 Status
= open_fcb_stream(Vcb
, dc
, sf
->fcb
, &fcb
, Irp
);
1267 if (!NT_SUCCESS(Status
)) {
1268 ERR("open_fcb_stream returned %08x\n", Status
);
1272 sf2
= create_fileref(Vcb
);
1274 ERR("out of memory\n");
1276 return STATUS_INSUFFICIENT_RESOURCES
;
1281 sf2
->parent
= (struct _file_ref
*)sf
;
1286 ExAcquireResourceExclusiveLite(&sf
->nonpaged
->children_lock
, TRUE
);
1287 InsertTailList(&sf
->children
, &sf2
->list_entry
);
1288 ExReleaseResourceLite(&sf
->nonpaged
->children_lock
);
1290 increase_fileref_refcount(sf
);
1296 Status
= find_file_in_dir(name
, sf
->fcb
, &subvol
, &inode
, &dc
, case_sensitive
);
1297 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
1298 TRACE("could not find %.*S\n", name
->Length
/ sizeof(WCHAR
), name
->Buffer
);
1300 return lastpart
? STATUS_OBJECT_NAME_NOT_FOUND
: STATUS_OBJECT_PATH_NOT_FOUND
;
1301 } else if (!NT_SUCCESS(Status
)) {
1302 ERR("find_file_in_dir returned %08x\n", Status
);
1307 LARGE_INTEGER time1
, time2
;
1311 if (!lastpart
&& dc
->type
!= BTRFS_TYPE_DIRECTORY
) {
1312 TRACE("passed path including file as subdirectory\n");
1313 return STATUS_OBJECT_PATH_NOT_FOUND
;
1316 InterlockedIncrement(&dc
->fileref
->refcount
);
1317 *psf2
= dc
->fileref
;
1318 return STATUS_SUCCESS
;
1321 if (!subvol
|| (subvol
!= Vcb
->root_fileref
->fcb
->subvol
&& inode
== SUBVOL_ROOT_INODE
&& subvol
->parent
!= sf
->fcb
->subvol
->id
)) {
1322 fcb
= Vcb
->dummy_fcb
;
1323 InterlockedIncrement(&fcb
->refcount
);
1326 time1
= KeQueryPerformanceCounter(NULL
);
1328 Status
= open_fcb(Vcb
, subvol
, inode
, dc
->type
, &dc
->utf8
, sf
->fcb
, &fcb
, pooltype
, Irp
);
1330 time2
= KeQueryPerformanceCounter(NULL
);
1331 Vcb
->stats
.open_fcb_calls
++;
1332 Vcb
->stats
.open_fcb_time
+= time2
.QuadPart
- time1
.QuadPart
;
1335 if (!NT_SUCCESS(Status
)) {
1336 ERR("open_fcb returned %08x\n", Status
);
1341 if (dc
->type
!= BTRFS_TYPE_DIRECTORY
&& !lastpart
&& !(fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
)) {
1342 TRACE("passed path including file as subdirectory\n");
1344 return STATUS_OBJECT_PATH_NOT_FOUND
;
1347 sf2
= create_fileref(Vcb
);
1349 ERR("out of memory\n");
1351 return STATUS_INSUFFICIENT_RESOURCES
;
1356 if (dc
->type
== BTRFS_TYPE_DIRECTORY
)
1362 sf2
->parent
= (struct _file_ref
*)sf
;
1364 ExAcquireResourceExclusiveLite(&sf
->nonpaged
->children_lock
, TRUE
);
1365 InsertTailList(&sf
->children
, &sf2
->list_entry
);
1366 ExReleaseResourceLite(&sf
->nonpaged
->children_lock
);
1368 increase_fileref_refcount(sf
);
1374 return STATUS_SUCCESS
;
1377 NTSTATUS
open_fileref(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) _In_ device_extension
* Vcb
, _Out_ file_ref
** pfr
,
1378 _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
,
1379 _In_ BOOL case_sensitive
, _In_opt_ PIRP Irp
) {
1380 UNICODE_STRING fnus2
;
1381 file_ref
*dir
, *sf
, *sf2
;
1387 TRACE("(%p, %p, %p, %u, %p)\n", Vcb
, pfr
, related
, parent
, parsed
);
1390 if (!ExIsResourceAcquiredExclusiveLite(&Vcb
->fcb_lock
) && !ExIsResourceAcquiredExclusiveLite(&Vcb
->tree_lock
)) {
1391 ERR("fcb_lock not acquired exclusively\n");
1396 if (Vcb
->removing
|| Vcb
->locked
)
1397 return STATUS_ACCESS_DENIED
;
1401 if (fnus2
.Length
< sizeof(WCHAR
) && !related
) {
1402 ERR("error - fnus was too short\n");
1403 return STATUS_INTERNAL_ERROR
;
1406 if (related
&& fnus
->Length
== 0) {
1407 increase_fileref_refcount(related
);
1410 return STATUS_SUCCESS
;
1416 if (fnus2
.Buffer
[0] != '\\') {
1417 ERR("error - filename %.*S did not begin with \\\n", fnus2
.Length
/ sizeof(WCHAR
), fnus2
.Buffer
);
1418 return STATUS_OBJECT_PATH_NOT_FOUND
;
1421 // if path starts with two backslashes, ignore one of them
1422 if (fnus2
.Length
>= 2 * sizeof(WCHAR
) && fnus2
.Buffer
[1] == '\\') {
1424 fnus2
.Length
-= sizeof(WCHAR
);
1425 fnus2
.MaximumLength
-= sizeof(WCHAR
);
1428 if (fnus2
.Length
== sizeof(WCHAR
)) {
1429 if (Vcb
->root_fileref
->open_count
== 0 && !(Vcb
->Vpb
->Flags
& VPB_MOUNTED
)) // don't allow root to be opened on unmounted FS
1430 return STATUS_DEVICE_NOT_READY
;
1432 increase_fileref_refcount(Vcb
->root_fileref
);
1433 *pfr
= Vcb
->root_fileref
;
1438 return STATUS_SUCCESS
;
1441 dir
= Vcb
->root_fileref
;
1444 fnus2
.Length
-= sizeof(WCHAR
);
1445 fnus2
.MaximumLength
-= sizeof(WCHAR
);
1448 if (dir
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& (fnus
->Length
< sizeof(WCHAR
) || fnus
->Buffer
[0] != ':')) {
1449 WARN("passed related fileref which isn't a directory (%S) (fnus = %.*S)\n",
1450 file_desc_fileref(related
), fnus
->Length
/ sizeof(WCHAR
), fnus
->Buffer
);
1451 return STATUS_OBJECT_PATH_NOT_FOUND
;
1454 InitializeListHead(&parts
);
1456 if (fnus
->Length
!= 0 &&
1457 (fnus
->Length
!= wcslen(datastring
) * sizeof(WCHAR
) || RtlCompareMemory(fnus
->Buffer
, datastring
, wcslen(datastring
) * sizeof(WCHAR
)) != wcslen(datastring
) * sizeof(WCHAR
))) {
1458 Status
= split_path(Vcb
, &fnus2
, &parts
, &has_stream
);
1459 if (!NT_SUCCESS(Status
)) {
1460 ERR("split_path returned %08x\n", Status
);
1466 increase_fileref_refcount(dir
);
1468 if (parent
&& !IsListEmpty(&parts
)) {
1471 nb
= CONTAINING_RECORD(RemoveTailList(&parts
), name_bit
, list_entry
);
1474 if (has_stream
&& !IsListEmpty(&parts
)) {
1475 nb
= CONTAINING_RECORD(RemoveTailList(&parts
), name_bit
, list_entry
);
1482 if (IsListEmpty(&parts
)) {
1483 Status
= STATUS_SUCCESS
;
1494 name_bit
* nb
= CONTAINING_RECORD(le
, name_bit
, list_entry
);
1495 BOOL lastpart
= le
->Flink
== &parts
|| (has_stream
&& le
->Flink
->Flink
== &parts
);
1496 BOOL streampart
= has_stream
&& le
->Flink
== &parts
;
1498 LARGE_INTEGER time1
, time2
;
1502 time1
= KeQueryPerformanceCounter(NULL
);
1504 Status
= open_fileref_child(Vcb
, sf
, &nb
->us
, case_sensitive
, lastpart
, streampart
, pooltype
, &sf2
, Irp
);
1506 time2
= KeQueryPerformanceCounter(NULL
);
1507 Vcb
->stats
.open_fileref_child_calls
++;
1508 Vcb
->stats
.open_fileref_child_time
+= time2
.QuadPart
- time1
.QuadPart
;
1510 if (!NT_SUCCESS(Status
)) {
1511 if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
|| Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1512 TRACE("open_fileref_child returned %08x\n", Status
);
1514 ERR("open_fileref_child returned %08x\n", Status
);
1519 if (le
->Flink
== &parts
) { // last entry
1522 nb
= CONTAINING_RECORD(le
->Blink
, name_bit
, list_entry
);
1524 *fn_offset
= (ULONG
)(nb
->us
.Buffer
- fnus
->Buffer
);
1530 if (sf2
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
) {
1531 Status
= STATUS_REPARSE
;
1534 name_bit
* nb2
= CONTAINING_RECORD(le
->Flink
, name_bit
, list_entry
);
1536 *parsed
= (USHORT
)(nb2
->us
.Buffer
- fnus
->Buffer
- 1) * sizeof(WCHAR
);
1542 free_fileref(Vcb
, sf
);
1546 } while (le
!= &parts
);
1548 if (Status
!= STATUS_REPARSE
)
1549 Status
= STATUS_SUCCESS
;
1553 free_fileref(Vcb
, sf
);
1555 while (!IsListEmpty(&parts
)) {
1556 name_bit
* nb
= CONTAINING_RECORD(RemoveHeadList(&parts
), name_bit
, list_entry
);
1557 ExFreeToPagedLookasideList(&Vcb
->name_bit_lookaside
, nb
);
1561 TRACE("returning %08x\n", Status
);
1566 NTSTATUS
add_dir_child(fcb
* fcb
, UINT64 inode
, BOOL subvol
, PANSI_STRING utf8
, PUNICODE_STRING name
, UINT8 type
, dir_child
** pdc
) {
1570 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
1572 ERR("out of memory\n");
1573 return STATUS_INSUFFICIENT_RESOURCES
;
1576 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
->Length
, ALLOC_TAG
);
1577 if (!dc
->utf8
.Buffer
) {
1578 ERR("out of memory\n");
1580 return STATUS_INSUFFICIENT_RESOURCES
;
1583 dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, name
->Length
, ALLOC_TAG
);
1584 if (!dc
->name
.Buffer
) {
1585 ERR("out of memory\n");
1586 ExFreePool(dc
->utf8
.Buffer
);
1588 return STATUS_INSUFFICIENT_RESOURCES
;
1591 dc
->key
.obj_id
= inode
;
1592 dc
->key
.obj_type
= subvol
? TYPE_ROOT_ITEM
: TYPE_INODE_ITEM
;
1593 dc
->key
.offset
= subvol
? 0xffffffffffffffff : 0;
1597 dc
->utf8
.Length
= dc
->utf8
.MaximumLength
= utf8
->Length
;
1598 RtlCopyMemory(dc
->utf8
.Buffer
, utf8
->Buffer
, utf8
->Length
);
1600 dc
->name
.Length
= dc
->name
.MaximumLength
= name
->Length
;
1601 RtlCopyMemory(dc
->name
.Buffer
, name
->Buffer
, name
->Length
);
1603 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, name
, TRUE
);
1604 if (!NT_SUCCESS(Status
)) {
1605 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1606 ExFreePool(dc
->utf8
.Buffer
);
1607 ExFreePool(dc
->name
.Buffer
);
1612 dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name
.Buffer
, dc
->name
.Length
);
1613 dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name_uc
.Buffer
, dc
->name_uc
.Length
);
1615 ExAcquireResourceExclusiveLite(&fcb
->nonpaged
->dir_children_lock
, TRUE
);
1617 if (IsListEmpty(&fcb
->dir_children_index
))
1620 dir_child
* dc2
= CONTAINING_RECORD(fcb
->dir_children_index
.Blink
, dir_child
, list_entry_index
);
1622 dc
->index
= max(2, dc2
->index
+ 1);
1625 InsertTailList(&fcb
->dir_children_index
, &dc
->list_entry_index
);
1627 insert_dir_child_into_hash_lists(fcb
, dc
);
1629 ExReleaseResourceLite(&fcb
->nonpaged
->dir_children_lock
);
1633 return STATUS_SUCCESS
;
1636 UINT32
inherit_mode(fcb
* parfcb
, BOOL is_dir
) {
1642 mode
= parfcb
->inode_item
.st_mode
& ~S_IFDIR
;
1643 mode
&= ~S_ISVTX
; // clear sticky bit
1644 mode
&= ~S_ISUID
; // clear setuid bit
1647 mode
&= ~S_ISGID
; // if not directory, clear setgid bit
1652 static NTSTATUS
file_create2(_In_ PIRP Irp
, _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) _In_ device_extension
* Vcb
, _In_ PUNICODE_STRING fpus
,
1653 _In_ file_ref
* parfileref
, _In_ ULONG options
, _In_reads_bytes_opt_(ealen
) FILE_FULL_EA_INFORMATION
* ea
, _In_ ULONG ealen
,
1654 _Out_ file_ref
** pfr
, _In_ LIST_ENTRY
* rollback
) {
1663 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1664 POOL_TYPE pool_type
= IrpSp
->Flags
& SL_OPEN_PAGING_FILE
? NonPagedPool
: PagedPool
;
1669 #ifdef DEBUG_FCB_REFCOUNTS
1673 if (parfileref
->fcb
== Vcb
->dummy_fcb
)
1674 return STATUS_ACCESS_DENIED
;
1676 if (options
& FILE_DIRECTORY_FILE
&& IrpSp
->Parameters
.Create
.FileAttributes
& FILE_ATTRIBUTE_TEMPORARY
)
1677 return STATUS_INVALID_PARAMETER
;
1679 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, fpus
->Buffer
, fpus
->Length
);
1680 if (!NT_SUCCESS(Status
)) {
1681 ERR("RtlUnicodeToUTF8N returned %08x\n", Status
);
1685 utf8
= ExAllocatePoolWithTag(pool_type
, utf8len
+ 1, ALLOC_TAG
);
1687 ERR("out of memory\n");
1688 return STATUS_INSUFFICIENT_RESOURCES
;
1691 Status
= RtlUnicodeToUTF8N(utf8
, utf8len
, &utf8len
, fpus
->Buffer
, fpus
->Length
);
1692 if (!NT_SUCCESS(Status
)) {
1693 ERR("RtlUnicodeToUTF8N returned %08x\n", Status
);
1700 KeQuerySystemTime(&time
);
1701 win_time_to_unix(time
, &now
);
1703 TRACE("create file %.*S\n", fpus
->Length
/ sizeof(WCHAR
), fpus
->Buffer
);
1704 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1705 TRACE("parfileref->fcb->inode_item.st_size (inode %llx) was %llx\n", parfileref
->fcb
->inode
, parfileref
->fcb
->inode_item
.st_size
);
1706 parfileref
->fcb
->inode_item
.st_size
+= utf8len
* 2;
1707 TRACE("parfileref->fcb->inode_item.st_size (inode %llx) now %llx\n", parfileref
->fcb
->inode
, parfileref
->fcb
->inode_item
.st_size
);
1708 parfileref
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1709 parfileref
->fcb
->inode_item
.sequence
++;
1710 parfileref
->fcb
->inode_item
.st_ctime
= now
;
1711 parfileref
->fcb
->inode_item
.st_mtime
= now
;
1712 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1714 parfileref
->fcb
->inode_item_changed
= TRUE
;
1715 mark_fcb_dirty(parfileref
->fcb
);
1717 inode
= InterlockedIncrement64(&parfileref
->fcb
->subvol
->lastinode
);
1719 type
= options
& FILE_DIRECTORY_FILE
? BTRFS_TYPE_DIRECTORY
: BTRFS_TYPE_FILE
;
1721 // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
1723 TRACE("requested attributes = %x\n", IrpSp
->Parameters
.Create
.FileAttributes
);
1728 defda
|= FILE_ATTRIBUTE_HIDDEN
;
1730 if (options
& FILE_DIRECTORY_FILE
) {
1731 defda
|= FILE_ATTRIBUTE_DIRECTORY
;
1732 IrpSp
->Parameters
.Create
.FileAttributes
|= FILE_ATTRIBUTE_DIRECTORY
;
1734 IrpSp
->Parameters
.Create
.FileAttributes
&= ~FILE_ATTRIBUTE_DIRECTORY
;
1736 if (!(IrpSp
->Parameters
.Create
.FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)) {
1737 IrpSp
->Parameters
.Create
.FileAttributes
|= FILE_ATTRIBUTE_ARCHIVE
;
1738 defda
|= FILE_ATTRIBUTE_ARCHIVE
;
1741 TRACE("defda = %x\n", defda
);
1743 if (IrpSp
->Parameters
.Create
.FileAttributes
== FILE_ATTRIBUTE_NORMAL
)
1744 IrpSp
->Parameters
.Create
.FileAttributes
= defda
;
1746 fcb
= create_fcb(Vcb
, pool_type
);
1748 ERR("out of memory\n");
1751 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1752 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1753 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1755 return STATUS_INSUFFICIENT_RESOURCES
;
1760 if (IrpSp
->Flags
& SL_OPEN_PAGING_FILE
) {
1761 fcb
->Header
.Flags2
|= FSRTL_FLAG2_IS_PAGING_FILE
;
1762 Vcb
->disallow_dismount
= TRUE
;
1765 fcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
1766 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1767 fcb
->inode_item
.st_size
= 0;
1768 fcb
->inode_item
.st_blocks
= 0;
1769 fcb
->inode_item
.block_group
= 0;
1770 fcb
->inode_item
.st_nlink
= 1;
1771 fcb
->inode_item
.st_gid
= GID_NOBODY
; // FIXME?
1772 fcb
->inode_item
.st_mode
= inherit_mode(parfileref
->fcb
, type
== BTRFS_TYPE_DIRECTORY
); // use parent's permissions by default
1773 fcb
->inode_item
.st_rdev
= 0;
1774 fcb
->inode_item
.flags
= 0;
1775 fcb
->inode_item
.sequence
= 1;
1776 fcb
->inode_item
.st_atime
= now
;
1777 fcb
->inode_item
.st_ctime
= now
;
1778 fcb
->inode_item
.st_mtime
= now
;
1779 fcb
->inode_item
.otime
= now
;
1781 if (type
== BTRFS_TYPE_DIRECTORY
)
1782 fcb
->inode_item
.st_mode
|= S_IFDIR
;
1784 fcb
->inode_item
.st_mode
|= S_IFREG
;
1785 fcb
->inode_item
.st_mode
&= ~(S_IXUSR
| S_IXGRP
| S_IXOTH
); // remove executable bit if not directory
1788 if (IrpSp
->Flags
& SL_OPEN_PAGING_FILE
) {
1789 fcb
->inode_item
.flags
= BTRFS_INODE_NODATACOW
| BTRFS_INODE_NODATASUM
| BTRFS_INODE_NOCOMPRESS
;
1791 // inherit nodatacow flag from parent directory
1792 if (parfileref
->fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
) {
1793 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATACOW
;
1795 if (type
!= BTRFS_TYPE_DIRECTORY
)
1796 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATASUM
;
1799 if (parfileref
->fcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
1800 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
1803 fcb
->prop_compression
= parfileref
->fcb
->prop_compression
;
1804 fcb
->prop_compression_changed
= fcb
->prop_compression
!= PropCompression_None
;
1806 fcb
->inode_item_changed
= TRUE
;
1808 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
1809 fcb
->Header
.AllocationSize
.QuadPart
= 0;
1810 fcb
->Header
.FileSize
.QuadPart
= 0;
1811 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
1813 fcb
->atts
= IrpSp
->Parameters
.Create
.FileAttributes
& ~FILE_ATTRIBUTE_NORMAL
;
1814 fcb
->atts_changed
= fcb
->atts
!= defda
;
1816 #ifdef DEBUG_FCB_REFCOUNTS
1817 rc
= InterlockedIncrement(&parfileref
->fcb
->refcount
);
1818 WARN("fcb %p: refcount now %i (%S)\n", parfileref
->fcb
, rc
, file_desc_fileref(parfileref
));
1820 InterlockedIncrement(&parfileref
->fcb
->refcount
);
1822 fcb
->subvol
= parfileref
->fcb
->subvol
;
1825 fcb
->created
= TRUE
;
1826 fcb
->deleted
= TRUE
;
1828 mark_fcb_dirty(fcb
);
1830 Status
= fcb_get_new_sd(fcb
, parfileref
, IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
);
1832 if (!NT_SUCCESS(Status
)) {
1833 ERR("fcb_get_new_sd returned %08x\n", Status
);
1836 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1837 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1838 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1843 fcb
->sd_dirty
= TRUE
;
1845 if (ea
&& ealen
> 0) {
1846 FILE_FULL_EA_INFORMATION
* eainfo
;
1850 // capitalize EA names
1855 s
.Length
= s
.MaximumLength
= eainfo
->EaNameLength
;
1856 s
.Buffer
= eainfo
->EaName
;
1858 RtlUpperString(&s
, &s
);
1860 fcb
->ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
1862 if (eainfo
->NextEntryOffset
== 0)
1865 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
1868 fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(pool_type
, ealen
, ALLOC_TAG
);
1869 if (!fcb
->ea_xattr
.Buffer
) {
1870 ERR("out of memory\n");
1873 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1874 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1875 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1877 return STATUS_INSUFFICIENT_RESOURCES
;
1880 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= (UINT16
)ealen
;
1881 RtlCopyMemory(fcb
->ea_xattr
.Buffer
, ea
, fcb
->ea_xattr
.Length
);
1883 fcb
->ea_changed
= TRUE
;
1886 fileref
= create_fileref(Vcb
);
1888 ERR("out of memory\n");
1891 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1892 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1893 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1895 return STATUS_INSUFFICIENT_RESOURCES
;
1900 if (Irp
->Overlay
.AllocationSize
.QuadPart
> 0 && !write_fcb_compressed(fcb
)) {
1901 Status
= extend_file(fcb
, fileref
, Irp
->Overlay
.AllocationSize
.QuadPart
, TRUE
, NULL
, rollback
);
1903 if (!NT_SUCCESS(Status
)) {
1904 ERR("extend_file returned %08x\n", Status
);
1905 free_fileref(Vcb
, fileref
);
1907 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1908 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1909 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1915 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
1916 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
1917 if (!fcb
->hash_ptrs
) {
1918 ERR("out of memory\n");
1919 free_fileref(Vcb
, fileref
);
1921 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1922 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1923 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1925 return STATUS_INSUFFICIENT_RESOURCES
;
1928 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
1930 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
1931 if (!fcb
->hash_ptrs_uc
) {
1932 ERR("out of memory\n");
1933 free_fileref(Vcb
, fileref
);
1935 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1936 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1937 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1939 return STATUS_INSUFFICIENT_RESOURCES
;
1942 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
1945 fcb
->deleted
= FALSE
;
1947 fileref
->created
= TRUE
;
1948 mark_fileref_dirty(fileref
);
1950 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
1951 fcb
->subvol
->root_item
.ctime
= now
;
1953 fileref
->parent
= parfileref
;
1955 utf8as
.Buffer
= utf8
;
1956 utf8as
.Length
= utf8as
.MaximumLength
= (UINT16
)utf8len
;
1958 Status
= add_dir_child(fileref
->parent
->fcb
, fcb
->inode
, FALSE
, &utf8as
, fpus
, fcb
->type
, &dc
);
1959 if (!NT_SUCCESS(Status
))
1960 WARN("add_dir_child returned %08x\n", Status
);
1965 dc
->fileref
= fileref
;
1967 ExAcquireResourceExclusiveLite(&parfileref
->nonpaged
->children_lock
, TRUE
);
1968 InsertTailList(&parfileref
->children
, &fileref
->list_entry
);
1969 ExReleaseResourceLite(&parfileref
->nonpaged
->children_lock
);
1971 increase_fileref_refcount(parfileref
);
1973 InsertTailList(&fcb
->subvol
->fcbs
, &fcb
->list_entry
);
1974 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
1978 if (type
== BTRFS_TYPE_DIRECTORY
)
1979 fileref
->fcb
->fileref
= fileref
;
1981 TRACE("created new file %S in subvol %llx, inode %llx\n", file_desc_fileref(fileref
), fcb
->subvol
->id
, fcb
->inode
);
1983 return STATUS_SUCCESS
;
1986 static NTSTATUS
create_stream(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
1987 file_ref
** pfileref
, file_ref
** pparfileref
, PUNICODE_STRING fpus
, PUNICODE_STRING stream
, PIRP Irp
,
1988 ULONG options
, POOL_TYPE pool_type
, BOOL case_sensitive
, LIST_ENTRY
* rollback
) {
1989 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1990 file_ref
*fileref
, *newpar
, *parfileref
;
1992 static char xapref
[] = "user.";
1993 static WCHAR DOSATTRIB
[] = L
"DOSATTRIB";
1994 static WCHAR EA
[] = L
"EA";
1995 static WCHAR reparse
[] = L
"reparse";
1996 UINT16 xapreflen
= (UINT16
)strlen(xapref
);
1999 ULONG utf8len
, overhead
;
2004 ACCESS_MASK granted_access
;
2005 #ifdef DEBUG_FCB_REFCOUNTS
2009 TRACE("fpus = %.*S\n", fpus
->Length
/ sizeof(WCHAR
), fpus
->Buffer
);
2010 TRACE("stream = %.*S\n", stream
->Length
/ sizeof(WCHAR
), stream
->Buffer
);
2012 parfileref
= *pparfileref
;
2014 if (parfileref
->fcb
== Vcb
->dummy_fcb
)
2015 return STATUS_ACCESS_DENIED
;
2017 Status
= open_fileref(Vcb
, &newpar
, fpus
, parfileref
, FALSE
, NULL
, NULL
, PagedPool
, case_sensitive
, Irp
);
2019 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
2020 UNICODE_STRING fpus2
;
2022 if (!is_file_name_valid(fpus
, FALSE
))
2023 return STATUS_OBJECT_NAME_INVALID
;
2025 fpus2
.Length
= fpus2
.MaximumLength
= fpus
->Length
;
2026 fpus2
.Buffer
= ExAllocatePoolWithTag(pool_type
, fpus2
.MaximumLength
, ALLOC_TAG
);
2028 if (!fpus2
.Buffer
) {
2029 ERR("out of memory\n");
2030 return STATUS_INSUFFICIENT_RESOURCES
;
2033 RtlCopyMemory(fpus2
.Buffer
, fpus
->Buffer
, fpus2
.Length
);
2035 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2037 if (!SeAccessCheck(parfileref
->fcb
->sd
, &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
2038 TRUE
, options
& FILE_DIRECTORY_FILE
? FILE_ADD_SUBDIRECTORY
: FILE_ADD_FILE
, 0, NULL
,
2039 IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
2040 &granted_access
, &Status
)) {
2041 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2045 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2047 Status
= file_create2(Irp
, Vcb
, &fpus2
, parfileref
, options
, NULL
, 0, &newpar
, rollback
);
2049 if (!NT_SUCCESS(Status
)) {
2050 ERR("file_create2 returned %08x\n", Status
);
2051 ExFreePool(fpus2
.Buffer
);
2055 send_notification_fileref(newpar
, options
& FILE_DIRECTORY_FILE
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
2056 send_notification_fcb(newpar
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2057 } else if (!NT_SUCCESS(Status
)) {
2058 ERR("open_fileref returned %08x\n", Status
);
2062 parfileref
= newpar
;
2063 *pparfileref
= parfileref
;
2065 if (parfileref
->fcb
->type
!= BTRFS_TYPE_FILE
&& parfileref
->fcb
->type
!= BTRFS_TYPE_SYMLINK
&& parfileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
2066 WARN("parent not file, directory, or symlink\n");
2067 return STATUS_INVALID_PARAMETER
;
2070 if (options
& FILE_DIRECTORY_FILE
) {
2071 WARN("tried to create directory as stream\n");
2072 return STATUS_INVALID_PARAMETER
;
2075 if (parfileref
->fcb
->atts
& FILE_ATTRIBUTE_READONLY
)
2076 return STATUS_ACCESS_DENIED
;
2078 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2080 if (!SeAccessCheck(parfileref
->fcb
->sd
, &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
2081 TRUE
, FILE_WRITE_DATA
, 0, NULL
, IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
2082 &granted_access
, &Status
)) {
2083 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2087 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2089 if ((stream
->Length
== wcslen(DOSATTRIB
) * sizeof(WCHAR
) && RtlCompareMemory(stream
->Buffer
, DOSATTRIB
, stream
->Length
) == stream
->Length
) ||
2090 (stream
->Length
== wcslen(EA
) * sizeof(WCHAR
) && RtlCompareMemory(stream
->Buffer
, EA
, stream
->Length
) == stream
->Length
) ||
2091 (stream
->Length
== wcslen(reparse
) * sizeof(WCHAR
) && RtlCompareMemory(stream
->Buffer
, reparse
, stream
->Length
) == stream
->Length
)) {
2092 return STATUS_OBJECT_NAME_INVALID
;
2095 fcb
= create_fcb(Vcb
, pool_type
);
2097 ERR("out of memory\n");
2098 return STATUS_INSUFFICIENT_RESOURCES
;
2103 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
2104 fcb
->Header
.AllocationSize
.QuadPart
= 0;
2105 fcb
->Header
.FileSize
.QuadPart
= 0;
2106 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
2108 #ifdef DEBUG_FCB_REFCOUNTS
2109 rc
= InterlockedIncrement(&parfileref
->fcb
->refcount
);
2110 WARN("fcb %p: refcount now %i (%S)\n", parfileref
->fcb
, rc
, file_desc_fileref(parfileref
));
2112 InterlockedIncrement(&parfileref
->fcb
->refcount
);
2114 fcb
->subvol
= parfileref
->fcb
->subvol
;
2115 fcb
->inode
= parfileref
->fcb
->inode
;
2116 fcb
->type
= parfileref
->fcb
->type
;
2120 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, stream
->Buffer
, stream
->Length
);
2121 if (!NT_SUCCESS(Status
)) {
2122 ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status
);
2127 fcb
->adsxattr
.Length
= (UINT16
)utf8len
+ xapreflen
;
2128 fcb
->adsxattr
.MaximumLength
= fcb
->adsxattr
.Length
+ 1;
2129 fcb
->adsxattr
.Buffer
= ExAllocatePoolWithTag(pool_type
, fcb
->adsxattr
.MaximumLength
, ALLOC_TAG
);
2130 if (!fcb
->adsxattr
.Buffer
) {
2131 ERR("out of memory\n");
2133 return STATUS_INSUFFICIENT_RESOURCES
;
2136 RtlCopyMemory(fcb
->adsxattr
.Buffer
, xapref
, xapreflen
);
2138 Status
= RtlUnicodeToUTF8N(&fcb
->adsxattr
.Buffer
[xapreflen
], utf8len
, &utf8len
, stream
->Buffer
, stream
->Length
);
2139 if (!NT_SUCCESS(Status
)) {
2140 ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status
);
2145 fcb
->adsxattr
.Buffer
[fcb
->adsxattr
.Length
] = 0;
2147 TRACE("adsxattr = %s\n", fcb
->adsxattr
.Buffer
);
2149 fcb
->adshash
= calc_crc32c(0xfffffffe, (UINT8
*)fcb
->adsxattr
.Buffer
, fcb
->adsxattr
.Length
);
2150 TRACE("adshash = %08x\n", fcb
->adshash
);
2152 searchkey
.obj_id
= parfileref
->fcb
->inode
;
2153 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
2154 searchkey
.offset
= fcb
->adshash
;
2156 Status
= find_item(Vcb
, parfileref
->fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
2157 if (!NT_SUCCESS(Status
)) {
2158 ERR("find_item returned %08x\n", Status
);
2163 if (!keycmp(tp
.item
->key
, searchkey
))
2164 overhead
= tp
.item
->size
;
2168 fcb
->adsmaxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
) - (sizeof(DIR_ITEM
) - 1);
2170 if (utf8len
+ xapreflen
+ overhead
> fcb
->adsmaxlen
) {
2171 WARN("not enough room for new DIR_ITEM (%u + %u > %u)", utf8len
+ xapreflen
, overhead
, fcb
->adsmaxlen
);
2173 return STATUS_DISK_FULL
;
2175 fcb
->adsmaxlen
-= overhead
+ utf8len
+ xapreflen
;
2177 fileref
= create_fileref(Vcb
);
2179 ERR("out of memory\n");
2181 return STATUS_INSUFFICIENT_RESOURCES
;
2186 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
2188 ERR("out of memory\n");
2189 free_fileref(Vcb
, fileref
);
2190 return STATUS_INSUFFICIENT_RESOURCES
;
2193 RtlZeroMemory(dc
, sizeof(dir_child
));
2195 dc
->utf8
.MaximumLength
= dc
->utf8
.Length
= fcb
->adsxattr
.Length
- xapreflen
;
2196 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dc
->utf8
.MaximumLength
, ALLOC_TAG
);
2197 if (!dc
->utf8
.Buffer
) {
2198 ERR("out of memory\n");
2200 free_fileref(Vcb
, fileref
);
2201 return STATUS_INSUFFICIENT_RESOURCES
;
2204 RtlCopyMemory(dc
->utf8
.Buffer
, &fcb
->adsxattr
.Buffer
[xapreflen
], fcb
->adsxattr
.Length
- xapreflen
);
2206 dc
->name
.MaximumLength
= dc
->name
.Length
= stream
->Length
;
2207 dc
->name
.Buffer
= ExAllocatePoolWithTag(pool_type
, dc
->name
.MaximumLength
, ALLOC_TAG
);
2208 if (!dc
->name
.Buffer
) {
2209 ERR("out of memory\n");
2210 ExFreePool(dc
->utf8
.Buffer
);
2212 free_fileref(Vcb
, fileref
);
2213 return STATUS_INSUFFICIENT_RESOURCES
;
2216 RtlCopyMemory(dc
->name
.Buffer
, stream
->Buffer
, stream
->Length
);
2218 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, &dc
->name
, TRUE
);
2219 if (!NT_SUCCESS(Status
)) {
2220 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
2221 ExFreePool(dc
->utf8
.Buffer
);
2222 ExFreePool(dc
->name
.Buffer
);
2224 free_fileref(Vcb
, fileref
);
2228 dc
->fileref
= fileref
;
2231 InsertHeadList(&parfileref
->fcb
->dir_children_index
, &dc
->list_entry_index
);
2233 mark_fcb_dirty(fcb
);
2234 mark_fileref_dirty(fileref
);
2236 InsertHeadList(&parfileref
->fcb
->list_entry
, &fcb
->list_entry
); // insert in list after parent fcb
2237 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
2239 KeQuerySystemTime(&time
);
2240 win_time_to_unix(time
, &now
);
2242 parfileref
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2243 parfileref
->fcb
->inode_item
.sequence
++;
2244 parfileref
->fcb
->inode_item
.st_ctime
= now
;
2245 parfileref
->fcb
->inode_item_changed
= TRUE
;
2247 mark_fcb_dirty(parfileref
->fcb
);
2249 parfileref
->fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
2250 parfileref
->fcb
->subvol
->root_item
.ctime
= now
;
2252 fileref
->parent
= (struct _file_ref
*)parfileref
;
2254 ExAcquireResourceExclusiveLite(&parfileref
->nonpaged
->children_lock
, TRUE
);
2255 InsertTailList(&parfileref
->children
, &fileref
->list_entry
);
2256 ExReleaseResourceLite(&parfileref
->nonpaged
->children_lock
);
2258 increase_fileref_refcount(parfileref
);
2260 *pfileref
= fileref
;
2262 send_notification_fileref(parfileref
, FILE_NOTIFY_CHANGE_STREAM_NAME
, FILE_ACTION_ADDED_STREAM
, &fileref
->dc
->name
);
2264 return STATUS_SUCCESS
;
2267 // LXSS programs can be distinguished by the fact they have a NULL PEB.
2273 ZwQueryInformationProcess (
2274 _In_ HANDLE ProcessHandle
,
2275 _In_ PROCESSINFOCLASS ProcessInformationClass
,
2276 _Out_ PVOID ProcessInformation
,
2277 _In_ ULONG ProcessInformationLength
,
2278 _Out_opt_ PULONG ReturnLength
2281 static __inline BOOL
called_from_lxss() {
2283 PROCESS_BASIC_INFORMATION pbi
;
2286 Status
= ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation
, &pbi
, sizeof(pbi
), &retlen
);
2288 if (!NT_SUCCESS(Status
)) {
2289 ERR("ZwQueryInformationProcess returned %08x\n", Status
);
2293 return !pbi
.PebBaseAddress
;
2296 #define called_from_lxss() FALSE
2299 static NTSTATUS
file_create(PIRP Irp
, _Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
2300 PFILE_OBJECT FileObject
, file_ref
* related
, BOOL loaded_related
, PUNICODE_STRING fnus
, ULONG disposition
, ULONG options
, LIST_ENTRY
* rollback
) {
2302 file_ref
*fileref
, *parfileref
= NULL
;
2305 static WCHAR datasuf
[] = {':','$','D','A','T','A',0};
2306 UNICODE_STRING dsus
, fpus
, stream
;
2307 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2308 POOL_TYPE pool_type
= IrpSp
->Flags
& SL_OPEN_PAGING_FILE
? NonPagedPool
: PagedPool
;
2309 #ifdef DEBUG_FCB_REFCOUNTS
2313 TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp
, Vcb
, FileObject
, fnus
->Length
/ sizeof(WCHAR
), fnus
->Buffer
, disposition
, options
);
2316 return STATUS_MEDIA_WRITE_PROTECTED
;
2318 if (options
& FILE_DELETE_ON_CLOSE
&& IrpSp
->Parameters
.Create
.FileAttributes
& FILE_ATTRIBUTE_READONLY
)
2319 return STATUS_CANNOT_DELETE
;
2321 dsus
.Buffer
= datasuf
;
2322 dsus
.Length
= dsus
.MaximumLength
= (USHORT
)wcslen(datasuf
) * sizeof(WCHAR
);
2325 if (!loaded_related
) {
2326 Status
= open_fileref(Vcb
, &parfileref
, fnus
, related
, TRUE
, NULL
, NULL
, pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
2328 if (!NT_SUCCESS(Status
))
2331 parfileref
= related
;
2333 if (parfileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& (fnus
->Length
< sizeof(WCHAR
) || fnus
->Buffer
[0] != ':')) {
2334 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
2338 if (is_subvol_readonly(parfileref
->fcb
->subvol
, Irp
)) {
2339 Status
= STATUS_ACCESS_DENIED
;
2343 i
= (fnus
->Length
/ sizeof(WCHAR
))-1;
2344 while ((fnus
->Buffer
[i
] == '\\' || fnus
->Buffer
[i
] == '/') && i
> 0) { i
--; }
2348 while (i
> 0 && fnus
->Buffer
[i
-1] != '\\' && fnus
->Buffer
[i
-1] != '/') { i
--; }
2350 fpus
.MaximumLength
= (USHORT
)((j
- i
+ 2) * sizeof(WCHAR
));
2351 fpus
.Buffer
= ExAllocatePoolWithTag(pool_type
, fpus
.MaximumLength
, ALLOC_TAG
);
2353 ERR("out of memory\n");
2354 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2358 fpus
.Length
= (USHORT
)((j
- i
+ 1) * sizeof(WCHAR
));
2360 RtlCopyMemory(fpus
.Buffer
, &fnus
->Buffer
[i
], (j
- i
+ 1) * sizeof(WCHAR
));
2361 fpus
.Buffer
[j
- i
+ 1] = 0;
2363 if (fpus
.Length
> dsus
.Length
) { // check for :$DATA suffix
2366 lb
.Buffer
= &fpus
.Buffer
[(fpus
.Length
- dsus
.Length
)/sizeof(WCHAR
)];
2367 lb
.Length
= lb
.MaximumLength
= dsus
.Length
;
2369 TRACE("lb = %.*S\n", lb
.Length
/sizeof(WCHAR
), lb
.Buffer
);
2371 if (FsRtlAreNamesEqual(&dsus
, &lb
, TRUE
, NULL
)) {
2372 TRACE("ignoring :$DATA suffix\n");
2374 fpus
.Length
-= lb
.Length
;
2376 if (fpus
.Length
> sizeof(WCHAR
) && fpus
.Buffer
[(fpus
.Length
-1)/sizeof(WCHAR
)] == ':')
2377 fpus
.Length
-= sizeof(WCHAR
);
2379 TRACE("fpus = %.*S\n", fpus
.Length
/ sizeof(WCHAR
), fpus
.Buffer
);
2385 for (i
= 0; i
< fpus
.Length
/ sizeof(WCHAR
); i
++) {
2386 if (fpus
.Buffer
[i
] == ':') {
2387 stream
.Length
= (USHORT
)(fpus
.Length
- (i
* sizeof(WCHAR
)) - sizeof(WCHAR
));
2388 stream
.Buffer
= &fpus
.Buffer
[i
+1];
2390 fpus
.Length
= (USHORT
)(i
* sizeof(WCHAR
));
2395 if (stream
.Length
> 0) {
2396 Status
= create_stream(Vcb
, &fileref
, &parfileref
, &fpus
, &stream
, Irp
, options
, pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, rollback
);
2397 if (!NT_SUCCESS(Status
)) {
2398 ERR("create_stream returned %08x\n", Status
);
2402 IoSetShareAccess(IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
, IrpSp
->Parameters
.Create
.ShareAccess
,
2403 FileObject
, &fileref
->fcb
->share_access
);
2405 ACCESS_MASK granted_access
;
2407 if (!is_file_name_valid(&fpus
, FALSE
)) {
2408 Status
= STATUS_OBJECT_NAME_INVALID
;
2412 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2414 if (!SeAccessCheck(parfileref
->fcb
->sd
, &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
2415 TRUE
, options
& FILE_DIRECTORY_FILE
? FILE_ADD_SUBDIRECTORY
: FILE_ADD_FILE
, 0, NULL
,
2416 IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
2417 &granted_access
, &Status
)) {
2418 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2422 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2424 if (Irp
->AssociatedIrp
.SystemBuffer
&& IrpSp
->Parameters
.Create
.EaLength
> 0) {
2427 Status
= IoCheckEaBufferValidity(Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.Create
.EaLength
, &offset
);
2428 if (!NT_SUCCESS(Status
)) {
2429 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
2434 Status
= file_create2(Irp
, Vcb
, &fpus
, parfileref
, options
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.Create
.EaLength
,
2435 &fileref
, rollback
);
2437 if (!NT_SUCCESS(Status
)) {
2438 ERR("file_create2 returned %08x\n", Status
);
2442 IoSetShareAccess(IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
, IrpSp
->Parameters
.Create
.ShareAccess
, FileObject
, &fileref
->fcb
->share_access
);
2444 send_notification_fileref(fileref
, options
& FILE_DIRECTORY_FILE
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
2445 send_notification_fcb(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2448 FileObject
->FsContext
= fileref
->fcb
;
2450 ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ccb
), ALLOC_TAG
);
2452 ERR("out of memory\n");
2453 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2454 free_fileref(Vcb
, fileref
);
2458 RtlZeroMemory(ccb
, sizeof(*ccb
));
2460 ccb
->fileref
= fileref
;
2462 ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
2463 ccb
->NodeSize
= sizeof(*ccb
);
2464 ccb
->disposition
= disposition
;
2465 ccb
->options
= options
;
2466 ccb
->query_dir_offset
= 0;
2467 RtlInitUnicodeString(&ccb
->query_string
, NULL
);
2468 ccb
->has_wildcard
= FALSE
;
2469 ccb
->specific_file
= FALSE
;
2470 ccb
->access
= IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
;
2471 ccb
->case_sensitive
= IrpSp
->Flags
& SL_CASE_SENSITIVE
;
2472 ccb
->reserving
= FALSE
;
2473 ccb
->lxss
= called_from_lxss();
2475 #ifdef DEBUG_FCB_REFCOUNTS
2476 oc
= InterlockedIncrement(&fileref
->open_count
);
2477 ERR("fileref %p: open_count now %i\n", fileref
, oc
);
2479 InterlockedIncrement(&fileref
->open_count
);
2481 InterlockedIncrement(&Vcb
->open_files
);
2483 FileObject
->FsContext2
= ccb
;
2485 FileObject
->SectionObjectPointer
= &fileref
->fcb
->nonpaged
->segment_object
;
2491 ExFreePool(fpus
.Buffer
);
2494 if (parfileref
&& !loaded_related
)
2495 free_fileref(Vcb
, parfileref
);
2500 static __inline
void debug_create_options(ULONG RequestedOptions
) {
2501 if (RequestedOptions
!= 0) {
2502 ULONG options
= RequestedOptions
;
2504 TRACE("requested options:\n");
2506 if (options
& FILE_DIRECTORY_FILE
) {
2507 TRACE(" FILE_DIRECTORY_FILE\n");
2508 options
&= ~FILE_DIRECTORY_FILE
;
2511 if (options
& FILE_WRITE_THROUGH
) {
2512 TRACE(" FILE_WRITE_THROUGH\n");
2513 options
&= ~FILE_WRITE_THROUGH
;
2516 if (options
& FILE_SEQUENTIAL_ONLY
) {
2517 TRACE(" FILE_SEQUENTIAL_ONLY\n");
2518 options
&= ~FILE_SEQUENTIAL_ONLY
;
2521 if (options
& FILE_NO_INTERMEDIATE_BUFFERING
) {
2522 TRACE(" FILE_NO_INTERMEDIATE_BUFFERING\n");
2523 options
&= ~FILE_NO_INTERMEDIATE_BUFFERING
;
2526 if (options
& FILE_SYNCHRONOUS_IO_ALERT
) {
2527 TRACE(" FILE_SYNCHRONOUS_IO_ALERT\n");
2528 options
&= ~FILE_SYNCHRONOUS_IO_ALERT
;
2531 if (options
& FILE_SYNCHRONOUS_IO_NONALERT
) {
2532 TRACE(" FILE_SYNCHRONOUS_IO_NONALERT\n");
2533 options
&= ~FILE_SYNCHRONOUS_IO_NONALERT
;
2536 if (options
& FILE_NON_DIRECTORY_FILE
) {
2537 TRACE(" FILE_NON_DIRECTORY_FILE\n");
2538 options
&= ~FILE_NON_DIRECTORY_FILE
;
2541 if (options
& FILE_CREATE_TREE_CONNECTION
) {
2542 TRACE(" FILE_CREATE_TREE_CONNECTION\n");
2543 options
&= ~FILE_CREATE_TREE_CONNECTION
;
2546 if (options
& FILE_COMPLETE_IF_OPLOCKED
) {
2547 TRACE(" FILE_COMPLETE_IF_OPLOCKED\n");
2548 options
&= ~FILE_COMPLETE_IF_OPLOCKED
;
2551 if (options
& FILE_NO_EA_KNOWLEDGE
) {
2552 TRACE(" FILE_NO_EA_KNOWLEDGE\n");
2553 options
&= ~FILE_NO_EA_KNOWLEDGE
;
2556 if (options
& FILE_OPEN_REMOTE_INSTANCE
) {
2557 TRACE(" FILE_OPEN_REMOTE_INSTANCE\n");
2558 options
&= ~FILE_OPEN_REMOTE_INSTANCE
;
2561 if (options
& FILE_RANDOM_ACCESS
) {
2562 TRACE(" FILE_RANDOM_ACCESS\n");
2563 options
&= ~FILE_RANDOM_ACCESS
;
2566 if (options
& FILE_DELETE_ON_CLOSE
) {
2567 TRACE(" FILE_DELETE_ON_CLOSE\n");
2568 options
&= ~FILE_DELETE_ON_CLOSE
;
2571 if (options
& FILE_OPEN_BY_FILE_ID
) {
2572 TRACE(" FILE_OPEN_BY_FILE_ID\n");
2573 options
&= ~FILE_OPEN_BY_FILE_ID
;
2576 if (options
& FILE_OPEN_FOR_BACKUP_INTENT
) {
2577 TRACE(" FILE_OPEN_FOR_BACKUP_INTENT\n");
2578 options
&= ~FILE_OPEN_FOR_BACKUP_INTENT
;
2581 if (options
& FILE_NO_COMPRESSION
) {
2582 TRACE(" FILE_NO_COMPRESSION\n");
2583 options
&= ~FILE_NO_COMPRESSION
;
2586 #if NTDDI_VERSION >= NTDDI_WIN7
2587 if (options
& FILE_OPEN_REQUIRING_OPLOCK
) {
2588 TRACE(" FILE_OPEN_REQUIRING_OPLOCK\n");
2589 options
&= ~FILE_OPEN_REQUIRING_OPLOCK
;
2592 if (options
& FILE_DISALLOW_EXCLUSIVE
) {
2593 TRACE(" FILE_DISALLOW_EXCLUSIVE\n");
2594 options
&= ~FILE_DISALLOW_EXCLUSIVE
;
2598 if (options
& FILE_RESERVE_OPFILTER
) {
2599 TRACE(" FILE_RESERVE_OPFILTER\n");
2600 options
&= ~FILE_RESERVE_OPFILTER
;
2603 if (options
& FILE_OPEN_REPARSE_POINT
) {
2604 TRACE(" FILE_OPEN_REPARSE_POINT\n");
2605 options
&= ~FILE_OPEN_REPARSE_POINT
;
2608 if (options
& FILE_OPEN_NO_RECALL
) {
2609 TRACE(" FILE_OPEN_NO_RECALL\n");
2610 options
&= ~FILE_OPEN_NO_RECALL
;
2613 if (options
& FILE_OPEN_FOR_FREE_SPACE_QUERY
) {
2614 TRACE(" FILE_OPEN_FOR_FREE_SPACE_QUERY\n");
2615 options
&= ~FILE_OPEN_FOR_FREE_SPACE_QUERY
;
2619 TRACE(" unknown options: %x\n", options
);
2621 TRACE("requested options: (none)\n");
2625 static NTSTATUS
get_reparse_block(fcb
* fcb
, UINT8
** data
) {
2628 if (fcb
->type
== BTRFS_TYPE_FILE
|| fcb
->type
== BTRFS_TYPE_SYMLINK
) {
2629 ULONG size
, bytes_read
, i
;
2631 if (fcb
->type
== BTRFS_TYPE_FILE
&& fcb
->inode_item
.st_size
< sizeof(ULONG
)) {
2632 WARN("file was too short to be a reparse point\n");
2633 return STATUS_INVALID_PARAMETER
;
2636 // 0x10007 = 0xffff (maximum length of data buffer) + 8 bytes header
2637 size
= (ULONG
)min(0x10007, fcb
->inode_item
.st_size
);
2640 return STATUS_INVALID_PARAMETER
;
2642 *data
= ExAllocatePoolWithTag(PagedPool
, size
, ALLOC_TAG
);
2644 ERR("out of memory\n");
2645 return STATUS_INSUFFICIENT_RESOURCES
;
2648 Status
= read_file(fcb
, *data
, 0, size
, &bytes_read
, NULL
);
2649 if (!NT_SUCCESS(Status
)) {
2650 ERR("read_file_fcb returned %08x\n", Status
);
2655 if (fcb
->type
== BTRFS_TYPE_SYMLINK
) {
2656 ULONG stringlen
, reqlen
;
2657 UINT16 subnamelen
, printnamelen
;
2658 REPARSE_DATA_BUFFER
* rdb
;
2660 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, (char*)*data
, bytes_read
);
2661 if (!NT_SUCCESS(Status
)) {
2662 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
2667 subnamelen
= printnamelen
= (USHORT
)stringlen
;
2669 reqlen
= offsetof(REPARSE_DATA_BUFFER
, SymbolicLinkReparseBuffer
.PathBuffer
) + subnamelen
+ printnamelen
;
2671 rdb
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
2674 ERR("out of memory\n");
2676 return STATUS_INSUFFICIENT_RESOURCES
;
2679 rdb
->ReparseTag
= IO_REPARSE_TAG_SYMLINK
;
2680 rdb
->ReparseDataLength
= (USHORT
)(reqlen
- offsetof(REPARSE_DATA_BUFFER
, SymbolicLinkReparseBuffer
));
2683 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
= 0;
2684 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameLength
= subnamelen
;
2685 rdb
->SymbolicLinkReparseBuffer
.PrintNameOffset
= subnamelen
;
2686 rdb
->SymbolicLinkReparseBuffer
.PrintNameLength
= printnamelen
;
2687 rdb
->SymbolicLinkReparseBuffer
.Flags
= SYMLINK_FLAG_RELATIVE
;
2689 Status
= RtlUTF8ToUnicodeN(&rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)],
2690 stringlen
, &stringlen
, (char*)*data
, size
);
2692 if (!NT_SUCCESS(Status
)) {
2693 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
2699 for (i
= 0; i
< stringlen
/ sizeof(WCHAR
); i
++) {
2700 if (rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[(rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)) + i
] == '/')
2701 rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[(rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)) + i
] = '\\';
2704 RtlCopyMemory(&rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.PrintNameOffset
/ sizeof(WCHAR
)],
2705 &rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)],
2706 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameLength
);
2710 *data
= (UINT8
*)rdb
;
2712 Status
= FsRtlValidateReparsePointBuffer(bytes_read
, (REPARSE_DATA_BUFFER
*)*data
);
2713 if (!NT_SUCCESS(Status
)) {
2714 ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status
);
2719 } else if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
2720 if (!fcb
->reparse_xattr
.Buffer
|| fcb
->reparse_xattr
.Length
== 0)
2721 return STATUS_INTERNAL_ERROR
;
2723 if (fcb
->reparse_xattr
.Length
< sizeof(ULONG
)) {
2724 WARN("xattr was too short to be a reparse point\n");
2725 return STATUS_INTERNAL_ERROR
;
2728 Status
= FsRtlValidateReparsePointBuffer(fcb
->reparse_xattr
.Length
, (REPARSE_DATA_BUFFER
*)fcb
->reparse_xattr
.Buffer
);
2729 if (!NT_SUCCESS(Status
)) {
2730 ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status
);
2734 *data
= ExAllocatePoolWithTag(PagedPool
, fcb
->reparse_xattr
.Length
, ALLOC_TAG
);
2736 ERR("out of memory\n");
2737 return STATUS_INSUFFICIENT_RESOURCES
;
2740 RtlCopyMemory(*data
, fcb
->reparse_xattr
.Buffer
, fcb
->reparse_xattr
.Length
);
2743 return STATUS_SUCCESS
;
2746 static void fcb_load_csums(_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, fcb
* fcb
, PIRP Irp
) {
2750 if (fcb
->csum_loaded
)
2753 if (IsListEmpty(&fcb
->extents
) || fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)
2756 le
= fcb
->extents
.Flink
;
2757 while (le
!= &fcb
->extents
) {
2758 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2760 if (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
) {
2761 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)&ext
->extent_data
.data
[0];
2764 len
= (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
? ed2
->num_bytes
: ed2
->size
) / Vcb
->superblock
.sector_size
;
2766 ext
->csum
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)(len
* sizeof(UINT32
)), ALLOC_TAG
);
2768 ERR("out of memory\n");
2772 Status
= load_csum(Vcb
, ext
->csum
, ed2
->address
+ (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
? ed2
->offset
: 0), len
, Irp
);
2774 if (!NT_SUCCESS(Status
)) {
2775 ERR("load_csum returned %08x\n", Status
);
2784 fcb
->csum_loaded
= TRUE
;
2787 static NTSTATUS
open_file(PDEVICE_OBJECT DeviceObject
, _Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2788 PFILE_OBJECT FileObject
= NULL
;
2789 ULONG RequestedDisposition
;
2793 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2795 ULONG fn_offset
= 0;
2796 file_ref
*related
, *fileref
= NULL
;
2797 POOL_TYPE pool_type
= IrpSp
->Flags
& SL_OPEN_PAGING_FILE
? NonPagedPool
: PagedPool
;
2798 ACCESS_MASK granted_access
;
2799 BOOL loaded_related
= FALSE
;
2801 #ifdef DEBUG_FCB_REFCOUNTS
2805 LARGE_INTEGER time1
, time2
;
2806 UINT8 open_type
= 0;
2808 time1
= KeQueryPerformanceCounter(NULL
);
2811 Irp
->IoStatus
.Information
= 0;
2813 RequestedDisposition
= ((IrpSp
->Parameters
.Create
.Options
>> 24) & 0xff);
2814 options
= IrpSp
->Parameters
.Create
.Options
& FILE_VALID_OPTION_FLAGS
;
2816 if (options
& FILE_DIRECTORY_FILE
&& RequestedDisposition
== FILE_SUPERSEDE
) {
2817 WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
2818 return STATUS_INVALID_PARAMETER
;
2821 FileObject
= IrpSp
->FileObject
;
2824 ERR("FileObject was NULL\n");
2825 return STATUS_INVALID_PARAMETER
;
2828 if (FileObject
->RelatedFileObject
&& FileObject
->RelatedFileObject
->FsContext2
) {
2829 struct _ccb
* relatedccb
= FileObject
->RelatedFileObject
->FsContext2
;
2831 related
= relatedccb
->fileref
;
2835 debug_create_options(options
);
2837 switch (RequestedDisposition
) {
2838 case FILE_SUPERSEDE
:
2839 TRACE("requested disposition: FILE_SUPERSEDE\n");
2843 TRACE("requested disposition: FILE_CREATE\n");
2847 TRACE("requested disposition: FILE_OPEN\n");
2851 TRACE("requested disposition: FILE_OPEN_IF\n");
2854 case FILE_OVERWRITE
:
2855 TRACE("requested disposition: FILE_OVERWRITE\n");
2858 case FILE_OVERWRITE_IF
:
2859 TRACE("requested disposition: FILE_OVERWRITE_IF\n");
2863 ERR("unknown disposition: %x\n", RequestedDisposition
);
2864 Status
= STATUS_NOT_IMPLEMENTED
;
2868 fn
= FileObject
->FileName
;
2870 TRACE("(%.*S)\n", fn
.Length
/ sizeof(WCHAR
), fn
.Buffer
);
2871 TRACE("FileObject = %p\n", FileObject
);
2873 if (Vcb
->readonly
&& (RequestedDisposition
== FILE_SUPERSEDE
|| RequestedDisposition
== FILE_CREATE
|| RequestedDisposition
== FILE_OVERWRITE
)) {
2874 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
2878 acquire_fcb_lock_exclusive(Vcb
);
2880 if (options
& FILE_OPEN_BY_FILE_ID
) {
2881 if (fn
.Length
== sizeof(UINT64
) && related
&& RequestedDisposition
== FILE_OPEN
) {
2884 RtlCopyMemory(&inode
, fn
.Buffer
, sizeof(UINT64
));
2886 if (related
->fcb
== Vcb
->root_fileref
->fcb
&& inode
== 0)
2887 inode
= Vcb
->root_fileref
->fcb
->inode
;
2889 if (inode
== 0) { // we use 0 to mean the parent of a subvolume
2890 fileref
= related
->parent
;
2891 increase_fileref_refcount(fileref
);
2892 Status
= STATUS_SUCCESS
;
2894 Status
= open_fileref_by_inode(Vcb
, related
->fcb
->subvol
, inode
, &fileref
, Irp
);
2897 WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
2898 Status
= STATUS_NOT_IMPLEMENTED
;
2899 release_fcb_lock(Vcb
);
2903 if (related
&& fn
.Length
!= 0 && fn
.Buffer
[0] == '\\') {
2904 Status
= STATUS_INVALID_PARAMETER
;
2905 release_fcb_lock(Vcb
);
2909 if (!related
&& RequestedDisposition
!= FILE_OPEN
&& !(IrpSp
->Flags
& SL_OPEN_TARGET_DIRECTORY
)) {
2912 Status
= open_fileref(Vcb
, &related
, &fn
, NULL
, TRUE
, &parsed
, &fnoff
,
2913 pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
2915 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
2916 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
2917 else if (Status
== STATUS_REPARSE
)
2919 else if (NT_SUCCESS(Status
)) {
2920 fnoff
*= sizeof(WCHAR
);
2921 fnoff
+= (related
->dc
? related
->dc
->name
.Length
: 0) + sizeof(WCHAR
);
2923 if (related
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
) {
2924 Status
= STATUS_REPARSE
;
2926 parsed
= (USHORT
)fnoff
- sizeof(WCHAR
);
2928 fn
.Buffer
= &fn
.Buffer
[fnoff
/ sizeof(WCHAR
)];
2929 fn
.Length
-= (USHORT
)fnoff
;
2931 Status
= open_fileref(Vcb
, &fileref
, &fn
, related
, IrpSp
->Flags
& SL_OPEN_TARGET_DIRECTORY
, &parsed
, &fn_offset
,
2932 pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
2934 loaded_related
= TRUE
;
2939 Status
= open_fileref(Vcb
, &fileref
, &fn
, related
, IrpSp
->Flags
& SL_OPEN_TARGET_DIRECTORY
, &parsed
, &fn_offset
,
2940 pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
2944 if (Status
== STATUS_REPARSE
) {
2945 REPARSE_DATA_BUFFER
* data
;
2947 ExAcquireResourceSharedLite(fileref
->fcb
->Header
.Resource
, TRUE
);
2948 Status
= get_reparse_block(fileref
->fcb
, (UINT8
**)&data
);
2949 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2951 if (!NT_SUCCESS(Status
)) {
2952 ERR("get_reparse_block returned %08x\n", Status
);
2954 Status
= STATUS_SUCCESS
;
2956 Status
= STATUS_REPARSE
;
2957 RtlCopyMemory(&Irp
->IoStatus
.Information
, data
, sizeof(ULONG
));
2959 data
->Reserved
= FileObject
->FileName
.Length
- parsed
;
2961 Irp
->Tail
.Overlay
.AuxiliaryBuffer
= (void*)data
;
2963 free_fileref(Vcb
, fileref
);
2964 release_fcb_lock(Vcb
);
2970 if (NT_SUCCESS(Status
) && fileref
->deleted
)
2971 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
2973 if (NT_SUCCESS(Status
)) {
2974 if (RequestedDisposition
== FILE_CREATE
) {
2975 TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref
));
2976 Status
= STATUS_OBJECT_NAME_COLLISION
;
2978 free_fileref(Vcb
, fileref
);
2979 release_fcb_lock(Vcb
);
2983 } else if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
2984 if (RequestedDisposition
== FILE_OPEN
|| RequestedDisposition
== FILE_OVERWRITE
) {
2985 TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
2986 release_fcb_lock(Vcb
);
2989 } else if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
) {
2990 TRACE("open_fileref returned %08x\n", Status
);
2991 release_fcb_lock(Vcb
);
2994 ERR("open_fileref returned %08x\n", Status
);
2995 release_fcb_lock(Vcb
);
2999 if (NT_SUCCESS(Status
)) { // file already exists
3003 release_fcb_lock(Vcb
);
3005 if (RequestedDisposition
== FILE_SUPERSEDE
|| RequestedDisposition
== FILE_OVERWRITE
|| RequestedDisposition
== FILE_OVERWRITE_IF
) {
3011 if (fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
|| is_subvol_readonly(fileref
->fcb
->subvol
, Irp
)) {
3012 Status
= STATUS_ACCESS_DENIED
;
3014 acquire_fcb_lock_exclusive(Vcb
);
3015 free_fileref(Vcb
, fileref
);
3016 release_fcb_lock(Vcb
);
3021 if (Vcb
->readonly
) {
3022 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
3024 acquire_fcb_lock_exclusive(Vcb
);
3025 free_fileref(Vcb
, fileref
);
3026 release_fcb_lock(Vcb
);
3032 if (!MmCanFileBeTruncated(&fileref
->fcb
->nonpaged
->segment_object
, &zero
)) {
3033 Status
= STATUS_USER_MAPPED_FILE
;
3035 acquire_fcb_lock_exclusive(Vcb
);
3036 free_fileref(Vcb
, fileref
);
3037 release_fcb_lock(Vcb
);
3043 if (IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
!= 0) {
3044 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3046 if (!SeAccessCheck((fileref
->fcb
->ads
|| fileref
->fcb
== Vcb
->dummy_fcb
) ? fileref
->parent
->fcb
->sd
: fileref
->fcb
->sd
,
3047 &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
3048 TRUE
, IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
, 0, NULL
,
3049 IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
3050 &granted_access
, &Status
)) {
3051 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3052 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
3054 acquire_fcb_lock_exclusive(Vcb
);
3055 free_fileref(Vcb
, fileref
);
3056 release_fcb_lock(Vcb
);
3061 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3065 TRACE("deleted = %s\n", fileref
->deleted
? "TRUE" : "FALSE");
3069 if (sf
->delete_on_close
) {
3070 TRACE("could not open as deletion pending\n");
3071 Status
= STATUS_DELETE_PENDING
;
3073 acquire_fcb_lock_exclusive(Vcb
);
3074 free_fileref(Vcb
, fileref
);
3075 release_fcb_lock(Vcb
);
3082 readonly
= (!fileref
->fcb
->ads
&& fileref
->fcb
->atts
& FILE_ATTRIBUTE_READONLY
) || (fileref
->fcb
->ads
&& fileref
->parent
->fcb
->atts
& FILE_ATTRIBUTE_READONLY
) ||
3083 is_subvol_readonly(fileref
->fcb
->subvol
, Irp
) || fileref
->fcb
== Vcb
->dummy_fcb
|| Vcb
->readonly
;
3085 if (options
& FILE_DELETE_ON_CLOSE
&& (fileref
== Vcb
->root_fileref
|| readonly
)) {
3086 Status
= STATUS_CANNOT_DELETE
;
3088 acquire_fcb_lock_exclusive(Vcb
);
3089 free_fileref(Vcb
, fileref
);
3090 release_fcb_lock(Vcb
);
3096 ACCESS_MASK allowed
;
3098 allowed
= READ_CONTROL
| SYNCHRONIZE
| ACCESS_SYSTEM_SECURITY
| FILE_READ_DATA
|
3099 FILE_READ_EA
| FILE_READ_ATTRIBUTES
| FILE_EXECUTE
| FILE_LIST_DIRECTORY
|
3102 if (!Vcb
->readonly
&& (fileref
->fcb
== Vcb
->dummy_fcb
|| fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
))
3105 if (fileref
->fcb
!= Vcb
->dummy_fcb
&& !is_subvol_readonly(fileref
->fcb
->subvol
, Irp
) && !Vcb
->readonly
) {
3106 allowed
|= DELETE
| WRITE_OWNER
| WRITE_DAC
| FILE_WRITE_EA
| FILE_WRITE_ATTRIBUTES
;
3108 if (!fileref
->fcb
->ads
&& fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
3109 allowed
|= FILE_ADD_SUBDIRECTORY
| FILE_ADD_FILE
| FILE_DELETE_CHILD
;
3110 } else if (fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
&& is_subvol_readonly(fileref
->fcb
->subvol
, Irp
) && !Vcb
->readonly
) {
3111 // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
3113 allowed
|= FILE_WRITE_ATTRIBUTES
;
3116 if (IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
& MAXIMUM_ALLOWED
) {
3117 granted_access
&= allowed
;
3118 IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->PreviouslyGrantedAccess
&= allowed
;
3119 } else if (granted_access
& ~allowed
) {
3120 Status
= Vcb
->readonly
? STATUS_MEDIA_WRITE_PROTECTED
: STATUS_ACCESS_DENIED
;
3122 acquire_fcb_lock_exclusive(Vcb
);
3123 free_fileref(Vcb
, fileref
);
3124 release_fcb_lock(Vcb
);
3130 if ((fileref
->fcb
->type
== BTRFS_TYPE_SYMLINK
|| fileref
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
) && !(options
& FILE_OPEN_REPARSE_POINT
)) {
3131 REPARSE_DATA_BUFFER
* data
;
3133 /* How reparse points work from the point of view of the filesystem appears to
3134 * undocumented. When returning STATUS_REPARSE, MSDN encourages us to return
3135 * IO_REPARSE in Irp->IoStatus.Information, but that means we have to do our own
3136 * translation. If we instead return the reparse tag in Information, and store
3137 * a pointer to the reparse data buffer in Irp->Tail.Overlay.AuxiliaryBuffer,
3138 * IopSymlinkProcessReparse will do the translation for us. */
3140 Status
= get_reparse_block(fileref
->fcb
, (UINT8
**)&data
);
3141 if (!NT_SUCCESS(Status
)) {
3142 ERR("get_reparse_block returned %08x\n", Status
);
3143 Status
= STATUS_SUCCESS
;
3145 Status
= STATUS_REPARSE
;
3146 Irp
->IoStatus
.Information
= data
->ReparseTag
;
3148 if (fn
.Buffer
[(fn
.Length
/ sizeof(WCHAR
)) - 1] == '\\')
3149 data
->Reserved
= sizeof(WCHAR
);
3151 Irp
->Tail
.Overlay
.AuxiliaryBuffer
= (void*)data
;
3153 acquire_fcb_lock_exclusive(Vcb
);
3154 free_fileref(Vcb
, fileref
);
3155 release_fcb_lock(Vcb
);
3161 if (fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
&& !fileref
->fcb
->ads
) {
3162 if (options
& FILE_NON_DIRECTORY_FILE
&& !(fileref
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
)) {
3163 Status
= STATUS_FILE_IS_A_DIRECTORY
;
3165 acquire_fcb_lock_exclusive(Vcb
);
3166 free_fileref(Vcb
, fileref
);
3167 release_fcb_lock(Vcb
);
3171 } else if (options
& FILE_DIRECTORY_FILE
) {
3172 TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref
->fcb
->type
, file_desc_fileref(fileref
));
3173 Status
= STATUS_NOT_A_DIRECTORY
;
3175 acquire_fcb_lock_exclusive(Vcb
);
3176 free_fileref(Vcb
, fileref
);
3177 release_fcb_lock(Vcb
);
3182 if (fileref
->open_count
> 0) {
3183 Status
= IoCheckShareAccess(granted_access
, IrpSp
->Parameters
.Create
.ShareAccess
, FileObject
, &fileref
->fcb
->share_access
, FALSE
);
3185 if (!NT_SUCCESS(Status
)) {
3186 if (Status
== STATUS_SHARING_VIOLATION
)
3187 TRACE("IoCheckShareAccess failed, returning %08x\n", Status
);
3189 WARN("IoCheckShareAccess failed, returning %08x\n", Status
);
3191 acquire_fcb_lock_exclusive(Vcb
);
3192 free_fileref(Vcb
, fileref
);
3193 release_fcb_lock(Vcb
);
3198 IoUpdateShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3200 IoSetShareAccess(granted_access
, IrpSp
->Parameters
.Create
.ShareAccess
, FileObject
, &fileref
->fcb
->share_access
);
3202 if (granted_access
& FILE_WRITE_DATA
|| options
& FILE_DELETE_ON_CLOSE
) {
3203 if (!MmFlushImageSection(&fileref
->fcb
->nonpaged
->segment_object
, MmFlushForWrite
)) {
3204 Status
= (options
& FILE_DELETE_ON_CLOSE
) ? STATUS_CANNOT_DELETE
: STATUS_SHARING_VIOLATION
;
3206 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3208 acquire_fcb_lock_exclusive(Vcb
);
3209 free_fileref(Vcb
, fileref
);
3210 release_fcb_lock(Vcb
);
3216 if (RequestedDisposition
== FILE_OVERWRITE
|| RequestedDisposition
== FILE_OVERWRITE_IF
|| RequestedDisposition
== FILE_SUPERSEDE
) {
3217 ULONG defda
, oldatts
, filter
;
3221 if ((RequestedDisposition
== FILE_OVERWRITE
|| RequestedDisposition
== FILE_OVERWRITE_IF
) && readonly
) {
3222 WARN("cannot overwrite readonly file\n");
3223 Status
= STATUS_ACCESS_DENIED
;
3225 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3227 acquire_fcb_lock_exclusive(Vcb
);
3228 free_fileref(Vcb
, fileref
);
3229 release_fcb_lock(Vcb
);
3234 if (!fileref
->fcb
->ads
&& (IrpSp
->Parameters
.Create
.FileAttributes
& (FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
)) != ((fileref
->fcb
->atts
& (FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
)))) {
3235 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3237 acquire_fcb_lock_exclusive(Vcb
);
3238 free_fileref(Vcb
, fileref
);
3239 release_fcb_lock(Vcb
);
3241 Status
= STATUS_ACCESS_DENIED
;
3245 if (fileref
->fcb
->ads
) {
3246 Status
= stream_set_end_of_file_information(Vcb
, 0, fileref
->fcb
, fileref
, FALSE
);
3247 if (!NT_SUCCESS(Status
)) {
3248 ERR("stream_set_end_of_file_information returned %08x\n", Status
);
3250 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3252 acquire_fcb_lock_exclusive(Vcb
);
3253 free_fileref(Vcb
, fileref
);
3254 release_fcb_lock(Vcb
);
3259 Status
= truncate_file(fileref
->fcb
, 0, Irp
, rollback
);
3260 if (!NT_SUCCESS(Status
)) {
3261 ERR("truncate_file returned %08x\n", Status
);
3263 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3265 acquire_fcb_lock_exclusive(Vcb
);
3266 free_fileref(Vcb
, fileref
);
3267 release_fcb_lock(Vcb
);
3273 if (Irp
->Overlay
.AllocationSize
.QuadPart
> 0) {
3274 Status
= extend_file(fileref
->fcb
, fileref
, Irp
->Overlay
.AllocationSize
.QuadPart
, TRUE
, NULL
, rollback
);
3276 if (!NT_SUCCESS(Status
)) {
3277 ERR("extend_file returned %08x\n", Status
);
3279 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3281 acquire_fcb_lock_exclusive(Vcb
);
3282 free_fileref(Vcb
, fileref
);
3283 release_fcb_lock(Vcb
);
3289 if (!fileref
->fcb
->ads
) {
3292 if (Irp
->AssociatedIrp
.SystemBuffer
&& IrpSp
->Parameters
.Create
.EaLength
> 0) {
3294 FILE_FULL_EA_INFORMATION
* eainfo
;
3296 Status
= IoCheckEaBufferValidity(Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.Create
.EaLength
, &offset
);
3297 if (!NT_SUCCESS(Status
)) {
3298 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
3300 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3302 acquire_fcb_lock_exclusive(Vcb
);
3303 free_fileref(Vcb
, fileref
);
3304 release_fcb_lock(Vcb
);
3309 fileref
->fcb
->ealen
= 4;
3311 // capitalize EA name
3312 eainfo
= Irp
->AssociatedIrp
.SystemBuffer
;
3316 s
.Length
= s
.MaximumLength
= eainfo
->EaNameLength
;
3317 s
.Buffer
= eainfo
->EaName
;
3319 RtlUpperString(&s
, &s
);
3321 fileref
->fcb
->ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
3323 if (eainfo
->NextEntryOffset
== 0)
3326 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
3329 if (fileref
->fcb
->ea_xattr
.Buffer
)
3330 ExFreePool(fileref
->fcb
->ea_xattr
.Buffer
);
3332 fileref
->fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(pool_type
, IrpSp
->Parameters
.Create
.EaLength
, ALLOC_TAG
);
3333 if (!fileref
->fcb
->ea_xattr
.Buffer
) {
3334 ERR("out of memory\n");
3335 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3337 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3339 acquire_fcb_lock_exclusive(Vcb
);
3340 free_fileref(Vcb
, fileref
);
3341 release_fcb_lock(Vcb
);
3346 fileref
->fcb
->ea_xattr
.Length
= fileref
->fcb
->ea_xattr
.MaximumLength
= (USHORT
)IrpSp
->Parameters
.Create
.EaLength
;
3347 RtlCopyMemory(fileref
->fcb
->ea_xattr
.Buffer
, Irp
->AssociatedIrp
.SystemBuffer
, fileref
->fcb
->ea_xattr
.Length
);
3349 if (fileref
->fcb
->ea_xattr
.Length
> 0) {
3350 ExFreePool(fileref
->fcb
->ea_xattr
.Buffer
);
3351 fileref
->fcb
->ea_xattr
.Buffer
= NULL
;
3352 fileref
->fcb
->ea_xattr
.Length
= fileref
->fcb
->ea_xattr
.MaximumLength
= 0;
3354 fileref
->fcb
->ea_changed
= TRUE
;
3355 fileref
->fcb
->ealen
= 0;
3359 // remove streams and send notifications
3360 le
= fileref
->fcb
->dir_children_index
.Flink
;
3361 while (le
!= &fileref
->fcb
->dir_children_index
) {
3362 dir_child
* dc
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
3363 LIST_ENTRY
* le2
= le
->Flink
;
3365 if (dc
->index
== 0) {
3369 Status
= open_fileref_child(Vcb
, fileref
, &dc
->name
, TRUE
, TRUE
, TRUE
, PagedPool
, &fr2
, NULL
);
3370 if (!NT_SUCCESS(Status
))
3371 WARN("open_fileref_child returned %08x\n", Status
);
3375 send_notification_fcb(fileref
, FILE_NOTIFY_CHANGE_STREAM_NAME
, FILE_ACTION_REMOVED_STREAM
, &dc
->name
);
3377 Status
= delete_fileref(dc
->fileref
, NULL
, NULL
, rollback
);
3378 if (!NT_SUCCESS(Status
)) {
3379 ERR("delete_fileref returned %08x\n", Status
);
3381 acquire_fcb_lock_exclusive(Vcb
);
3382 free_fileref(Vcb
, fileref
);
3383 release_fcb_lock(Vcb
);
3395 KeQuerySystemTime(&time
);
3396 win_time_to_unix(time
, &now
);
3398 filter
= FILE_NOTIFY_CHANGE_SIZE
| FILE_NOTIFY_CHANGE_LAST_WRITE
;
3400 if (fileref
->fcb
->ads
) {
3401 fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
3402 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
3403 mark_fcb_dirty(fileref
->parent
->fcb
);
3405 send_notification_fcb(fileref
->parent
, filter
, FILE_ACTION_MODIFIED
, &fileref
->dc
->name
);
3407 mark_fcb_dirty(fileref
->fcb
);
3409 oldatts
= fileref
->fcb
->atts
;
3411 defda
= get_file_attributes(Vcb
, fileref
->fcb
->subvol
, fileref
->fcb
->inode
, fileref
->fcb
->type
,
3412 fileref
->dc
&& fileref
->dc
->name
.Length
>= sizeof(WCHAR
) && fileref
->dc
->name
.Buffer
[0] == '.', TRUE
, Irp
);
3414 if (RequestedDisposition
== FILE_SUPERSEDE
)
3415 fileref
->fcb
->atts
= IrpSp
->Parameters
.Create
.FileAttributes
| FILE_ATTRIBUTE_ARCHIVE
;
3417 fileref
->fcb
->atts
|= IrpSp
->Parameters
.Create
.FileAttributes
| FILE_ATTRIBUTE_ARCHIVE
;
3419 if (fileref
->fcb
->atts
!= oldatts
) {
3420 fileref
->fcb
->atts_changed
= TRUE
;
3421 fileref
->fcb
->atts_deleted
= IrpSp
->Parameters
.Create
.FileAttributes
== defda
;
3422 filter
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
3425 fileref
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
3426 fileref
->fcb
->inode_item
.sequence
++;
3427 fileref
->fcb
->inode_item
.st_ctime
= now
;
3428 fileref
->fcb
->inode_item
.st_mtime
= now
;
3429 fileref
->fcb
->inode_item_changed
= TRUE
;
3431 send_notification_fcb(fileref
, filter
, FILE_ACTION_MODIFIED
, NULL
);
3434 if (options
& FILE_NO_EA_KNOWLEDGE
&& fileref
->fcb
->ea_xattr
.Length
> 0) {
3435 FILE_FULL_EA_INFORMATION
* ffei
= (FILE_FULL_EA_INFORMATION
*)fileref
->fcb
->ea_xattr
.Buffer
;
3438 if (ffei
->Flags
& FILE_NEED_EA
) {
3439 WARN("returning STATUS_ACCESS_DENIED as no EA knowledge\n");
3440 Status
= STATUS_ACCESS_DENIED
;
3442 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3444 acquire_fcb_lock_exclusive(Vcb
);
3445 free_fileref(Vcb
, fileref
);
3446 release_fcb_lock(Vcb
);
3451 if (ffei
->NextEntryOffset
== 0)
3454 ffei
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ffei
) + ffei
->NextEntryOffset
);
3459 FileObject
->FsContext
= fileref
->fcb
;
3461 ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ccb
), ALLOC_TAG
);
3463 ERR("out of memory\n");
3464 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3466 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3468 acquire_fcb_lock_exclusive(Vcb
);
3469 free_fileref(Vcb
, fileref
);
3470 release_fcb_lock(Vcb
);
3475 RtlZeroMemory(ccb
, sizeof(*ccb
));
3477 ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
3478 ccb
->NodeSize
= sizeof(*ccb
);
3479 ccb
->disposition
= RequestedDisposition
;
3480 ccb
->options
= options
;
3481 ccb
->query_dir_offset
= 0;
3482 RtlInitUnicodeString(&ccb
->query_string
, NULL
);
3483 ccb
->has_wildcard
= FALSE
;
3484 ccb
->specific_file
= FALSE
;
3485 ccb
->access
= granted_access
;
3486 ccb
->case_sensitive
= IrpSp
->Flags
& SL_CASE_SENSITIVE
;
3487 ccb
->reserving
= FALSE
;
3488 ccb
->lxss
= called_from_lxss();
3490 ccb
->fileref
= fileref
;
3492 FileObject
->FsContext2
= ccb
;
3493 FileObject
->SectionObjectPointer
= &fileref
->fcb
->nonpaged
->segment_object
;
3495 if (NT_SUCCESS(Status
)) {
3496 switch (RequestedDisposition
) {
3497 case FILE_SUPERSEDE
:
3498 Irp
->IoStatus
.Information
= FILE_SUPERSEDED
;
3503 Irp
->IoStatus
.Information
= FILE_OPENED
;
3506 case FILE_OVERWRITE
:
3507 case FILE_OVERWRITE_IF
:
3508 Irp
->IoStatus
.Information
= FILE_OVERWRITTEN
;
3513 // Make sure paging files don't have any extents marked as being prealloc,
3514 // as this would mean we'd have to lock exclusively when writing.
3515 if (IrpSp
->Flags
& SL_OPEN_PAGING_FILE
) {
3517 BOOL changed
= FALSE
;
3519 ExAcquireResourceExclusiveLite(fileref
->fcb
->Header
.Resource
, TRUE
);
3521 le
= fileref
->fcb
->extents
.Flink
;
3523 while (le
!= &fileref
->fcb
->extents
) {
3524 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3526 if (ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) {
3527 ext
->extent_data
.type
= EXTENT_TYPE_REGULAR
;
3534 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
3537 fileref
->fcb
->extents_changed
= TRUE
;
3538 mark_fcb_dirty(fileref
->fcb
);
3541 fileref
->fcb
->Header
.Flags2
|= FSRTL_FLAG2_IS_PAGING_FILE
;
3542 Vcb
->disallow_dismount
= TRUE
;
3545 #ifdef DEBUG_FCB_REFCOUNTS
3546 oc
= InterlockedIncrement(&fileref
->open_count
);
3547 ERR("fileref %p: open_count now %i\n", fileref
, oc
);
3549 InterlockedIncrement(&fileref
->open_count
);
3551 InterlockedIncrement(&Vcb
->open_files
);
3556 Status
= file_create(Irp
, Vcb
, FileObject
, related
, loaded_related
, &fn
, RequestedDisposition
, options
, rollback
);
3557 release_fcb_lock(Vcb
);
3559 Irp
->IoStatus
.Information
= NT_SUCCESS(Status
) ? FILE_CREATED
: 0;
3562 if (NT_SUCCESS(Status
) && !(options
& FILE_NO_INTERMEDIATE_BUFFERING
))
3563 FileObject
->Flags
|= FO_CACHE_SUPPORTED
;
3566 if (loaded_related
) {
3567 acquire_fcb_lock_exclusive(Vcb
);
3568 free_fileref(Vcb
, related
);
3569 release_fcb_lock(Vcb
);
3572 if (Status
== STATUS_SUCCESS
) {
3575 IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->PreviouslyGrantedAccess
|= granted_access
;
3576 IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->RemainingDesiredAccess
&= ~(granted_access
| MAXIMUM_ALLOWED
);
3578 if (!FileObject
->Vpb
)
3579 FileObject
->Vpb
= DeviceObject
->Vpb
;
3581 fcb2
= FileObject
->FsContext
;
3584 struct _ccb
* ccb2
= FileObject
->FsContext2
;
3586 fcb2
= ccb2
->fileref
->parent
->fcb
;
3589 ExAcquireResourceExclusiveLite(fcb2
->Header
.Resource
, TRUE
);
3590 fcb_load_csums(Vcb
, fcb2
, Irp
);
3591 ExReleaseResourceLite(fcb2
->Header
.Resource
);
3592 } else if (Status
!= STATUS_REPARSE
&& Status
!= STATUS_OBJECT_NAME_NOT_FOUND
&& Status
!= STATUS_OBJECT_PATH_NOT_FOUND
)
3593 TRACE("returning %08x\n", Status
);
3596 time2
= KeQueryPerformanceCounter(NULL
);
3598 if (open_type
== 0) {
3599 Vcb
->stats
.open_total_time
+= time2
.QuadPart
- time1
.QuadPart
;
3600 Vcb
->stats
.num_opens
++;
3601 } else if (open_type
== 1) {
3602 Vcb
->stats
.overwrite_total_time
+= time2
.QuadPart
- time1
.QuadPart
;
3603 Vcb
->stats
.num_overwrites
++;
3604 } else if (open_type
== 2) {
3605 Vcb
->stats
.create_total_time
+= time2
.QuadPart
- time1
.QuadPart
;
3606 Vcb
->stats
.num_creates
++;
3613 static NTSTATUS
verify_vcb(device_extension
* Vcb
, PIRP Irp
) {
3616 BOOL need_verify
= FALSE
;
3618 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3620 le
= Vcb
->devices
.Flink
;
3621 while (le
!= &Vcb
->devices
) {
3622 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3624 if (dev
->devobj
&& dev
->removable
) {
3626 IO_STATUS_BLOCK iosb
;
3628 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
3630 if (IoIsErrorUserInduced(Status
)) {
3631 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status
);
3633 } else if (!NT_SUCCESS(Status
)) {
3634 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status
);
3636 } else if (iosb
.Information
< sizeof(ULONG
)) {
3637 ERR("iosb.Information was too short\n");
3638 Status
= STATUS_INTERNAL_ERROR
;
3639 } else if (cc
!= dev
->change_count
) {
3640 dev
->devobj
->Flags
|= DO_VERIFY_VOLUME
;
3648 Status
= STATUS_SUCCESS
;
3651 ExReleaseResourceLite(&Vcb
->tree_lock
);
3654 PDEVICE_OBJECT devobj
;
3656 devobj
= IoGetDeviceToVerify(Irp
->Tail
.Overlay
.Thread
);
3657 IoSetDeviceToVerify(Irp
->Tail
.Overlay
.Thread
, NULL
);
3660 devobj
= IoGetDeviceToVerify(PsGetCurrentThread());
3661 IoSetDeviceToVerify(PsGetCurrentThread(), NULL
);
3664 devobj
= Vcb
->Vpb
? Vcb
->Vpb
->RealDevice
: NULL
;
3667 Status
= IoVerifyVolume(devobj
, FALSE
);
3669 Status
= STATUS_VERIFY_REQUIRED
;
3675 static BOOL
has_manage_volume_privilege(ACCESS_STATE
* access_state
, KPROCESSOR_MODE processor_mode
) {
3676 PRIVILEGE_SET privset
;
3678 privset
.PrivilegeCount
= 1;
3679 privset
.Control
= PRIVILEGE_SET_ALL_NECESSARY
;
3680 privset
.Privilege
[0].Luid
= RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
);
3681 privset
.Privilege
[0].Attributes
= 0;
3683 return SePrivilegeCheck(&privset
, &access_state
->SubjectSecurityContext
, processor_mode
) ? TRUE
: FALSE
;
3686 _Dispatch_type_(IRP_MJ_CREATE
)
3687 _Function_class_(DRIVER_DISPATCH
)
3688 NTSTATUS
drv_create(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3690 PIO_STACK_LOCATION IrpSp
;
3691 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3692 BOOL top_level
, locked
= FALSE
;
3694 FsRtlEnterFileSystem();
3696 TRACE("create (flags = %x)\n", Irp
->Flags
);
3698 top_level
= is_top_level(Irp
);
3700 /* return success if just called for FS device object */
3701 if (DeviceObject
== master_devobj
) {
3702 TRACE("create called for FS device object\n");
3704 Irp
->IoStatus
.Information
= FILE_OPENED
;
3705 Status
= STATUS_SUCCESS
;
3708 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
3709 Status
= vol_create(DeviceObject
, Irp
);
3711 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
3712 Status
= STATUS_INVALID_PARAMETER
;
3716 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
)) {
3717 Status
= STATUS_DEVICE_NOT_READY
;
3721 if (Vcb
->removing
) {
3722 Status
= STATUS_ACCESS_DENIED
;
3726 Status
= verify_vcb(Vcb
, Irp
);
3727 if (!NT_SUCCESS(Status
)) {
3728 ERR("verify_vcb returned %08x\n", Status
);
3732 ExAcquireResourceSharedLite(&Vcb
->load_lock
, TRUE
);
3735 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3737 if (IrpSp
->Flags
!= 0) {
3738 UINT32 flags
= IrpSp
->Flags
;
3742 if (flags
& SL_CASE_SENSITIVE
) {
3743 TRACE("SL_CASE_SENSITIVE\n");
3744 flags
&= ~SL_CASE_SENSITIVE
;
3747 if (flags
& SL_FORCE_ACCESS_CHECK
) {
3748 TRACE("SL_FORCE_ACCESS_CHECK\n");
3749 flags
&= ~SL_FORCE_ACCESS_CHECK
;
3752 if (flags
& SL_OPEN_PAGING_FILE
) {
3753 TRACE("SL_OPEN_PAGING_FILE\n");
3754 flags
&= ~SL_OPEN_PAGING_FILE
;
3757 if (flags
& SL_OPEN_TARGET_DIRECTORY
) {
3758 TRACE("SL_OPEN_TARGET_DIRECTORY\n");
3759 flags
&= ~SL_OPEN_TARGET_DIRECTORY
;
3762 if (flags
& SL_STOP_ON_SYMLINK
) {
3763 TRACE("SL_STOP_ON_SYMLINK\n");
3764 flags
&= ~SL_STOP_ON_SYMLINK
;
3768 WARN("unknown flags: %x\n", flags
);
3770 TRACE("flags: (none)\n");
3773 if (!IrpSp
->FileObject
) {
3774 ERR("FileObject was NULL\n");
3775 Status
= STATUS_INVALID_PARAMETER
;
3779 if (IrpSp
->FileObject
->RelatedFileObject
) {
3780 fcb
* relatedfcb
= IrpSp
->FileObject
->RelatedFileObject
->FsContext
;
3782 if (relatedfcb
&& relatedfcb
->Vcb
!= Vcb
) {
3783 WARN("RelatedFileObject was for different device\n");
3784 Status
= STATUS_INVALID_PARAMETER
;
3790 if (IrpSp
->FileObject
->FileName
.Length
== 0 && !IrpSp
->FileObject
->RelatedFileObject
) {
3791 ULONG RequestedDisposition
= ((IrpSp
->Parameters
.Create
.Options
>> 24) & 0xff);
3792 ULONG RequestedOptions
= IrpSp
->Parameters
.Create
.Options
& FILE_VALID_OPTION_FLAGS
;
3793 #ifdef DEBUG_FCB_REFCOUNTS
3798 TRACE("open operation for volume\n");
3800 if (RequestedDisposition
!= FILE_OPEN
&& RequestedDisposition
!= FILE_OPEN_IF
) {
3801 Status
= STATUS_ACCESS_DENIED
;
3805 if (RequestedOptions
& FILE_DIRECTORY_FILE
) {
3806 Status
= STATUS_NOT_A_DIRECTORY
;
3810 ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ccb
), ALLOC_TAG
);
3812 ERR("out of memory\n");
3813 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3817 RtlZeroMemory(ccb
, sizeof(*ccb
));
3819 ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
3820 ccb
->NodeSize
= sizeof(*ccb
);
3821 ccb
->disposition
= RequestedDisposition
;
3822 ccb
->options
= RequestedOptions
;
3823 ccb
->access
= IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->PreviouslyGrantedAccess
;
3824 ccb
->manage_volume_privilege
= has_manage_volume_privilege(IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
,
3825 IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
);
3826 ccb
->reserving
= FALSE
;
3827 ccb
->lxss
= called_from_lxss();
3829 #ifdef DEBUG_FCB_REFCOUNTS
3830 rc
= InterlockedIncrement(&Vcb
->volume_fcb
->refcount
);
3831 WARN("fcb %p: refcount now %i (volume)\n", Vcb
->volume_fcb
, rc
);
3833 InterlockedIncrement(&Vcb
->volume_fcb
->refcount
);
3835 IrpSp
->FileObject
->FsContext
= Vcb
->volume_fcb
;
3836 IrpSp
->FileObject
->FsContext2
= ccb
;
3838 IrpSp
->FileObject
->SectionObjectPointer
= &Vcb
->volume_fcb
->nonpaged
->segment_object
;
3840 if (!IrpSp
->FileObject
->Vpb
)
3841 IrpSp
->FileObject
->Vpb
= DeviceObject
->Vpb
;
3843 InterlockedIncrement(&Vcb
->open_files
);
3845 Irp
->IoStatus
.Information
= FILE_OPENED
;
3846 Status
= STATUS_SUCCESS
;
3848 LIST_ENTRY rollback
;
3851 InitializeListHead(&rollback
);
3853 TRACE("file name: %.*S\n", IrpSp
->FileObject
->FileName
.Length
/ sizeof(WCHAR
), IrpSp
->FileObject
->FileName
.Buffer
);
3855 if (IrpSp
->FileObject
->RelatedFileObject
)
3856 TRACE("related file = %S\n", file_desc(IrpSp
->FileObject
->RelatedFileObject
));
3858 // Don't lock again if we're being called from within CcCopyRead etc.
3859 skip_lock
= ExIsResourceAcquiredExclusiveLite(&Vcb
->tree_lock
);
3862 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3864 Status
= open_file(DeviceObject
, Vcb
, Irp
, &rollback
);
3866 if (!NT_SUCCESS(Status
))
3867 do_rollback(Vcb
, &rollback
);
3869 clear_rollback(&rollback
);
3872 ExReleaseResourceLite(&Vcb
->tree_lock
);
3876 Irp
->IoStatus
.Status
= Status
;
3877 IoCompleteRequest( Irp
, NT_SUCCESS(Status
) ? IO_DISK_INCREMENT
: IO_NO_INCREMENT
);
3879 TRACE("create returning %08x\n", Status
);
3882 ExReleaseResourceLite(&Vcb
->load_lock
);
3885 IoSetTopLevelIrp(NULL
);
3887 FsRtlExitFileSystem();