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