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 (fnus2
.Length
== sizeof(WCHAR
)) {
1422 if (Vcb
->root_fileref
->open_count
== 0 && !(Vcb
->Vpb
->Flags
& VPB_MOUNTED
)) // don't allow root to be opened on unmounted FS
1423 return STATUS_DEVICE_NOT_READY
;
1425 increase_fileref_refcount(Vcb
->root_fileref
);
1426 *pfr
= Vcb
->root_fileref
;
1431 return STATUS_SUCCESS
;
1434 dir
= Vcb
->root_fileref
;
1437 fnus2
.Length
-= sizeof(WCHAR
);
1438 fnus2
.MaximumLength
-= sizeof(WCHAR
);
1441 if (dir
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& (fnus
->Length
< sizeof(WCHAR
) || fnus
->Buffer
[0] != ':')) {
1442 WARN("passed related fileref which isn't a directory (%S) (fnus = %.*S)\n",
1443 file_desc_fileref(related
), fnus
->Length
/ sizeof(WCHAR
), fnus
->Buffer
);
1444 return STATUS_OBJECT_PATH_NOT_FOUND
;
1447 InitializeListHead(&parts
);
1449 if (fnus
->Length
!= 0 &&
1450 (fnus
->Length
!= wcslen(datastring
) * sizeof(WCHAR
) || RtlCompareMemory(fnus
->Buffer
, datastring
, wcslen(datastring
) * sizeof(WCHAR
)) != wcslen(datastring
) * sizeof(WCHAR
))) {
1451 Status
= split_path(Vcb
, &fnus2
, &parts
, &has_stream
);
1452 if (!NT_SUCCESS(Status
)) {
1453 ERR("split_path returned %08x\n", Status
);
1459 increase_fileref_refcount(dir
);
1461 if (parent
&& !IsListEmpty(&parts
)) {
1464 nb
= CONTAINING_RECORD(RemoveTailList(&parts
), name_bit
, list_entry
);
1467 if (has_stream
&& !IsListEmpty(&parts
)) {
1468 nb
= CONTAINING_RECORD(RemoveTailList(&parts
), name_bit
, list_entry
);
1475 if (IsListEmpty(&parts
)) {
1476 Status
= STATUS_SUCCESS
;
1487 name_bit
* nb
= CONTAINING_RECORD(le
, name_bit
, list_entry
);
1488 BOOL lastpart
= le
->Flink
== &parts
|| (has_stream
&& le
->Flink
->Flink
== &parts
);
1489 BOOL streampart
= has_stream
&& le
->Flink
== &parts
;
1491 Status
= open_fileref_child(Vcb
, sf
, &nb
->us
, case_sensitive
, lastpart
, streampart
, pooltype
, &sf2
, Irp
);
1492 if (!NT_SUCCESS(Status
)) {
1493 if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
|| Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1494 TRACE("open_fileref_child returned %08x\n", Status
);
1496 ERR("open_fileref_child returned %08x\n", Status
);
1501 if (le
->Flink
== &parts
) { // last entry
1504 nb
= CONTAINING_RECORD(le
->Blink
, name_bit
, list_entry
);
1506 *fn_offset
= (ULONG
)(nb
->us
.Buffer
- fnus
->Buffer
);
1512 if (sf2
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
) {
1513 Status
= STATUS_REPARSE
;
1516 name_bit
* nb2
= CONTAINING_RECORD(le
->Flink
, name_bit
, list_entry
);
1518 *parsed
= (USHORT
)(nb2
->us
.Buffer
- fnus
->Buffer
- 1) * sizeof(WCHAR
);
1524 free_fileref(Vcb
, sf
);
1528 } while (le
!= &parts
);
1530 if (Status
!= STATUS_REPARSE
)
1531 Status
= STATUS_SUCCESS
;
1535 free_fileref(Vcb
, sf
);
1537 while (!IsListEmpty(&parts
)) {
1538 name_bit
* nb
= CONTAINING_RECORD(RemoveHeadList(&parts
), name_bit
, list_entry
);
1539 ExFreeToPagedLookasideList(&Vcb
->name_bit_lookaside
, nb
);
1543 TRACE("returning %08x\n", Status
);
1548 NTSTATUS
add_dir_child(fcb
* fcb
, UINT64 inode
, BOOL subvol
, PANSI_STRING utf8
, PUNICODE_STRING name
, UINT8 type
, dir_child
** pdc
) {
1552 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
1554 ERR("out of memory\n");
1555 return STATUS_INSUFFICIENT_RESOURCES
;
1558 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, utf8
->Length
, ALLOC_TAG
);
1559 if (!dc
->utf8
.Buffer
) {
1560 ERR("out of memory\n");
1562 return STATUS_INSUFFICIENT_RESOURCES
;
1565 dc
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, name
->Length
, ALLOC_TAG
);
1566 if (!dc
->name
.Buffer
) {
1567 ERR("out of memory\n");
1568 ExFreePool(dc
->utf8
.Buffer
);
1570 return STATUS_INSUFFICIENT_RESOURCES
;
1573 dc
->key
.obj_id
= inode
;
1574 dc
->key
.obj_type
= subvol
? TYPE_ROOT_ITEM
: TYPE_INODE_ITEM
;
1575 dc
->key
.offset
= subvol
? 0xffffffffffffffff : 0;
1579 dc
->utf8
.Length
= dc
->utf8
.MaximumLength
= utf8
->Length
;
1580 RtlCopyMemory(dc
->utf8
.Buffer
, utf8
->Buffer
, utf8
->Length
);
1582 dc
->name
.Length
= dc
->name
.MaximumLength
= name
->Length
;
1583 RtlCopyMemory(dc
->name
.Buffer
, name
->Buffer
, name
->Length
);
1585 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, name
, TRUE
);
1586 if (!NT_SUCCESS(Status
)) {
1587 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
1588 ExFreePool(dc
->utf8
.Buffer
);
1589 ExFreePool(dc
->name
.Buffer
);
1594 dc
->hash
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name
.Buffer
, dc
->name
.Length
);
1595 dc
->hash_uc
= calc_crc32c(0xffffffff, (UINT8
*)dc
->name_uc
.Buffer
, dc
->name_uc
.Length
);
1597 ExAcquireResourceExclusiveLite(&fcb
->nonpaged
->dir_children_lock
, TRUE
);
1599 if (IsListEmpty(&fcb
->dir_children_index
))
1602 dir_child
* dc2
= CONTAINING_RECORD(fcb
->dir_children_index
.Blink
, dir_child
, list_entry_index
);
1604 dc
->index
= max(2, dc2
->index
+ 1);
1607 InsertTailList(&fcb
->dir_children_index
, &dc
->list_entry_index
);
1609 insert_dir_child_into_hash_lists(fcb
, dc
);
1611 ExReleaseResourceLite(&fcb
->nonpaged
->dir_children_lock
);
1615 return STATUS_SUCCESS
;
1618 UINT32
inherit_mode(fcb
* parfcb
, BOOL is_dir
) {
1624 mode
= parfcb
->inode_item
.st_mode
& ~S_IFDIR
;
1625 mode
&= ~S_ISVTX
; // clear sticky bit
1626 mode
&= ~S_ISUID
; // clear setuid bit
1629 mode
&= ~S_ISGID
; // if not directory, clear setgid bit
1634 static NTSTATUS
file_create2(_In_ PIRP Irp
, _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) _In_ device_extension
* Vcb
, _In_ PUNICODE_STRING fpus
,
1635 _In_ file_ref
* parfileref
, _In_ ULONG options
, _In_reads_bytes_opt_(ealen
) FILE_FULL_EA_INFORMATION
* ea
, _In_ ULONG ealen
,
1636 _Out_ file_ref
** pfr
, _In_ LIST_ENTRY
* rollback
) {
1645 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1646 POOL_TYPE pool_type
= IrpSp
->Flags
& SL_OPEN_PAGING_FILE
? NonPagedPool
: PagedPool
;
1651 #ifdef DEBUG_FCB_REFCOUNTS
1655 if (parfileref
->fcb
== Vcb
->dummy_fcb
)
1656 return STATUS_ACCESS_DENIED
;
1658 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, fpus
->Buffer
, fpus
->Length
);
1659 if (!NT_SUCCESS(Status
)) {
1660 ERR("RtlUnicodeToUTF8N returned %08x\n", Status
);
1664 utf8
= ExAllocatePoolWithTag(pool_type
, utf8len
+ 1, ALLOC_TAG
);
1666 ERR("out of memory\n");
1667 return STATUS_INSUFFICIENT_RESOURCES
;
1670 Status
= RtlUnicodeToUTF8N(utf8
, utf8len
, &utf8len
, fpus
->Buffer
, fpus
->Length
);
1671 if (!NT_SUCCESS(Status
)) {
1672 ERR("RtlUnicodeToUTF8N returned %08x\n", Status
);
1679 KeQuerySystemTime(&time
);
1680 win_time_to_unix(time
, &now
);
1682 TRACE("create file %.*S\n", fpus
->Length
/ sizeof(WCHAR
), fpus
->Buffer
);
1683 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1684 TRACE("parfileref->fcb->inode_item.st_size (inode %llx) was %llx\n", parfileref
->fcb
->inode
, parfileref
->fcb
->inode_item
.st_size
);
1685 parfileref
->fcb
->inode_item
.st_size
+= utf8len
* 2;
1686 TRACE("parfileref->fcb->inode_item.st_size (inode %llx) now %llx\n", parfileref
->fcb
->inode
, parfileref
->fcb
->inode_item
.st_size
);
1687 parfileref
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1688 parfileref
->fcb
->inode_item
.sequence
++;
1689 parfileref
->fcb
->inode_item
.st_ctime
= now
;
1690 parfileref
->fcb
->inode_item
.st_mtime
= now
;
1691 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1693 parfileref
->fcb
->inode_item_changed
= TRUE
;
1694 mark_fcb_dirty(parfileref
->fcb
);
1696 inode
= InterlockedIncrement64(&parfileref
->fcb
->subvol
->lastinode
);
1698 type
= options
& FILE_DIRECTORY_FILE
? BTRFS_TYPE_DIRECTORY
: BTRFS_TYPE_FILE
;
1700 // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
1702 TRACE("requested attributes = %x\n", IrpSp
->Parameters
.Create
.FileAttributes
);
1704 IrpSp
->Parameters
.Create
.FileAttributes
|= FILE_ATTRIBUTE_ARCHIVE
;
1706 defda
= FILE_ATTRIBUTE_ARCHIVE
;
1709 defda
|= FILE_ATTRIBUTE_HIDDEN
;
1711 if (options
& FILE_DIRECTORY_FILE
) {
1712 defda
|= FILE_ATTRIBUTE_DIRECTORY
;
1713 IrpSp
->Parameters
.Create
.FileAttributes
|= FILE_ATTRIBUTE_DIRECTORY
;
1715 IrpSp
->Parameters
.Create
.FileAttributes
&= ~FILE_ATTRIBUTE_DIRECTORY
;
1717 TRACE("defda = %x\n", defda
);
1719 if (IrpSp
->Parameters
.Create
.FileAttributes
== FILE_ATTRIBUTE_NORMAL
)
1720 IrpSp
->Parameters
.Create
.FileAttributes
= defda
;
1722 fcb
= create_fcb(Vcb
, pool_type
);
1724 ERR("out of memory\n");
1727 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1728 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1729 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1731 return STATUS_INSUFFICIENT_RESOURCES
;
1736 if (IrpSp
->Flags
& SL_OPEN_PAGING_FILE
) {
1737 fcb
->Header
.Flags2
|= FSRTL_FLAG2_IS_PAGING_FILE
;
1738 Vcb
->disallow_dismount
= TRUE
;
1741 fcb
->inode_item
.generation
= Vcb
->superblock
.generation
;
1742 fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
1743 fcb
->inode_item
.st_size
= 0;
1744 fcb
->inode_item
.st_blocks
= 0;
1745 fcb
->inode_item
.block_group
= 0;
1746 fcb
->inode_item
.st_nlink
= 1;
1747 fcb
->inode_item
.st_gid
= GID_NOBODY
; // FIXME?
1748 fcb
->inode_item
.st_mode
= inherit_mode(parfileref
->fcb
, type
== BTRFS_TYPE_DIRECTORY
); // use parent's permissions by default
1749 fcb
->inode_item
.st_rdev
= 0;
1750 fcb
->inode_item
.flags
= 0;
1751 fcb
->inode_item
.sequence
= 1;
1752 fcb
->inode_item
.st_atime
= now
;
1753 fcb
->inode_item
.st_ctime
= now
;
1754 fcb
->inode_item
.st_mtime
= now
;
1755 fcb
->inode_item
.otime
= now
;
1757 if (type
== BTRFS_TYPE_DIRECTORY
)
1758 fcb
->inode_item
.st_mode
|= S_IFDIR
;
1760 fcb
->inode_item
.st_mode
|= S_IFREG
;
1761 fcb
->inode_item
.st_mode
&= ~(S_IXUSR
| S_IXGRP
| S_IXOTH
); // remove executable bit if not directory
1764 if (IrpSp
->Flags
& SL_OPEN_PAGING_FILE
) {
1765 fcb
->inode_item
.flags
= BTRFS_INODE_NODATACOW
| BTRFS_INODE_NODATASUM
| BTRFS_INODE_NOCOMPRESS
;
1767 // inherit nodatacow flag from parent directory
1768 if (parfileref
->fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
) {
1769 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATACOW
;
1771 if (type
!= BTRFS_TYPE_DIRECTORY
)
1772 fcb
->inode_item
.flags
|= BTRFS_INODE_NODATASUM
;
1775 if (parfileref
->fcb
->inode_item
.flags
& BTRFS_INODE_COMPRESS
)
1776 fcb
->inode_item
.flags
|= BTRFS_INODE_COMPRESS
;
1779 fcb
->prop_compression
= parfileref
->fcb
->prop_compression
;
1780 fcb
->prop_compression_changed
= fcb
->prop_compression
!= PropCompression_None
;
1782 fcb
->inode_item_changed
= TRUE
;
1784 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
1785 fcb
->Header
.AllocationSize
.QuadPart
= 0;
1786 fcb
->Header
.FileSize
.QuadPart
= 0;
1787 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
1789 fcb
->atts
= IrpSp
->Parameters
.Create
.FileAttributes
& ~FILE_ATTRIBUTE_NORMAL
;
1790 fcb
->atts_changed
= fcb
->atts
!= defda
;
1792 #ifdef DEBUG_FCB_REFCOUNTS
1793 rc
= InterlockedIncrement(&parfileref
->fcb
->refcount
);
1794 WARN("fcb %p: refcount now %i (%S)\n", parfileref
->fcb
, rc
, file_desc_fileref(parfileref
));
1796 InterlockedIncrement(&parfileref
->fcb
->refcount
);
1798 fcb
->subvol
= parfileref
->fcb
->subvol
;
1801 fcb
->created
= TRUE
;
1802 fcb
->deleted
= TRUE
;
1804 mark_fcb_dirty(fcb
);
1806 Status
= fcb_get_new_sd(fcb
, parfileref
, IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
);
1808 if (!NT_SUCCESS(Status
)) {
1809 ERR("fcb_get_new_sd returned %08x\n", Status
);
1812 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1813 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1814 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1819 fcb
->sd_dirty
= TRUE
;
1821 if (ea
&& ealen
> 0) {
1822 FILE_FULL_EA_INFORMATION
* eainfo
;
1826 // capitalize EA names
1831 s
.Length
= s
.MaximumLength
= eainfo
->EaNameLength
;
1832 s
.Buffer
= eainfo
->EaName
;
1834 RtlUpperString(&s
, &s
);
1836 fcb
->ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
1838 if (eainfo
->NextEntryOffset
== 0)
1841 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
1844 fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(pool_type
, ealen
, ALLOC_TAG
);
1845 if (!fcb
->ea_xattr
.Buffer
) {
1846 ERR("out of memory\n");
1849 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1850 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1851 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1853 return STATUS_INSUFFICIENT_RESOURCES
;
1856 fcb
->ea_xattr
.Length
= fcb
->ea_xattr
.MaximumLength
= (UINT16
)ealen
;
1857 RtlCopyMemory(fcb
->ea_xattr
.Buffer
, ea
, fcb
->ea_xattr
.Length
);
1859 fcb
->ea_changed
= TRUE
;
1862 fileref
= create_fileref(Vcb
);
1864 ERR("out of memory\n");
1867 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1868 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1869 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1871 return STATUS_INSUFFICIENT_RESOURCES
;
1876 if (Irp
->Overlay
.AllocationSize
.QuadPart
> 0 && !write_fcb_compressed(fcb
)) {
1877 Status
= extend_file(fcb
, fileref
, Irp
->Overlay
.AllocationSize
.QuadPart
, TRUE
, NULL
, rollback
);
1879 if (!NT_SUCCESS(Status
)) {
1880 ERR("extend_file returned %08x\n", Status
);
1881 free_fileref(Vcb
, fileref
);
1883 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1884 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1885 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1891 if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
1892 fcb
->hash_ptrs
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
1893 if (!fcb
->hash_ptrs
) {
1894 ERR("out of memory\n");
1895 free_fileref(Vcb
, fileref
);
1897 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1898 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1899 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1901 return STATUS_INSUFFICIENT_RESOURCES
;
1904 RtlZeroMemory(fcb
->hash_ptrs
, sizeof(LIST_ENTRY
*) * 256);
1906 fcb
->hash_ptrs_uc
= ExAllocatePoolWithTag(PagedPool
, sizeof(LIST_ENTRY
*) * 256, ALLOC_TAG
);
1907 if (!fcb
->hash_ptrs_uc
) {
1908 ERR("out of memory\n");
1909 free_fileref(Vcb
, fileref
);
1911 ExAcquireResourceExclusiveLite(parfileref
->fcb
->Header
.Resource
, TRUE
);
1912 parfileref
->fcb
->inode_item
.st_size
-= utf8len
* 2;
1913 ExReleaseResourceLite(parfileref
->fcb
->Header
.Resource
);
1915 return STATUS_INSUFFICIENT_RESOURCES
;
1918 RtlZeroMemory(fcb
->hash_ptrs_uc
, sizeof(LIST_ENTRY
*) * 256);
1921 fcb
->deleted
= FALSE
;
1923 fileref
->created
= TRUE
;
1924 mark_fileref_dirty(fileref
);
1926 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
1927 fcb
->subvol
->root_item
.ctime
= now
;
1929 fileref
->parent
= parfileref
;
1931 utf8as
.Buffer
= utf8
;
1932 utf8as
.Length
= utf8as
.MaximumLength
= (UINT16
)utf8len
;
1934 Status
= add_dir_child(fileref
->parent
->fcb
, fcb
->inode
, FALSE
, &utf8as
, fpus
, fcb
->type
, &dc
);
1935 if (!NT_SUCCESS(Status
))
1936 WARN("add_dir_child returned %08x\n", Status
);
1941 dc
->fileref
= fileref
;
1943 ExAcquireResourceExclusiveLite(&parfileref
->nonpaged
->children_lock
, TRUE
);
1944 InsertTailList(&parfileref
->children
, &fileref
->list_entry
);
1945 ExReleaseResourceLite(&parfileref
->nonpaged
->children_lock
);
1947 increase_fileref_refcount(parfileref
);
1949 InsertTailList(&fcb
->subvol
->fcbs
, &fcb
->list_entry
);
1950 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
1954 if (type
== BTRFS_TYPE_DIRECTORY
)
1955 fileref
->fcb
->fileref
= fileref
;
1957 TRACE("created new file %S in subvol %llx, inode %llx\n", file_desc_fileref(fileref
), fcb
->subvol
->id
, fcb
->inode
);
1959 return STATUS_SUCCESS
;
1962 static NTSTATUS
create_stream(_Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
1963 file_ref
** pfileref
, file_ref
** pparfileref
, PUNICODE_STRING fpus
, PUNICODE_STRING stream
, PIRP Irp
,
1964 ULONG options
, POOL_TYPE pool_type
, BOOL case_sensitive
, LIST_ENTRY
* rollback
) {
1965 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1966 file_ref
*fileref
, *newpar
, *parfileref
;
1968 static char xapref
[] = "user.";
1969 static WCHAR DOSATTRIB
[] = L
"DOSATTRIB";
1970 static WCHAR EA
[] = L
"EA";
1971 static WCHAR reparse
[] = L
"reparse";
1972 UINT16 xapreflen
= (UINT16
)strlen(xapref
);
1975 ULONG utf8len
, overhead
;
1980 ACCESS_MASK granted_access
;
1981 #ifdef DEBUG_FCB_REFCOUNTS
1985 TRACE("fpus = %.*S\n", fpus
->Length
/ sizeof(WCHAR
), fpus
->Buffer
);
1986 TRACE("stream = %.*S\n", stream
->Length
/ sizeof(WCHAR
), stream
->Buffer
);
1988 parfileref
= *pparfileref
;
1990 if (parfileref
->fcb
== Vcb
->dummy_fcb
)
1991 return STATUS_ACCESS_DENIED
;
1993 Status
= open_fileref(Vcb
, &newpar
, fpus
, parfileref
, FALSE
, NULL
, NULL
, PagedPool
, case_sensitive
, Irp
);
1995 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
1996 UNICODE_STRING fpus2
;
1998 if (!is_file_name_valid(fpus
, FALSE
))
1999 return STATUS_OBJECT_NAME_INVALID
;
2001 fpus2
.Length
= fpus2
.MaximumLength
= fpus
->Length
;
2002 fpus2
.Buffer
= ExAllocatePoolWithTag(pool_type
, fpus2
.MaximumLength
, ALLOC_TAG
);
2004 if (!fpus2
.Buffer
) {
2005 ERR("out of memory\n");
2006 return STATUS_INSUFFICIENT_RESOURCES
;
2009 RtlCopyMemory(fpus2
.Buffer
, fpus
->Buffer
, fpus2
.Length
);
2011 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2013 if (!SeAccessCheck(parfileref
->fcb
->sd
, &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
2014 TRUE
, options
& FILE_DIRECTORY_FILE
? FILE_ADD_SUBDIRECTORY
: FILE_ADD_FILE
, 0, NULL
,
2015 IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
2016 &granted_access
, &Status
)) {
2017 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2021 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2023 Status
= file_create2(Irp
, Vcb
, &fpus2
, parfileref
, options
, NULL
, 0, &newpar
, rollback
);
2025 if (!NT_SUCCESS(Status
)) {
2026 ERR("file_create2 returned %08x\n", Status
);
2027 ExFreePool(fpus2
.Buffer
);
2031 send_notification_fileref(newpar
, options
& FILE_DIRECTORY_FILE
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
2032 send_notification_fcb(newpar
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2033 } else if (!NT_SUCCESS(Status
)) {
2034 ERR("open_fileref returned %08x\n", Status
);
2038 parfileref
= newpar
;
2039 *pparfileref
= parfileref
;
2041 if (parfileref
->fcb
->type
!= BTRFS_TYPE_FILE
&& parfileref
->fcb
->type
!= BTRFS_TYPE_SYMLINK
&& parfileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
2042 WARN("parent not file, directory, or symlink\n");
2043 return STATUS_INVALID_PARAMETER
;
2046 if (options
& FILE_DIRECTORY_FILE
) {
2047 WARN("tried to create directory as stream\n");
2048 return STATUS_INVALID_PARAMETER
;
2051 if (parfileref
->fcb
->atts
& FILE_ATTRIBUTE_READONLY
)
2052 return STATUS_ACCESS_DENIED
;
2054 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2056 if (!SeAccessCheck(parfileref
->fcb
->sd
, &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
2057 TRUE
, FILE_WRITE_DATA
, 0, NULL
, IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
2058 &granted_access
, &Status
)) {
2059 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2063 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2065 if ((stream
->Length
== wcslen(DOSATTRIB
) * sizeof(WCHAR
) && RtlCompareMemory(stream
->Buffer
, DOSATTRIB
, stream
->Length
) == stream
->Length
) ||
2066 (stream
->Length
== wcslen(EA
) * sizeof(WCHAR
) && RtlCompareMemory(stream
->Buffer
, EA
, stream
->Length
) == stream
->Length
) ||
2067 (stream
->Length
== wcslen(reparse
) * sizeof(WCHAR
) && RtlCompareMemory(stream
->Buffer
, reparse
, stream
->Length
) == stream
->Length
)) {
2068 return STATUS_OBJECT_NAME_INVALID
;
2071 fcb
= create_fcb(Vcb
, pool_type
);
2073 ERR("out of memory\n");
2074 return STATUS_INSUFFICIENT_RESOURCES
;
2079 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
2080 fcb
->Header
.AllocationSize
.QuadPart
= 0;
2081 fcb
->Header
.FileSize
.QuadPart
= 0;
2082 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
2084 #ifdef DEBUG_FCB_REFCOUNTS
2085 rc
= InterlockedIncrement(&parfileref
->fcb
->refcount
);
2086 WARN("fcb %p: refcount now %i (%S)\n", parfileref
->fcb
, rc
, file_desc_fileref(parfileref
));
2088 InterlockedIncrement(&parfileref
->fcb
->refcount
);
2090 fcb
->subvol
= parfileref
->fcb
->subvol
;
2091 fcb
->inode
= parfileref
->fcb
->inode
;
2092 fcb
->type
= parfileref
->fcb
->type
;
2096 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, stream
->Buffer
, stream
->Length
);
2097 if (!NT_SUCCESS(Status
)) {
2098 ERR("RtlUnicodeToUTF8N 1 returned %08x\n", Status
);
2103 fcb
->adsxattr
.Length
= (UINT16
)utf8len
+ xapreflen
;
2104 fcb
->adsxattr
.MaximumLength
= fcb
->adsxattr
.Length
+ 1;
2105 fcb
->adsxattr
.Buffer
= ExAllocatePoolWithTag(pool_type
, fcb
->adsxattr
.MaximumLength
, ALLOC_TAG
);
2106 if (!fcb
->adsxattr
.Buffer
) {
2107 ERR("out of memory\n");
2109 return STATUS_INSUFFICIENT_RESOURCES
;
2112 RtlCopyMemory(fcb
->adsxattr
.Buffer
, xapref
, xapreflen
);
2114 Status
= RtlUnicodeToUTF8N(&fcb
->adsxattr
.Buffer
[xapreflen
], utf8len
, &utf8len
, stream
->Buffer
, stream
->Length
);
2115 if (!NT_SUCCESS(Status
)) {
2116 ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status
);
2121 fcb
->adsxattr
.Buffer
[fcb
->adsxattr
.Length
] = 0;
2123 TRACE("adsxattr = %s\n", fcb
->adsxattr
.Buffer
);
2125 fcb
->adshash
= calc_crc32c(0xfffffffe, (UINT8
*)fcb
->adsxattr
.Buffer
, fcb
->adsxattr
.Length
);
2126 TRACE("adshash = %08x\n", fcb
->adshash
);
2128 searchkey
.obj_id
= parfileref
->fcb
->inode
;
2129 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
2130 searchkey
.offset
= fcb
->adshash
;
2132 Status
= find_item(Vcb
, parfileref
->fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
2133 if (!NT_SUCCESS(Status
)) {
2134 ERR("find_item returned %08x\n", Status
);
2139 if (!keycmp(tp
.item
->key
, searchkey
))
2140 overhead
= tp
.item
->size
;
2144 fcb
->adsmaxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
) - (sizeof(DIR_ITEM
) - 1);
2146 if (utf8len
+ xapreflen
+ overhead
> fcb
->adsmaxlen
) {
2147 WARN("not enough room for new DIR_ITEM (%u + %u > %u)", utf8len
+ xapreflen
, overhead
, fcb
->adsmaxlen
);
2149 return STATUS_DISK_FULL
;
2151 fcb
->adsmaxlen
-= overhead
+ utf8len
+ xapreflen
;
2153 fileref
= create_fileref(Vcb
);
2155 ERR("out of memory\n");
2157 return STATUS_INSUFFICIENT_RESOURCES
;
2162 dc
= ExAllocatePoolWithTag(PagedPool
, sizeof(dir_child
), ALLOC_TAG
);
2164 ERR("out of memory\n");
2165 free_fileref(Vcb
, fileref
);
2166 return STATUS_INSUFFICIENT_RESOURCES
;
2169 RtlZeroMemory(dc
, sizeof(dir_child
));
2171 dc
->utf8
.MaximumLength
= dc
->utf8
.Length
= fcb
->adsxattr
.Length
- xapreflen
;
2172 dc
->utf8
.Buffer
= ExAllocatePoolWithTag(PagedPool
, dc
->utf8
.MaximumLength
, ALLOC_TAG
);
2173 if (!dc
->utf8
.Buffer
) {
2174 ERR("out of memory\n");
2176 free_fileref(Vcb
, fileref
);
2177 return STATUS_INSUFFICIENT_RESOURCES
;
2180 RtlCopyMemory(dc
->utf8
.Buffer
, &fcb
->adsxattr
.Buffer
[xapreflen
], fcb
->adsxattr
.Length
- xapreflen
);
2182 dc
->name
.MaximumLength
= dc
->name
.Length
= stream
->Length
;
2183 dc
->name
.Buffer
= ExAllocatePoolWithTag(pool_type
, dc
->name
.MaximumLength
, ALLOC_TAG
);
2184 if (!dc
->name
.Buffer
) {
2185 ERR("out of memory\n");
2186 ExFreePool(dc
->utf8
.Buffer
);
2188 free_fileref(Vcb
, fileref
);
2189 return STATUS_INSUFFICIENT_RESOURCES
;
2192 RtlCopyMemory(dc
->name
.Buffer
, stream
->Buffer
, stream
->Length
);
2194 Status
= RtlUpcaseUnicodeString(&dc
->name_uc
, &dc
->name
, TRUE
);
2195 if (!NT_SUCCESS(Status
)) {
2196 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
2197 ExFreePool(dc
->utf8
.Buffer
);
2198 ExFreePool(dc
->name
.Buffer
);
2200 free_fileref(Vcb
, fileref
);
2204 dc
->fileref
= fileref
;
2207 InsertHeadList(&parfileref
->fcb
->dir_children_index
, &dc
->list_entry_index
);
2209 mark_fcb_dirty(fcb
);
2210 mark_fileref_dirty(fileref
);
2212 InsertHeadList(&parfileref
->fcb
->list_entry
, &fcb
->list_entry
); // insert in list after parent fcb
2213 InsertTailList(&Vcb
->all_fcbs
, &fcb
->list_entry_all
);
2215 KeQuerySystemTime(&time
);
2216 win_time_to_unix(time
, &now
);
2218 parfileref
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
2219 parfileref
->fcb
->inode_item
.sequence
++;
2220 parfileref
->fcb
->inode_item
.st_ctime
= now
;
2221 parfileref
->fcb
->inode_item_changed
= TRUE
;
2223 mark_fcb_dirty(parfileref
->fcb
);
2225 parfileref
->fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
2226 parfileref
->fcb
->subvol
->root_item
.ctime
= now
;
2228 fileref
->parent
= (struct _file_ref
*)parfileref
;
2230 ExAcquireResourceExclusiveLite(&parfileref
->nonpaged
->children_lock
, TRUE
);
2231 InsertTailList(&parfileref
->children
, &fileref
->list_entry
);
2232 ExReleaseResourceLite(&parfileref
->nonpaged
->children_lock
);
2234 increase_fileref_refcount(parfileref
);
2236 *pfileref
= fileref
;
2238 send_notification_fileref(parfileref
, options
& FILE_DIRECTORY_FILE
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, &fileref
->dc
->name
);
2240 return STATUS_SUCCESS
;
2243 // LXSS programs can be distinguished by the fact they have a NULL PEB.
2245 static __inline BOOL
called_from_lxss() {
2247 PROCESS_BASIC_INFORMATION pbi
;
2250 Status
= ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation
, &pbi
, sizeof(pbi
), &retlen
);
2252 if (!NT_SUCCESS(Status
)) {
2253 ERR("ZwQueryInformationProcess returned %08x\n", Status
);
2257 return !pbi
.PebBaseAddress
;
2260 #define called_from_lxss() FALSE
2263 static NTSTATUS
file_create(PIRP Irp
, _Requires_lock_held_(_Curr_
->tree_lock
) _Requires_exclusive_lock_held_(_Curr_
->fcb_lock
) device_extension
* Vcb
,
2264 PFILE_OBJECT FileObject
, file_ref
* related
, BOOL loaded_related
, PUNICODE_STRING fnus
, ULONG disposition
, ULONG options
, LIST_ENTRY
* rollback
) {
2266 file_ref
*fileref
, *parfileref
= NULL
;
2269 static WCHAR datasuf
[] = {':','$','D','A','T','A',0};
2270 UNICODE_STRING dsus
, fpus
, stream
;
2271 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2272 POOL_TYPE pool_type
= IrpSp
->Flags
& SL_OPEN_PAGING_FILE
? NonPagedPool
: PagedPool
;
2273 #ifdef DEBUG_FCB_REFCOUNTS
2277 TRACE("(%p, %p, %p, %.*S, %x, %x)\n", Irp
, Vcb
, FileObject
, fnus
->Length
/ sizeof(WCHAR
), fnus
->Buffer
, disposition
, options
);
2280 return STATUS_MEDIA_WRITE_PROTECTED
;
2282 dsus
.Buffer
= datasuf
;
2283 dsus
.Length
= dsus
.MaximumLength
= (USHORT
)wcslen(datasuf
) * sizeof(WCHAR
);
2286 if (!loaded_related
) {
2287 Status
= open_fileref(Vcb
, &parfileref
, fnus
, related
, TRUE
, NULL
, NULL
, pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
2289 if (!NT_SUCCESS(Status
))
2292 parfileref
= related
;
2294 if (parfileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& (fnus
->Length
< sizeof(WCHAR
) || fnus
->Buffer
[0] != ':')) {
2295 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
2299 if (is_subvol_readonly(parfileref
->fcb
->subvol
, Irp
)) {
2300 Status
= STATUS_ACCESS_DENIED
;
2304 i
= (fnus
->Length
/ sizeof(WCHAR
))-1;
2305 while ((fnus
->Buffer
[i
] == '\\' || fnus
->Buffer
[i
] == '/') && i
> 0) { i
--; }
2309 while (i
> 0 && fnus
->Buffer
[i
-1] != '\\' && fnus
->Buffer
[i
-1] != '/') { i
--; }
2311 fpus
.MaximumLength
= (USHORT
)((j
- i
+ 2) * sizeof(WCHAR
));
2312 fpus
.Buffer
= ExAllocatePoolWithTag(pool_type
, fpus
.MaximumLength
, ALLOC_TAG
);
2314 ERR("out of memory\n");
2315 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2319 fpus
.Length
= (USHORT
)((j
- i
+ 1) * sizeof(WCHAR
));
2321 RtlCopyMemory(fpus
.Buffer
, &fnus
->Buffer
[i
], (j
- i
+ 1) * sizeof(WCHAR
));
2322 fpus
.Buffer
[j
- i
+ 1] = 0;
2324 if (fpus
.Length
> dsus
.Length
) { // check for :$DATA suffix
2327 lb
.Buffer
= &fpus
.Buffer
[(fpus
.Length
- dsus
.Length
)/sizeof(WCHAR
)];
2328 lb
.Length
= lb
.MaximumLength
= dsus
.Length
;
2330 TRACE("lb = %.*S\n", lb
.Length
/sizeof(WCHAR
), lb
.Buffer
);
2332 if (FsRtlAreNamesEqual(&dsus
, &lb
, TRUE
, NULL
)) {
2333 TRACE("ignoring :$DATA suffix\n");
2335 fpus
.Length
-= lb
.Length
;
2337 if (fpus
.Length
> sizeof(WCHAR
) && fpus
.Buffer
[(fpus
.Length
-1)/sizeof(WCHAR
)] == ':')
2338 fpus
.Length
-= sizeof(WCHAR
);
2340 TRACE("fpus = %.*S\n", fpus
.Length
/ sizeof(WCHAR
), fpus
.Buffer
);
2346 for (i
= 0; i
< fpus
.Length
/ sizeof(WCHAR
); i
++) {
2347 if (fpus
.Buffer
[i
] == ':') {
2348 stream
.Length
= (USHORT
)(fpus
.Length
- (i
* sizeof(WCHAR
)) - sizeof(WCHAR
));
2349 stream
.Buffer
= &fpus
.Buffer
[i
+1];
2351 fpus
.Length
= (USHORT
)(i
* sizeof(WCHAR
));
2356 if (stream
.Length
> 0) {
2357 Status
= create_stream(Vcb
, &fileref
, &parfileref
, &fpus
, &stream
, Irp
, options
, pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, rollback
);
2358 if (!NT_SUCCESS(Status
)) {
2359 ERR("create_stream returned %08x\n", Status
);
2363 IoSetShareAccess(IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
, IrpSp
->Parameters
.Create
.ShareAccess
,
2364 FileObject
, &fileref
->fcb
->share_access
);
2366 ACCESS_MASK granted_access
;
2368 if (!is_file_name_valid(&fpus
, FALSE
)) {
2369 Status
= STATUS_OBJECT_NAME_INVALID
;
2373 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2375 if (!SeAccessCheck(parfileref
->fcb
->sd
, &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
2376 TRUE
, options
& FILE_DIRECTORY_FILE
? FILE_ADD_SUBDIRECTORY
: FILE_ADD_FILE
, 0, NULL
,
2377 IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
2378 &granted_access
, &Status
)) {
2379 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2383 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
2385 if (Irp
->AssociatedIrp
.SystemBuffer
&& IrpSp
->Parameters
.Create
.EaLength
> 0) {
2388 Status
= IoCheckEaBufferValidity(Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.Create
.EaLength
, &offset
);
2389 if (!NT_SUCCESS(Status
)) {
2390 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
2395 Status
= file_create2(Irp
, Vcb
, &fpus
, parfileref
, options
, Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.Create
.EaLength
,
2396 &fileref
, rollback
);
2398 if (!NT_SUCCESS(Status
)) {
2399 ERR("file_create2 returned %08x\n", Status
);
2403 IoSetShareAccess(IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
, IrpSp
->Parameters
.Create
.ShareAccess
, FileObject
, &fileref
->fcb
->share_access
);
2405 send_notification_fileref(fileref
, options
& FILE_DIRECTORY_FILE
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_ADDED
, NULL
);
2406 send_notification_fcb(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
, NULL
);
2409 FileObject
->FsContext
= fileref
->fcb
;
2411 ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ccb
), ALLOC_TAG
);
2413 ERR("out of memory\n");
2414 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2415 free_fileref(Vcb
, fileref
);
2419 RtlZeroMemory(ccb
, sizeof(*ccb
));
2421 ccb
->fileref
= fileref
;
2423 ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
2424 ccb
->NodeSize
= sizeof(*ccb
);
2425 ccb
->disposition
= disposition
;
2426 ccb
->options
= options
;
2427 ccb
->query_dir_offset
= 0;
2428 RtlInitUnicodeString(&ccb
->query_string
, NULL
);
2429 ccb
->has_wildcard
= FALSE
;
2430 ccb
->specific_file
= FALSE
;
2431 ccb
->access
= IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
;
2432 ccb
->case_sensitive
= IrpSp
->Flags
& SL_CASE_SENSITIVE
;
2433 ccb
->reserving
= FALSE
;
2434 ccb
->lxss
= called_from_lxss();
2436 #ifdef DEBUG_FCB_REFCOUNTS
2437 oc
= InterlockedIncrement(&fileref
->open_count
);
2438 ERR("fileref %p: open_count now %i\n", fileref
, oc
);
2440 InterlockedIncrement(&fileref
->open_count
);
2442 InterlockedIncrement(&Vcb
->open_files
);
2444 FileObject
->FsContext2
= ccb
;
2446 FileObject
->SectionObjectPointer
= &fileref
->fcb
->nonpaged
->segment_object
;
2452 ExFreePool(fpus
.Buffer
);
2455 if (parfileref
&& !loaded_related
)
2456 free_fileref(Vcb
, parfileref
);
2461 static __inline
void debug_create_options(ULONG RequestedOptions
) {
2462 if (RequestedOptions
!= 0) {
2463 ULONG options
= RequestedOptions
;
2465 TRACE("requested options:\n");
2467 if (options
& FILE_DIRECTORY_FILE
) {
2468 TRACE(" FILE_DIRECTORY_FILE\n");
2469 options
&= ~FILE_DIRECTORY_FILE
;
2472 if (options
& FILE_WRITE_THROUGH
) {
2473 TRACE(" FILE_WRITE_THROUGH\n");
2474 options
&= ~FILE_WRITE_THROUGH
;
2477 if (options
& FILE_SEQUENTIAL_ONLY
) {
2478 TRACE(" FILE_SEQUENTIAL_ONLY\n");
2479 options
&= ~FILE_SEQUENTIAL_ONLY
;
2482 if (options
& FILE_NO_INTERMEDIATE_BUFFERING
) {
2483 TRACE(" FILE_NO_INTERMEDIATE_BUFFERING\n");
2484 options
&= ~FILE_NO_INTERMEDIATE_BUFFERING
;
2487 if (options
& FILE_SYNCHRONOUS_IO_ALERT
) {
2488 TRACE(" FILE_SYNCHRONOUS_IO_ALERT\n");
2489 options
&= ~FILE_SYNCHRONOUS_IO_ALERT
;
2492 if (options
& FILE_SYNCHRONOUS_IO_NONALERT
) {
2493 TRACE(" FILE_SYNCHRONOUS_IO_NONALERT\n");
2494 options
&= ~FILE_SYNCHRONOUS_IO_NONALERT
;
2497 if (options
& FILE_NON_DIRECTORY_FILE
) {
2498 TRACE(" FILE_NON_DIRECTORY_FILE\n");
2499 options
&= ~FILE_NON_DIRECTORY_FILE
;
2502 if (options
& FILE_CREATE_TREE_CONNECTION
) {
2503 TRACE(" FILE_CREATE_TREE_CONNECTION\n");
2504 options
&= ~FILE_CREATE_TREE_CONNECTION
;
2507 if (options
& FILE_COMPLETE_IF_OPLOCKED
) {
2508 TRACE(" FILE_COMPLETE_IF_OPLOCKED\n");
2509 options
&= ~FILE_COMPLETE_IF_OPLOCKED
;
2512 if (options
& FILE_NO_EA_KNOWLEDGE
) {
2513 TRACE(" FILE_NO_EA_KNOWLEDGE\n");
2514 options
&= ~FILE_NO_EA_KNOWLEDGE
;
2517 if (options
& FILE_OPEN_REMOTE_INSTANCE
) {
2518 TRACE(" FILE_OPEN_REMOTE_INSTANCE\n");
2519 options
&= ~FILE_OPEN_REMOTE_INSTANCE
;
2522 if (options
& FILE_RANDOM_ACCESS
) {
2523 TRACE(" FILE_RANDOM_ACCESS\n");
2524 options
&= ~FILE_RANDOM_ACCESS
;
2527 if (options
& FILE_DELETE_ON_CLOSE
) {
2528 TRACE(" FILE_DELETE_ON_CLOSE\n");
2529 options
&= ~FILE_DELETE_ON_CLOSE
;
2532 if (options
& FILE_OPEN_BY_FILE_ID
) {
2533 TRACE(" FILE_OPEN_BY_FILE_ID\n");
2534 options
&= ~FILE_OPEN_BY_FILE_ID
;
2537 if (options
& FILE_OPEN_FOR_BACKUP_INTENT
) {
2538 TRACE(" FILE_OPEN_FOR_BACKUP_INTENT\n");
2539 options
&= ~FILE_OPEN_FOR_BACKUP_INTENT
;
2542 if (options
& FILE_NO_COMPRESSION
) {
2543 TRACE(" FILE_NO_COMPRESSION\n");
2544 options
&= ~FILE_NO_COMPRESSION
;
2547 #if NTDDI_VERSION >= NTDDI_WIN7
2548 if (options
& FILE_OPEN_REQUIRING_OPLOCK
) {
2549 TRACE(" FILE_OPEN_REQUIRING_OPLOCK\n");
2550 options
&= ~FILE_OPEN_REQUIRING_OPLOCK
;
2553 if (options
& FILE_DISALLOW_EXCLUSIVE
) {
2554 TRACE(" FILE_DISALLOW_EXCLUSIVE\n");
2555 options
&= ~FILE_DISALLOW_EXCLUSIVE
;
2559 if (options
& FILE_RESERVE_OPFILTER
) {
2560 TRACE(" FILE_RESERVE_OPFILTER\n");
2561 options
&= ~FILE_RESERVE_OPFILTER
;
2564 if (options
& FILE_OPEN_REPARSE_POINT
) {
2565 TRACE(" FILE_OPEN_REPARSE_POINT\n");
2566 options
&= ~FILE_OPEN_REPARSE_POINT
;
2569 if (options
& FILE_OPEN_NO_RECALL
) {
2570 TRACE(" FILE_OPEN_NO_RECALL\n");
2571 options
&= ~FILE_OPEN_NO_RECALL
;
2574 if (options
& FILE_OPEN_FOR_FREE_SPACE_QUERY
) {
2575 TRACE(" FILE_OPEN_FOR_FREE_SPACE_QUERY\n");
2576 options
&= ~FILE_OPEN_FOR_FREE_SPACE_QUERY
;
2580 TRACE(" unknown options: %x\n", options
);
2582 TRACE("requested options: (none)\n");
2586 static NTSTATUS
get_reparse_block(fcb
* fcb
, UINT8
** data
) {
2589 if (fcb
->type
== BTRFS_TYPE_FILE
|| fcb
->type
== BTRFS_TYPE_SYMLINK
) {
2590 ULONG size
, bytes_read
, i
;
2592 if (fcb
->type
== BTRFS_TYPE_FILE
&& fcb
->inode_item
.st_size
< sizeof(ULONG
)) {
2593 WARN("file was too short to be a reparse point\n");
2594 return STATUS_INVALID_PARAMETER
;
2597 // 0x10007 = 0xffff (maximum length of data buffer) + 8 bytes header
2598 size
= (ULONG
)min(0x10007, fcb
->inode_item
.st_size
);
2601 return STATUS_INVALID_PARAMETER
;
2603 *data
= ExAllocatePoolWithTag(PagedPool
, size
, ALLOC_TAG
);
2605 ERR("out of memory\n");
2606 return STATUS_INSUFFICIENT_RESOURCES
;
2609 Status
= read_file(fcb
, *data
, 0, size
, &bytes_read
, NULL
);
2610 if (!NT_SUCCESS(Status
)) {
2611 ERR("read_file_fcb returned %08x\n", Status
);
2616 if (fcb
->type
== BTRFS_TYPE_SYMLINK
) {
2617 ULONG stringlen
, reqlen
;
2618 UINT16 subnamelen
, printnamelen
;
2619 REPARSE_DATA_BUFFER
* rdb
;
2621 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, (char*)*data
, bytes_read
);
2622 if (!NT_SUCCESS(Status
)) {
2623 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
2628 subnamelen
= printnamelen
= (USHORT
)stringlen
;
2630 reqlen
= offsetof(REPARSE_DATA_BUFFER
, SymbolicLinkReparseBuffer
.PathBuffer
) + subnamelen
+ printnamelen
;
2632 rdb
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
2635 ERR("out of memory\n");
2637 return STATUS_INSUFFICIENT_RESOURCES
;
2640 rdb
->ReparseTag
= IO_REPARSE_TAG_SYMLINK
;
2641 rdb
->ReparseDataLength
= (USHORT
)(reqlen
- offsetof(REPARSE_DATA_BUFFER
, SymbolicLinkReparseBuffer
));
2644 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
= 0;
2645 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameLength
= subnamelen
;
2646 rdb
->SymbolicLinkReparseBuffer
.PrintNameOffset
= subnamelen
;
2647 rdb
->SymbolicLinkReparseBuffer
.PrintNameLength
= printnamelen
;
2648 rdb
->SymbolicLinkReparseBuffer
.Flags
= SYMLINK_FLAG_RELATIVE
;
2650 Status
= RtlUTF8ToUnicodeN(&rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)],
2651 stringlen
, &stringlen
, (char*)*data
, size
);
2653 if (!NT_SUCCESS(Status
)) {
2654 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
2660 for (i
= 0; i
< stringlen
/ sizeof(WCHAR
); i
++) {
2661 if (rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[(rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)) + i
] == '/')
2662 rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[(rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)) + i
] = '\\';
2665 RtlCopyMemory(&rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.PrintNameOffset
/ sizeof(WCHAR
)],
2666 &rdb
->SymbolicLinkReparseBuffer
.PathBuffer
[rdb
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)],
2667 rdb
->SymbolicLinkReparseBuffer
.SubstituteNameLength
);
2671 *data
= (UINT8
*)rdb
;
2673 Status
= FsRtlValidateReparsePointBuffer(bytes_read
, (REPARSE_DATA_BUFFER
*)*data
);
2674 if (!NT_SUCCESS(Status
)) {
2675 ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status
);
2680 } else if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
2681 if (!fcb
->reparse_xattr
.Buffer
|| fcb
->reparse_xattr
.Length
== 0)
2682 return STATUS_INTERNAL_ERROR
;
2684 if (fcb
->reparse_xattr
.Length
< sizeof(ULONG
)) {
2685 WARN("xattr was too short to be a reparse point\n");
2686 return STATUS_INTERNAL_ERROR
;
2689 Status
= FsRtlValidateReparsePointBuffer(fcb
->reparse_xattr
.Length
, (REPARSE_DATA_BUFFER
*)fcb
->reparse_xattr
.Buffer
);
2690 if (!NT_SUCCESS(Status
)) {
2691 ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status
);
2695 *data
= ExAllocatePoolWithTag(PagedPool
, fcb
->reparse_xattr
.Length
, ALLOC_TAG
);
2697 ERR("out of memory\n");
2698 return STATUS_INSUFFICIENT_RESOURCES
;
2701 RtlCopyMemory(*data
, fcb
->reparse_xattr
.Buffer
, fcb
->reparse_xattr
.Length
);
2704 return STATUS_SUCCESS
;
2707 static void fcb_load_csums(_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, fcb
* fcb
, PIRP Irp
) {
2711 if (fcb
->csum_loaded
)
2714 if (IsListEmpty(&fcb
->extents
) || fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)
2717 le
= fcb
->extents
.Flink
;
2718 while (le
!= &fcb
->extents
) {
2719 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2721 if (ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
) {
2722 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)&ext
->extent_data
.data
[0];
2725 len
= (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
? ed2
->num_bytes
: ed2
->size
) / Vcb
->superblock
.sector_size
;
2727 ext
->csum
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)(len
* sizeof(UINT32
)), ALLOC_TAG
);
2729 ERR("out of memory\n");
2733 Status
= load_csum(Vcb
, ext
->csum
, ed2
->address
+ (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
? ed2
->offset
: 0), len
, Irp
);
2735 if (!NT_SUCCESS(Status
)) {
2736 ERR("load_csum returned %08x\n", Status
);
2745 fcb
->csum_loaded
= TRUE
;
2748 static NTSTATUS
open_file(PDEVICE_OBJECT DeviceObject
, _Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2749 PFILE_OBJECT FileObject
= NULL
;
2750 ULONG RequestedDisposition
;
2754 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2756 ULONG fn_offset
= 0;
2757 file_ref
*related
, *fileref
= NULL
;
2758 POOL_TYPE pool_type
= IrpSp
->Flags
& SL_OPEN_PAGING_FILE
? NonPagedPool
: PagedPool
;
2759 ACCESS_MASK granted_access
;
2760 BOOL loaded_related
= FALSE
;
2762 #ifdef DEBUG_FCB_REFCOUNTS
2766 LARGE_INTEGER time1
, time2
;
2767 UINT8 open_type
= 0;
2769 time1
= KeQueryPerformanceCounter(NULL
);
2772 Irp
->IoStatus
.Information
= 0;
2774 RequestedDisposition
= ((IrpSp
->Parameters
.Create
.Options
>> 24) & 0xff);
2775 options
= IrpSp
->Parameters
.Create
.Options
& FILE_VALID_OPTION_FLAGS
;
2777 if (options
& FILE_DIRECTORY_FILE
&& RequestedDisposition
== FILE_SUPERSEDE
) {
2778 WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
2779 return STATUS_INVALID_PARAMETER
;
2782 FileObject
= IrpSp
->FileObject
;
2785 ERR("FileObject was NULL\n");
2786 return STATUS_INVALID_PARAMETER
;
2789 if (FileObject
->RelatedFileObject
&& FileObject
->RelatedFileObject
->FsContext2
) {
2790 struct _ccb
* relatedccb
= FileObject
->RelatedFileObject
->FsContext2
;
2792 related
= relatedccb
->fileref
;
2796 debug_create_options(options
);
2798 switch (RequestedDisposition
) {
2799 case FILE_SUPERSEDE
:
2800 TRACE("requested disposition: FILE_SUPERSEDE\n");
2804 TRACE("requested disposition: FILE_CREATE\n");
2808 TRACE("requested disposition: FILE_OPEN\n");
2812 TRACE("requested disposition: FILE_OPEN_IF\n");
2815 case FILE_OVERWRITE
:
2816 TRACE("requested disposition: FILE_OVERWRITE\n");
2819 case FILE_OVERWRITE_IF
:
2820 TRACE("requested disposition: FILE_OVERWRITE_IF\n");
2824 ERR("unknown disposition: %x\n", RequestedDisposition
);
2825 Status
= STATUS_NOT_IMPLEMENTED
;
2829 fn
= FileObject
->FileName
;
2831 TRACE("(%.*S)\n", fn
.Length
/ sizeof(WCHAR
), fn
.Buffer
);
2832 TRACE("FileObject = %p\n", FileObject
);
2834 if (Vcb
->readonly
&& (RequestedDisposition
== FILE_SUPERSEDE
|| RequestedDisposition
== FILE_CREATE
|| RequestedDisposition
== FILE_OVERWRITE
)) {
2835 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
2839 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
2841 if (options
& FILE_OPEN_BY_FILE_ID
) {
2842 if (fn
.Length
== sizeof(UINT64
) && related
&& RequestedDisposition
== FILE_OPEN
) {
2845 RtlCopyMemory(&inode
, fn
.Buffer
, sizeof(UINT64
));
2847 if (related
->fcb
== Vcb
->root_fileref
->fcb
&& inode
== 0)
2848 inode
= Vcb
->root_fileref
->fcb
->inode
;
2850 if (inode
== 0) { // we use 0 to mean the parent of a subvolume
2851 fileref
= related
->parent
;
2852 increase_fileref_refcount(fileref
);
2853 Status
= STATUS_SUCCESS
;
2855 Status
= open_fileref_by_inode(Vcb
, related
->fcb
->subvol
, inode
, &fileref
, Irp
);
2858 WARN("FILE_OPEN_BY_FILE_ID only supported for inodes\n");
2859 Status
= STATUS_NOT_IMPLEMENTED
;
2860 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2864 if (related
&& fn
.Length
!= 0 && fn
.Buffer
[0] == '\\') {
2865 Status
= STATUS_OBJECT_NAME_INVALID
;
2866 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2870 if (!related
&& RequestedDisposition
!= FILE_OPEN
&& !(IrpSp
->Flags
& SL_OPEN_TARGET_DIRECTORY
)) {
2873 Status
= open_fileref(Vcb
, &related
, &fn
, NULL
, TRUE
, &parsed
, &fnoff
,
2874 pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
2876 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
2877 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
2878 else if (Status
== STATUS_REPARSE
)
2880 else if (NT_SUCCESS(Status
)) {
2881 fnoff
*= sizeof(WCHAR
);
2882 fnoff
+= (related
->dc
? related
->dc
->name
.Length
: 0) + sizeof(WCHAR
);
2884 if (related
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
) {
2885 Status
= STATUS_REPARSE
;
2887 parsed
= (USHORT
)fnoff
- sizeof(WCHAR
);
2889 fn
.Buffer
= &fn
.Buffer
[fnoff
/ sizeof(WCHAR
)];
2890 fn
.Length
-= (USHORT
)fnoff
;
2892 Status
= open_fileref(Vcb
, &fileref
, &fn
, related
, IrpSp
->Flags
& SL_OPEN_TARGET_DIRECTORY
, &parsed
, &fn_offset
,
2893 pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
2895 loaded_related
= TRUE
;
2900 Status
= open_fileref(Vcb
, &fileref
, &fn
, related
, IrpSp
->Flags
& SL_OPEN_TARGET_DIRECTORY
, &parsed
, &fn_offset
,
2901 pool_type
, IrpSp
->Flags
& SL_CASE_SENSITIVE
, Irp
);
2905 if (Status
== STATUS_REPARSE
) {
2906 REPARSE_DATA_BUFFER
* data
;
2908 ExAcquireResourceSharedLite(fileref
->fcb
->Header
.Resource
, TRUE
);
2909 Status
= get_reparse_block(fileref
->fcb
, (UINT8
**)&data
);
2910 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2912 if (!NT_SUCCESS(Status
)) {
2913 ERR("get_reparse_block returned %08x\n", Status
);
2915 Status
= STATUS_SUCCESS
;
2917 Status
= STATUS_REPARSE
;
2918 RtlCopyMemory(&Irp
->IoStatus
.Information
, data
, sizeof(ULONG
));
2920 data
->Reserved
= FileObject
->FileName
.Length
- parsed
;
2922 Irp
->Tail
.Overlay
.AuxiliaryBuffer
= (void*)data
;
2924 free_fileref(Vcb
, fileref
);
2925 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2931 if (NT_SUCCESS(Status
) && fileref
->deleted
)
2932 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
2934 if (NT_SUCCESS(Status
)) {
2935 if (RequestedDisposition
== FILE_CREATE
) {
2936 TRACE("file %S already exists, returning STATUS_OBJECT_NAME_COLLISION\n", file_desc_fileref(fileref
));
2937 Status
= STATUS_OBJECT_NAME_COLLISION
;
2939 free_fileref(Vcb
, fileref
);
2940 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2944 } else if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
2945 if (RequestedDisposition
== FILE_OPEN
|| RequestedDisposition
== FILE_OVERWRITE
) {
2946 TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
2947 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2950 } else if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
) {
2951 TRACE("open_fileref returned %08x\n", Status
);
2952 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2955 ERR("open_fileref returned %08x\n", Status
);
2956 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2960 if (NT_SUCCESS(Status
)) { // file already exists
2964 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2966 if (RequestedDisposition
== FILE_SUPERSEDE
|| RequestedDisposition
== FILE_OVERWRITE
|| RequestedDisposition
== FILE_OVERWRITE_IF
) {
2972 if (fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
|| is_subvol_readonly(fileref
->fcb
->subvol
, Irp
)) {
2973 Status
= STATUS_ACCESS_DENIED
;
2975 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
2976 free_fileref(Vcb
, fileref
);
2977 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2982 if (Vcb
->readonly
) {
2983 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
2985 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
2986 free_fileref(Vcb
, fileref
);
2987 ExReleaseResourceLite(&Vcb
->fcb_lock
);
2993 if (!MmCanFileBeTruncated(&fileref
->fcb
->nonpaged
->segment_object
, &zero
)) {
2994 Status
= STATUS_USER_MAPPED_FILE
;
2996 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
2997 free_fileref(Vcb
, fileref
);
2998 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3004 if (IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
!= 0) {
3005 SeLockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3007 if (!SeAccessCheck((fileref
->fcb
->ads
|| fileref
->fcb
== Vcb
->dummy_fcb
) ? fileref
->parent
->fcb
->sd
: fileref
->fcb
->sd
,
3008 &IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
,
3009 TRUE
, IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
, 0, NULL
,
3010 IoGetFileObjectGenericMapping(), IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
,
3011 &granted_access
, &Status
)) {
3012 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3013 TRACE("SeAccessCheck failed, returning %08x\n", Status
);
3015 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3016 free_fileref(Vcb
, fileref
);
3017 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3022 SeUnlockSubjectContext(&IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->SubjectSecurityContext
);
3026 TRACE("deleted = %s\n", fileref
->deleted
? "TRUE" : "FALSE");
3030 if (sf
->delete_on_close
) {
3031 TRACE("could not open as deletion pending\n");
3032 Status
= STATUS_DELETE_PENDING
;
3034 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3035 free_fileref(Vcb
, fileref
);
3036 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3043 readonly
= (!fileref
->fcb
->ads
&& fileref
->fcb
->atts
& FILE_ATTRIBUTE_READONLY
) || (fileref
->fcb
->ads
&& fileref
->parent
->fcb
->atts
& FILE_ATTRIBUTE_READONLY
) ||
3044 is_subvol_readonly(fileref
->fcb
->subvol
, Irp
) || fileref
->fcb
== Vcb
->dummy_fcb
|| Vcb
->readonly
;
3047 ACCESS_MASK allowed
;
3049 allowed
= READ_CONTROL
| SYNCHRONIZE
| ACCESS_SYSTEM_SECURITY
| FILE_READ_DATA
|
3050 FILE_READ_EA
| FILE_READ_ATTRIBUTES
| FILE_EXECUTE
| FILE_LIST_DIRECTORY
|
3053 if (!Vcb
->readonly
&& (fileref
->fcb
== Vcb
->dummy_fcb
|| fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
))
3056 if (fileref
->fcb
!= Vcb
->dummy_fcb
&& !is_subvol_readonly(fileref
->fcb
->subvol
, Irp
) && !Vcb
->readonly
) {
3057 allowed
|= WRITE_OWNER
| WRITE_DAC
| FILE_WRITE_EA
| FILE_WRITE_ATTRIBUTES
;
3059 if (!fileref
->fcb
->ads
&& fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
)
3060 allowed
|= FILE_ADD_SUBDIRECTORY
| FILE_ADD_FILE
| FILE_DELETE_CHILD
;
3061 } else if (fileref
->fcb
->inode
== SUBVOL_ROOT_INODE
&& is_subvol_readonly(fileref
->fcb
->subvol
, Irp
) && !Vcb
->readonly
) {
3062 // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
3064 allowed
|= FILE_WRITE_ATTRIBUTES
;
3067 if (IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
& MAXIMUM_ALLOWED
) {
3068 granted_access
&= allowed
;
3069 IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->PreviouslyGrantedAccess
&= allowed
;
3070 } else if (granted_access
& ~allowed
) {
3071 Status
= Vcb
->readonly
? STATUS_MEDIA_WRITE_PROTECTED
: STATUS_ACCESS_DENIED
;
3073 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3074 free_fileref(Vcb
, fileref
);
3075 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3081 if (options
& FILE_DELETE_ON_CLOSE
&& (fileref
== Vcb
->root_fileref
|| Vcb
->readonly
||
3082 is_subvol_readonly(fileref
->fcb
->subvol
, Irp
) || readonly
)) {
3083 Status
= STATUS_CANNOT_DELETE
;
3085 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3086 free_fileref(Vcb
, fileref
);
3087 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3092 if ((fileref
->fcb
->type
== BTRFS_TYPE_SYMLINK
|| fileref
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
) && !(options
& FILE_OPEN_REPARSE_POINT
)) {
3093 REPARSE_DATA_BUFFER
* data
;
3095 /* How reparse points work from the point of view of the filesystem appears to
3096 * undocumented. When returning STATUS_REPARSE, MSDN encourages us to return
3097 * IO_REPARSE in Irp->IoStatus.Information, but that means we have to do our own
3098 * translation. If we instead return the reparse tag in Information, and store
3099 * a pointer to the reparse data buffer in Irp->Tail.Overlay.AuxiliaryBuffer,
3100 * IopSymlinkProcessReparse will do the translation for us. */
3102 Status
= get_reparse_block(fileref
->fcb
, (UINT8
**)&data
);
3103 if (!NT_SUCCESS(Status
)) {
3104 ERR("get_reparse_block returned %08x\n", Status
);
3105 Status
= STATUS_SUCCESS
;
3107 Status
= STATUS_REPARSE
;
3108 Irp
->IoStatus
.Information
= data
->ReparseTag
;
3110 if (fn
.Buffer
[(fn
.Length
/ sizeof(WCHAR
)) - 1] == '\\')
3111 data
->Reserved
= sizeof(WCHAR
);
3113 Irp
->Tail
.Overlay
.AuxiliaryBuffer
= (void*)data
;
3115 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3116 free_fileref(Vcb
, fileref
);
3117 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3123 if (fileref
->fcb
->type
== BTRFS_TYPE_DIRECTORY
&& !fileref
->fcb
->ads
) {
3124 if (options
& FILE_NON_DIRECTORY_FILE
&& !(fileref
->fcb
->atts
& FILE_ATTRIBUTE_REPARSE_POINT
)) {
3125 Status
= STATUS_FILE_IS_A_DIRECTORY
;
3127 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3128 free_fileref(Vcb
, fileref
);
3129 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3133 } else if (options
& FILE_DIRECTORY_FILE
) {
3134 TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u, %S)\n", fileref
->fcb
->type
, file_desc_fileref(fileref
));
3135 Status
= STATUS_NOT_A_DIRECTORY
;
3137 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3138 free_fileref(Vcb
, fileref
);
3139 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3144 if (fileref
->open_count
> 0) {
3145 Status
= IoCheckShareAccess(granted_access
, IrpSp
->Parameters
.Create
.ShareAccess
, FileObject
, &fileref
->fcb
->share_access
, FALSE
);
3147 if (!NT_SUCCESS(Status
)) {
3148 if (Status
== STATUS_SHARING_VIOLATION
)
3149 TRACE("IoCheckShareAccess failed, returning %08x\n", Status
);
3151 WARN("IoCheckShareAccess failed, returning %08x\n", Status
);
3153 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3154 free_fileref(Vcb
, fileref
);
3155 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3160 IoUpdateShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3162 IoSetShareAccess(granted_access
, IrpSp
->Parameters
.Create
.ShareAccess
, FileObject
, &fileref
->fcb
->share_access
);
3164 if (granted_access
& FILE_WRITE_DATA
|| options
& FILE_DELETE_ON_CLOSE
) {
3165 if (!MmFlushImageSection(&fileref
->fcb
->nonpaged
->segment_object
, MmFlushForWrite
)) {
3166 Status
= (options
& FILE_DELETE_ON_CLOSE
) ? STATUS_CANNOT_DELETE
: STATUS_SHARING_VIOLATION
;
3168 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3170 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3171 free_fileref(Vcb
, fileref
);
3172 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3178 if (RequestedDisposition
== FILE_OVERWRITE
|| RequestedDisposition
== FILE_OVERWRITE_IF
|| RequestedDisposition
== FILE_SUPERSEDE
) {
3179 ULONG defda
, oldatts
, filter
;
3183 if ((RequestedDisposition
== FILE_OVERWRITE
|| RequestedDisposition
== FILE_OVERWRITE_IF
) && readonly
) {
3184 WARN("cannot overwrite readonly file\n");
3185 Status
= STATUS_ACCESS_DENIED
;
3187 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3189 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3190 free_fileref(Vcb
, fileref
);
3191 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3196 if (fileref
->fcb
->ads
) {
3197 Status
= stream_set_end_of_file_information(Vcb
, 0, fileref
->fcb
, fileref
, FALSE
);
3198 if (!NT_SUCCESS(Status
)) {
3199 ERR("stream_set_end_of_file_information returned %08x\n", Status
);
3201 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3203 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3204 free_fileref(Vcb
, fileref
);
3205 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3210 Status
= truncate_file(fileref
->fcb
, 0, Irp
, rollback
);
3211 if (!NT_SUCCESS(Status
)) {
3212 ERR("truncate_file returned %08x\n", Status
);
3214 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3216 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3217 free_fileref(Vcb
, fileref
);
3218 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3224 if (Irp
->Overlay
.AllocationSize
.QuadPart
> 0) {
3225 Status
= extend_file(fileref
->fcb
, fileref
, Irp
->Overlay
.AllocationSize
.QuadPart
, TRUE
, NULL
, rollback
);
3227 if (!NT_SUCCESS(Status
)) {
3228 ERR("extend_file returned %08x\n", Status
);
3230 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3232 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3233 free_fileref(Vcb
, fileref
);
3234 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3240 if (!fileref
->fcb
->ads
) {
3241 if (Irp
->AssociatedIrp
.SystemBuffer
&& IrpSp
->Parameters
.Create
.EaLength
> 0) {
3243 FILE_FULL_EA_INFORMATION
* eainfo
;
3245 Status
= IoCheckEaBufferValidity(Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.Create
.EaLength
, &offset
);
3246 if (!NT_SUCCESS(Status
)) {
3247 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
3249 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3251 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3252 free_fileref(Vcb
, fileref
);
3253 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3258 fileref
->fcb
->ealen
= 4;
3260 // capitalize EA name
3261 eainfo
= Irp
->AssociatedIrp
.SystemBuffer
;
3265 s
.Length
= s
.MaximumLength
= eainfo
->EaNameLength
;
3266 s
.Buffer
= eainfo
->EaName
;
3268 RtlUpperString(&s
, &s
);
3270 fileref
->fcb
->ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
3272 if (eainfo
->NextEntryOffset
== 0)
3275 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
3278 if (fileref
->fcb
->ea_xattr
.Buffer
)
3279 ExFreePool(fileref
->fcb
->ea_xattr
.Buffer
);
3281 fileref
->fcb
->ea_xattr
.Buffer
= ExAllocatePoolWithTag(pool_type
, IrpSp
->Parameters
.Create
.EaLength
, ALLOC_TAG
);
3282 if (!fileref
->fcb
->ea_xattr
.Buffer
) {
3283 ERR("out of memory\n");
3284 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3286 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3288 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3289 free_fileref(Vcb
, fileref
);
3290 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3295 fileref
->fcb
->ea_xattr
.Length
= fileref
->fcb
->ea_xattr
.MaximumLength
= (USHORT
)IrpSp
->Parameters
.Create
.EaLength
;
3296 RtlCopyMemory(fileref
->fcb
->ea_xattr
.Buffer
, Irp
->AssociatedIrp
.SystemBuffer
, fileref
->fcb
->ea_xattr
.Length
);
3298 if (fileref
->fcb
->ea_xattr
.Length
> 0) {
3299 ExFreePool(fileref
->fcb
->ea_xattr
.Buffer
);
3300 fileref
->fcb
->ea_xattr
.Buffer
= NULL
;
3301 fileref
->fcb
->ea_xattr
.Length
= fileref
->fcb
->ea_xattr
.MaximumLength
= 0;
3303 fileref
->fcb
->ea_changed
= TRUE
;
3304 fileref
->fcb
->ealen
= 0;
3309 KeQuerySystemTime(&time
);
3310 win_time_to_unix(time
, &now
);
3312 filter
= FILE_NOTIFY_CHANGE_SIZE
| FILE_NOTIFY_CHANGE_LAST_WRITE
;
3314 if (fileref
->fcb
->ads
) {
3315 fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
3316 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
3317 mark_fcb_dirty(fileref
->parent
->fcb
);
3319 send_notification_fcb(fileref
->parent
, filter
, FILE_ACTION_MODIFIED
, &fileref
->dc
->name
);
3321 mark_fcb_dirty(fileref
->fcb
);
3323 oldatts
= fileref
->fcb
->atts
;
3325 defda
= get_file_attributes(Vcb
, fileref
->fcb
->subvol
, fileref
->fcb
->inode
, fileref
->fcb
->type
,
3326 fileref
->dc
&& fileref
->dc
->name
.Length
>= sizeof(WCHAR
) && fileref
->dc
->name
.Buffer
[0] == '.', TRUE
, Irp
);
3328 if (RequestedDisposition
== FILE_SUPERSEDE
)
3329 fileref
->fcb
->atts
= IrpSp
->Parameters
.Create
.FileAttributes
| FILE_ATTRIBUTE_ARCHIVE
;
3331 fileref
->fcb
->atts
|= IrpSp
->Parameters
.Create
.FileAttributes
| FILE_ATTRIBUTE_ARCHIVE
;
3333 if (fileref
->fcb
->atts
!= oldatts
) {
3334 fileref
->fcb
->atts_changed
= TRUE
;
3335 fileref
->fcb
->atts_deleted
= IrpSp
->Parameters
.Create
.FileAttributes
== defda
;
3336 filter
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
3339 fileref
->fcb
->inode_item
.transid
= Vcb
->superblock
.generation
;
3340 fileref
->fcb
->inode_item
.sequence
++;
3341 fileref
->fcb
->inode_item
.st_ctime
= now
;
3342 fileref
->fcb
->inode_item
.st_mtime
= now
;
3343 fileref
->fcb
->inode_item_changed
= TRUE
;
3345 send_notification_fcb(fileref
, filter
, FILE_ACTION_MODIFIED
, NULL
);
3348 if (options
& FILE_NO_EA_KNOWLEDGE
&& fileref
->fcb
->ea_xattr
.Length
> 0) {
3349 FILE_FULL_EA_INFORMATION
* ffei
= (FILE_FULL_EA_INFORMATION
*)fileref
->fcb
->ea_xattr
.Buffer
;
3352 if (ffei
->Flags
& FILE_NEED_EA
) {
3353 WARN("returning STATUS_ACCESS_DENIED as no EA knowledge\n");
3354 Status
= STATUS_ACCESS_DENIED
;
3356 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3358 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3359 free_fileref(Vcb
, fileref
);
3360 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3365 if (ffei
->NextEntryOffset
== 0)
3368 ffei
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)ffei
) + ffei
->NextEntryOffset
);
3373 FileObject
->FsContext
= fileref
->fcb
;
3375 ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ccb
), ALLOC_TAG
);
3377 ERR("out of memory\n");
3378 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3380 IoRemoveShareAccess(FileObject
, &fileref
->fcb
->share_access
);
3382 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3383 free_fileref(Vcb
, fileref
);
3384 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3389 RtlZeroMemory(ccb
, sizeof(*ccb
));
3391 ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
3392 ccb
->NodeSize
= sizeof(*ccb
);
3393 ccb
->disposition
= RequestedDisposition
;
3394 ccb
->options
= options
;
3395 ccb
->query_dir_offset
= 0;
3396 RtlInitUnicodeString(&ccb
->query_string
, NULL
);
3397 ccb
->has_wildcard
= FALSE
;
3398 ccb
->specific_file
= FALSE
;
3399 ccb
->access
= granted_access
;
3400 ccb
->case_sensitive
= IrpSp
->Flags
& SL_CASE_SENSITIVE
;
3401 ccb
->reserving
= FALSE
;
3402 ccb
->lxss
= called_from_lxss();
3404 ccb
->fileref
= fileref
;
3406 FileObject
->FsContext2
= ccb
;
3407 FileObject
->SectionObjectPointer
= &fileref
->fcb
->nonpaged
->segment_object
;
3409 if (NT_SUCCESS(Status
)) {
3410 switch (RequestedDisposition
) {
3411 case FILE_SUPERSEDE
:
3412 Irp
->IoStatus
.Information
= FILE_SUPERSEDED
;
3417 Irp
->IoStatus
.Information
= FILE_OPENED
;
3420 case FILE_OVERWRITE
:
3421 case FILE_OVERWRITE_IF
:
3422 Irp
->IoStatus
.Information
= FILE_OVERWRITTEN
;
3427 // Make sure paging files don't have any extents marked as being prealloc,
3428 // as this would mean we'd have to lock exclusively when writing.
3429 if (IrpSp
->Flags
& SL_OPEN_PAGING_FILE
) {
3431 BOOL changed
= FALSE
;
3433 ExAcquireResourceExclusiveLite(fileref
->fcb
->Header
.Resource
, TRUE
);
3435 le
= fileref
->fcb
->extents
.Flink
;
3437 while (le
!= &fileref
->fcb
->extents
) {
3438 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3440 if (ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) {
3441 ext
->extent_data
.type
= EXTENT_TYPE_REGULAR
;
3448 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
3451 fileref
->fcb
->extents_changed
= TRUE
;
3452 mark_fcb_dirty(fileref
->fcb
);
3455 fileref
->fcb
->Header
.Flags2
|= FSRTL_FLAG2_IS_PAGING_FILE
;
3456 Vcb
->disallow_dismount
= TRUE
;
3459 #ifdef DEBUG_FCB_REFCOUNTS
3460 oc
= InterlockedIncrement(&fileref
->open_count
);
3461 ERR("fileref %p: open_count now %i\n", fileref
, oc
);
3463 InterlockedIncrement(&fileref
->open_count
);
3465 InterlockedIncrement(&Vcb
->open_files
);
3470 Status
= file_create(Irp
, DeviceObject
->DeviceExtension
, FileObject
, related
, loaded_related
, &fn
, RequestedDisposition
, options
, rollback
);
3471 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3473 Irp
->IoStatus
.Information
= NT_SUCCESS(Status
) ? FILE_CREATED
: 0;
3476 if (NT_SUCCESS(Status
) && !(options
& FILE_NO_INTERMEDIATE_BUFFERING
))
3477 FileObject
->Flags
|= FO_CACHE_SUPPORTED
;
3480 if (loaded_related
) {
3481 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
3482 free_fileref(Vcb
, related
);
3483 ExReleaseResourceLite(&Vcb
->fcb_lock
);
3486 if (Status
== STATUS_SUCCESS
) {
3489 IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->PreviouslyGrantedAccess
|= granted_access
;
3490 IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->RemainingDesiredAccess
&= ~(granted_access
| MAXIMUM_ALLOWED
);
3492 if (!FileObject
->Vpb
)
3493 FileObject
->Vpb
= DeviceObject
->Vpb
;
3495 fcb2
= FileObject
->FsContext
;
3498 struct _ccb
* ccb2
= FileObject
->FsContext2
;
3500 fcb2
= ccb2
->fileref
->parent
->fcb
;
3503 ExAcquireResourceExclusiveLite(fcb2
->Header
.Resource
, TRUE
);
3504 fcb_load_csums(Vcb
, fcb2
, Irp
);
3505 ExReleaseResourceLite(fcb2
->Header
.Resource
);
3506 } else if (Status
!= STATUS_REPARSE
&& Status
!= STATUS_OBJECT_NAME_NOT_FOUND
&& Status
!= STATUS_OBJECT_PATH_NOT_FOUND
)
3507 TRACE("returning %08x\n", Status
);
3510 time2
= KeQueryPerformanceCounter(NULL
);
3512 if (open_type
== 0) {
3513 Vcb
->stats
.open_total_time
+= time2
.QuadPart
- time1
.QuadPart
;
3514 Vcb
->stats
.num_opens
++;
3515 } else if (open_type
== 1) {
3516 Vcb
->stats
.overwrite_total_time
+= time2
.QuadPart
- time1
.QuadPart
;
3517 Vcb
->stats
.num_overwrites
++;
3518 } else if (open_type
== 2) {
3519 Vcb
->stats
.create_total_time
+= time2
.QuadPart
- time1
.QuadPart
;
3520 Vcb
->stats
.num_creates
++;
3527 static NTSTATUS
verify_vcb(device_extension
* Vcb
, PIRP Irp
) {
3530 BOOL need_verify
= FALSE
;
3532 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3534 le
= Vcb
->devices
.Flink
;
3535 while (le
!= &Vcb
->devices
) {
3536 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
3538 if (dev
->devobj
&& dev
->removable
) {
3540 IO_STATUS_BLOCK iosb
;
3542 Status
= dev_ioctl(dev
->devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
3544 if (IoIsErrorUserInduced(Status
)) {
3545 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status
);
3547 } else if (!NT_SUCCESS(Status
)) {
3548 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status
);
3550 } else if (iosb
.Information
< sizeof(ULONG
)) {
3551 ERR("iosb.Information was too short\n");
3552 Status
= STATUS_INTERNAL_ERROR
;
3553 } else if (cc
!= dev
->change_count
) {
3554 dev
->devobj
->Flags
|= DO_VERIFY_VOLUME
;
3562 Status
= STATUS_SUCCESS
;
3565 ExReleaseResourceLite(&Vcb
->tree_lock
);
3568 PDEVICE_OBJECT devobj
;
3570 devobj
= IoGetDeviceToVerify(Irp
->Tail
.Overlay
.Thread
);
3571 IoSetDeviceToVerify(Irp
->Tail
.Overlay
.Thread
, NULL
);
3574 devobj
= IoGetDeviceToVerify(PsGetCurrentThread());
3575 IoSetDeviceToVerify(PsGetCurrentThread(), NULL
);
3578 devobj
= Vcb
->Vpb
? Vcb
->Vpb
->RealDevice
: NULL
;
3581 Status
= IoVerifyVolume(devobj
, FALSE
);
3583 Status
= STATUS_VERIFY_REQUIRED
;
3589 static BOOL
has_manage_volume_privilege(ACCESS_STATE
* access_state
, KPROCESSOR_MODE processor_mode
) {
3590 PRIVILEGE_SET privset
;
3592 privset
.PrivilegeCount
= 1;
3593 privset
.Control
= PRIVILEGE_SET_ALL_NECESSARY
;
3594 privset
.Privilege
[0].Luid
= RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
);
3595 privset
.Privilege
[0].Attributes
= 0;
3597 return SePrivilegeCheck(&privset
, &access_state
->SubjectSecurityContext
, processor_mode
) ? TRUE
: FALSE
;
3600 _Dispatch_type_(IRP_MJ_CREATE
)
3601 _Function_class_(DRIVER_DISPATCH
)
3602 NTSTATUS
drv_create(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3604 PIO_STACK_LOCATION IrpSp
;
3605 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3606 BOOL top_level
, locked
= FALSE
;
3608 FsRtlEnterFileSystem();
3610 TRACE("create (flags = %x)\n", Irp
->Flags
);
3612 top_level
= is_top_level(Irp
);
3614 /* return success if just called for FS device object */
3615 if (DeviceObject
== master_devobj
) {
3616 TRACE("create called for FS device object\n");
3618 Irp
->IoStatus
.Information
= FILE_OPENED
;
3619 Status
= STATUS_SUCCESS
;
3622 } else if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
3623 Status
= vol_create(DeviceObject
, Irp
);
3625 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
3626 Status
= STATUS_INVALID_PARAMETER
;
3630 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
)) {
3631 Status
= STATUS_DEVICE_NOT_READY
;
3635 if (Vcb
->removing
) {
3636 Status
= STATUS_ACCESS_DENIED
;
3640 Status
= verify_vcb(Vcb
, Irp
);
3641 if (!NT_SUCCESS(Status
)) {
3642 ERR("verify_vcb returned %08x\n", Status
);
3646 ExAcquireResourceSharedLite(&Vcb
->load_lock
, TRUE
);
3649 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3651 if (IrpSp
->Flags
!= 0) {
3652 UINT32 flags
= IrpSp
->Flags
;
3656 if (flags
& SL_CASE_SENSITIVE
) {
3657 TRACE("SL_CASE_SENSITIVE\n");
3658 flags
&= ~SL_CASE_SENSITIVE
;
3661 if (flags
& SL_FORCE_ACCESS_CHECK
) {
3662 TRACE("SL_FORCE_ACCESS_CHECK\n");
3663 flags
&= ~SL_FORCE_ACCESS_CHECK
;
3666 if (flags
& SL_OPEN_PAGING_FILE
) {
3667 TRACE("SL_OPEN_PAGING_FILE\n");
3668 flags
&= ~SL_OPEN_PAGING_FILE
;
3671 if (flags
& SL_OPEN_TARGET_DIRECTORY
) {
3672 TRACE("SL_OPEN_TARGET_DIRECTORY\n");
3673 flags
&= ~SL_OPEN_TARGET_DIRECTORY
;
3676 if (flags
& SL_STOP_ON_SYMLINK
) {
3677 TRACE("SL_STOP_ON_SYMLINK\n");
3678 flags
&= ~SL_STOP_ON_SYMLINK
;
3682 WARN("unknown flags: %x\n", flags
);
3684 TRACE("flags: (none)\n");
3687 if (!IrpSp
->FileObject
) {
3688 ERR("FileObject was NULL\n");
3689 Status
= STATUS_INVALID_PARAMETER
;
3693 if (IrpSp
->FileObject
->RelatedFileObject
) {
3694 fcb
* relatedfcb
= IrpSp
->FileObject
->RelatedFileObject
->FsContext
;
3696 if (relatedfcb
&& relatedfcb
->Vcb
!= Vcb
) {
3697 WARN("RelatedFileObject was for different device\n");
3698 Status
= STATUS_INVALID_PARAMETER
;
3704 if (IrpSp
->FileObject
->FileName
.Length
== 0 && !IrpSp
->FileObject
->RelatedFileObject
) {
3705 ULONG RequestedDisposition
= ((IrpSp
->Parameters
.Create
.Options
>> 24) & 0xff);
3706 ULONG RequestedOptions
= IrpSp
->Parameters
.Create
.Options
& FILE_VALID_OPTION_FLAGS
;
3707 #ifdef DEBUG_FCB_REFCOUNTS
3712 TRACE("open operation for volume\n");
3714 if (RequestedDisposition
!= FILE_OPEN
&& RequestedDisposition
!= FILE_OPEN_IF
) {
3715 Status
= STATUS_ACCESS_DENIED
;
3719 if (RequestedOptions
& FILE_DIRECTORY_FILE
) {
3720 Status
= STATUS_NOT_A_DIRECTORY
;
3724 ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ccb
), ALLOC_TAG
);
3726 ERR("out of memory\n");
3727 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3731 RtlZeroMemory(ccb
, sizeof(*ccb
));
3733 ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
3734 ccb
->NodeSize
= sizeof(*ccb
);
3735 ccb
->disposition
= RequestedDisposition
;
3736 ccb
->options
= RequestedOptions
;
3737 ccb
->access
= IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
->PreviouslyGrantedAccess
;
3738 ccb
->manage_volume_privilege
= has_manage_volume_privilege(IrpSp
->Parameters
.Create
.SecurityContext
->AccessState
,
3739 IrpSp
->Flags
& SL_FORCE_ACCESS_CHECK
? UserMode
: Irp
->RequestorMode
);
3740 ccb
->reserving
= FALSE
;
3741 ccb
->lxss
= called_from_lxss();
3743 #ifdef DEBUG_FCB_REFCOUNTS
3744 rc
= InterlockedIncrement(&Vcb
->volume_fcb
->refcount
);
3745 WARN("fcb %p: refcount now %i (volume)\n", Vcb
->volume_fcb
, rc
);
3747 InterlockedIncrement(&Vcb
->volume_fcb
->refcount
);
3749 IrpSp
->FileObject
->FsContext
= Vcb
->volume_fcb
;
3750 IrpSp
->FileObject
->FsContext2
= ccb
;
3752 IrpSp
->FileObject
->SectionObjectPointer
= &Vcb
->volume_fcb
->nonpaged
->segment_object
;
3754 if (!IrpSp
->FileObject
->Vpb
)
3755 IrpSp
->FileObject
->Vpb
= DeviceObject
->Vpb
;
3757 InterlockedIncrement(&Vcb
->open_files
);
3759 Irp
->IoStatus
.Information
= FILE_OPENED
;
3760 Status
= STATUS_SUCCESS
;
3762 LIST_ENTRY rollback
;
3765 InitializeListHead(&rollback
);
3767 TRACE("file name: %.*S\n", IrpSp
->FileObject
->FileName
.Length
/ sizeof(WCHAR
), IrpSp
->FileObject
->FileName
.Buffer
);
3769 if (IrpSp
->FileObject
->RelatedFileObject
)
3770 TRACE("related file = %S\n", file_desc(IrpSp
->FileObject
->RelatedFileObject
));
3772 // Don't lock again if we're being called from within CcCopyRead etc.
3773 skip_lock
= ExIsResourceAcquiredExclusiveLite(&Vcb
->tree_lock
);
3776 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
3778 Status
= open_file(DeviceObject
, Vcb
, Irp
, &rollback
);
3780 if (!NT_SUCCESS(Status
))
3781 do_rollback(Vcb
, &rollback
);
3783 clear_rollback(&rollback
);
3786 ExReleaseResourceLite(&Vcb
->tree_lock
);
3790 Irp
->IoStatus
.Status
= Status
;
3791 IoCompleteRequest( Irp
, NT_SUCCESS(Status
) ? IO_DISK_INCREMENT
: IO_NO_INCREMENT
);
3793 TRACE("create returning %08x\n", Status
);
3796 ExReleaseResourceLite(&Vcb
->load_lock
);
3799 IoSetTopLevelIrp(NULL
);
3801 FsRtlExitFileSystem();