1 /* 2 * ReactOS kernel 3 * Copyright (C) 2002, 2003, 2014 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program 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 General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 18 * 19 * COPYRIGHT: See COPYING in the top level directory 20 * PROJECT: ReactOS kernel 21 * FILE: drivers/filesystem/ntfs/dirctl.c 22 * PURPOSE: NTFS filesystem driver 23 * PROGRAMMERS: Eric Kohl 24 * Pierre Schweitzer (pierre@reactos.org) 25 * Hervé Poussineau (hpoussin@reactos.org) 26 */ 27 28 /* INCLUDES *****************************************************************/ 29 30 #include "ntfs.h" 31 32 #define NDEBUG 33 #include <debug.h> 34 35 /* FUNCTIONS ****************************************************************/ 36 37 ULONGLONG 38 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt, 39 PFILE_RECORD_HEADER FileRecord, 40 PCWSTR Stream, 41 ULONG StreamLength, 42 PULONGLONG AllocatedSize) 43 { 44 ULONGLONG Size = 0ULL; 45 ULONGLONG Allocated = 0ULL; 46 NTSTATUS Status; 47 PNTFS_ATTR_CONTEXT DataContext; 48 49 Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext, NULL); 50 if (NT_SUCCESS(Status)) 51 { 52 Size = AttributeDataLength(DataContext->pRecord); 53 Allocated = AttributeAllocatedLength(DataContext->pRecord); 54 ReleaseAttributeContext(DataContext); 55 } 56 57 if (AllocatedSize != NULL) *AllocatedSize = Allocated; 58 59 return Size; 60 } 61 62 63 static NTSTATUS 64 NtfsGetNamesInformation(PDEVICE_EXTENSION DeviceExt, 65 PFILE_RECORD_HEADER FileRecord, 66 ULONGLONG MFTIndex, 67 PFILE_NAMES_INFORMATION Info, 68 ULONG BufferLength) 69 { 70 ULONG Length; 71 PFILENAME_ATTRIBUTE FileName; 72 73 DPRINT("NtfsGetNamesInformation() called\n"); 74 75 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord); 76 if (FileName == NULL) 77 { 78 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex); 79 NtfsDumpFileAttributes(DeviceExt, FileRecord); 80 return STATUS_OBJECT_NAME_NOT_FOUND; 81 } 82 83 Length = FileName->NameLength * sizeof (WCHAR); 84 if ((sizeof(FILE_NAMES_INFORMATION) + Length) > BufferLength) 85 return(STATUS_BUFFER_OVERFLOW); 86 87 Info->FileNameLength = Length; 88 Info->NextEntryOffset = 89 ROUND_UP(sizeof(FILE_NAMES_INFORMATION) + Length, sizeof(ULONG)); 90 RtlCopyMemory(Info->FileName, FileName->Name, Length); 91 92 return(STATUS_SUCCESS); 93 } 94 95 96 static NTSTATUS 97 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt, 98 PFILE_RECORD_HEADER FileRecord, 99 ULONGLONG MFTIndex, 100 PFILE_DIRECTORY_INFORMATION Info, 101 ULONG BufferLength) 102 { 103 ULONG Length; 104 PFILENAME_ATTRIBUTE FileName; 105 PSTANDARD_INFORMATION StdInfo; 106 107 DPRINT("NtfsGetDirectoryInformation() called\n"); 108 109 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord); 110 if (FileName == NULL) 111 { 112 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex); 113 NtfsDumpFileAttributes(DeviceExt, FileRecord); 114 return STATUS_OBJECT_NAME_NOT_FOUND; 115 } 116 117 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord); 118 ASSERT(StdInfo != NULL); 119 120 Length = FileName->NameLength * sizeof (WCHAR); 121 if ((sizeof(FILE_DIRECTORY_INFORMATION) + Length) > BufferLength) 122 return(STATUS_BUFFER_OVERFLOW); 123 124 Info->FileNameLength = Length; 125 Info->NextEntryOffset = 126 ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) + Length, sizeof(ULONG)); 127 RtlCopyMemory(Info->FileName, FileName->Name, Length); 128 129 Info->CreationTime.QuadPart = FileName->CreationTime; 130 Info->LastAccessTime.QuadPart = FileName->LastAccessTime; 131 Info->LastWriteTime.QuadPart = FileName->LastWriteTime; 132 Info->ChangeTime.QuadPart = FileName->ChangeTime; 133 134 /* Convert file flags */ 135 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes); 136 137 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart); 138 139 Info->FileIndex = MFTIndex; 140 141 return STATUS_SUCCESS; 142 } 143 144 145 static NTSTATUS 146 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt, 147 PFILE_RECORD_HEADER FileRecord, 148 ULONGLONG MFTIndex, 149 PFILE_FULL_DIRECTORY_INFORMATION Info, 150 ULONG BufferLength) 151 { 152 ULONG Length; 153 PFILENAME_ATTRIBUTE FileName; 154 PSTANDARD_INFORMATION StdInfo; 155 156 DPRINT("NtfsGetFullDirectoryInformation() called\n"); 157 158 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord); 159 if (FileName == NULL) 160 { 161 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex); 162 NtfsDumpFileAttributes(DeviceExt, FileRecord); 163 return STATUS_OBJECT_NAME_NOT_FOUND; 164 } 165 166 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord); 167 ASSERT(StdInfo != NULL); 168 169 Length = FileName->NameLength * sizeof (WCHAR); 170 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length) > BufferLength) 171 return(STATUS_BUFFER_OVERFLOW); 172 173 Info->FileNameLength = Length; 174 Info->NextEntryOffset = 175 ROUND_UP(sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length, sizeof(ULONG)); 176 RtlCopyMemory(Info->FileName, FileName->Name, Length); 177 178 Info->CreationTime.QuadPart = FileName->CreationTime; 179 Info->LastAccessTime.QuadPart = FileName->LastAccessTime; 180 Info->LastWriteTime.QuadPart = FileName->LastWriteTime; 181 Info->ChangeTime.QuadPart = FileName->ChangeTime; 182 183 /* Convert file flags */ 184 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes); 185 186 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart); 187 188 Info->FileIndex = MFTIndex; 189 Info->EaSize = 0; 190 191 return STATUS_SUCCESS; 192 } 193 194 195 static NTSTATUS 196 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt, 197 PFILE_RECORD_HEADER FileRecord, 198 ULONGLONG MFTIndex, 199 PFILE_BOTH_DIR_INFORMATION Info, 200 ULONG BufferLength) 201 { 202 ULONG Length; 203 PFILENAME_ATTRIBUTE FileName, ShortFileName; 204 PSTANDARD_INFORMATION StdInfo; 205 206 DPRINT("NtfsGetBothDirectoryInformation() called\n"); 207 208 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord); 209 if (FileName == NULL) 210 { 211 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex); 212 NtfsDumpFileAttributes(DeviceExt, FileRecord); 213 return STATUS_OBJECT_NAME_NOT_FOUND; 214 } 215 ShortFileName = GetFileNameFromRecord(DeviceExt, FileRecord, NTFS_FILE_NAME_DOS); 216 217 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord); 218 ASSERT(StdInfo != NULL); 219 220 Length = FileName->NameLength * sizeof (WCHAR); 221 if ((sizeof(FILE_BOTH_DIR_INFORMATION) + Length) > BufferLength) 222 return(STATUS_BUFFER_OVERFLOW); 223 224 Info->FileNameLength = Length; 225 Info->NextEntryOffset = 226 ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) + Length, sizeof(ULONG)); 227 RtlCopyMemory(Info->FileName, FileName->Name, Length); 228 229 if (ShortFileName) 230 { 231 /* Should we upcase the filename? */ 232 ASSERT(ShortFileName->NameLength <= ARRAYSIZE(Info->ShortName)); 233 Info->ShortNameLength = ShortFileName->NameLength * sizeof(WCHAR); 234 RtlCopyMemory(Info->ShortName, ShortFileName->Name, Info->ShortNameLength); 235 } 236 else 237 { 238 Info->ShortName[0] = 0; 239 Info->ShortNameLength = 0; 240 } 241 242 Info->CreationTime.QuadPart = FileName->CreationTime; 243 Info->LastAccessTime.QuadPart = FileName->LastAccessTime; 244 Info->LastWriteTime.QuadPart = FileName->LastWriteTime; 245 Info->ChangeTime.QuadPart = FileName->ChangeTime; 246 247 /* Convert file flags */ 248 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes); 249 250 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart); 251 252 Info->FileIndex = MFTIndex; 253 Info->EaSize = 0; 254 255 return STATUS_SUCCESS; 256 } 257 258 259 NTSTATUS 260 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext) 261 { 262 PIRP Irp; 263 PDEVICE_OBJECT DeviceObject; 264 PDEVICE_EXTENSION DeviceExtension; 265 LONG BufferLength = 0; 266 PUNICODE_STRING SearchPattern = NULL; 267 FILE_INFORMATION_CLASS FileInformationClass; 268 ULONG FileIndex = 0; 269 PUCHAR Buffer = NULL; 270 PFILE_NAMES_INFORMATION Buffer0 = NULL; 271 PNTFS_FCB Fcb; 272 PNTFS_CCB Ccb; 273 BOOLEAN First = FALSE; 274 PIO_STACK_LOCATION Stack; 275 PFILE_OBJECT FileObject; 276 NTSTATUS Status = STATUS_SUCCESS; 277 PFILE_RECORD_HEADER FileRecord; 278 ULONGLONG MFTRecord, OldMFTRecord = 0; 279 UNICODE_STRING Pattern; 280 281 DPRINT1("NtfsQueryDirectory() called\n"); 282 283 ASSERT(IrpContext); 284 Irp = IrpContext->Irp; 285 DeviceObject = IrpContext->DeviceObject; 286 287 DeviceExtension = DeviceObject->DeviceExtension; 288 Stack = IoGetCurrentIrpStackLocation(Irp); 289 FileObject = Stack->FileObject; 290 291 Ccb = (PNTFS_CCB)FileObject->FsContext2; 292 Fcb = (PNTFS_FCB)FileObject->FsContext; 293 294 /* Obtain the callers parameters */ 295 BufferLength = Stack->Parameters.QueryDirectory.Length; 296 SearchPattern = Stack->Parameters.QueryDirectory.FileName; 297 FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass; 298 FileIndex = Stack->Parameters.QueryDirectory.FileIndex; 299 300 if (NtfsFCBIsCompressed(Fcb)) 301 { 302 DPRINT1("Compressed directory!\n"); 303 UNIMPLEMENTED; 304 return STATUS_NOT_IMPLEMENTED; 305 } 306 307 if (!ExAcquireResourceSharedLite(&Fcb->MainResource, 308 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT))) 309 { 310 return STATUS_PENDING; 311 } 312 313 if (SearchPattern != NULL) 314 { 315 if (!Ccb->DirectorySearchPattern) 316 { 317 First = TRUE; 318 Pattern.Length = 0; 319 Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR); 320 Ccb->DirectorySearchPattern = Pattern.Buffer = 321 ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS); 322 if (!Ccb->DirectorySearchPattern) 323 { 324 ExReleaseResourceLite(&Fcb->MainResource); 325 return STATUS_INSUFFICIENT_RESOURCES; 326 } 327 328 memcpy(Ccb->DirectorySearchPattern, SearchPattern->Buffer, SearchPattern->Length); 329 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0; 330 } 331 } 332 else if (!Ccb->DirectorySearchPattern) 333 { 334 First = TRUE; 335 Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS); 336 if (!Ccb->DirectorySearchPattern) 337 { 338 ExReleaseResourceLite(&Fcb->MainResource); 339 return STATUS_INSUFFICIENT_RESOURCES; 340 } 341 342 Ccb->DirectorySearchPattern[0] = L'*'; 343 Ccb->DirectorySearchPattern[1] = 0; 344 } 345 346 RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern); 347 DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern); 348 DPRINT("In: '%S'\n", Fcb->PathName); 349 350 /* Determine directory index */ 351 if (Stack->Flags & SL_INDEX_SPECIFIED) 352 { 353 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart; 354 } 355 else if (First || (Stack->Flags & SL_RESTART_SCAN)) 356 { 357 Ccb->Entry = 0; 358 } 359 360 /* Get Buffer for result */ 361 Buffer = NtfsGetUserBuffer(Irp, FALSE); 362 363 DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern); 364 365 if (!ExAcquireResourceExclusiveLite(&DeviceExtension->DirResource, 366 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT))) 367 { 368 ExReleaseResourceLite(&Fcb->MainResource); 369 return STATUS_PENDING; 370 } 371 372 while (Status == STATUS_SUCCESS && BufferLength > 0) 373 { 374 Status = NtfsFindFileAt(DeviceExtension, 375 &Pattern, 376 &Ccb->Entry, 377 &FileRecord, 378 &MFTRecord, 379 Fcb->MFTIndex, 380 BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE)); 381 382 if (NT_SUCCESS(Status)) 383 { 384 /* HACK: files with both a short name and a long name are present twice in the index. 385 * Ignore the second entry, if it is immediately following the first one. 386 */ 387 if (MFTRecord == OldMFTRecord) 388 { 389 DPRINT1("Ignoring duplicate MFT entry 0x%x\n", MFTRecord); 390 Ccb->Entry++; 391 ExFreeToNPagedLookasideList(&DeviceExtension->FileRecLookasideList, FileRecord); 392 continue; 393 } 394 OldMFTRecord = MFTRecord; 395 396 switch (FileInformationClass) 397 { 398 case FileNamesInformation: 399 Status = NtfsGetNamesInformation(DeviceExtension, 400 FileRecord, 401 MFTRecord, 402 (PFILE_NAMES_INFORMATION)Buffer, 403 BufferLength); 404 break; 405 406 case FileDirectoryInformation: 407 Status = NtfsGetDirectoryInformation(DeviceExtension, 408 FileRecord, 409 MFTRecord, 410 (PFILE_DIRECTORY_INFORMATION)Buffer, 411 BufferLength); 412 break; 413 414 case FileFullDirectoryInformation: 415 Status = NtfsGetFullDirectoryInformation(DeviceExtension, 416 FileRecord, 417 MFTRecord, 418 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer, 419 BufferLength); 420 break; 421 422 case FileBothDirectoryInformation: 423 Status = NtfsGetBothDirectoryInformation(DeviceExtension, 424 FileRecord, 425 MFTRecord, 426 (PFILE_BOTH_DIR_INFORMATION)Buffer, 427 BufferLength); 428 break; 429 430 default: 431 Status = STATUS_INVALID_INFO_CLASS; 432 } 433 434 if (Status == STATUS_BUFFER_OVERFLOW) 435 { 436 if (Buffer0) 437 { 438 Buffer0->NextEntryOffset = 0; 439 } 440 break; 441 } 442 } 443 else 444 { 445 if (Buffer0) 446 { 447 Buffer0->NextEntryOffset = 0; 448 } 449 450 if (First) 451 { 452 Status = STATUS_NO_SUCH_FILE; 453 } 454 else 455 { 456 Status = STATUS_NO_MORE_FILES; 457 } 458 break; 459 } 460 461 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer; 462 Buffer0->FileIndex = FileIndex++; 463 Ccb->Entry++; 464 465 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY) 466 { 467 break; 468 } 469 BufferLength -= Buffer0->NextEntryOffset; 470 Buffer += Buffer0->NextEntryOffset; 471 ExFreeToNPagedLookasideList(&DeviceExtension->FileRecLookasideList, FileRecord); 472 } 473 474 if (Buffer0) 475 { 476 Buffer0->NextEntryOffset = 0; 477 } 478 479 ExReleaseResourceLite(&DeviceExtension->DirResource); 480 ExReleaseResourceLite(&Fcb->MainResource); 481 482 if (FileIndex > 0) 483 { 484 Status = STATUS_SUCCESS; 485 } 486 487 return Status; 488 } 489 490 491 NTSTATUS 492 NtfsDirectoryControl(PNTFS_IRP_CONTEXT IrpContext) 493 { 494 NTSTATUS Status = STATUS_UNSUCCESSFUL; 495 496 DPRINT1("NtfsDirectoryControl() called\n"); 497 498 switch (IrpContext->MinorFunction) 499 { 500 case IRP_MN_QUERY_DIRECTORY: 501 Status = NtfsQueryDirectory(IrpContext); 502 break; 503 504 case IRP_MN_NOTIFY_CHANGE_DIRECTORY: 505 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n"); 506 Status = STATUS_NOT_IMPLEMENTED; 507 break; 508 509 default: 510 Status = STATUS_INVALID_DEVICE_REQUEST; 511 break; 512 } 513 514 if (Status == STATUS_PENDING && IrpContext->Flags & IRPCONTEXT_COMPLETE) 515 { 516 return NtfsMarkIrpContextForQueue(IrpContext); 517 } 518 519 IrpContext->Irp->IoStatus.Information = 0; 520 521 return Status; 522 } 523 524 /* EOF */ 525