Doxygen

dirctl.c
Go to the documentation of this file.
00001 /*
00002  *  ReactOS kernel
00003  *  Copyright (C) 2002, 2003, 2014 ReactOS Team
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
00018  *
00019  * COPYRIGHT:        See COPYING in the top level directory
00020  * PROJECT:          ReactOS kernel
00021  * FILE:             drivers/filesystem/ntfs/dirctl.c
00022  * PURPOSE:          NTFS filesystem driver
00023  * PROGRAMMERS:      Eric Kohl
00024  *                   Pierre Schweitzer (pierre@reactos.org)
00025  *                   Hervé Poussineau (hpoussin@reactos.org)
00026  */
00027 
00028 /* INCLUDES *****************************************************************/
00029 
00030 #include "ntfs.h"
00031 
00032 #define NDEBUG
00033 #include <debug.h>
00034 
00035 /* FUNCTIONS ****************************************************************/
00036 
00037 
00038 ULONGLONG
00039 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
00040                 PFILE_RECORD_HEADER FileRecord,
00041                 PCWSTR Stream,
00042                 ULONG StreamLength,
00043                 PULONGLONG AllocatedSize)
00044 {
00045     ULONGLONG Size = 0ULL;
00046     ULONGLONG Allocated = 0ULL;
00047     NTSTATUS Status;
00048     PNTFS_ATTR_CONTEXT DataContext;
00049 
00050     Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext);
00051     if (NT_SUCCESS(Status))
00052     {
00053         Size = AttributeDataLength(&DataContext->Record);
00054         Allocated = AttributeAllocatedLength(&DataContext->Record);
00055         ReleaseAttributeContext(DataContext);
00056     }
00057 
00058     if (AllocatedSize != NULL) *AllocatedSize = Allocated;
00059 
00060     return Size;
00061 }
00062 
00063 
00064 static NTSTATUS
00065 NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt,
00066                        PFILE_RECORD_HEADER FileRecord,
00067                        ULONGLONG MFTIndex,
00068                        PFILE_NAMES_INFORMATION Info,
00069                        ULONG BufferLength)
00070 {
00071     ULONG Length;
00072     PFILENAME_ATTRIBUTE FileName;
00073 
00074     DPRINT("NtfsGetNameInformation() called\n");
00075 
00076     FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
00077     if (FileName == NULL)
00078     {
00079         DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
00080         NtfsDumpFileAttributes(DeviceExt, FileRecord);
00081         return STATUS_OBJECT_NAME_NOT_FOUND;
00082     }
00083 
00084     Length = FileName->NameLength * sizeof (WCHAR);
00085     if ((sizeof(FILE_NAMES_INFORMATION) + Length) > BufferLength)
00086         return(STATUS_BUFFER_OVERFLOW);
00087 
00088     Info->FileNameLength = Length;
00089     Info->NextEntryOffset =
00090         ROUND_UP(sizeof(FILE_NAMES_INFORMATION) + Length, sizeof(ULONG));
00091     RtlCopyMemory(Info->FileName, FileName->Name, Length);
00092 
00093     return(STATUS_SUCCESS);
00094 }
00095 
00096 
00097 static NTSTATUS
00098 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
00099                             PFILE_RECORD_HEADER FileRecord,
00100                             ULONGLONG MFTIndex,
00101                             PFILE_DIRECTORY_INFORMATION Info,
00102                             ULONG BufferLength)
00103 {
00104     ULONG Length;
00105     PFILENAME_ATTRIBUTE FileName;
00106     PSTANDARD_INFORMATION StdInfo;
00107 
00108     DPRINT("NtfsGetDirectoryInformation() called\n");
00109 
00110     FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
00111     if (FileName == NULL)
00112     {
00113         DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
00114         NtfsDumpFileAttributes(DeviceExt, FileRecord);
00115         return STATUS_OBJECT_NAME_NOT_FOUND;
00116     }
00117 
00118     StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
00119     ASSERT(StdInfo != NULL);
00120 
00121     Length = FileName->NameLength * sizeof (WCHAR);
00122     if ((sizeof(FILE_DIRECTORY_INFORMATION) + Length) > BufferLength)
00123         return(STATUS_BUFFER_OVERFLOW);
00124 
00125     Info->FileNameLength = Length;
00126     Info->NextEntryOffset =
00127         ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
00128     RtlCopyMemory(Info->FileName, FileName->Name, Length);
00129 
00130     Info->CreationTime.QuadPart = FileName->CreationTime;
00131     Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
00132     Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
00133     Info->ChangeTime.QuadPart = FileName->ChangeTime;
00134 
00135     /* Convert file flags */
00136     NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
00137 
00138     Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
00139 
00140     Info->FileIndex = MFTIndex;
00141 
00142     return STATUS_SUCCESS;
00143 }
00144 
00145 
00146 static NTSTATUS
00147 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
00148                                 PFILE_RECORD_HEADER FileRecord,
00149                                 ULONGLONG MFTIndex,
00150                                 PFILE_FULL_DIRECTORY_INFORMATION Info,
00151                                 ULONG BufferLength)
00152 {
00153     ULONG Length;
00154     PFILENAME_ATTRIBUTE FileName;
00155     PSTANDARD_INFORMATION StdInfo;
00156 
00157     DPRINT("NtfsGetFullDirectoryInformation() called\n");
00158 
00159     FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
00160     if (FileName == NULL)
00161     {
00162         DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
00163         NtfsDumpFileAttributes(DeviceExt, FileRecord);
00164         return STATUS_OBJECT_NAME_NOT_FOUND;
00165     }
00166 
00167     StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
00168     ASSERT(StdInfo != NULL);
00169 
00170     Length = FileName->NameLength * sizeof (WCHAR);
00171     if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length) > BufferLength)
00172         return(STATUS_BUFFER_OVERFLOW);
00173 
00174     Info->FileNameLength = Length;
00175     Info->NextEntryOffset =
00176         ROUND_UP(sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
00177     RtlCopyMemory(Info->FileName, FileName->Name, Length);
00178 
00179     Info->CreationTime.QuadPart = FileName->CreationTime;
00180     Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
00181     Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
00182     Info->ChangeTime.QuadPart = FileName->ChangeTime;
00183 
00184     /* Convert file flags */
00185     NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
00186 
00187     Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
00188 
00189     Info->FileIndex = MFTIndex;
00190     Info->EaSize = 0;
00191 
00192     return STATUS_SUCCESS;
00193 }
00194 
00195 
00196 static NTSTATUS
00197 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
00198                                 PFILE_RECORD_HEADER FileRecord,
00199                                 ULONGLONG MFTIndex,
00200                                 PFILE_BOTH_DIR_INFORMATION Info,
00201                                 ULONG BufferLength)
00202 {
00203     ULONG Length;
00204     PFILENAME_ATTRIBUTE FileName, ShortFileName;
00205     PSTANDARD_INFORMATION StdInfo;
00206 
00207     DPRINT("NtfsGetBothDirectoryInformation() called\n");
00208 
00209     FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
00210     if (FileName == NULL)
00211     {
00212         DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
00213         NtfsDumpFileAttributes(DeviceExt, FileRecord);
00214         return STATUS_OBJECT_NAME_NOT_FOUND;
00215     }
00216     ShortFileName = GetFileNameFromRecord(DeviceExt, FileRecord, NTFS_FILE_NAME_DOS);
00217 
00218     StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
00219     ASSERT(StdInfo != NULL);
00220 
00221     Length = FileName->NameLength * sizeof (WCHAR);
00222     if ((sizeof(FILE_BOTH_DIR_INFORMATION) + Length) > BufferLength)
00223         return(STATUS_BUFFER_OVERFLOW);
00224 
00225     Info->FileNameLength = Length;
00226     Info->NextEntryOffset =
00227         ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) + Length, sizeof(ULONG));
00228     RtlCopyMemory(Info->FileName, FileName->Name, Length);
00229 
00230     if (ShortFileName)
00231     {
00232         /* Should we upcase the filename? */
00233         ASSERT(ShortFileName->NameLength <= ARRAYSIZE(Info->ShortName));
00234         Info->ShortNameLength = ShortFileName->NameLength * sizeof(WCHAR);
00235         RtlCopyMemory(Info->ShortName, ShortFileName->Name, Info->ShortNameLength);
00236     }
00237     else
00238     {
00239         Info->ShortName[0] = 0;
00240         Info->ShortNameLength = 0;
00241     }
00242 
00243     Info->CreationTime.QuadPart = FileName->CreationTime;
00244     Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
00245     Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
00246     Info->ChangeTime.QuadPart = FileName->ChangeTime;
00247 
00248     /* Convert file flags */
00249     NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
00250 
00251     Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
00252 
00253     Info->FileIndex = MFTIndex;
00254     Info->EaSize = 0;
00255 
00256     return STATUS_SUCCESS;
00257 }
00258 
00259 
00260 NTSTATUS
00261 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
00262 {
00263     PIRP Irp;
00264     PDEVICE_OBJECT DeviceObject;
00265     PDEVICE_EXTENSION DeviceExtension;
00266     LONG BufferLength = 0;
00267     PUNICODE_STRING SearchPattern = NULL;
00268     FILE_INFORMATION_CLASS FileInformationClass;
00269     ULONG FileIndex = 0;
00270     PUCHAR Buffer = NULL;
00271     PFILE_NAMES_INFORMATION Buffer0 = NULL;
00272     PNTFS_FCB Fcb;
00273     PNTFS_CCB Ccb;
00274     BOOLEAN First = FALSE;
00275     PIO_STACK_LOCATION Stack;
00276     PFILE_OBJECT FileObject;
00277     NTSTATUS Status = STATUS_SUCCESS;
00278     PFILE_RECORD_HEADER FileRecord;
00279     ULONGLONG MFTRecord, OldMFTRecord = 0;
00280     UNICODE_STRING Pattern;
00281 
00282     DPRINT1("NtfsQueryDirectory() called\n");
00283 
00284     ASSERT(IrpContext);
00285     Irp = IrpContext->Irp;
00286     DeviceObject = IrpContext->DeviceObject;
00287 
00288     DeviceExtension = DeviceObject->DeviceExtension;
00289     Stack = IoGetCurrentIrpStackLocation(Irp);
00290     FileObject = Stack->FileObject;
00291 
00292     Ccb = (PNTFS_CCB)FileObject->FsContext2;
00293     Fcb = (PNTFS_FCB)FileObject->FsContext;
00294 
00295     /* Obtain the callers parameters */
00296     BufferLength = Stack->Parameters.QueryDirectory.Length;
00297     SearchPattern = Stack->Parameters.QueryDirectory.FileName;
00298     FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
00299     FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
00300 
00301     if (SearchPattern != NULL)
00302     {
00303         if (!Ccb->DirectorySearchPattern)
00304         {
00305             First = TRUE;
00306             Pattern.Length = 0;
00307             Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
00308             Ccb->DirectorySearchPattern = Pattern.Buffer =
00309                 ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS);
00310             if (!Ccb->DirectorySearchPattern)
00311             {
00312                 return STATUS_INSUFFICIENT_RESOURCES;
00313             }
00314 
00315             memcpy(Ccb->DirectorySearchPattern, SearchPattern->Buffer, SearchPattern->Length);
00316             Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
00317         }
00318     }
00319     else if (!Ccb->DirectorySearchPattern)
00320     {
00321         First = TRUE;
00322         Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
00323         if (!Ccb->DirectorySearchPattern)
00324         {
00325             return STATUS_INSUFFICIENT_RESOURCES;
00326         }
00327 
00328         Ccb->DirectorySearchPattern[0] = L'*';
00329         Ccb->DirectorySearchPattern[1] = 0;
00330     }
00331 
00332     RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
00333     DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
00334     DPRINT("In: '%S'\n", Fcb->PathName);
00335 
00336     /* Determine directory index */
00337     if (Stack->Flags & SL_INDEX_SPECIFIED)
00338     {
00339         Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
00340     }
00341     else if (First || (Stack->Flags & SL_RESTART_SCAN))
00342     {
00343         Ccb->Entry = 0;
00344     }
00345 
00346     /* Get Buffer for result */
00347     Buffer = NtfsGetUserBuffer(Irp, FALSE);
00348 
00349     DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
00350 
00351     while (Status == STATUS_SUCCESS && BufferLength > 0)
00352     {
00353         Status = NtfsFindFileAt(DeviceExtension,
00354                                 &Pattern,
00355                                 &Ccb->Entry,
00356                                 &FileRecord,
00357                                 &MFTRecord,
00358                                 Fcb->MFTIndex);
00359 
00360         if (NT_SUCCESS(Status))
00361         {
00362             /* HACK: files with both a short name and a long name are present twice in the index.
00363              * Ignore the second entry, if it is immediately following the first one.
00364              */
00365             if (MFTRecord == OldMFTRecord)
00366             {
00367                 DPRINT1("Ignoring duplicate MFT entry 0x%x\n", MFTRecord);
00368                 Ccb->Entry++;
00369                 ExFreePoolWithTag(FileRecord, TAG_NTFS);
00370                 continue;
00371             }
00372             OldMFTRecord = MFTRecord;
00373 
00374             switch (FileInformationClass)
00375             {
00376                 case FileNameInformation:
00377                     Status = NtfsGetNameInformation(DeviceExtension,
00378                                                     FileRecord,
00379                                                     MFTRecord,
00380                                                     (PFILE_NAMES_INFORMATION)Buffer,
00381                                                     BufferLength);
00382                     break;
00383 
00384                 case FileDirectoryInformation:
00385                     Status = NtfsGetDirectoryInformation(DeviceExtension,
00386                                                          FileRecord,
00387                                                          MFTRecord,
00388                                                          (PFILE_DIRECTORY_INFORMATION)Buffer,
00389                                                          BufferLength);
00390                     break;
00391 
00392                 case FileFullDirectoryInformation:
00393                     Status = NtfsGetFullDirectoryInformation(DeviceExtension,
00394                                                              FileRecord,
00395                                                              MFTRecord,
00396                                                              (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
00397                                                              BufferLength);
00398                     break;
00399 
00400                 case FileBothDirectoryInformation:
00401                     Status = NtfsGetBothDirectoryInformation(DeviceExtension,
00402                                                              FileRecord,
00403                                                              MFTRecord,
00404                                                              (PFILE_BOTH_DIR_INFORMATION)Buffer,
00405                                                              BufferLength);
00406                     break;
00407 
00408                 default:
00409                     Status = STATUS_INVALID_INFO_CLASS;
00410             }
00411 
00412             if (Status == STATUS_BUFFER_OVERFLOW)
00413             {
00414                 if (Buffer0)
00415                 {
00416                     Buffer0->NextEntryOffset = 0;
00417                 }
00418                 break;
00419             }
00420         }
00421         else
00422         {
00423             if (Buffer0)
00424             {
00425                 Buffer0->NextEntryOffset = 0;
00426             }
00427 
00428             if (First)
00429             {
00430                 Status = STATUS_NO_SUCH_FILE;
00431             }
00432             else
00433             {
00434                 Status = STATUS_NO_MORE_FILES;
00435             }
00436             break;
00437         }
00438 
00439         Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
00440         Buffer0->FileIndex = FileIndex++;
00441         Ccb->Entry++;
00442 
00443         if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
00444         {
00445             break;
00446         }
00447         BufferLength -= Buffer0->NextEntryOffset;
00448         Buffer += Buffer0->NextEntryOffset;
00449         ExFreePoolWithTag(FileRecord, TAG_NTFS);
00450     }
00451 
00452     if (Buffer0)
00453     {
00454         Buffer0->NextEntryOffset = 0;
00455     }
00456 
00457     if (FileIndex > 0)
00458     {
00459         Status = STATUS_SUCCESS;
00460     }
00461 
00462     return Status;
00463 }
00464 
00465 
00466 NTSTATUS
00467 NtfsDirectoryControl(PNTFS_IRP_CONTEXT IrpContext)
00468 {
00469     NTSTATUS Status = STATUS_UNSUCCESSFUL;
00470 
00471     DPRINT1("NtfsDirectoryControl() called\n");
00472 
00473     switch (IrpContext->MinorFunction)
00474     {
00475         case IRP_MN_QUERY_DIRECTORY:
00476             Status = NtfsQueryDirectory(IrpContext);
00477             break;
00478 
00479         case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
00480             DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
00481             Status = STATUS_NOT_IMPLEMENTED;
00482             break;
00483 
00484         default:
00485             Status = STATUS_INVALID_DEVICE_REQUEST;
00486             break;
00487     }
00488 
00489     IrpContext->Irp->IoStatus.Information = 0;
00490 
00491     return Status;
00492 }
00493 
00494 /* EOF */