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/>. */
18 #include "btrfs_drv.h"
30 enum DirEntryType dir_entry_type
;
34 ULONG
get_reparse_tag_fcb(fcb
* fcb
) {
37 if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
38 return IO_REPARSE_TAG_SYMLINK
;
39 else if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
40 if (!fcb
->reparse_xattr
.Buffer
|| fcb
->reparse_xattr
.Length
< sizeof(ULONG
))
43 RtlCopyMemory(&tag
, fcb
->reparse_xattr
.Buffer
, sizeof(ULONG
));
48 Status
= read_file(fcb
, (UINT8
*)&tag
, 0, sizeof(ULONG
), &br
, NULL
);
49 if (!NT_SUCCESS(Status
)) {
50 ERR("read_file returned %08x\n", Status
);
58 ULONG
get_reparse_tag(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT8 type
, ULONG atts
, BOOL lxss
, PIRP Irp
) {
63 if (type
== BTRFS_TYPE_SYMLINK
)
64 return IO_REPARSE_TAG_SYMLINK
;
66 if (type
== BTRFS_TYPE_SOCKET
)
67 return IO_REPARSE_TAG_LXSS_SOCKET
;
68 else if (type
== BTRFS_TYPE_FIFO
)
69 return IO_REPARSE_TAG_LXSS_FIFO
;
70 else if (type
== BTRFS_TYPE_CHARDEV
)
71 return IO_REPARSE_TAG_LXSS_CHARDEV
;
72 else if (type
== BTRFS_TYPE_BLOCKDEV
)
73 return IO_REPARSE_TAG_LXSS_BLOCKDEV
;
76 if (type
!= BTRFS_TYPE_FILE
&& type
!= BTRFS_TYPE_DIRECTORY
)
79 if (!(atts
& FILE_ATTRIBUTE_REPARSE_POINT
))
82 Status
= open_fcb(Vcb
, subvol
, inode
, type
, NULL
, NULL
, &fcb
, PagedPool
, Irp
);
83 if (!NT_SUCCESS(Status
)) {
84 ERR("open_fcb returned %08x\n", Status
);
88 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
90 tag
= get_reparse_tag_fcb(fcb
);
92 ExReleaseResourceLite(fcb
->Header
.Resource
);
99 static ULONG
get_ea_len(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, PIRP Irp
) {
103 if (get_xattr(Vcb
, subvol
, inode
, EA_EA
, EA_EA_HASH
, &eadata
, &len
, Irp
)) {
107 Status
= IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION
*)eadata
, len
, &offset
);
109 if (!NT_SUCCESS(Status
)) {
110 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
114 FILE_FULL_EA_INFORMATION
* eainfo
;
118 eainfo
= (FILE_FULL_EA_INFORMATION
*)eadata
;
120 ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
122 if (eainfo
->NextEntryOffset
== 0)
125 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
136 static NTSTATUS
query_dir_item(fcb
* fcb
, ccb
* ccb
, void* buf
, LONG
* len
, PIRP Irp
, dir_entry
* de
, root
* r
) {
137 PIO_STACK_LOCATION IrpSp
;
142 ULONG atts
= 0, ealen
= 0;
143 file_ref
* fileref
= ccb
->fileref
;
145 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
147 if (de
->key
.obj_type
== TYPE_ROOT_ITEM
) { // subvol
152 le
= fcb
->Vcb
->roots
.Flink
;
153 while (le
!= &fcb
->Vcb
->roots
) {
154 root
* subvol
= CONTAINING_RECORD(le
, root
, list_entry
);
156 if (subvol
->id
== de
->key
.obj_id
) {
164 if (r
&& r
->parent
!= fcb
->subvol
->id
)
167 inode
= SUBVOL_ROOT_INODE
;
169 inode
= de
->key
.obj_id
;
172 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
!= FileNamesInformation
) { // FIXME - object ID and reparse point classes too?
173 switch (de
->dir_entry_type
) {
174 case DirEntryType_File
:
179 ii
= fcb
->Vcb
->dummy_fcb
->inode_item
;
180 atts
= fcb
->Vcb
->dummy_fcb
->atts
;
181 ealen
= fcb
->Vcb
->dummy_fcb
->ealen
;
183 KeQuerySystemTime(&time
);
184 win_time_to_unix(time
, &ii
.otime
);
185 ii
.st_atime
= ii
.st_mtime
= ii
.st_ctime
= ii
.otime
;
189 if (de
->dc
&& de
->dc
->fileref
&& de
->dc
->fileref
->fcb
) {
190 ii
= de
->dc
->fileref
->fcb
->inode_item
;
191 atts
= de
->dc
->fileref
->fcb
->atts
;
192 ealen
= de
->dc
->fileref
->fcb
->ealen
;
200 searchkey
.obj_id
= inode
;
201 searchkey
.obj_type
= TYPE_INODE_ITEM
;
202 searchkey
.offset
= 0xffffffffffffffff;
204 Status
= find_item(fcb
->Vcb
, r
, &tp
, &searchkey
, FALSE
, Irp
);
205 if (!NT_SUCCESS(Status
)) {
206 ERR("error - find_item returned %08x\n", Status
);
210 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
211 ERR("could not find inode item for inode %llx in root %llx\n", inode
, r
->id
);
212 return STATUS_INTERNAL_ERROR
;
215 RtlZeroMemory(&ii
, sizeof(INODE_ITEM
));
217 if (tp
.item
->size
> 0)
218 RtlCopyMemory(&ii
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
220 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileBothDirectoryInformation
||
221 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileDirectoryInformation
||
222 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileFullDirectoryInformation
||
223 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdBothDirectoryInformation
||
224 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdFullDirectoryInformation
) {
226 BOOL dotfile
= de
->name
.Length
> sizeof(WCHAR
) && de
->name
.Buffer
[0] == '.';
228 atts
= get_file_attributes(fcb
->Vcb
, r
, inode
, de
->type
, dotfile
, FALSE
, Irp
);
231 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileBothDirectoryInformation
||
232 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileFullDirectoryInformation
||
233 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdBothDirectoryInformation
||
234 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdFullDirectoryInformation
) {
235 ealen
= get_ea_len(fcb
->Vcb
, r
, inode
, Irp
);
243 case DirEntryType_Self
:
244 ii
= fcb
->inode_item
;
251 case DirEntryType_Parent
:
252 if (fileref
&& fileref
->parent
) {
253 ii
= fileref
->parent
->fcb
->inode_item
;
254 r
= fileref
->parent
->fcb
->subvol
;
255 inode
= fileref
->parent
->fcb
->inode
;
256 atts
= fileref
->parent
->fcb
->atts
;
257 ealen
= fileref
->parent
->fcb
->ealen
;
260 return STATUS_INTERNAL_ERROR
;
266 atts
= FILE_ATTRIBUTE_NORMAL
;
269 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
270 case FileBothDirectoryInformation
:
272 FILE_BOTH_DIR_INFORMATION
* fbdi
= buf
;
274 TRACE("FileBothDirectoryInformation\n");
276 needed
= sizeof(FILE_BOTH_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
279 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
280 return STATUS_BUFFER_OVERFLOW
;
283 fbdi
->NextEntryOffset
= 0;
285 fbdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
286 fbdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
287 fbdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
288 fbdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
289 fbdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
291 if (de
->type
== BTRFS_TYPE_SYMLINK
)
292 fbdi
->AllocationSize
.QuadPart
= 0;
293 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
294 fbdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
296 fbdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
298 fbdi
->FileAttributes
= atts
;
299 fbdi
->FileNameLength
= de
->name
.Length
;
300 fbdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
301 fbdi
->ShortNameLength
= 0;
303 RtlCopyMemory(fbdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
307 return STATUS_SUCCESS
;
310 case FileDirectoryInformation
:
312 FILE_DIRECTORY_INFORMATION
* fdi
= buf
;
314 TRACE("FileDirectoryInformation\n");
316 needed
= sizeof(FILE_DIRECTORY_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
319 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
320 return STATUS_BUFFER_OVERFLOW
;
323 fdi
->NextEntryOffset
= 0;
325 fdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
326 fdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
327 fdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
328 fdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
329 fdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
331 if (de
->type
== BTRFS_TYPE_SYMLINK
)
332 fdi
->AllocationSize
.QuadPart
= 0;
333 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
334 fdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
336 fdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
338 fdi
->FileAttributes
= atts
;
339 fdi
->FileNameLength
= de
->name
.Length
;
341 RtlCopyMemory(fdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
345 return STATUS_SUCCESS
;
348 case FileFullDirectoryInformation
:
350 FILE_FULL_DIR_INFORMATION
* ffdi
= buf
;
352 TRACE("FileFullDirectoryInformation\n");
354 needed
= sizeof(FILE_FULL_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
357 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
358 return STATUS_BUFFER_OVERFLOW
;
361 ffdi
->NextEntryOffset
= 0;
363 ffdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
364 ffdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
365 ffdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
366 ffdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
367 ffdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
369 if (de
->type
== BTRFS_TYPE_SYMLINK
)
370 ffdi
->AllocationSize
.QuadPart
= 0;
371 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
372 ffdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
374 ffdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
376 ffdi
->FileAttributes
= atts
;
377 ffdi
->FileNameLength
= de
->name
.Length
;
378 ffdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
380 RtlCopyMemory(ffdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
384 return STATUS_SUCCESS
;
387 case FileIdBothDirectoryInformation
:
389 FILE_ID_BOTH_DIR_INFORMATION
* fibdi
= buf
;
391 TRACE("FileIdBothDirectoryInformation\n");
393 needed
= sizeof(FILE_ID_BOTH_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
396 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
397 return STATUS_BUFFER_OVERFLOW
;
400 fibdi
->NextEntryOffset
= 0;
401 fibdi
->FileIndex
= 0;
402 fibdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
403 fibdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
404 fibdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
405 fibdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
406 fibdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
408 if (de
->type
== BTRFS_TYPE_SYMLINK
)
409 fibdi
->AllocationSize
.QuadPart
= 0;
410 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
411 fibdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
413 fibdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
415 fibdi
->FileAttributes
= atts
;
416 fibdi
->FileNameLength
= de
->name
.Length
;
417 fibdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
418 fibdi
->ShortNameLength
= 0;
419 fibdi
->FileId
.QuadPart
= r
? make_file_id(r
, inode
) : make_file_id(fcb
->Vcb
->dummy_fcb
->subvol
, fcb
->Vcb
->dummy_fcb
->inode
);
421 RtlCopyMemory(fibdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
425 return STATUS_SUCCESS
;
428 case FileIdFullDirectoryInformation
:
430 FILE_ID_FULL_DIR_INFORMATION
* fifdi
= buf
;
432 TRACE("FileIdFullDirectoryInformation\n");
434 needed
= sizeof(FILE_ID_FULL_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
437 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
438 return STATUS_BUFFER_OVERFLOW
;
441 fifdi
->NextEntryOffset
= 0;
442 fifdi
->FileIndex
= 0;
443 fifdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
444 fifdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
445 fifdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
446 fifdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
447 fifdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
449 if (de
->type
== BTRFS_TYPE_SYMLINK
)
450 fifdi
->AllocationSize
.QuadPart
= 0;
451 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
452 fifdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
454 fifdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
456 fifdi
->FileAttributes
= atts
;
457 fifdi
->FileNameLength
= de
->name
.Length
;
458 fifdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
459 fifdi
->FileId
.QuadPart
= r
? make_file_id(r
, inode
) : make_file_id(fcb
->Vcb
->dummy_fcb
->subvol
, fcb
->Vcb
->dummy_fcb
->inode
);
461 RtlCopyMemory(fifdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
465 return STATUS_SUCCESS
;
468 case FileNamesInformation
:
470 FILE_NAMES_INFORMATION
* fni
= buf
;
472 TRACE("FileNamesInformation\n");
474 needed
= sizeof(FILE_NAMES_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
477 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
478 return STATUS_BUFFER_OVERFLOW
;
481 fni
->NextEntryOffset
= 0;
483 fni
->FileNameLength
= de
->name
.Length
;
485 RtlCopyMemory(fni
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
489 return STATUS_SUCCESS
;
492 case FileObjectIdInformation
:
493 FIXME("STUB: FileObjectIdInformation\n");
494 return STATUS_NOT_IMPLEMENTED
;
496 case FileQuotaInformation
:
497 FIXME("STUB: FileQuotaInformation\n");
498 return STATUS_NOT_IMPLEMENTED
;
500 case FileReparsePointInformation
:
501 FIXME("STUB: FileReparsePointInformation\n");
502 return STATUS_NOT_IMPLEMENTED
;
505 WARN("Unknown FileInformationClass %u\n", IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
);
506 return STATUS_NOT_IMPLEMENTED
;
509 return STATUS_NO_MORE_FILES
;
512 static NTSTATUS
next_dir_entry(file_ref
* fileref
, UINT64
* offset
, dir_entry
* de
, dir_child
** pdc
) {
517 dir_child
* dc2
= *pdc
;
519 if (dc2
->list_entry_index
.Flink
!= &fileref
->fcb
->dir_children_index
)
520 dc
= CONTAINING_RECORD(dc2
->list_entry_index
.Flink
, dir_child
, list_entry_index
);
527 if (fileref
->parent
) { // don't return . and .. if root directory
529 de
->key
.obj_id
= fileref
->fcb
->inode
;
530 de
->key
.obj_type
= TYPE_INODE_ITEM
;
532 de
->dir_entry_type
= DirEntryType_Self
;
533 de
->name
.Buffer
= L
".";
534 de
->name
.Length
= de
->name
.MaximumLength
= sizeof(WCHAR
);
535 de
->type
= BTRFS_TYPE_DIRECTORY
;
540 return STATUS_SUCCESS
;
541 } else if (*offset
== 1) {
542 de
->key
.obj_id
= fileref
->parent
->fcb
->inode
;
543 de
->key
.obj_type
= TYPE_INODE_ITEM
;
545 de
->dir_entry_type
= DirEntryType_Parent
;
546 de
->name
.Buffer
= L
"..";
547 de
->name
.Length
= de
->name
.MaximumLength
= sizeof(WCHAR
) * 2;
548 de
->type
= BTRFS_TYPE_DIRECTORY
;
553 return STATUS_SUCCESS
;
561 le
= fileref
->fcb
->dir_children_index
.Flink
;
563 // skip entries before offset
564 while (le
!= &fileref
->fcb
->dir_children_index
) {
565 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
567 if (dc2
->index
>= *offset
) {
577 return STATUS_NO_MORE_FILES
;
582 de
->dir_entry_type
= DirEntryType_File
;
585 *offset
= dc
->index
+ 1;
588 return STATUS_SUCCESS
;
591 static NTSTATUS
query_directory(PIRP Irp
) {
592 PIO_STACK_LOCATION IrpSp
;
593 NTSTATUS Status
, status2
;
597 device_extension
* Vcb
;
599 UINT8
*curitem
, *lastitem
;
602 BOOL has_wildcard
= FALSE
, specific_file
= FALSE
, initial
;
605 dir_child
* dc
= NULL
;
607 TRACE("query directory\n");
609 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
610 fcb
= IrpSp
->FileObject
->FsContext
;
611 ccb
= IrpSp
->FileObject
->FsContext2
;
612 fileref
= ccb
? ccb
->fileref
: NULL
;
615 return STATUS_INVALID_PARAMETER
;
618 ERR("ccb was NULL\n");
619 return STATUS_INVALID_PARAMETER
;
623 ERR("fcb was NULL\n");
624 return STATUS_INVALID_PARAMETER
;
627 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_LIST_DIRECTORY
)) {
628 WARN("insufficient privileges\n");
629 return STATUS_ACCESS_DENIED
;
635 ERR("Vcb was NULL\n");
636 return STATUS_INVALID_PARAMETER
;
639 if (fileref
->fcb
== Vcb
->dummy_fcb
)
640 return STATUS_NO_MORE_FILES
;
642 if (IrpSp
->Flags
== 0) {
643 TRACE("QD flags: (none)\n");
645 ULONG flags
= IrpSp
->Flags
;
647 TRACE("QD flags:\n");
649 if (flags
& SL_INDEX_SPECIFIED
) {
650 TRACE(" SL_INDEX_SPECIFIED\n");
651 flags
&= ~SL_INDEX_SPECIFIED
;
654 if (flags
& SL_RESTART_SCAN
) {
655 TRACE(" SL_RESTART_SCAN\n");
656 flags
&= ~SL_RESTART_SCAN
;
659 if (flags
& SL_RETURN_SINGLE_ENTRY
) {
660 TRACE(" SL_RETURN_SINGLE_ENTRY\n");
661 flags
&= ~SL_RETURN_SINGLE_ENTRY
;
665 TRACE(" unknown flags: %u\n", flags
);
668 if (IrpSp
->Flags
& SL_RESTART_SCAN
) {
669 ccb
->query_dir_offset
= 0;
671 if (ccb
->query_string
.Buffer
) {
672 RtlFreeUnicodeString(&ccb
->query_string
);
673 ccb
->query_string
.Buffer
= NULL
;
676 ccb
->has_wildcard
= FALSE
;
677 ccb
->specific_file
= FALSE
;
680 initial
= !ccb
->query_string
.Buffer
;
682 if (IrpSp
->Parameters
.QueryDirectory
.FileName
&& IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
> 1) {
683 TRACE("QD filename: %.*S\n", IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
/ sizeof(WCHAR
), IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
);
685 if (IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
> sizeof(WCHAR
) || IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
[0] != L
'*') {
686 specific_file
= TRUE
;
688 if (FsRtlDoesNameContainWildCards(IrpSp
->Parameters
.QueryDirectory
.FileName
)) {
690 specific_file
= FALSE
;
694 if (ccb
->query_string
.Buffer
)
695 RtlFreeUnicodeString(&ccb
->query_string
);
698 RtlUpcaseUnicodeString(&ccb
->query_string
, IrpSp
->Parameters
.QueryDirectory
.FileName
, TRUE
);
700 ccb
->query_string
.Buffer
= ExAllocatePoolWithTag(PagedPool
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
, ALLOC_TAG
);
701 if (!ccb
->query_string
.Buffer
) {
702 ERR("out of memory\n");
703 return STATUS_INSUFFICIENT_RESOURCES
;
706 ccb
->query_string
.Length
= ccb
->query_string
.MaximumLength
= IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
;
707 RtlCopyMemory(ccb
->query_string
.Buffer
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
);
710 ccb
->has_wildcard
= has_wildcard
;
711 ccb
->specific_file
= specific_file
;
713 has_wildcard
= ccb
->has_wildcard
;
714 specific_file
= ccb
->specific_file
;
716 if (!(IrpSp
->Flags
& SL_RESTART_SCAN
)) {
720 return STATUS_NO_MORE_FILES
;
724 if (ccb
->query_string
.Buffer
) {
725 TRACE("query string = %.*S\n", ccb
->query_string
.Length
/ sizeof(WCHAR
), ccb
->query_string
.Buffer
);
728 newoffset
= ccb
->query_dir_offset
;
730 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
732 ExAcquireResourceSharedLite(&fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
734 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
);
736 if (!NT_SUCCESS(Status
)) {
737 if (Status
== STATUS_NO_MORE_FILES
&& initial
)
738 Status
= STATUS_NO_SUCH_FILE
;
742 ccb
->query_dir_offset
= newoffset
;
744 buf
= map_user_buffer(Irp
, NormalPagePriority
);
746 if (Irp
->MdlAddress
&& !buf
) {
747 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
748 Status
= STATUS_INSUFFICIENT_RESOURCES
;
752 length
= IrpSp
->Parameters
.QueryDirectory
.Length
;
763 if (!ccb
->case_sensitive
) {
764 Status
= RtlUpcaseUnicodeString(&us
, &ccb
->query_string
, TRUE
);
765 if (!NT_SUCCESS(Status
)) {
766 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
770 hash
= calc_crc32c(0xffffffff, (UINT8
*)us
.Buffer
, us
.Length
);
772 hash
= calc_crc32c(0xffffffff, (UINT8
*)ccb
->query_string
.Buffer
, ccb
->query_string
.Length
);
776 if (ccb
->case_sensitive
) {
777 if (fileref
->fcb
->hash_ptrs
[c
]) {
778 le
= fileref
->fcb
->hash_ptrs
[c
];
779 while (le
!= &fileref
->fcb
->dir_children_hash
) {
780 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash
);
782 if (dc2
->hash
== hash
) {
783 if (dc2
->name
.Length
== ccb
->query_string
.Length
&& RtlCompareMemory(dc2
->name
.Buffer
, ccb
->query_string
.Buffer
, ccb
->query_string
.Length
) == ccb
->query_string
.Length
) {
789 de
.dir_entry_type
= DirEntryType_File
;
794 } else if (dc2
->hash
> hash
)
801 if (fileref
->fcb
->hash_ptrs_uc
[c
]) {
802 le
= fileref
->fcb
->hash_ptrs_uc
[c
];
803 while (le
!= &fileref
->fcb
->dir_children_hash_uc
) {
804 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash_uc
);
806 if (dc2
->hash_uc
== hash
) {
807 if (dc2
->name_uc
.Length
== us
.Length
&& RtlCompareMemory(dc2
->name_uc
.Buffer
, us
.Buffer
, us
.Length
) == us
.Length
) {
813 de
.dir_entry_type
= DirEntryType_File
;
818 } else if (dc2
->hash_uc
> hash
)
827 ExFreePool(us
.Buffer
);
830 Status
= STATUS_NO_SUCH_FILE
;
833 } else if (has_wildcard
) {
834 while (!FsRtlIsNameInExpression(&ccb
->query_string
, &de
.name
, !ccb
->case_sensitive
, NULL
)) {
835 newoffset
= ccb
->query_dir_offset
;
836 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
);
838 if (NT_SUCCESS(Status
))
839 ccb
->query_dir_offset
= newoffset
;
841 if (Status
== STATUS_NO_MORE_FILES
&& initial
)
842 Status
= STATUS_NO_SUCH_FILE
;
849 TRACE("file(0) = %.*S\n", de
.name
.Length
/ sizeof(WCHAR
), de
.name
.Buffer
);
850 TRACE("offset = %u\n", ccb
->query_dir_offset
- 1);
852 Status
= query_dir_item(fcb
, ccb
, buf
, &length
, Irp
, &de
, fcb
->subvol
);
855 if (NT_SUCCESS(Status
) && !(IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
) && !specific_file
) {
856 lastitem
= (UINT8
*)buf
;
859 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
860 case FileBothDirectoryInformation
:
861 case FileDirectoryInformation
:
862 case FileIdBothDirectoryInformation
:
863 case FileFullDirectoryInformation
:
864 case FileIdFullDirectoryInformation
:
865 length
-= length
% 8;
868 case FileNamesInformation
:
869 length
-= length
% 4;
873 WARN("unhandled file information class %u\n", IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
);
878 newoffset
= ccb
->query_dir_offset
;
879 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
);
880 if (NT_SUCCESS(Status
)) {
881 if (!has_wildcard
|| FsRtlIsNameInExpression(&ccb
->query_string
, &de
.name
, !ccb
->case_sensitive
, NULL
)) {
882 curitem
= (UINT8
*)buf
+ IrpSp
->Parameters
.QueryDirectory
.Length
- length
;
885 TRACE("file(%u) %u = %.*S\n", count
, curitem
- (UINT8
*)buf
, de
.name
.Length
/ sizeof(WCHAR
), de
.name
.Buffer
);
886 TRACE("offset = %u\n", ccb
->query_dir_offset
- 1);
888 status2
= query_dir_item(fcb
, ccb
, curitem
, &length
, Irp
, &de
, fcb
->subvol
);
890 if (NT_SUCCESS(status2
)) {
891 ULONG
* lastoffset
= (ULONG
*)lastitem
;
893 *lastoffset
= (ULONG
)(curitem
- lastitem
);
894 ccb
->query_dir_offset
= newoffset
;
900 ccb
->query_dir_offset
= newoffset
;
902 if (Status
== STATUS_NO_MORE_FILES
)
903 Status
= STATUS_SUCCESS
;
912 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.QueryDirectory
.Length
- length
;
915 ExReleaseResourceLite(&fileref
->fcb
->nonpaged
->dir_children_lock
);
917 ExReleaseResourceLite(&Vcb
->tree_lock
);
919 TRACE("returning %08x\n", Status
);
924 static NTSTATUS
notify_change_directory(device_extension
* Vcb
, PIRP Irp
) {
925 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
926 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
927 fcb
* fcb
= FileObject
->FsContext
;
928 ccb
* ccb
= FileObject
->FsContext2
;
929 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
932 TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
935 ERR("ccb was NULL\n");
936 return STATUS_INVALID_PARAMETER
;
941 return STATUS_INVALID_PARAMETER
;
944 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_LIST_DIRECTORY
)) {
945 WARN("insufficient privileges\n");
946 return STATUS_ACCESS_DENIED
;
949 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, TRUE
);
950 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
952 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
953 Status
= STATUS_INVALID_PARAMETER
;
957 // FIXME - raise exception if FCB marked for deletion?
959 TRACE("%S\n", file_desc(FileObject
));
961 if (ccb
->filename
.Length
== 0) {
964 ccb
->filename
.MaximumLength
= ccb
->filename
.Length
= 0;
966 Status
= fileref_get_filename(fileref
, &ccb
->filename
, NULL
, &reqlen
);
967 if (Status
== STATUS_BUFFER_OVERFLOW
) {
968 ccb
->filename
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
969 if (!ccb
->filename
.Buffer
) {
970 ERR("out of memory\n");
971 Status
= STATUS_INSUFFICIENT_RESOURCES
;
975 ccb
->filename
.MaximumLength
= (UINT16
)reqlen
;
977 Status
= fileref_get_filename(fileref
, &ccb
->filename
, NULL
, &reqlen
);
978 if (!NT_SUCCESS(Status
)) {
979 ERR("fileref_get_filename returned %08x\n", Status
);
983 ERR("fileref_get_filename returned %08x\n", Status
);
988 FsRtlNotifyFilterChangeDirectory(Vcb
->NotifySync
, &Vcb
->DirNotifyList
, FileObject
->FsContext2
, (PSTRING
)&ccb
->filename
,
989 IrpSp
->Flags
& SL_WATCH_TREE
, FALSE
, IrpSp
->Parameters
.NotifyDirectory
.CompletionFilter
, Irp
,
992 Status
= STATUS_PENDING
;
995 ExReleaseResourceLite(fcb
->Header
.Resource
);
996 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
1001 _Dispatch_type_(IRP_MJ_DIRECTORY_CONTROL
)
1002 _Function_class_(DRIVER_DISPATCH
)
1003 NTSTATUS NTAPI
drv_directory_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
1004 PIO_STACK_LOCATION IrpSp
;
1008 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
1010 FsRtlEnterFileSystem();
1012 TRACE("directory control\n");
1014 top_level
= is_top_level(Irp
);
1016 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
1017 Status
= vol_directory_control(DeviceObject
, Irp
);
1019 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
1020 Status
= STATUS_INVALID_PARAMETER
;
1024 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1026 Irp
->IoStatus
.Information
= 0;
1028 func
= IrpSp
->MinorFunction
;
1031 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
1032 Status
= notify_change_directory(Vcb
, Irp
);
1035 case IRP_MN_QUERY_DIRECTORY
:
1036 Status
= query_directory(Irp
);
1040 WARN("unknown minor %u\n", func
);
1041 Status
= STATUS_NOT_IMPLEMENTED
;
1042 Irp
->IoStatus
.Status
= Status
;
1046 if (Status
== STATUS_PENDING
)
1050 Irp
->IoStatus
.Status
= Status
;
1052 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
1055 TRACE("returning %08x\n", Status
);
1058 IoSetTopLevelIrp(NULL
);
1060 FsRtlExitFileSystem();