[BTRFS] Allow driver to start at first stage when no hive is present.
[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 acquire_fcb_lock_shared(Vcb);
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 if (IrpSp->Flags & SL_RESTART_SCAN) {
662 ccb->query_dir_offset = 0;
663
664 if (ccb->query_string.Buffer) {
665 RtlFreeUnicodeString(&ccb->query_string);
666 ccb->query_string.Buffer = NULL;
667 }
668
669 ccb->has_wildcard = FALSE;
670 ccb->specific_file = FALSE;
671 }
672
673 initial = !ccb->query_string.Buffer;
674
675 if (IrpSp->Parameters.QueryDirectory.FileName && IrpSp->Parameters.QueryDirectory.FileName->Length > 1) {
676 TRACE("QD filename: %.*S\n", IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR), IrpSp->Parameters.QueryDirectory.FileName->Buffer);
677
678 if (IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != '*') {
679 specific_file = TRUE;
680
681 if (FsRtlDoesNameContainWildCards(IrpSp->Parameters.QueryDirectory.FileName)) {
682 has_wildcard = TRUE;
683 specific_file = FALSE;
684 }
685 }
686
687 if (ccb->query_string.Buffer)
688 RtlFreeUnicodeString(&ccb->query_string);
689
690 if (has_wildcard)
691 RtlUpcaseUnicodeString(&ccb->query_string, IrpSp->Parameters.QueryDirectory.FileName, TRUE);
692 else {
693 ccb->query_string.Buffer = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length, ALLOC_TAG);
694 if (!ccb->query_string.Buffer) {
695 ERR("out of memory\n");
696 Status = STATUS_INSUFFICIENT_RESOURCES;
697 goto end2;
698 }
699
700 ccb->query_string.Length = ccb->query_string.MaximumLength = IrpSp->Parameters.QueryDirectory.FileName->Length;
701 RtlCopyMemory(ccb->query_string.Buffer, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length);
702 }
703
704 ccb->has_wildcard = has_wildcard;
705 ccb->specific_file = specific_file;
706 } else {
707 has_wildcard = ccb->has_wildcard;
708 specific_file = ccb->specific_file;
709
710 if (!(IrpSp->Flags & SL_RESTART_SCAN)) {
711 initial = FALSE;
712
713 if (specific_file) {
714 Status = STATUS_NO_MORE_FILES;
715 goto end2;
716 }
717 }
718 }
719
720 if (ccb->query_string.Buffer) {
721 TRACE("query string = %.*S\n", ccb->query_string.Length / sizeof(WCHAR), ccb->query_string.Buffer);
722 }
723
724 newoffset = ccb->query_dir_offset;
725
726 ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE);
727
728 Status = next_dir_entry(fileref, &newoffset, &de, &dc);
729
730 if (!NT_SUCCESS(Status)) {
731 if (Status == STATUS_NO_MORE_FILES && initial)
732 Status = STATUS_NO_SUCH_FILE;
733 goto end;
734 }
735
736 ccb->query_dir_offset = newoffset;
737
738 buf = map_user_buffer(Irp, NormalPagePriority);
739
740 if (Irp->MdlAddress && !buf) {
741 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
742 Status = STATUS_INSUFFICIENT_RESOURCES;
743 goto end;
744 }
745
746 length = IrpSp->Parameters.QueryDirectory.Length;
747
748 if (specific_file) {
749 BOOL found = FALSE;
750 UNICODE_STRING us;
751 LIST_ENTRY* le;
752 UINT32 hash;
753 UINT8 c;
754
755 us.Buffer = NULL;
756
757 if (!ccb->case_sensitive) {
758 Status = RtlUpcaseUnicodeString(&us, &ccb->query_string, TRUE);
759 if (!NT_SUCCESS(Status)) {
760 ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
761 goto end;
762 }
763
764 hash = calc_crc32c(0xffffffff, (UINT8*)us.Buffer, us.Length);
765 } else
766 hash = calc_crc32c(0xffffffff, (UINT8*)ccb->query_string.Buffer, ccb->query_string.Length);
767
768 c = hash >> 24;
769
770 if (ccb->case_sensitive) {
771 if (fileref->fcb->hash_ptrs[c]) {
772 le = fileref->fcb->hash_ptrs[c];
773 while (le != &fileref->fcb->dir_children_hash) {
774 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash);
775
776 if (dc2->hash == hash) {
777 if (dc2->name.Length == ccb->query_string.Length && RtlCompareMemory(dc2->name.Buffer, ccb->query_string.Buffer, ccb->query_string.Length) == ccb->query_string.Length) {
778 found = TRUE;
779
780 de.key = dc2->key;
781 de.name = dc2->name;
782 de.type = dc2->type;
783 de.dir_entry_type = DirEntryType_File;
784 de.dc = dc2;
785
786 break;
787 }
788 } else if (dc2->hash > hash)
789 break;
790
791 le = le->Flink;
792 }
793 }
794 } else {
795 if (fileref->fcb->hash_ptrs_uc[c]) {
796 le = fileref->fcb->hash_ptrs_uc[c];
797 while (le != &fileref->fcb->dir_children_hash_uc) {
798 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
799
800 if (dc2->hash_uc == hash) {
801 if (dc2->name_uc.Length == us.Length && RtlCompareMemory(dc2->name_uc.Buffer, us.Buffer, us.Length) == us.Length) {
802 found = TRUE;
803
804 de.key = dc2->key;
805 de.name = dc2->name;
806 de.type = dc2->type;
807 de.dir_entry_type = DirEntryType_File;
808 de.dc = dc2;
809
810 break;
811 }
812 } else if (dc2->hash_uc > hash)
813 break;
814
815 le = le->Flink;
816 }
817 }
818 }
819
820 if (us.Buffer)
821 ExFreePool(us.Buffer);
822
823 if (!found) {
824 Status = STATUS_NO_SUCH_FILE;
825 goto end;
826 }
827 } else if (has_wildcard) {
828 while (!FsRtlIsNameInExpression(&ccb->query_string, &de.name, !ccb->case_sensitive, NULL)) {
829 newoffset = ccb->query_dir_offset;
830 Status = next_dir_entry(fileref, &newoffset, &de, &dc);
831
832 if (NT_SUCCESS(Status))
833 ccb->query_dir_offset = newoffset;
834 else {
835 if (Status == STATUS_NO_MORE_FILES && initial)
836 Status = STATUS_NO_SUCH_FILE;
837
838 goto end;
839 }
840 }
841 }
842
843 TRACE("file(0) = %.*S\n", de.name.Length / sizeof(WCHAR), de.name.Buffer);
844 TRACE("offset = %u\n", ccb->query_dir_offset - 1);
845
846 Status = query_dir_item(fcb, ccb, buf, &length, Irp, &de, fcb->subvol);
847
848 count = 0;
849 if (NT_SUCCESS(Status) && !(IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) && !specific_file) {
850 lastitem = (UINT8*)buf;
851
852 while (length > 0) {
853 switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
854 case FileBothDirectoryInformation:
855 case FileDirectoryInformation:
856 case FileIdBothDirectoryInformation:
857 case FileFullDirectoryInformation:
858 case FileIdFullDirectoryInformation:
859 length -= length % 8;
860 break;
861
862 case FileNamesInformation:
863 length -= length % 4;
864 break;
865
866 default:
867 WARN("unhandled file information class %u\n", IrpSp->Parameters.QueryDirectory.FileInformationClass);
868 break;
869 }
870
871 if (length > 0) {
872 newoffset = ccb->query_dir_offset;
873 Status = next_dir_entry(fileref, &newoffset, &de, &dc);
874 if (NT_SUCCESS(Status)) {
875 if (!has_wildcard || FsRtlIsNameInExpression(&ccb->query_string, &de.name, !ccb->case_sensitive, NULL)) {
876 curitem = (UINT8*)buf + IrpSp->Parameters.QueryDirectory.Length - length;
877 count++;
878
879 TRACE("file(%u) %u = %.*S\n", count, curitem - (UINT8*)buf, de.name.Length / sizeof(WCHAR), de.name.Buffer);
880 TRACE("offset = %u\n", ccb->query_dir_offset - 1);
881
882 status2 = query_dir_item(fcb, ccb, curitem, &length, Irp, &de, fcb->subvol);
883
884 if (NT_SUCCESS(status2)) {
885 ULONG* lastoffset = (ULONG*)lastitem;
886
887 *lastoffset = (ULONG)(curitem - lastitem);
888 ccb->query_dir_offset = newoffset;
889
890 lastitem = curitem;
891 } else
892 break;
893 } else
894 ccb->query_dir_offset = newoffset;
895 } else {
896 if (Status == STATUS_NO_MORE_FILES)
897 Status = STATUS_SUCCESS;
898
899 break;
900 }
901 } else
902 break;
903 }
904 }
905
906 Irp->IoStatus.Information = IrpSp->Parameters.QueryDirectory.Length - length;
907
908 end:
909 ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock);
910
911 end2:
912 release_fcb_lock(Vcb);
913 ExReleaseResourceLite(&Vcb->tree_lock);
914
915 TRACE("returning %08x\n", Status);
916
917 if (utf8.Buffer)
918 ExFreePool(utf8.Buffer);
919
920 return Status;
921 }
922
923 static NTSTATUS notify_change_directory(device_extension* Vcb, PIRP Irp) {
924 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
925 PFILE_OBJECT FileObject = IrpSp->FileObject;
926 fcb* fcb = FileObject->FsContext;
927 ccb* ccb = FileObject->FsContext2;
928 file_ref* fileref = ccb ? ccb->fileref : NULL;
929 NTSTATUS Status;
930
931 TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
932
933 if (!ccb) {
934 ERR("ccb was NULL\n");
935 return STATUS_INVALID_PARAMETER;
936 }
937
938 if (!fileref) {
939 ERR("no fileref\n");
940 return STATUS_INVALID_PARAMETER;
941 }
942
943 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_LIST_DIRECTORY)) {
944 WARN("insufficient privileges\n");
945 return STATUS_ACCESS_DENIED;
946 }
947
948 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
949 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
950
951 if (fcb->type != BTRFS_TYPE_DIRECTORY) {
952 Status = STATUS_INVALID_PARAMETER;
953 goto end;
954 }
955
956 // FIXME - raise exception if FCB marked for deletion?
957
958 TRACE("%S\n", file_desc(FileObject));
959
960 if (ccb->filename.Length == 0) {
961 ULONG reqlen;
962
963 ccb->filename.MaximumLength = ccb->filename.Length = 0;
964
965 Status = fileref_get_filename(fileref, &ccb->filename, NULL, &reqlen);
966 if (Status == STATUS_BUFFER_OVERFLOW) {
967 ccb->filename.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
968 if (!ccb->filename.Buffer) {
969 ERR("out of memory\n");
970 Status = STATUS_INSUFFICIENT_RESOURCES;
971 goto end;
972 }
973
974 ccb->filename.MaximumLength = (UINT16)reqlen;
975
976 Status = fileref_get_filename(fileref, &ccb->filename, NULL, &reqlen);
977 if (!NT_SUCCESS(Status)) {
978 ERR("fileref_get_filename returned %08x\n", Status);
979 goto end;
980 }
981 } else {
982 ERR("fileref_get_filename returned %08x\n", Status);
983 goto end;
984 }
985 }
986
987 FsRtlNotifyFilterChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&ccb->filename,
988 IrpSp->Flags & SL_WATCH_TREE, FALSE, IrpSp->Parameters.NotifyDirectory.CompletionFilter, Irp,
989 NULL, NULL, NULL);
990
991 Status = STATUS_PENDING;
992
993 end:
994 ExReleaseResourceLite(fcb->Header.Resource);
995 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
996
997 return Status;
998 }
999
1000 _Dispatch_type_(IRP_MJ_DIRECTORY_CONTROL)
1001 _Function_class_(DRIVER_DISPATCH)
1002 NTSTATUS drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
1003 PIO_STACK_LOCATION IrpSp;
1004 NTSTATUS Status;
1005 ULONG func;
1006 BOOL top_level;
1007 device_extension* Vcb = DeviceObject->DeviceExtension;
1008
1009 FsRtlEnterFileSystem();
1010
1011 TRACE("directory control\n");
1012
1013 top_level = is_top_level(Irp);
1014
1015 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
1016 Status = vol_directory_control(DeviceObject, Irp);
1017 goto end;
1018 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
1019 Status = STATUS_INVALID_PARAMETER;
1020 goto end;
1021 }
1022
1023 IrpSp = IoGetCurrentIrpStackLocation(Irp);
1024
1025 Irp->IoStatus.Information = 0;
1026
1027 func = IrpSp->MinorFunction;
1028
1029 switch (func) {
1030 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
1031 Status = notify_change_directory(Vcb, Irp);
1032 break;
1033
1034 case IRP_MN_QUERY_DIRECTORY:
1035 Status = query_directory(Irp);
1036 break;
1037
1038 default:
1039 WARN("unknown minor %u\n", func);
1040 Status = STATUS_NOT_IMPLEMENTED;
1041 Irp->IoStatus.Status = Status;
1042 break;
1043 }
1044
1045 if (Status == STATUS_PENDING)
1046 goto exit;
1047
1048 end:
1049 Irp->IoStatus.Status = Status;
1050
1051 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1052
1053 exit:
1054 TRACE("returning %08x\n", Status);
1055
1056 if (top_level)
1057 IoSetTopLevelIrp(NULL);
1058
1059 FsRtlExitFileSystem();
1060
1061 return Status;
1062 }