1 /* Copyright (c) Mark Harmstone 2016
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/>. */
18 #include "btrfs_drv.h"
30 enum DirEntryType dir_entry_type
;
33 ULONG STDCALL
get_reparse_tag(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT8 type
, ULONG atts
, PIRP Irp
) {
38 if (type
== BTRFS_TYPE_SYMLINK
) {
39 if (called_from_lxss())
40 return IO_REPARSE_TAG_LXSS_SYMLINK
;
42 return IO_REPARSE_TAG_SYMLINK
;
45 if (type
!= BTRFS_TYPE_FILE
&& type
!= BTRFS_TYPE_DIRECTORY
)
48 if (!(atts
& FILE_ATTRIBUTE_REPARSE_POINT
))
51 Status
= open_fcb(Vcb
, subvol
, inode
, type
, NULL
, NULL
, &fcb
, PagedPool
, Irp
);
52 if (!NT_SUCCESS(Status
)) {
53 ERR("open_fcb returned %08x\n", Status
);
57 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
59 if (type
== BTRFS_TYPE_DIRECTORY
) {
60 if (!fcb
->reparse_xattr
.Buffer
|| fcb
->reparse_xattr
.Length
< sizeof(ULONG
))
63 RtlCopyMemory(&tag
, fcb
->reparse_xattr
.Buffer
, sizeof(ULONG
));
65 Status
= read_file(fcb
, (UINT8
*)&tag
, 0, sizeof(ULONG
), &br
, NULL
, TRUE
);
66 if (!NT_SUCCESS(Status
)) {
67 ERR("read_file returned %08x\n", Status
);
71 if (br
< sizeof(ULONG
))
76 ExReleaseResourceLite(fcb
->Header
.Resource
);
83 static ULONG
get_ea_len(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, PIRP Irp
) {
87 if (get_xattr(Vcb
, subvol
, inode
, EA_EA
, EA_EA_HASH
, &eadata
, &len
, Irp
)) {
91 Status
= IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION
*)eadata
, len
, &offset
);
93 if (!NT_SUCCESS(Status
)) {
94 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
98 FILE_FULL_EA_INFORMATION
* eainfo
;
102 eainfo
= (FILE_FULL_EA_INFORMATION
*)eadata
;
104 ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
106 if (eainfo
->NextEntryOffset
== 0)
109 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
120 static NTSTATUS STDCALL
query_dir_item(fcb
* fcb
, file_ref
* fileref
, void* buf
, LONG
* len
, PIRP Irp
, dir_entry
* de
, root
* r
) {
121 PIO_STACK_LOCATION IrpSp
;
128 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
130 if (de
->key
.obj_type
== TYPE_ROOT_ITEM
) { // subvol
135 le
= fcb
->Vcb
->roots
.Flink
;
136 while (le
!= &fcb
->Vcb
->roots
) {
137 root
* subvol
= CONTAINING_RECORD(le
, root
, list_entry
);
139 if (subvol
->id
== de
->key
.obj_id
) {
148 ERR("could not find root %llx\n", de
->key
.obj_id
);
149 return STATUS_OBJECT_NAME_NOT_FOUND
;
152 inode
= SUBVOL_ROOT_INODE
;
154 inode
= de
->key
.obj_id
;
157 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
!= FileNamesInformation
) { // FIXME - object ID and reparse point classes too?
158 switch (de
->dir_entry_type
) {
159 case DirEntryType_File
:
164 if (!IsListEmpty(&r
->fcbs
)) {
166 while (le
!= &r
->fcbs
) {
167 struct _fcb
* fcb2
= CONTAINING_RECORD(le
, struct _fcb
, list_entry
);
169 if (fcb2
->inode
== inode
&& !fcb2
->ads
) {
170 ii
= fcb2
->inode_item
;
175 } else if (fcb2
->inode
> inode
)
186 searchkey
.obj_id
= inode
;
187 searchkey
.obj_type
= TYPE_INODE_ITEM
;
188 searchkey
.offset
= 0xffffffffffffffff;
190 Status
= find_item(fcb
->Vcb
, r
, &tp
, &searchkey
, FALSE
, Irp
);
191 if (!NT_SUCCESS(Status
)) {
192 ERR("error - find_item returned %08x\n", Status
);
196 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
197 ERR("could not find inode item for inode %llx in root %llx\n", inode
, r
->id
);
198 return STATUS_INTERNAL_ERROR
;
201 RtlZeroMemory(&ii
, sizeof(INODE_ITEM
));
203 if (tp
.item
->size
> 0)
204 RtlCopyMemory(&ii
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
206 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileBothDirectoryInformation
||
207 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileDirectoryInformation
||
208 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileFullDirectoryInformation
||
209 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdBothDirectoryInformation
||
210 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdFullDirectoryInformation
) {
212 BOOL dotfile
= de
->name
.Length
> sizeof(WCHAR
) && de
->name
.Buffer
[0] == '.';
214 atts
= get_file_attributes(fcb
->Vcb
, &ii
, r
, inode
, de
->type
, dotfile
, FALSE
, Irp
);
217 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileBothDirectoryInformation
||
218 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileFullDirectoryInformation
||
219 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdBothDirectoryInformation
||
220 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdFullDirectoryInformation
) {
221 ealen
= get_ea_len(fcb
->Vcb
, r
, inode
, Irp
);
228 case DirEntryType_Self
:
229 ii
= fcb
->inode_item
;
236 case DirEntryType_Parent
:
237 if (fileref
&& fileref
->parent
) {
238 ii
= fileref
->parent
->fcb
->inode_item
;
239 r
= fileref
->parent
->fcb
->subvol
;
240 inode
= fileref
->parent
->fcb
->inode
;
241 atts
= fileref
->parent
->fcb
->atts
;
242 ealen
= fileref
->parent
->fcb
->ealen
;
245 return STATUS_INTERNAL_ERROR
;
251 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
252 case FileBothDirectoryInformation
:
254 FILE_BOTH_DIR_INFORMATION
* fbdi
= buf
;
256 TRACE("FileBothDirectoryInformation\n");
258 needed
= sizeof(FILE_BOTH_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
261 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
262 return STATUS_BUFFER_OVERFLOW
;
265 fbdi
->NextEntryOffset
= 0;
267 fbdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
268 fbdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
269 fbdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
270 fbdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
271 fbdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
272 fbdi
->AllocationSize
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_blocks
;
273 fbdi
->FileAttributes
= atts
;
274 fbdi
->FileNameLength
= de
->name
.Length
;
275 fbdi
->EaSize
= atts
& FILE_ATTRIBUTE_REPARSE_POINT
? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, Irp
) : ealen
;
276 fbdi
->ShortNameLength
= 0;
277 // fibdi->ShortName[12];
279 RtlCopyMemory(fbdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
283 return STATUS_SUCCESS
;
286 case FileDirectoryInformation
:
288 FILE_DIRECTORY_INFORMATION
* fdi
= buf
;
290 TRACE("FileDirectoryInformation\n");
292 needed
= sizeof(FILE_DIRECTORY_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
295 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
296 return STATUS_BUFFER_OVERFLOW
;
299 fdi
->NextEntryOffset
= 0;
301 fdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
302 fdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
303 fdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
304 fdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
305 fdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
306 fdi
->AllocationSize
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_blocks
;
307 fdi
->FileAttributes
= atts
;
308 fdi
->FileNameLength
= de
->name
.Length
;
310 RtlCopyMemory(fdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
314 return STATUS_SUCCESS
;
317 case FileFullDirectoryInformation
:
319 FILE_FULL_DIR_INFORMATION
* ffdi
= buf
;
321 TRACE("FileFullDirectoryInformation\n");
323 needed
= sizeof(FILE_FULL_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
326 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
327 return STATUS_BUFFER_OVERFLOW
;
330 ffdi
->NextEntryOffset
= 0;
332 ffdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
333 ffdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
334 ffdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
335 ffdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
336 ffdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
337 ffdi
->AllocationSize
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_blocks
;
338 ffdi
->FileAttributes
= atts
;
339 ffdi
->FileNameLength
= de
->name
.Length
;
340 ffdi
->EaSize
= atts
& FILE_ATTRIBUTE_REPARSE_POINT
? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, Irp
) : ealen
;
342 RtlCopyMemory(ffdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
346 return STATUS_SUCCESS
;
349 case FileIdBothDirectoryInformation
:
351 FILE_ID_BOTH_DIR_INFORMATION
* fibdi
= buf
;
353 TRACE("FileIdBothDirectoryInformation\n");
355 needed
= sizeof(FILE_ID_BOTH_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
358 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
359 return STATUS_BUFFER_OVERFLOW
;
363 // return STATUS_INVALID_POINTER;
365 fibdi
->NextEntryOffset
= 0;
366 fibdi
->FileIndex
= 0;
367 fibdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
368 fibdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
369 fibdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
370 fibdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
371 fibdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
372 fibdi
->AllocationSize
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_blocks
;
373 fibdi
->FileAttributes
= atts
;
374 fibdi
->FileNameLength
= de
->name
.Length
;
375 fibdi
->EaSize
= atts
& FILE_ATTRIBUTE_REPARSE_POINT
? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, Irp
) : ealen
;
376 fibdi
->ShortNameLength
= 0;
377 // fibdi->ShortName[12];
378 fibdi
->FileId
.QuadPart
= make_file_id(r
, inode
);
380 RtlCopyMemory(fibdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
384 return STATUS_SUCCESS
;
387 case FileIdFullDirectoryInformation
:
389 FILE_ID_FULL_DIR_INFORMATION
* fifdi
= buf
;
391 TRACE("FileIdFullDirectoryInformation\n");
393 needed
= sizeof(FILE_ID_FULL_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
396 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
397 return STATUS_BUFFER_OVERFLOW
;
401 // return STATUS_INVALID_POINTER;
403 fifdi
->NextEntryOffset
= 0;
404 fifdi
->FileIndex
= 0;
405 fifdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
406 fifdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
407 fifdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
408 fifdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
409 fifdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
410 fifdi
->AllocationSize
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_blocks
;
411 fifdi
->FileAttributes
= atts
;
412 fifdi
->FileNameLength
= de
->name
.Length
;
413 fifdi
->EaSize
= atts
& FILE_ATTRIBUTE_REPARSE_POINT
? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, Irp
) : ealen
;
414 fifdi
->FileId
.QuadPart
= make_file_id(r
, inode
);
416 RtlCopyMemory(fifdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
420 return STATUS_SUCCESS
;
423 case FileNamesInformation
:
425 FILE_NAMES_INFORMATION
* fni
= buf
;
427 TRACE("FileNamesInformation\n");
429 needed
= sizeof(FILE_NAMES_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
432 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
433 return STATUS_BUFFER_OVERFLOW
;
436 fni
->NextEntryOffset
= 0;
438 fni
->FileNameLength
= de
->name
.Length
;
440 RtlCopyMemory(fni
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
444 return STATUS_SUCCESS
;
447 case FileObjectIdInformation
:
448 FIXME("STUB: FileObjectIdInformation\n");
449 return STATUS_NOT_IMPLEMENTED
;
451 case FileQuotaInformation
:
452 FIXME("STUB: FileQuotaInformation\n");
453 return STATUS_NOT_IMPLEMENTED
;
455 case FileReparsePointInformation
:
456 FIXME("STUB: FileReparsePointInformation\n");
457 return STATUS_NOT_IMPLEMENTED
;
460 WARN("Unknown FileInformationClass %u\n", IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
);
461 return STATUS_NOT_IMPLEMENTED
;
464 return STATUS_NO_MORE_FILES
;
467 static NTSTATUS STDCALL
next_dir_entry(file_ref
* fileref
, UINT64
* offset
, dir_entry
* de
, dir_child
** pdc
, PIRP Irp
) {
472 dir_child
* dc2
= *pdc
;
474 if (dc2
->list_entry_index
.Flink
!= &fileref
->fcb
->dir_children_index
)
475 dc
= CONTAINING_RECORD(dc2
->list_entry_index
.Flink
, dir_child
, list_entry_index
);
482 if (fileref
->parent
) { // don't return . and .. if root directory
484 de
->key
.obj_id
= fileref
->fcb
->inode
;
485 de
->key
.obj_type
= TYPE_INODE_ITEM
;
487 de
->dir_entry_type
= DirEntryType_Self
;
488 de
->name
.Buffer
= L
".";
489 de
->name
.Length
= de
->name
.MaximumLength
= sizeof(WCHAR
);
490 de
->type
= BTRFS_TYPE_DIRECTORY
;
495 return STATUS_SUCCESS
;
496 } else if (*offset
== 1) {
497 de
->key
.obj_id
= fileref
->parent
->fcb
->inode
;
498 de
->key
.obj_type
= TYPE_INODE_ITEM
;
500 de
->dir_entry_type
= DirEntryType_Parent
;
501 de
->name
.Buffer
= L
"..";
502 de
->name
.Length
= de
->name
.MaximumLength
= sizeof(WCHAR
) * 2;
503 de
->type
= BTRFS_TYPE_DIRECTORY
;
508 return STATUS_SUCCESS
;
516 le
= fileref
->fcb
->dir_children_index
.Flink
;
518 // skip entries before offset
519 while (le
!= &fileref
->fcb
->dir_children_index
) {
520 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
522 if (dc2
->index
>= *offset
) {
532 return STATUS_NO_MORE_FILES
;
537 de
->dir_entry_type
= DirEntryType_File
;
539 *offset
= dc
->index
+ 1;
542 return STATUS_SUCCESS
;
545 static NTSTATUS STDCALL
query_directory(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
546 PIO_STACK_LOCATION IrpSp
;
547 NTSTATUS Status
, status2
;
552 UINT8
*curitem
, *lastitem
;
555 BOOL has_wildcard
= FALSE
, specific_file
= FALSE
, initial
;
556 // UINT64 num_reads_orig;
560 dir_child
* dc
= NULL
;
562 TRACE("query directory\n");
564 // get_uid(); // TESTING
566 // num_reads_orig = num_reads;
568 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
569 fcb
= IrpSp
->FileObject
->FsContext
;
570 ccb
= IrpSp
->FileObject
->FsContext2
;
571 fileref
= ccb
? ccb
->fileref
: NULL
;
576 return STATUS_INVALID_PARAMETER
;
579 ERR("ccb was NULL\n");
580 return STATUS_INVALID_PARAMETER
;
583 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_LIST_DIRECTORY
)) {
584 WARN("insufficient privileges\n");
585 return STATUS_ACCESS_DENIED
;
588 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, TRUE
);
589 ExAcquireResourceSharedLite(&fcb
->Vcb
->fcb_lock
, TRUE
);
591 TRACE("%S\n", file_desc(IrpSp
->FileObject
));
593 if (IrpSp
->Flags
== 0) {
594 TRACE("QD flags: (none)\n");
596 ULONG flags
= IrpSp
->Flags
;
598 TRACE("QD flags:\n");
600 if (flags
& SL_INDEX_SPECIFIED
) {
601 TRACE(" SL_INDEX_SPECIFIED\n");
602 flags
&= ~SL_INDEX_SPECIFIED
;
605 if (flags
& SL_RESTART_SCAN
) {
606 TRACE(" SL_RESTART_SCAN\n");
607 flags
&= ~SL_RESTART_SCAN
;
610 if (flags
& SL_RETURN_SINGLE_ENTRY
) {
611 TRACE(" SL_RETURN_SINGLE_ENTRY\n");
612 flags
&= ~SL_RETURN_SINGLE_ENTRY
;
616 TRACE(" unknown flags: %u\n", flags
);
619 initial
= !ccb
->query_string
.Buffer
;
621 if (IrpSp
->Flags
& SL_RESTART_SCAN
) {
622 ccb
->query_dir_offset
= 0;
624 if (ccb
->query_string
.Buffer
) {
625 RtlFreeUnicodeString(&ccb
->query_string
);
626 ccb
->query_string
.Buffer
= NULL
;
630 if (IrpSp
->Parameters
.QueryDirectory
.FileName
&& IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
> 1) {
631 TRACE("QD filename: %.*S\n", IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
/ sizeof(WCHAR
), IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
);
633 if (IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
[0] != '*') {
634 specific_file
= TRUE
;
635 if (!ccb
->case_sensitive
|| FsRtlDoesNameContainWildCards(IrpSp
->Parameters
.QueryDirectory
.FileName
)) {
637 specific_file
= FALSE
;
641 if (ccb
->query_string
.Buffer
)
642 RtlFreeUnicodeString(&ccb
->query_string
);
645 RtlUpcaseUnicodeString(&ccb
->query_string
, IrpSp
->Parameters
.QueryDirectory
.FileName
, TRUE
);
647 ccb
->query_string
.Buffer
= ExAllocatePoolWithTag(PagedPool
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
, ALLOC_TAG
);
648 if (!ccb
->query_string
.Buffer
) {
649 ERR("out of memory\n");
650 Status
= STATUS_INSUFFICIENT_RESOURCES
;
654 ccb
->query_string
.Length
= ccb
->query_string
.MaximumLength
= IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
;
655 RtlCopyMemory(ccb
->query_string
.Buffer
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
);
658 ccb
->has_wildcard
= has_wildcard
;
659 ccb
->specific_file
= specific_file
;
661 has_wildcard
= ccb
->has_wildcard
;
662 specific_file
= ccb
->specific_file
;
664 if (!(IrpSp
->Flags
& SL_RESTART_SCAN
)) {
668 Status
= STATUS_NO_MORE_FILES
;
674 if (ccb
->query_string
.Buffer
) {
675 TRACE("query string = %.*S\n", ccb
->query_string
.Length
/ sizeof(WCHAR
), ccb
->query_string
.Buffer
);
678 newoffset
= ccb
->query_dir_offset
;
680 ExAcquireResourceSharedLite(&fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
682 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
, Irp
);
684 if (!NT_SUCCESS(Status
)) {
685 if (Status
== STATUS_NO_MORE_FILES
&& initial
)
686 Status
= STATUS_NO_SUCH_FILE
;
690 ccb
->query_dir_offset
= newoffset
;
692 buf
= map_user_buffer(Irp
);
694 if (Irp
->MdlAddress
&& !buf
) {
695 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
696 Status
= STATUS_INSUFFICIENT_RESOURCES
;
700 length
= IrpSp
->Parameters
.QueryDirectory
.Length
;
711 if (!ccb
->case_sensitive
) {
712 Status
= RtlUpcaseUnicodeString(&us
, &ccb
->query_string
, TRUE
);
713 if (!NT_SUCCESS(Status
)) {
714 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
718 hash
= calc_crc32c(0xffffffff, (UINT8
*)us
.Buffer
, us
.Length
);
720 hash
= calc_crc32c(0xffffffff, (UINT8
*)ccb
->query_string
.Buffer
, ccb
->query_string
.Length
);
724 if (ccb
->case_sensitive
) {
725 if (fileref
->fcb
->hash_ptrs
[c
]) {
726 le
= fileref
->fcb
->hash_ptrs
[c
];
727 while (le
!= &fileref
->fcb
->dir_children_hash
) {
728 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash
);
730 if (dc2
->hash
== hash
) {
731 if (dc2
->name
.Length
== ccb
->query_string
.Length
&& RtlCompareMemory(dc2
->name
.Buffer
, ccb
->query_string
.Buffer
, ccb
->query_string
.Length
) == ccb
->query_string
.Length
) {
737 de
.dir_entry_type
= DirEntryType_File
;
741 } else if (dc2
->hash
> hash
)
748 if (fileref
->fcb
->hash_ptrs_uc
[c
]) {
749 le
= fileref
->fcb
->hash_ptrs_uc
[c
];
750 while (le
!= &fileref
->fcb
->dir_children_hash_uc
) {
751 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash_uc
);
753 if (dc2
->hash_uc
== hash
) {
754 if (dc2
->name_uc
.Length
== us
.Length
&& RtlCompareMemory(dc2
->name_uc
.Buffer
, us
.Buffer
, us
.Length
) == us
.Length
) {
760 de
.dir_entry_type
= DirEntryType_File
;
764 } else if (dc2
->hash_uc
> hash
)
773 ExFreePool(us
.Buffer
);
776 Status
= STATUS_NO_SUCH_FILE
;
779 } else if (has_wildcard
) {
780 while (!FsRtlIsNameInExpression(&ccb
->query_string
, &de
.name
, !ccb
->case_sensitive
, NULL
)) {
781 newoffset
= ccb
->query_dir_offset
;
782 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
, Irp
);
784 if (NT_SUCCESS(Status
))
785 ccb
->query_dir_offset
= newoffset
;
787 if (Status
== STATUS_NO_MORE_FILES
&& initial
)
788 Status
= STATUS_NO_SUCH_FILE
;
795 TRACE("file(0) = %.*S\n", de
.name
.Length
/ sizeof(WCHAR
), de
.name
.Buffer
);
796 TRACE("offset = %u\n", ccb
->query_dir_offset
- 1);
798 Status
= query_dir_item(fcb
, fileref
, buf
, &length
, Irp
, &de
, fcb
->subvol
);
801 if (NT_SUCCESS(Status
) && !(IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
) && !specific_file
) {
802 lastitem
= (UINT8
*)buf
;
805 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
806 case FileBothDirectoryInformation
:
807 case FileDirectoryInformation
:
808 case FileIdBothDirectoryInformation
:
809 case FileFullDirectoryInformation
:
810 case FileIdFullDirectoryInformation
:
811 length
-= length
% 8;
814 case FileNamesInformation
:
815 length
-= length
% 4;
819 WARN("unhandled file information class %u\n", IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
);
824 newoffset
= ccb
->query_dir_offset
;
825 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
, Irp
);
826 if (NT_SUCCESS(Status
)) {
827 if (!has_wildcard
|| FsRtlIsNameInExpression(&ccb
->query_string
, &de
.name
, !ccb
->case_sensitive
, NULL
)) {
828 curitem
= (UINT8
*)buf
+ IrpSp
->Parameters
.QueryDirectory
.Length
- length
;
831 TRACE("file(%u) %u = %.*S\n", count
, curitem
- (UINT8
*)buf
, de
.name
.Length
/ sizeof(WCHAR
), de
.name
.Buffer
);
832 TRACE("offset = %u\n", ccb
->query_dir_offset
- 1);
834 status2
= query_dir_item(fcb
, fileref
, curitem
, &length
, Irp
, &de
, fcb
->subvol
);
836 if (NT_SUCCESS(status2
)) {
837 ULONG
* lastoffset
= (ULONG
*)lastitem
;
839 *lastoffset
= (ULONG
)(curitem
- lastitem
);
840 ccb
->query_dir_offset
= newoffset
;
846 ccb
->query_dir_offset
= newoffset
;
848 if (Status
== STATUS_NO_MORE_FILES
)
849 Status
= STATUS_SUCCESS
;
858 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.QueryDirectory
.Length
- length
;
861 ExReleaseResourceLite(&fileref
->fcb
->nonpaged
->dir_children_lock
);
864 ExReleaseResourceLite(&fcb
->Vcb
->fcb_lock
);
865 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
867 TRACE("returning %08x\n", Status
);
870 ExFreePool(utf8
.Buffer
);
875 static NTSTATUS STDCALL
notify_change_directory(device_extension
* Vcb
, PIRP Irp
) {
876 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
877 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
878 fcb
* fcb
= FileObject
->FsContext
;
879 ccb
* ccb
= FileObject
->FsContext2
;
880 file_ref
* fileref
= ccb
->fileref
;
883 TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
886 ERR("ccb was NULL\n");
887 return STATUS_INVALID_PARAMETER
;
892 return STATUS_INVALID_PARAMETER
;
895 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_LIST_DIRECTORY
)) {
896 WARN("insufficient privileges\n");
897 return STATUS_ACCESS_DENIED
;
900 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, TRUE
);
901 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
903 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
904 Status
= STATUS_INVALID_PARAMETER
;
908 // FIXME - raise exception if FCB marked for deletion?
910 TRACE("%S\n", file_desc(FileObject
));
912 if (ccb
->filename
.Length
== 0) {
913 Status
= fileref_get_filename(fileref
, &ccb
->filename
, NULL
);
914 if (!NT_SUCCESS(Status
)) {
915 ERR("fileref_get_filename returned %08x\n", Status
);
920 FsRtlNotifyFilterChangeDirectory(Vcb
->NotifySync
, &Vcb
->DirNotifyList
, FileObject
->FsContext2
, (PSTRING
)&ccb
->filename
,
921 IrpSp
->Flags
& SL_WATCH_TREE
, FALSE
, IrpSp
->Parameters
.NotifyDirectory
.CompletionFilter
, Irp
,
924 Status
= STATUS_PENDING
;
927 ExReleaseResourceLite(fcb
->Header
.Resource
);
928 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
933 NTSTATUS STDCALL
drv_directory_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
934 PIO_STACK_LOCATION IrpSp
;
938 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
940 TRACE("directory control\n");
942 FsRtlEnterFileSystem();
944 top_level
= is_top_level(Irp
);
946 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
947 Status
= part0_passthrough(DeviceObject
, Irp
);
951 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
953 Irp
->IoStatus
.Information
= 0;
955 func
= IrpSp
->MinorFunction
;
958 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
959 Status
= notify_change_directory(Vcb
, Irp
);
962 case IRP_MN_QUERY_DIRECTORY
:
963 Status
= query_directory(DeviceObject
, Irp
);
967 WARN("unknown minor %u\n", func
);
968 Status
= STATUS_NOT_IMPLEMENTED
;
969 Irp
->IoStatus
.Status
= Status
;
973 if (Status
== STATUS_PENDING
)
976 Irp
->IoStatus
.Status
= Status
;
978 // if (Irp->UserIosb)
979 // *Irp->UserIosb = Irp->IoStatus;
981 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
985 IoSetTopLevelIrp(NULL
);
987 FsRtlExitFileSystem();