CloverBootloader/FileSystems/FatPkg/EnhancedFatDxe/Misc.c
2019-09-03 12:58:42 +03:00

738 lines
16 KiB
C

/*++
Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the Software
License Agreement which accompanies this distribution.
Module Name:
Misc.c
Abstract:
Miscellaneous functions
Revision History
--*/
#include "Fat.h"
FAT_TASK *
FatCreateTask (
FAT_IFILE *IFile,
EFI_FILE_IO_TOKEN *Token
)
/*++
Routine Description:
Create the task
Arguments:
IFile - The instance of the open file.
Token - A pointer to the token associated with the transaction.
Return:
FAT_TASK * - Return the task instance.
**/
{
FAT_TASK *Task;
Task = AllocateZeroPool (sizeof (*Task));
if (Task != NULL) {
Task->Signature = FAT_TASK_SIGNATURE;
Task->IFile = IFile;
Task->FileIoToken = Token;
InitializeListHead (&Task->Subtasks);
InitializeListHead (&Task->Link);
}
return Task;
}
VOID
FatDestroyTask (
FAT_TASK *Task
)
/*++
Routine Description:
Destroy the task
Arguments:
Task - The task to be destroyed.
**/
{
LIST_ENTRY *Link;
FAT_SUBTASK *Subtask;
Link = GetFirstNode (&Task->Subtasks);
while (!IsNull (&Task->Subtasks, Link)) {
Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);
Link = FatDestroySubtask (Subtask);
}
FreePool (Task);
}
VOID
FatWaitNonblockingTask (
FAT_IFILE *IFile
)
/*++
Routine Description:
Wait all non-blocking requests complete.
Arguments:
IFile - The instance of the open file.
**/
{
BOOLEAN TaskQueueEmpty;
do {
EfiAcquireLock (&FatTaskLock);
TaskQueueEmpty = IsListEmpty (&IFile->Tasks);
EfiReleaseLock (&FatTaskLock);
} while (!TaskQueueEmpty);
}
LIST_ENTRY *
FatDestroySubtask (
FAT_SUBTASK *Subtask
)
/*++
Routine Description:
Remove the subtask from subtask list.
Arguments:
Subtask - The subtask to be removed.
Returns:
LIST_ENTRY * - The next node in the list.
--*/
{
LIST_ENTRY *Link;
gBS->CloseEvent (Subtask->DiskIo2Token.Event);
Link = RemoveEntryList (&Subtask->Link);
FreePool (Subtask);
return Link;
}
EFI_STATUS
FatQueueTask (
IN FAT_IFILE *IFile,
IN FAT_TASK *Task
)
/*++
Routine Description:
Execute the task
Arguments:
IFile - The instance of the open file.
Task - The task to be executed.
Returns:
EFI_SUCCESS - The task was executed sucessfully.
other - An error occurred when executing the task.
--*/
{
EFI_STATUS Status;
LIST_ENTRY *Link;
FAT_SUBTASK *Subtask;
//
// Sometimes the Task doesn't contain any subtasks, signal the event directly.
//
if (IsListEmpty (&Task->Subtasks)) {
Task->FileIoToken->Status = EFI_SUCCESS;
gBS->SignalEvent (Task->FileIoToken->Event);
FreePool (Task);
return EFI_SUCCESS;
}
EfiAcquireLock (&FatTaskLock);
InsertTailList (&IFile->Tasks, &Task->Link);
EfiReleaseLock (&FatTaskLock);
Status = EFI_SUCCESS;
for ( Link = GetFirstNode (&Task->Subtasks)
; !IsNull (&Task->Subtasks, Link)
; Link = GetNextNode (&Task->Subtasks, Link)
) {
Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);
if (Subtask->Write) {
Status = IFile->OFile->Volume->DiskIo2->WriteDiskEx (
IFile->OFile->Volume->DiskIo2,
IFile->OFile->Volume->MediaId,
Subtask->Offset,
&Subtask->DiskIo2Token,
Subtask->BufferSize,
Subtask->Buffer
);
} else {
Status = IFile->OFile->Volume->DiskIo2->ReadDiskEx (
IFile->OFile->Volume->DiskIo2,
IFile->OFile->Volume->MediaId,
Subtask->Offset,
&Subtask->DiskIo2Token,
Subtask->BufferSize,
Subtask->Buffer
);
}
if (EFI_ERROR (Status)) {
break;
}
}
if (EFI_ERROR (Status)) {
EfiAcquireLock (&FatTaskLock);
//
// Remove all the remaining subtasks when failure.
// We shouldn't remove all the tasks because the non-blocking requests have
// been submitted and cannot be canceled.
//
while (!IsNull (&Task->Subtasks, Link)) {
Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);
Link = FatDestroySubtask (Subtask);
}
if (IsListEmpty (&Task->Subtasks)) {
RemoveEntryList (&Task->Link);
FreePool (Task);
} else {
//
// If one or more subtasks have been already submitted, set FileIoToken
// to NULL so that the callback won't signal the event.
//
Task->FileIoToken = NULL;
}
EfiReleaseLock (&FatTaskLock);
}
return Status;
}
EFI_STATUS
FatAccessVolumeDirty (
IN FAT_VOLUME *Volume,
IN IO_MODE IoMode,
IN VOID *DirtyValue
)
/*++
Routine Description:
Set the volume as dirty or not
Arguments:
Volume - FAT file system volume.
IoMode - The access mode.
DirtyValue - Set the volume as dirty or not.
Returns:
EFI_SUCCESS - Set the new FAT entry value sucessfully.
other - An error occurred when operation the FAT entries.
--*/
{
UINTN WriteCount;
WriteCount = Volume->FatEntrySize;
return FatDiskIo (Volume, IoMode, Volume->FatPos + WriteCount, WriteCount, DirtyValue, NULL);
}
/**
Invoke a notification event
@param Event Event whose notification function is being invoked.
@param Context The pointer to the notification function's context,
which is implementation-dependent.
**/
VOID
EFIAPI
FatOnAccessComplete (
IN EFI_EVENT Event,
IN VOID *Context
)
/*++
Routine Description:
Invoke a notification event
case #1. some subtasks are not completed when the FatOpenEx checks the Task->Subtasks
- sets Task->SubtaskCollected so callback to signal the event and free the task.
case #2. all subtasks are completed when the FatOpenEx checks the Task->Subtasks
- FatOpenEx signal the event and free the task.
Arguments:
Event - Event whose notification function is being invoked.
Context - The pointer to the notification function's context,
which is implementation-dependent.
--*/
{
EFI_STATUS Status;
FAT_SUBTASK *Subtask;
FAT_TASK *Task;
//
// Avoid someone in future breaks the below assumption.
//
ASSERT (EfiGetCurrentTpl () == FatTaskLock.Tpl);
Subtask = (FAT_SUBTASK *) Context;
Task = Subtask->Task;
Status = Subtask->DiskIo2Token.TransactionStatus;
ASSERT (Task->Signature == FAT_TASK_SIGNATURE);
ASSERT (Subtask->Signature == FAT_SUBTASK_SIGNATURE);
//
// Remove the task unconditionally
//
FatDestroySubtask (Subtask);
//
// Task->FileIoToken is NULL which means the task will be ignored (just recycle the subtask and task memory).
//
if (Task->FileIoToken != NULL) {
if (IsListEmpty (&Task->Subtasks) || EFI_ERROR (Status)) {
Task->FileIoToken->Status = Status;
gBS->SignalEvent (Task->FileIoToken->Event);
//
// Mark Task->FileIoToken to NULL so that the subtasks belonging to the task will be ignored.
//
Task->FileIoToken = NULL;
}
}
if (IsListEmpty (&Task->Subtasks)) {
RemoveEntryList (&Task->Link);
FreePool (Task);
}
}
EFI_STATUS
FatDiskIo (
IN FAT_VOLUME *Volume,
IN IO_MODE IoMode,
IN UINT64 Offset,
IN UINTN BufferSize,
IN OUT VOID *Buffer,
IN FAT_TASK *Task
)
/*++
Routine Description:
General disk access function
Arguments:
Volume - FAT file system volume.
IoMode - The access mode (disk read/write or cache access).
Offset - The starting byte offset to read from.
BufferSize - Size of Buffer.
Buffer - Buffer containing read data.
Returns:
EFI_SUCCESS - The operation is performed successfully.
EFI_VOLUME_CORRUPTED - The accesss is
Others - The status of read/write the disk
--*/
{
EFI_STATUS Status;
EFI_DISK_IO_PROTOCOL *DiskIo;
EFI_DISK_READ IoFunction;
FAT_SUBTASK *Subtask;
//
// Verify the IO is in devices range
//
Status = EFI_VOLUME_CORRUPTED;
if (Offset + BufferSize <= Volume->VolumeSize) {
if (CACHE_ENABLED (IoMode)) {
//
// Access cache
//
Status = FatAccessCache (Volume, CACHE_TYPE (IoMode), RAW_ACCESS (IoMode), Offset, BufferSize, Buffer, Task);
} else {
//
// Access disk directly
//
if (Task == NULL) {
//
// Blocking access
//
DiskIo = Volume->DiskIo;
IoFunction = (IoMode == READ_DISK) ? DiskIo->ReadDisk : DiskIo->WriteDisk;
Status = IoFunction (DiskIo, Volume->MediaId, Offset, BufferSize, Buffer);
} else {
//
// Non-blocking access
//
Subtask = AllocateZeroPool (sizeof (*Subtask));
if (Subtask == NULL) {
Status = EFI_OUT_OF_RESOURCES;
} else {
Subtask->Signature = FAT_SUBTASK_SIGNATURE;
Subtask->Task = Task;
Subtask->Write = (BOOLEAN) (IoMode == WRITE_DISK);
Subtask->Offset = Offset;
Subtask->Buffer = Buffer;
Subtask->BufferSize = BufferSize;
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
FatOnAccessComplete,
Subtask,
&Subtask->DiskIo2Token.Event
);
if (!EFI_ERROR (Status)) {
InsertTailList (&Task->Subtasks, &Subtask->Link);
} else {
FreePool (Subtask);
}
}
}
}
}
if (EFI_ERROR (Status)) {
Volume->DiskError = TRUE;
// DEBUG ((EFI_D_ERROR, "FatDiskIo: error %r\n", Status));
}
return Status;
}
VOID
FatAcquireLock (
VOID
)
/*++
Routine Description:
Lock the volume.
Arguments:
None.
Returns:
None.
--*/
{
EfiAcquireLock (&FatFsLock);
}
EFI_STATUS
FatAcquireLockOrFail (
VOID
)
/*++
Routine Description:
Lock the volume.
If the lock is already in the acquired state, then EFI_ACCESS_DENIED is returned.
Otherwise, EFI_SUCCESS is returned.
Arguments:
None.
Returns:
EFI_SUCCESS - The volume is locked.
EFI_ACCESS_DENIED - The volume could not be locked because it is already locked.
--*/
{
return EfiAcquireLockOrFail (&FatFsLock);
}
VOID
FatReleaseLock (
VOID
)
/*++
Routine Description:
Unlock the volume.
Arguments:
Null.
Returns:
None.
--*/
{
EfiReleaseLock (&FatFsLock);
}
VOID
FatFreeDirEnt (
IN FAT_DIRENT *DirEnt
)
/*++
Routine Description:
Free directory entry.
Arguments:
DirEnt - The directory entry to be freed.
Returns:
None.
--*/
{
if (DirEnt->FileString != NULL) {
FreePool (DirEnt->FileString);
}
FreePool (DirEnt);
}
VOID
FatFreeVolume (
IN FAT_VOLUME *Volume
)
/*++
Routine Description:
Free volume structure (including the contents of directory cache and disk cache).
Arguments:
Volume - The volume structure to be freed.
Returns:
None.
--*/
{
//
// Free disk cache
//
if (Volume->CacheBuffer != NULL) {
FreePool (Volume->CacheBuffer);
}
//
// Free directory cache
//
FatCleanupODirCache (Volume);
FreePool (Volume);
}
VOID
FatEfiTimeToFatTime (
IN EFI_TIME *ETime,
OUT FAT_DATE_TIME *FTime
)
/*++
Routine Description:
Translate EFI time to FAT time.
Arguments:
ETime - The time of EFI_TIME.
FTime - The time of FAT_DATE_TIME.
Returns:
None.
--*/
{
//
// ignores timezone info in source ETime
//
if (ETime->Year > 1980) {
FTime->Date.Year = (UINT16) (ETime->Year - 1980);
}
if (ETime->Year >= 1980 + FAT_MAX_YEAR_FROM_1980) {
FTime->Date.Year = FAT_MAX_YEAR_FROM_1980;
}
FTime->Date.Month = ETime->Month;
FTime->Date.Day = ETime->Day;
FTime->Time.Hour = ETime->Hour;
FTime->Time.Minute = ETime->Minute;
FTime->Time.DoubleSecond = (UINT16) (ETime->Second / 2);
}
VOID
FatFatTimeToEfiTime (
IN FAT_DATE_TIME *FTime,
OUT EFI_TIME *ETime
)
/*++
Routine Description:
Translate Fat time to EFI time.
Arguments:
FTime - The time of FAT_DATE_TIME.
ETime - The time of EFI_TIME.
Returns:
None.
--*/
{
ETime->Year = (UINT16) (FTime->Date.Year + 1980);
ETime->Month = (UINT8) FTime->Date.Month;
ETime->Day = (UINT8) FTime->Date.Day;
ETime->Hour = (UINT8) FTime->Time.Hour;
ETime->Minute = (UINT8) FTime->Time.Minute;
ETime->Second = (UINT8) (FTime->Time.DoubleSecond * 2);
ETime->Nanosecond = 0;
ETime->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
ETime->Daylight = 0;
}
VOID
FatGetCurrentFatTime (
OUT FAT_DATE_TIME *FatNow
)
/*++
Routine Description:
Get Current FAT time.
Arguments:
FatNow - Current FAT time.
Returns:
None.
--*/
{
EFI_STATUS Status;
EFI_TIME Now;
Status = gRT->GetTime (&Now, NULL);
if (!EFI_ERROR (Status)) {
FatEfiTimeToFatTime (&Now, FatNow);
} else {
ZeroMem (&Now, sizeof (EFI_TIME));
Now.Year = 1980;
Now.Month = 1;
Now.Day = 1;
FatEfiTimeToFatTime (&Now, FatNow);
}
}
BOOLEAN
FatIsValidTime (
IN EFI_TIME *Time
)
/*++
Routine Description:
Check whether a time is valid.
Arguments:
Time - The time of EFI_TIME.
Returns:
TRUE - The time is valid.
FALSE - The time is not valid.
--*/
{
static UINT8 MonthDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
UINTN Day;
BOOLEAN ValidTime;
ValidTime = TRUE;
//
// Check the fields for range problems
// Fat can only support from 1980
//
if (Time->Year < 1980 ||
Time->Month < 1 ||
Time->Month > 12 ||
Time->Day < 1 ||
Time->Day > 31 ||
Time->Hour > 23 ||
Time->Minute > 59 ||
Time->Second > 59 ||
Time->Nanosecond > 999999999
) {
ValidTime = FALSE;
} else {
//
// Perform a more specific check of the day of the month
//
Day = MonthDays[Time->Month - 1];
if (Time->Month == 2 && IS_LEAP_YEAR (Time->Year)) {
Day += 1;
//
// 1 extra day this month
//
}
if (Time->Day > Day) {
ValidTime = FALSE;
}
}
return ValidTime;
}