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(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT8 type
, ULONG atts
, BOOL lxss
, PIRP Irp
) {
39 if (type
== BTRFS_TYPE_SYMLINK
) {
41 return IO_REPARSE_TAG_LXSS_SYMLINK
;
43 return IO_REPARSE_TAG_SYMLINK
;
46 if (type
!= BTRFS_TYPE_FILE
&& type
!= BTRFS_TYPE_DIRECTORY
)
49 if (!(atts
& FILE_ATTRIBUTE_REPARSE_POINT
))
52 Status
= open_fcb(Vcb
, subvol
, inode
, type
, NULL
, NULL
, &fcb
, PagedPool
, Irp
);
53 if (!NT_SUCCESS(Status
)) {
54 ERR("open_fcb returned %08x\n", Status
);
58 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
60 if (type
== BTRFS_TYPE_DIRECTORY
) {
61 if (!fcb
->reparse_xattr
.Buffer
|| fcb
->reparse_xattr
.Length
< sizeof(ULONG
))
64 RtlCopyMemory(&tag
, fcb
->reparse_xattr
.Buffer
, sizeof(ULONG
));
66 Status
= read_file(fcb
, (UINT8
*)&tag
, 0, sizeof(ULONG
), &br
, NULL
);
67 if (!NT_SUCCESS(Status
)) {
68 ERR("read_file returned %08x\n", Status
);
72 if (br
< sizeof(ULONG
))
77 ExReleaseResourceLite(fcb
->Header
.Resource
);
84 static ULONG
get_ea_len(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, PIRP Irp
) {
88 if (get_xattr(Vcb
, subvol
, inode
, EA_EA
, EA_EA_HASH
, &eadata
, &len
, Irp
)) {
92 Status
= IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION
*)eadata
, len
, &offset
);
94 if (!NT_SUCCESS(Status
)) {
95 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
99 FILE_FULL_EA_INFORMATION
* eainfo
;
103 eainfo
= (FILE_FULL_EA_INFORMATION
*)eadata
;
105 ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
107 if (eainfo
->NextEntryOffset
== 0)
110 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
121 static NTSTATUS
query_dir_item(fcb
* fcb
, ccb
* ccb
, void* buf
, LONG
* len
, PIRP Irp
, dir_entry
* de
, root
* r
) {
122 PIO_STACK_LOCATION IrpSp
;
127 ULONG atts
= 0, ealen
= 0;
128 file_ref
* fileref
= ccb
->fileref
;
130 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
132 if (de
->key
.obj_type
== TYPE_ROOT_ITEM
) { // subvol
137 le
= fcb
->Vcb
->roots
.Flink
;
138 while (le
!= &fcb
->Vcb
->roots
) {
139 root
* subvol
= CONTAINING_RECORD(le
, root
, list_entry
);
141 if (subvol
->id
== de
->key
.obj_id
) {
149 if (r
&& r
->parent
!= fcb
->subvol
->id
)
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 ii
= fcb
->Vcb
->dummy_fcb
->inode_item
;
165 atts
= fcb
->Vcb
->dummy_fcb
->atts
;
166 ealen
= fcb
->Vcb
->dummy_fcb
->ealen
;
168 KeQuerySystemTime(&time
);
169 win_time_to_unix(time
, &ii
.otime
);
170 ii
.st_atime
= ii
.st_mtime
= ii
.st_ctime
= ii
.otime
;
174 if (de
->dc
&& de
->dc
->fileref
&& de
->dc
->fileref
->fcb
) {
175 ii
= de
->dc
->fileref
->fcb
->inode_item
;
176 atts
= de
->dc
->fileref
->fcb
->atts
;
177 ealen
= de
->dc
->fileref
->fcb
->ealen
;
185 searchkey
.obj_id
= inode
;
186 searchkey
.obj_type
= TYPE_INODE_ITEM
;
187 searchkey
.offset
= 0xffffffffffffffff;
189 Status
= find_item(fcb
->Vcb
, r
, &tp
, &searchkey
, FALSE
, Irp
);
190 if (!NT_SUCCESS(Status
)) {
191 ERR("error - find_item returned %08x\n", Status
);
195 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
196 ERR("could not find inode item for inode %llx in root %llx\n", inode
, r
->id
);
197 return STATUS_INTERNAL_ERROR
;
200 RtlZeroMemory(&ii
, sizeof(INODE_ITEM
));
202 if (tp
.item
->size
> 0)
203 RtlCopyMemory(&ii
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
205 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileBothDirectoryInformation
||
206 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileDirectoryInformation
||
207 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileFullDirectoryInformation
||
208 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdBothDirectoryInformation
||
209 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdFullDirectoryInformation
) {
211 BOOL dotfile
= de
->name
.Length
> sizeof(WCHAR
) && de
->name
.Buffer
[0] == '.';
213 atts
= get_file_attributes(fcb
->Vcb
, r
, inode
, de
->type
, dotfile
, FALSE
, Irp
);
216 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileBothDirectoryInformation
||
217 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileFullDirectoryInformation
||
218 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdBothDirectoryInformation
||
219 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdFullDirectoryInformation
) {
220 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 atts
= FILE_ATTRIBUTE_NORMAL
;
254 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
255 case FileBothDirectoryInformation
:
257 FILE_BOTH_DIR_INFORMATION
* fbdi
= buf
;
259 TRACE("FileBothDirectoryInformation\n");
261 needed
= sizeof(FILE_BOTH_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
264 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
265 return STATUS_BUFFER_OVERFLOW
;
268 fbdi
->NextEntryOffset
= 0;
270 fbdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
271 fbdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
272 fbdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
273 fbdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
274 fbdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
276 if (de
->type
== BTRFS_TYPE_SYMLINK
)
277 fbdi
->AllocationSize
.QuadPart
= 0;
278 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
279 fbdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
281 fbdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
283 fbdi
->FileAttributes
= atts
;
284 fbdi
->FileNameLength
= de
->name
.Length
;
285 fbdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
286 fbdi
->ShortNameLength
= 0;
288 RtlCopyMemory(fbdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
292 return STATUS_SUCCESS
;
295 case FileDirectoryInformation
:
297 FILE_DIRECTORY_INFORMATION
* fdi
= buf
;
299 TRACE("FileDirectoryInformation\n");
301 needed
= sizeof(FILE_DIRECTORY_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
304 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
305 return STATUS_BUFFER_OVERFLOW
;
308 fdi
->NextEntryOffset
= 0;
310 fdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
311 fdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
312 fdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
313 fdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
314 fdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
316 if (de
->type
== BTRFS_TYPE_SYMLINK
)
317 fdi
->AllocationSize
.QuadPart
= 0;
318 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
319 fdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
321 fdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
323 fdi
->FileAttributes
= atts
;
324 fdi
->FileNameLength
= de
->name
.Length
;
326 RtlCopyMemory(fdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
330 return STATUS_SUCCESS
;
333 case FileFullDirectoryInformation
:
335 FILE_FULL_DIR_INFORMATION
* ffdi
= buf
;
337 TRACE("FileFullDirectoryInformation\n");
339 needed
= sizeof(FILE_FULL_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
342 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
343 return STATUS_BUFFER_OVERFLOW
;
346 ffdi
->NextEntryOffset
= 0;
348 ffdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
349 ffdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
350 ffdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
351 ffdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
352 ffdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
354 if (de
->type
== BTRFS_TYPE_SYMLINK
)
355 ffdi
->AllocationSize
.QuadPart
= 0;
356 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
357 ffdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
359 ffdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
361 ffdi
->FileAttributes
= atts
;
362 ffdi
->FileNameLength
= de
->name
.Length
;
363 ffdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
365 RtlCopyMemory(ffdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
369 return STATUS_SUCCESS
;
372 case FileIdBothDirectoryInformation
:
374 FILE_ID_BOTH_DIR_INFORMATION
* fibdi
= buf
;
376 TRACE("FileIdBothDirectoryInformation\n");
378 needed
= sizeof(FILE_ID_BOTH_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
381 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
382 return STATUS_BUFFER_OVERFLOW
;
385 fibdi
->NextEntryOffset
= 0;
386 fibdi
->FileIndex
= 0;
387 fibdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
388 fibdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
389 fibdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
390 fibdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
391 fibdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
393 if (de
->type
== BTRFS_TYPE_SYMLINK
)
394 fibdi
->AllocationSize
.QuadPart
= 0;
395 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
396 fibdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
398 fibdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
400 fibdi
->FileAttributes
= atts
;
401 fibdi
->FileNameLength
= de
->name
.Length
;
402 fibdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
403 fibdi
->ShortNameLength
= 0;
404 fibdi
->FileId
.QuadPart
= r
? make_file_id(r
, inode
) : make_file_id(fcb
->Vcb
->dummy_fcb
->subvol
, fcb
->Vcb
->dummy_fcb
->inode
);
406 RtlCopyMemory(fibdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
410 return STATUS_SUCCESS
;
413 case FileIdFullDirectoryInformation
:
415 FILE_ID_FULL_DIR_INFORMATION
* fifdi
= buf
;
417 TRACE("FileIdFullDirectoryInformation\n");
419 needed
= sizeof(FILE_ID_FULL_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
422 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
423 return STATUS_BUFFER_OVERFLOW
;
426 fifdi
->NextEntryOffset
= 0;
427 fifdi
->FileIndex
= 0;
428 fifdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
429 fifdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
430 fifdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
431 fifdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
432 fifdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
434 if (de
->type
== BTRFS_TYPE_SYMLINK
)
435 fifdi
->AllocationSize
.QuadPart
= 0;
436 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
437 fifdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
439 fifdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
441 fifdi
->FileAttributes
= atts
;
442 fifdi
->FileNameLength
= de
->name
.Length
;
443 fifdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
444 fifdi
->FileId
.QuadPart
= r
? make_file_id(r
, inode
) : make_file_id(fcb
->Vcb
->dummy_fcb
->subvol
, fcb
->Vcb
->dummy_fcb
->inode
);
446 RtlCopyMemory(fifdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
450 return STATUS_SUCCESS
;
453 case FileNamesInformation
:
455 FILE_NAMES_INFORMATION
* fni
= buf
;
457 TRACE("FileNamesInformation\n");
459 needed
= sizeof(FILE_NAMES_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
462 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
463 return STATUS_BUFFER_OVERFLOW
;
466 fni
->NextEntryOffset
= 0;
468 fni
->FileNameLength
= de
->name
.Length
;
470 RtlCopyMemory(fni
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
474 return STATUS_SUCCESS
;
477 case FileObjectIdInformation
:
478 FIXME("STUB: FileObjectIdInformation\n");
479 return STATUS_NOT_IMPLEMENTED
;
481 case FileQuotaInformation
:
482 FIXME("STUB: FileQuotaInformation\n");
483 return STATUS_NOT_IMPLEMENTED
;
485 case FileReparsePointInformation
:
486 FIXME("STUB: FileReparsePointInformation\n");
487 return STATUS_NOT_IMPLEMENTED
;
490 WARN("Unknown FileInformationClass %u\n", IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
);
491 return STATUS_NOT_IMPLEMENTED
;
494 return STATUS_NO_MORE_FILES
;
497 static NTSTATUS
next_dir_entry(file_ref
* fileref
, UINT64
* offset
, dir_entry
* de
, dir_child
** pdc
) {
502 dir_child
* dc2
= *pdc
;
504 if (dc2
->list_entry_index
.Flink
!= &fileref
->fcb
->dir_children_index
)
505 dc
= CONTAINING_RECORD(dc2
->list_entry_index
.Flink
, dir_child
, list_entry_index
);
512 if (fileref
->parent
) { // don't return . and .. if root directory
514 de
->key
.obj_id
= fileref
->fcb
->inode
;
515 de
->key
.obj_type
= TYPE_INODE_ITEM
;
517 de
->dir_entry_type
= DirEntryType_Self
;
518 de
->name
.Buffer
= L
".";
519 de
->name
.Length
= de
->name
.MaximumLength
= sizeof(WCHAR
);
520 de
->type
= BTRFS_TYPE_DIRECTORY
;
525 return STATUS_SUCCESS
;
526 } else if (*offset
== 1) {
527 de
->key
.obj_id
= fileref
->parent
->fcb
->inode
;
528 de
->key
.obj_type
= TYPE_INODE_ITEM
;
530 de
->dir_entry_type
= DirEntryType_Parent
;
531 de
->name
.Buffer
= L
"..";
532 de
->name
.Length
= de
->name
.MaximumLength
= sizeof(WCHAR
) * 2;
533 de
->type
= BTRFS_TYPE_DIRECTORY
;
538 return STATUS_SUCCESS
;
546 le
= fileref
->fcb
->dir_children_index
.Flink
;
548 // skip entries before offset
549 while (le
!= &fileref
->fcb
->dir_children_index
) {
550 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
552 if (dc2
->index
>= *offset
) {
562 return STATUS_NO_MORE_FILES
;
567 de
->dir_entry_type
= DirEntryType_File
;
570 *offset
= dc
->index
+ 1;
573 return STATUS_SUCCESS
;
576 static NTSTATUS
query_directory(PIRP Irp
) {
577 PIO_STACK_LOCATION IrpSp
;
578 NTSTATUS Status
, status2
;
582 device_extension
* Vcb
;
584 UINT8
*curitem
, *lastitem
;
587 BOOL has_wildcard
= FALSE
, specific_file
= FALSE
, initial
;
591 dir_child
* dc
= NULL
;
593 TRACE("query directory\n");
595 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
596 fcb
= IrpSp
->FileObject
->FsContext
;
597 ccb
= IrpSp
->FileObject
->FsContext2
;
598 fileref
= ccb
? ccb
->fileref
: NULL
;
603 return STATUS_INVALID_PARAMETER
;
606 ERR("ccb was NULL\n");
607 return STATUS_INVALID_PARAMETER
;
611 ERR("fcb was NULL\n");
612 return STATUS_INVALID_PARAMETER
;
615 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_LIST_DIRECTORY
)) {
616 WARN("insufficient privileges\n");
617 return STATUS_ACCESS_DENIED
;
623 ERR("Vcb was NULL\n");
624 return STATUS_INVALID_PARAMETER
;
627 if (fileref
->fcb
== Vcb
->dummy_fcb
)
628 return STATUS_NO_MORE_FILES
;
630 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
631 acquire_fcb_lock_shared(Vcb
);
633 TRACE("%S\n", file_desc(IrpSp
->FileObject
));
635 if (IrpSp
->Flags
== 0) {
636 TRACE("QD flags: (none)\n");
638 ULONG flags
= IrpSp
->Flags
;
640 TRACE("QD flags:\n");
642 if (flags
& SL_INDEX_SPECIFIED
) {
643 TRACE(" SL_INDEX_SPECIFIED\n");
644 flags
&= ~SL_INDEX_SPECIFIED
;
647 if (flags
& SL_RESTART_SCAN
) {
648 TRACE(" SL_RESTART_SCAN\n");
649 flags
&= ~SL_RESTART_SCAN
;
652 if (flags
& SL_RETURN_SINGLE_ENTRY
) {
653 TRACE(" SL_RETURN_SINGLE_ENTRY\n");
654 flags
&= ~SL_RETURN_SINGLE_ENTRY
;
658 TRACE(" unknown flags: %u\n", flags
);
661 if (IrpSp
->Flags
& SL_RESTART_SCAN
) {
662 ccb
->query_dir_offset
= 0;
664 if (ccb
->query_string
.Buffer
) {
665 RtlFreeUnicodeString(&ccb
->query_string
);
666 ccb
->query_string
.Buffer
= NULL
;
669 ccb
->has_wildcard
= FALSE
;
670 ccb
->specific_file
= FALSE
;
673 initial
= !ccb
->query_string
.Buffer
;
675 if (IrpSp
->Parameters
.QueryDirectory
.FileName
&& IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
> 1) {
676 TRACE("QD filename: %.*S\n", IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
/ sizeof(WCHAR
), IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
);
678 if (IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
[0] != '*') {
679 specific_file
= TRUE
;
681 if (FsRtlDoesNameContainWildCards(IrpSp
->Parameters
.QueryDirectory
.FileName
)) {
683 specific_file
= FALSE
;
687 if (ccb
->query_string
.Buffer
)
688 RtlFreeUnicodeString(&ccb
->query_string
);
691 RtlUpcaseUnicodeString(&ccb
->query_string
, IrpSp
->Parameters
.QueryDirectory
.FileName
, TRUE
);
693 ccb
->query_string
.Buffer
= ExAllocatePoolWithTag(PagedPool
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
, ALLOC_TAG
);
694 if (!ccb
->query_string
.Buffer
) {
695 ERR("out of memory\n");
696 Status
= STATUS_INSUFFICIENT_RESOURCES
;
700 ccb
->query_string
.Length
= ccb
->query_string
.MaximumLength
= IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
;
701 RtlCopyMemory(ccb
->query_string
.Buffer
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
);
704 ccb
->has_wildcard
= has_wildcard
;
705 ccb
->specific_file
= specific_file
;
707 has_wildcard
= ccb
->has_wildcard
;
708 specific_file
= ccb
->specific_file
;
710 if (!(IrpSp
->Flags
& SL_RESTART_SCAN
)) {
714 Status
= STATUS_NO_MORE_FILES
;
720 if (ccb
->query_string
.Buffer
) {
721 TRACE("query string = %.*S\n", ccb
->query_string
.Length
/ sizeof(WCHAR
), ccb
->query_string
.Buffer
);
724 newoffset
= ccb
->query_dir_offset
;
726 ExAcquireResourceSharedLite(&fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
728 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
);
730 if (!NT_SUCCESS(Status
)) {
731 if (Status
== STATUS_NO_MORE_FILES
&& initial
)
732 Status
= STATUS_NO_SUCH_FILE
;
736 ccb
->query_dir_offset
= newoffset
;
738 buf
= map_user_buffer(Irp
, NormalPagePriority
);
740 if (Irp
->MdlAddress
&& !buf
) {
741 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
742 Status
= STATUS_INSUFFICIENT_RESOURCES
;
746 length
= IrpSp
->Parameters
.QueryDirectory
.Length
;
757 if (!ccb
->case_sensitive
) {
758 Status
= RtlUpcaseUnicodeString(&us
, &ccb
->query_string
, TRUE
);
759 if (!NT_SUCCESS(Status
)) {
760 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
764 hash
= calc_crc32c(0xffffffff, (UINT8
*)us
.Buffer
, us
.Length
);
766 hash
= calc_crc32c(0xffffffff, (UINT8
*)ccb
->query_string
.Buffer
, ccb
->query_string
.Length
);
770 if (ccb
->case_sensitive
) {
771 if (fileref
->fcb
->hash_ptrs
[c
]) {
772 le
= fileref
->fcb
->hash_ptrs
[c
];
773 while (le
!= &fileref
->fcb
->dir_children_hash
) {
774 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash
);
776 if (dc2
->hash
== hash
) {
777 if (dc2
->name
.Length
== ccb
->query_string
.Length
&& RtlCompareMemory(dc2
->name
.Buffer
, ccb
->query_string
.Buffer
, ccb
->query_string
.Length
) == ccb
->query_string
.Length
) {
783 de
.dir_entry_type
= DirEntryType_File
;
788 } else if (dc2
->hash
> hash
)
795 if (fileref
->fcb
->hash_ptrs_uc
[c
]) {
796 le
= fileref
->fcb
->hash_ptrs_uc
[c
];
797 while (le
!= &fileref
->fcb
->dir_children_hash_uc
) {
798 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash_uc
);
800 if (dc2
->hash_uc
== hash
) {
801 if (dc2
->name_uc
.Length
== us
.Length
&& RtlCompareMemory(dc2
->name_uc
.Buffer
, us
.Buffer
, us
.Length
) == us
.Length
) {
807 de
.dir_entry_type
= DirEntryType_File
;
812 } else if (dc2
->hash_uc
> hash
)
821 ExFreePool(us
.Buffer
);
824 Status
= STATUS_NO_SUCH_FILE
;
827 } else if (has_wildcard
) {
828 while (!FsRtlIsNameInExpression(&ccb
->query_string
, &de
.name
, !ccb
->case_sensitive
, NULL
)) {
829 newoffset
= ccb
->query_dir_offset
;
830 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
);
832 if (NT_SUCCESS(Status
))
833 ccb
->query_dir_offset
= newoffset
;
835 if (Status
== STATUS_NO_MORE_FILES
&& initial
)
836 Status
= STATUS_NO_SUCH_FILE
;
843 TRACE("file(0) = %.*S\n", de
.name
.Length
/ sizeof(WCHAR
), de
.name
.Buffer
);
844 TRACE("offset = %u\n", ccb
->query_dir_offset
- 1);
846 Status
= query_dir_item(fcb
, ccb
, buf
, &length
, Irp
, &de
, fcb
->subvol
);
849 if (NT_SUCCESS(Status
) && !(IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
) && !specific_file
) {
850 lastitem
= (UINT8
*)buf
;
853 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
854 case FileBothDirectoryInformation
:
855 case FileDirectoryInformation
:
856 case FileIdBothDirectoryInformation
:
857 case FileFullDirectoryInformation
:
858 case FileIdFullDirectoryInformation
:
859 length
-= length
% 8;
862 case FileNamesInformation
:
863 length
-= length
% 4;
867 WARN("unhandled file information class %u\n", IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
);
872 newoffset
= ccb
->query_dir_offset
;
873 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
);
874 if (NT_SUCCESS(Status
)) {
875 if (!has_wildcard
|| FsRtlIsNameInExpression(&ccb
->query_string
, &de
.name
, !ccb
->case_sensitive
, NULL
)) {
876 curitem
= (UINT8
*)buf
+ IrpSp
->Parameters
.QueryDirectory
.Length
- length
;
879 TRACE("file(%u) %u = %.*S\n", count
, curitem
- (UINT8
*)buf
, de
.name
.Length
/ sizeof(WCHAR
), de
.name
.Buffer
);
880 TRACE("offset = %u\n", ccb
->query_dir_offset
- 1);
882 status2
= query_dir_item(fcb
, ccb
, curitem
, &length
, Irp
, &de
, fcb
->subvol
);
884 if (NT_SUCCESS(status2
)) {
885 ULONG
* lastoffset
= (ULONG
*)lastitem
;
887 *lastoffset
= (ULONG
)(curitem
- lastitem
);
888 ccb
->query_dir_offset
= newoffset
;
894 ccb
->query_dir_offset
= newoffset
;
896 if (Status
== STATUS_NO_MORE_FILES
)
897 Status
= STATUS_SUCCESS
;
906 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.QueryDirectory
.Length
- length
;
909 ExReleaseResourceLite(&fileref
->fcb
->nonpaged
->dir_children_lock
);
912 release_fcb_lock(Vcb
);
913 ExReleaseResourceLite(&Vcb
->tree_lock
);
915 TRACE("returning %08x\n", Status
);
918 ExFreePool(utf8
.Buffer
);
923 static NTSTATUS
notify_change_directory(device_extension
* Vcb
, PIRP Irp
) {
924 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
925 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
926 fcb
* fcb
= FileObject
->FsContext
;
927 ccb
* ccb
= FileObject
->FsContext2
;
928 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
931 TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
934 ERR("ccb was NULL\n");
935 return STATUS_INVALID_PARAMETER
;
940 return STATUS_INVALID_PARAMETER
;
943 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_LIST_DIRECTORY
)) {
944 WARN("insufficient privileges\n");
945 return STATUS_ACCESS_DENIED
;
948 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, TRUE
);
949 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
951 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
952 Status
= STATUS_INVALID_PARAMETER
;
956 // FIXME - raise exception if FCB marked for deletion?
958 TRACE("%S\n", file_desc(FileObject
));
960 if (ccb
->filename
.Length
== 0) {
963 ccb
->filename
.MaximumLength
= ccb
->filename
.Length
= 0;
965 Status
= fileref_get_filename(fileref
, &ccb
->filename
, NULL
, &reqlen
);
966 if (Status
== STATUS_BUFFER_OVERFLOW
) {
967 ccb
->filename
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
968 if (!ccb
->filename
.Buffer
) {
969 ERR("out of memory\n");
970 Status
= STATUS_INSUFFICIENT_RESOURCES
;
974 ccb
->filename
.MaximumLength
= (UINT16
)reqlen
;
976 Status
= fileref_get_filename(fileref
, &ccb
->filename
, NULL
, &reqlen
);
977 if (!NT_SUCCESS(Status
)) {
978 ERR("fileref_get_filename returned %08x\n", Status
);
982 ERR("fileref_get_filename returned %08x\n", Status
);
987 FsRtlNotifyFilterChangeDirectory(Vcb
->NotifySync
, &Vcb
->DirNotifyList
, FileObject
->FsContext2
, (PSTRING
)&ccb
->filename
,
988 IrpSp
->Flags
& SL_WATCH_TREE
, FALSE
, IrpSp
->Parameters
.NotifyDirectory
.CompletionFilter
, Irp
,
991 Status
= STATUS_PENDING
;
994 ExReleaseResourceLite(fcb
->Header
.Resource
);
995 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
1000 _Dispatch_type_(IRP_MJ_DIRECTORY_CONTROL
)
1001 _Function_class_(DRIVER_DISPATCH
)
1002 NTSTATUS
drv_directory_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
1003 PIO_STACK_LOCATION IrpSp
;
1007 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
1009 FsRtlEnterFileSystem();
1011 TRACE("directory control\n");
1013 top_level
= is_top_level(Irp
);
1015 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
1016 Status
= vol_directory_control(DeviceObject
, Irp
);
1018 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
1019 Status
= STATUS_INVALID_PARAMETER
;
1023 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1025 Irp
->IoStatus
.Information
= 0;
1027 func
= IrpSp
->MinorFunction
;
1030 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
1031 Status
= notify_change_directory(Vcb
, Irp
);
1034 case IRP_MN_QUERY_DIRECTORY
:
1035 Status
= query_directory(Irp
);
1039 WARN("unknown minor %u\n", func
);
1040 Status
= STATUS_NOT_IMPLEMENTED
;
1041 Irp
->IoStatus
.Status
= Status
;
1045 if (Status
== STATUS_PENDING
)
1049 Irp
->IoStatus
.Status
= Status
;
1051 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
1054 TRACE("returning %08x\n", Status
);
1057 IoSetTopLevelIrp(NULL
);
1059 FsRtlExitFileSystem();