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"
21 // not currently in mingw
23 #define FileIdExtdDirectoryInformation (enum _FILE_INFORMATION_CLASS)60
24 #define FileIdExtdBothDirectoryInformation (enum _FILE_INFORMATION_CLASS)63
26 typedef struct _FILE_ID_EXTD_DIR_INFORMATION
{
27 ULONG NextEntryOffset
;
29 LARGE_INTEGER CreationTime
;
30 LARGE_INTEGER LastAccessTime
;
31 LARGE_INTEGER LastWriteTime
;
32 LARGE_INTEGER ChangeTime
;
33 LARGE_INTEGER EndOfFile
;
34 LARGE_INTEGER AllocationSize
;
38 ULONG ReparsePointTag
;
41 } FILE_ID_EXTD_DIR_INFORMATION
, *PFILE_ID_EXTD_DIR_INFORMATION
;
43 typedef struct _FILE_ID_EXTD_BOTH_DIR_INFORMATION
{
44 ULONG NextEntryOffset
;
46 LARGE_INTEGER CreationTime
;
47 LARGE_INTEGER LastAccessTime
;
48 LARGE_INTEGER LastWriteTime
;
49 LARGE_INTEGER ChangeTime
;
50 LARGE_INTEGER EndOfFile
;
51 LARGE_INTEGER AllocationSize
;
55 ULONG ReparsePointTag
;
57 CCHAR ShortNameLength
;
60 } FILE_ID_EXTD_BOTH_DIR_INFORMATION
, *PFILE_ID_EXTD_BOTH_DIR_INFORMATION
;
64 #define FileIdExtdDirectoryInformation (enum _FILE_INFORMATION_CLASS)60
65 #define FileIdExtdBothDirectoryInformation (enum _FILE_INFORMATION_CLASS)63
78 enum DirEntryType dir_entry_type
;
82 ULONG
get_reparse_tag_fcb(fcb
* fcb
) {
85 if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
86 return IO_REPARSE_TAG_SYMLINK
;
87 else if (fcb
->type
== BTRFS_TYPE_DIRECTORY
) {
88 if (!fcb
->reparse_xattr
.Buffer
|| fcb
->reparse_xattr
.Length
< sizeof(ULONG
))
91 RtlCopyMemory(&tag
, fcb
->reparse_xattr
.Buffer
, sizeof(ULONG
));
96 Status
= read_file(fcb
, (uint8_t*)&tag
, 0, sizeof(ULONG
), &br
, NULL
);
97 if (!NT_SUCCESS(Status
)) {
98 ERR("read_file returned %08x\n", Status
);
106 ULONG
get_reparse_tag(device_extension
* Vcb
, root
* subvol
, uint64_t inode
, uint8_t type
, ULONG atts
, bool lxss
, PIRP Irp
) {
111 if (type
== BTRFS_TYPE_SYMLINK
)
112 return IO_REPARSE_TAG_SYMLINK
;
114 if (type
== BTRFS_TYPE_SOCKET
)
115 return IO_REPARSE_TAG_LXSS_SOCKET
;
116 else if (type
== BTRFS_TYPE_FIFO
)
117 return IO_REPARSE_TAG_LXSS_FIFO
;
118 else if (type
== BTRFS_TYPE_CHARDEV
)
119 return IO_REPARSE_TAG_LXSS_CHARDEV
;
120 else if (type
== BTRFS_TYPE_BLOCKDEV
)
121 return IO_REPARSE_TAG_LXSS_BLOCKDEV
;
124 if (type
!= BTRFS_TYPE_FILE
&& type
!= BTRFS_TYPE_DIRECTORY
)
127 if (!(atts
& FILE_ATTRIBUTE_REPARSE_POINT
))
130 Status
= open_fcb(Vcb
, subvol
, inode
, type
, NULL
, false, NULL
, &fcb
, PagedPool
, Irp
);
131 if (!NT_SUCCESS(Status
)) {
132 ERR("open_fcb returned %08x\n", Status
);
136 ExAcquireResourceSharedLite(fcb
->Header
.Resource
, true);
138 tag
= get_reparse_tag_fcb(fcb
);
140 ExReleaseResourceLite(fcb
->Header
.Resource
);
147 static ULONG
get_ea_len(device_extension
* Vcb
, root
* subvol
, uint64_t inode
, PIRP Irp
) {
151 if (get_xattr(Vcb
, subvol
, inode
, EA_EA
, EA_EA_HASH
, &eadata
, &len
, Irp
)) {
155 Status
= IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION
*)eadata
, len
, &offset
);
157 if (!NT_SUCCESS(Status
)) {
158 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status
, offset
);
162 FILE_FULL_EA_INFORMATION
* eainfo
;
166 eainfo
= (FILE_FULL_EA_INFORMATION
*)eadata
;
168 ealen
+= 5 + eainfo
->EaNameLength
+ eainfo
->EaValueLength
;
170 if (eainfo
->NextEntryOffset
== 0)
173 eainfo
= (FILE_FULL_EA_INFORMATION
*)(((uint8_t*)eainfo
) + eainfo
->NextEntryOffset
);
184 static NTSTATUS
query_dir_item(fcb
* fcb
, ccb
* ccb
, void* buf
, LONG
* len
, PIRP Irp
, dir_entry
* de
, root
* r
) {
185 PIO_STACK_LOCATION IrpSp
;
190 ULONG atts
= 0, ealen
= 0;
191 file_ref
* fileref
= ccb
->fileref
;
193 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
195 if (de
->key
.obj_type
== TYPE_ROOT_ITEM
) { // subvol
200 le
= fcb
->Vcb
->roots
.Flink
;
201 while (le
!= &fcb
->Vcb
->roots
) {
202 root
* subvol
= CONTAINING_RECORD(le
, root
, list_entry
);
204 if (subvol
->id
== de
->key
.obj_id
) {
212 if (r
&& r
->parent
!= fcb
->subvol
->id
)
215 inode
= SUBVOL_ROOT_INODE
;
217 inode
= de
->key
.obj_id
;
220 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
!= FileNamesInformation
) { // FIXME - object ID and reparse point classes too?
221 switch (de
->dir_entry_type
) {
222 case DirEntryType_File
:
227 ii
= fcb
->Vcb
->dummy_fcb
->inode_item
;
228 atts
= fcb
->Vcb
->dummy_fcb
->atts
;
229 ealen
= fcb
->Vcb
->dummy_fcb
->ealen
;
231 KeQuerySystemTime(&time
);
232 win_time_to_unix(time
, &ii
.otime
);
233 ii
.st_atime
= ii
.st_mtime
= ii
.st_ctime
= ii
.otime
;
237 if (de
->dc
&& de
->dc
->fileref
&& de
->dc
->fileref
->fcb
) {
238 ii
= de
->dc
->fileref
->fcb
->inode_item
;
239 atts
= de
->dc
->fileref
->fcb
->atts
;
240 ealen
= de
->dc
->fileref
->fcb
->ealen
;
248 searchkey
.obj_id
= inode
;
249 searchkey
.obj_type
= TYPE_INODE_ITEM
;
250 searchkey
.offset
= 0xffffffffffffffff;
252 Status
= find_item(fcb
->Vcb
, r
, &tp
, &searchkey
, false, Irp
);
253 if (!NT_SUCCESS(Status
)) {
254 ERR("error - find_item returned %08x\n", Status
);
258 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
259 ERR("could not find inode item for inode %I64x in root %I64x\n", inode
, r
->id
);
260 return STATUS_INTERNAL_ERROR
;
263 RtlZeroMemory(&ii
, sizeof(INODE_ITEM
));
265 if (tp
.item
->size
> 0)
266 RtlCopyMemory(&ii
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
268 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileBothDirectoryInformation
||
269 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileDirectoryInformation
||
270 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileFullDirectoryInformation
||
271 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdBothDirectoryInformation
||
272 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdFullDirectoryInformation
||
273 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdExtdDirectoryInformation
||
274 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdExtdBothDirectoryInformation
) {
276 bool dotfile
= de
->name
.Length
> sizeof(WCHAR
) && de
->name
.Buffer
[0] == '.';
278 atts
= get_file_attributes(fcb
->Vcb
, r
, inode
, de
->type
, dotfile
, false, Irp
);
281 if (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileBothDirectoryInformation
||
282 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileFullDirectoryInformation
||
283 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdBothDirectoryInformation
||
284 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdFullDirectoryInformation
||
285 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdExtdDirectoryInformation
||
286 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdExtdBothDirectoryInformation
) {
287 ealen
= get_ea_len(fcb
->Vcb
, r
, inode
, Irp
);
295 case DirEntryType_Self
:
296 ii
= fcb
->inode_item
;
303 case DirEntryType_Parent
:
304 if (fileref
&& fileref
->parent
) {
305 ii
= fileref
->parent
->fcb
->inode_item
;
306 r
= fileref
->parent
->fcb
->subvol
;
307 inode
= fileref
->parent
->fcb
->inode
;
308 atts
= fileref
->parent
->fcb
->atts
;
309 ealen
= fileref
->parent
->fcb
->ealen
;
312 return STATUS_INTERNAL_ERROR
;
318 atts
= FILE_ATTRIBUTE_NORMAL
;
321 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
322 case FileBothDirectoryInformation
:
324 FILE_BOTH_DIR_INFORMATION
* fbdi
= buf
;
326 TRACE("FileBothDirectoryInformation\n");
328 needed
= sizeof(FILE_BOTH_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
331 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
332 return STATUS_BUFFER_OVERFLOW
;
335 fbdi
->NextEntryOffset
= 0;
337 fbdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
338 fbdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
339 fbdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
340 fbdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
341 fbdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
343 if (de
->type
== BTRFS_TYPE_SYMLINK
)
344 fbdi
->AllocationSize
.QuadPart
= 0;
345 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
346 fbdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
348 fbdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
350 fbdi
->FileAttributes
= atts
;
351 fbdi
->FileNameLength
= de
->name
.Length
;
352 fbdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
353 fbdi
->ShortNameLength
= 0;
355 RtlCopyMemory(fbdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
359 return STATUS_SUCCESS
;
362 case FileDirectoryInformation
:
364 FILE_DIRECTORY_INFORMATION
* fdi
= buf
;
366 TRACE("FileDirectoryInformation\n");
368 needed
= sizeof(FILE_DIRECTORY_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
371 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
372 return STATUS_BUFFER_OVERFLOW
;
375 fdi
->NextEntryOffset
= 0;
377 fdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
378 fdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
379 fdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
380 fdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
381 fdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
383 if (de
->type
== BTRFS_TYPE_SYMLINK
)
384 fdi
->AllocationSize
.QuadPart
= 0;
385 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
386 fdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
388 fdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
390 fdi
->FileAttributes
= atts
;
391 fdi
->FileNameLength
= de
->name
.Length
;
393 RtlCopyMemory(fdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
397 return STATUS_SUCCESS
;
400 case FileFullDirectoryInformation
:
402 FILE_FULL_DIR_INFORMATION
* ffdi
= buf
;
404 TRACE("FileFullDirectoryInformation\n");
406 needed
= sizeof(FILE_FULL_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
409 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
410 return STATUS_BUFFER_OVERFLOW
;
413 ffdi
->NextEntryOffset
= 0;
415 ffdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
416 ffdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
417 ffdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
418 ffdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
419 ffdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
421 if (de
->type
== BTRFS_TYPE_SYMLINK
)
422 ffdi
->AllocationSize
.QuadPart
= 0;
423 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
424 ffdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
426 ffdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
428 ffdi
->FileAttributes
= atts
;
429 ffdi
->FileNameLength
= de
->name
.Length
;
430 ffdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
432 RtlCopyMemory(ffdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
436 return STATUS_SUCCESS
;
439 case FileIdBothDirectoryInformation
:
441 FILE_ID_BOTH_DIR_INFORMATION
* fibdi
= buf
;
443 TRACE("FileIdBothDirectoryInformation\n");
445 needed
= sizeof(FILE_ID_BOTH_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
448 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
449 return STATUS_BUFFER_OVERFLOW
;
452 fibdi
->NextEntryOffset
= 0;
453 fibdi
->FileIndex
= 0;
454 fibdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
455 fibdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
456 fibdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
457 fibdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
458 fibdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
460 if (de
->type
== BTRFS_TYPE_SYMLINK
)
461 fibdi
->AllocationSize
.QuadPart
= 0;
462 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
463 fibdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
465 fibdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
467 fibdi
->FileAttributes
= atts
;
468 fibdi
->FileNameLength
= de
->name
.Length
;
469 fibdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
470 fibdi
->ShortNameLength
= 0;
471 fibdi
->FileId
.QuadPart
= r
? make_file_id(r
, inode
) : make_file_id(fcb
->Vcb
->dummy_fcb
->subvol
, fcb
->Vcb
->dummy_fcb
->inode
);
473 RtlCopyMemory(fibdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
477 return STATUS_SUCCESS
;
480 case FileIdFullDirectoryInformation
:
482 FILE_ID_FULL_DIR_INFORMATION
* fifdi
= buf
;
484 TRACE("FileIdFullDirectoryInformation\n");
486 needed
= sizeof(FILE_ID_FULL_DIR_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
489 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
490 return STATUS_BUFFER_OVERFLOW
;
493 fifdi
->NextEntryOffset
= 0;
494 fifdi
->FileIndex
= 0;
495 fifdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
496 fifdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
497 fifdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
498 fifdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
499 fifdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
501 if (de
->type
== BTRFS_TYPE_SYMLINK
)
502 fifdi
->AllocationSize
.QuadPart
= 0;
503 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
504 fifdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
506 fifdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
508 fifdi
->FileAttributes
= atts
;
509 fifdi
->FileNameLength
= de
->name
.Length
;
510 fifdi
->EaSize
= (r
&& atts
& FILE_ATTRIBUTE_REPARSE_POINT
) ? get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
) : ealen
;
511 fifdi
->FileId
.QuadPart
= r
? make_file_id(r
, inode
) : make_file_id(fcb
->Vcb
->dummy_fcb
->subvol
, fcb
->Vcb
->dummy_fcb
->inode
);
513 RtlCopyMemory(fifdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
517 return STATUS_SUCCESS
;
521 #pragma GCC diagnostic push
522 #pragma GCC diagnostic ignored "-Wswitch"
524 #if (NTDDI_VERSION >= NTDDI_VISTA)
525 case FileIdExtdDirectoryInformation
:
527 FILE_ID_EXTD_DIR_INFORMATION
* fiedi
= buf
;
529 TRACE("FileIdExtdDirectoryInformation\n");
531 needed
= offsetof(FILE_ID_EXTD_DIR_INFORMATION
, FileName
[0]) + de
->name
.Length
;
534 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
535 return STATUS_BUFFER_OVERFLOW
;
538 fiedi
->NextEntryOffset
= 0;
539 fiedi
->FileIndex
= 0;
540 fiedi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
541 fiedi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
542 fiedi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
543 fiedi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
544 fiedi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
546 if (de
->type
== BTRFS_TYPE_SYMLINK
)
547 fiedi
->AllocationSize
.QuadPart
= 0;
548 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
549 fiedi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
551 fiedi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
553 fiedi
->FileAttributes
= atts
;
554 fiedi
->FileNameLength
= de
->name
.Length
;
555 fiedi
->EaSize
= ealen
;
556 fiedi
->ReparsePointTag
= get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
);
558 RtlCopyMemory(&fiedi
->FileId
.Identifier
[0], &fcb
->inode
, sizeof(uint64_t));
559 RtlCopyMemory(&fiedi
->FileId
.Identifier
[sizeof(uint64_t)], &fcb
->subvol
->id
, sizeof(uint64_t));
561 RtlCopyMemory(fiedi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
565 return STATUS_SUCCESS
;
568 case FileIdExtdBothDirectoryInformation
:
570 FILE_ID_EXTD_BOTH_DIR_INFORMATION
* fiebdi
= buf
;
572 TRACE("FileIdExtdBothDirectoryInformation\n");
574 needed
= offsetof(FILE_ID_EXTD_BOTH_DIR_INFORMATION
, FileName
[0]) + de
->name
.Length
;
577 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
578 return STATUS_BUFFER_OVERFLOW
;
581 fiebdi
->NextEntryOffset
= 0;
582 fiebdi
->FileIndex
= 0;
583 fiebdi
->CreationTime
.QuadPart
= unix_time_to_win(&ii
.otime
);
584 fiebdi
->LastAccessTime
.QuadPart
= unix_time_to_win(&ii
.st_atime
);
585 fiebdi
->LastWriteTime
.QuadPart
= unix_time_to_win(&ii
.st_mtime
);
586 fiebdi
->ChangeTime
.QuadPart
= unix_time_to_win(&ii
.st_ctime
);
587 fiebdi
->EndOfFile
.QuadPart
= de
->type
== BTRFS_TYPE_SYMLINK
? 0 : ii
.st_size
;
589 if (de
->type
== BTRFS_TYPE_SYMLINK
)
590 fiebdi
->AllocationSize
.QuadPart
= 0;
591 else if (atts
& FILE_ATTRIBUTE_SPARSE_FILE
)
592 fiebdi
->AllocationSize
.QuadPart
= ii
.st_blocks
;
594 fiebdi
->AllocationSize
.QuadPart
= sector_align(ii
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
596 fiebdi
->FileAttributes
= atts
;
597 fiebdi
->FileNameLength
= de
->name
.Length
;
598 fiebdi
->EaSize
= ealen
;
599 fiebdi
->ReparsePointTag
= get_reparse_tag(fcb
->Vcb
, r
, inode
, de
->type
, atts
, ccb
->lxss
, Irp
);
601 RtlCopyMemory(&fiebdi
->FileId
.Identifier
[0], &fcb
->inode
, sizeof(uint64_t));
602 RtlCopyMemory(&fiebdi
->FileId
.Identifier
[sizeof(uint64_t)], &fcb
->subvol
->id
, sizeof(uint64_t));
604 fiebdi
->ShortNameLength
= 0;
606 RtlCopyMemory(fiebdi
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
610 return STATUS_SUCCESS
;
615 #pragma GCC diagnostic pop
618 case FileNamesInformation
:
620 FILE_NAMES_INFORMATION
* fni
= buf
;
622 TRACE("FileNamesInformation\n");
624 needed
= sizeof(FILE_NAMES_INFORMATION
) - sizeof(WCHAR
) + de
->name
.Length
;
627 TRACE("buffer overflow - %u > %u\n", needed
, *len
);
628 return STATUS_BUFFER_OVERFLOW
;
631 fni
->NextEntryOffset
= 0;
633 fni
->FileNameLength
= de
->name
.Length
;
635 RtlCopyMemory(fni
->FileName
, de
->name
.Buffer
, de
->name
.Length
);
639 return STATUS_SUCCESS
;
642 case FileObjectIdInformation
:
643 FIXME("STUB: FileObjectIdInformation\n");
644 return STATUS_NOT_IMPLEMENTED
;
646 case FileQuotaInformation
:
647 FIXME("STUB: FileQuotaInformation\n");
648 return STATUS_NOT_IMPLEMENTED
;
650 case FileReparsePointInformation
:
651 FIXME("STUB: FileReparsePointInformation\n");
652 return STATUS_NOT_IMPLEMENTED
;
655 WARN("Unknown FileInformationClass %u\n", IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
);
656 return STATUS_NOT_IMPLEMENTED
;
659 return STATUS_NO_MORE_FILES
;
662 static NTSTATUS
next_dir_entry(file_ref
* fileref
, uint64_t* offset
, dir_entry
* de
, dir_child
** pdc
) {
667 dir_child
* dc2
= *pdc
;
669 if (dc2
->list_entry_index
.Flink
!= &fileref
->fcb
->dir_children_index
)
670 dc
= CONTAINING_RECORD(dc2
->list_entry_index
.Flink
, dir_child
, list_entry_index
);
677 if (fileref
->parent
) { // don't return . and .. if root directory
679 de
->key
.obj_id
= fileref
->fcb
->inode
;
680 de
->key
.obj_type
= TYPE_INODE_ITEM
;
682 de
->dir_entry_type
= DirEntryType_Self
;
683 de
->name
.Buffer
= L
".";
684 de
->name
.Length
= de
->name
.MaximumLength
= sizeof(WCHAR
);
685 de
->type
= BTRFS_TYPE_DIRECTORY
;
690 return STATUS_SUCCESS
;
691 } else if (*offset
== 1) {
692 de
->key
.obj_id
= fileref
->parent
->fcb
->inode
;
693 de
->key
.obj_type
= TYPE_INODE_ITEM
;
695 de
->dir_entry_type
= DirEntryType_Parent
;
696 de
->name
.Buffer
= L
"..";
697 de
->name
.Length
= de
->name
.MaximumLength
= sizeof(WCHAR
) * 2;
698 de
->type
= BTRFS_TYPE_DIRECTORY
;
703 return STATUS_SUCCESS
;
711 le
= fileref
->fcb
->dir_children_index
.Flink
;
713 // skip entries before offset
714 while (le
!= &fileref
->fcb
->dir_children_index
) {
715 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_index
);
717 if (dc2
->index
>= *offset
) {
727 return STATUS_NO_MORE_FILES
;
732 de
->dir_entry_type
= DirEntryType_File
;
735 *offset
= dc
->index
+ 1;
738 return STATUS_SUCCESS
;
741 static NTSTATUS
query_directory(PIRP Irp
) {
742 PIO_STACK_LOCATION IrpSp
;
743 NTSTATUS Status
, status2
;
747 device_extension
* Vcb
;
749 uint8_t *curitem
, *lastitem
;
752 bool has_wildcard
= false, specific_file
= false, initial
;
755 dir_child
* dc
= NULL
;
757 TRACE("query directory\n");
759 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
760 fcb
= IrpSp
->FileObject
->FsContext
;
761 ccb
= IrpSp
->FileObject
->FsContext2
;
762 fileref
= ccb
? ccb
->fileref
: NULL
;
765 return STATUS_INVALID_PARAMETER
;
768 ERR("ccb was NULL\n");
769 return STATUS_INVALID_PARAMETER
;
773 ERR("fcb was NULL\n");
774 return STATUS_INVALID_PARAMETER
;
777 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_LIST_DIRECTORY
)) {
778 WARN("insufficient privileges\n");
779 return STATUS_ACCESS_DENIED
;
785 ERR("Vcb was NULL\n");
786 return STATUS_INVALID_PARAMETER
;
789 if (fileref
->fcb
== Vcb
->dummy_fcb
)
790 return STATUS_NO_MORE_FILES
;
792 if (IrpSp
->Flags
== 0) {
793 TRACE("QD flags: (none)\n");
795 ULONG flags
= IrpSp
->Flags
;
797 TRACE("QD flags:\n");
799 if (flags
& SL_INDEX_SPECIFIED
) {
800 TRACE(" SL_INDEX_SPECIFIED\n");
801 flags
&= ~SL_INDEX_SPECIFIED
;
804 if (flags
& SL_RESTART_SCAN
) {
805 TRACE(" SL_RESTART_SCAN\n");
806 flags
&= ~SL_RESTART_SCAN
;
809 if (flags
& SL_RETURN_SINGLE_ENTRY
) {
810 TRACE(" SL_RETURN_SINGLE_ENTRY\n");
811 flags
&= ~SL_RETURN_SINGLE_ENTRY
;
815 TRACE(" unknown flags: %u\n", flags
);
818 if (IrpSp
->Flags
& SL_RESTART_SCAN
) {
819 ccb
->query_dir_offset
= 0;
821 if (ccb
->query_string
.Buffer
) {
822 RtlFreeUnicodeString(&ccb
->query_string
);
823 ccb
->query_string
.Buffer
= NULL
;
826 ccb
->has_wildcard
= false;
827 ccb
->specific_file
= false;
830 initial
= !ccb
->query_string
.Buffer
;
832 if (IrpSp
->Parameters
.QueryDirectory
.FileName
&& IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
> 1) {
833 TRACE("QD filename: %.*S\n", IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
/ sizeof(WCHAR
), IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
);
835 if (IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
> sizeof(WCHAR
) || IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
[0] != L
'*') {
836 specific_file
= true;
838 if (FsRtlDoesNameContainWildCards(IrpSp
->Parameters
.QueryDirectory
.FileName
)) {
840 specific_file
= false;
844 if (ccb
->query_string
.Buffer
)
845 RtlFreeUnicodeString(&ccb
->query_string
);
848 RtlUpcaseUnicodeString(&ccb
->query_string
, IrpSp
->Parameters
.QueryDirectory
.FileName
, true);
850 ccb
->query_string
.Buffer
= ExAllocatePoolWithTag(PagedPool
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
, ALLOC_TAG
);
851 if (!ccb
->query_string
.Buffer
) {
852 ERR("out of memory\n");
853 return STATUS_INSUFFICIENT_RESOURCES
;
856 ccb
->query_string
.Length
= ccb
->query_string
.MaximumLength
= IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
;
857 RtlCopyMemory(ccb
->query_string
.Buffer
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Buffer
, IrpSp
->Parameters
.QueryDirectory
.FileName
->Length
);
860 ccb
->has_wildcard
= has_wildcard
;
861 ccb
->specific_file
= specific_file
;
863 has_wildcard
= ccb
->has_wildcard
;
864 specific_file
= ccb
->specific_file
;
866 if (!(IrpSp
->Flags
& SL_RESTART_SCAN
)) {
870 return STATUS_NO_MORE_FILES
;
874 if (ccb
->query_string
.Buffer
) {
875 TRACE("query string = %.*S\n", ccb
->query_string
.Length
/ sizeof(WCHAR
), ccb
->query_string
.Buffer
);
878 newoffset
= ccb
->query_dir_offset
;
880 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
882 ExAcquireResourceSharedLite(&fileref
->fcb
->nonpaged
->dir_children_lock
, true);
884 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
);
886 if (!NT_SUCCESS(Status
)) {
887 if (Status
== STATUS_NO_MORE_FILES
&& initial
)
888 Status
= STATUS_NO_SUCH_FILE
;
892 ccb
->query_dir_offset
= newoffset
;
894 buf
= map_user_buffer(Irp
, NormalPagePriority
);
896 if (Irp
->MdlAddress
&& !buf
) {
897 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
898 Status
= STATUS_INSUFFICIENT_RESOURCES
;
902 length
= IrpSp
->Parameters
.QueryDirectory
.Length
;
913 if (!ccb
->case_sensitive
) {
914 Status
= RtlUpcaseUnicodeString(&us
, &ccb
->query_string
, true);
915 if (!NT_SUCCESS(Status
)) {
916 ERR("RtlUpcaseUnicodeString returned %08x\n", Status
);
920 hash
= calc_crc32c(0xffffffff, (uint8_t*)us
.Buffer
, us
.Length
);
922 hash
= calc_crc32c(0xffffffff, (uint8_t*)ccb
->query_string
.Buffer
, ccb
->query_string
.Length
);
926 if (ccb
->case_sensitive
) {
927 if (fileref
->fcb
->hash_ptrs
[c
]) {
928 le
= fileref
->fcb
->hash_ptrs
[c
];
929 while (le
!= &fileref
->fcb
->dir_children_hash
) {
930 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash
);
932 if (dc2
->hash
== hash
) {
933 if (dc2
->name
.Length
== ccb
->query_string
.Length
&& RtlCompareMemory(dc2
->name
.Buffer
, ccb
->query_string
.Buffer
, ccb
->query_string
.Length
) == ccb
->query_string
.Length
) {
939 de
.dir_entry_type
= DirEntryType_File
;
944 } else if (dc2
->hash
> hash
)
951 if (fileref
->fcb
->hash_ptrs_uc
[c
]) {
952 le
= fileref
->fcb
->hash_ptrs_uc
[c
];
953 while (le
!= &fileref
->fcb
->dir_children_hash_uc
) {
954 dir_child
* dc2
= CONTAINING_RECORD(le
, dir_child
, list_entry_hash_uc
);
956 if (dc2
->hash_uc
== hash
) {
957 if (dc2
->name_uc
.Length
== us
.Length
&& RtlCompareMemory(dc2
->name_uc
.Buffer
, us
.Buffer
, us
.Length
) == us
.Length
) {
963 de
.dir_entry_type
= DirEntryType_File
;
968 } else if (dc2
->hash_uc
> hash
)
977 ExFreePool(us
.Buffer
);
980 Status
= STATUS_NO_SUCH_FILE
;
983 } else if (has_wildcard
) {
984 while (!FsRtlIsNameInExpression(&ccb
->query_string
, &de
.name
, !ccb
->case_sensitive
, NULL
)) {
985 newoffset
= ccb
->query_dir_offset
;
986 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
);
988 if (NT_SUCCESS(Status
))
989 ccb
->query_dir_offset
= newoffset
;
991 if (Status
== STATUS_NO_MORE_FILES
&& initial
)
992 Status
= STATUS_NO_SUCH_FILE
;
999 TRACE("file(0) = %.*S\n", de
.name
.Length
/ sizeof(WCHAR
), de
.name
.Buffer
);
1000 TRACE("offset = %u\n", ccb
->query_dir_offset
- 1);
1002 Status
= query_dir_item(fcb
, ccb
, buf
, &length
, Irp
, &de
, fcb
->subvol
);
1005 if (NT_SUCCESS(Status
) && !(IrpSp
->Flags
& SL_RETURN_SINGLE_ENTRY
) && !specific_file
) {
1006 lastitem
= (uint8_t*)buf
;
1008 while (length
> 0) {
1009 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
1011 #pragma GCC diagnostic push
1012 #pragma GCC diagnostic ignored "-Wswitch"
1014 case FileBothDirectoryInformation
:
1015 case FileDirectoryInformation
:
1016 case FileIdBothDirectoryInformation
:
1017 case FileFullDirectoryInformation
:
1018 case FileIdFullDirectoryInformation
:
1019 case FileIdExtdDirectoryInformation
:
1020 case FileIdExtdBothDirectoryInformation
:
1021 length
-= length
% 8;
1024 #pragma GCC diagnostic pop
1027 case FileNamesInformation
:
1028 length
-= length
% 4;
1032 WARN("unhandled file information class %u\n", IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
);
1037 newoffset
= ccb
->query_dir_offset
;
1038 Status
= next_dir_entry(fileref
, &newoffset
, &de
, &dc
);
1039 if (NT_SUCCESS(Status
)) {
1040 if (!has_wildcard
|| FsRtlIsNameInExpression(&ccb
->query_string
, &de
.name
, !ccb
->case_sensitive
, NULL
)) {
1041 curitem
= (uint8_t*)buf
+ IrpSp
->Parameters
.QueryDirectory
.Length
- length
;
1044 TRACE("file(%u) %u = %.*S\n", count
, curitem
- (uint8_t*)buf
, de
.name
.Length
/ sizeof(WCHAR
), de
.name
.Buffer
);
1045 TRACE("offset = %u\n", ccb
->query_dir_offset
- 1);
1047 status2
= query_dir_item(fcb
, ccb
, curitem
, &length
, Irp
, &de
, fcb
->subvol
);
1049 if (NT_SUCCESS(status2
)) {
1050 ULONG
* lastoffset
= (ULONG
*)lastitem
;
1052 *lastoffset
= (ULONG
)(curitem
- lastitem
);
1053 ccb
->query_dir_offset
= newoffset
;
1059 ccb
->query_dir_offset
= newoffset
;
1061 if (Status
== STATUS_NO_MORE_FILES
)
1062 Status
= STATUS_SUCCESS
;
1071 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.QueryDirectory
.Length
- length
;
1074 ExReleaseResourceLite(&fileref
->fcb
->nonpaged
->dir_children_lock
);
1076 ExReleaseResourceLite(&Vcb
->tree_lock
);
1078 TRACE("returning %08x\n", Status
);
1083 static NTSTATUS
notify_change_directory(device_extension
* Vcb
, PIRP Irp
) {
1084 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1085 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
1086 fcb
* fcb
= FileObject
->FsContext
;
1087 ccb
* ccb
= FileObject
->FsContext2
;
1088 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1091 TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
1094 ERR("ccb was NULL\n");
1095 return STATUS_INVALID_PARAMETER
;
1099 ERR("no fileref\n");
1100 return STATUS_INVALID_PARAMETER
;
1103 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& FILE_LIST_DIRECTORY
)) {
1104 WARN("insufficient privileges\n");
1105 return STATUS_ACCESS_DENIED
;
1108 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, true);
1109 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, true);
1111 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
1112 Status
= STATUS_INVALID_PARAMETER
;
1116 // FIXME - raise exception if FCB marked for deletion?
1118 TRACE("%S\n", file_desc(FileObject
));
1120 if (ccb
->filename
.Length
== 0) {
1123 ccb
->filename
.MaximumLength
= ccb
->filename
.Length
= 0;
1125 Status
= fileref_get_filename(fileref
, &ccb
->filename
, NULL
, &reqlen
);
1126 if (Status
== STATUS_BUFFER_OVERFLOW
) {
1127 ccb
->filename
.Buffer
= ExAllocatePoolWithTag(PagedPool
, reqlen
, ALLOC_TAG
);
1128 if (!ccb
->filename
.Buffer
) {
1129 ERR("out of memory\n");
1130 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1134 ccb
->filename
.MaximumLength
= (uint16_t)reqlen
;
1136 Status
= fileref_get_filename(fileref
, &ccb
->filename
, NULL
, &reqlen
);
1137 if (!NT_SUCCESS(Status
)) {
1138 ERR("fileref_get_filename returned %08x\n", Status
);
1142 ERR("fileref_get_filename returned %08x\n", Status
);
1147 FsRtlNotifyFilterChangeDirectory(Vcb
->NotifySync
, &Vcb
->DirNotifyList
, FileObject
->FsContext2
, (PSTRING
)&ccb
->filename
,
1148 IrpSp
->Flags
& SL_WATCH_TREE
, false, IrpSp
->Parameters
.NotifyDirectory
.CompletionFilter
, Irp
,
1151 Status
= STATUS_PENDING
;
1154 ExReleaseResourceLite(fcb
->Header
.Resource
);
1155 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
1160 _Dispatch_type_(IRP_MJ_DIRECTORY_CONTROL
)
1161 _Function_class_(DRIVER_DISPATCH
)
1162 NTSTATUS __stdcall
drv_directory_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
1163 PIO_STACK_LOCATION IrpSp
;
1167 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
1169 FsRtlEnterFileSystem();
1171 TRACE("directory control\n");
1173 top_level
= is_top_level(Irp
);
1175 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
1176 Status
= vol_directory_control(DeviceObject
, Irp
);
1178 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
1179 Status
= STATUS_INVALID_PARAMETER
;
1183 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1185 Irp
->IoStatus
.Information
= 0;
1187 func
= IrpSp
->MinorFunction
;
1190 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
1191 Status
= notify_change_directory(Vcb
, Irp
);
1194 case IRP_MN_QUERY_DIRECTORY
:
1195 Status
= query_directory(Irp
);
1199 WARN("unknown minor %u\n", func
);
1200 Status
= STATUS_NOT_IMPLEMENTED
;
1201 Irp
->IoStatus
.Status
= Status
;
1205 if (Status
== STATUS_PENDING
)
1209 Irp
->IoStatus
.Status
= Status
;
1211 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
1214 TRACE("returning %08x\n", Status
);
1217 IoSetTopLevelIrp(NULL
);
1219 FsRtlExitFileSystem();