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