+\r
+/**\r
+ Open or create a file or directory, possibly creating the chain of\r
+ directories leading up to the directory.\r
+\r
+ EfiOpenFileByDevicePath() first locates EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on\r
+ FilePath, and opens the root directory of that filesystem with\r
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume().\r
+\r
+ On the remaining device path, the longest initial sequence of\r
+ FILEPATH_DEVICE_PATH nodes is node-wise traversed with\r
+ EFI_FILE_PROTOCOL.Open(). For the pathname fragment specified by each\r
+ traversed FILEPATH_DEVICE_PATH node, EfiOpenFileByDevicePath() first masks\r
+ EFI_FILE_MODE_CREATE out of OpenMode, and passes 0 for Attributes. If\r
+ EFI_FILE_PROTOCOL.Open() fails, and OpenMode includes EFI_FILE_MODE_CREATE,\r
+ then the operation is retried with the caller's OpenMode and Attributes\r
+ unmodified.\r
+\r
+ (As a consequence, if OpenMode includes EFI_FILE_MODE_CREATE, and Attributes\r
+ includes EFI_FILE_DIRECTORY, and each FILEPATH_DEVICE_PATH specifies a single\r
+ pathname component, then EfiOpenFileByDevicePath() ensures that the specified\r
+ series of subdirectories exist on return.)\r
+\r
+ The EFI_FILE_PROTOCOL identified by the last FILEPATH_DEVICE_PATH node is\r
+ output to the caller; intermediate EFI_FILE_PROTOCOL instances are closed. If\r
+ there are no FILEPATH_DEVICE_PATH nodes past the node that identifies the\r
+ filesystem, then the EFI_FILE_PROTOCOL of the root directory of the\r
+ filesystem is output to the caller. If a device path node that is different\r
+ from FILEPATH_DEVICE_PATH is encountered relative to the filesystem, the\r
+ traversal is stopped with an error, and a NULL EFI_FILE_PROTOCOL is output.\r
+\r
+ @param[in,out] FilePath On input, the device path to the file or directory\r
+ to open or create. The caller is responsible for\r
+ ensuring that the device path pointed-to by FilePath\r
+ is well-formed. On output, FilePath points one past\r
+ the last node in the original device path that has\r
+ been successfully processed. FilePath is set on\r
+ output even if EfiOpenFileByDevicePath() returns an\r
+ error.\r
+\r
+ @param[out] File On error, File is set to NULL. On success, File is\r
+ set to the EFI_FILE_PROTOCOL of the root directory\r
+ of the filesystem, if there are no\r
+ FILEPATH_DEVICE_PATH nodes in FilePath; otherwise,\r
+ File is set to the EFI_FILE_PROTOCOL identified by\r
+ the last node in FilePath.\r
+\r
+ @param[in] OpenMode The OpenMode parameter to pass to\r
+ EFI_FILE_PROTOCOL.Open(). For each\r
+ FILEPATH_DEVICE_PATH node in FilePath,\r
+ EfiOpenFileByDevicePath() first opens the specified\r
+ pathname fragment with EFI_FILE_MODE_CREATE masked\r
+ out of OpenMode and with Attributes set to 0, and\r
+ only retries the operation with EFI_FILE_MODE_CREATE\r
+ unmasked and Attributes propagated if the first open\r
+ attempt fails.\r
+\r
+ @param[in] Attributes The Attributes parameter to pass to\r
+ EFI_FILE_PROTOCOL.Open(), when EFI_FILE_MODE_CREATE\r
+ is propagated unmasked in OpenMode.\r
+\r
+ @retval EFI_SUCCESS The file or directory has been opened or\r
+ created.\r
+\r
+ @retval EFI_INVALID_PARAMETER FilePath is NULL; or File is NULL; or FilePath\r
+ contains a device path node, past the node\r
+ that identifies\r
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, that is not a\r
+ FILEPATH_DEVICE_PATH node.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
+\r
+ @return Error codes propagated from the\r
+ LocateDevicePath() and OpenProtocol() boot\r
+ services, and from the\r
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume()\r
+ and EFI_FILE_PROTOCOL.Open() member functions.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiOpenFileByDevicePath (\r
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath,\r
+ OUT EFI_FILE_PROTOCOL **File,\r
+ IN UINT64 OpenMode,\r
+ IN UINT64 Attributes\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE FileSystemHandle;\r
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;\r
+ EFI_FILE_PROTOCOL *LastFile;\r
+ FILEPATH_DEVICE_PATH *FilePathNode;\r
+ CHAR16 *AlignedPathName;\r
+ CHAR16 *PathName;\r
+ EFI_FILE_PROTOCOL *NextFile;\r
+\r
+ if (File == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ *File = NULL;\r
+\r
+ if (FilePath == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Look up the filesystem.\r
+ //\r
+ Status = gBS->LocateDevicePath (\r
+ &gEfiSimpleFileSystemProtocolGuid,\r
+ FilePath,\r
+ &FileSystemHandle\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ Status = gBS->OpenProtocol (\r
+ FileSystemHandle,\r
+ &gEfiSimpleFileSystemProtocolGuid,\r
+ (VOID **)&FileSystem,\r
+ gImageHandle,\r
+ NULL,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Open the root directory of the filesystem. After this operation succeeds,\r
+ // we have to release LastFile on error.\r
+ //\r
+ Status = FileSystem->OpenVolume (FileSystem, &LastFile);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Traverse the device path nodes relative to the filesystem.\r
+ //\r
+ while (!IsDevicePathEnd (*FilePath)) {\r
+ if (DevicePathType (*FilePath) != MEDIA_DEVICE_PATH ||\r
+ DevicePathSubType (*FilePath) != MEDIA_FILEPATH_DP) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto CloseLastFile;\r
+ }\r
+ FilePathNode = (FILEPATH_DEVICE_PATH *)*FilePath;\r
+\r
+ //\r
+ // FilePathNode->PathName may be unaligned, and the UEFI specification\r
+ // requires pointers that are passed to protocol member functions to be\r
+ // aligned. Create an aligned copy of the pathname if necessary.\r
+ //\r
+ if ((UINTN)FilePathNode->PathName % sizeof *FilePathNode->PathName == 0) {\r
+ AlignedPathName = NULL;\r
+ PathName = FilePathNode->PathName;\r
+ } else {\r
+ AlignedPathName = AllocateCopyPool (\r
+ (DevicePathNodeLength (FilePathNode) -\r
+ SIZE_OF_FILEPATH_DEVICE_PATH),\r
+ FilePathNode->PathName\r
+ );\r
+ if (AlignedPathName == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto CloseLastFile;\r
+ }\r
+ PathName = AlignedPathName;\r
+ }\r
+\r
+ //\r
+ // Open the next pathname fragment with EFI_FILE_MODE_CREATE masked out and\r
+ // with Attributes set to 0.\r
+ //\r
+ Status = LastFile->Open (\r
+ LastFile,\r
+ &NextFile,\r
+ PathName,\r
+ OpenMode & ~(UINT64)EFI_FILE_MODE_CREATE,\r
+ 0\r
+ );\r
+\r
+ //\r
+ // Retry with EFI_FILE_MODE_CREATE and the original Attributes if the first\r
+ // attempt failed, and the caller specified EFI_FILE_MODE_CREATE.\r
+ //\r
+ if (EFI_ERROR (Status) && (OpenMode & EFI_FILE_MODE_CREATE) != 0) {\r
+ Status = LastFile->Open (\r
+ LastFile,\r
+ &NextFile,\r
+ PathName,\r
+ OpenMode,\r
+ Attributes\r
+ );\r
+ }\r
+\r
+ //\r
+ // Release any AlignedPathName on both error and success paths; PathName is\r
+ // no longer needed.\r
+ //\r
+ if (AlignedPathName != NULL) {\r
+ FreePool (AlignedPathName);\r
+ }\r
+ if (EFI_ERROR (Status)) {\r
+ goto CloseLastFile;\r
+ }\r
+\r
+ //\r
+ // Advance to the next device path node.\r
+ //\r
+ LastFile->Close (LastFile);\r
+ LastFile = NextFile;\r
+ *FilePath = NextDevicePathNode (FilePathNode);\r
+ }\r
+\r
+ *File = LastFile;\r
+ return EFI_SUCCESS;\r
+\r
+CloseLastFile:\r
+ LastFile->Close (LastFile);\r
+\r
+ //\r
+ // We are on the error path; we must have set an error Status for returning\r
+ // to the caller.\r
+ //\r
+ ASSERT (EFI_ERROR (Status));\r
+ return Status;\r
+}\r