3 * Copyright (C) 2002 ReactOS Team
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.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * FILE: services/fs/ntfs/dirctl.c
24 * PURPOSE: NTFS filesystem driver
25 * PROGRAMMER: Eric Kohl
28 /* INCLUDES *****************************************************************/
30 #include <ddk/ntddk.h>
38 /* FUNCTIONS ****************************************************************/
42 CdfsGetEntryName(PDEVICE_EXTENSION DeviceExt,
45 PLARGE_INTEGER StreamOffset,
52 * FUNCTION: Retrieves the file name, be it in short or long file name format
59 ULONG BlockOffset = 0;
61 Record = (PDIR_RECORD)*Block;
62 while(Index < *pIndex)
64 BlockOffset += Record->RecordLength;
65 Offset += Record->RecordLength;
67 Record = (PDIR_RECORD)(*Block + BlockOffset);
68 if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
70 DPRINT("Map next sector\n");
71 CcUnpinData(*Context);
72 StreamOffset->QuadPart += BLOCKSIZE;
73 Offset = ROUND_UP(Offset, BLOCKSIZE);
76 if (!CcMapData(DeviceExt->StreamFileObject,
81 DPRINT("CcMapData() failed\n");
82 return(STATUS_UNSUCCESSFUL);
84 Record = (PDIR_RECORD)(*Block + BlockOffset);
87 if (Offset >= DirLength)
88 return(STATUS_NO_MORE_ENTRIES);
93 DPRINT("Index %lu RecordLength %lu Offset %lu\n",
94 Index, Record->RecordLength, Offset);
96 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
100 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
106 if (DeviceExt->CdInfo.JolietLevel == 0)
110 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
111 Name[i] = (WCHAR)Record->FileId[i];
116 CdfsSwapString(Name, Record->FileId, Record->FileIdLength);
120 DPRINT("Name '%S'\n", Name);
126 return(STATUS_SUCCESS);
131 CdfsFindFile(PDEVICE_EXTENSION DeviceExt,
138 * FUNCTION: Find a file
150 PVOID Context = NULL;
154 LARGE_INTEGER StreamOffset;
156 DPRINT("FindFile(Parent %x, FileToFind '%S', DirIndex: %d)\n",
157 Parent, FileToFind, pDirIndex ? *pDirIndex : 0);
158 DPRINT("FindFile: old Pathname %x, old Objectname %x)\n",
159 Fcb->PathName, Fcb->ObjectName);
163 if (wcslen (FileToFind) == 0)
166 TempStr[0] = (WCHAR) '.';
168 FileToFind = (PWSTR)&TempStr;
173 if (Parent->Entry.ExtentLocationL == DeviceExt->CdInfo.RootStart)
185 StreamOffset.QuadPart = (LONGLONG)DeviceExt->CdInfo.RootStart * (LONGLONG)BLOCKSIZE;
186 DirSize = DeviceExt->CdInfo.RootSize;
189 if (FileToFind[0] == 0 || (FileToFind[0] == '\\' && FileToFind[1] == 0)
190 || (FileToFind[0] == '.' && FileToFind[1] == 0))
192 /* it's root : complete essentials fields then return ok */
193 RtlZeroMemory(Fcb, sizeof(FCB));
195 Fcb->PathName[0]='\\';
196 Fcb->ObjectName = &Fcb->PathName[1];
197 Fcb->Entry.ExtentLocationL = DeviceExt->CdInfo.RootStart;
198 Fcb->Entry.DataLengthL = DeviceExt->CdInfo.RootSize;
199 Fcb->Entry.FileFlags = 0x02; //FILE_ATTRIBUTE_DIRECTORY;
205 DPRINT("CdfsFindFile: new Pathname %S, new Objectname %S)\n",Fcb->PathName, Fcb->ObjectName);
206 return (STATUS_SUCCESS);
211 StreamOffset.QuadPart = (LONGLONG)Parent->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE;
212 DirSize = Parent->Entry.DataLengthL;
215 DPRINT("StreamOffset %I64u DirSize %lu\n", StreamOffset.QuadPart, DirSize);
217 if (pDirIndex && (*pDirIndex))
218 DirIndex = *pDirIndex;
220 if(!CcMapData(DeviceExt->StreamFileObject, &StreamOffset,
221 BLOCKSIZE, TRUE, &Context, &Block))
223 DPRINT("CcMapData() failed\n");
224 return(STATUS_UNSUCCESSFUL);
230 Record = (PDIR_RECORD)Ptr;
231 if (Record->RecordLength == 0)
233 DPRINT1("Stopped!\n");
237 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
238 Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
240 Status = CdfsGetEntryName(DeviceExt, &Context, &Block, &StreamOffset,
241 DirSize, (PVOID*)&Ptr, name, &DirIndex, pDirIndex2);
242 if (Status == STATUS_NO_MORE_ENTRIES)
246 else if (Status == STATUS_UNSUCCESSFUL)
248 /* Note: the directory cache has already been unpinned */
252 DPRINT("Name '%S'\n", name);
254 if (wstrcmpjoki(name, FileToFind)) /* || wstrcmpjoki (name2, FileToFind)) */
256 if (Parent && Parent->PathName)
258 len = wcslen(Parent->PathName);
259 memcpy(Fcb->PathName, Parent->PathName, len*sizeof(WCHAR));
260 Fcb->ObjectName=&Fcb->PathName[len];
261 if (len != 1 || Fcb->PathName[0] != '\\')
263 Fcb->ObjectName[0] = '\\';
264 Fcb->ObjectName = &Fcb->ObjectName[1];
269 Fcb->ObjectName=Fcb->PathName;
270 Fcb->ObjectName[0]='\\';
271 Fcb->ObjectName=&Fcb->ObjectName[1];
274 DPRINT("PathName '%S' ObjectName '%S'\n", Fcb->PathName, Fcb->ObjectName);
276 memcpy(&Fcb->Entry, Ptr, sizeof(DIR_RECORD));
277 wcsncpy(Fcb->ObjectName, name, MAX_PATH);
279 *pDirIndex = DirIndex;
281 DPRINT("FindFile: new Pathname %S, new Objectname %S, DirIndex %d\n",
282 Fcb->PathName, Fcb->ObjectName, DirIndex);
284 CcUnpinData(Context);
286 return(STATUS_SUCCESS);
290 Ptr = Ptr + Record->RecordLength;
293 if (((ULONG)Ptr - (ULONG)Block) >= DirSize)
295 DPRINT("Stopped!\n");
300 CcUnpinData(Context);
303 *pDirIndex = DirIndex;
305 return(STATUS_UNSUCCESSFUL);
310 CdfsGetNameInformation(PFCB Fcb,
311 PDEVICE_EXTENSION DeviceExt,
312 PFILE_NAMES_INFORMATION Info,
317 DPRINT("CdfsGetNameInformation() called\n");
319 Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
320 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
321 return(STATUS_BUFFER_OVERFLOW);
323 Info->FileNameLength = Length;
324 Info->NextEntryOffset =
325 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
326 memcpy(Info->FileName, Fcb->ObjectName, Length);
328 return(STATUS_SUCCESS);
333 CdfsGetDirectoryInformation(PFCB Fcb,
334 PDEVICE_EXTENSION DeviceExt,
335 PFILE_DIRECTORY_INFORMATION Info,
340 DPRINT("CdfsGetDirectoryInformation() called\n");
342 Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
343 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
344 return(STATUS_BUFFER_OVERFLOW);
346 Info->FileNameLength = Length;
347 Info->NextEntryOffset =
348 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
349 memcpy(Info->FileName, Fcb->ObjectName, Length);
351 /* Convert file times */
352 CdfsDateTimeToFileTime(Fcb,
353 &Info->CreationTime);
354 CdfsDateTimeToFileTime(Fcb,
355 &Info->LastAccessTime);
356 CdfsDateTimeToFileTime(Fcb,
357 &Info->LastWriteTime);
358 CdfsDateTimeToFileTime(Fcb,
361 /* Convert file flags */
362 CdfsFileFlagsToAttributes(Fcb,
363 &Info->FileAttributes);
365 Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
367 /* Make AllocSize a rounded up multiple of the sector size */
368 Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
372 return(STATUS_SUCCESS);
377 CdfsGetFullDirectoryInformation(PFCB Fcb,
378 PDEVICE_EXTENSION DeviceExt,
379 PFILE_FULL_DIRECTORY_INFORMATION Info,
384 DPRINT("CdfsGetFullDirectoryInformation() called\n");
386 Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
387 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
388 return(STATUS_BUFFER_OVERFLOW);
390 Info->FileNameLength = Length;
391 Info->NextEntryOffset =
392 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
393 memcpy(Info->FileName, Fcb->ObjectName, Length);
395 /* Convert file times */
396 CdfsDateTimeToFileTime(Fcb,
397 &Info->CreationTime);
398 CdfsDateTimeToFileTime(Fcb,
399 &Info->LastAccessTime);
400 CdfsDateTimeToFileTime(Fcb,
401 &Info->LastWriteTime);
402 CdfsDateTimeToFileTime(Fcb,
405 /* Convert file flags */
406 CdfsFileFlagsToAttributes(Fcb,
407 &Info->FileAttributes);
409 Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
411 /* Make AllocSize a rounded up multiple of the sector size */
412 Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
417 return(STATUS_SUCCESS);
422 CdfsGetBothDirectoryInformation(PFCB Fcb,
423 PDEVICE_EXTENSION DeviceExt,
424 PFILE_BOTH_DIRECTORY_INFORMATION Info,
429 DPRINT("CdfsGetBothDirectoryInformation() called\n");
431 Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
432 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
433 return(STATUS_BUFFER_OVERFLOW);
435 Info->FileNameLength = Length;
436 Info->NextEntryOffset =
437 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
438 memcpy(Info->FileName, Fcb->ObjectName, Length);
440 /* Convert file times */
441 CdfsDateTimeToFileTime(Fcb,
442 &Info->CreationTime);
443 CdfsDateTimeToFileTime(Fcb,
444 &Info->LastAccessTime);
445 CdfsDateTimeToFileTime(Fcb,
446 &Info->LastWriteTime);
447 CdfsDateTimeToFileTime(Fcb,
450 /* Convert file flags */
451 CdfsFileFlagsToAttributes(Fcb,
452 &Info->FileAttributes);
454 Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
456 /* Make AllocSize a rounded up multiple of the sector size */
457 Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
462 if (DeviceExt->CdInfo.JolietLevel == 0)
464 /* Standard ISO-9660 format */
465 Info->ShortNameLength = Length;
466 memcpy(Info->ShortName, Fcb->ObjectName, Length);
470 /* Joliet extension */
472 /* FIXME: Copy or create a short file name */
474 Info->ShortName[0] = 0;
475 Info->ShortNameLength = 0;
478 return(STATUS_SUCCESS);
483 NtfsQueryDirectory(PDEVICE_OBJECT DeviceObject,
486 PDEVICE_EXTENSION DeviceExtension;
487 LONG BufferLength = 0;
488 PUNICODE_STRING SearchPattern = NULL;
489 FILE_INFORMATION_CLASS FileInformationClass;
491 PUCHAR Buffer = NULL;
492 PFILE_NAMES_INFORMATION Buffer0 = NULL;
496 BOOLEAN First = FALSE;
497 PIO_STACK_LOCATION Stack;
498 PFILE_OBJECT FileObject;
499 NTSTATUS Status = STATUS_SUCCESS;
501 DPRINT1("NtfsQueryDirectory() called\n");
503 DeviceExtension = DeviceObject->DeviceExtension;
504 Stack = IoGetCurrentIrpStackLocation(Irp);
505 FileObject = Stack->FileObject;
507 Ccb = (PCCB)FileObject->FsContext2;
510 /* Obtain the callers parameters */
511 BufferLength = Stack->Parameters.QueryDirectory.Length;
512 SearchPattern = Stack->Parameters.QueryDirectory.FileName;
513 FileInformationClass =
514 Stack->Parameters.QueryDirectory.FileInformationClass;
515 FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
518 if (SearchPattern != NULL)
520 if (!Ccb->DirectorySearchPattern)
523 Ccb->DirectorySearchPattern =
524 ExAllocatePool(NonPagedPool, SearchPattern->Length + sizeof(WCHAR));
525 if (!Ccb->DirectorySearchPattern)
527 return(STATUS_INSUFFICIENT_RESOURCES);
530 memcpy(Ccb->DirectorySearchPattern,
531 SearchPattern->Buffer,
532 SearchPattern->Length);
533 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
536 else if (!Ccb->DirectorySearchPattern)
539 Ccb->DirectorySearchPattern = ExAllocatePool(NonPagedPool, 2 * sizeof(WCHAR));
540 if (!Ccb->DirectorySearchPattern)
542 return(STATUS_INSUFFICIENT_RESOURCES);
544 Ccb->DirectorySearchPattern[0] = L'*';
545 Ccb->DirectorySearchPattern[1] = 0;
547 DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
549 /* Determine directory index */
550 if (Stack->Flags & SL_INDEX_SPECIFIED)
552 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
554 else if (First || (Stack->Flags & SL_RESTART_SCAN))
559 /* Determine Buffer for result */
562 Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
566 Buffer = Irp->UserBuffer;
568 DPRINT("Buffer=%x tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
570 TempFcb.ObjectName = TempFcb.PathName;
571 while (Status == STATUS_SUCCESS && BufferLength > 0)
573 Status = CdfsFindFile(DeviceExtension,
576 Ccb->DirectorySearchPattern,
579 DPRINT("Found %S, Status=%x, entry %x\n", TempFcb.ObjectName, Status, Ccb->Entry);
581 if (NT_SUCCESS(Status))
583 switch (FileInformationClass)
585 case FileNameInformation:
586 Status = CdfsGetNameInformation(&TempFcb,
588 (PFILE_NAMES_INFORMATION)Buffer,
592 case FileDirectoryInformation:
593 Status = CdfsGetDirectoryInformation(&TempFcb,
595 (PFILE_DIRECTORY_INFORMATION)Buffer,
599 case FileFullDirectoryInformation:
600 Status = CdfsGetFullDirectoryInformation(&TempFcb,
602 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
606 case FileBothDirectoryInformation:
607 Status = NtfsGetBothDirectoryInformation(&TempFcb,
609 (PFILE_BOTH_DIRECTORY_INFORMATION)Buffer,
614 Status = STATUS_INVALID_INFO_CLASS;
617 if (Status == STATUS_BUFFER_OVERFLOW)
621 Buffer0->NextEntryOffset = 0;
630 Buffer0->NextEntryOffset = 0;
635 Status = STATUS_NO_SUCH_FILE;
639 Status = STATUS_NO_MORE_FILES;
644 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
645 Buffer0->FileIndex = FileIndex++;
648 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
652 BufferLength -= Buffer0->NextEntryOffset;
653 Buffer += Buffer0->NextEntryOffset;
659 Buffer0->NextEntryOffset = 0;
664 Status = STATUS_SUCCESS;
673 NtfsDirectoryControl(PDEVICE_OBJECT DeviceObject,
676 PIO_STACK_LOCATION Stack;
679 DPRINT1("NtfsDirectoryControl() called\n");
681 Stack = IoGetCurrentIrpStackLocation(Irp);
683 switch (Stack->MinorFunction)
685 case IRP_MN_QUERY_DIRECTORY:
686 Status = NtfsQueryDirectory(DeviceObject,
690 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
691 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
692 Status = STATUS_NOT_IMPLEMENTED;
696 DPRINT1("NTFS: MinorFunction %d\n", Stack->MinorFunction);
697 Status = STATUS_INVALID_DEVICE_REQUEST;
701 Irp->IoStatus.Status = Status;
702 Irp->IoStatus.Information = 0;
704 IoCompleteRequest(Irp, IO_NO_INCREMENT);