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