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