/* * ReactOS kernel * Copyright (C) 2002 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: drivers/filesystem/ntfs/dirctl.c * PURPOSE: NTFS filesystem driver * PROGRAMMERS: Eric Kohl * Hervé Poussineau (hpoussin@reactos.org) * Pierre Schweitzer (pierre@reactos.org) */ /* INCLUDES *****************************************************************/ #include "ntfs.h" #define NDEBUG #include /* FUNCTIONS ****************************************************************/ /* * FUNCTION: Retrieve the standard file information */ static NTSTATUS NtfsGetStandardInformation(PNTFS_FCB Fcb, PDEVICE_OBJECT DeviceObject, PFILE_STANDARD_INFORMATION StandardInfo, PULONG BufferLength) { UNREFERENCED_PARAMETER(DeviceObject); DPRINT1("NtfsGetStandardInformation(%p, %p, %p, %p)\n", Fcb, DeviceObject, StandardInfo, BufferLength); if (*BufferLength < sizeof(FILE_STANDARD_INFORMATION)) return STATUS_BUFFER_TOO_SMALL; /* PRECONDITION */ ASSERT(StandardInfo != NULL); ASSERT(Fcb != NULL); RtlZeroMemory(StandardInfo, sizeof(FILE_STANDARD_INFORMATION)); StandardInfo->AllocationSize = Fcb->RFCB.AllocationSize; StandardInfo->EndOfFile = Fcb->RFCB.FileSize; StandardInfo->NumberOfLinks = Fcb->LinkCount; StandardInfo->DeletePending = FALSE; StandardInfo->Directory = NtfsFCBIsDirectory(Fcb); *BufferLength -= sizeof(FILE_STANDARD_INFORMATION); return STATUS_SUCCESS; } static NTSTATUS NtfsGetPositionInformation(PFILE_OBJECT FileObject, PFILE_POSITION_INFORMATION PositionInfo, PULONG BufferLength) { DPRINT1("NtfsGetPositionInformation(%p, %p, %p)\n", FileObject, PositionInfo, BufferLength); if (*BufferLength < sizeof(FILE_POSITION_INFORMATION)) return STATUS_BUFFER_TOO_SMALL; PositionInfo->CurrentByteOffset.QuadPart = FileObject->CurrentByteOffset.QuadPart; DPRINT("Getting position %I64x\n", PositionInfo->CurrentByteOffset.QuadPart); *BufferLength -= sizeof(FILE_POSITION_INFORMATION); return STATUS_SUCCESS; } static NTSTATUS NtfsGetBasicInformation(PFILE_OBJECT FileObject, PNTFS_FCB Fcb, PDEVICE_OBJECT DeviceObject, PFILE_BASIC_INFORMATION BasicInfo, PULONG BufferLength) { PFILENAME_ATTRIBUTE FileName = &Fcb->Entry; DPRINT1("NtfsGetBasicInformation(%p, %p, %p, %p, %p)\n", FileObject, Fcb, DeviceObject, BasicInfo, BufferLength); if (*BufferLength < sizeof(FILE_BASIC_INFORMATION)) return STATUS_BUFFER_TOO_SMALL; BasicInfo->CreationTime.QuadPart = FileName->CreationTime; BasicInfo->LastAccessTime.QuadPart = FileName->LastAccessTime; BasicInfo->LastWriteTime.QuadPart = FileName->LastWriteTime; BasicInfo->ChangeTime.QuadPart = FileName->ChangeTime; NtfsFileFlagsToAttributes(FileName->FileAttributes, &BasicInfo->FileAttributes); *BufferLength -= sizeof(FILE_BASIC_INFORMATION); return STATUS_SUCCESS; } /* * FUNCTION: Retrieve the file name information */ static NTSTATUS NtfsGetNameInformation(PFILE_OBJECT FileObject, PNTFS_FCB Fcb, PDEVICE_OBJECT DeviceObject, PFILE_NAME_INFORMATION NameInfo, PULONG BufferLength) { ULONG BytesToCopy; UNREFERENCED_PARAMETER(FileObject); UNREFERENCED_PARAMETER(DeviceObject); DPRINT1("NtfsGetNameInformation(%p, %p, %p, %p, %p)\n", FileObject, Fcb, DeviceObject, NameInfo, BufferLength); ASSERT(NameInfo != NULL); ASSERT(Fcb != NULL); /* If buffer can't hold at least the file name length, bail out */ if (*BufferLength < (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0])) return STATUS_BUFFER_TOO_SMALL; /* Save file name length, and as much file len, as buffer length allows */ NameInfo->FileNameLength = wcslen(Fcb->PathName) * sizeof(WCHAR); /* Calculate amount of bytes to copy not to overflow the buffer */ BytesToCopy = min(NameInfo->FileNameLength, *BufferLength - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0])); /* Fill in the bytes */ RtlCopyMemory(NameInfo->FileName, Fcb->PathName, BytesToCopy); /* Check if we could write more but are not able to */ if (*BufferLength < NameInfo->FileNameLength + (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0])) { /* Return number of bytes written */ *BufferLength -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + BytesToCopy; return STATUS_BUFFER_OVERFLOW; } /* We filled up as many bytes, as needed */ *BufferLength -= (FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + NameInfo->FileNameLength); return STATUS_SUCCESS; } static NTSTATUS NtfsGetInternalInformation(PNTFS_FCB Fcb, PFILE_INTERNAL_INFORMATION InternalInfo, PULONG BufferLength) { DPRINT1("NtfsGetInternalInformation(%p, %p, %p)\n", Fcb, InternalInfo, BufferLength); ASSERT(InternalInfo); ASSERT(Fcb); if (*BufferLength < sizeof(FILE_INTERNAL_INFORMATION)) return STATUS_BUFFER_TOO_SMALL; InternalInfo->IndexNumber.QuadPart = Fcb->MFTIndex; *BufferLength -= sizeof(FILE_INTERNAL_INFORMATION); return STATUS_SUCCESS; } static NTSTATUS NtfsGetNetworkOpenInformation(PNTFS_FCB Fcb, PDEVICE_EXTENSION DeviceExt, PFILE_NETWORK_OPEN_INFORMATION NetworkInfo, PULONG BufferLength) { PFILENAME_ATTRIBUTE FileName = &Fcb->Entry; DPRINT1("NtfsGetNetworkOpenInformation(%p, %p, %p, %p)\n", Fcb, DeviceExt, NetworkInfo, BufferLength); if (*BufferLength < sizeof(FILE_NETWORK_OPEN_INFORMATION)) return STATUS_BUFFER_TOO_SMALL; NetworkInfo->CreationTime.QuadPart = FileName->CreationTime; NetworkInfo->LastAccessTime.QuadPart = FileName->LastAccessTime; NetworkInfo->LastWriteTime.QuadPart = FileName->LastWriteTime; NetworkInfo->ChangeTime.QuadPart = FileName->ChangeTime; NetworkInfo->EndOfFile = Fcb->RFCB.FileSize; NetworkInfo->AllocationSize = Fcb->RFCB.AllocationSize; NtfsFileFlagsToAttributes(FileName->FileAttributes, &NetworkInfo->FileAttributes); *BufferLength -= sizeof(FILE_NETWORK_OPEN_INFORMATION); return STATUS_SUCCESS; } static NTSTATUS NtfsGetSteamInformation(PNTFS_FCB Fcb, PDEVICE_EXTENSION DeviceExt, PFILE_STREAM_INFORMATION StreamInfo, PULONG BufferLength) { ULONG CurrentSize; FIND_ATTR_CONTXT Context; PNTFS_ATTR_RECORD Attribute; NTSTATUS Status, BrowseStatus; PFILE_RECORD_HEADER FileRecord; PFILE_STREAM_INFORMATION CurrentInfo = StreamInfo, Previous = NULL; if (*BufferLength < sizeof(FILE_STREAM_INFORMATION)) return STATUS_BUFFER_TOO_SMALL; FileRecord = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS); if (FileRecord == NULL) { DPRINT1("Not enough memory!\n"); return STATUS_INSUFFICIENT_RESOURCES; } Status = ReadFileRecord(DeviceExt, Fcb->MFTIndex, FileRecord); if (!NT_SUCCESS(Status)) { DPRINT1("Can't find record!\n"); ExFreePoolWithTag(FileRecord, TAG_NTFS); return Status; } BrowseStatus = FindFirstAttribute(&Context, DeviceExt, FileRecord, FALSE, &Attribute); while (NT_SUCCESS(BrowseStatus)) { if (Attribute->Type == AttributeData) { CurrentSize = FIELD_OFFSET(FILE_STREAM_INFORMATION, StreamName) + Attribute->NameLength * sizeof(WCHAR) + wcslen(L"::$DATA") * sizeof(WCHAR); if (CurrentSize > *BufferLength) { Status = STATUS_BUFFER_OVERFLOW; break; } CurrentInfo->NextEntryOffset = 0; CurrentInfo->StreamNameLength = (Attribute->NameLength + wcslen(L"::$DATA")) * sizeof(WCHAR); CurrentInfo->StreamSize.QuadPart = AttributeDataLength(Attribute); CurrentInfo->StreamAllocationSize.QuadPart = AttributeAllocatedLength(Attribute); CurrentInfo->StreamName[0] = L':'; RtlMoveMemory(&CurrentInfo->StreamName[1], (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset), CurrentInfo->StreamNameLength); RtlMoveMemory(&CurrentInfo->StreamName[Attribute->NameLength + 1], L":$DATA", sizeof(L":$DATA") - sizeof(UNICODE_NULL)); if (Previous != NULL) { Previous->NextEntryOffset = (ULONG_PTR)CurrentInfo - (ULONG_PTR)Previous; } Previous = CurrentInfo; CurrentInfo = (PFILE_STREAM_INFORMATION)((ULONG_PTR)CurrentInfo + CurrentSize); *BufferLength -= CurrentSize; } BrowseStatus = FindNextAttribute(&Context, &Attribute); } FindCloseAttribute(&Context); ExFreePoolWithTag(FileRecord, TAG_NTFS); return Status; } /* * FUNCTION: Retrieve the specified file information */ NTSTATUS NtfsQueryInformation(PNTFS_IRP_CONTEXT IrpContext) { FILE_INFORMATION_CLASS FileInformationClass; PIO_STACK_LOCATION Stack; PFILE_OBJECT FileObject; PNTFS_FCB Fcb; PVOID SystemBuffer; ULONG BufferLength; PIRP Irp; PDEVICE_OBJECT DeviceObject; NTSTATUS Status = STATUS_SUCCESS; DPRINT1("NtfsQueryInformation(%p)\n", IrpContext); Irp = IrpContext->Irp; Stack = IrpContext->Stack; DeviceObject = IrpContext->DeviceObject; FileInformationClass = Stack->Parameters.QueryFile.FileInformationClass; FileObject = IrpContext->FileObject; Fcb = FileObject->FsContext; SystemBuffer = Irp->AssociatedIrp.SystemBuffer; BufferLength = Stack->Parameters.QueryFile.Length; if (!ExAcquireResourceSharedLite(&Fcb->MainResource, BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT))) { return NtfsMarkIrpContextForQueue(IrpContext); } switch (FileInformationClass) { case FileStandardInformation: Status = NtfsGetStandardInformation(Fcb, DeviceObject, SystemBuffer, &BufferLength); break; case FilePositionInformation: Status = NtfsGetPositionInformation(FileObject, SystemBuffer, &BufferLength); break; case FileBasicInformation: Status = NtfsGetBasicInformation(FileObject, Fcb, DeviceObject, SystemBuffer, &BufferLength); break; case FileNameInformation: Status = NtfsGetNameInformation(FileObject, Fcb, DeviceObject, SystemBuffer, &BufferLength); break; case FileInternalInformation: Status = NtfsGetInternalInformation(Fcb, SystemBuffer, &BufferLength); break; case FileNetworkOpenInformation: Status = NtfsGetNetworkOpenInformation(Fcb, DeviceObject->DeviceExtension, SystemBuffer, &BufferLength); break; case FileStreamInformation: Status = NtfsGetSteamInformation(Fcb, DeviceObject->DeviceExtension, SystemBuffer, &BufferLength); break; case FileAlternateNameInformation: case FileAllInformation: DPRINT1("Unimplemented information class %u\n", FileInformationClass); Status = STATUS_NOT_IMPLEMENTED; break; default: DPRINT1("Unimplemented information class %u\n", FileInformationClass); Status = STATUS_INVALID_PARAMETER; } ExReleaseResourceLite(&Fcb->MainResource); if (NT_SUCCESS(Status)) Irp->IoStatus.Information = Stack->Parameters.QueryFile.Length - BufferLength; else Irp->IoStatus.Information = 0; return Status; } /* EOF */