summaryrefslogtreecommitdiff
path: root/Core/EM/FileSystem/Info.c
diff options
context:
space:
mode:
Diffstat (limited to 'Core/EM/FileSystem/Info.c')
-rw-r--r--Core/EM/FileSystem/Info.c1313
1 files changed, 1313 insertions, 0 deletions
diff --git a/Core/EM/FileSystem/Info.c b/Core/EM/FileSystem/Info.c
new file mode 100644
index 0000000..9435c34
--- /dev/null
+++ b/Core/EM/FileSystem/Info.c
@@ -0,0 +1,1313 @@
+//**********************************************************************
+//**********************************************************************
+//** **
+//** (C)Copyright 1985-2011, American Megatrends, Inc. **
+//** **
+//** All Rights Reserved. **
+//** **
+//** 5555 Oakbrook Pkwy, Suite 200, Norcross, GA 30093 **
+//** **
+//** Phone: (770)-246-8600 **
+//** **
+//**********************************************************************
+//**********************************************************************
+
+//**********************************************************************
+// $Header: /Alaska/SOURCE/Core/Modules/FileSystem/Info.c 15 5/05/11 3:44p Pats $
+//
+// $Revision: 15 $
+//
+// $Date: 5/05/11 3:44p $
+//**********************************************************************
+// Revision History
+// ----------------
+// $Log: /Alaska/SOURCE/Core/Modules/FileSystem/Info.c $
+//
+// 15 5/05/11 3:44p Pats
+// [TAG] - EIP 58999
+// [Category]- BUG FIX
+// [Severity]- Major
+// [Symptom] - Cannot launch Shell from USB Filesystem device in Debug
+// mode with latest Filesystem driver.
+// [RootCause] - Functions using DISKIO were raising TPL to a fixed level.
+// [Solution] - Modified above functions to check for the higher of the
+// fixed level or current level.
+// [Files] - Info.c, MediaAccess.c, Open.c, FileSystem.h
+//
+// 14 2/25/11 2:44p Pats
+// [TAG] - EIP 54399
+// [Category]- BUG FIX
+// [Severity] - Urgent
+// [Symptom] - SCT 2.3 fails Bootable Image Support Test\Simple File
+// System Protocol
+// [RootCause] - Does not check for matching name when attempting to
+// change file name.
+// [Solution] - Modified function SetfileInfo() to check for matching
+// name.
+// [Files] - info.c
+//
+// 13 1/19/11 4:56p Pats
+// [TAG] - EIP 52430
+// [Category]- BUG FIX
+// [Severity] - Normal
+// [Symptom] - When a directory is moved, the long name of the old
+// directory is not deleted.
+// [RootCause] - Only the short name component was being deleted in
+// SetFileInfo().
+// [Solution] - Modified function SetfileInfo() to delete the long name
+// component also.
+// [Files] - info.c
+//
+// 12 1/14/11 2:59p Pats
+// [TAG] - EIP 51754
+// [Category]- BUG FIX
+// [Severity]- Minor
+// [Symptom] - The shell "mv" command makes directories dissapear.
+// [RootCause] - Directorys were not removed from cache when deleted..
+// [Solution] - Modified function SetfileInfo() to remove directories from
+// cache when deleted.
+// [Files] - info.c
+//
+// 11 1/06/11 5:54p Oleksiyy
+// [TAG] EIP28607
+// [Category] Improvement
+// [Description] System was hanging for some time if Floppy Media
+// removed while writing in meda in progress.
+// EFI_TPL_CALLBACK priority level rised during media related calls.
+//
+// [Files] DiskIo.c, Info.c, MediaAccess.c, Open.c, Partition.c
+//
+// 10 1/22/10 4:39p Yul
+// Refer to EIP 32983.
+//
+// 9 7/02/09 5:47p Pats
+// Updated to latest coding standard. No code changes.
+//
+// 8 4/13/07 7:07p Pats
+// Edited to conform with coding standards. No code changes.
+//
+// 7 8/16/06 12:03p Markw
+// Fixed UINTN* and UINT32* 64-bit issues.
+//
+// 10 11/03/05 2:16p Srinin
+// Fixed VC7.1 warning msg.
+//
+// 9 8/08/05 3:55p Markw
+// In SetVolumeLabel, after CreateDirectoryEntry, check if valid file
+// handle before removing.
+//
+// 6 6/22/05 4:29p Pats
+// Added function Labcpy() to replace Wcscpy() so that volume labels are
+// copied correctly. Dumps period added by ExtractShortFileName.
+//
+// 4 6/21/05 9:51a Pats
+// Fixed GetVolumeLabel so that it properly handles the case when there is
+// no label on the volume, to pass SCT tests.
+//
+// 3 6/16/05 4:17p Pats
+// Modified to mark both long and short name entries deleted when a file
+// is deleted, instead of just short name entries.
+//
+// 2 4/27/05 5:30p Srinin
+// 'MV' command supported (Move file/Directories in a volume)
+//
+// 1 4/26/05 6:05p Srinin
+//
+//
+//**********************************************************************
+
+//<AMI_FHDR_START>
+//----------------------------------------------------------------------
+//
+// Name: Info.c
+//
+// Description: Simple File System driver information handling functions
+//
+//----------------------------------------------------------------------
+//<AMI_FHDR_END>
+//**********************************************************************
+
+//----------------------------------------------------------------------
+
+#include "FileSystem.h"
+#define EFI_TPL_CALLBACK TPL_CALLBACK
+//----------------------------------------------------------------------
+
+extern EFI_GUID gEfiFileSystemVolumeLabelGuid;
+extern EFI_GUID gEfiFileInfoGuid;
+extern EFI_GUID gEfiFileSystemInfoGuid;
+
+//----------------------------------------------------------------------
+
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: UpdateAccAndWriteTime
+//
+// Description: Updates access and write times in a directory entry
+//
+// Parameters: DIR_ENTRY_LIST *del - pointer to the directory entry list
+// structure
+//
+// Return value: EFI_STATUS - Status of the operation
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+UpdateAccAndWriteTime (
+ DIR_ENTRY_LIST *del
+)
+{
+
+ EFI_TIME EfiTime;
+ EFI_TIME_CAPABILITIES Capabilities;
+ UINT16 DummyTime;
+
+ pRS->GetTime(&EfiTime, &Capabilities);
+ EfiToFatTime (EfiTime, &del->DirectoryEntry.Dir_LstAccDate, &DummyTime);
+ EfiToFatTime (EfiTime, &del->DirectoryEntry.Dir_WrtDate, &del->DirectoryEntry.Dir_WrtTime);
+ return EFI_SUCCESS;
+
+}
+
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: UpdateAccDate
+//
+// Description: Updates the access date in a directory entry
+//
+// Parameters: DIR_ENTRY_LIST *del - pointer to the directory entry list
+// structure
+//
+// Return value: EFI_STATUS - Status of the operation
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+UpdateAccDate(
+ DIR_ENTRY_LIST *del
+)
+{
+
+ EFI_TIME EfiTime;
+ EFI_TIME_CAPABILITIES Capabilities;
+ UINT16 DummyTime;
+
+ pRS->GetTime(&EfiTime, &Capabilities);
+ EfiToFatTime (EfiTime, &del->DirectoryEntry.Dir_LstAccDate, &DummyTime);
+ return EFI_SUCCESS;
+
+}
+
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: UpdateAccDateRequired
+//
+// Description: Indicates when update of the access date of a file is
+// required
+//
+// Parameters: DIR_ENTRY_LIST *del - pointer to the directory entry list
+// structure
+//
+// Return value: BOOLEAN - True if update required
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+BOOLEAN
+UpdateAccDateRequired (
+ DIR_ENTRY_LIST *del
+)
+{
+
+ EFI_TIME EfiTime;
+ EFI_TIME_CAPABILITIES Capabilities;
+ UINT16 DummyTime;
+ UINT16 DummyDate;
+
+ pRS->GetTime(&EfiTime, &Capabilities);
+ EfiToFatTime (EfiTime, &DummyDate, &DummyTime);
+
+ if (DummyDate == del->DirectoryEntry.Dir_LstAccDate) return FALSE;
+
+ del->DirectoryEntry.Dir_LstAccDate = DummyDate;
+ return TRUE;
+
+}
+
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: GetInfoFileHandle
+//
+// Description: Returns file info, system info, or the label of a volume
+// depending on information type specified
+//
+// Parameters: EFI_FILE_PROTOCOL *This - Pointer to this instance of
+// file protocol
+// EFI_GUID *InformationType - Pointer to information type
+// requested
+// UINTN *BufferSize - Pointer to size of buffer
+// VOID *Buffer - Pointer to buffer for returned info
+//
+// Return value: EFI_STATUS - Status of the operation
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+GetInfoFileHandle(
+ IN EFI_FILE_PROTOCOL *This,
+ IN EFI_GUID *InformationType,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+)
+{
+
+ EFI_STATUS Status = EFI_UNSUPPORTED;
+ FILE_HANDLE_INSTANCE *fhi = (FILE_HANDLE_INSTANCE *)This;
+ FILE_HANDLE *Pfh;
+ VOLUME_INFO *vi;
+ EFI_TPL SaveTpl;
+ EFI_TPL NextTpl;
+
+ Status = ValidateCurrentStatus (fhi);
+
+ if (EFI_ERROR(Status)) return Status;
+ Pfh = fhi->pFH;
+ vi = Pfh->VolumeInterface;
+// Compare the GUID
+ NextTpl = FindNextTpl (EFI_TPL_CALLBACK);
+ SaveTpl = pBS->RaiseTPL (NextTpl);
+ if (!guidcmp(InformationType, &gEfiFileInfoGuid))
+ Status = GetfileInfo (fhi, BufferSize, Buffer);
+
+ else {
+ if (!guidcmp(InformationType, &gEfiFileSystemInfoGuid))
+ Status = GetSystemInfo (vi, BufferSize, (EFI_FILE_SYSTEM_INFO *)Buffer);
+
+ else {
+ if (!guidcmp(InformationType, &gEfiFileSystemVolumeLabelGuid))
+ Status = GetVolumeLabel (vi, BufferSize, Buffer);
+
+ else{
+ pBS->RestoreTPL(SaveTpl);
+ return EFI_UNSUPPORTED;
+ }
+ }
+ }
+ pBS->RestoreTPL(SaveTpl);
+ return Status;
+}
+
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: GetfileInfo
+//
+// Description: Returns information on a file
+//
+// Parameters: FILE_HANDLE_INSTANCE *fh1 - Pointer to file handle
+// instance
+// UINTN *BufferSize - Pointer to size of buffer
+// VOID *Buffer - Ponter to buffer for returned data
+//
+// Return value: EFI_STATUS - Status of the operation
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+GetfileInfo (
+ IN FILE_HANDLE_INSTANCE *fhi,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+)
+{
+
+ FILE_HANDLE *Pfh = fhi->pFH;
+ return (GetFileInfoFromFH (Pfh, BufferSize, Buffer));
+
+}
+
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: GetSystemInfo
+//
+// Description: Returns file system info on a volume
+//
+// Parameters: VOLUME_INFO *vi - Pointer to volume info structure
+// UINTN *BufferSize - Pointer to size of buffer
+// VOID *Buffer - Ponter to buffer for returned data
+//
+// Return value: EFI_STATUS - Status of the operation
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+GetSystemInfo (
+ IN VOLUME_INFO *vi,
+ IN OUT UINTN *BufferSize,
+ OUT EFI_FILE_SYSTEM_INFO *Buffer
+)
+{
+
+ UINTN Length = 0;
+ GetVolumeLabel (vi, &Length, Buffer);
+
+ if (*BufferSize < Length + EFI_FIELD_OFFSET(EFI_FILE_SYSTEM_INFO, VolumeLabel)) {
+
+#ifdef Debug_GetSystemInfo
+ EfiDebugPrint(TRACE_ALWAYS,"GetSystemInfo: I/P BufferSize %x O/P BufferSize %x\n", *BufferSize, Length + EFI_FIELD_OFFSET(EFI_FILE_SYSTEM_INFO, VolumeLabel));
+#endif
+
+ *BufferSize = Length + EFI_FIELD_OFFSET(EFI_FILE_SYSTEM_INFO, VolumeLabel);
+ return EFI_BUFFER_TOO_SMALL;
+
+ }
+
+ Buffer->Size = Length + EFI_FIELD_OFFSET(EFI_FILE_SYSTEM_INFO, VolumeLabel);
+ Buffer->ReadOnly = vi->ReadOnly;
+ Buffer->VolumeSize = vi->VolumeSize;
+ Buffer->FreeSpace = Mul64(ReturnFreeSpace(vi), vi->BytesPerCluster);
+ Buffer->BlockSize = vi->BytesPerCluster;
+ GetVolumeLabel (vi, BufferSize, ((UINT8 *)Buffer) + EFI_FIELD_OFFSET(EFI_FILE_SYSTEM_INFO, VolumeLabel));
+ *BufferSize += EFI_FIELD_OFFSET(EFI_FILE_SYSTEM_INFO, VolumeLabel);
+
+#ifdef Debug_GetSystemInfo
+ EfiDebugPrint(TRACE_ALWAYS,"GetSystemInfo: VolumeLabel %s, BufferSize %x\n", Buffer->VolumeLabel, *BufferSize);
+ EfiDebugPrint(TRACE_ALWAYS,"Size %lx, ReadOnly %x, VolumeSize %lx, FreeSpace %lx, BlockSize %x\n", Buffer->Size, Buffer->ReadOnly, Buffer->VolumeSize, Buffer->FreeSpace, Buffer->BlockSize);
+#endif
+
+ return EFI_SUCCESS;
+
+}
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: GetVolumeLabel
+//
+// Description: Returns label of a volume
+//
+// Parameters: VOLUME_INFO *vi - Pointer to volume info structure
+// UINTN *BufferSize - Pointer to size of buffer
+// VOID *Buffer - Ponter to buffer for returned data
+//
+// Return value: EFI_STATUS - Status of the operation
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+GetVolumeLabel (
+ IN VOLUME_INFO *vi,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+)
+{
+
+ EFI_STATUS Status;
+ EFI_STATUS LocalStatus;
+ FILE_HANDLE *fh = vi->RootFH;
+ UINTN Length;
+ DLINK *dlink;
+ DIR_ENTRY_LIST *del=NULL;
+
+ if (vi->ValidVolumeID) {
+ Length = Wcslen (vi->VolumeID) << 1;
+
+#ifdef Debug_GetVolumeLabel
+ EfiDebugPrint(-1,"GetVolumeLabel: VolLabel %s I/P BufferSize %x O/P BufferSize %x\n", vi->VolumeID, *BufferSize, Length + 2);
+#endif
+
+ if (*BufferSize < (Length + 2)) {
+ *BufferSize = Length + 2;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ Wcscpy (Buffer, vi->VolumeID);
+ *BufferSize = Length + 2;
+ return EFI_SUCCESS;
+
+ } else {
+ if (*BufferSize < 2) {
+ Status = EFI_BUFFER_TOO_SMALL; // To pass SCT test.
+
+ } else Status = EFI_NOT_FOUND;
+ }
+
+
+// See if the Linked List is Valid
+ if (!fh->DirList.pHead) ReadAllDirectoryEntries (fh);
+
+ dlink = fh->DirList.pHead;
+ LocalStatus = EFI_UNSUPPORTED;
+
+ if (!dlink) return EFI_UNSUPPORTED;
+
+ for ( ; dlink; ) {
+ del = OUTTER(dlink, DirLink, DIR_ENTRY_LIST);
+
+ if ((del->DirectoryEntry.Dir_Attr & (ATTR_DIRECTORY | ATTR_VOLUME_ID)) == ATTR_VOLUME_ID) {
+ LocalStatus = EFI_SUCCESS;
+ break;
+ }
+
+ dlink = dlink->pNext;
+ }
+
+ if (LocalStatus == EFI_SUCCESS) {
+ Length = Lablen (del->FileNameLFN) << 1;
+
+ if (Length <= 22) {
+ Zeromemory (vi->VolumeID, sizeof (vi->VolumeID));
+ Labcpy (vi->VolumeID, del->FileNameLFN); // Don't put "." in name
+ vi->ValidVolumeID = TRUE;
+ }
+
+ if (*BufferSize < (Length + 2)) {
+
+#ifdef Debug_GetVolumeLabel
+ EfiDebugPrint(-1,"GetVolumeLabel: VolLabel %s I/P BufferSize %x O/P BufferSize %x\n", vi->VolumeID, *BufferSize, Length + 2);
+#endif
+
+ *BufferSize = Length + 2;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ Status = EFI_SUCCESS;
+ Labcpy (Buffer, del->FileNameLFN);
+ *BufferSize = Length >> 1;
+
+#ifdef Debug_GetVolumeLabel
+ EfiDebugPrint(-1,"GetVolumeLabel: VolLabel %s I/P BufferSize %x O/P BufferSize %x\n", Buffer, *BufferSize, Length + 2);
+#endif
+
+ }
+
+// If there is no volume label, we must return a null label of size 2.
+ else if (Status == EFI_NOT_FOUND) {
+ if (*BufferSize >= 2) Zeromemory (Buffer, 2);
+
+ *BufferSize = 2;
+ Status = EFI_SUCCESS;
+
+ } else if (Status == EFI_BUFFER_TOO_SMALL) *BufferSize = 2;
+
+ return Status;
+}
+
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: SetfileInfo
+//
+// Description: Sets the information on a file (updates dir entry)
+//
+// Parameters: FILE_HANDLE_INSTANCE *fh1 - Pointer to file handle
+// instance
+// UINTN *BufferSize - Pointer to size of buffer
+// VOID *Buffer - Ponter to buffer containing new data
+//
+// Return value: EFI_STATUS - Status of the operation
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+SetfileInfo (
+ IN EFI_FILE_PROTOCOL *This,
+ IN FILE_HANDLE_INSTANCE *fhi,
+ IN OUT UINTN BufferSize,
+ OUT VOID *Buffer
+)
+{
+
+ EFI_STATUS Status;
+ EFI_FILE_PROTOCOL *NewHandle;
+ FILE_HANDLE *fh = fhi->pFH;
+ FILE_HANDLE *ParentFh = fh->Parent;
+ DIR_ENTRY_LIST *Del = NULL;
+ FILE_HANDLE_INSTANCE *Newfhi;
+ VOLUME_INFO *vi = fh->VolumeInterface;
+ EFI_FILE_INFO *buffer = Buffer;
+ UINT16 Dummy;
+ UINT32 Length;
+ UINT32 OldClusterCount, NewClusterCount;
+ UINT8 DirErase = DIR_ENTRY_ERASE;
+ CHAR16 *NewFileName;
+ BOOLEAN IsSizeChange = fh->DirectoryEntry.Dir_FileSize != (UINT32)(buffer->FileSize) ? TRUE : FALSE;
+ BOOLEAN IsNameChange = (BOOLEAN)Wcscmpcaseinsensitive(buffer->FileName, fh->FileNameLFN);
+
+#ifdef Debug_SetFileInfo
+ EfiDebugPrint(-1,"SetFileInfo: %S I/P BufferSize %x\n", fh->FileNameLFN, BufferSize);
+ EfiDebugPrint(-1,"FileSize %lx, Physical %lx Attribute %lx\n", buffer->FileSize, buffer->PhysicalSize, buffer->Attribute);
+#endif
+
+ Length = EFI_FIELD_OFFSET(EFI_FILE_INFO,FileName);
+
+ if (BufferSize < Length) return EFI_BAD_BUFFER_SIZE;
+
+// Is it a Directory Entry, attr can't be set
+// For a file, it cannot be set as Directory
+ if ((fh->DirectoryEntry.Dir_Attr & ATTR_DIRECTORY) ^
+ ((UINT8)(buffer->Attribute) & ATTR_DIRECTORY)) return EFI_ACCESS_DENIED;
+
+// For Root Directory no entries can be modified
+ if (fh->ROOTDIR) return EFI_ACCESS_DENIED;
+
+ if (buffer->Attribute & ~EFI_FILE_VALID_ATTR) return EFI_INVALID_PARAMETER;
+
+ if (fh->DirectoryEntry.Dir_Attr & ATTR_DIRECTORY && IsSizeChange) return EFI_ACCESS_DENIED;
+
+ if (vi->ReadOnly) return EFI_WRITE_PROTECTED;
+
+ if ((fh->DirectoryEntry.Dir_Attr & ATTR_READ_ONLY ||
+ fhi->OpenMode == EFI_FILE_MODE_READ) && (IsSizeChange || IsNameChange))
+ return EFI_ACCESS_DENIED;
+
+// Update Attribute
+#ifdef Debug_SetFileInfo
+ EfiDebugPrint(-1, "Updated Attribute %x\n", fh->DirectoryEntry.Dir_Attr);
+#endif
+
+ fh->DirectoryEntry.Dir_Attr = (UINT8) buffer->Attribute;
+
+// Rename File/Directory?
+ if (IsNameChange) {
+
+#ifdef Debug_SetFileInfo
+ EfiDebugPrint(-1,"SetFileInfo: New File Name %S Old FileName %S \n", buffer->FileName, fh->FileNameLFN);
+#endif
+
+// Copy the Input FileName
+ Length = (UINT32) (Wcslen(buffer->FileName) * sizeof (CHAR16))+ 2;
+ Status = fsAllocateMemory (vi, Length, &NewFileName, FALSE);
+ pBS->CopyMem (NewFileName, buffer->FileName, Length);
+
+ if (FindMatchingDirEntry (ParentFh, NewFileName, &Del)) {
+ fsDeAllocateMemory (vi, NewFileName);
+ return EFI_ACCESS_DENIED; // Name already exists, can't rename to it
+ }
+
+ Status = ProcessOpenFileHandle (fhi, &NewFileName, &NewHandle, EFI_FILE_MODE_CREATE | 3, 0);
+ fsDeAllocateMemory (vi, NewFileName);
+
+ if (EFI_ERROR(Status)) return Status;
+
+ Newfhi = (FILE_HANDLE_INSTANCE *) NewHandle;
+
+// Delete the old Entry
+ if (fh->DirCluster || fh->DirOffset) {
+ fh->DirectoryEntry.Dir_Name[0] = DirErase;
+ Status = UpdateDirListFromFHDir(fh);
+ if (EFI_ERROR(Status)) return Status;
+ //Remove directory list from cache when deleted.
+ RemoveAllDirList(fh);
+ }
+
+// Other than FileName, copy all the directory entries.
+ pBS->CopyMem (&(Newfhi->pFH->DirectoryEntry.Dir_Attr), &(fh->DirectoryEntry.Dir_Attr), 32 - 11);
+
+// Transfer Information from New to Old
+ fhi->pFH = Newfhi->pFH;
+
+// Release the NewFileHandle Instance Created.
+ DListDelete (&(vi->OpenFIs), &(Newfhi->ViFILink));
+ fsDeAllocateMemory (vi, Newfhi);
+
+ RemoveAllDirList(fh->Parent); // Dir Listing for OLD File Handle
+ RemoveAllDirList(Newfhi->pFH->Parent); // Dir Listing for New File Handle
+ fh->InstancesCount--;
+ RemoveFH(fh); // Remove old File Handle
+ fh = Newfhi->pFH; // fh points to new FH
+//###DEBUG
+ /*
+ // For Directories, ".." entry should point to the new Parent.
+ if (fh->DirectoryEntry.Dir_Attr & ATTR_DIRECTORY) {
+ // Read 40h Bytes
+ GetTempBuffer (vi, &TempBuffer);
+ GetSectorAddressDir(fh, FIRSTCLUSTER(fh->DirectoryEntry), 0, &Sector, &SectorOffset);
+ Status = FsReadMedia (vi, TempBuffer, Sector, SectorOffset, 0x40, DIRECTORY_REGION);
+ if (EFI_ERROR(Status)) return Status;
+
+ if (TempBuffer [0] == '.' && TempBuffer[0x20] == '.' && TempBuffer[0x21] == '.') {
+ (UINT32) TempBuffer += sizeof (DIR_ENTRY_32);
+ ((DIR_ENTRY_32 *) TempBuffer)->Dir_FstClusLO = fh->Parent->DirectoryEntry.Dir_FstClusLO;
+ ((DIR_ENTRY_32 *) TempBuffer)->Dir_FstClusHI = fh->Parent->DirectoryEntry.Dir_FstClusHI;
+ Status = FsWriteMedia (vi, TempBuffer, Sector, SectorOffset, 0x40, DIRECTORY_REGION);
+ if (EFI_ERROR(Status)) return Status;
+ }
+ ReleaseTempBuffer(vi);
+ }
+ */
+//###DEBUG END
+ fh->DirectoryEntry.Dir_Attr |= ATTR_ARCHIVE;
+
+ }
+
+// Update the Date and Time?
+ if (*(UINT32 *)&(buffer->CreateTime) != 0)
+ EfiToFatTime(buffer->CreateTime, &fh->DirectoryEntry.Dir_CrtDate, &fh->DirectoryEntry.Dir_CrtTime);
+
+ if (*(UINT32 *)&(buffer->LastAccessTime) != 0)
+ EfiToFatTime(buffer->LastAccessTime, &fh->DirectoryEntry.Dir_LstAccDate, &Dummy);
+
+ if (*(UINT32 *)&(buffer->ModificationTime) != 0)
+ EfiToFatTime(buffer->ModificationTime, &fh->DirectoryEntry.Dir_WrtDate, &fh->DirectoryEntry.Dir_WrtTime);
+
+// Newsize smaller than Original size?
+ if (fh->DirectoryEntry.Dir_FileSize > (UINT32)(buffer->FileSize)) {
+
+ Status = CheckFileWrite (fhi, FALSE);
+
+ if (EFI_ERROR(Status)) return Status;
+
+#ifdef Debug_SetFileInfo
+ EfiDebugPrint(-1,"SetFileInfo: File Name %S New Size %x Old Size %x\n", buffer->FileName, buffer->FileSize, fh->DirectoryEntry.Dir_FileSize);
+#endif
+
+ Length = fh->DirectoryEntry.Dir_FileSize - (UINT32)(buffer->FileSize);
+ OldClusterCount = GetClustersRequired(vi, fh->DirectoryEntry.Dir_FileSize);
+ NewClusterCount = GetClustersRequired(vi, (UINT32)(buffer->FileSize));
+
+ if (NewClusterCount < OldClusterCount)
+ ShrinkClusters (vi, FIRSTCLUSTER(fh->DirectoryEntry), NewClusterCount);
+
+ fh->DirectoryEntry.Dir_FileSize = (UINT32)(buffer->FileSize);
+ fh->DirectoryEntry.Dir_Attr |= ATTR_ARCHIVE;
+
+ if (buffer->FileSize == 0 ) {
+ fh->DirectoryEntry.Dir_FstClusLO = 0;
+ fh->DirectoryEntry.Dir_FstClusHI = 0;
+ SetPositionFileHandle (fhi, 0);
+ }
+
+ if (fhi->Position > buffer->FileSize) {
+ SetPositionFileHandle (fhi, 0);
+ }
+ }
+
+// Newsize bigger than Original size?
+ if (fh->DirectoryEntry.Dir_FileSize < buffer->FileSize) {
+
+#ifdef Debug_SetFileInfo
+ EfiDebugPrint(-1,"SetFileInfo: File Name %S New Size %x Old Size %x\n", buffer->FileName, buffer->FileSize, fh->DirectoryEntry.Dir_FileSize);
+#endif
+
+ Status = CheckFileWrite (fhi, FALSE);
+
+ if (EFI_ERROR(Status)) return Status;
+
+// Get the Size in Bytes to be grown
+ Length = (UINT32) (buffer->FileSize) - fh->DirectoryEntry.Dir_FileSize;
+ Status = ExtendFile (fhi, Length);
+
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+// Update New File Size
+ fh->DirectoryEntry.Dir_FileSize = (UINT32)(buffer->FileSize);
+
+ }
+
+
+ Status = UpdateDirListFromFHDir(fh);
+ fhi->pFH->DirEntryChanged = FALSE;
+ return Status;
+
+}
+
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: UpdateDirListFromFHDir
+//
+// Description: Updates directory list from file handle directory entry
+//
+// Parameters: FILE_HANDLE *fh - Pointer to file handle structure
+//
+// Return value: EFI_STATUS - Status of the operation
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S): Updates DIR list from FH Directory Entry and also writes
+// to the disk.
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+UpdateDirListFromFHDir (
+ FILE_HANDLE *fh
+)
+{
+ EFI_STATUS Status;
+ DIR_ENTRY_LIST *Del;
+ UINT32 Sector, SectorOffset, Position;
+ UINT8 TempDirEntry[32];
+ UINT32 TempOffset, TempSector;
+ UINTN i;
+
+#ifdef Debug_CloseFileHandle
+// EfiDebugPrint(-1,"Update Dir Entry: %s, Dir Cluster %x, Dir Offset %x\n", fh->FileNameLFN, fh->DirCluster, fh->DirOffset);
+#endif
+
+ Status = LocateDel (fh->Parent->DirList.pHead, fh->DirCluster, fh->DirOffset, &Del, &Position);
+
+ if (!EFI_ERROR(Status)) {
+ pBS->CopyMem (&(Del->DirectoryEntry), &fh->DirectoryEntry, sizeof(DIR_ENTRY_32));
+ Del->Cluster = fh->DirCluster;
+ Del->Offset = fh->DirOffset;
+ Del->SlotNumber = fh->SlotNumber;
+ }
+
+ GetSectorAddressDir(fh->Parent, fh->DirCluster, fh->DirOffset, &Sector, &SectorOffset);
+
+ if (fh->DirectoryEntry.Dir_Name[0] == DIR_ENTRY_ERASE) {
+ TempSector = Sector;
+ TempOffset = SectorOffset;
+
+ if (TempOffset == 0) {
+ TempSector--;
+ TempOffset = fh->VolumeInterface->VolumeBPB.BPB_BytePerSec;
+ }
+
+ for (i = 0; i < MAX_LFN_SLOTS; i++) {
+ TempOffset -= sizeof(DIR_ENTRY_LFN);
+ Status = FsReadMedia (fh->VolumeInterface, (void *)&TempDirEntry, TempSector, TempOffset, sizeof(DIR_ENTRY_32), DIRECTORY_REGION);
+
+ if (TempDirEntry[11] == (ATTR_LONG_NAME)) {
+ TempDirEntry[0] = DIR_ENTRY_ERASE; // Mark long entry erased.
+ Status = FsWriteMedia (fh->VolumeInterface, (void *)&TempDirEntry, TempSector, TempOffset, sizeof(DIR_ENTRY_32), DIRECTORY_REGION);
+
+ } else break;
+
+ if (TempOffset == 0) {
+ TempSector--;
+ TempOffset = fh->VolumeInterface->VolumeBPB.BPB_BytePerSec;
+ }
+ }
+ }
+
+ return FsWriteMedia (fh->VolumeInterface, (void *)&fh->DirectoryEntry, Sector,SectorOffset, sizeof(DIR_ENTRY_32), DIRECTORY_REGION);
+}
+
+
+//**********************************************************************
+//<AMI_PHDR_START>
+//
+// Procedure: LocateDel
+//
+// Description:
+//
+// Input:
+//
+// Output:
+//
+//
+// Modified:
+//
+// Referrals:
+//
+// Notes:
+//
+//<AMI_PHDR_END>
+//**********************************************************************
+EFI_STATUS
+LocateDel (
+ DLINK *dlink,
+ UINT32 Cluster,
+ UINT32 ClusterOffset,
+ DIR_ENTRY_LIST **Del,
+ UINT32 *Position
+)
+{
+
+ EFI_STATUS Status = EFI_NOT_FOUND;
+// DLINK *dlink = fh->Parent->DirList.pHead;
+ DIR_ENTRY_LIST *del;
+
+ *Del = NULL;
+ *Position = 0;
+
+// Copy this info to DIR_LIST if Present
+ for ( ; dlink; dlink = dlink->pNext, ++*Position) {
+ del = OUTTER(dlink, DirLink, DIR_ENTRY_LIST);
+
+// if (del->Cluster == fh->DirCluster && del->Offset == fh->DirOffset) {
+ if (del->Cluster == Cluster && del->Offset == ClusterOffset) {
+ *Del = del;
+ Status = EFI_SUCCESS;
+ break;
+ }
+ }
+
+ return Status;
+
+}
+
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: SetSystemInfo
+//
+// Description: Sets the information in a volume label
+//
+// Parameters: VOLUME_INFO *vi - Pointer to volume info structure
+// UINTN *BufferSize - Pointer to size of buffer
+// VOID *Buffer - Ponter to buffer containing new data
+//
+// Return value: EFI_STATUS - Status of the operation
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+SetSystemInfo (
+ IN VOLUME_INFO *vi,
+ IN UINTN BufferSize,
+ OUT EFI_FILE_SYSTEM_INFO *Buffer
+)
+{
+
+ EFI_STATUS Status;
+ EFI_FILE_SYSTEM_INFO *buffer = Buffer;
+ UINT32 Length = (UINT32)Wcslen(Buffer->VolumeLabel) + 2;
+
+#ifdef Debug_SetSystemInfo
+ Length += EFI_FIELD_OFFSET(EFI_FILE_SYSTEM_INFO, VolumeLabel);
+
+ EfiDebugPrint(-1,"SetSystemInfo: VolumeLabel %s, BufferSize %x BufferRequired %x\n", Buffer->VolumeLabel, BufferSize, Length);
+ EfiDebugPrint(-1,"Size %lx, ReadOnly %x, VolumeSize %lx, FreeSpace %lx, BlockSize %x\n", Buffer->Size, Buffer->ReadOnly, Buffer->VolumeSize, Buffer->FreeSpace, Buffer->BlockSize);
+#endif
+
+ if (BufferSize < (EFI_FIELD_OFFSET(EFI_FILE_SYSTEM_INFO, VolumeLabel) + 2) || buffer->Size > BufferSize) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+// if (BufferSize < Length) return EFI_BAD_BUFFER_SIZE;
+
+// Only Volume Label can be set
+ Status = SetVolumeLabel (vi, Length, &Buffer->VolumeLabel);
+ return Status;
+
+}
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: SetVolumeLabel
+//
+// Description: Updates the label on a volume
+//
+// Parameters: VOLUME_INFO *vi - Pointer to volume info structure
+// UINTN *BufferSize - Pointer to size of buffer
+// VOID *Buffer - Ponter to buffer containing new label
+//
+// Return value: EFI_STATUS - Status of the operation
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+SetVolumeLabel (
+ IN VOLUME_INFO *vi,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+)
+{
+
+ EFI_STATUS Status;
+ UINT8 VolumeLabel[12];
+ UINT32 Sector, SectorOffset, Length;
+ FILE_HANDLE *fh = vi->RootFH, *fh1;
+ DLINK *dlink;
+ DIR_ENTRY_LIST *del;
+ UINT8 i;
+
+// Convert the Unicode string to English
+// Shell Doen't support remove of LABEL right now.
+// if (!BufferSize ) return EFI_BAD_BUFFER_SIZE;
+
+ Length = (UINT32)Wcslen(Buffer) * sizeof (CHAR16) + 2;
+
+#ifdef Debug_SetVolumeLabel
+ EfiDebugPrint(-1,"SetVolumeLabel: VolumeLabel %s BufferSize %x Length %x\n", Buffer, BufferSize, Length);
+#endif
+
+ Length = EFI_FIELD_OFFSET(EFI_FILE_SYSTEM_VOLUME_LABEL, VolumeLabel);
+
+ if (BufferSize < Length + 2) return EFI_BAD_BUFFER_SIZE;
+
+ MemSet(VolumeLabel, 11, 0x20); // Fill short name with spaces
+ vi->UnicodeCollationInterface->StrToFat (vi->UnicodeCollationInterface,
+ Buffer,
+ 11,
+ VolumeLabel);
+ VolumeLabel[11] = 0;
+
+// Update volume Label in vi->Volume
+ if (Strlen(VolumeLabel) > 11) return EFI_BAD_BUFFER_SIZE;
+
+ vi->UnicodeCollationInterface->FatToStr(vi->UnicodeCollationInterface, 11, VolumeLabel, vi->VolumeID);
+
+ for (i=0; i<11; i++) {
+ if (vi->VolumeID[i] == 0x20) break;
+ }
+
+ vi->VolumeID[i] = 0;
+ vi->ValidVolumeID = TRUE;
+
+// Check if Volume ID Directory Entry is present. If so Modify it.
+ if (!fh->DirList.pHead) ReadAllDirectoryEntries (fh);
+
+ dlink = fh->DirList.pHead;
+ Status = EFI_NOT_FOUND;
+
+ for ( ; dlink; dlink = dlink->pNext) {
+ del = OUTTER(dlink, DirLink, DIR_ENTRY_LIST);
+
+ if ((del->DirectoryEntry.Dir_Attr & (ATTR_DIRECTORY | ATTR_VOLUME_ID)) == ATTR_VOLUME_ID) {
+
+ pBS->CopyMem(&del->DirectoryEntry.Dir_Name, VolumeLabel, 11);
+
+// Update LastAcc date and Wrtdate/Time and the Attribute
+ UpdateAccAndWriteTime (del);
+ del->DirectoryEntry.Dir_Attr |= ATTR_ARCHIVE;
+
+ if (VolumeLabel[0] == 0x20) del->DirectoryEntry.Dir_Name[0] = 0xE5;
+
+ GetSectorAddressDir (fh, del->Cluster, del->Offset, &Sector, &SectorOffset);
+ Status = FsWriteMedia (vi, (void *)&del->DirectoryEntry, Sector, SectorOffset, sizeof(DIR_ENTRY_32), DIRECTORY_REGION);
+
+ if (EFI_ERROR(Status)) return Status;
+
+ if (VolumeLabel[0] == 0x20) RemoveDirList(fh, del);
+
+ break;
+ }
+ }
+
+
+ if (Status == EFI_NOT_FOUND && VolumeLabel[0] != 0x20) {
+ fh1 = 0;
+ CreateDirectoryEntry (fh, vi->VolumeID, &fh1, ATTR_VOLUME_ID |ATTR_ARCHIVE);
+
+ if (fh1) RemoveFH(fh1); // FILE_HANDLE Created is not needed.
+ }
+
+ return EFI_SUCCESS;
+
+}
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: GetFileInfoFromFH
+//
+// Description: Returns information on a file from it's handle
+//
+// Parameters: FILE_HANDLE_INSTANCE *fh1 - Pointer to file handle
+// instance
+// UINTN *BufferSize - Pointer to size of buffer
+// VOID *Buffer - Ponter to buffer for returned data
+//
+// Return value: EFI_STATUS - Status of the operation
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+GetFileInfoFromFH (
+ FILE_HANDLE *Pfh,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+)
+{
+ VOLUME_INFO *vi = Pfh->VolumeInterface;
+ EFI_FILE_INFO *buffer = Buffer;
+ UINTN Length;
+
+ Length = (UINT32)Wcslen(Pfh->FileNameLFN) * sizeof (CHAR16) + 2;
+ Length += EFI_FIELD_OFFSET(EFI_FILE_INFO,FileName);
+
+// Length += sizeof(EFI_FILE_INFO);
+ if (*BufferSize < Length) {
+#ifdef Debug_GetFileInfo_1
+ EfiDebugPrint(-1,"GetFileInfo: %S BufferSize %x LengthRequired %x Status: EFI_BUFFER_TOO_SMALL\n", Pfh->FileNameLFN, *BufferSize, Length + 2);
+#endif
+ *BufferSize = Length;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *BufferSize = Length;
+ buffer->Size = Length;
+ buffer->FileSize = Pfh->DirectoryEntry.Dir_FileSize;
+ buffer->PhysicalSize = buffer->FileSize;
+
+ Length = (Pfh->DirectoryEntry.Dir_FileSize % vi->BytesPerCluster);
+
+ if (Length)
+ buffer->PhysicalSize += vi->BytesPerCluster - (Pfh->DirectoryEntry.Dir_FileSize % vi->BytesPerCluster);
+
+ FatToEfiTime (&(buffer->CreateTime), Pfh->DirectoryEntry.Dir_CrtDate, Pfh->DirectoryEntry.Dir_CrtTime);
+ FatToEfiTime (&(buffer->LastAccessTime), Pfh->DirectoryEntry.Dir_LstAccDate, 0);
+ FatToEfiTime (&(buffer->ModificationTime), Pfh->DirectoryEntry.Dir_WrtDate, Pfh->DirectoryEntry.Dir_WrtTime);
+ buffer->Attribute = Pfh->DirectoryEntry.Dir_Attr & EFI_FILE_VALID_ATTR;
+ Wcscpy (buffer->FileName, Pfh->FileNameLFN);
+#ifdef Debug_GetFileInfo_0
+ EfiDebugPrint(-1,"GetFileInfo: %S File Size %lx \n", Pfh->FileNameLFN, buffer->FileSize);
+#endif
+ return EFI_SUCCESS;
+
+}
+
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: FatToEfiTime
+//
+// Description: Converts time/date in FAT format to EFI format
+//
+// Parameters: EFI_TIME *EfiTime - Ponter to returned time in EFI format
+// UINT16 Date - Date in FAT format
+// UINT16 Time - Time in FAT format
+//
+// Return value: None
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+VOID
+FatToEfiTime (
+ EFI_TIME *EfiTime,
+ UINT16 Date,
+ UINT16 Time
+)
+{
+
+ EfiTime->Year = (UINT16) (((FAT_DATE*) &Date)->Year + 1980);
+ EfiTime->Month = (UINT8 ) ((FAT_DATE*) &Date)->Month;
+ EfiTime->Day = (UINT8) ((FAT_DATE*) &Date)->Day;
+ EfiTime->Hour = (UINT8) ((FAT_TIME*) &Time)->Hour;
+ EfiTime->Minute = (UINT8) ((FAT_TIME*) &Time)->Minute;
+ EfiTime->Second = (UINT8) ((FAT_TIME*) &Time)->DoubleSecond;
+ EfiTime->Nanosecond = 0;
+ EfiTime->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
+ EfiTime->Pad1 = 0;
+ EfiTime->Daylight = 0;
+ EfiTime->Pad2 = 0;
+
+}
+
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: EfiToFatTime
+//
+// Description: Converts date/time in EFI format to FAT format
+//
+// Parameters: EFI_TIME EfiTime - Time in EFI format
+// UINT16 *Date - Pointer to returned date in FAT format
+// UINT16 *Time - Pointer to returned time in FAT format
+//
+// Return value: None
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+VOID
+EfiToFatTime (
+ EFI_TIME EfiTime,
+ UINT16 *Date,
+ UINT16 *Time
+)
+{
+
+ ((FAT_DATE*)Date)->Year = EfiTime.Year - 1980;
+ ((FAT_DATE*)Date)->Month = EfiTime.Month;
+ ((FAT_DATE*)Date)->Day = EfiTime.Day;
+ ((FAT_TIME*)Time)->Hour = EfiTime.Hour;
+ ((FAT_TIME*)Time)->Minute = EfiTime.Minute;
+ ((FAT_TIME*)Time)->DoubleSecond = EfiTime.Second;
+
+}
+
+// Special string copy for volume labels.
+// Skips the "." that ExtractShortFileName puts in. Replaces Wcscpy().
+VOID Labcpy(
+ CHAR16 *string1,
+ CHAR16 * string2
+)
+{
+ while (*string2) {
+ if ((CHAR8)*string2 != '.') {
+ *string1++ = *string2++;
+
+ } else *string2++;
+ }
+}
+
+// Special string length counter for volume labels.
+// Skips the "." that ExtractShortFileName puts in. Replaces Wcslen().
+UINTN Lablen(
+ CHAR16 *string
+)
+{
+ UINTN length=0;
+
+ while (*string++) {
+ if ((CHAR8)*string != '.') length++;
+ }
+
+ return length;
+}
+
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+//
+// Procedure: FindNextTpl
+//
+// Description: Returns the higher of a desired TPL level or the current
+// next higher TPL level
+//
+// Parameters: EFI_TPL TplLevel -- desired level to raise to
+//
+// Return Value: EFI_TPL
+//
+// Modified:
+//
+// Referral(s):
+//
+// NOTE(S):
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_TPL
+FindNextTpl (
+ EFI_TPL TplLevel
+ )
+{
+ EFI_TPL NextTpl;
+
+ NextTpl = pBS->RaiseTPL (TPL_HIGH_LEVEL);
+ pBS->RestoreTPL(NextTpl);\
+ if(NextTpl<=TplLevel) NextTpl = TplLevel;
+ return NextTpl;
+}
+
+//**********************************************************************
+//**********************************************************************
+//** **
+//** (C)Copyright 1985-2011, American Megatrends, Inc. **
+//** **
+//** All Rights Reserved. **
+//** **
+//** 5555 Oakbrook Pkwy, Suite 200, Norcross, GA 30093 **
+//** **
+//** Phone: (770)-246-8600 **
+//** **
+//**********************************************************************
+//**********************************************************************