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