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