]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsOpenClose.c
Fix IA32 build failure: Use MultU64x32 for 64bit * 32bit.
[mirror_edk2.git] / ArmPlatformPkg / FileSystem / BootMonFs / BootMonFsOpenClose.c
CommitLineData
94e0955d
OM
1/** @file\r
2*\r
3* Copyright (c) 2012-2014, ARM Limited. All rights reserved.\r
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
fb08c455 15#include <Library/PathLib.h>\r
94e0955d
OM
16#include "BootMonFsInternal.h"\r
17\r
18// Clear a file's image description on storage media:\r
19// UEFI allows you to seek past the end of a file, a subsequent write will grow\r
20// the file. It does not specify how space between the former end of the file\r
21// and the beginning of the write should be filled. It's therefore possible that\r
22// BootMonFs metadata, that comes after the end of a file, could be left there\r
23// and wrongly detected by BootMonFsImageInBlock.\r
24STATIC\r
25EFI_STATUS\r
26InvalidateImageDescription (\r
27 IN BOOTMON_FS_FILE *File\r
28 )\r
29{\r
30 EFI_DISK_IO_PROTOCOL *DiskIo;\r
31 EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
32 UINT32 MediaId;\r
94e0955d
OM
33 VOID *Buffer;\r
34 EFI_STATUS Status;\r
94e0955d
OM
35\r
36 DiskIo = File->Instance->DiskIo;\r
37 BlockIo = File->Instance->BlockIo;\r
38 MediaId = BlockIo->Media->MediaId;\r
94e0955d
OM
39\r
40 Buffer = AllocateZeroPool (sizeof (HW_IMAGE_DESCRIPTION));\r
41\r
95204533
RC
42 if (Buffer == NULL) {\r
43 return EFI_OUT_OF_RESOURCES;\r
44 }\r
45\r
94e0955d
OM
46 Status = DiskIo->WriteDisk (DiskIo,\r
47 MediaId,\r
79e12331 48 File->HwDescAddress,\r
94e0955d
OM
49 sizeof (HW_IMAGE_DESCRIPTION),\r
50 Buffer\r
51 );\r
52\r
53 FreePool(Buffer);\r
54\r
55 return Status;\r
56}\r
57\r
95204533
RC
58/**\r
59 Write the description of a file to storage media.\r
60\r
61 This function uses DiskIo to write to the media, so call BlockIo->FlushBlocks()\r
62 after calling it to ensure the data are written on the media.\r
63\r
64 @param[in] File Description of the file whose description on the\r
65 storage media has to be updated.\r
66 @param[in] FileName Name of the file. Its length is assumed to be\r
67 lower than MAX_NAME_LENGTH.\r
68 @param[in] DataSize Number of data bytes of the file.\r
69 @param[in] FileStart File's starting position on media. FileStart must\r
70 be aligned to the media's block size.\r
71\r
72 @retval EFI_WRITE_PROTECTED The device cannot be written to.\r
73 @retval EFI_DEVICE_ERROR The device reported an error while performing\r
74 the write operation.\r
75\r
76**/\r
94e0955d
OM
77STATIC\r
78EFI_STATUS\r
95204533
RC
79WriteFileDescription (\r
80 IN BOOTMON_FS_FILE *File,\r
81 IN CHAR8 *FileName,\r
82 IN UINT32 DataSize,\r
83 IN UINT64 FileStart\r
94e0955d
OM
84 )\r
85{\r
95204533
RC
86 EFI_STATUS Status;\r
87 EFI_DISK_IO_PROTOCOL *DiskIo;\r
88 UINTN BlockSize;\r
89 UINT32 FileSize;\r
90 HW_IMAGE_DESCRIPTION *Description;\r
94e0955d 91\r
95204533 92 DiskIo = File->Instance->DiskIo;\r
94e0955d 93 BlockSize = File->Instance->BlockIo->Media->BlockSize;\r
94e0955d
OM
94 ASSERT (FileStart % BlockSize == 0);\r
95\r
94e0955d 96 //\r
95204533 97 // Construct the file description\r
94e0955d 98 //\r
95204533
RC
99\r
100 FileSize = DataSize + sizeof (HW_IMAGE_DESCRIPTION);\r
94e0955d
OM
101 Description = &File->HwDescription;\r
102 Description->Attributes = 1;\r
103 Description->BlockStart = FileStart / BlockSize;\r
95204533
RC
104 Description->BlockEnd = Description->BlockStart + (FileSize / BlockSize);\r
105 AsciiStrCpy (Description->Footer.Filename, FileName);\r
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
298 UnicodeStrToAsciiStr (Info->FileName, AsciiFileName);\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
fb08c455
RC
517\r
518 if (This == NULL) {\r
519 return EFI_INVALID_PARAMETER;\r
520 }\r
94e0955d 521\r
95204533
RC
522 Directory = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
523 if (Directory->Info == NULL) {\r
524 return EFI_INVALID_PARAMETER;\r
525 }\r
526\r
94e0955d
OM
527 if ((FileName == NULL) || (NewHandle == NULL)) {\r
528 return EFI_INVALID_PARAMETER;\r
529 }\r
530\r
fb08c455 531 //\r
94e0955d 532 // The only valid modes are read, read/write, and read/write/create\r
fb08c455
RC
533 //\r
534 if ( (OpenMode != EFI_FILE_MODE_READ) &&\r
535 (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE)) &&\r
536 (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE)) ) {\r
94e0955d
OM
537 return EFI_INVALID_PARAMETER;\r
538 }\r
539\r
94e0955d
OM
540 Instance = Directory->Instance;\r
541\r
fb08c455
RC
542 //\r
543 // If the instance has not been initialized yet then do it ...\r
544 //\r
94e0955d
OM
545 if (!Instance->Initialized) {\r
546 Status = BootMonFsInitialize (Instance);\r
547 if (EFI_ERROR (Status)) {\r
548 return Status;\r
549 }\r
550 }\r
551\r
fb08c455
RC
552 //\r
553 // Copy the file path to be able to work on it. We do not want to\r
554 // modify the input file name string "FileName".\r
555 //\r
556 Buf = AllocateCopyPool (StrSize (FileName), FileName);\r
557 if (Buf == NULL) {\r
558 return EFI_OUT_OF_RESOURCES;\r
559 }\r
560 Path = (CHAR16*)Buf;\r
561 AsciiFileName = NULL;\r
95204533 562 Info = NULL;\r
fb08c455
RC
563\r
564 //\r
565 // Handle single periods, double periods and convert forward slashes '/'\r
566 // to backward '\' ones. Does not handle a '.' at the beginning of the\r
567 // path for the time being.\r
568 //\r
569 if (PathCleanUpDirectories (Path) == NULL) {\r
570 Status = EFI_INVALID_PARAMETER;\r
571 goto Error;\r
572 }\r
573\r
574 //\r
575 // Detect if the first component of the path refers to a directory.\r
576 // This is done to return the correct error code when trying to\r
577 // access or create a directory other than the root directory.\r
578 //\r
579\r
580 //\r
581 // Search for the '\\' sequence and if found return in error\r
582 // with the EFI_INVALID_PARAMETER error code. ere in the path.\r
583 //\r
584 if (StrStr (Path, L"\\\\") != NULL) {\r
585 Status = EFI_INVALID_PARAMETER;\r
586 goto Error;\r
587 }\r
588 //\r
589 // Get rid of the leading '\' if any.\r
590 //\r
591 Path += (Path[0] == L'\\');\r
592\r
593 //\r
594 // Look for a '\' in the file path. If one is found then\r
595 // the first component of the path refers to a directory\r
596 // that is not the root directory.\r
597 //\r
598 Separator = StrStr (Path, L"\\");\r
599 if (Separator != NULL) {\r
600 //\r
601 // In the case '<dir name>\' and a creation, return\r
602 // EFI_WRITE_PROTECTED if this is for a directory\r
603 // creation, EFI_INVALID_PARAMETER otherwise.\r
604 //\r
605 if ((*(Separator + 1) == '\0') && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) {\r
606 if (Attributes & EFI_FILE_DIRECTORY) {\r
607 Status = EFI_WRITE_PROTECTED;\r
608 } else {\r
609 Status = EFI_INVALID_PARAMETER;\r
610 }\r
611 } else {\r
612 //\r
613 // Attempt to open a file or a directory that is not in the\r
614 // root directory or to open without creation a directory\r
615 // located in the root directory, returns EFI_NOT_FOUND.\r
616 //\r
617 Status = EFI_NOT_FOUND;\r
618 }\r
619 goto Error;\r
620 }\r
621\r
622 //\r
94e0955d 623 // BootMonFs interface requires ASCII filenames\r
fb08c455
RC
624 //\r
625 AsciiFileName = AllocatePool (StrLen (Path) + 1);\r
94e0955d 626 if (AsciiFileName == NULL) {\r
fb08c455
RC
627 Status = EFI_OUT_OF_RESOURCES;\r
628 goto Error;\r
629 }\r
630 UnicodeStrToAsciiStr (Path, AsciiFileName);\r
631 if (AsciiStrSize (AsciiFileName) > MAX_NAME_LENGTH) {\r
632 AsciiFileName[MAX_NAME_LENGTH - 1] = '\0';\r
94e0955d 633 }\r
94e0955d 634\r
fb08c455
RC
635 if ((AsciiFileName[0] == '\0') ||\r
636 (AsciiFileName[0] == '.' ) ) {\r
94e0955d 637 //\r
fb08c455 638 // Opening the root directory\r
94e0955d
OM
639 //\r
640\r
641 *NewHandle = &Instance->RootFile->File;\r
642 Instance->RootFile->Position = 0;\r
643 Status = EFI_SUCCESS;\r
644 } else {\r
fb08c455
RC
645\r
646 if ((OpenMode & EFI_FILE_MODE_CREATE) &&\r
647 (Attributes & EFI_FILE_DIRECTORY) ) {\r
648 Status = EFI_WRITE_PROTECTED;\r
649 goto Error;\r
650 }\r
651\r
95204533
RC
652 //\r
653 // Allocate a buffer to store the characteristics of the file while the\r
654 // file is open. We allocate the maximum size to not have to reallocate\r
655 // if the file name is changed.\r
656 //\r
657 Info = AllocateZeroPool (\r
658 SIZE_OF_EFI_FILE_INFO + (sizeof (CHAR16) * MAX_NAME_LENGTH));\r
659 if (Info == NULL) {\r
660 Status = EFI_OUT_OF_RESOURCES;\r
661 goto Error;\r
662 }\r
663\r
94e0955d 664 //\r
fb08c455 665 // Open or create a file in the root directory.\r
94e0955d
OM
666 //\r
667\r
94e0955d
OM
668 Status = BootMonGetFileFromAsciiFileName (Instance, AsciiFileName, &File);\r
669 if (Status == EFI_NOT_FOUND) {\r
fb08c455
RC
670 if ((OpenMode & EFI_FILE_MODE_CREATE) == 0) {\r
671 goto Error;\r
94e0955d 672 }\r
fb08c455 673\r
95204533
RC
674 Status = BootMonFsCreateFile (Instance, &File);\r
675 if (EFI_ERROR (Status)) {\r
676 goto Error;\r
fb08c455 677 }\r
95204533
RC
678 InsertHeadList (&Instance->RootFile->Link, &File->Link);\r
679 Info->Attribute = Attributes;\r
fb08c455
RC
680 } else {\r
681 //\r
95204533 682 // File already open, not supported yet.\r
fb08c455 683 //\r
95204533
RC
684 if (File->Info != NULL) {\r
685 Status = EFI_UNSUPPORTED;\r
686 goto Error;\r
687 }\r
94e0955d 688 }\r
95204533
RC
689\r
690 Info->FileSize = BootMonFsGetImageLength (File);\r
691 Info->PhysicalSize = BootMonFsGetPhysicalSize (File);\r
692 AsciiStrToUnicodeStr (AsciiFileName, Info->FileName);\r
693\r
694 File->Info = Info;\r
695 Info = NULL;\r
696 File->Position = 0;\r
697 File->OpenMode = OpenMode;\r
698\r
699 *NewHandle = &File->File;\r
94e0955d
OM
700 }\r
701\r
fb08c455
RC
702Error:\r
703\r
704 FreePool (Buf);\r
705 if (AsciiFileName != NULL) {\r
706 FreePool (AsciiFileName);\r
707 }\r
95204533
RC
708 if (Info != NULL) {\r
709 FreePool (Info);\r
710 }\r
94e0955d
OM
711\r
712 return Status;\r
713}\r
714\r
715// Delete() for the root directory's EFI_FILE_PROTOCOL instance\r
716EFIAPI\r
717EFI_STATUS\r
718BootMonFsDeleteFail (\r
719 IN EFI_FILE_PROTOCOL *This\r
720 )\r
721{\r
722 This->Close(This);\r
723 // You can't delete the root directory\r
724 return EFI_WARN_DELETE_FAILURE;\r
725}\r
95204533
RC
726\r
727/**\r
728 Close and delete a file from the boot monitor file system.\r
729\r
730 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file\r
731 handle to delete.\r
732\r
733 @retval EFI_SUCCESS The file was closed and deleted.\r
734 @retval EFI_INVALID_PARAMETER The parameter "This" is NULL or is not an open\r
735 file handle.\r
736 @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not deleted.\r
737\r
738**/\r
94e0955d
OM
739EFIAPI\r
740EFI_STATUS\r
741BootMonFsDelete (\r
742 IN EFI_FILE_PROTOCOL *This\r
743 )\r
744{\r
745 EFI_STATUS Status;\r
746 BOOTMON_FS_FILE *File;\r
747 LIST_ENTRY *RegionToFlushLink;\r
748 BOOTMON_FS_FILE_REGION *Region;\r
94e0955d 749\r
95204533
RC
750 if (This == NULL) {\r
751 return EFI_INVALID_PARAMETER;\r
94e0955d
OM
752 }\r
753\r
95204533
RC
754 File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
755 if (File->Info == NULL) {\r
756 return EFI_INVALID_PARAMETER;\r
757 }\r
94e0955d 758\r
95204533 759 if (!IsListEmpty (&File->RegionToFlushLink)) {\r
94e0955d
OM
760 // Free the entries from the Buffer List\r
761 RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);\r
762 do {\r
763 Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;\r
764\r
95204533
RC
765 //\r
766 // Get next element of the list before deleting the region description\r
767 // that contain the LIST_ENTRY structure.\r
768 //\r
94e0955d
OM
769 RegionToFlushLink = RemoveEntryList (RegionToFlushLink);\r
770\r
771 // Free the buffers\r
772 FreePool (Region->Buffer);\r
773 FreePool (Region);\r
774 } while (!IsListEmpty (&File->RegionToFlushLink));\r
775 }\r
776\r
777 // If (RegionCount is greater than 0) then the file already exists\r
778 if (File->HwDescription.RegionCount > 0) {\r
94e0955d 779 // Invalidate the last Block\r
79e12331 780 Status = InvalidateImageDescription (File);\r
94e0955d 781 ASSERT_EFI_ERROR (Status);\r
95204533
RC
782 if (EFI_ERROR (Status)) {\r
783 return EFI_WARN_DELETE_FAILURE;\r
784 }\r
94e0955d
OM
785 }\r
786\r
787 // Remove the entry from the list\r
788 RemoveEntryList (&File->Link);\r
95204533 789 FreePool (File->Info);\r
94e0955d 790 FreePool (File);\r
94e0955d 791\r
95204533
RC
792 return EFI_SUCCESS;\r
793}\r