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