/*++ @file Support OS native directory access. Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "WinHost.h" #define WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE_SIGNATURE SIGNATURE_32 ('N', 'T', 'f', 's') typedef struct { UINTN Signature; EMU_IO_THUNK_PROTOCOL *Thunk; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL SimpleFileSystem; CHAR16 *FilePath; CHAR16 *VolumeLabel; } WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE; #define WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE_DATA_FROM_THIS(a) \ CR (a, \ WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE, \ SimpleFileSystem, \ WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE_SIGNATURE \ ) #define WIN_NT_EFI_FILE_PRIVATE_SIGNATURE SIGNATURE_32 ('l', 'o', 'f', 's') typedef struct { UINTN Signature; EMU_IO_THUNK_PROTOCOL *Thunk; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem; EFI_FILE_PROTOCOL EfiFile; HANDLE LHandle; HANDLE DirHandle; BOOLEAN IsRootDirectory; BOOLEAN IsDirectoryPath; BOOLEAN IsOpenedByRead; CHAR16 *FilePath; WCHAR *FileName; BOOLEAN IsValidFindBuf; WIN32_FIND_DATA FindBuf; } WIN_NT_EFI_FILE_PRIVATE; #define WIN_NT_EFI_FILE_PRIVATE_DATA_FROM_THIS(a) \ CR (a, \ WIN_NT_EFI_FILE_PRIVATE, \ EfiFile, \ WIN_NT_EFI_FILE_PRIVATE_SIGNATURE \ ) extern EFI_FILE_PROTOCOL gWinNtFileProtocol; extern EFI_SIMPLE_FILE_SYSTEM_PROTOCOL gWinNtFileSystemProtocol; EFI_STATUS WinNtFileGetInfo ( IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN OUT UINTN *BufferSize, OUT VOID *Buffer ); EFI_STATUS WinNtFileSetInfo ( IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN UINTN BufferSize, IN VOID *Buffer ); CHAR16 * EfiStrChr ( IN CHAR16 *Str, IN CHAR16 Chr ) /*++ Routine Description: Locate the first occurance of a character in a string. Arguments: Str - Pointer to NULL terminated unicode string. Chr - Character to locate. Returns: If Str is NULL, then NULL is returned. If Chr is not contained in Str, then NULL is returned. If Chr is contained in Str, then a pointer to the first occurance of Chr in Str is returned. --*/ { if (Str == NULL) { return Str; } while (*Str != '\0' && *Str != Chr) { ++Str; } return (*Str == Chr) ? Str : NULL; } BOOLEAN IsZero ( IN VOID *Buffer, IN UINTN Length ) { if ((Buffer == NULL) || (Length == 0)) { return FALSE; } if (*(UINT8 *)Buffer != 0) { return FALSE; } if (Length > 1) { if (!CompareMem (Buffer, (UINT8 *)Buffer + 1, Length - 1)) { return FALSE; } } return TRUE; } VOID CutPrefix ( IN CHAR16 *Str, IN UINTN Count ) { CHAR16 *Pointer; if (StrLen (Str) < Count) { ASSERT (0); } if (Count != 0) { for (Pointer = Str; *(Pointer + Count); Pointer++) { *Pointer = *(Pointer + Count); } *Pointer = *(Pointer + Count); } } /** Open the root directory on a volume. @param This Protocol instance pointer. @param Root Returns an Open file handle for the root directory @retval EFI_SUCCESS The device was opened. @retval EFI_UNSUPPORTED This volume does not support the file system. @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_ACCESS_DENIED The service denied access to the file. @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources. **/ EFI_STATUS WinNtOpenVolume ( IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, OUT EFI_FILE_PROTOCOL **Root ) { EFI_STATUS Status; WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE *Private; WIN_NT_EFI_FILE_PRIVATE *PrivateFile; CHAR16 *TempFileName; UINTN Size; if ((This == NULL) || (Root == NULL)) { return EFI_INVALID_PARAMETER; } Private = WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE_DATA_FROM_THIS (This); PrivateFile = AllocatePool (sizeof (WIN_NT_EFI_FILE_PRIVATE)); if (PrivateFile == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } PrivateFile->FileName = AllocatePool (StrSize (Private->FilePath)); if (PrivateFile->FileName == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } PrivateFile->FilePath = AllocatePool (StrSize (Private->FilePath)); if (PrivateFile->FilePath == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } StrCpyS ( PrivateFile->FilePath, StrSize (Private->FilePath) / sizeof (CHAR16), Private->FilePath ); StrCpyS ( PrivateFile->FileName, StrSize (Private->FilePath) / sizeof (CHAR16), PrivateFile->FilePath ); PrivateFile->Signature = WIN_NT_EFI_FILE_PRIVATE_SIGNATURE; PrivateFile->Thunk = Private->Thunk; PrivateFile->SimpleFileSystem = This; PrivateFile->IsRootDirectory = TRUE; PrivateFile->IsDirectoryPath = TRUE; PrivateFile->IsOpenedByRead = TRUE; CopyMem (&PrivateFile->EfiFile, &gWinNtFileProtocol, sizeof (gWinNtFileProtocol)); PrivateFile->IsValidFindBuf = FALSE; // // Set DirHandle // PrivateFile->DirHandle = CreateFile ( PrivateFile->FilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); if (PrivateFile->DirHandle == INVALID_HANDLE_VALUE) { Status = EFI_NOT_FOUND; goto Done; } // // Find the first file under it // Size = StrSize (PrivateFile->FilePath); Size += StrSize (L"\\*"); TempFileName = AllocatePool (Size); if (TempFileName == NULL) { goto Done; } StrCpyS (TempFileName, Size / sizeof (CHAR16), PrivateFile->FilePath); StrCatS (TempFileName, Size / sizeof (CHAR16), L"\\*"); PrivateFile->LHandle = FindFirstFile (TempFileName, &PrivateFile->FindBuf); FreePool (TempFileName); if (PrivateFile->LHandle == INVALID_HANDLE_VALUE) { PrivateFile->IsValidFindBuf = FALSE; } else { PrivateFile->IsValidFindBuf = TRUE; } *Root = &PrivateFile->EfiFile; Status = EFI_SUCCESS; Done: if (EFI_ERROR (Status)) { if (PrivateFile) { if (PrivateFile->FileName) { FreePool (PrivateFile->FileName); } if (PrivateFile->FilePath) { FreePool (PrivateFile->FilePath); } FreePool (PrivateFile); } } return Status; } /** Count the number of Leading Dot in FileNameToken. @param FileNameToken A string representing a token in the path name. @return UINTN The number of leading dot in the name. **/ UINTN CountLeadingDots ( IN CONST CHAR16 *FileNameToken ) { UINTN Num; Num = 0; while (*FileNameToken == L'.') { Num++; FileNameToken++; } return Num; } BOOLEAN IsFileNameTokenValid ( IN CONST CHAR16 *FileNameToken ) { UINTN Num; if (StrStr (FileNameToken, L"/") != NULL) { // // No L'/' in file name. // return FALSE; } else { // // If Token has all dot, the number should not exceed 2 // Num = CountLeadingDots (FileNameToken); if (Num == StrLen (FileNameToken)) { // // If the FileNameToken only contains a number of L'.'. // if (Num > 2) { return FALSE; } } } return TRUE; } /** Return the first string token found in the indirect pointer a String named by FileName. On input, FileName is a indirect pointer pointing to a String. On output, FileName is a updated to point to the next character after the first found L"\" or NULL if there is no L"\" found. @param FileName A indirect pointer pointing to a FileName. @return Token The first string token found before a L"\". **/ CHAR16 * GetNextFileNameToken ( IN OUT CONST CHAR16 **FileName ) { CHAR16 *SlashPos; CHAR16 *Token; UINTN Offset; ASSERT (**FileName != L'\\'); ASSERT (**FileName != L'\0'); SlashPos = StrStr (*FileName, L"\\"); if (SlashPos == NULL) { Token = AllocateCopyPool (StrSize (*FileName), *FileName); *FileName = NULL; } else { Offset = SlashPos - *FileName; Token = AllocateZeroPool ((Offset + 1) * sizeof (CHAR16)); StrnCpyS (Token, Offset + 1, *FileName, Offset); // // Point *FileName to the next character after L'\'. // *FileName = *FileName + Offset + 1; // // If *FileName is an empty string, then set *FileName to NULL // if (**FileName == L'\0') { *FileName = NULL; } } return Token; } /** Check if a FileName contains only Valid Characters. If FileName contains only a single L'\', return TRUE. If FileName contains two adjacent L'\', return FALSE. If FileName conatins L'/' , return FALSE. If FileName contains more than two dots separated with other FileName characters by L'\', return FALSE. For example, L'.\...\filename.txt' is invalid path name. But L'..TwoDots\filename.txt' is valid path name. @param FileName The File Name String to check. @return TRUE FileName only contains valid characters. @return FALSE FileName contains at least one invalid character. **/ BOOLEAN IsFileNameValid ( IN CONST CHAR16 *FileName ) { CHAR16 *Token; BOOLEAN Valid; // // If FileName is just L'\', then it is a valid pathname. // if (StrCmp (FileName, L"\\") == 0) { return TRUE; } // // We don't support two or more adjacent L'\'. // if (StrStr (FileName, L"\\\\") != NULL) { return FALSE; } // // Is FileName has a leading L"\", skip to next character. // if (FileName[0] == L'\\') { FileName++; } do { Token = GetNextFileNameToken (&FileName); Valid = IsFileNameTokenValid (Token); FreePool (Token); if (!Valid) { return FALSE; } } while (FileName != NULL); return TRUE; } /** Opens a new file relative to the source file's location. @param This The protocol instance pointer. @param NewHandle Returns File Handle for FileName. @param FileName Null terminated string. "\", ".", and ".." are supported. @param OpenMode Open mode for file. @param Attributes Only used for EFI_FILE_MODE_CREATE. @retval EFI_SUCCESS The device was opened. @retval EFI_NOT_FOUND The specified file could not be found on the device. @retval EFI_NO_MEDIA The device has no media. @retval EFI_MEDIA_CHANGED The media has changed. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_ACCESS_DENIED The service denied access to the file. @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources. @retval EFI_VOLUME_FULL The volume is full. **/ EFI_STATUS WinNtFileOpen ( IN EFI_FILE_PROTOCOL *This, OUT EFI_FILE_PROTOCOL **NewHandle, IN CHAR16 *FileName, IN UINT64 OpenMode, IN UINT64 Attributes ) { WIN_NT_EFI_FILE_PRIVATE *PrivateFile; WIN_NT_EFI_FILE_PRIVATE *NewPrivateFile; WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE *PrivateRoot; EFI_STATUS Status; CHAR16 *RealFileName; CHAR16 *TempFileName; CHAR16 *ParseFileName; CHAR16 *GuardPointer; CHAR16 TempChar; DWORD LastError; UINTN Count; BOOLEAN LoopFinish; UINTN InfoSize; EFI_FILE_INFO *Info; UINTN Size; // // Init local variables // PrivateFile = WIN_NT_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); PrivateRoot = WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE_DATA_FROM_THIS (PrivateFile->SimpleFileSystem); NewPrivateFile = NULL; // // Allocate buffer for FileName as the passed in FileName may be read only // TempFileName = AllocatePool (StrSize (FileName)); if (TempFileName == NULL) { return EFI_OUT_OF_RESOURCES; } StrCpyS (TempFileName, StrSize (FileName) / sizeof (CHAR16), FileName); FileName = TempFileName; if (FileName[StrLen (FileName) - 1] == L'\\') { FileName[StrLen (FileName) - 1] = 0; } // // If file name does not equal to "." or ".." and not trailed with "\..", // then we trim the leading/trailing blanks and trailing dots // if ((StrCmp (FileName, L".") != 0) && (StrCmp (FileName, L"..") != 0) && ((StrLen (FileName) >= 3) ? (StrCmp (&FileName[StrLen (FileName) - 3], L"\\..") != 0) : TRUE)) { // // Trim leading blanks // Count = 0; for (TempFileName = FileName; *TempFileName != 0 && *TempFileName == L' '; TempFileName++) { Count++; } CutPrefix (FileName, Count); // // Trim trailing blanks // for (TempFileName = FileName + StrLen (FileName) - 1; TempFileName >= FileName && (*TempFileName == L' '); TempFileName--) { } *(TempFileName + 1) = 0; } // // Attempt to open the file // NewPrivateFile = AllocatePool (sizeof (WIN_NT_EFI_FILE_PRIVATE)); if (NewPrivateFile == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } CopyMem (NewPrivateFile, PrivateFile, sizeof (WIN_NT_EFI_FILE_PRIVATE)); NewPrivateFile->FilePath = AllocatePool (StrSize (PrivateFile->FileName)); if (NewPrivateFile->FilePath == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } if (PrivateFile->IsDirectoryPath) { StrCpyS ( NewPrivateFile->FilePath, StrSize (PrivateFile->FileName) / sizeof (CHAR16), PrivateFile->FileName ); } else { StrCpyS ( NewPrivateFile->FilePath, StrSize (PrivateFile->FileName) / sizeof (CHAR16), PrivateFile->FilePath ); } Size = StrSize (NewPrivateFile->FilePath); Size += StrSize (L"\\"); Size += StrSize (FileName); NewPrivateFile->FileName = AllocatePool (Size); if (NewPrivateFile->FileName == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } if (*FileName == L'\\') { StrCpyS (NewPrivateFile->FileName, Size / sizeof (CHAR16), PrivateRoot->FilePath); StrCatS (NewPrivateFile->FileName, Size / sizeof (CHAR16), L"\\"); StrCatS (NewPrivateFile->FileName, Size / sizeof (CHAR16), FileName + 1); } else { StrCpyS (NewPrivateFile->FileName, Size / sizeof (CHAR16), NewPrivateFile->FilePath); if (StrCmp (FileName, L"") != 0) { // // In case the filename becomes empty, especially after trimming dots and blanks // StrCatS (NewPrivateFile->FileName, Size / sizeof (CHAR16), L"\\"); StrCatS (NewPrivateFile->FileName, Size / sizeof (CHAR16), FileName); } } if (!IsFileNameValid (NewPrivateFile->FileName)) { Status = EFI_NOT_FOUND; goto Done; } // // Get rid of . and .., except leading . or .. // // // GuardPointer protect simplefilesystem root path not be destroyed // GuardPointer = NewPrivateFile->FileName + StrLen (PrivateRoot->FilePath); LoopFinish = FALSE; while (!LoopFinish) { LoopFinish = TRUE; for (ParseFileName = GuardPointer; *ParseFileName; ParseFileName++) { if ((*ParseFileName == L'.') && ((*(ParseFileName + 1) == 0) || (*(ParseFileName + 1) == L'\\')) && (*(ParseFileName - 1) == L'\\') ) { // // cut \. // CutPrefix (ParseFileName - 1, 2); LoopFinish = FALSE; break; } if ((*ParseFileName == L'.') && (*(ParseFileName + 1) == L'.') && ((*(ParseFileName + 2) == 0) || (*(ParseFileName + 2) == L'\\')) && (*(ParseFileName - 1) == L'\\') ) { ParseFileName--; Count = 3; while (ParseFileName != GuardPointer) { ParseFileName--; Count++; if (*ParseFileName == L'\\') { break; } } // // cut \.. and its left directory // CutPrefix (ParseFileName, Count); LoopFinish = FALSE; break; } } } RealFileName = NewPrivateFile->FileName; while (EfiStrChr (RealFileName, L'\\') != NULL) { RealFileName = EfiStrChr (RealFileName, L'\\') + 1; } TempChar = 0; if (RealFileName != NewPrivateFile->FileName) { TempChar = *(RealFileName - 1); *(RealFileName - 1) = 0; } FreePool (NewPrivateFile->FilePath); NewPrivateFile->FilePath = NULL; NewPrivateFile->FilePath = AllocatePool (StrSize (NewPrivateFile->FileName)); if (NewPrivateFile->FilePath == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } StrCpyS ( NewPrivateFile->FilePath, StrSize (NewPrivateFile->FileName) / sizeof (CHAR16), NewPrivateFile->FileName ); if (TempChar != 0) { *(RealFileName - 1) = TempChar; } NewPrivateFile->IsRootDirectory = FALSE; // // Test whether file or directory // if (OpenMode & EFI_FILE_MODE_CREATE) { if (Attributes & EFI_FILE_DIRECTORY) { NewPrivateFile->IsDirectoryPath = TRUE; } else { NewPrivateFile->IsDirectoryPath = FALSE; } } else { NewPrivateFile->LHandle = CreateFile ( NewPrivateFile->FileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (NewPrivateFile->LHandle != INVALID_HANDLE_VALUE) { NewPrivateFile->IsDirectoryPath = FALSE; CloseHandle (NewPrivateFile->LHandle); } else { NewPrivateFile->IsDirectoryPath = TRUE; } NewPrivateFile->LHandle = INVALID_HANDLE_VALUE; } if (OpenMode & EFI_FILE_MODE_WRITE) { NewPrivateFile->IsOpenedByRead = FALSE; } else { NewPrivateFile->IsOpenedByRead = TRUE; } Status = EFI_SUCCESS; // // deal with directory // if (NewPrivateFile->IsDirectoryPath) { Size = StrSize (NewPrivateFile->FileName); Size += StrSize (L"\\*"); TempFileName = AllocatePool (Size); if (TempFileName == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } StrCpyS (TempFileName, Size / sizeof (CHAR16), NewPrivateFile->FileName); if ((OpenMode & EFI_FILE_MODE_CREATE)) { // // Create a directory // if (!CreateDirectory (TempFileName, NULL)) { LastError = GetLastError (); if (LastError != ERROR_ALREADY_EXISTS) { FreePool (TempFileName); Status = EFI_ACCESS_DENIED; goto Done; } } } NewPrivateFile->DirHandle = CreateFile ( TempFileName, NewPrivateFile->IsOpenedByRead ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE), FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); if (NewPrivateFile->DirHandle == INVALID_HANDLE_VALUE) { NewPrivateFile->DirHandle = CreateFile ( TempFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); if (NewPrivateFile->DirHandle != INVALID_HANDLE_VALUE) { CloseHandle (NewPrivateFile->DirHandle); NewPrivateFile->DirHandle = INVALID_HANDLE_VALUE; Status = EFI_ACCESS_DENIED; } else { Status = EFI_NOT_FOUND; } FreePool (TempFileName); goto Done; } // // Find the first file under it // StrCatS (TempFileName, Size / sizeof (CHAR16), L"\\*"); NewPrivateFile->LHandle = FindFirstFile (TempFileName, &NewPrivateFile->FindBuf); FreePool (TempFileName); if (NewPrivateFile->LHandle == INVALID_HANDLE_VALUE) { NewPrivateFile->IsValidFindBuf = FALSE; } else { NewPrivateFile->IsValidFindBuf = TRUE; } } else { // // deal with file // if (!NewPrivateFile->IsOpenedByRead) { NewPrivateFile->LHandle = CreateFile ( NewPrivateFile->FileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, (OpenMode & EFI_FILE_MODE_CREATE) ? OPEN_ALWAYS : OPEN_EXISTING, 0, NULL ); if (NewPrivateFile->LHandle == INVALID_HANDLE_VALUE) { NewPrivateFile->LHandle = CreateFile ( NewPrivateFile->FileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (NewPrivateFile->LHandle == INVALID_HANDLE_VALUE) { Status = EFI_NOT_FOUND; } else { Status = EFI_ACCESS_DENIED; CloseHandle (NewPrivateFile->LHandle); NewPrivateFile->LHandle = INVALID_HANDLE_VALUE; } } } else { NewPrivateFile->LHandle = CreateFile ( NewPrivateFile->FileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (NewPrivateFile->LHandle == INVALID_HANDLE_VALUE) { Status = EFI_NOT_FOUND; } } } if ((OpenMode & EFI_FILE_MODE_CREATE) && (Status == EFI_SUCCESS)) { // // Set the attribute // InfoSize = 0; Info = NULL; Status = WinNtFileGetInfo (&NewPrivateFile->EfiFile, &gEfiFileInfoGuid, &InfoSize, Info); if (Status != EFI_BUFFER_TOO_SMALL) { Status = EFI_DEVICE_ERROR; goto Done; } Info = AllocatePool (InfoSize); if (Info == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } Status = WinNtFileGetInfo (&NewPrivateFile->EfiFile, &gEfiFileInfoGuid, &InfoSize, Info); if (EFI_ERROR (Status)) { FreePool (Info); goto Done; } Info->Attribute = Attributes; WinNtFileSetInfo (&NewPrivateFile->EfiFile, &gEfiFileInfoGuid, InfoSize, Info); FreePool (Info); } Done: FreePool (FileName); if (EFI_ERROR (Status)) { if (NewPrivateFile) { if (NewPrivateFile->FileName) { FreePool (NewPrivateFile->FileName); } if (NewPrivateFile->FilePath) { FreePool (NewPrivateFile->FilePath); } FreePool (NewPrivateFile); } } else { *NewHandle = &NewPrivateFile->EfiFile; if (StrCmp (NewPrivateFile->FileName, PrivateRoot->FilePath) == 0) { NewPrivateFile->IsRootDirectory = TRUE; } } return Status; } /** Close the file handle @param This Protocol instance pointer. @retval EFI_SUCCESS The device was opened. **/ EFI_STATUS WinNtFileClose ( IN EFI_FILE_PROTOCOL *This ) { WIN_NT_EFI_FILE_PRIVATE *PrivateFile; PrivateFile = WIN_NT_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); if (PrivateFile->LHandle != INVALID_HANDLE_VALUE) { if (PrivateFile->IsDirectoryPath) { FindClose (PrivateFile->LHandle); } else { CloseHandle (PrivateFile->LHandle); } PrivateFile->LHandle = INVALID_HANDLE_VALUE; } if (PrivateFile->IsDirectoryPath && (PrivateFile->DirHandle != INVALID_HANDLE_VALUE)) { CloseHandle (PrivateFile->DirHandle); PrivateFile->DirHandle = INVALID_HANDLE_VALUE; } if (PrivateFile->FileName) { FreePool (PrivateFile->FileName); } if (PrivateFile->FilePath) { FreePool (PrivateFile->FilePath); } FreePool (PrivateFile); return EFI_SUCCESS; } /** Close and delete the file handle. @param This Protocol instance pointer. @retval EFI_SUCCESS The device was opened. @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted. **/ EFI_STATUS WinNtFileDelete ( IN EFI_FILE_PROTOCOL *This ) { EFI_STATUS Status; WIN_NT_EFI_FILE_PRIVATE *PrivateFile; PrivateFile = WIN_NT_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); Status = EFI_WARN_DELETE_FAILURE; if (PrivateFile->IsDirectoryPath) { if (PrivateFile->LHandle != INVALID_HANDLE_VALUE) { FindClose (PrivateFile->LHandle); } if (PrivateFile->DirHandle != INVALID_HANDLE_VALUE) { CloseHandle (PrivateFile->DirHandle); PrivateFile->DirHandle = INVALID_HANDLE_VALUE; } if (RemoveDirectory (PrivateFile->FileName)) { Status = EFI_SUCCESS; } } else { CloseHandle (PrivateFile->LHandle); PrivateFile->LHandle = INVALID_HANDLE_VALUE; if (!PrivateFile->IsOpenedByRead) { if (DeleteFile (PrivateFile->FileName)) { Status = EFI_SUCCESS; } } } FreePool (PrivateFile->FileName); FreePool (PrivateFile->FilePath); FreePool (PrivateFile); return Status; } VOID WinNtSystemTimeToEfiTime ( IN SYSTEMTIME *SystemTime, IN TIME_ZONE_INFORMATION *TimeZone, OUT EFI_TIME *Time ) /*++ Routine Description: TODO: Add function description Arguments: SystemTime - TODO: add argument description TimeZone - TODO: add argument description Time - TODO: add argument description Returns: TODO: add return values --*/ { Time->Year = (UINT16)SystemTime->wYear; Time->Month = (UINT8)SystemTime->wMonth; Time->Day = (UINT8)SystemTime->wDay; Time->Hour = (UINT8)SystemTime->wHour; Time->Minute = (UINT8)SystemTime->wMinute; Time->Second = (UINT8)SystemTime->wSecond; Time->Nanosecond = (UINT32)SystemTime->wMilliseconds * 1000000; Time->TimeZone = (INT16)TimeZone->Bias; if (TimeZone->StandardDate.wMonth) { Time->Daylight = EFI_TIME_ADJUST_DAYLIGHT; } } /** Convert the FileTime to EfiTime. @param PrivateFile Pointer to WIN_NT_EFI_FILE_PRIVATE. @param TimeZone Pointer to the current time zone. @param FileTime Pointer to file time. @param EfiTime Pointer to EFI time. **/ VOID WinNtFileTimeToEfiTime ( IN CONST WIN_NT_EFI_FILE_PRIVATE *PrivateFile, IN TIME_ZONE_INFORMATION *TimeZone, IN CONST FILETIME *FileTime, OUT EFI_TIME *EfiTime ) { FILETIME TempFileTime; SYSTEMTIME SystemTime; FileTimeToLocalFileTime (FileTime, &TempFileTime); FileTimeToSystemTime (&TempFileTime, &SystemTime); WinNtSystemTimeToEfiTime (&SystemTime, TimeZone, EfiTime); } /** Read data from the file. @param This Protocol instance pointer. @param BufferSize On input size of buffer, on output amount of data in buffer. @param Buffer The buffer in which data is read. @retval EFI_SUCCESS Data was read. @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_BUFFER_TO_SMALL BufferSize is too small. BufferSize contains required size. **/ EFI_STATUS WinNtFileRead ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { WIN_NT_EFI_FILE_PRIVATE *PrivateFile; EFI_STATUS Status; UINTN Size; UINTN NameSize; UINTN ResultSize; UINTN Index; EFI_FILE_INFO *Info; WCHAR *pw; TIME_ZONE_INFORMATION TimeZone; EFI_FILE_INFO *FileInfo; UINT64 Pos; UINT64 FileSize; UINTN FileInfoSize; PrivateFile = WIN_NT_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); if (PrivateFile->LHandle == INVALID_HANDLE_VALUE) { Status = EFI_DEVICE_ERROR; goto Done; } if (!PrivateFile->IsDirectoryPath) { if (This->GetPosition (This, &Pos) != EFI_SUCCESS) { Status = EFI_DEVICE_ERROR; goto Done; } FileInfoSize = SIZE_OF_EFI_FILE_SYSTEM_INFO; FileInfo = AllocatePool (FileInfoSize); Status = This->GetInfo ( This, &gEfiFileInfoGuid, &FileInfoSize, FileInfo ); if (Status == EFI_BUFFER_TOO_SMALL) { FreePool (FileInfo); FileInfo = AllocatePool (FileInfoSize); Status = This->GetInfo ( This, &gEfiFileInfoGuid, &FileInfoSize, FileInfo ); } if (EFI_ERROR (Status)) { Status = EFI_DEVICE_ERROR; goto Done; } FileSize = FileInfo->FileSize; FreePool (FileInfo); if (Pos >= FileSize) { *BufferSize = 0; if (Pos == FileSize) { Status = EFI_SUCCESS; goto Done; } else { Status = EFI_DEVICE_ERROR; goto Done; } } Status = ReadFile ( PrivateFile->LHandle, Buffer, (DWORD)*BufferSize, (LPDWORD)BufferSize, NULL ) ? EFI_SUCCESS : EFI_DEVICE_ERROR; goto Done; } // // Read on a directory. Perform a find next // if (!PrivateFile->IsValidFindBuf) { *BufferSize = 0; Status = EFI_SUCCESS; goto Done; } Size = SIZE_OF_EFI_FILE_INFO; NameSize = StrSize (PrivateFile->FindBuf.cFileName); ResultSize = Size + NameSize; Status = EFI_BUFFER_TOO_SMALL; if (*BufferSize >= ResultSize) { Status = EFI_SUCCESS; Info = Buffer; ZeroMem (Info, ResultSize); Info->Size = ResultSize; GetTimeZoneInformation (&TimeZone); WinNtFileTimeToEfiTime (PrivateFile, &TimeZone, &PrivateFile->FindBuf.ftCreationTime, &Info->CreateTime); WinNtFileTimeToEfiTime (PrivateFile, &TimeZone, &PrivateFile->FindBuf.ftLastAccessTime, &Info->LastAccessTime); WinNtFileTimeToEfiTime (PrivateFile, &TimeZone, &PrivateFile->FindBuf.ftLastWriteTime, &Info->ModificationTime); Info->FileSize = PrivateFile->FindBuf.nFileSizeLow; Info->PhysicalSize = PrivateFile->FindBuf.nFileSizeLow; if (PrivateFile->FindBuf.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) { Info->Attribute |= EFI_FILE_ARCHIVE; } if (PrivateFile->FindBuf.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) { Info->Attribute |= EFI_FILE_HIDDEN; } if (PrivateFile->FindBuf.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) { Info->Attribute |= EFI_FILE_SYSTEM; } if (PrivateFile->FindBuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { Info->Attribute |= EFI_FILE_READ_ONLY; } if (PrivateFile->FindBuf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { Info->Attribute |= EFI_FILE_DIRECTORY; } NameSize = NameSize / sizeof (WCHAR); pw = (WCHAR *)(((CHAR8 *)Buffer) + Size); for (Index = 0; Index < NameSize; Index++) { pw[Index] = PrivateFile->FindBuf.cFileName[Index]; } if (FindNextFile (PrivateFile->LHandle, &PrivateFile->FindBuf)) { PrivateFile->IsValidFindBuf = TRUE; } else { PrivateFile->IsValidFindBuf = FALSE; } } *BufferSize = ResultSize; Done: return Status; } /** Write data to a file. @param This Protocol instance pointer. @param BufferSize On input size of buffer, on output amount of data in buffer. @param Buffer The buffer in which data to write. @retval EFI_SUCCESS Data was written. @retval EFI_UNSUPPORTED Writes to Open directory are not supported. @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_WRITE_PROTECTED The device is write protected. @retval EFI_ACCESS_DENIED The file was open for read only. @retval EFI_VOLUME_FULL The volume is full. **/ EFI_STATUS WinNtFileWrite ( IN EFI_FILE_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer ) { WIN_NT_EFI_FILE_PRIVATE *PrivateFile; EFI_STATUS Status; PrivateFile = WIN_NT_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); if (PrivateFile->LHandle == INVALID_HANDLE_VALUE) { Status = EFI_DEVICE_ERROR; goto Done; } if (PrivateFile->IsDirectoryPath) { Status = EFI_UNSUPPORTED; goto Done; } if (PrivateFile->IsOpenedByRead) { Status = EFI_ACCESS_DENIED; goto Done; } Status = WriteFile ( PrivateFile->LHandle, Buffer, (DWORD)*BufferSize, (LPDWORD)BufferSize, NULL ) ? EFI_SUCCESS : EFI_DEVICE_ERROR; Done: return Status; // // bugbug: need to access windows error reporting // } /** Set a files current position @param This Protocol instance pointer. @param Position Byte position from the start of the file. @retval EFI_SUCCESS Data was written. @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open. **/ EFI_STATUS WinNtFileSetPossition ( IN EFI_FILE_PROTOCOL *This, IN UINT64 Position ) { EFI_STATUS Status; WIN_NT_EFI_FILE_PRIVATE *PrivateFile; UINT32 PosLow; UINT32 PosHigh; CHAR16 *FileName; UINTN Size; PrivateFile = WIN_NT_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); if (PrivateFile->IsDirectoryPath) { if (Position != 0) { Status = EFI_UNSUPPORTED; goto Done; } Size = StrSize (PrivateFile->FileName); Size += StrSize (L"\\*"); FileName = AllocatePool (Size); if (FileName == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } StrCpyS (FileName, Size / sizeof (CHAR16), PrivateFile->FileName); StrCatS (FileName, Size / sizeof (CHAR16), L"\\*"); if (PrivateFile->LHandle != INVALID_HANDLE_VALUE) { FindClose (PrivateFile->LHandle); } PrivateFile->LHandle = FindFirstFile (FileName, &PrivateFile->FindBuf); if (PrivateFile->LHandle == INVALID_HANDLE_VALUE) { PrivateFile->IsValidFindBuf = FALSE; } else { PrivateFile->IsValidFindBuf = TRUE; } FreePool (FileName); Status = (PrivateFile->LHandle == INVALID_HANDLE_VALUE) ? EFI_DEVICE_ERROR : EFI_SUCCESS; } else { if (Position == (UINT64)-1) { PosLow = SetFilePointer (PrivateFile->LHandle, (ULONG)0, NULL, FILE_END); } else { PosHigh = (UINT32)RShiftU64 (Position, 32); PosLow = SetFilePointer (PrivateFile->LHandle, (ULONG)Position, (PLONG)&PosHigh, FILE_BEGIN); } Status = (PosLow == 0xFFFFFFFF) ? EFI_DEVICE_ERROR : EFI_SUCCESS; } Done: return Status; } /** Get a file's current position @param This Protocol instance pointer. @param Position Byte position from the start of the file. @retval EFI_SUCCESS Data was written. @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open.. **/ EFI_STATUS WinNtFileGetPossition ( IN EFI_FILE_PROTOCOL *This, OUT UINT64 *Position ) { EFI_STATUS Status; WIN_NT_EFI_FILE_PRIVATE *PrivateFile; INT32 PositionHigh; UINT64 PosHigh64; PrivateFile = WIN_NT_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); PositionHigh = 0; PosHigh64 = 0; if (PrivateFile->IsDirectoryPath) { Status = EFI_UNSUPPORTED; goto Done; } else { PositionHigh = 0; *Position = SetFilePointer ( PrivateFile->LHandle, 0, (PLONG)&PositionHigh, FILE_CURRENT ); Status = *Position == 0xffffffff ? EFI_DEVICE_ERROR : EFI_SUCCESS; if (EFI_ERROR (Status)) { goto Done; } PosHigh64 = PositionHigh; *Position += LShiftU64 (PosHigh64, 32); } Done: return Status; } EFI_STATUS WinNtSimpleFileSystemFileInfo ( IN WIN_NT_EFI_FILE_PRIVATE *PrivateFile, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) /*++ Routine Description: TODO: Add function description Arguments: PrivateFile - TODO: add argument description BufferSize - TODO: add argument description Buffer - TODO: add argument description Returns: TODO: add return values --*/ { EFI_STATUS Status; UINTN Size; UINTN NameSize; UINTN ResultSize; EFI_FILE_INFO *Info; BY_HANDLE_FILE_INFORMATION FileInfo; CHAR16 *RealFileName; CHAR16 *TempPointer; TIME_ZONE_INFORMATION TimeZone; Size = SIZE_OF_EFI_FILE_INFO; RealFileName = PrivateFile->FileName; TempPointer = RealFileName; while (*TempPointer) { if (*TempPointer == '\\') { RealFileName = TempPointer + 1; } TempPointer++; } NameSize = StrSize (RealFileName); ResultSize = Size + NameSize; Status = EFI_BUFFER_TOO_SMALL; if (*BufferSize >= ResultSize) { Status = EFI_SUCCESS; Info = Buffer; ZeroMem (Info, ResultSize); Info->Size = ResultSize; GetFileInformationByHandle ( PrivateFile->IsDirectoryPath ? PrivateFile->DirHandle : PrivateFile->LHandle, &FileInfo ); Info->FileSize = FileInfo.nFileSizeLow; Info->PhysicalSize = Info->FileSize; GetTimeZoneInformation (&TimeZone); WinNtFileTimeToEfiTime (PrivateFile, &TimeZone, &FileInfo.ftCreationTime, &Info->CreateTime); WinNtFileTimeToEfiTime (PrivateFile, &TimeZone, &FileInfo.ftLastAccessTime, &Info->LastAccessTime); WinNtFileTimeToEfiTime (PrivateFile, &TimeZone, &FileInfo.ftLastWriteTime, &Info->ModificationTime); if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) { Info->Attribute |= EFI_FILE_ARCHIVE; } if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) { Info->Attribute |= EFI_FILE_HIDDEN; } if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { Info->Attribute |= EFI_FILE_READ_ONLY; } if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) { Info->Attribute |= EFI_FILE_SYSTEM; } if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { Info->Attribute |= EFI_FILE_DIRECTORY; } if (PrivateFile->IsDirectoryPath) { Info->Attribute |= EFI_FILE_DIRECTORY; } if (PrivateFile->IsRootDirectory) { *((CHAR8 *)Buffer + Size) = 0; } else { CopyMem ((CHAR8 *)Buffer + Size, RealFileName, NameSize); } } *BufferSize = ResultSize; return Status; } /** Get information about a file. @param This Protocol instance pointer. @param InformationType Type of information to return in Buffer. @param BufferSize On input size of buffer, on output amount of data in buffer. @param Buffer The buffer to return data. @retval EFI_SUCCESS Data was returned. @retval EFI_UNSUPPORTED InformationType is not supported. @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_WRITE_PROTECTED The device is write protected. @retval EFI_ACCESS_DENIED The file was open for read only. @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in BufferSize. **/ EFI_STATUS WinNtFileGetInfo ( IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) { EFI_STATUS Status; WIN_NT_EFI_FILE_PRIVATE *PrivateFile; EFI_FILE_SYSTEM_INFO *FileSystemInfoBuffer; UINT32 SectorsPerCluster; UINT32 BytesPerSector; UINT32 FreeClusters; UINT32 TotalClusters; UINT32 BytesPerCluster; CHAR16 *DriveName; BOOLEAN DriveNameFound; BOOL NtStatus; UINTN Index; WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE *PrivateRoot; if ((This == NULL) || (InformationType == NULL) || (BufferSize == NULL)) { return EFI_INVALID_PARAMETER; } PrivateFile = WIN_NT_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); PrivateRoot = WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE_DATA_FROM_THIS (PrivateFile->SimpleFileSystem); Status = EFI_UNSUPPORTED; if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { Status = WinNtSimpleFileSystemFileInfo (PrivateFile, BufferSize, Buffer); } if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { if (*BufferSize < SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (PrivateRoot->VolumeLabel)) { *BufferSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (PrivateRoot->VolumeLabel); Status = EFI_BUFFER_TOO_SMALL; goto Done; } FileSystemInfoBuffer = (EFI_FILE_SYSTEM_INFO *)Buffer; FileSystemInfoBuffer->Size = SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (PrivateRoot->VolumeLabel); FileSystemInfoBuffer->ReadOnly = FALSE; // // Try to get the drive name // DriveNameFound = FALSE; DriveName = AllocatePool (StrSize (PrivateFile->FilePath) + 1); if (DriveName == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } StrCpyS ( DriveName, (StrSize (PrivateFile->FilePath) + 1) / sizeof (CHAR16), PrivateFile->FilePath ); for (Index = 0; DriveName[Index] != 0 && DriveName[Index] != ':'; Index++) { } if (DriveName[Index] == ':') { DriveName[Index + 1] = '\\'; DriveName[Index + 2] = 0; DriveNameFound = TRUE; } else if ((DriveName[0] == '\\') && (DriveName[1] == '\\')) { for (Index = 2; DriveName[Index] != 0 && DriveName[Index] != '\\'; Index++) { } if (DriveName[Index] == '\\') { DriveNameFound = TRUE; for (Index++; DriveName[Index] != 0 && DriveName[Index] != '\\'; Index++) { } DriveName[Index] = '\\'; DriveName[Index + 1] = 0; } } // // Try GetDiskFreeSpace first // NtStatus = GetDiskFreeSpace ( DriveNameFound ? DriveName : NULL, (LPDWORD)&SectorsPerCluster, (LPDWORD)&BytesPerSector, (LPDWORD)&FreeClusters, (LPDWORD)&TotalClusters ); if (DriveName) { FreePool (DriveName); } if (NtStatus) { // // Succeeded // BytesPerCluster = BytesPerSector * SectorsPerCluster; FileSystemInfoBuffer->VolumeSize = MultU64x32 (TotalClusters, BytesPerCluster); FileSystemInfoBuffer->FreeSpace = MultU64x32 (FreeClusters, BytesPerCluster); FileSystemInfoBuffer->BlockSize = BytesPerCluster; } else { // // try GetDiskFreeSpaceEx then // FileSystemInfoBuffer->BlockSize = 0; NtStatus = GetDiskFreeSpaceEx ( PrivateFile->FilePath, (PULARGE_INTEGER)(&FileSystemInfoBuffer->FreeSpace), (PULARGE_INTEGER)(&FileSystemInfoBuffer->VolumeSize), NULL ); if (!NtStatus) { Status = EFI_DEVICE_ERROR; goto Done; } } StrCpyS ( (CHAR16 *)FileSystemInfoBuffer->VolumeLabel, (*BufferSize - SIZE_OF_EFI_FILE_SYSTEM_INFO) / sizeof (CHAR16), PrivateRoot->VolumeLabel ); *BufferSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (PrivateRoot->VolumeLabel); Status = EFI_SUCCESS; } if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { if (*BufferSize < StrSize (PrivateRoot->VolumeLabel)) { *BufferSize = StrSize (PrivateRoot->VolumeLabel); Status = EFI_BUFFER_TOO_SMALL; goto Done; } StrCpyS ( (CHAR16 *)Buffer, *BufferSize / sizeof (CHAR16), PrivateRoot->VolumeLabel ); *BufferSize = StrSize (PrivateRoot->VolumeLabel); Status = EFI_SUCCESS; } Done: return Status; } /** Set information about a file @param File Protocol instance pointer. @param InformationType Type of information in Buffer. @param BufferSize Size of buffer. @param Buffer The data to write. @retval EFI_SUCCESS Data was returned. @retval EFI_UNSUPPORTED InformationType is not supported. @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_WRITE_PROTECTED The device is write protected. @retval EFI_ACCESS_DENIED The file was open for read only. **/ EFI_STATUS WinNtFileSetInfo ( IN EFI_FILE_PROTOCOL *This, IN EFI_GUID *InformationType, IN UINTN BufferSize, IN VOID *Buffer ) { WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE *PrivateRoot; WIN_NT_EFI_FILE_PRIVATE *PrivateFile; EFI_FILE_INFO *OldFileInfo; EFI_FILE_INFO *NewFileInfo; EFI_STATUS Status; UINTN OldInfoSize; INTN NtStatus; UINT32 NewAttr; UINT32 OldAttr; CHAR16 *OldFileName; CHAR16 *NewFileName; CHAR16 *TempFileName; CHAR16 *CharPointer; BOOLEAN AttrChangeFlag; BOOLEAN NameChangeFlag; BOOLEAN SizeChangeFlag; BOOLEAN TimeChangeFlag; UINT64 CurPos; SYSTEMTIME NewCreationSystemTime; SYSTEMTIME NewLastAccessSystemTime; SYSTEMTIME NewLastWriteSystemTime; FILETIME NewCreationFileTime; FILETIME NewLastAccessFileTime; FILETIME NewLastWriteFileTime; WIN32_FIND_DATA FindBuf; EFI_FILE_SYSTEM_INFO *NewFileSystemInfo; UINTN Size; // // Initialise locals. // PrivateFile = WIN_NT_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); PrivateRoot = WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE_DATA_FROM_THIS (PrivateFile->SimpleFileSystem); Status = EFI_UNSUPPORTED; OldFileInfo = NewFileInfo = NULL; OldFileName = NewFileName = NULL; AttrChangeFlag = NameChangeFlag = SizeChangeFlag = TimeChangeFlag = FALSE; // // Set file system information. // if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { NewFileSystemInfo = (EFI_FILE_SYSTEM_INFO *)Buffer; if (BufferSize < SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (NewFileSystemInfo->VolumeLabel)) { Status = EFI_BAD_BUFFER_SIZE; goto Done; } FreePool (PrivateRoot->VolumeLabel); PrivateRoot->VolumeLabel = AllocatePool (StrSize (NewFileSystemInfo->VolumeLabel)); if (PrivateRoot->VolumeLabel == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } StrCpyS ( PrivateRoot->VolumeLabel, StrSize (NewFileSystemInfo->VolumeLabel) / sizeof (CHAR16), NewFileSystemInfo->VolumeLabel ); Status = EFI_SUCCESS; goto Done; } // // Set volume label information. // if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { if (BufferSize < StrSize (PrivateRoot->VolumeLabel)) { Status = EFI_BAD_BUFFER_SIZE; goto Done; } StrCpyS ( PrivateRoot->VolumeLabel, StrSize (PrivateRoot->VolumeLabel) / sizeof (CHAR16), (CHAR16 *)Buffer ); Status = EFI_SUCCESS; goto Done; } if (!CompareGuid (InformationType, &gEfiFileInfoGuid)) { Status = EFI_UNSUPPORTED; goto Done; } if (BufferSize < SIZE_OF_EFI_FILE_INFO) { Status = EFI_BAD_BUFFER_SIZE; goto Done; } // // Set file/directory information. // // // Check for invalid set file information parameters. // NewFileInfo = (EFI_FILE_INFO *)Buffer; if ((NewFileInfo->Size <= SIZE_OF_EFI_FILE_INFO) || (NewFileInfo->Attribute &~(EFI_FILE_VALID_ATTR)) || ((sizeof (UINTN) == 4) && (NewFileInfo->Size > 0xFFFFFFFF)) ) { Status = EFI_INVALID_PARAMETER; goto Done; } // // bugbug: - This is not safe. We need something like EfiStrMaxSize() // that would have an additional parameter that would be the size // of the string array just in case there are no NULL characters in // the string array. // // // Get current file information so we can determine what kind // of change request this is. // OldInfoSize = 0; Status = WinNtSimpleFileSystemFileInfo (PrivateFile, &OldInfoSize, NULL); if (Status != EFI_BUFFER_TOO_SMALL) { Status = EFI_DEVICE_ERROR; goto Done; } OldFileInfo = AllocatePool (OldInfoSize); if (OldFileInfo == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } Status = WinNtSimpleFileSystemFileInfo (PrivateFile, &OldInfoSize, OldFileInfo); if (EFI_ERROR (Status)) { goto Done; } OldFileName = AllocatePool (StrSize (PrivateFile->FileName)); if (OldFileName == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } StrCpyS ( OldFileName, StrSize (PrivateFile->FileName) / sizeof (CHAR16), PrivateFile->FileName ); // // Make full pathname from new filename and rootpath. // if (NewFileInfo->FileName[0] == '\\') { Size = StrSize (PrivateRoot->FilePath); Size += StrSize (L"\\"); Size += StrSize (NewFileInfo->FileName); NewFileName = AllocatePool (Size); if (NewFileName == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } StrCpyS (NewFileName, Size / sizeof (CHAR16), PrivateRoot->FilePath); StrCatS (NewFileName, Size / sizeof (CHAR16), L"\\"); StrCatS (NewFileName, Size / sizeof (CHAR16), NewFileInfo->FileName + 1); } else { Size = StrSize (PrivateFile->FilePath); Size += StrSize (L"\\"); Size += StrSize (NewFileInfo->FileName); NewFileName = AllocatePool (Size); if (NewFileName == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } StrCpyS (NewFileName, Size / sizeof (CHAR16), PrivateFile->FilePath); StrCatS (NewFileName, Size / sizeof (CHAR16), L"\\"); StrCatS (NewFileName, Size / sizeof (CHAR16), NewFileInfo->FileName); } // // Is there an attribute change request? // if (NewFileInfo->Attribute != OldFileInfo->Attribute) { if ((NewFileInfo->Attribute & EFI_FILE_DIRECTORY) != (OldFileInfo->Attribute & EFI_FILE_DIRECTORY)) { Status = EFI_INVALID_PARAMETER; goto Done; } AttrChangeFlag = TRUE; } // // Is there a name change request? // bugbug: - Need EfiStrCaseCmp() // if (StrCmp (NewFileInfo->FileName, OldFileInfo->FileName)) { NameChangeFlag = TRUE; } // // Is there a size change request? // if (NewFileInfo->FileSize != OldFileInfo->FileSize) { SizeChangeFlag = TRUE; } // // Is there a time stamp change request? // if (!IsZero (&NewFileInfo->CreateTime, sizeof (EFI_TIME)) && CompareMem (&NewFileInfo->CreateTime, &OldFileInfo->CreateTime, sizeof (EFI_TIME)) ) { TimeChangeFlag = TRUE; } else if (!IsZero (&NewFileInfo->LastAccessTime, sizeof (EFI_TIME)) && CompareMem (&NewFileInfo->LastAccessTime, &OldFileInfo->LastAccessTime, sizeof (EFI_TIME)) ) { TimeChangeFlag = TRUE; } else if (!IsZero (&NewFileInfo->ModificationTime, sizeof (EFI_TIME)) && CompareMem (&NewFileInfo->ModificationTime, &OldFileInfo->ModificationTime, sizeof (EFI_TIME)) ) { TimeChangeFlag = TRUE; } // // All done if there are no change requests being made. // if (!(AttrChangeFlag || NameChangeFlag || SizeChangeFlag || TimeChangeFlag)) { Status = EFI_SUCCESS; goto Done; } // // Set file or directory information. // OldAttr = GetFileAttributes (OldFileName); // // Name change. // if (NameChangeFlag) { // // Close the handles first // if (PrivateFile->IsOpenedByRead) { Status = EFI_ACCESS_DENIED; goto Done; } for (CharPointer = NewFileName; *CharPointer != 0 && *CharPointer != L'/'; CharPointer++) { } if (*CharPointer != 0) { Status = EFI_ACCESS_DENIED; goto Done; } if (PrivateFile->LHandle != INVALID_HANDLE_VALUE) { if (PrivateFile->IsDirectoryPath) { FindClose (PrivateFile->LHandle); } else { CloseHandle (PrivateFile->LHandle); PrivateFile->LHandle = INVALID_HANDLE_VALUE; } } if (PrivateFile->IsDirectoryPath && (PrivateFile->DirHandle != INVALID_HANDLE_VALUE)) { CloseHandle (PrivateFile->DirHandle); PrivateFile->DirHandle = INVALID_HANDLE_VALUE; } NtStatus = MoveFile (OldFileName, NewFileName); if (NtStatus) { // // modify file name // FreePool (PrivateFile->FileName); PrivateFile->FileName = AllocatePool (StrSize (NewFileName)); if (PrivateFile->FileName == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } StrCpyS (PrivateFile->FileName, StrSize (NewFileName) / sizeof (CHAR16), NewFileName); Size = StrSize (NewFileName); Size += StrSize (L"\\*"); TempFileName = AllocatePool (Size); StrCpyS (TempFileName, Size / sizeof (CHAR16), NewFileName); if (!PrivateFile->IsDirectoryPath) { PrivateFile->LHandle = CreateFile ( TempFileName, PrivateFile->IsOpenedByRead ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); FreePool (TempFileName); // // Flush buffers just in case // if (FlushFileBuffers (PrivateFile->LHandle) == 0) { Status = EFI_DEVICE_ERROR; goto Done; } } else { PrivateFile->DirHandle = CreateFile ( TempFileName, PrivateFile->IsOpenedByRead ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); StrCatS (TempFileName, Size / sizeof (CHAR16), L"\\*"); PrivateFile->LHandle = FindFirstFile (TempFileName, &FindBuf); FreePool (TempFileName); } } else { Status = EFI_ACCESS_DENIED; Reopen:; NtStatus = SetFileAttributes (OldFileName, OldAttr); if (!NtStatus) { goto Done; } Size = StrSize (OldFileName); Size += StrSize (L"\\*"); TempFileName = AllocatePool (Size); StrCpyS (TempFileName, Size / sizeof (CHAR16), OldFileName); if (!PrivateFile->IsDirectoryPath) { PrivateFile->LHandle = CreateFile ( TempFileName, PrivateFile->IsOpenedByRead ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); } else { PrivateFile->DirHandle = CreateFile ( TempFileName, PrivateFile->IsOpenedByRead ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); StrCatS (TempFileName, Size / sizeof (CHAR16), L"\\*"); PrivateFile->LHandle = FindFirstFile (TempFileName, &FindBuf); } FreePool (TempFileName); goto Done; } } // // Size change // if (SizeChangeFlag) { if (PrivateFile->IsDirectoryPath) { Status = EFI_UNSUPPORTED; goto Done; } if (PrivateFile->IsOpenedByRead || OldFileInfo->Attribute & EFI_FILE_READ_ONLY) { Status = EFI_ACCESS_DENIED; goto Done; } Status = This->GetPosition (This, &CurPos); if (EFI_ERROR (Status)) { goto Done; } Status = This->SetPosition (This, NewFileInfo->FileSize); if (EFI_ERROR (Status)) { goto Done; } if (SetEndOfFile (PrivateFile->LHandle) == 0) { Status = EFI_DEVICE_ERROR; goto Done; } Status = This->SetPosition (This, CurPos); if (EFI_ERROR (Status)) { goto Done; } } // // Time change // if (TimeChangeFlag) { NewCreationSystemTime.wYear = NewFileInfo->CreateTime.Year; NewCreationSystemTime.wMonth = NewFileInfo->CreateTime.Month; NewCreationSystemTime.wDay = NewFileInfo->CreateTime.Day; NewCreationSystemTime.wHour = NewFileInfo->CreateTime.Hour; NewCreationSystemTime.wMinute = NewFileInfo->CreateTime.Minute; NewCreationSystemTime.wSecond = NewFileInfo->CreateTime.Second; NewCreationSystemTime.wMilliseconds = 0; if (!SystemTimeToFileTime ( &NewCreationSystemTime, &NewCreationFileTime )) { goto Done; } if (!LocalFileTimeToFileTime ( &NewCreationFileTime, &NewCreationFileTime )) { goto Done; } NewLastAccessSystemTime.wYear = NewFileInfo->LastAccessTime.Year; NewLastAccessSystemTime.wMonth = NewFileInfo->LastAccessTime.Month; NewLastAccessSystemTime.wDay = NewFileInfo->LastAccessTime.Day; NewLastAccessSystemTime.wHour = NewFileInfo->LastAccessTime.Hour; NewLastAccessSystemTime.wMinute = NewFileInfo->LastAccessTime.Minute; NewLastAccessSystemTime.wSecond = NewFileInfo->LastAccessTime.Second; NewLastAccessSystemTime.wMilliseconds = 0; if (!SystemTimeToFileTime ( &NewLastAccessSystemTime, &NewLastAccessFileTime )) { goto Done; } if (!LocalFileTimeToFileTime ( &NewLastAccessFileTime, &NewLastAccessFileTime )) { goto Done; } NewLastWriteSystemTime.wYear = NewFileInfo->ModificationTime.Year; NewLastWriteSystemTime.wMonth = NewFileInfo->ModificationTime.Month; NewLastWriteSystemTime.wDay = NewFileInfo->ModificationTime.Day; NewLastWriteSystemTime.wHour = NewFileInfo->ModificationTime.Hour; NewLastWriteSystemTime.wMinute = NewFileInfo->ModificationTime.Minute; NewLastWriteSystemTime.wSecond = NewFileInfo->ModificationTime.Second; NewLastWriteSystemTime.wMilliseconds = 0; if (!SystemTimeToFileTime ( &NewLastWriteSystemTime, &NewLastWriteFileTime )) { goto Done; } if (!LocalFileTimeToFileTime ( &NewLastWriteFileTime, &NewLastWriteFileTime )) { goto Done; } if (!SetFileTime ( PrivateFile->IsDirectoryPath ? PrivateFile->DirHandle : PrivateFile->LHandle, &NewCreationFileTime, &NewLastAccessFileTime, &NewLastWriteFileTime )) { Status = EFI_DEVICE_ERROR; goto Done; } } // // No matter about AttrChangeFlag, Attribute must be set. // Because operation before may cause attribute change. // NewAttr = OldAttr; if (NewFileInfo->Attribute & EFI_FILE_ARCHIVE) { NewAttr |= FILE_ATTRIBUTE_ARCHIVE; } else { NewAttr &= ~FILE_ATTRIBUTE_ARCHIVE; } if (NewFileInfo->Attribute & EFI_FILE_HIDDEN) { NewAttr |= FILE_ATTRIBUTE_HIDDEN; } else { NewAttr &= ~FILE_ATTRIBUTE_HIDDEN; } if (NewFileInfo->Attribute & EFI_FILE_SYSTEM) { NewAttr |= FILE_ATTRIBUTE_SYSTEM; } else { NewAttr &= ~FILE_ATTRIBUTE_SYSTEM; } if (NewFileInfo->Attribute & EFI_FILE_READ_ONLY) { NewAttr |= FILE_ATTRIBUTE_READONLY; } else { NewAttr &= ~FILE_ATTRIBUTE_READONLY; } NtStatus = SetFileAttributes (NewFileName, NewAttr); if (!NtStatus) { Status = EFI_DEVICE_ERROR; goto Reopen; } Done: if (OldFileInfo != NULL) { FreePool (OldFileInfo); } if (OldFileName != NULL) { FreePool (OldFileName); } if (NewFileName != NULL) { FreePool (NewFileName); } return Status; } /** Flush data back for the file handle. @param This Protocol instance pointer. @retval EFI_SUCCESS Data was written. @retval EFI_UNSUPPORTED Writes to Open directory are not supported. @retval EFI_NO_MEDIA The device has no media. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. @retval EFI_WRITE_PROTECTED The device is write protected. @retval EFI_ACCESS_DENIED The file was open for read only. @retval EFI_VOLUME_FULL The volume is full. **/ EFI_STATUS WinNtFileFlush ( IN EFI_FILE_PROTOCOL *This ) { BY_HANDLE_FILE_INFORMATION FileInfo; WIN_NT_EFI_FILE_PRIVATE *PrivateFile; EFI_STATUS Status; PrivateFile = WIN_NT_EFI_FILE_PRIVATE_DATA_FROM_THIS (This); if (PrivateFile->LHandle == INVALID_HANDLE_VALUE) { Status = EFI_DEVICE_ERROR; goto Done; } if (PrivateFile->IsDirectoryPath) { Status = EFI_SUCCESS; goto Done; } if (PrivateFile->IsOpenedByRead) { Status = EFI_ACCESS_DENIED; goto Done; } GetFileInformationByHandle (PrivateFile->LHandle, &FileInfo); if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { Status = EFI_ACCESS_DENIED; goto Done; } Status = FlushFileBuffers (PrivateFile->LHandle) ? EFI_SUCCESS : EFI_DEVICE_ERROR; Done: return Status; // // bugbug: - Use Windows error reporting. // } EFI_STATUS WinNtFileSystmeThunkOpen ( IN EMU_IO_THUNK_PROTOCOL *This ) { WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE *Private; Private = AllocateZeroPool (sizeof (*Private)); if (Private == NULL) { return EFI_OUT_OF_RESOURCES; } Private->FilePath = AllocateCopyPool (StrSize (This->ConfigString), This->ConfigString); if (Private->FilePath == NULL) { FreePool (Private); return EFI_OUT_OF_RESOURCES; } Private->VolumeLabel = AllocateCopyPool (StrSize (L"EFI_EMULATED"), L"EFI_EMULATED"); if (Private->VolumeLabel == NULL) { FreePool (Private->FilePath); FreePool (Private); return EFI_OUT_OF_RESOURCES; } Private->Signature = WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE_SIGNATURE; Private->Thunk = This; CopyMem (&Private->SimpleFileSystem, &gWinNtFileSystemProtocol, sizeof (Private->SimpleFileSystem)); This->Interface = &Private->SimpleFileSystem; This->Private = Private; return EFI_SUCCESS; } EFI_STATUS WinNtFileSystmeThunkClose ( IN EMU_IO_THUNK_PROTOCOL *This ) { WIN_NT_SIMPLE_FILE_SYSTEM_PRIVATE *Private; Private = This->Private; ASSERT (Private != NULL); if (Private->VolumeLabel != NULL) { FreePool (Private->VolumeLabel); } if (Private->FilePath != NULL) { FreePool (Private->FilePath); } FreePool (Private); return EFI_SUCCESS; } EFI_FILE_PROTOCOL gWinNtFileProtocol = { EFI_FILE_REVISION, WinNtFileOpen, WinNtFileClose, WinNtFileDelete, WinNtFileRead, WinNtFileWrite, WinNtFileGetPossition, WinNtFileSetPossition, WinNtFileGetInfo, WinNtFileSetInfo, WinNtFileFlush }; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL gWinNtFileSystemProtocol = { EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION, WinNtOpenVolume }; EMU_IO_THUNK_PROTOCOL mWinNtFileSystemThunkIo = { &gEfiSimpleFileSystemProtocolGuid, NULL, NULL, 0, WinNtFileSystmeThunkOpen, WinNtFileSystmeThunkClose, NULL };