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 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 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 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 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 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 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 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) 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