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