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