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