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"
20 // not currently in mingw
22 #define FileIdExtdDirectoryInformation (enum _FILE_INFORMATION_CLASS)60
23 #define FileIdExtdBothDirectoryInformation (enum _FILE_INFORMATION_CLASS)63
25 typedef struct _FILE_ID_EXTD_DIR_INFORMATION
{
26 ULONG NextEntryOffset
;
28 LARGE_INTEGER CreationTime
;
29 LARGE_INTEGER LastAccessTime
;
30 LARGE_INTEGER LastWriteTime
;
31 LARGE_INTEGER ChangeTime
;
32 LARGE_INTEGER EndOfFile
;
33 LARGE_INTEGER AllocationSize
;
37 ULONG ReparsePointTag
;
40 } FILE_ID_EXTD_DIR_INFORMATION
, *PFILE_ID_EXTD_DIR_INFORMATION
;
42 typedef struct _FILE_ID_EXTD_BOTH_DIR_INFORMATION
{
43 ULONG NextEntryOffset
;
45 LARGE_INTEGER CreationTime
;
46 LARGE_INTEGER LastAccessTime
;
47 LARGE_INTEGER LastWriteTime
;
48 LARGE_INTEGER ChangeTime
;
49 LARGE_INTEGER EndOfFile
;
50 LARGE_INTEGER AllocationSize
;
54 ULONG ReparsePointTag
;
56 CCHAR ShortNameLength
;
59 } FILE_ID_EXTD_BOTH_DIR_INFORMATION
, *PFILE_ID_EXTD_BOTH_DIR_INFORMATION
;
73 enum DirEntryType dir_entry_type
;
77 ULONG
get_reparse_tag_fcb(fcb
* fcb
) {
80 if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
81 return IO_REPARSE_TAG_SYMLINK
;
82 else if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
83 if (!fcb
->reparse_xattr
.Buffer
|| fcb
->reparse_xattr
.Length
< sizeof(ULONG
))
86 RtlCopyMemory(&tag
, fcb
->reparse_xattr
.Buffer
, sizeof(ULONG
));
91 Status
= read_file(fcb
, (UINT8
*)&tag
, 0, sizeof(ULONG
), &br
, NULL
);
92 if (!NT_SUCCESS(Status
)) {
93 ERR("read_file returned %08x\n", Status
);
101 ULONG
get_reparse_tag(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT8 type
, ULONG atts
, BOOL lxss
, PIRP Irp
) {
106 if (type
== BTRFS_TYPE_SYMLINK
)
107 return IO_REPARSE_TAG_SYMLINK
;
109 if (type
== BTRFS_TYPE_SOCKET
)
110 return IO_REPARSE_TAG_LXSS_SOCKET
;
111 else if (type
== BTRFS_TYPE_FIFO
)
112 return IO_REPARSE_TAG_LXSS_FIFO
;
113 else if (type
== BTRFS_TYPE_CHARDEV
)
114 return IO_REPARSE_TAG_LXSS_CHARDEV
;
115 else if (type
== BTRFS_TYPE_BLOCKDEV
)
116 return IO_REPARSE_TAG_LXSS_BLOCKDEV
;
119 if (type
!= BTRFS_TYPE_FILE
&& type
!= BTRFS_TYPE_DIRECTORY
)
122 if (!(atts
& FILE_ATTRIBUTE_REPARSE_POINT
))
125 Status
= open_fcb(Vcb
, subvol
, inode
, type
, NULL
, FALSE
, NULL
, &fcb
, PagedPool
, Irp
);
126 if (!NT_SUCCESS(Status
)) {
127 ERR("open_fcb returned %08x\n", Status
);
131 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, TRUE
);
133 tag
= get_reparse_tag_fcb(fcb
);
135 ExReleaseResourceLite(fcb
->Header
.Resource
);
142 static ULONG
get_ea_len(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, PIRP Irp
) {
146 if (get_xattr(Vcb
, subvol
, inode
, EA_EA
, EA_EA_HASH
, &eadata
, &len
, Irp
)) {
150 Status
= IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION
*)eadata
, len
, &offset
);
152 if (!NT_SUCCESS(Status
)) {
153 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
157 FILE_FULL_EA_INFORMATION
* eainfo
;
161 eainfo
= (FILE_FULL_EA_INFORMATION
*)eadata
;
163 ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
165 if (eainfo
->NextEntryOffset
== 0)
168 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((UINT8
*)eainfo
) + eainfo
->NextEntryOffset
);
179 static NTSTATUS
query_dir_item(fcb
* fcb
, ccb
* ccb
, void* buf
, LONG
* len
, PIRP Irp
, dir_entry
* de
, root
* r
) {
180 PIO_STACK_LOCATION IrpSp
;
185 ULONG atts
= 0, ealen
= 0;
186 file_ref
* fileref
= ccb
->fileref
;
188 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
190 if (de
->key
.obj_type
== TYPE_ROOT_ITEM
) { // subvol
195 le
= fcb
->Vcb
->roots
.Flink
;
196 while (le
!= &fcb
->Vcb
->roots
) {
197 root
* subvol
= CONTAINING_RECORD(le
, root
, list_entry
);
199 if (subvol
->id
== de
->key
.obj_id
) {
207 if (r
&& r
->parent
!= fcb
->subvol
->id
)
210 inode
= SUBVOL_ROOT_INODE
;
212 inode
= de
->key
.obj_id
;
215 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
!= FileNamesInformation
) { // FIXME - object ID and reparse point classes too?
216 switch (de
->dir_entry_type
) {
217 case DirEntryType_File
:
222 ii
= fcb
->Vcb
->dummy_fcb
->inode_item
;
223 atts
= fcb
->Vcb
->dummy_fcb
->atts
;
224 ealen
= fcb
->Vcb
->dummy_fcb
->ealen
;
226 KeQuerySystemTime(&time
);
227 win_time_to_unix(time
, &ii
.otime
);
228 ii
.st_atime
= ii
.st_mtime
= ii
.st_ctime
= ii
.otime
;
232 if (de
->dc
&& de
->dc
->fileref
&& de
->dc
->fileref
->fcb
) {
233 ii
= de
->dc
->fileref
->fcb
->inode_item
;
234 atts
= de
->dc
->fileref
->fcb
->atts
;
235 ealen
= de
->dc
->fileref
->fcb
->ealen
;
243 searchkey
.obj_id
= inode
;
244 searchkey
.obj_type
= TYPE_INODE_ITEM
;
245 searchkey
.offset
= 0xffffffffffffffff;
247 Status
= find_item(fcb
->Vcb
, r
, &tp
, &searchkey
, FALSE
, Irp
);
248 if (!NT_SUCCESS(Status
)) {
249 ERR("error - find_item returned %08x\n", Status
);
253 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
254 ERR("could not find inode item for inode %llx in root %llx\n", inode
, r
->id
);
255 return STATUS_INTERNAL_ERROR
;
258 RtlZeroMemory(&ii
, sizeof(INODE_ITEM
));
260 if (tp
.item
->size
> 0)
261 RtlCopyMemory(&ii
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
263 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileBothDirectoryInformation
||
264 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileDirectoryInformation
||
265 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileFullDirectoryInformation
||
266 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdBothDirectoryInformation
||
267 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdFullDirectoryInformation
||
268 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdExtdDirectoryInformation
||
269 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdExtdBothDirectoryInformation
) {
271 BOOL dotfile
= de
->name
.Length
> sizeof(WCHAR
) && de
->name
.Buffer
[0] == '.';
273 atts
= get_file_attributes(fcb
->Vcb
, r
, inode
, de
->type
, dotfile
, FALSE
, Irp
);
276 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileBothDirectoryInformation
||
277 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileFullDirectoryInformation
||
278 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdBothDirectoryInformation
||
279 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdFullDirectoryInformation
||
280 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdExtdDirectoryInformation
||
281 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdExtdBothDirectoryInformation
) {
282 ealen
= get_ea_len(fcb
->Vcb
, r
, inode
, Irp
);
290 case DirEntryType_Self
:
291 ii
= fcb
->inode_item
;
298 case DirEntryType_Parent
:
299 if (fileref
&& fileref
->parent
) {
300 ii
= fileref
->parent
->fcb
->inode_item
;
301 r
= fileref
->parent
->fcb
->subvol
;
302 inode
= fileref
->parent
->fcb
->inode
;
303 atts
= fileref
->parent
->fcb
->atts
;
304 ealen
= fileref
->parent
->fcb
->ealen
;
307 return STATUS_INTERNAL_ERROR
;
313 atts
= FILE_ATTRIBUTE_NORMAL
;
316 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
317 case FileBothDirectoryInformation
:
319 FILE_BOTH_DIR_INFORMATION
* fbdi
= buf
;
321 TRACE("FileBothDirectoryInformation\n");
323 needed
= sizeof(FILE_BOTH_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
326 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
327 return STATUS_BUFFER_OVERFLOW
;
330 fbdi
->NextEntryOffset
= 0;
332 fbdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
333 fbdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
334 fbdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
335 fbdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
336 fbdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
338 if (de
->type
== BTRFS_TYPE_SYMLINK
)
339 fbdi
->AllocationSize
.QuadPart
= 0;
340 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
341 fbdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
343 fbdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
345 fbdi
->FileAttributes
= atts
;
346 fbdi
->FileNameLength
= de
->name
.Length
;
347 fbdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
348 fbdi
->ShortNameLength
= 0;
350 RtlCopyMemory(fbdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
354 return STATUS_SUCCESS
;
357 case FileDirectoryInformation
:
359 FILE_DIRECTORY_INFORMATION
* fdi
= buf
;
361 TRACE("FileDirectoryInformation\n");
363 needed
= sizeof(FILE_DIRECTORY_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
366 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
367 return STATUS_BUFFER_OVERFLOW
;
370 fdi
->NextEntryOffset
= 0;
372 fdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
373 fdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
374 fdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
375 fdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
376 fdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
378 if (de
->type
== BTRFS_TYPE_SYMLINK
)
379 fdi
->AllocationSize
.QuadPart
= 0;
380 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
381 fdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
383 fdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
385 fdi
->FileAttributes
= atts
;
386 fdi
->FileNameLength
= de
->name
.Length
;
388 RtlCopyMemory(fdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
392 return STATUS_SUCCESS
;
395 case FileFullDirectoryInformation
:
397 FILE_FULL_DIR_INFORMATION
* ffdi
= buf
;
399 TRACE("FileFullDirectoryInformation\n");
401 needed
= sizeof(FILE_FULL_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
404 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
405 return STATUS_BUFFER_OVERFLOW
;
408 ffdi
->NextEntryOffset
= 0;
410 ffdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
411 ffdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
412 ffdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
413 ffdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
414 ffdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
416 if (de
->type
== BTRFS_TYPE_SYMLINK
)
417 ffdi
->AllocationSize
.QuadPart
= 0;
418 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
419 ffdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
421 ffdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
423 ffdi
->FileAttributes
= atts
;
424 ffdi
->FileNameLength
= de
->name
.Length
;
425 ffdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
427 RtlCopyMemory(ffdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
431 return STATUS_SUCCESS
;
434 case FileIdBothDirectoryInformation
:
436 FILE_ID_BOTH_DIR_INFORMATION
* fibdi
= buf
;
438 TRACE("FileIdBothDirectoryInformation\n");
440 needed
= sizeof(FILE_ID_BOTH_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
443 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
444 return STATUS_BUFFER_OVERFLOW
;
447 fibdi
->NextEntryOffset
= 0;
448 fibdi
->FileIndex
= 0;
449 fibdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
450 fibdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
451 fibdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
452 fibdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
453 fibdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
455 if (de
->type
== BTRFS_TYPE_SYMLINK
)
456 fibdi
->AllocationSize
.QuadPart
= 0;
457 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
458 fibdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
460 fibdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
462 fibdi
->FileAttributes
= atts
;
463 fibdi
->FileNameLength
= de
->name
.Length
;
464 fibdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
465 fibdi
->ShortNameLength
= 0;
466 fibdi
->FileId
.QuadPart
= r
? make_file_id(r
, inode
) : make_file_id(fcb
->Vcb
->dummy_fcb
->subvol
, fcb
->Vcb
->dummy_fcb
->inode
);
468 RtlCopyMemory(fibdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
472 return STATUS_SUCCESS
;
475 case FileIdFullDirectoryInformation
:
477 FILE_ID_FULL_DIR_INFORMATION
* fifdi
= buf
;
479 TRACE("FileIdFullDirectoryInformation\n");
481 needed
= sizeof(FILE_ID_FULL_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
484 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
485 return STATUS_BUFFER_OVERFLOW
;
488 fifdi
->NextEntryOffset
= 0;
489 fifdi
->FileIndex
= 0;
490 fifdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
491 fifdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
492 fifdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
493 fifdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
494 fifdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
496 if (de
->type
== BTRFS_TYPE_SYMLINK
)
497 fifdi
->AllocationSize
.QuadPart
= 0;
498 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
499 fifdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
501 fifdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
503 fifdi
->FileAttributes
= atts
;
504 fifdi
->FileNameLength
= de
->name
.Length
;
505 fifdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
506 fifdi
->FileId
.QuadPart
= r
? make_file_id(r
, inode
) : make_file_id(fcb
->Vcb
->dummy_fcb
->subvol
, fcb
->Vcb
->dummy_fcb
->inode
);
508 RtlCopyMemory(fifdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
512 return STATUS_SUCCESS
;
516 #pragma GCC diagnostic push
517 #pragma GCC diagnostic ignored "-Wswitch"
519 case FileIdExtdDirectoryInformation
:
521 FILE_ID_EXTD_DIR_INFORMATION
* fiedi
= buf
;
523 TRACE("FileIdExtdDirectoryInformation\n");
525 needed
= offsetof(FILE_ID_EXTD_DIR_INFORMATION
, FileName
[0]) + de
->name
.Length
;
528 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
529 return STATUS_BUFFER_OVERFLOW
;
532 fiedi
->NextEntryOffset
= 0;
533 fiedi
->FileIndex
= 0;
534 fiedi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
535 fiedi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
536 fiedi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
537 fiedi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
538 fiedi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
540 if (de
->type
== BTRFS_TYPE_SYMLINK
)
541 fiedi
->AllocationSize
.QuadPart
= 0;
542 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
543 fiedi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
545 fiedi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
547 fiedi
->FileAttributes
= atts
;
548 fiedi
->FileNameLength
= de
->name
.Length
;
549 fiedi
->EaSize
= ealen
;
550 fiedi
->ReparsePointTag
= get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
);
552 RtlCopyMemory(&fiedi
->FileId
.Identifier
[0], &fcb
->inode
, sizeof(UINT64
));
553 RtlCopyMemory(&fiedi
->FileId
.Identifier
[sizeof(UINT64
)], &fcb
->subvol
->id
, sizeof(UINT64
));
555 RtlCopyMemory(fiedi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
559 return STATUS_SUCCESS
;
562 case FileIdExtdBothDirectoryInformation
:
564 FILE_ID_EXTD_BOTH_DIR_INFORMATION
* fiebdi
= buf
;
566 TRACE("FileIdExtdBothDirectoryInformation\n");
568 needed
= offsetof(FILE_ID_EXTD_BOTH_DIR_INFORMATION
, FileName
[0]) + de
->name
.Length
;
571 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
572 return STATUS_BUFFER_OVERFLOW
;
575 fiebdi
->NextEntryOffset
= 0;
576 fiebdi
->FileIndex
= 0;
577 fiebdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
578 fiebdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
579 fiebdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
580 fiebdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
581 fiebdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
583 if (de
->type
== BTRFS_TYPE_SYMLINK
)
584 fiebdi
->AllocationSize
.QuadPart
= 0;
585 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
586 fiebdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
588 fiebdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
590 fiebdi
->FileAttributes
= atts
;
591 fiebdi
->FileNameLength
= de
->name
.Length
;
592 fiebdi
->EaSize
= ealen
;
593 fiebdi
->ReparsePointTag
= get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
);
595 RtlCopyMemory(&fiebdi
->FileId
.Identifier
[0], &fcb
->inode
, sizeof(UINT64
));
596 RtlCopyMemory(&fiebdi
->FileId
.Identifier
[sizeof(UINT64
)], &fcb
->subvol
->id
, sizeof(UINT64
));
598 fiebdi
->ShortNameLength
= 0;
600 RtlCopyMemory(fiebdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
604 return STATUS_SUCCESS
;
608 #pragma GCC diagnostic pop
611 case FileNamesInformation
:
613 FILE_NAMES_INFORMATION
* fni
= buf
;
615 TRACE("FileNamesInformation\n");
617 needed
= sizeof(FILE_NAMES_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
620 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
621 return STATUS_BUFFER_OVERFLOW
;
624 fni
->NextEntryOffset
= 0;
626 fni
->FileNameLength
= de
->name
.Length
;
628 RtlCopyMemory(fni
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
632 return STATUS_SUCCESS
;
635 case FileObjectIdInformation
:
636 FIXME("STUB: FileObjectIdInformation\n");
637 return STATUS_NOT_IMPLEMENTED
;
639 case FileQuotaInformation
:
640 FIXME("STUB: FileQuotaInformation\n");
641 return STATUS_NOT_IMPLEMENTED
;
643 case FileReparsePointInformation
:
644 FIXME("STUB: FileReparsePointInformation\n");
645 return STATUS_NOT_IMPLEMENTED
;
648 WARN("Unknown FileInformationClass %u\n", IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
);
649 return STATUS_NOT_IMPLEMENTED
;
652 return STATUS_NO_MORE_FILES
;
655 static NTSTATUS
next_dir_entry(file_ref
* fileref
, UINT64
* offset
, dir_entry
* de
, dir_child
** pdc
) {
660 dir_child
* dc2
= *pdc
;
662 if (dc2
->list_entry_index
.Flink
!= &fileref
->fcb
->dir_children_index
)
663 dc
= CONTAINING_RECORD(dc2
->list_entry_index
.Flink
, dir_child
, list_entry_index
);
670 if (fileref
->parent
) { // don't return . and .. if root directory
672 de
->key
.obj_id
= fileref
->fcb
->inode
;
673 de
->key
.obj_type
= TYPE_INODE_ITEM
;
675 de
->dir_entry_type
= DirEntryType_Self
;
676 de
->name
.Buffer
= L
".";
677 de
->name
.Length
= de
->name
.MaximumLength
= sizeof(WCHAR
);
678 de
->type
= BTRFS_TYPE_DIRECTORY
;
683 return STATUS_SUCCESS
;
684 } else if (*offset
== 1) {
685 de
->key
.obj_id
= fileref
->parent
->fcb
->inode
;
686 de
->key
.obj_type
= TYPE_INODE_ITEM
;
688 de
->dir_entry_type
= DirEntryType_Parent
;
689 de
->name
.Buffer
= L
"..";
690 de
->name
.Length
= de
->name
.MaximumLength
= sizeof(WCHAR
) * 2;
691 de
->type
= BTRFS_TYPE_DIRECTORY
;
696 return STATUS_SUCCESS
;
704 le
= fileref
->fcb
->dir_children_index
.Flink
;
706 // skip entries before offset
707 while (le
!= &fileref
->fcb
->dir_children_index
) {
708 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
710 if (dc2
->index
>= *offset
) {
720 return STATUS_NO_MORE_FILES
;
725 de
->dir_entry_type
= DirEntryType_File
;
728 *offset
= dc
->index
+ 1;
731 return STATUS_SUCCESS
;
734 static NTSTATUS
query_directory(PIRP Irp
) {
735 PIO_STACK_LOCATION IrpSp
;
736 NTSTATUS Status
, status2
;
740 device_extension
* Vcb
;
742 UINT8
*curitem
, *lastitem
;
745 BOOL has_wildcard
= FALSE
, specific_file
= FALSE
, initial
;
748 dir_child
* dc
= NULL
;
750 TRACE("query directory\n");
752 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
753 fcb
= IrpSp
->FileObject
->FsContext
;
754 ccb
= IrpSp
->FileObject
->FsContext2
;
755 fileref
= ccb
? ccb
->fileref
: NULL
;
758 return STATUS_INVALID_PARAMETER
;
761 ERR("ccb was NULL\n");
762 return STATUS_INVALID_PARAMETER
;
766 ERR("fcb was NULL\n");
767 return STATUS_INVALID_PARAMETER
;
770 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_LIST_DIRECTORY
)) {
771 WARN("insufficient privileges\n");
772 return STATUS_ACCESS_DENIED
;
778 ERR("Vcb was NULL\n");
779 return STATUS_INVALID_PARAMETER
;
782 if (fileref
->fcb
== Vcb
->dummy_fcb
)
783 return STATUS_NO_MORE_FILES
;
785 if (IrpSp
->Flags
== 0) {
786 TRACE("QD flags: (none)\n");
788 ULONG flags
= IrpSp
->Flags
;
790 TRACE("QD flags:\n");
792 if (flags
& SL_INDEX_SPECIFIED
) {
793 TRACE(" SL_INDEX_SPECIFIED\n");
794 flags
&= ~SL_INDEX_SPECIFIED
;
797 if (flags
& SL_RESTART_SCAN
) {
798 TRACE(" SL_RESTART_SCAN\n");
799 flags
&= ~SL_RESTART_SCAN
;
802 if (flags
& SL_RETURN_SINGLE_ENTRY
) {
803 TRACE(" SL_RETURN_SINGLE_ENTRY\n");
804 flags
&= ~SL_RETURN_SINGLE_ENTRY
;
808 TRACE(" unknown flags: %u\n", flags
);
811 if (IrpSp
->Flags
& SL_RESTART_SCAN
) {
812 ccb
->query_dir_offset
= 0;
814 if (ccb
->query_string
.Buffer
) {
815 RtlFreeUnicodeString(&ccb
->query_string
);
816 ccb
->query_string
.Buffer
= NULL
;
819 ccb
->has_wildcard
= FALSE
;
820 ccb
->specific_file
= FALSE
;
823 initial
= !ccb
->query_string
.Buffer
;
825 if (IrpSp
->Parameters
.QueryDirectory
.FileName
&& IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
> 1) {
826 TRACE("QD filename: %.*S\n", IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
/ sizeof(WCHAR
), IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
);
828 if (IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
> sizeof(WCHAR
) || IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
[0] != L
'*') {
829 specific_file
= TRUE
;
831 if (FsRtlDoesNameContainWildCards(IrpSp
->Parameters
.QueryDirectory
.FileName
)) {
833 specific_file
= FALSE
;
837 if (ccb
->query_string
.Buffer
)
838 RtlFreeUnicodeString(&ccb
->query_string
);
841 RtlUpcaseUnicodeString(&ccb
->query_string
, IrpSp
->Parameters
.QueryDirectory
.FileName
, TRUE
);
843 ccb
->query_string
.Buffer
= ExAllocatePoolWithTag(PagedPool
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
, ALLOC_TAG
);
844 if (!ccb
->query_string
.Buffer
) {
845 ERR("out of memory\n");
846 return STATUS_INSUFFICIENT_RESOURCES
;
849 ccb
->query_string
.Length
= ccb
->query_string
.MaximumLength
= IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
;
850 RtlCopyMemory(ccb
->query_string
.Buffer
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
);
853 ccb
->has_wildcard
= has_wildcard
;
854 ccb
->specific_file
= specific_file
;
856 has_wildcard
= ccb
->has_wildcard
;
857 specific_file
= ccb
->specific_file
;
859 if (!(IrpSp
->Flags
& SL_RESTART_SCAN
)) {
863 return STATUS_NO_MORE_FILES
;
867 if (ccb
->query_string
.Buffer
) {
868 TRACE("query string = %.*S\n", ccb
->query_string
.Length
/ sizeof(WCHAR
), ccb
->query_string
.Buffer
);
871 newoffset
= ccb
->query_dir_offset
;
873 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
875 ExAcquireResourceSharedLite(&fileref
->fcb
->nonpaged
->dir_children_lock
, TRUE
);
877 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
);
879 if (!NT_SUCCESS(Status
)) {
880 if (Status
== STATUS_NO_MORE_FILES
&& initial
)
881 Status
= STATUS_NO_SUCH_FILE
;
885 ccb
->query_dir_offset
= newoffset
;
887 buf
= map_user_buffer(Irp
, NormalPagePriority
);
889 if (Irp
->MdlAddress
&& !buf
) {
890 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
891 Status
= STATUS_INSUFFICIENT_RESOURCES
;
895 length
= IrpSp
->Parameters
.QueryDirectory
.Length
;
906 if (!ccb
->case_sensitive
) {
907 Status
= RtlUpcaseUnicodeString(&us
, &ccb
->query_string
, TRUE
);
908 if (!NT_SUCCESS(Status
)) {
909 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
913 hash
= calc_crc32c(0xffffffff, (UINT8
*)us
.Buffer
, us
.Length
);
915 hash
= calc_crc32c(0xffffffff, (UINT8
*)ccb
->query_string
.Buffer
, ccb
->query_string
.Length
);
919 if (ccb
->case_sensitive
) {
920 if (fileref
->fcb
->hash_ptrs
[c
]) {
921 le
= fileref
->fcb
->hash_ptrs
[c
];
922 while (le
!= &fileref
->fcb
->dir_children_hash
) {
923 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash
);
925 if (dc2
->hash
== hash
) {
926 if (dc2
->name
.Length
== ccb
->query_string
.Length
&& RtlCompareMemory(dc2
->name
.Buffer
, ccb
->query_string
.Buffer
, ccb
->query_string
.Length
) == ccb
->query_string
.Length
) {
932 de
.dir_entry_type
= DirEntryType_File
;
937 } else if (dc2
->hash
> hash
)
944 if (fileref
->fcb
->hash_ptrs_uc
[c
]) {
945 le
= fileref
->fcb
->hash_ptrs_uc
[c
];
946 while (le
!= &fileref
->fcb
->dir_children_hash_uc
) {
947 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash_uc
);
949 if (dc2
->hash_uc
== hash
) {
950 if (dc2
->name_uc
.Length
== us
.Length
&& RtlCompareMemory(dc2
->name_uc
.Buffer
, us
.Buffer
, us
.Length
) == us
.Length
) {
956 de
.dir_entry_type
= DirEntryType_File
;
961 } else if (dc2
->hash_uc
> hash
)
970 ExFreePool(us
.Buffer
);
973 Status
= STATUS_NO_SUCH_FILE
;
976 } else if (has_wildcard
) {
977 while (!FsRtlIsNameInExpression(&ccb
->query_string
, &de
.name
, !ccb
->case_sensitive
, NULL
)) {
978 newoffset
= ccb
->query_dir_offset
;
979 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
);
981 if (NT_SUCCESS(Status
))
982 ccb
->query_dir_offset
= newoffset
;
984 if (Status
== STATUS_NO_MORE_FILES
&& initial
)
985 Status
= STATUS_NO_SUCH_FILE
;
992 TRACE("file(0) = %.*S\n", de
.name
.Length
/ sizeof(WCHAR
), de
.name
.Buffer
);
993 TRACE("offset = %u\n", ccb
->query_dir_offset
- 1);
995 Status
= query_dir_item(fcb
, ccb
, buf
, &length
, Irp
, &de
, fcb
->subvol
);
998 if (NT_SUCCESS(Status
) && !(IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
) && !specific_file
) {
999 lastitem
= (UINT8
*)buf
;
1001 while (length
> 0) {
1002 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
1004 #pragma GCC diagnostic push
1005 #pragma GCC diagnostic ignored "-Wswitch"
1007 case FileBothDirectoryInformation
:
1008 case FileDirectoryInformation
:
1009 case FileIdBothDirectoryInformation
:
1010 case FileFullDirectoryInformation
:
1011 case FileIdFullDirectoryInformation
:
1012 case FileIdExtdDirectoryInformation
:
1013 case FileIdExtdBothDirectoryInformation
:
1014 length
-= length
% 8;
1017 #pragma GCC diagnostic pop
1020 case FileNamesInformation
:
1021 length
-= length
% 4;
1025 WARN("unhandled file information class %u\n", IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
);
1030 newoffset
= ccb
->query_dir_offset
;
1031 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
);
1032 if (NT_SUCCESS(Status
)) {
1033 if (!has_wildcard
|| FsRtlIsNameInExpression(&ccb
->query_string
, &de
.name
, !ccb
->case_sensitive
, NULL
)) {
1034 curitem
= (UINT8
*)buf
+ IrpSp
->Parameters
.QueryDirectory
.Length
- length
;
1037 TRACE("file(%u) %u = %.*S\n", count
, curitem
- (UINT8
*)buf
, de
.name
.Length
/ sizeof(WCHAR
), de
.name
.Buffer
);
1038 TRACE("offset = %u\n", ccb
->query_dir_offset
- 1);
1040 status2
= query_dir_item(fcb
, ccb
, curitem
, &length
, Irp
, &de
, fcb
->subvol
);
1042 if (NT_SUCCESS(status2
)) {
1043 ULONG
* lastoffset
= (ULONG
*)lastitem
;
1045 *lastoffset
= (ULONG
)(curitem
- lastitem
);
1046 ccb
->query_dir_offset
= newoffset
;
1052 ccb
->query_dir_offset
= newoffset
;
1054 if (Status
== STATUS_NO_MORE_FILES
)
1055 Status
= STATUS_SUCCESS
;
1064 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.QueryDirectory
.Length
- length
;
1067 ExReleaseResourceLite(&fileref
->fcb
->nonpaged
->dir_children_lock
);
1069 ExReleaseResourceLite(&Vcb
->tree_lock
);
1071 TRACE("returning %08x\n", Status
);
1076 static NTSTATUS
notify_change_directory(device_extension
* Vcb
, PIRP Irp
) {
1077 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1078 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
1079 fcb
* fcb
= FileObject
->FsContext
;
1080 ccb
* ccb
= FileObject
->FsContext2
;
1081 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1084 TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
1087 ERR("ccb was NULL\n");
1088 return STATUS_INVALID_PARAMETER
;
1092 ERR("no fileref\n");
1093 return STATUS_INVALID_PARAMETER
;
1096 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_LIST_DIRECTORY
)) {
1097 WARN("insufficient privileges\n");
1098 return STATUS_ACCESS_DENIED
;
1101 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, TRUE
);
1102 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
1104 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
1105 Status
= STATUS_INVALID_PARAMETER
;
1109 // FIXME - raise exception if FCB marked for deletion?
1111 TRACE("%S\n", file_desc(FileObject
));
1113 if (ccb
->filename
.Length
== 0) {
1116 ccb
->filename
.MaximumLength
= ccb
->filename
.Length
= 0;
1118 Status
= fileref_get_filename(fileref
, &ccb
->filename
, NULL
, &reqlen
);
1119 if (Status
== STATUS_BUFFER_OVERFLOW
) {
1120 ccb
->filename
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
1121 if (!ccb
->filename
.Buffer
) {
1122 ERR("out of memory\n");
1123 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1127 ccb
->filename
.MaximumLength
= (UINT16
)reqlen
;
1129 Status
= fileref_get_filename(fileref
, &ccb
->filename
, NULL
, &reqlen
);
1130 if (!NT_SUCCESS(Status
)) {
1131 ERR("fileref_get_filename returned %08x\n", Status
);
1135 ERR("fileref_get_filename returned %08x\n", Status
);
1140 FsRtlNotifyFilterChangeDirectory(Vcb
->NotifySync
, &Vcb
->DirNotifyList
, FileObject
->FsContext2
, (PSTRING
)&ccb
->filename
,
1141 IrpSp
->Flags
& SL_WATCH_TREE
, FALSE
, IrpSp
->Parameters
.NotifyDirectory
.CompletionFilter
, Irp
,
1144 Status
= STATUS_PENDING
;
1147 ExReleaseResourceLite(fcb
->Header
.Resource
);
1148 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
1153 _Dispatch_type_(IRP_MJ_DIRECTORY_CONTROL
)
1154 _Function_class_(DRIVER_DISPATCH
)
1155 NTSTATUS NTAPI
drv_directory_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
1156 PIO_STACK_LOCATION IrpSp
;
1160 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
1162 FsRtlEnterFileSystem();
1164 TRACE("directory control\n");
1166 top_level
= is_top_level(Irp
);
1168 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
1169 Status
= vol_directory_control(DeviceObject
, Irp
);
1171 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
1172 Status
= STATUS_INVALID_PARAMETER
;
1176 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1178 Irp
->IoStatus
.Information
= 0;
1180 func
= IrpSp
->MinorFunction
;
1183 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
1184 Status
= notify_change_directory(Vcb
, Irp
);
1187 case IRP_MN_QUERY_DIRECTORY
:
1188 Status
= query_directory(Irp
);
1192 WARN("unknown minor %u\n", func
);
1193 Status
= STATUS_NOT_IMPLEMENTED
;
1194 Irp
->IoStatus
.Status
= Status
;
1198 if (Status
== STATUS_PENDING
)
1202 Irp
->IoStatus
.Status
= Status
;
1204 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
1207 TRACE("returning %08x\n", Status
);
1210 IoSetTopLevelIrp(NULL
);
1212 FsRtlExitFileSystem();