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"
31 enum DirEntryType dir_entry_type
;
34 ULONG STDCALL
get_reparse_tag(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT8 type
) {
38 // FIXME - will this slow things down?
40 if (type
== BTRFS_TYPE_SYMLINK
)
41 return IO_REPARSE_TAG_SYMLINK
;
43 if (type
!= BTRFS_TYPE_FILE
&& type
!= BTRFS_TYPE_DIRECTORY
)
46 att
= get_file_attributes(Vcb
, NULL
, subvol
, inode
, type
, FALSE
, FALSE
);
48 if (!(att
& FILE_ATTRIBUTE_REPARSE_POINT
))
51 if (type
== BTRFS_TYPE_DIRECTORY
) {
55 if (!get_xattr(Vcb
, subvol
, inode
, EA_REPARSE
, EA_REPARSE_HASH
, &data
, &datalen
))
61 if (datalen
< sizeof(ULONG
)) {
66 RtlCopyMemory(&tag
, data
, sizeof(ULONG
));
70 // FIXME - see if file loaded and cached, and do CcCopyRead if it is
72 Status
= read_file(Vcb
, subvol
, inode
, (UINT8
*)&tag
, 0, sizeof(ULONG
), &br
);
74 if (!NT_SUCCESS(Status
)) {
75 ERR("read_file returned %08x\n", Status
);
79 if (br
< sizeof(ULONG
))
86 static NTSTATUS STDCALL
query_dir_item(fcb
* fcb
, file_ref
* fileref
, void* buf
, LONG
* len
, PIRP Irp
, dir_entry
* de
, root
* r
) {
87 PIO_STACK_LOCATION IrpSp
;
95 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
97 if (de
->key
.obj_type
== TYPE_ROOT_ITEM
) { // subvol
102 le
= fcb
->Vcb
->roots
.Flink
;
103 while (le
!= &fcb
->Vcb
->roots
) {
104 root
* subvol
= CONTAINING_RECORD(le
, root
, list_entry
);
106 if (subvol
->id
== de
->key
.obj_id
) {
115 ERR("could not find root %llx\n", de
->key
.obj_id
);
116 return STATUS_OBJECT_NAME_NOT_FOUND
;
119 inode
= SUBVOL_ROOT_INODE
;
121 inode
= de
->key
.obj_id
;
124 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
!= FileNamesInformation
) { // FIXME - object ID and reparse point classes too?
125 switch (de
->dir_entry_type
) {
126 case DirEntryType_File
:
132 ExAcquireResourceSharedLite(&fcb
->Vcb
->fcb_lock
, TRUE
);
134 le
= fileref
->children
.Flink
;
135 while (le
!= &fileref
->children
) {
136 file_ref
* c
= CONTAINING_RECORD(le
, file_ref
, list_entry
);
138 if (c
->fcb
->subvol
== r
&& c
->fcb
->inode
== inode
&& !c
->fcb
->ads
) {
139 ii
= c
->fcb
->inode_item
;
147 ExReleaseResourceLite(&fcb
->Vcb
->fcb_lock
);
154 searchkey
.obj_id
= inode
;
155 searchkey
.obj_type
= TYPE_INODE_ITEM
;
156 searchkey
.offset
= 0xffffffffffffffff;
158 Status
= find_item(fcb
->Vcb
, r
, &tp
, &searchkey
, FALSE
);
159 if (!NT_SUCCESS(Status
)) {
160 ERR("error - find_item returned %08x\n", Status
);
164 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
165 ERR("could not find inode item for inode %llx in root %llx\n", inode
, r
->id
);
166 return STATUS_INTERNAL_ERROR
;
169 RtlZeroMemory(&ii
, sizeof(INODE_ITEM
));
171 if (tp
.item
->size
> 0)
172 RtlCopyMemory(&ii
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
178 case DirEntryType_Self
:
179 ii
= fcb
->inode_item
;
184 case DirEntryType_Parent
:
185 if (fileref
&& fileref
->parent
) {
186 ii
= fileref
->parent
->fcb
->inode_item
;
187 r
= fileref
->parent
->fcb
->subvol
;
188 inode
= fileref
->parent
->fcb
->inode
;
191 return STATUS_INTERNAL_ERROR
;
197 // FICs which return the filename
198 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileBothDirectoryInformation
||
199 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileDirectoryInformation
||
200 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileFullDirectoryInformation
||
201 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdBothDirectoryInformation
||
202 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileNamesInformation
) {
204 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, de
->name
, de
->namelen
);
205 if (!NT_SUCCESS(Status
)) {
206 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
211 dotfile
= de
->name
[0] == '.' && (de
->name
[1] != '.' || de
->name
[2] != 0) && (de
->name
[1] != 0);
213 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
214 case FileBothDirectoryInformation
:
216 FILE_BOTH_DIR_INFORMATION
* fbdi
= buf
;
218 TRACE("FileBothDirectoryInformation\n");
220 needed
= sizeof(FILE_BOTH_DIR_INFORMATION
) - sizeof(WCHAR
) + stringlen
;
223 WARN("buffer overflow - %u > %u\n", needed
, *len
);
224 return STATUS_BUFFER_OVERFLOW
;
227 fbdi
->NextEntryOffset
= 0;
229 fbdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
230 fbdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
231 fbdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
232 fbdi
->ChangeTime
.QuadPart
= 0;
233 fbdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
234 fbdi
->AllocationSize
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_blocks
;
235 fbdi
->FileAttributes
= get_file_attributes(fcb
->Vcb
, &ii
, r
, inode
, de
->type
, dotfile
, FALSE
);
236 fbdi
->FileNameLength
= stringlen
;
237 fbdi
->EaSize
= get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
);
238 fbdi
->ShortNameLength
= 0;
239 // fibdi->ShortName[12];
241 Status
= RtlUTF8ToUnicodeN(fbdi
->FileName
, stringlen
, &stringlen
, de
->name
, de
->namelen
);
243 if (!NT_SUCCESS(Status
)) {
244 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
250 return STATUS_SUCCESS
;
253 case FileDirectoryInformation
:
255 FILE_DIRECTORY_INFORMATION
* fdi
= buf
;
257 TRACE("FileDirectoryInformation\n");
259 needed
= sizeof(FILE_DIRECTORY_INFORMATION
) - sizeof(WCHAR
) + stringlen
;
262 WARN("buffer overflow - %u > %u\n", needed
, *len
);
263 return STATUS_BUFFER_OVERFLOW
;
266 fdi
->NextEntryOffset
= 0;
268 fdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
269 fdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
270 fdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
271 fdi
->ChangeTime
.QuadPart
= 0;
272 fdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
273 fdi
->AllocationSize
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_blocks
;
274 fdi
->FileAttributes
= get_file_attributes(fcb
->Vcb
, &ii
, r
, inode
, de
->type
, dotfile
, FALSE
);
275 fdi
->FileNameLength
= stringlen
;
277 Status
= RtlUTF8ToUnicodeN(fdi
->FileName
, stringlen
, &stringlen
, de
->name
, de
->namelen
);
279 if (!NT_SUCCESS(Status
)) {
280 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
286 return STATUS_SUCCESS
;
289 case FileFullDirectoryInformation
:
291 FILE_FULL_DIR_INFORMATION
* ffdi
= buf
;
293 TRACE("FileFullDirectoryInformation\n");
295 needed
= sizeof(FILE_FULL_DIR_INFORMATION
) - sizeof(WCHAR
) + stringlen
;
298 WARN("buffer overflow - %u > %u\n", needed
, *len
);
299 return STATUS_BUFFER_OVERFLOW
;
302 ffdi
->NextEntryOffset
= 0;
304 ffdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
305 ffdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
306 ffdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
307 ffdi
->ChangeTime
.QuadPart
= 0;
308 ffdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
309 ffdi
->AllocationSize
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_blocks
;
310 ffdi
->FileAttributes
= get_file_attributes(fcb
->Vcb
, &ii
, r
, inode
, de
->type
, dotfile
, FALSE
);
311 ffdi
->FileNameLength
= stringlen
;
312 ffdi
->EaSize
= get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
);
314 Status
= RtlUTF8ToUnicodeN(ffdi
->FileName
, stringlen
, &stringlen
, de
->name
, de
->namelen
);
316 if (!NT_SUCCESS(Status
)) {
317 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
323 return STATUS_SUCCESS
;
326 case FileIdBothDirectoryInformation
:
328 FILE_ID_BOTH_DIR_INFORMATION
* fibdi
= buf
;
330 TRACE("FileIdBothDirectoryInformation\n");
332 needed
= sizeof(FILE_ID_BOTH_DIR_INFORMATION
) - sizeof(WCHAR
) + stringlen
;
335 WARN("buffer overflow - %u > %u\n", needed
, *len
);
336 return STATUS_BUFFER_OVERFLOW
;
340 // return STATUS_INVALID_POINTER;
342 fibdi
->NextEntryOffset
= 0;
343 fibdi
->FileIndex
= 0;
344 fibdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
345 fibdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
346 fibdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
347 fibdi
->ChangeTime
.QuadPart
= 0;
348 fibdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
349 fibdi
->AllocationSize
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_blocks
;
350 fibdi
->FileAttributes
= get_file_attributes(fcb
->Vcb
, &ii
, r
, inode
, de
->type
, dotfile
, FALSE
);
351 fibdi
->FileNameLength
= stringlen
;
352 fibdi
->EaSize
= get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
);
353 fibdi
->ShortNameLength
= 0;
354 // fibdi->ShortName[12];
355 fibdi
->FileId
.QuadPart
= inode
;
357 Status
= RtlUTF8ToUnicodeN(fibdi
->FileName
, stringlen
, &stringlen
, de
->name
, de
->namelen
);
359 if (!NT_SUCCESS(Status
)) {
360 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
366 return STATUS_SUCCESS
;
369 case FileIdFullDirectoryInformation
:
370 FIXME("STUB: FileIdFullDirectoryInformation\n");
373 case FileNamesInformation
:
375 FILE_NAMES_INFORMATION
* fni
= buf
;
377 TRACE("FileNamesInformation\n");
379 needed
= sizeof(FILE_NAMES_INFORMATION
) - sizeof(WCHAR
) + stringlen
;
382 WARN("buffer overflow - %u > %u\n", needed
, *len
);
383 return STATUS_BUFFER_OVERFLOW
;
386 fni
->NextEntryOffset
= 0;
388 fni
->FileNameLength
= stringlen
;
390 Status
= RtlUTF8ToUnicodeN(fni
->FileName
, stringlen
, &stringlen
, de
->name
, de
->namelen
);
392 if (!NT_SUCCESS(Status
)) {
393 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
399 return STATUS_SUCCESS
;
402 case FileObjectIdInformation
:
403 FIXME("STUB: FileObjectIdInformation\n");
404 return STATUS_NOT_IMPLEMENTED
;
406 case FileQuotaInformation
:
407 FIXME("STUB: FileQuotaInformation\n");
408 return STATUS_NOT_IMPLEMENTED
;
410 case FileReparsePointInformation
:
411 FIXME("STUB: FileReparsePointInformation\n");
412 return STATUS_NOT_IMPLEMENTED
;
415 WARN("Unknown FileInformationClass %u\n", IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
);
416 return STATUS_NOT_IMPLEMENTED
;
419 return STATUS_NO_MORE_FILES
;
422 static NTSTATUS STDCALL
next_dir_entry(fcb
* fcb
, file_ref
* fileref
, UINT64
* offset
, dir_entry
* de
, traverse_ptr
* tp
) {
424 traverse_ptr next_tp
;
428 if (fileref
&& fileref
->parent
) { // don't return . and .. if root directory
430 de
->key
.obj_id
= fcb
->inode
;
431 de
->key
.obj_type
= TYPE_INODE_ITEM
;
433 de
->dir_entry_type
= DirEntryType_Self
;
436 de
->type
= BTRFS_TYPE_DIRECTORY
;
440 return STATUS_SUCCESS
;
441 } else if (*offset
== 1) {
442 de
->key
.obj_id
= fileref
->parent
->fcb
->inode
;
443 de
->key
.obj_type
= TYPE_INODE_ITEM
;
445 de
->dir_entry_type
= DirEntryType_Parent
;
448 de
->type
= BTRFS_TYPE_DIRECTORY
;
452 return STATUS_SUCCESS
;
457 searchkey
.obj_id
= fcb
->inode
;
458 searchkey
.obj_type
= TYPE_DIR_INDEX
;
459 searchkey
.offset
= *offset
;
461 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, tp
, &searchkey
, FALSE
);
462 if (!NT_SUCCESS(Status
)) {
463 ERR("error - find_item returned %08x\n", Status
);
468 TRACE("found item %llx,%x,%llx\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
);
470 if (keycmp(&tp
->item
->key
, &searchkey
) == -1) {
471 if (find_next_item(fcb
->Vcb
, tp
, &next_tp
, FALSE
)) {
474 TRACE("moving on to %llx,%x,%llx\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
);
478 if (tp
->item
->key
.obj_id
!= searchkey
.obj_id
|| tp
->item
->key
.obj_type
!= searchkey
.obj_type
|| tp
->item
->key
.offset
< *offset
) {
480 return STATUS_NO_MORE_FILES
;
483 *offset
= tp
->item
->key
.offset
+ 1;
485 di
= (DIR_ITEM
*)tp
->item
->data
;
487 if (tp
->item
->size
< sizeof(DIR_ITEM
) || tp
->item
->size
< sizeof(DIR_ITEM
) - 1 + di
->m
+ di
->n
) {
488 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
, sizeof(DIR_ITEM
));
491 return STATUS_INTERNAL_ERROR
;
498 de
->dir_entry_type
= DirEntryType_File
;
500 return STATUS_SUCCESS
;
502 if (find_next_item(fcb
->Vcb
, tp
, &next_tp
, FALSE
)) {
503 if (next_tp
.item
->key
.obj_type
== TYPE_DIR_INDEX
&& next_tp
.item
->key
.obj_id
== tp
->item
->key
.obj_id
) {
506 *offset
= tp
->item
->key
.offset
+ 1;
508 di
= (DIR_ITEM
*)tp
->item
->data
;
510 if (tp
->item
->size
< sizeof(DIR_ITEM
) || tp
->item
->size
< sizeof(DIR_ITEM
) - 1 + di
->m
+ di
->n
) {
511 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
, sizeof(DIR_ITEM
));
512 return STATUS_INTERNAL_ERROR
;
519 de
->dir_entry_type
= DirEntryType_File
;
521 return STATUS_SUCCESS
;
523 return STATUS_NO_MORE_FILES
;
525 return STATUS_NO_MORE_FILES
;
529 static NTSTATUS STDCALL
query_directory(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
530 PIO_STACK_LOCATION IrpSp
;
531 NTSTATUS Status
, status2
;
536 UINT8
*curitem
, *lastitem
;
539 BOOL has_wildcard
= FALSE
, specific_file
= FALSE
, initial
;
540 // UINT64 num_reads_orig;
544 TRACE("query directory\n");
546 // get_uid(); // TESTING
548 // num_reads_orig = num_reads;
550 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
551 fcb
= IrpSp
->FileObject
->FsContext
;
552 ccb
= IrpSp
->FileObject
->FsContext2
;
553 fileref
= ccb
? ccb
->fileref
: NULL
;
555 acquire_tree_lock(fcb
->Vcb
, FALSE
);
557 TRACE("%S\n", file_desc(IrpSp
->FileObject
));
559 if (IrpSp
->Flags
== 0) {
560 TRACE("QD flags: (none)\n");
562 ULONG flags
= IrpSp
->Flags
;
564 TRACE("QD flags:\n");
566 if (flags
& SL_INDEX_SPECIFIED
) {
567 TRACE(" SL_INDEX_SPECIFIED\n");
568 flags
&= ~SL_INDEX_SPECIFIED
;
571 if (flags
& SL_RESTART_SCAN
) {
572 TRACE(" SL_RESTART_SCAN\n");
573 flags
&= ~SL_RESTART_SCAN
;
576 if (flags
& SL_RETURN_SINGLE_ENTRY
) {
577 TRACE(" SL_RETURN_SINGLE_ENTRY\n");
578 flags
&= ~SL_RETURN_SINGLE_ENTRY
;
582 TRACE(" unknown flags: %u\n", flags
);
585 initial
= !ccb
->query_string
.Buffer
;
587 if (IrpSp
->Flags
& SL_RESTART_SCAN
) {
588 ccb
->query_dir_offset
= 0;
590 if (ccb
->query_string
.Buffer
) {
591 RtlFreeUnicodeString(&ccb
->query_string
);
592 ccb
->query_string
.Buffer
= NULL
;
596 if (IrpSp
->Parameters
.QueryDirectory
.FileName
) {
600 TRACE("QD filename: %.*S\n", IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
/ sizeof(WCHAR
), IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
);
602 // if (IrpSp->Parameters.QueryDirectory.FileName->Length > 1 || IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != '*') {
603 // specific_file = TRUE;
604 // for (i = 0; i < IrpSp->Parameters.QueryDirectory.FileName->Length; i++) {
605 // if (IrpSp->Parameters.QueryDirectory.FileName->Buffer[i] == '?' || IrpSp->Parameters.QueryDirectory.FileName->Buffer[i] == '*') {
606 // has_wildcard = TRUE;
607 // specific_file = FALSE;
613 if (ccb
->query_string
.Buffer
)
614 RtlFreeUnicodeString(&ccb
->query_string
);
616 // us = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length + sizeof(WCHAR), ALLOC_TAG);
617 // RtlCopyMemory(us, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length);
618 // us[IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR)] = 0;
620 // ccb->query_string = ExAllocatePoolWithTag(NonPagedPool, utf16_to_utf8_len(us), ALLOC_TAG);
621 // utf16_to_utf8(us, ccb->query_string);
623 // ccb->query_string.Buffer = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length, ALLOC_TAG);
624 // RtlCopyMemory(ccb->query_string.Buffer, IrpSp->Parameters.QueryDirectory.FileName->Buffer,
625 // IrpSp->Parameters.QueryDirectory.FileName->Length);
626 // ccb->query_string.Length = IrpSp->Parameters.QueryDirectory.FileName->Length;
627 // ccb->query_string.MaximumLength = IrpSp->Parameters.QueryDirectory.FileName->Length;
628 RtlUpcaseUnicodeString(&ccb
->query_string
, IrpSp
->Parameters
.QueryDirectory
.FileName
, TRUE
);
630 ccb
->has_wildcard
= has_wildcard
;
631 ccb
->specific_file
= specific_file
;
635 has_wildcard
= ccb
->has_wildcard
;
636 specific_file
= ccb
->specific_file
;
638 if (!(IrpSp
->Flags
& SL_RESTART_SCAN
))
642 if (ccb
->query_string
.Buffer
) {
643 TRACE("query string = %.*S\n", ccb
->query_string
.Length
/ sizeof(WCHAR
), ccb
->query_string
.Buffer
);
647 Status
= next_dir_entry(fcb
, fileref
, &ccb
->query_dir_offset
, &de
, &tp
);
649 if (!NT_SUCCESS(Status
)) {
650 if (Status
== STATUS_NO_MORE_FILES
&& initial
)
651 Status
= STATUS_NO_SUCH_FILE
;
655 // FIXME - make this work
656 // if (specific_file) {
657 // UINT64 filesubvol, fileinode;
660 // us = ExAllocatePoolWithTag(NonPagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length + sizeof(WCHAR), ALLOC_TAG);
661 // RtlCopyMemory(us, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length);
662 // us[IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR)] = 0;
664 // if (!find_file_in_dir(fcb->Vcb, us, fcb->subvolume, fcb->inode, &filesubvol, &fileinode)) {
666 // return STATUS_NO_MORE_FILES;
672 buf
= map_user_buffer(Irp
);
674 if (Irp
->MdlAddress
&& !buf
) {
675 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
676 Status
= STATUS_INSUFFICIENT_RESOURCES
;
680 length
= IrpSp
->Parameters
.QueryDirectory
.Length
;
682 // if (specific_file) {
686 UNICODE_STRING di_uni_fn
;
688 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, de
.name
, de
.namelen
);
689 if (!NT_SUCCESS(Status
)) {
690 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
694 uni_fn
= ExAllocatePoolWithTag(PagedPool
, stringlen
, ALLOC_TAG
);
696 ERR("out of memory\n");
697 Status
= STATUS_INSUFFICIENT_RESOURCES
;
701 Status
= RtlUTF8ToUnicodeN(uni_fn
, stringlen
, &stringlen
, de
.name
, de
.namelen
);
703 if (!NT_SUCCESS(Status
)) {
704 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
708 di_uni_fn
.Length
= di_uni_fn
.MaximumLength
= stringlen
;
709 di_uni_fn
.Buffer
= uni_fn
;
711 while (!FsRtlIsNameInExpression(&ccb
->query_string
, &di_uni_fn
, TRUE
, NULL
)) {
712 Status
= next_dir_entry(fcb
, fileref
, &ccb
->query_dir_offset
, &de
, &tp
);
715 if (NT_SUCCESS(Status
)) {
716 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, de
.name
, de
.namelen
);
717 if (!NT_SUCCESS(Status
)) {
718 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
722 uni_fn
= ExAllocatePoolWithTag(PagedPool
, stringlen
, ALLOC_TAG
);
724 ERR("out of memory\n");
725 Status
= STATUS_INSUFFICIENT_RESOURCES
;
729 Status
= RtlUTF8ToUnicodeN(uni_fn
, stringlen
, &stringlen
, de
.name
, de
.namelen
);
731 if (!NT_SUCCESS(Status
)) {
732 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
737 di_uni_fn
.Length
= di_uni_fn
.MaximumLength
= stringlen
;
738 di_uni_fn
.Buffer
= uni_fn
;
740 if (Status
== STATUS_NO_MORE_FILES
&& initial
)
741 Status
= STATUS_NO_SUCH_FILE
;
750 TRACE("file(0) = %.*s\n", de
.namelen
, de
.name
);
751 TRACE("offset = %u\n", ccb
->query_dir_offset
- 1);
753 Status
= query_dir_item(fcb
, fileref
, buf
, &length
, Irp
, &de
, fcb
->subvol
);
756 if (NT_SUCCESS(Status
) && !(IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
) && !specific_file
) {
757 lastitem
= (UINT8
*)buf
;
760 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
761 case FileBothDirectoryInformation
:
762 case FileDirectoryInformation
:
763 case FileIdBothDirectoryInformation
:
764 case FileFullDirectoryInformation
:
765 length
-= length
% 8;
768 case FileNamesInformation
:
769 length
-= length
% 4;
773 WARN("unhandled file information class %u\n", IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
);
778 WCHAR
* uni_fn
= NULL
;
779 UNICODE_STRING di_uni_fn
;
781 Status
= next_dir_entry(fcb
, fileref
, &ccb
->query_dir_offset
, &de
, &tp
);
782 if (NT_SUCCESS(Status
)) {
786 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &stringlen
, de
.name
, de
.namelen
);
787 if (!NT_SUCCESS(Status
)) {
788 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
792 uni_fn
= ExAllocatePoolWithTag(PagedPool
, stringlen
, ALLOC_TAG
);
794 ERR("out of memory\n");
795 Status
= STATUS_INSUFFICIENT_RESOURCES
;
799 Status
= RtlUTF8ToUnicodeN(uni_fn
, stringlen
, &stringlen
, de
.name
, de
.namelen
);
801 if (!NT_SUCCESS(Status
)) {
802 ERR("RtlUTF8ToUnicodeN returned %08x\n", Status
);
807 di_uni_fn
.Length
= di_uni_fn
.MaximumLength
= stringlen
;
808 di_uni_fn
.Buffer
= uni_fn
;
811 if (!has_wildcard
|| FsRtlIsNameInExpression(&ccb
->query_string
, &di_uni_fn
, TRUE
, NULL
)) {
812 curitem
= (UINT8
*)buf
+ IrpSp
->Parameters
.QueryDirectory
.Length
- length
;
815 TRACE("file(%u) %u = %.*s\n", count
, curitem
- (UINT8
*)buf
, de
.namelen
, de
.name
);
816 TRACE("offset = %u\n", ccb
->query_dir_offset
- 1);
818 status2
= query_dir_item(fcb
, fileref
, curitem
, &length
, Irp
, &de
, fcb
->subvol
);
820 if (NT_SUCCESS(status2
)) {
821 ULONG
* lastoffset
= (ULONG
*)lastitem
;
823 *lastoffset
= (ULONG
)(curitem
- lastitem
);
827 if (uni_fn
) ExFreePool(uni_fn
);
838 if (Status
== STATUS_NO_MORE_FILES
)
839 Status
= STATUS_SUCCESS
;
848 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.QueryDirectory
.Length
- length
;
851 release_tree_lock(fcb
->Vcb
, FALSE
);
853 // TRACE("query directory performed %u reads\n", (UINT32)(num_reads-num_reads_orig));
854 TRACE("returning %08x\n", Status
);
859 static NTSTATUS STDCALL
notify_change_directory(device_extension
* Vcb
, PIRP Irp
) {
860 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
861 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
862 fcb
* fcb
= FileObject
->FsContext
;
863 ccb
* ccb
= FileObject
->FsContext2
;
864 file_ref
* fileref
= ccb
->fileref
;
866 // WCHAR fn[MAX_PATH];
868 TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
872 return STATUS_INVALID_PARAMETER
;
875 acquire_tree_lock(fcb
->Vcb
, FALSE
);
877 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
878 Status
= STATUS_INVALID_PARAMETER
;
882 // FIXME - raise exception if FCB marked for deletion?
884 TRACE("%S\n", file_desc(FileObject
));
886 FsRtlNotifyFullChangeDirectory(Vcb
->NotifySync
, &Vcb
->DirNotifyList
, FileObject
->FsContext2
, (PSTRING
)&fileref
->full_filename
,
887 IrpSp
->Flags
& SL_WATCH_TREE
, FALSE
, IrpSp
->Parameters
.NotifyDirectory
.CompletionFilter
, Irp
, NULL
, NULL
);
889 Status
= STATUS_PENDING
;
892 release_tree_lock(fcb
->Vcb
, FALSE
);
897 NTSTATUS STDCALL
drv_directory_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
898 PIO_STACK_LOCATION IrpSp
;
903 TRACE("directory control\n");
905 FsRtlEnterFileSystem();
907 top_level
= is_top_level(Irp
);
909 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
911 Irp
->IoStatus
.Information
= 0;
913 func
= IrpSp
->MinorFunction
;
916 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
917 Status
= notify_change_directory(DeviceObject
->DeviceExtension
, Irp
);
920 case IRP_MN_QUERY_DIRECTORY
:
921 Status
= query_directory(DeviceObject
, Irp
);
925 WARN("unknown minor %u\n", func
);
926 Status
= STATUS_NOT_IMPLEMENTED
;
927 Irp
->IoStatus
.Status
= Status
;
931 if (func
!= IRP_MN_NOTIFY_CHANGE_DIRECTORY
|| Status
!= STATUS_PENDING
) {
932 Irp
->IoStatus
.Status
= Status
;
935 *Irp
->UserIosb
= Irp
->IoStatus
;
937 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
941 IoSetTopLevelIrp(NULL
);
943 FsRtlExitFileSystem();