[CDFS]
[reactos.git] / reactos / drivers / filesystems / btrfs / dirctrl.c
1 /* Copyright (c) Mark Harmstone 2016
2 *
3 * This file is part of WinBtrfs.
4 *
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.
9 *
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.
14 *
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/>. */
17
18 #include "btrfs_drv.h"
19
20 enum DirEntryType {
21 DirEntryType_File,
22 DirEntryType_Self,
23 DirEntryType_Parent
24 };
25
26 typedef struct {
27 KEY key;
28 UNICODE_STRING name;
29 UINT8 type;
30 enum DirEntryType dir_entry_type;
31 } dir_entry;
32
33 ULONG STDCALL get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type, ULONG atts, PIRP Irp) {
34 fcb* fcb;
35 ULONG tag = 0, br;
36 NTSTATUS Status;
37
38 if (type == BTRFS_TYPE_SYMLINK) {
39 if (called_from_lxss())
40 return IO_REPARSE_TAG_LXSS_SYMLINK;
41 else
42 return IO_REPARSE_TAG_SYMLINK;
43 }
44
45 if (type != BTRFS_TYPE_FILE && type != BTRFS_TYPE_DIRECTORY)
46 return 0;
47
48 if (!(atts & FILE_ATTRIBUTE_REPARSE_POINT))
49 return 0;
50
51 Status = open_fcb(Vcb, subvol, inode, type, NULL, NULL, &fcb, PagedPool, Irp);
52 if (!NT_SUCCESS(Status)) {
53 ERR("open_fcb returned %08x\n", Status);
54 return 0;
55 }
56
57 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
58
59 if (type == BTRFS_TYPE_DIRECTORY) {
60 if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length < sizeof(ULONG))
61 goto end;
62
63 RtlCopyMemory(&tag, fcb->reparse_xattr.Buffer, sizeof(ULONG));
64 } else {
65 Status = read_file(fcb, (UINT8*)&tag, 0, sizeof(ULONG), &br, NULL, TRUE);
66 if (!NT_SUCCESS(Status)) {
67 ERR("read_file returned %08x\n", Status);
68 goto end;
69 }
70
71 if (br < sizeof(ULONG))
72 goto end;
73 }
74
75 end:
76 ExReleaseResourceLite(fcb->Header.Resource);
77
78 free_fcb(fcb);
79
80 return tag;
81 }
82
83 static ULONG get_ea_len(device_extension* Vcb, root* subvol, UINT64 inode, PIRP Irp) {
84 UINT8* eadata;
85 UINT16 len;
86
87 if (get_xattr(Vcb, subvol, inode, EA_EA, EA_EA_HASH, &eadata, &len, Irp)) {
88 ULONG offset;
89 NTSTATUS Status;
90
91 Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)eadata, len, &offset);
92
93 if (!NT_SUCCESS(Status)) {
94 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
95 ExFreePool(eadata);
96 return 0;
97 } else {
98 FILE_FULL_EA_INFORMATION* eainfo;
99 ULONG ealen;
100
101 ealen = 4;
102 eainfo = (FILE_FULL_EA_INFORMATION*)eadata;
103 do {
104 ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
105
106 if (eainfo->NextEntryOffset == 0)
107 break;
108
109 eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) + eainfo->NextEntryOffset);
110 } while (TRUE);
111
112 ExFreePool(eadata);
113
114 return ealen;
115 }
116 } else
117 return 0;
118 }
119
120 static NTSTATUS STDCALL query_dir_item(fcb* fcb, file_ref* fileref, void* buf, LONG* len, PIRP Irp, dir_entry* de, root* r) {
121 PIO_STACK_LOCATION IrpSp;
122 UINT32 needed;
123 UINT64 inode;
124 INODE_ITEM ii;
125 NTSTATUS Status;
126 ULONG atts, ealen;
127
128 IrpSp = IoGetCurrentIrpStackLocation(Irp);
129
130 if (de->key.obj_type == TYPE_ROOT_ITEM) { // subvol
131 LIST_ENTRY* le;
132
133 r = NULL;
134
135 le = fcb->Vcb->roots.Flink;
136 while (le != &fcb->Vcb->roots) {
137 root* subvol = CONTAINING_RECORD(le, root, list_entry);
138
139 if (subvol->id == de->key.obj_id) {
140 r = subvol;
141 break;
142 }
143
144 le = le->Flink;
145 }
146
147 if (!r) {
148 ERR("could not find root %llx\n", de->key.obj_id);
149 return STATUS_OBJECT_NAME_NOT_FOUND;
150 }
151
152 inode = SUBVOL_ROOT_INODE;
153 } else {
154 inode = de->key.obj_id;
155 }
156
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:
160 {
161 LIST_ENTRY* le;
162 BOOL found = FALSE;
163
164 if (!IsListEmpty(&r->fcbs)) {
165 le = r->fcbs.Flink;
166 while (le != &r->fcbs) {
167 struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
168
169 if (fcb2->inode == inode && !fcb2->ads) {
170 ii = fcb2->inode_item;
171 atts = fcb2->atts;
172 ealen = fcb2->ealen;
173 found = TRUE;
174 break;
175 } else if (fcb2->inode > inode)
176 break;
177
178 le = le->Flink;
179 }
180 }
181
182 if (!found) {
183 KEY searchkey;
184 traverse_ptr tp;
185
186 searchkey.obj_id = inode;
187 searchkey.obj_type = TYPE_INODE_ITEM;
188 searchkey.offset = 0xffffffffffffffff;
189
190 Status = find_item(fcb->Vcb, r, &tp, &searchkey, FALSE, Irp);
191 if (!NT_SUCCESS(Status)) {
192 ERR("error - find_item returned %08x\n", Status);
193 return Status;
194 }
195
196 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
197 ERR("could not find inode item for inode %llx in root %llx\n", inode, r->id);
198 return STATUS_INTERNAL_ERROR;
199 }
200
201 RtlZeroMemory(&ii, sizeof(INODE_ITEM));
202
203 if (tp.item->size > 0)
204 RtlCopyMemory(&ii, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
205
206 if (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation ||
207 IrpSp->Parameters.QueryDirectory.FileInformationClass == FileDirectoryInformation ||
208 IrpSp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation ||
209 IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation ||
210 IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdFullDirectoryInformation) {
211
212 BOOL dotfile = de->name.Length > sizeof(WCHAR) && de->name.Buffer[0] == '.';
213
214 atts = get_file_attributes(fcb->Vcb, &ii, r, inode, de->type, dotfile, FALSE, Irp);
215 }
216
217 if (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation ||
218 IrpSp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation ||
219 IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation ||
220 IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdFullDirectoryInformation) {
221 ealen = get_ea_len(fcb->Vcb, r, inode, Irp);
222 }
223 }
224
225 break;
226 }
227
228 case DirEntryType_Self:
229 ii = fcb->inode_item;
230 r = fcb->subvol;
231 inode = fcb->inode;
232 atts = fcb->atts;
233 ealen = fcb->ealen;
234 break;
235
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;
243 } else {
244 ERR("no fileref\n");
245 return STATUS_INTERNAL_ERROR;
246 }
247 break;
248 }
249 }
250
251 switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
252 case FileBothDirectoryInformation:
253 {
254 FILE_BOTH_DIR_INFORMATION* fbdi = buf;
255
256 TRACE("FileBothDirectoryInformation\n");
257
258 needed = sizeof(FILE_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + de->name.Length;
259
260 if (needed > *len) {
261 TRACE("buffer overflow - %u > %u\n", needed, *len);
262 return STATUS_BUFFER_OVERFLOW;
263 }
264
265 fbdi->NextEntryOffset = 0;
266 fbdi->FileIndex = 0;
267 fbdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
268 fbdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
269 fbdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
270 fbdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
271 fbdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
272 fbdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
273 fbdi->FileAttributes = atts;
274 fbdi->FileNameLength = de->name.Length;
275 fbdi->EaSize = atts & FILE_ATTRIBUTE_REPARSE_POINT ? get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp) : ealen;
276 fbdi->ShortNameLength = 0;
277 // fibdi->ShortName[12];
278
279 RtlCopyMemory(fbdi->FileName, de->name.Buffer, de->name.Length);
280
281 *len -= needed;
282
283 return STATUS_SUCCESS;
284 }
285
286 case FileDirectoryInformation:
287 {
288 FILE_DIRECTORY_INFORMATION* fdi = buf;
289
290 TRACE("FileDirectoryInformation\n");
291
292 needed = sizeof(FILE_DIRECTORY_INFORMATION) - sizeof(WCHAR) + de->name.Length;
293
294 if (needed > *len) {
295 TRACE("buffer overflow - %u > %u\n", needed, *len);
296 return STATUS_BUFFER_OVERFLOW;
297 }
298
299 fdi->NextEntryOffset = 0;
300 fdi->FileIndex = 0;
301 fdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
302 fdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
303 fdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
304 fdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
305 fdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
306 fdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
307 fdi->FileAttributes = atts;
308 fdi->FileNameLength = de->name.Length;
309
310 RtlCopyMemory(fdi->FileName, de->name.Buffer, de->name.Length);
311
312 *len -= needed;
313
314 return STATUS_SUCCESS;
315 }
316
317 case FileFullDirectoryInformation:
318 {
319 FILE_FULL_DIR_INFORMATION* ffdi = buf;
320
321 TRACE("FileFullDirectoryInformation\n");
322
323 needed = sizeof(FILE_FULL_DIR_INFORMATION) - sizeof(WCHAR) + de->name.Length;
324
325 if (needed > *len) {
326 TRACE("buffer overflow - %u > %u\n", needed, *len);
327 return STATUS_BUFFER_OVERFLOW;
328 }
329
330 ffdi->NextEntryOffset = 0;
331 ffdi->FileIndex = 0;
332 ffdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
333 ffdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
334 ffdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
335 ffdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
336 ffdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
337 ffdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
338 ffdi->FileAttributes = atts;
339 ffdi->FileNameLength = de->name.Length;
340 ffdi->EaSize = atts & FILE_ATTRIBUTE_REPARSE_POINT ? get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp) : ealen;
341
342 RtlCopyMemory(ffdi->FileName, de->name.Buffer, de->name.Length);
343
344 *len -= needed;
345
346 return STATUS_SUCCESS;
347 }
348
349 case FileIdBothDirectoryInformation:
350 {
351 FILE_ID_BOTH_DIR_INFORMATION* fibdi = buf;
352
353 TRACE("FileIdBothDirectoryInformation\n");
354
355 needed = sizeof(FILE_ID_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + de->name.Length;
356
357 if (needed > *len) {
358 TRACE("buffer overflow - %u > %u\n", needed, *len);
359 return STATUS_BUFFER_OVERFLOW;
360 }
361
362 // if (!buf)
363 // return STATUS_INVALID_POINTER;
364
365 fibdi->NextEntryOffset = 0;
366 fibdi->FileIndex = 0;
367 fibdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
368 fibdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
369 fibdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
370 fibdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
371 fibdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
372 fibdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
373 fibdi->FileAttributes = atts;
374 fibdi->FileNameLength = de->name.Length;
375 fibdi->EaSize = atts & FILE_ATTRIBUTE_REPARSE_POINT ? get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp) : ealen;
376 fibdi->ShortNameLength = 0;
377 // fibdi->ShortName[12];
378 fibdi->FileId.QuadPart = make_file_id(r, inode);
379
380 RtlCopyMemory(fibdi->FileName, de->name.Buffer, de->name.Length);
381
382 *len -= needed;
383
384 return STATUS_SUCCESS;
385 }
386
387 case FileIdFullDirectoryInformation:
388 {
389 FILE_ID_FULL_DIR_INFORMATION* fifdi = buf;
390
391 TRACE("FileIdFullDirectoryInformation\n");
392
393 needed = sizeof(FILE_ID_FULL_DIR_INFORMATION) - sizeof(WCHAR) + de->name.Length;
394
395 if (needed > *len) {
396 TRACE("buffer overflow - %u > %u\n", needed, *len);
397 return STATUS_BUFFER_OVERFLOW;
398 }
399
400 // if (!buf)
401 // return STATUS_INVALID_POINTER;
402
403 fifdi->NextEntryOffset = 0;
404 fifdi->FileIndex = 0;
405 fifdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime);
406 fifdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime);
407 fifdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime);
408 fifdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime);
409 fifdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size;
410 fifdi->AllocationSize.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_blocks;
411 fifdi->FileAttributes = atts;
412 fifdi->FileNameLength = de->name.Length;
413 fifdi->EaSize = atts & FILE_ATTRIBUTE_REPARSE_POINT ? get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, Irp) : ealen;
414 fifdi->FileId.QuadPart = make_file_id(r, inode);
415
416 RtlCopyMemory(fifdi->FileName, de->name.Buffer, de->name.Length);
417
418 *len -= needed;
419
420 return STATUS_SUCCESS;
421 }
422
423 case FileNamesInformation:
424 {
425 FILE_NAMES_INFORMATION* fni = buf;
426
427 TRACE("FileNamesInformation\n");
428
429 needed = sizeof(FILE_NAMES_INFORMATION) - sizeof(WCHAR) + de->name.Length;
430
431 if (needed > *len) {
432 TRACE("buffer overflow - %u > %u\n", needed, *len);
433 return STATUS_BUFFER_OVERFLOW;
434 }
435
436 fni->NextEntryOffset = 0;
437 fni->FileIndex = 0;
438 fni->FileNameLength = de->name.Length;
439
440 RtlCopyMemory(fni->FileName, de->name.Buffer, de->name.Length);
441
442 *len -= needed;
443
444 return STATUS_SUCCESS;
445 }
446
447 case FileObjectIdInformation:
448 FIXME("STUB: FileObjectIdInformation\n");
449 return STATUS_NOT_IMPLEMENTED;
450
451 case FileQuotaInformation:
452 FIXME("STUB: FileQuotaInformation\n");
453 return STATUS_NOT_IMPLEMENTED;
454
455 case FileReparsePointInformation:
456 FIXME("STUB: FileReparsePointInformation\n");
457 return STATUS_NOT_IMPLEMENTED;
458
459 default:
460 WARN("Unknown FileInformationClass %u\n", IrpSp->Parameters.QueryDirectory.FileInformationClass);
461 return STATUS_NOT_IMPLEMENTED;
462 }
463
464 return STATUS_NO_MORE_FILES;
465 }
466
467 static NTSTATUS STDCALL next_dir_entry(file_ref* fileref, UINT64* offset, dir_entry* de, dir_child** pdc, PIRP Irp) {
468 LIST_ENTRY* le;
469 dir_child* dc;
470
471 if (*pdc) {
472 dir_child* dc2 = *pdc;
473
474 if (dc2->list_entry_index.Flink != &fileref->fcb->dir_children_index)
475 dc = CONTAINING_RECORD(dc2->list_entry_index.Flink, dir_child, list_entry_index);
476 else
477 dc = NULL;
478
479 goto next;
480 }
481
482 if (fileref->parent) { // don't return . and .. if root directory
483 if (*offset == 0) {
484 de->key.obj_id = fileref->fcb->inode;
485 de->key.obj_type = TYPE_INODE_ITEM;
486 de->key.offset = 0;
487 de->dir_entry_type = DirEntryType_Self;
488 de->name.Buffer = L".";
489 de->name.Length = de->name.MaximumLength = sizeof(WCHAR);
490 de->type = BTRFS_TYPE_DIRECTORY;
491
492 *offset = 1;
493 *pdc = NULL;
494
495 return STATUS_SUCCESS;
496 } else if (*offset == 1) {
497 de->key.obj_id = fileref->parent->fcb->inode;
498 de->key.obj_type = TYPE_INODE_ITEM;
499 de->key.offset = 0;
500 de->dir_entry_type = DirEntryType_Parent;
501 de->name.Buffer = L"..";
502 de->name.Length = de->name.MaximumLength = sizeof(WCHAR) * 2;
503 de->type = BTRFS_TYPE_DIRECTORY;
504
505 *offset = 2;
506 *pdc = NULL;
507
508 return STATUS_SUCCESS;
509 }
510 }
511
512 if (*offset < 2)
513 *offset = 2;
514
515 dc = NULL;
516 le = fileref->fcb->dir_children_index.Flink;
517
518 // skip entries before offset
519 while (le != &fileref->fcb->dir_children_index) {
520 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_index);
521
522 if (dc2->index >= *offset) {
523 dc = dc2;
524 break;
525 }
526
527 le = le->Flink;
528 }
529
530 next:
531 if (!dc)
532 return STATUS_NO_MORE_FILES;
533
534 de->key = dc->key;
535 de->name = dc->name;
536 de->type = dc->type;
537 de->dir_entry_type = DirEntryType_File;
538
539 *offset = dc->index + 1;
540 *pdc = dc;
541
542 return STATUS_SUCCESS;
543 }
544
545 static NTSTATUS STDCALL query_directory(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
546 PIO_STACK_LOCATION IrpSp;
547 NTSTATUS Status, status2;
548 fcb* fcb;
549 ccb* ccb;
550 file_ref* fileref;
551 void* buf;
552 UINT8 *curitem, *lastitem;
553 LONG length;
554 ULONG count;
555 BOOL has_wildcard = FALSE, specific_file = FALSE, initial;
556 // UINT64 num_reads_orig;
557 dir_entry de;
558 UINT64 newoffset;
559 ANSI_STRING utf8;
560 dir_child* dc = NULL;
561
562 TRACE("query directory\n");
563
564 // get_uid(); // TESTING
565
566 // num_reads_orig = num_reads;
567
568 IrpSp = IoGetCurrentIrpStackLocation(Irp);
569 fcb = IrpSp->FileObject->FsContext;
570 ccb = IrpSp->FileObject->FsContext2;
571 fileref = ccb ? ccb->fileref : NULL;
572
573 utf8.Buffer = NULL;
574
575 if (!fileref)
576 return STATUS_INVALID_PARAMETER;
577
578 if (!ccb) {
579 ERR("ccb was NULL\n");
580 return STATUS_INVALID_PARAMETER;
581 }
582
583 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_LIST_DIRECTORY)) {
584 WARN("insufficient privileges\n");
585 return STATUS_ACCESS_DENIED;
586 }
587
588 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
589 ExAcquireResourceSharedLite(&fcb->Vcb->fcb_lock, TRUE);
590
591 TRACE("%S\n", file_desc(IrpSp->FileObject));
592
593 if (IrpSp->Flags == 0) {
594 TRACE("QD flags: (none)\n");
595 } else {
596 ULONG flags = IrpSp->Flags;
597
598 TRACE("QD flags:\n");
599
600 if (flags & SL_INDEX_SPECIFIED) {
601 TRACE(" SL_INDEX_SPECIFIED\n");
602 flags &= ~SL_INDEX_SPECIFIED;
603 }
604
605 if (flags & SL_RESTART_SCAN) {
606 TRACE(" SL_RESTART_SCAN\n");
607 flags &= ~SL_RESTART_SCAN;
608 }
609
610 if (flags & SL_RETURN_SINGLE_ENTRY) {
611 TRACE(" SL_RETURN_SINGLE_ENTRY\n");
612 flags &= ~SL_RETURN_SINGLE_ENTRY;
613 }
614
615 if (flags != 0)
616 TRACE(" unknown flags: %u\n", flags);
617 }
618
619 initial = !ccb->query_string.Buffer;
620
621 if (IrpSp->Flags & SL_RESTART_SCAN) {
622 ccb->query_dir_offset = 0;
623
624 if (ccb->query_string.Buffer) {
625 RtlFreeUnicodeString(&ccb->query_string);
626 ccb->query_string.Buffer = NULL;
627 }
628 }
629
630 if (IrpSp->Parameters.QueryDirectory.FileName && IrpSp->Parameters.QueryDirectory.FileName->Length > 1) {
631 TRACE("QD filename: %.*S\n", IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR), IrpSp->Parameters.QueryDirectory.FileName->Buffer);
632
633 if (IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != '*') {
634 specific_file = TRUE;
635 if (!ccb->case_sensitive || FsRtlDoesNameContainWildCards(IrpSp->Parameters.QueryDirectory.FileName)) {
636 has_wildcard = TRUE;
637 specific_file = FALSE;
638 }
639 }
640
641 if (ccb->query_string.Buffer)
642 RtlFreeUnicodeString(&ccb->query_string);
643
644 if (has_wildcard)
645 RtlUpcaseUnicodeString(&ccb->query_string, IrpSp->Parameters.QueryDirectory.FileName, TRUE);
646 else {
647 ccb->query_string.Buffer = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length, ALLOC_TAG);
648 if (!ccb->query_string.Buffer) {
649 ERR("out of memory\n");
650 Status = STATUS_INSUFFICIENT_RESOURCES;
651 goto end2;
652 }
653
654 ccb->query_string.Length = ccb->query_string.MaximumLength = IrpSp->Parameters.QueryDirectory.FileName->Length;
655 RtlCopyMemory(ccb->query_string.Buffer, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length);
656 }
657
658 ccb->has_wildcard = has_wildcard;
659 ccb->specific_file = specific_file;
660 } else {
661 has_wildcard = ccb->has_wildcard;
662 specific_file = ccb->specific_file;
663
664 if (!(IrpSp->Flags & SL_RESTART_SCAN)) {
665 initial = FALSE;
666
667 if (specific_file) {
668 Status = STATUS_NO_MORE_FILES;
669 goto end2;
670 }
671 }
672 }
673
674 if (ccb->query_string.Buffer) {
675 TRACE("query string = %.*S\n", ccb->query_string.Length / sizeof(WCHAR), ccb->query_string.Buffer);
676 }
677
678 newoffset = ccb->query_dir_offset;
679
680 ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
681
682 Status = next_dir_entry(fileref, &newoffset, &de, &dc, Irp);
683
684 if (!NT_SUCCESS(Status)) {
685 if (Status == STATUS_NO_MORE_FILES && initial)
686 Status = STATUS_NO_SUCH_FILE;
687 goto end;
688 }
689
690 ccb->query_dir_offset = newoffset;
691
692 buf = map_user_buffer(Irp);
693
694 if (Irp->MdlAddress && !buf) {
695 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
696 Status = STATUS_INSUFFICIENT_RESOURCES;
697 goto end;
698 }
699
700 length = IrpSp->Parameters.QueryDirectory.Length;
701
702 if (specific_file) {
703 BOOL found = FALSE;
704 UNICODE_STRING us;
705 LIST_ENTRY* le;
706 UINT32 hash;
707 UINT8 c;
708
709 us.Buffer = NULL;
710
711 if (!ccb->case_sensitive) {
712 Status = RtlUpcaseUnicodeString(&us, &ccb->query_string, TRUE);
713 if (!NT_SUCCESS(Status)) {
714 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
715 goto end;
716 }
717
718 hash = calc_crc32c(0xffffffff, (UINT8*)us.Buffer, us.Length);
719 } else
720 hash = calc_crc32c(0xffffffff, (UINT8*)ccb->query_string.Buffer, ccb->query_string.Length);
721
722 c = hash >> 24;
723
724 if (ccb->case_sensitive) {
725 if (fileref->fcb->hash_ptrs[c]) {
726 le = fileref->fcb->hash_ptrs[c];
727 while (le != &fileref->fcb->dir_children_hash) {
728 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash);
729
730 if (dc2->hash == hash) {
731 if (dc2->name.Length == ccb->query_string.Length && RtlCompareMemory(dc2->name.Buffer, ccb->query_string.Buffer, ccb->query_string.Length) == ccb->query_string.Length) {
732 found = TRUE;
733
734 de.key = dc2->key;
735 de.name = dc2->name;
736 de.type = dc2->type;
737 de.dir_entry_type = DirEntryType_File;
738
739 break;
740 }
741 } else if (dc2->hash > hash)
742 break;
743
744 le = le->Flink;
745 }
746 }
747 } else {
748 if (fileref->fcb->hash_ptrs_uc[c]) {
749 le = fileref->fcb->hash_ptrs_uc[c];
750 while (le != &fileref->fcb->dir_children_hash_uc) {
751 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
752
753 if (dc2->hash_uc == hash) {
754 if (dc2->name_uc.Length == us.Length && RtlCompareMemory(dc2->name_uc.Buffer, us.Buffer, us.Length) == us.Length) {
755 found = TRUE;
756
757 de.key = dc2->key;
758 de.name = dc2->name;
759 de.type = dc2->type;
760 de.dir_entry_type = DirEntryType_File;
761
762 break;
763 }
764 } else if (dc2->hash_uc > hash)
765 break;
766
767 le = le->Flink;
768 }
769 }
770 }
771
772 if (us.Buffer)
773 ExFreePool(us.Buffer);
774
775 if (!found) {
776 Status = STATUS_NO_SUCH_FILE;
777 goto end;
778 }
779 } else if (has_wildcard) {
780 while (!FsRtlIsNameInExpression(&ccb->query_string, &de.name, !ccb->case_sensitive, NULL)) {
781 newoffset = ccb->query_dir_offset;
782 Status = next_dir_entry(fileref, &newoffset, &de, &dc, Irp);
783
784 if (NT_SUCCESS(Status))
785 ccb->query_dir_offset = newoffset;
786 else {
787 if (Status == STATUS_NO_MORE_FILES && initial)
788 Status = STATUS_NO_SUCH_FILE;
789
790 goto end;
791 }
792 }
793 }
794
795 TRACE("file(0) = %.*S\n", de.name.Length / sizeof(WCHAR), de.name.Buffer);
796 TRACE("offset = %u\n", ccb->query_dir_offset - 1);
797
798 Status = query_dir_item(fcb, fileref, buf, &length, Irp, &de, fcb->subvol);
799
800 count = 0;
801 if (NT_SUCCESS(Status) && !(IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) && !specific_file) {
802 lastitem = (UINT8*)buf;
803
804 while (length > 0) {
805 switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
806 case FileBothDirectoryInformation:
807 case FileDirectoryInformation:
808 case FileIdBothDirectoryInformation:
809 case FileFullDirectoryInformation:
810 case FileIdFullDirectoryInformation:
811 length -= length % 8;
812 break;
813
814 case FileNamesInformation:
815 length -= length % 4;
816 break;
817
818 default:
819 WARN("unhandled file information class %u\n", IrpSp->Parameters.QueryDirectory.FileInformationClass);
820 break;
821 }
822
823 if (length > 0) {
824 newoffset = ccb->query_dir_offset;
825 Status = next_dir_entry(fileref, &newoffset, &de, &dc, Irp);
826 if (NT_SUCCESS(Status)) {
827 if (!has_wildcard || FsRtlIsNameInExpression(&ccb->query_string, &de.name, !ccb->case_sensitive, NULL)) {
828 curitem = (UINT8*)buf + IrpSp->Parameters.QueryDirectory.Length - length;
829 count++;
830
831 TRACE("file(%u) %u = %.*S\n", count, curitem - (UINT8*)buf, de.name.Length / sizeof(WCHAR), de.name.Buffer);
832 TRACE("offset = %u\n", ccb->query_dir_offset - 1);
833
834 status2 = query_dir_item(fcb, fileref, curitem, &length, Irp, &de, fcb->subvol);
835
836 if (NT_SUCCESS(status2)) {
837 ULONG* lastoffset = (ULONG*)lastitem;
838
839 *lastoffset = (ULONG)(curitem - lastitem);
840 ccb->query_dir_offset = newoffset;
841
842 lastitem = curitem;
843 } else
844 break;
845 } else
846 ccb->query_dir_offset = newoffset;
847 } else {
848 if (Status == STATUS_NO_MORE_FILES)
849 Status = STATUS_SUCCESS;
850
851 break;
852 }
853 } else
854 break;
855 }
856 }
857
858 Irp->IoStatus.Information = IrpSp->Parameters.QueryDirectory.Length - length;
859
860 end:
861 ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
862
863 end2:
864 ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
865 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
866
867 TRACE("returning %08x\n", Status);
868
869 if (utf8.Buffer)
870 ExFreePool(utf8.Buffer);
871
872 return Status;
873 }
874
875 static NTSTATUS STDCALL notify_change_directory(device_extension* Vcb, PIRP Irp) {
876 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
877 PFILE_OBJECT FileObject = IrpSp->FileObject;
878 fcb* fcb = FileObject->FsContext;
879 ccb* ccb = FileObject->FsContext2;
880 file_ref* fileref = ccb->fileref;
881 NTSTATUS Status;
882
883 TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
884
885 if (!ccb) {
886 ERR("ccb was NULL\n");
887 return STATUS_INVALID_PARAMETER;
888 }
889
890 if (!fileref) {
891 ERR("no fileref\n");
892 return STATUS_INVALID_PARAMETER;
893 }
894
895 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_LIST_DIRECTORY)) {
896 WARN("insufficient privileges\n");
897 return STATUS_ACCESS_DENIED;
898 }
899
900 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
901 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
902
903 if (fcb->type != BTRFS_TYPE_DIRECTORY) {
904 Status = STATUS_INVALID_PARAMETER;
905 goto end;
906 }
907
908 // FIXME - raise exception if FCB marked for deletion?
909
910 TRACE("%S\n", file_desc(FileObject));
911
912 if (ccb->filename.Length == 0) {
913 Status = fileref_get_filename(fileref, &ccb->filename, NULL);
914 if (!NT_SUCCESS(Status)) {
915 ERR("fileref_get_filename returned %08x\n", Status);
916 goto end;
917 }
918 }
919
920 FsRtlNotifyFilterChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&ccb->filename,
921 IrpSp->Flags & SL_WATCH_TREE, FALSE, IrpSp->Parameters.NotifyDirectory.CompletionFilter, Irp,
922 NULL, NULL, NULL);
923
924 Status = STATUS_PENDING;
925
926 end:
927 ExReleaseResourceLite(fcb->Header.Resource);
928 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
929
930 return Status;
931 }
932
933 NTSTATUS STDCALL drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
934 PIO_STACK_LOCATION IrpSp;
935 NTSTATUS Status;
936 ULONG func;
937 BOOL top_level;
938 device_extension* Vcb = DeviceObject->DeviceExtension;
939
940 TRACE("directory control\n");
941
942 FsRtlEnterFileSystem();
943
944 top_level = is_top_level(Irp);
945
946 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
947 Status = part0_passthrough(DeviceObject, Irp);
948 goto exit;
949 }
950
951 IrpSp = IoGetCurrentIrpStackLocation(Irp);
952
953 Irp->IoStatus.Information = 0;
954
955 func = IrpSp->MinorFunction;
956
957 switch (func) {
958 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
959 Status = notify_change_directory(Vcb, Irp);
960 break;
961
962 case IRP_MN_QUERY_DIRECTORY:
963 Status = query_directory(DeviceObject, Irp);
964 break;
965
966 default:
967 WARN("unknown minor %u\n", func);
968 Status = STATUS_NOT_IMPLEMENTED;
969 Irp->IoStatus.Status = Status;
970 break;
971 }
972
973 if (Status == STATUS_PENDING)
974 goto exit;
975
976 Irp->IoStatus.Status = Status;
977
978 // if (Irp->UserIosb)
979 // *Irp->UserIosb = Irp->IoStatus;
980
981 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
982
983 exit:
984 if (top_level)
985 IoSetTopLevelIrp(NULL);
986
987 FsRtlExitFileSystem();
988
989 return Status;
990 }