xref: /reactos/drivers/filesystems/btrfs/dirctrl.c (revision 6e0cf03d)
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