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 = sizeof(FILE_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + 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 = sizeof(FILE_DIRECTORY_INFORMATION) - sizeof(WCHAR) + 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 = sizeof(FILE_FULL_DIR_INFORMATION) - sizeof(WCHAR) + 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 = sizeof(FILE_ID_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + 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 = sizeof(FILE_ID_FULL_DIR_INFORMATION) - sizeof(WCHAR) + 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[0]) + 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[0]) + 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 = sizeof(FILE_NAMES_INFORMATION) - sizeof(WCHAR) + 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 case FileObjectIdInformation: 644 FIXME("STUB: FileObjectIdInformation\n"); 645 return STATUS_NOT_IMPLEMENTED; 646 647 case FileQuotaInformation: 648 FIXME("STUB: FileQuotaInformation\n"); 649 return STATUS_NOT_IMPLEMENTED; 650 651 case FileReparsePointInformation: 652 FIXME("STUB: FileReparsePointInformation\n"); 653 return STATUS_NOT_IMPLEMENTED; 654 655 default: 656 WARN("Unknown FileInformationClass %u\n", IrpSp->Parameters.QueryDirectory.FileInformationClass); 657 return STATUS_NOT_IMPLEMENTED; 658 } 659 660 return STATUS_NO_MORE_FILES; 661 } 662 663 static NTSTATUS next_dir_entry(file_ref* fileref, uint64_t* offset, dir_entry* de, dir_child** pdc) { 664 LIST_ENTRY* le; 665 dir_child* dc; 666 667 if (*pdc) { 668 dir_child* dc2 = *pdc; 669 670 if (dc2->list_entry_index.Flink != &fileref->fcb->dir_children_index) 671 dc = CONTAINING_RECORD(dc2->list_entry_index.Flink, dir_child, list_entry_index); 672 else 673 dc = NULL; 674 675 goto next; 676 } 677 678 if (fileref->parent) { // don't return . and .. if root directory 679 if (*offset == 0) { 680 de->key.obj_id = fileref->fcb->inode; 681 de->key.obj_type = TYPE_INODE_ITEM; 682 de->key.offset = 0; 683 de->dir_entry_type = DirEntryType_Self; 684 de->name.Buffer = L"."; 685 de->name.Length = de->name.MaximumLength = sizeof(WCHAR); 686 de->type = BTRFS_TYPE_DIRECTORY; 687 688 *offset = 1; 689 *pdc = NULL; 690 691 return STATUS_SUCCESS; 692 } else if (*offset == 1) { 693 de->key.obj_id = fileref->parent->fcb->inode; 694 de->key.obj_type = TYPE_INODE_ITEM; 695 de->key.offset = 0; 696 de->dir_entry_type = DirEntryType_Parent; 697 de->name.Buffer = L".."; 698 de->name.Length = de->name.MaximumLength = sizeof(WCHAR) * 2; 699 de->type = BTRFS_TYPE_DIRECTORY; 700 701 *offset = 2; 702 *pdc = NULL; 703 704 return STATUS_SUCCESS; 705 } 706 } 707 708 if (*offset < 2) 709 *offset = 2; 710 711 dc = NULL; 712 le = fileref->fcb->dir_children_index.Flink; 713 714 // skip entries before offset 715 while (le != &fileref->fcb->dir_children_index) { 716 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_index); 717 718 if (dc2->index >= *offset) { 719 dc = dc2; 720 break; 721 } 722 723 le = le->Flink; 724 } 725 726 next: 727 if (!dc) 728 return STATUS_NO_MORE_FILES; 729 730 de->key = dc->key; 731 de->name = dc->name; 732 de->type = dc->type; 733 de->dir_entry_type = DirEntryType_File; 734 de->dc = dc; 735 736 *offset = dc->index + 1; 737 *pdc = dc; 738 739 return STATUS_SUCCESS; 740 } 741 742 static NTSTATUS query_directory(PIRP Irp) { 743 PIO_STACK_LOCATION IrpSp; 744 NTSTATUS Status, status2; 745 fcb* fcb; 746 ccb* ccb; 747 file_ref* fileref; 748 device_extension* Vcb; 749 void* buf; 750 uint8_t *curitem, *lastitem; 751 LONG length; 752 ULONG count; 753 bool has_wildcard = false, specific_file = false, initial; 754 dir_entry de; 755 uint64_t newoffset; 756 dir_child* dc = NULL; 757 758 TRACE("query directory\n"); 759 760 IrpSp = IoGetCurrentIrpStackLocation(Irp); 761 fcb = IrpSp->FileObject->FsContext; 762 ccb = IrpSp->FileObject->FsContext2; 763 fileref = ccb ? ccb->fileref : NULL; 764 765 if (!fileref) 766 return STATUS_INVALID_PARAMETER; 767 768 if (!ccb) { 769 ERR("ccb was NULL\n"); 770 return STATUS_INVALID_PARAMETER; 771 } 772 773 if (!fcb) { 774 ERR("fcb was NULL\n"); 775 return STATUS_INVALID_PARAMETER; 776 } 777 778 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_LIST_DIRECTORY)) { 779 WARN("insufficient privileges\n"); 780 return STATUS_ACCESS_DENIED; 781 } 782 783 Vcb = fcb->Vcb; 784 785 if (!Vcb) { 786 ERR("Vcb was NULL\n"); 787 return STATUS_INVALID_PARAMETER; 788 } 789 790 if (fileref->fcb == Vcb->dummy_fcb) 791 return STATUS_NO_MORE_FILES; 792 793 if (IrpSp->Flags == 0) { 794 TRACE("QD flags: (none)\n"); 795 } else { 796 ULONG flags = IrpSp->Flags; 797 798 TRACE("QD flags:\n"); 799 800 if (flags & SL_INDEX_SPECIFIED) { 801 TRACE(" SL_INDEX_SPECIFIED\n"); 802 flags &= ~SL_INDEX_SPECIFIED; 803 } 804 805 if (flags & SL_RESTART_SCAN) { 806 TRACE(" SL_RESTART_SCAN\n"); 807 flags &= ~SL_RESTART_SCAN; 808 } 809 810 if (flags & SL_RETURN_SINGLE_ENTRY) { 811 TRACE(" SL_RETURN_SINGLE_ENTRY\n"); 812 flags &= ~SL_RETURN_SINGLE_ENTRY; 813 } 814 815 if (flags != 0) 816 TRACE(" unknown flags: %lu\n", flags); 817 } 818 819 if (IrpSp->Flags & SL_RESTART_SCAN) { 820 ccb->query_dir_offset = 0; 821 822 if (ccb->query_string.Buffer) { 823 RtlFreeUnicodeString(&ccb->query_string); 824 ccb->query_string.Buffer = NULL; 825 } 826 827 ccb->has_wildcard = false; 828 ccb->specific_file = false; 829 } 830 831 initial = !ccb->query_string.Buffer; 832 833 if (IrpSp->Parameters.QueryDirectory.FileName && IrpSp->Parameters.QueryDirectory.FileName->Length > 1) { 834 TRACE("QD filename: %.*S\n", (int)(IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR)), IrpSp->Parameters.QueryDirectory.FileName->Buffer); 835 836 if (IrpSp->Parameters.QueryDirectory.FileName->Length > sizeof(WCHAR) || IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != L'*') { 837 specific_file = true; 838 839 if (FsRtlDoesNameContainWildCards(IrpSp->Parameters.QueryDirectory.FileName)) { 840 has_wildcard = true; 841 specific_file = false; 842 } 843 } 844 845 if (ccb->query_string.Buffer) 846 RtlFreeUnicodeString(&ccb->query_string); 847 848 if (has_wildcard) 849 RtlUpcaseUnicodeString(&ccb->query_string, IrpSp->Parameters.QueryDirectory.FileName, true); 850 else { 851 ccb->query_string.Buffer = ExAllocatePoolWithTag(PagedPool, IrpSp->Parameters.QueryDirectory.FileName->Length, ALLOC_TAG); 852 if (!ccb->query_string.Buffer) { 853 ERR("out of memory\n"); 854 return STATUS_INSUFFICIENT_RESOURCES; 855 } 856 857 ccb->query_string.Length = ccb->query_string.MaximumLength = IrpSp->Parameters.QueryDirectory.FileName->Length; 858 RtlCopyMemory(ccb->query_string.Buffer, IrpSp->Parameters.QueryDirectory.FileName->Buffer, IrpSp->Parameters.QueryDirectory.FileName->Length); 859 } 860 861 ccb->has_wildcard = has_wildcard; 862 ccb->specific_file = specific_file; 863 } else { 864 has_wildcard = ccb->has_wildcard; 865 specific_file = ccb->specific_file; 866 867 if (!(IrpSp->Flags & SL_RESTART_SCAN)) { 868 initial = false; 869 870 if (specific_file) 871 return STATUS_NO_MORE_FILES; 872 } 873 } 874 875 if (ccb->query_string.Buffer) { 876 TRACE("query string = %.*S\n", (int)(ccb->query_string.Length / sizeof(WCHAR)), ccb->query_string.Buffer); 877 } 878 879 newoffset = ccb->query_dir_offset; 880 881 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 882 883 ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, true); 884 885 Status = next_dir_entry(fileref, &newoffset, &de, &dc); 886 887 if (!NT_SUCCESS(Status)) { 888 if (Status == STATUS_NO_MORE_FILES && initial) 889 Status = STATUS_NO_SUCH_FILE; 890 goto end; 891 } 892 893 ccb->query_dir_offset = newoffset; 894 895 buf = map_user_buffer(Irp, NormalPagePriority); 896 897 if (Irp->MdlAddress && !buf) { 898 ERR("MmGetSystemAddressForMdlSafe returned NULL\n"); 899 Status = STATUS_INSUFFICIENT_RESOURCES; 900 goto end; 901 } 902 903 length = IrpSp->Parameters.QueryDirectory.Length; 904 905 if (specific_file) { 906 bool found = false; 907 UNICODE_STRING us; 908 LIST_ENTRY* le; 909 uint32_t hash; 910 uint8_t c; 911 912 us.Buffer = NULL; 913 914 if (!ccb->case_sensitive) { 915 Status = RtlUpcaseUnicodeString(&us, &ccb->query_string, true); 916 if (!NT_SUCCESS(Status)) { 917 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); 918 goto end; 919 } 920 921 hash = calc_crc32c(0xffffffff, (uint8_t*)us.Buffer, us.Length); 922 } else 923 hash = calc_crc32c(0xffffffff, (uint8_t*)ccb->query_string.Buffer, ccb->query_string.Length); 924 925 c = hash >> 24; 926 927 if (ccb->case_sensitive) { 928 if (fileref->fcb->hash_ptrs[c]) { 929 le = fileref->fcb->hash_ptrs[c]; 930 while (le != &fileref->fcb->dir_children_hash) { 931 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash); 932 933 if (dc2->hash == hash) { 934 if (dc2->name.Length == ccb->query_string.Length && RtlCompareMemory(dc2->name.Buffer, ccb->query_string.Buffer, ccb->query_string.Length) == ccb->query_string.Length) { 935 found = true; 936 937 de.key = dc2->key; 938 de.name = dc2->name; 939 de.type = dc2->type; 940 de.dir_entry_type = DirEntryType_File; 941 de.dc = dc2; 942 943 break; 944 } 945 } else if (dc2->hash > hash) 946 break; 947 948 le = le->Flink; 949 } 950 } 951 } else { 952 if (fileref->fcb->hash_ptrs_uc[c]) { 953 le = fileref->fcb->hash_ptrs_uc[c]; 954 while (le != &fileref->fcb->dir_children_hash_uc) { 955 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc); 956 957 if (dc2->hash_uc == hash) { 958 if (dc2->name_uc.Length == us.Length && RtlCompareMemory(dc2->name_uc.Buffer, us.Buffer, us.Length) == us.Length) { 959 found = true; 960 961 de.key = dc2->key; 962 de.name = dc2->name; 963 de.type = dc2->type; 964 de.dir_entry_type = DirEntryType_File; 965 de.dc = dc2; 966 967 break; 968 } 969 } else if (dc2->hash_uc > hash) 970 break; 971 972 le = le->Flink; 973 } 974 } 975 } 976 977 if (us.Buffer) 978 ExFreePool(us.Buffer); 979 980 if (!found) { 981 Status = STATUS_NO_SUCH_FILE; 982 goto end; 983 } 984 } else if (has_wildcard) { 985 while (!FsRtlIsNameInExpression(&ccb->query_string, &de.name, !ccb->case_sensitive, NULL)) { 986 newoffset = ccb->query_dir_offset; 987 Status = next_dir_entry(fileref, &newoffset, &de, &dc); 988 989 if (NT_SUCCESS(Status)) 990 ccb->query_dir_offset = newoffset; 991 else { 992 if (Status == STATUS_NO_MORE_FILES && initial) 993 Status = STATUS_NO_SUCH_FILE; 994 995 goto end; 996 } 997 } 998 } 999 1000 TRACE("file(0) = %.*S\n", (int)(de.name.Length / sizeof(WCHAR)), de.name.Buffer); 1001 TRACE("offset = %I64u\n", ccb->query_dir_offset - 1); 1002 1003 Status = query_dir_item(fcb, ccb, buf, &length, Irp, &de, fcb->subvol); 1004 1005 count = 0; 1006 if (NT_SUCCESS(Status) && !(IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) && !specific_file) { 1007 lastitem = (uint8_t*)buf; 1008 1009 while (length > 0) { 1010 switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) { 1011 #ifndef _MSC_VER 1012 #pragma GCC diagnostic push 1013 #pragma GCC diagnostic ignored "-Wswitch" 1014 #endif 1015 case FileBothDirectoryInformation: 1016 case FileDirectoryInformation: 1017 case FileIdBothDirectoryInformation: 1018 case FileFullDirectoryInformation: 1019 case FileIdFullDirectoryInformation: 1020 case FileIdExtdDirectoryInformation: 1021 case FileIdExtdBothDirectoryInformation: 1022 length -= length % 8; 1023 break; 1024 #ifndef _MSC_VER 1025 #pragma GCC diagnostic pop 1026 #endif 1027 1028 case FileNamesInformation: 1029 length -= length % 4; 1030 break; 1031 1032 default: 1033 WARN("unhandled file information class %u\n", IrpSp->Parameters.QueryDirectory.FileInformationClass); 1034 break; 1035 } 1036 1037 if (length > 0) { 1038 newoffset = ccb->query_dir_offset; 1039 Status = next_dir_entry(fileref, &newoffset, &de, &dc); 1040 if (NT_SUCCESS(Status)) { 1041 if (!has_wildcard || FsRtlIsNameInExpression(&ccb->query_string, &de.name, !ccb->case_sensitive, NULL)) { 1042 curitem = (uint8_t*)buf + IrpSp->Parameters.QueryDirectory.Length - length; 1043 count++; 1044 1045 TRACE("file(%lu) %Iu = %.*S\n", count, curitem - (uint8_t*)buf, (int)(de.name.Length / sizeof(WCHAR)), de.name.Buffer); 1046 TRACE("offset = %I64u\n", ccb->query_dir_offset - 1); 1047 1048 status2 = query_dir_item(fcb, ccb, curitem, &length, Irp, &de, fcb->subvol); 1049 1050 if (NT_SUCCESS(status2)) { 1051 ULONG* lastoffset = (ULONG*)lastitem; 1052 1053 *lastoffset = (ULONG)(curitem - lastitem); 1054 ccb->query_dir_offset = newoffset; 1055 1056 lastitem = curitem; 1057 } else 1058 break; 1059 } else 1060 ccb->query_dir_offset = newoffset; 1061 } else { 1062 if (Status == STATUS_NO_MORE_FILES) 1063 Status = STATUS_SUCCESS; 1064 1065 break; 1066 } 1067 } else 1068 break; 1069 } 1070 } 1071 1072 Irp->IoStatus.Information = IrpSp->Parameters.QueryDirectory.Length - length; 1073 1074 end: 1075 ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock); 1076 1077 ExReleaseResourceLite(&Vcb->tree_lock); 1078 1079 TRACE("returning %08lx\n", Status); 1080 1081 return Status; 1082 } 1083 1084 static NTSTATUS notify_change_directory(device_extension* Vcb, PIRP Irp) { 1085 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 1086 PFILE_OBJECT FileObject = IrpSp->FileObject; 1087 fcb* fcb = FileObject->FsContext; 1088 ccb* ccb = FileObject->FsContext2; 1089 file_ref* fileref = ccb ? ccb->fileref : NULL; 1090 NTSTATUS Status; 1091 1092 TRACE("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n"); 1093 1094 if (!ccb) { 1095 ERR("ccb was NULL\n"); 1096 return STATUS_INVALID_PARAMETER; 1097 } 1098 1099 if (!fileref) { 1100 ERR("no fileref\n"); 1101 return STATUS_INVALID_PARAMETER; 1102 } 1103 1104 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_LIST_DIRECTORY)) { 1105 WARN("insufficient privileges\n"); 1106 return STATUS_ACCESS_DENIED; 1107 } 1108 1109 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true); 1110 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 1111 1112 if (fcb->type != BTRFS_TYPE_DIRECTORY) { 1113 Status = STATUS_INVALID_PARAMETER; 1114 goto end; 1115 } 1116 1117 // FIXME - raise exception if FCB marked for deletion? 1118 1119 TRACE("FileObject %p\n", FileObject); 1120 1121 if (ccb->filename.Length == 0) { 1122 ULONG reqlen; 1123 1124 ccb->filename.MaximumLength = ccb->filename.Length = 0; 1125 1126 Status = fileref_get_filename(fileref, &ccb->filename, NULL, &reqlen); 1127 if (Status == STATUS_BUFFER_OVERFLOW) { 1128 ccb->filename.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG); 1129 if (!ccb->filename.Buffer) { 1130 ERR("out of memory\n"); 1131 Status = STATUS_INSUFFICIENT_RESOURCES; 1132 goto end; 1133 } 1134 1135 ccb->filename.MaximumLength = (uint16_t)reqlen; 1136 1137 Status = fileref_get_filename(fileref, &ccb->filename, NULL, &reqlen); 1138 if (!NT_SUCCESS(Status)) { 1139 ERR("fileref_get_filename returned %08lx\n", Status); 1140 goto end; 1141 } 1142 } else { 1143 ERR("fileref_get_filename returned %08lx\n", Status); 1144 goto end; 1145 } 1146 } 1147 1148 FsRtlNotifyFilterChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext2, (PSTRING)&ccb->filename, 1149 IrpSp->Flags & SL_WATCH_TREE, false, IrpSp->Parameters.NotifyDirectory.CompletionFilter, Irp, 1150 NULL, NULL, NULL); 1151 1152 Status = STATUS_PENDING; 1153 1154 end: 1155 ExReleaseResourceLite(fcb->Header.Resource); 1156 ExReleaseResourceLite(&fcb->Vcb->tree_lock); 1157 1158 return Status; 1159 } 1160 1161 _Dispatch_type_(IRP_MJ_DIRECTORY_CONTROL) 1162 _Function_class_(DRIVER_DISPATCH) 1163 NTSTATUS __stdcall drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 1164 PIO_STACK_LOCATION IrpSp; 1165 NTSTATUS Status; 1166 ULONG func; 1167 bool top_level; 1168 device_extension* Vcb = DeviceObject->DeviceExtension; 1169 1170 FsRtlEnterFileSystem(); 1171 1172 TRACE("directory control\n"); 1173 1174 top_level = is_top_level(Irp); 1175 1176 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 1177 Status = vol_directory_control(DeviceObject, Irp); 1178 goto end; 1179 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 1180 Status = STATUS_INVALID_PARAMETER; 1181 goto end; 1182 } 1183 1184 IrpSp = IoGetCurrentIrpStackLocation(Irp); 1185 1186 Irp->IoStatus.Information = 0; 1187 1188 func = IrpSp->MinorFunction; 1189 1190 switch (func) { 1191 case IRP_MN_NOTIFY_CHANGE_DIRECTORY: 1192 Status = notify_change_directory(Vcb, Irp); 1193 break; 1194 1195 case IRP_MN_QUERY_DIRECTORY: 1196 Status = query_directory(Irp); 1197 break; 1198 1199 default: 1200 WARN("unknown minor %lu\n", func); 1201 Status = STATUS_NOT_IMPLEMENTED; 1202 Irp->IoStatus.Status = Status; 1203 break; 1204 } 1205 1206 if (Status == STATUS_PENDING) 1207 goto exit; 1208 1209 end: 1210 Irp->IoStatus.Status = Status; 1211 1212 IoCompleteRequest(Irp, IO_DISK_INCREMENT); 1213 1214 exit: 1215 TRACE("returning %08lx\n", Status); 1216 1217 if (top_level) 1218 IoSetTopLevelIrp(NULL); 1219 1220 FsRtlExitFileSystem(); 1221 1222 return Status; 1223 } 1224