]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsOpenClose.c
ArmPlatformPkg: remove ArmPlatformSysConfigLib library class
[mirror_edk2.git] / ArmPlatformPkg / FileSystem / BootMonFs / BootMonFsOpenClose.c
CommitLineData
94e0955d
OM
1/** @file\r
2*\r
fa14cfc9 3* Copyright (c) 2012-2015, ARM Limited. All rights reserved.\r
94e0955d
OM
4*\r
5* This program and the accompanying materials\r
6* are licensed and made available under the terms and conditions of the BSD License\r
7* which accompanies this distribution. The full text of the license may be found at\r
8* http://opensource.org/licenses/bsd-license.php\r
9*\r
10* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12*\r
13**/\r
14\r
15#include "BootMonFsInternal.h"\r
16\r
17// Clear a file's image description on storage media:\r
18// UEFI allows you to seek past the end of a file, a subsequent write will grow\r
19// the file. It does not specify how space between the former end of the file\r
20// and the beginning of the write should be filled. It's therefore possible that\r
21// BootMonFs metadata, that comes after the end of a file, could be left there\r
22// and wrongly detected by BootMonFsImageInBlock.\r
23STATIC\r
24EFI_STATUS\r
25InvalidateImageDescription (\r
26 IN BOOTMON_FS_FILE *File\r
27 )\r
28{\r
29 EFI_DISK_IO_PROTOCOL *DiskIo;\r
30 EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
31 UINT32 MediaId;\r
94e0955d
OM
32 VOID *Buffer;\r
33 EFI_STATUS Status;\r
94e0955d
OM
34\r
35 DiskIo = File->Instance->DiskIo;\r
36 BlockIo = File->Instance->BlockIo;\r
37 MediaId = BlockIo->Media->MediaId;\r
94e0955d
OM
38\r
39 Buffer = AllocateZeroPool (sizeof (HW_IMAGE_DESCRIPTION));\r
40\r
95204533
RC
41 if (Buffer == NULL) {\r
42 return EFI_OUT_OF_RESOURCES;\r
43 }\r
44\r
94e0955d
OM
45 Status = DiskIo->WriteDisk (DiskIo,\r
46 MediaId,\r
79e12331 47 File->HwDescAddress,\r
94e0955d
OM
48 sizeof (HW_IMAGE_DESCRIPTION),\r
49 Buffer\r
50 );\r
51\r
52 FreePool(Buffer);\r
53\r
54 return Status;\r
55}\r
56\r
95204533
RC
57/**\r
58 Write the description of a file to storage media.\r
59\r
60 This function uses DiskIo to write to the media, so call BlockIo->FlushBlocks()\r
61 after calling it to ensure the data are written on the media.\r
62\r
63 @param[in] File Description of the file whose description on the\r
64 storage media has to be updated.\r
65 @param[in] FileName Name of the file. Its length is assumed to be\r
66 lower than MAX_NAME_LENGTH.\r
67 @param[in] DataSize Number of data bytes of the file.\r
68 @param[in] FileStart File's starting position on media. FileStart must\r
69 be aligned to the media's block size.\r
70\r
71 @retval EFI_WRITE_PROTECTED The device cannot be written to.\r
72 @retval EFI_DEVICE_ERROR The device reported an error while performing\r
73 the write operation.\r
74\r
75**/\r
94e0955d
OM
76STATIC\r
77EFI_STATUS\r
95204533
RC
78WriteFileDescription (\r
79 IN BOOTMON_FS_FILE *File,\r
80 IN CHAR8 *FileName,\r
81 IN UINT32 DataSize,\r
82 IN UINT64 FileStart\r
94e0955d
OM
83 )\r
84{\r
95204533
RC
85 EFI_STATUS Status;\r
86 EFI_DISK_IO_PROTOCOL *DiskIo;\r
87 UINTN BlockSize;\r
88 UINT32 FileSize;\r
89 HW_IMAGE_DESCRIPTION *Description;\r
94e0955d 90\r
95204533 91 DiskIo = File->Instance->DiskIo;\r
94e0955d 92 BlockSize = File->Instance->BlockIo->Media->BlockSize;\r
94e0955d
OM
93 ASSERT (FileStart % BlockSize == 0);\r
94\r
94e0955d 95 //\r
95204533 96 // Construct the file description\r
94e0955d 97 //\r
95204533
RC
98\r
99 FileSize = DataSize + sizeof (HW_IMAGE_DESCRIPTION);\r
94e0955d
OM
100 Description = &File->HwDescription;\r
101 Description->Attributes = 1;\r
102 Description->BlockStart = FileStart / BlockSize;\r
95204533 103 Description->BlockEnd = Description->BlockStart + (FileSize / BlockSize);\r
a5cd3bb0
AB
104 AsciiStrCpyS (Description->Footer.Filename,\r
105 sizeof Description->Footer.Filename, FileName);\r
95204533 106\r
cb77b48a 107#ifdef MDE_CPU_ARM\r
95204533 108 Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET;\r
94e0955d 109 Description->Footer.Version = HW_IMAGE_FOOTER_VERSION;\r
cb77b48a 110#else\r
95204533 111 Description->Footer.Offset = HW_IMAGE_FOOTER_OFFSET2;\r
cb77b48a 112 Description->Footer.Version = HW_IMAGE_FOOTER_VERSION2;\r
cb77b48a 113#endif\r
95204533
RC
114 Description->Footer.FooterSignature1 = HW_IMAGE_FOOTER_SIGNATURE_1;\r
115 Description->Footer.FooterSignature2 = HW_IMAGE_FOOTER_SIGNATURE_2;\r
94e0955d
OM
116 Description->RegionCount = 1;\r
117 Description->Region[0].Checksum = 0;\r
118 Description->Region[0].Offset = Description->BlockStart * BlockSize;\r
95204533 119 Description->Region[0].Size = DataSize;\r
94e0955d
OM
120\r
121 Status = BootMonFsComputeFooterChecksum (Description);\r
122 if (EFI_ERROR (Status)) {\r
123 return Status;\r
124 }\r
125\r
95204533 126 File->HwDescAddress = ((Description->BlockEnd + 1) * BlockSize) - sizeof (HW_IMAGE_DESCRIPTION);\r
79e12331 127\r
94e0955d
OM
128 // Update the file description on the media\r
129 Status = DiskIo->WriteDisk (\r
130 DiskIo,\r
131 File->Instance->Media->MediaId,\r
79e12331 132 File->HwDescAddress,\r
94e0955d
OM
133 sizeof (HW_IMAGE_DESCRIPTION),\r
134 Description\r
135 );\r
136 ASSERT_EFI_ERROR (Status);\r
137\r
138 return Status;\r
139}\r
140\r
94e0955d
OM
141// Find a space on media for a file that has not yet been flushed to disk.\r
142// Just returns the first space that's big enough.\r
143// This function could easily be adapted to:\r
144// - Find space for moving an existing file that has outgrown its space\r
145// (We do not currently move files, just return EFI_VOLUME_FULL)\r
146// - Find space for a fragment of a file that has outgrown its space\r
147// (We do not currently fragment files - it's not clear whether fragmentation\r
148// is actually part of BootMonFs as there is no spec)\r
149// - Be more clever about finding space (choosing the largest or smallest\r
150// suitable space)\r
151// Parameters:\r
152// File - the new (not yet flushed) file for which we need to find space.\r
153// FileStart - the position on media of the file (in bytes).\r
154STATIC\r
155EFI_STATUS\r
156BootMonFsFindSpaceForNewFile (\r
157 IN BOOTMON_FS_FILE *File,\r
95204533 158 IN UINT64 FileSize,\r
94e0955d
OM
159 OUT UINT64 *FileStart\r
160 )\r
161{\r
162 LIST_ENTRY *FileLink;\r
163 BOOTMON_FS_FILE *RootFile;\r
164 BOOTMON_FS_FILE *FileEntry;\r
165 UINTN BlockSize;\r
94e0955d
OM
166 EFI_BLOCK_IO_MEDIA *Media;\r
167\r
168 Media = File->Instance->BlockIo->Media;\r
169 BlockSize = Media->BlockSize;\r
170 RootFile = File->Instance->RootFile;\r
171\r
94e0955d
OM
172 // This function must only be called for file which has not been flushed into\r
173 // Flash yet\r
174 ASSERT (File->HwDescription.RegionCount == 0);\r
175\r
94e0955d
OM
176 *FileStart = 0;\r
177 // Go through all the files in the list\r
178 for (FileLink = GetFirstNode (&RootFile->Link);\r
179 !IsNull (&RootFile->Link, FileLink);\r
180 FileLink = GetNextNode (&RootFile->Link, FileLink)\r
181 )\r
182 {\r
183 FileEntry = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);\r
bf6091a9
BJ
184 // Skip files that aren't on disk yet\r
185 if (FileEntry->HwDescription.RegionCount == 0) {\r
186 continue;\r
187 }\r
188\r
94e0955d
OM
189 // If the free space preceding the file is big enough to contain the new\r
190 // file then use it!\r
191 if (((FileEntry->HwDescription.BlockStart * BlockSize) - *FileStart)\r
192 >= FileSize) {\r
193 // The file list must be in disk-order\r
194 RemoveEntryList (&File->Link);\r
195 File->Link.BackLink = FileLink->BackLink;\r
196 File->Link.ForwardLink = FileLink;\r
197 FileLink->BackLink->ForwardLink = &File->Link;\r
198 FileLink->BackLink = &File->Link;\r
199\r
200 return EFI_SUCCESS;\r
201 } else {\r
202 *FileStart = (FileEntry->HwDescription.BlockEnd + 1) * BlockSize;\r
203 }\r
204 }\r
205 // See if there's space after the last file\r
206 if ((((Media->LastBlock + 1) * BlockSize) - *FileStart) >= FileSize) {\r
207 return EFI_SUCCESS;\r
208 } else {\r
209 return EFI_VOLUME_FULL;\r
210 }\r
211}\r
212\r
213// Free the resources in the file's Region list.\r
214STATIC\r
215VOID\r
216FreeFileRegions (\r
217 IN BOOTMON_FS_FILE *File\r
218 )\r
219{\r
220 LIST_ENTRY *RegionToFlushLink;\r
221 BOOTMON_FS_FILE_REGION *Region;\r
222\r
223 RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);\r
224 while (!IsNull (&File->RegionToFlushLink, RegionToFlushLink)) {\r
225 // Repeatedly remove the first node from the list and free its resources.\r
226 Region = (BOOTMON_FS_FILE_REGION *) RegionToFlushLink;\r
227 RemoveEntryList (RegionToFlushLink);\r
228 FreePool (Region->Buffer);\r
229 FreePool (Region);\r
230\r
231 RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);\r
232 }\r
233}\r
234\r
95204533
RC
235/**\r
236 Flush all modified data associated with a file to a device.\r
237\r
238 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the\r
239 file handle to flush.\r
240\r
241 @retval EFI_SUCCESS The data was flushed.\r
242 @retval EFI_ACCESS_DENIED The file was opened read-only.\r
243 @retval EFI_DEVICE_ERROR The device reported an error.\r
244 @retval EFI_VOLUME_FULL The volume is full.\r
245 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to flush the data.\r
246 @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid.\r
247\r
248**/\r
94e0955d
OM
249EFIAPI\r
250EFI_STATUS\r
251BootMonFsFlushFile (\r
252 IN EFI_FILE_PROTOCOL *This\r
253 )\r
254{\r
255 EFI_STATUS Status;\r
256 BOOTMON_FS_INSTANCE *Instance;\r
95204533
RC
257 EFI_FILE_INFO *Info;\r
258 EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
259 EFI_BLOCK_IO_MEDIA *Media;\r
260 EFI_DISK_IO_PROTOCOL *DiskIo;\r
261 UINTN BlockSize;\r
262 CHAR8 AsciiFileName[MAX_NAME_LENGTH];\r
94e0955d
OM
263 LIST_ENTRY *RegionToFlushLink;\r
264 BOOTMON_FS_FILE *File;\r
265 BOOTMON_FS_FILE *NextFile;\r
266 BOOTMON_FS_FILE_REGION *Region;\r
267 LIST_ENTRY *FileLink;\r
268 UINTN CurrentPhysicalSize;\r
94e0955d
OM
269 UINT64 FileStart;\r
270 UINT64 FileEnd;\r
271 UINT64 RegionStart;\r
272 UINT64 RegionEnd;\r
95204533 273 UINT64 NewDataSize;\r
94e0955d
OM
274 UINT64 NewFileSize;\r
275 UINT64 EndOfAppendSpace;\r
276 BOOLEAN HasSpace;\r
94e0955d 277\r
95204533
RC
278 if (This == NULL) {\r
279 return EFI_INVALID_PARAMETER;\r
280 }\r
94e0955d
OM
281\r
282 File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
95204533 283 if (File->Info == NULL) {\r
94e0955d
OM
284 return EFI_INVALID_PARAMETER;\r
285 }\r
286\r
95204533
RC
287 if (File->OpenMode == EFI_FILE_MODE_READ) {\r
288 return EFI_ACCESS_DENIED;\r
94e0955d
OM
289 }\r
290\r
95204533
RC
291 Instance = File->Instance;\r
292 Info = File->Info;\r
293 BlockIo = Instance->BlockIo;\r
294 Media = BlockIo->Media;\r
295 DiskIo = Instance->DiskIo;\r
296 BlockSize = Media->BlockSize;\r
297\r
a5cd3bb0 298 UnicodeStrToAsciiStrS (Info->FileName, AsciiFileName, MAX_NAME_LENGTH);\r
94e0955d
OM
299\r
300 // If the file doesn't exist then find a space for it\r
301 if (File->HwDescription.RegionCount == 0) {\r
95204533
RC
302 Status = BootMonFsFindSpaceForNewFile (\r
303 File,\r
304 Info->FileSize + sizeof (HW_IMAGE_DESCRIPTION),\r
305 &FileStart\r
306 );\r
94e0955d
OM
307 if (EFI_ERROR (Status)) {\r
308 return Status;\r
309 }\r
310 } else {\r
311 FileStart = File->HwDescription.BlockStart * BlockSize;\r
312 }\r
e29771bb
BJ
313 // FileEnd is the current NOR address of the end of the file's data\r
314 FileEnd = FileStart + File->HwDescription.Region[0].Size;\r
94e0955d
OM
315\r
316 for (RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);\r
317 !IsNull (&File->RegionToFlushLink, RegionToFlushLink);\r
318 RegionToFlushLink = GetNextNode (&File->RegionToFlushLink, RegionToFlushLink)\r
319 )\r
320 {\r
321 Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;\r
95204533
RC
322 if (Region->Size == 0) {\r
323 continue;\r
324 }\r
94e0955d
OM
325\r
326 // RegionStart and RegionEnd are the the intended NOR address of the\r
327 // start and end of the region\r
95204533
RC
328 RegionStart = FileStart + Region->Offset;\r
329 RegionEnd = RegionStart + Region->Size;\r
94e0955d
OM
330\r
331 if (RegionEnd < FileEnd) {\r
332 // Handle regions representing edits to existing portions of the file\r
333 // Write the region data straight into the file\r
334 Status = DiskIo->WriteDisk (DiskIo,\r
95204533 335 Media->MediaId,\r
94e0955d
OM
336 RegionStart,\r
337 Region->Size,\r
338 Region->Buffer\r
339 );\r
340 if (EFI_ERROR (Status)) {\r
341 return Status;\r
342 }\r
343 } else {\r
344 // Handle regions representing appends to the file\r
345 //\r
346 // Note: Since seeking past the end of the file with SetPosition() is\r
347 // valid, it's possible there will be a gap between the current end of\r
348 // the file and the beginning of the new region. Since the UEFI spec\r
349 // says nothing about this case (except "a subsequent write would grow\r
350 // the file"), we just leave garbage in the gap.\r
351\r
352 // Check if there is space to append the new region\r
353 HasSpace = FALSE;\r
95204533
RC
354 NewDataSize = RegionEnd - FileStart;\r
355 NewFileSize = NewDataSize + sizeof (HW_IMAGE_DESCRIPTION);\r
94e0955d
OM
356 CurrentPhysicalSize = BootMonFsGetPhysicalSize (File);\r
357 if (NewFileSize <= CurrentPhysicalSize) {\r
358 HasSpace = TRUE;\r
359 } else {\r
360 // Get the File Description for the next file\r
361 FileLink = GetNextNode (&Instance->RootFile->Link, &File->Link);\r
362 if (!IsNull (&Instance->RootFile->Link, FileLink)) {\r
363 NextFile = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);\r
364\r
365 // If there is space between the beginning of the current file and the\r
366 // beginning of the next file then use it\r
367 EndOfAppendSpace = NextFile->HwDescription.BlockStart * BlockSize;\r
368 } else {\r
369 // We are flushing the last file.\r
95204533 370 EndOfAppendSpace = (Media->LastBlock + 1) * BlockSize;\r
94e0955d
OM
371 }\r
372 if (EndOfAppendSpace - FileStart >= NewFileSize) {\r
373 HasSpace = TRUE;\r
374 }\r
375 }\r
376\r
377 if (HasSpace == TRUE) {\r
95204533
RC
378 // Invalidate the current image description of the file if any.\r
379 if (File->HwDescAddress != 0) {\r
380 Status = InvalidateImageDescription (File);\r
381 if (EFI_ERROR (Status)) {\r
382 return Status;\r
383 }\r
384 }\r
385\r
386 // Write the new file data\r
387 Status = DiskIo->WriteDisk (\r
388 DiskIo,\r
389 Media->MediaId,\r
390 RegionStart,\r
391 Region->Size,\r
392 Region->Buffer\r
393 );\r
94e0955d
OM
394 if (EFI_ERROR (Status)) {\r
395 return Status;\r
396 }\r
95204533
RC
397\r
398 Status = WriteFileDescription (File, AsciiFileName, NewDataSize, FileStart);\r
399 if (EFI_ERROR (Status)) {\r
400 return Status;\r
401 }\r
402\r
94e0955d
OM
403 } else {\r
404 // There isn't a space for the file.\r
405 // Options here are to move the file or fragment it. However as files\r
406 // may represent boot images at fixed positions, these options will\r
407 // break booting if the bootloader doesn't use BootMonFs to find the\r
408 // image.\r
409\r
410 return EFI_VOLUME_FULL;\r
411 }\r
412 }\r
413 }\r
414\r
415 FreeFileRegions (File);\r
95204533
RC
416 Info->PhysicalSize = BootMonFsGetPhysicalSize (File);\r
417\r
418 if ((AsciiStrCmp (AsciiFileName, File->HwDescription.Footer.Filename) != 0) ||\r
419 (Info->FileSize != File->HwDescription.Region[0].Size) ) {\r
420 Status = WriteFileDescription (File, AsciiFileName, Info->FileSize, FileStart);\r
421 if (EFI_ERROR (Status)) {\r
422 return Status;\r
423 }\r
424 }\r
94e0955d
OM
425\r
426 // Flush DiskIo Buffers (see UEFI Spec 12.7 - DiskIo buffers are flushed by\r
427 // calling FlushBlocks on the same device's BlockIo).\r
428 BlockIo->FlushBlocks (BlockIo);\r
429\r
95204533 430 return EFI_SUCCESS;\r
94e0955d
OM
431}\r
432\r
433/**\r
95204533 434 Close a specified file handle.\r
94e0955d 435\r
95204533
RC
436 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file\r
437 handle to close.\r
94e0955d 438\r
95204533
RC
439 @retval EFI_SUCCESS The file was closed.\r
440 @retval EFI_INVALID_PARAMETER The parameter "This" is NULL or is not an open\r
441 file handle.\r
94e0955d
OM
442\r
443**/\r
444EFIAPI\r
445EFI_STATUS\r
446BootMonFsCloseFile (\r
447 IN EFI_FILE_PROTOCOL *This\r
448 )\r
449{\r
95204533 450 BOOTMON_FS_FILE *File;\r
94e0955d 451\r
95204533
RC
452 if (This == NULL) {\r
453 return EFI_INVALID_PARAMETER;\r
94e0955d
OM
454 }\r
455\r
95204533
RC
456 File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
457 if (File->Info == NULL) {\r
458 return EFI_INVALID_PARAMETER;\r
94e0955d
OM
459 }\r
460\r
95204533
RC
461 // In the case of a file and not the root directory\r
462 if (This != &File->Instance->RootFile->File) {\r
463 This->Flush (This);\r
464 FreePool (File->Info);\r
465 File->Info = NULL;\r
466 }\r
94e0955d 467\r
95204533 468 return EFI_SUCCESS;\r
94e0955d
OM
469}\r
470\r
471/**\r
fb08c455
RC
472 Open a file on the boot monitor file system.\r
473\r
474 The boot monitor file system does not allow for sub-directories. There is only\r
475 one directory, the root one. On any attempt to create a directory, the function\r
476 returns in error with the EFI_WRITE_PROTECTED error code.\r
477\r
478 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is\r
479 the file handle to source location.\r
480 @param[out] NewHandle A pointer to the location to return the opened\r
481 handle for the new file.\r
482 @param[in] FileName The Null-terminated string of the name of the file\r
483 to be opened.\r
484 @param[in] OpenMode The mode to open the file : Read or Read/Write or\r
485 Read/Write/Create\r
486 @param[in] Attributes Attributes of the file in case of a file creation\r
487\r
488 @retval EFI_SUCCESS The file was open.\r
489 @retval EFI_NOT_FOUND The specified file could not be found or the specified\r
490 directory in which to create a file could not be found.\r
491 @retval EFI_DEVICE_ERROR The device reported an error.\r
492 @retval EFI_WRITE_PROTECTED Attempt to create a directory. This is not possible\r
493 with the Boot Monitor file system.\r
494 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.\r
495 @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid.\r
94e0955d
OM
496\r
497**/\r
498EFIAPI\r
499EFI_STATUS\r
500BootMonFsOpenFile (\r
fb08c455
RC
501 IN EFI_FILE_PROTOCOL *This,\r
502 OUT EFI_FILE_PROTOCOL **NewHandle,\r
503 IN CHAR16 *FileName,\r
504 IN UINT64 OpenMode,\r
505 IN UINT64 Attributes\r
94e0955d
OM
506 )\r
507{\r
94e0955d 508 EFI_STATUS Status;\r
fb08c455
RC
509 BOOTMON_FS_FILE *Directory;\r
510 BOOTMON_FS_FILE *File;\r
511 BOOTMON_FS_INSTANCE *Instance;\r
512 CHAR8 *Buf;\r
513 CHAR16 *Path;\r
514 CHAR16 *Separator;\r
515 CHAR8 *AsciiFileName;\r
95204533 516 EFI_FILE_INFO *Info;\r
a5cd3bb0 517 UINTN AsciiFileNameSize;\r
fb08c455
RC
518\r
519 if (This == NULL) {\r
520 return EFI_INVALID_PARAMETER;\r
521 }\r
94e0955d 522\r
95204533
RC
523 Directory = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
524 if (Directory->Info == NULL) {\r
525 return EFI_INVALID_PARAMETER;\r
526 }\r
527\r
94e0955d
OM
528 if ((FileName == NULL) || (NewHandle == NULL)) {\r
529 return EFI_INVALID_PARAMETER;\r
530 }\r
531\r
fb08c455 532 //\r
94e0955d 533 // The only valid modes are read, read/write, and read/write/create\r
fb08c455
RC
534 //\r
535 if ( (OpenMode != EFI_FILE_MODE_READ) &&\r
536 (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE)) &&\r
537 (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE)) ) {\r
94e0955d
OM
538 return EFI_INVALID_PARAMETER;\r
539 }\r
540\r
94e0955d
OM
541 Instance = Directory->Instance;\r
542\r
fb08c455
RC
543 //\r
544 // If the instance has not been initialized yet then do it ...\r
545 //\r
94e0955d
OM
546 if (!Instance->Initialized) {\r
547 Status = BootMonFsInitialize (Instance);\r
548 if (EFI_ERROR (Status)) {\r
549 return Status;\r
550 }\r
551 }\r
552\r
fb08c455
RC
553 //\r
554 // Copy the file path to be able to work on it. We do not want to\r
555 // modify the input file name string "FileName".\r
556 //\r
557 Buf = AllocateCopyPool (StrSize (FileName), FileName);\r
558 if (Buf == NULL) {\r
559 return EFI_OUT_OF_RESOURCES;\r
560 }\r
561 Path = (CHAR16*)Buf;\r
562 AsciiFileName = NULL;\r
95204533 563 Info = NULL;\r
fb08c455
RC
564\r
565 //\r
566 // Handle single periods, double periods and convert forward slashes '/'\r
567 // to backward '\' ones. Does not handle a '.' at the beginning of the\r
568 // path for the time being.\r
569 //\r
570 if (PathCleanUpDirectories (Path) == NULL) {\r
571 Status = EFI_INVALID_PARAMETER;\r
572 goto Error;\r
573 }\r
574\r
575 //\r
576 // Detect if the first component of the path refers to a directory.\r
577 // This is done to return the correct error code when trying to\r
578 // access or create a directory other than the root directory.\r
579 //\r
580\r
581 //\r
582 // Search for the '\\' sequence and if found return in error\r
583 // with the EFI_INVALID_PARAMETER error code. ere in the path.\r
584 //\r
585 if (StrStr (Path, L"\\\\") != NULL) {\r
586 Status = EFI_INVALID_PARAMETER;\r
587 goto Error;\r
588 }\r
589 //\r
590 // Get rid of the leading '\' if any.\r
591 //\r
592 Path += (Path[0] == L'\\');\r
593\r
594 //\r
595 // Look for a '\' in the file path. If one is found then\r
596 // the first component of the path refers to a directory\r
597 // that is not the root directory.\r
598 //\r
599 Separator = StrStr (Path, L"\\");\r
600 if (Separator != NULL) {\r
601 //\r
602 // In the case '<dir name>\' and a creation, return\r
603 // EFI_WRITE_PROTECTED if this is for a directory\r
604 // creation, EFI_INVALID_PARAMETER otherwise.\r
605 //\r
606 if ((*(Separator + 1) == '\0') && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) {\r
607 if (Attributes & EFI_FILE_DIRECTORY) {\r
608 Status = EFI_WRITE_PROTECTED;\r
609 } else {\r
610 Status = EFI_INVALID_PARAMETER;\r
611 }\r
612 } else {\r
613 //\r
614 // Attempt to open a file or a directory that is not in the\r
615 // root directory or to open without creation a directory\r
616 // located in the root directory, returns EFI_NOT_FOUND.\r
617 //\r
618 Status = EFI_NOT_FOUND;\r
619 }\r
620 goto Error;\r
621 }\r
622\r
623 //\r
94e0955d 624 // BootMonFs interface requires ASCII filenames\r
fb08c455 625 //\r
a5cd3bb0
AB
626 AsciiFileNameSize = StrLen (Path) + 1;\r
627 if (AsciiFileNameSize > MAX_NAME_LENGTH) {\r
628 AsciiFileNameSize = MAX_NAME_LENGTH;\r
629 }\r
630 AsciiFileName = AllocatePool (AsciiFileNameSize);\r
94e0955d 631 if (AsciiFileName == NULL) {\r
fb08c455
RC
632 Status = EFI_OUT_OF_RESOURCES;\r
633 goto Error;\r
634 }\r
a5cd3bb0 635 UnicodeStrToAsciiStrS (Path, AsciiFileName, AsciiFileNameSize);\r
94e0955d 636\r
fb08c455
RC
637 if ((AsciiFileName[0] == '\0') ||\r
638 (AsciiFileName[0] == '.' ) ) {\r
94e0955d 639 //\r
fb08c455 640 // Opening the root directory\r
94e0955d
OM
641 //\r
642\r
643 *NewHandle = &Instance->RootFile->File;\r
644 Instance->RootFile->Position = 0;\r
645 Status = EFI_SUCCESS;\r
646 } else {\r
fb08c455
RC
647\r
648 if ((OpenMode & EFI_FILE_MODE_CREATE) &&\r
649 (Attributes & EFI_FILE_DIRECTORY) ) {\r
650 Status = EFI_WRITE_PROTECTED;\r
651 goto Error;\r
652 }\r
653\r
95204533
RC
654 //\r
655 // Allocate a buffer to store the characteristics of the file while the\r
656 // file is open. We allocate the maximum size to not have to reallocate\r
657 // if the file name is changed.\r
658 //\r
659 Info = AllocateZeroPool (\r
660 SIZE_OF_EFI_FILE_INFO + (sizeof (CHAR16) * MAX_NAME_LENGTH));\r
661 if (Info == NULL) {\r
662 Status = EFI_OUT_OF_RESOURCES;\r
663 goto Error;\r
664 }\r
665\r
94e0955d 666 //\r
fb08c455 667 // Open or create a file in the root directory.\r
94e0955d
OM
668 //\r
669\r
94e0955d
OM
670 Status = BootMonGetFileFromAsciiFileName (Instance, AsciiFileName, &File);\r
671 if (Status == EFI_NOT_FOUND) {\r
fb08c455
RC
672 if ((OpenMode & EFI_FILE_MODE_CREATE) == 0) {\r
673 goto Error;\r
94e0955d 674 }\r
fb08c455 675\r
95204533
RC
676 Status = BootMonFsCreateFile (Instance, &File);\r
677 if (EFI_ERROR (Status)) {\r
678 goto Error;\r
fb08c455 679 }\r
95204533
RC
680 InsertHeadList (&Instance->RootFile->Link, &File->Link);\r
681 Info->Attribute = Attributes;\r
fb08c455
RC
682 } else {\r
683 //\r
95204533 684 // File already open, not supported yet.\r
fb08c455 685 //\r
95204533
RC
686 if (File->Info != NULL) {\r
687 Status = EFI_UNSUPPORTED;\r
688 goto Error;\r
689 }\r
94e0955d 690 }\r
95204533
RC
691\r
692 Info->FileSize = BootMonFsGetImageLength (File);\r
693 Info->PhysicalSize = BootMonFsGetPhysicalSize (File);\r
a5cd3bb0 694 AsciiStrToUnicodeStrS (AsciiFileName, Info->FileName, MAX_NAME_LENGTH);\r
95204533
RC
695\r
696 File->Info = Info;\r
697 Info = NULL;\r
698 File->Position = 0;\r
699 File->OpenMode = OpenMode;\r
700\r
701 *NewHandle = &File->File;\r
94e0955d
OM
702 }\r
703\r
fb08c455
RC
704Error:\r
705\r
706 FreePool (Buf);\r
707 if (AsciiFileName != NULL) {\r
708 FreePool (AsciiFileName);\r
709 }\r
95204533
RC
710 if (Info != NULL) {\r
711 FreePool (Info);\r
712 }\r
94e0955d
OM
713\r
714 return Status;\r
715}\r
716\r
717// Delete() for the root directory's EFI_FILE_PROTOCOL instance\r
718EFIAPI\r
719EFI_STATUS\r
720BootMonFsDeleteFail (\r
721 IN EFI_FILE_PROTOCOL *This\r
722 )\r
723{\r
724 This->Close(This);\r
725 // You can't delete the root directory\r
726 return EFI_WARN_DELETE_FAILURE;\r
727}\r
95204533
RC
728\r
729/**\r
730 Close and delete a file from the boot monitor file system.\r
731\r
732 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file\r
733 handle to delete.\r
734\r
735 @retval EFI_SUCCESS The file was closed and deleted.\r
736 @retval EFI_INVALID_PARAMETER The parameter "This" is NULL or is not an open\r
737 file handle.\r
738 @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not deleted.\r
739\r
740**/\r
94e0955d
OM
741EFIAPI\r
742EFI_STATUS\r
743BootMonFsDelete (\r
744 IN EFI_FILE_PROTOCOL *This\r
745 )\r
746{\r
747 EFI_STATUS Status;\r
748 BOOTMON_FS_FILE *File;\r
749 LIST_ENTRY *RegionToFlushLink;\r
750 BOOTMON_FS_FILE_REGION *Region;\r
94e0955d 751\r
95204533
RC
752 if (This == NULL) {\r
753 return EFI_INVALID_PARAMETER;\r
94e0955d
OM
754 }\r
755\r
95204533
RC
756 File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
757 if (File->Info == NULL) {\r
758 return EFI_INVALID_PARAMETER;\r
759 }\r
94e0955d 760\r
95204533 761 if (!IsListEmpty (&File->RegionToFlushLink)) {\r
94e0955d
OM
762 // Free the entries from the Buffer List\r
763 RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);\r
764 do {\r
765 Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;\r
766\r
95204533
RC
767 //\r
768 // Get next element of the list before deleting the region description\r
769 // that contain the LIST_ENTRY structure.\r
770 //\r
94e0955d
OM
771 RegionToFlushLink = RemoveEntryList (RegionToFlushLink);\r
772\r
773 // Free the buffers\r
774 FreePool (Region->Buffer);\r
775 FreePool (Region);\r
776 } while (!IsListEmpty (&File->RegionToFlushLink));\r
777 }\r
778\r
779 // If (RegionCount is greater than 0) then the file already exists\r
780 if (File->HwDescription.RegionCount > 0) {\r
94e0955d 781 // Invalidate the last Block\r
79e12331 782 Status = InvalidateImageDescription (File);\r
94e0955d 783 ASSERT_EFI_ERROR (Status);\r
95204533
RC
784 if (EFI_ERROR (Status)) {\r
785 return EFI_WARN_DELETE_FAILURE;\r
786 }\r
94e0955d
OM
787 }\r
788\r
789 // Remove the entry from the list\r
790 RemoveEntryList (&File->Link);\r
95204533 791 FreePool (File->Info);\r
94e0955d 792 FreePool (File);\r
94e0955d 793\r
95204533
RC
794 return EFI_SUCCESS;\r
795}\r