]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPlatformPkg/FileSystem/BootMonFs/BootMonFsDir.c
ArmPlatformPkg/BootMonFs: Fix permission check in SetFileInfo
[mirror_edk2.git] / ArmPlatformPkg / FileSystem / BootMonFs / BootMonFsDir.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
15#include "BootMonFsInternal.h"\r
16\r
17EFIAPI\r
18EFI_STATUS\r
19OpenBootMonFsOpenVolume (\r
20 IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,\r
21 OUT EFI_FILE_PROTOCOL **Root\r
22 )\r
23{\r
24 BOOTMON_FS_INSTANCE *Instance;\r
25\r
26 Instance = BOOTMON_FS_FROM_FS_THIS (This);\r
27 if (Instance == NULL) {\r
28 return EFI_DEVICE_ERROR;\r
29 }\r
30\r
31 *Root = &Instance->RootFile->File;\r
32\r
33 return EFI_SUCCESS;\r
34}\r
35\r
36UINT32\r
37BootMonFsGetImageLength (\r
38 IN BOOTMON_FS_FILE *File\r
39 )\r
40{\r
41 UINT32 Index;\r
42 UINT32 FileSize;\r
43 LIST_ENTRY *RegionToFlushLink;\r
44 BOOTMON_FS_FILE_REGION *Region;\r
45\r
46 FileSize = 0;\r
47\r
48 // Look at all Flash areas to determine file size\r
49 for (Index = 0; Index < HW_IMAGE_DESCRIPTION_REGION_MAX; Index++) {\r
50 FileSize += File->HwDescription.Region[Index].Size;\r
51 }\r
52\r
53 // Add the regions that have not been flushed yet\r
54 for (RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);\r
55 !IsNull (&File->RegionToFlushLink, RegionToFlushLink);\r
56 RegionToFlushLink = GetNextNode (&File->RegionToFlushLink, RegionToFlushLink)\r
57 )\r
58 {\r
59 Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;\r
60 if (Region->Offset + Region->Size > FileSize) {\r
61 FileSize += Region->Offset + Region->Size;\r
62 }\r
63 }\r
64\r
65 return FileSize;\r
66}\r
67\r
68UINTN\r
69BootMonFsGetPhysicalSize (\r
70 IN BOOTMON_FS_FILE* File\r
71 )\r
72{\r
73 // Return 0 for files that haven't yet been flushed to media\r
74 if (File->HwDescription.RegionCount == 0) {\r
75 return 0;\r
76 }\r
77\r
78 return ((File->HwDescription.BlockEnd - File->HwDescription.BlockStart) + 1 )\r
79 * File->Instance->Media->BlockSize;\r
80}\r
81\r
82EFIAPI\r
83EFI_STATUS\r
84BootMonFsSetDirPosition (\r
85 IN EFI_FILE_PROTOCOL *This,\r
86 IN UINT64 Position\r
87 )\r
88{\r
89 BOOTMON_FS_FILE *File;\r
90\r
91 File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
92 if (File == NULL) {\r
93 return EFI_INVALID_PARAMETER;\r
94 }\r
95\r
96 // UEFI Spec section 12.5:\r
97 // "The seek request for nonzero is not valid on open directories."\r
98 if (Position != 0) {\r
99 return EFI_UNSUPPORTED;\r
100 }\r
101 File->Position = Position;\r
102\r
103 return EFI_SUCCESS;\r
104}\r
105\r
106EFI_STATUS\r
107BootMonFsOpenDirectory (\r
108 OUT EFI_FILE_PROTOCOL **NewHandle,\r
109 IN CHAR16 *FileName,\r
110 IN BOOTMON_FS_INSTANCE *Volume\r
111 )\r
112{\r
113 ASSERT(0);\r
114\r
115 return EFI_UNSUPPORTED;\r
116}\r
117EFI_STATUS\r
118GetFileSystemVolumeLabelInfo (\r
119 IN BOOTMON_FS_INSTANCE *Instance,\r
120 IN OUT UINTN *BufferSize,\r
121 OUT VOID *Buffer\r
122 )\r
123{\r
124 UINTN Size;\r
125 EFI_FILE_SYSTEM_VOLUME_LABEL *Label;\r
126 EFI_STATUS Status;\r
127\r
128 Label = Buffer;\r
129\r
130 // Value returned by StrSize includes null terminator.\r
131 Size = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL\r
132 + StrSize (Instance->FsInfo.VolumeLabel);\r
133\r
134 if (*BufferSize >= Size) {\r
135 CopyMem (&Label->VolumeLabel, &Instance->FsInfo.VolumeLabel, Size);\r
136 Status = EFI_SUCCESS;\r
137 } else {\r
138 Status = EFI_BUFFER_TOO_SMALL;\r
139 }\r
140 *BufferSize = Size;\r
141 return Status;\r
142}\r
143\r
144// Helper function that calculates a rough "free space" by:\r
145// - Taking the media size\r
146// - Subtracting the sum of all file sizes\r
147// - Subtracting the block size times the number of files\r
148// (To account for the blocks containing the HW_IMAGE_INFO\r
149STATIC\r
150UINT64\r
151ComputeFreeSpace (\r
152 IN BOOTMON_FS_INSTANCE *Instance\r
153 )\r
154{\r
155 LIST_ENTRY *FileLink;\r
156 UINT64 FileSizeSum;\r
157 UINT64 MediaSize;\r
158 UINTN NumFiles;\r
159 EFI_BLOCK_IO_MEDIA *Media;\r
160 BOOTMON_FS_FILE *File;\r
161\r
162 Media = Instance->BlockIo->Media;\r
163 MediaSize = Media->BlockSize * (Media->LastBlock + 1);\r
164\r
165 NumFiles = 0;\r
166 FileSizeSum = 0;\r
167 for (FileLink = GetFirstNode (&Instance->RootFile->Link);\r
168 !IsNull (&Instance->RootFile->Link, FileLink);\r
169 FileLink = GetNextNode (&Instance->RootFile->Link, FileLink)\r
170 )\r
171 {\r
172 File = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);\r
173 FileSizeSum += BootMonFsGetImageLength (File);\r
174\r
175 NumFiles++;\r
176 }\r
177\r
178 return MediaSize - (FileSizeSum + (Media->BlockSize + NumFiles));\r
179}\r
180\r
181EFI_STATUS\r
182GetFilesystemInfo (\r
183 IN BOOTMON_FS_INSTANCE *Instance,\r
184 IN OUT UINTN *BufferSize,\r
185 OUT VOID *Buffer\r
186 )\r
187{\r
188 EFI_STATUS Status;\r
189\r
190 if (*BufferSize >= Instance->FsInfo.Size) {\r
191 Instance->FsInfo.FreeSpace = ComputeFreeSpace (Instance);\r
192 CopyMem (Buffer, &Instance->FsInfo, Instance->FsInfo.Size);\r
193 Status = EFI_SUCCESS;\r
194 } else {\r
195 Status = EFI_BUFFER_TOO_SMALL;\r
196 }\r
197\r
198 *BufferSize = Instance->FsInfo.Size;\r
199 return Status;\r
200}\r
201\r
202EFI_STATUS\r
203GetFileInfo (\r
204 IN BOOTMON_FS_INSTANCE *Instance,\r
205 IN BOOTMON_FS_FILE *File,\r
206 IN OUT UINTN *BufferSize,\r
207 OUT VOID *Buffer\r
208 )\r
209{\r
210 EFI_FILE_INFO *Info;\r
211 UINTN ResultSize;\r
212 UINTN NameSize;\r
213 UINTN Index;\r
214\r
215 if (File == Instance->RootFile) {\r
216 NameSize = 0;\r
217 ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof (CHAR16);\r
218 } else {\r
219 NameSize = AsciiStrLen (File->HwDescription.Footer.Filename) + 1;\r
220 ResultSize = SIZE_OF_EFI_FILE_INFO + (NameSize * sizeof (CHAR16));\r
221 }\r
222\r
223 if (*BufferSize < ResultSize) {\r
224 *BufferSize = ResultSize;\r
225 return EFI_BUFFER_TOO_SMALL;\r
226 }\r
227\r
228 Info = Buffer;\r
229\r
230 // Zero out the structure\r
231 ZeroMem (Info, ResultSize);\r
232\r
233 // Fill in the structure\r
234 Info->Size = ResultSize;\r
235\r
236 if (File == Instance->RootFile) {\r
237 Info->Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;\r
238 Info->FileName[0] = L'\0';\r
239 } else {\r
240 Info->FileSize = BootMonFsGetImageLength (File);\r
241 Info->PhysicalSize = BootMonFsGetPhysicalSize (File);\r
242\r
243 for (Index = 0; Index < NameSize; Index++) {\r
244 Info->FileName[Index] = File->HwDescription.Footer.Filename[Index];\r
245 }\r
246 }\r
247\r
248 *BufferSize = ResultSize;\r
249\r
250 return EFI_SUCCESS;\r
251}\r
252\r
253STATIC\r
254EFI_STATUS\r
255SetFileName (\r
256 IN BOOTMON_FS_FILE *File,\r
257 IN CHAR16 *FileNameUnicode\r
258 )\r
259{\r
260 CHAR8 *FileNameAscii;\r
261 UINT16 SavedChar;\r
262 UINTN FileNameSize;\r
263 BOOTMON_FS_FILE *SameFile;\r
264 EFI_STATUS Status;\r
265\r
266 // EFI Shell inserts '\' in front of the filename that must be stripped\r
267 if (FileNameUnicode[0] == L'\\') {\r
268 FileNameUnicode++;\r
269 }\r
270 //\r
271 // Convert Unicode into Ascii\r
272 //\r
273 SavedChar = L'\0';\r
274 FileNameSize = StrLen (FileNameUnicode) + 1;\r
275 FileNameAscii = AllocatePool (FileNameSize * sizeof (CHAR8));\r
276 if (FileNameAscii == NULL) {\r
277 return EFI_OUT_OF_RESOURCES;\r
278 }\r
279 // If Unicode string is too long then truncate it.\r
280 if (FileNameSize > MAX_NAME_LENGTH) {\r
281 SavedChar = FileNameUnicode[MAX_NAME_LENGTH - 1];\r
282 FileNameUnicode[MAX_NAME_LENGTH - 1] = L'\0';\r
283 }\r
284 UnicodeStrToAsciiStr (FileNameUnicode, FileNameAscii);\r
285 // If the unicode string was truncated then restore its original content.\r
286 if (SavedChar != L'\0') {\r
287 FileNameUnicode[MAX_NAME_LENGTH - 1] = SavedChar;\r
288 }\r
289\r
290 // If we're changing the file name\r
35d3b52d
BJ
291 if (AsciiStrCmp (FileNameAscii, File->HwDescription.Footer.Filename) == 0) {\r
292 // No change to filename.\r
293 Status = EFI_SUCCESS;\r
294 } else if (!(File->OpenMode & EFI_FILE_MODE_WRITE)) {\r
295 // You can only change the filename if you open the file for write.\r
296 Status = EFI_ACCESS_DENIED;\r
297 } else if (BootMonGetFileFromAsciiFileName (\r
298 File->Instance,\r
299 File->HwDescription.Footer.Filename,\r
300 &SameFile) != EFI_NOT_FOUND) {\r
301 // A file with that name already exists.\r
302 Status = EFI_ACCESS_DENIED;\r
94e0955d 303 } else {\r
35d3b52d
BJ
304 // OK, change the filename.\r
305 AsciiStrCpy (FileNameAscii, File->HwDescription.Footer.Filename);\r
94e0955d
OM
306 Status = EFI_SUCCESS;\r
307 }\r
308\r
309 FreePool (FileNameAscii);\r
310 return Status;\r
311}\r
312\r
313// Set the file's size (NB "size", not "physical size"). If the change amounts\r
314// to an increase, simply do a write followed by a flush.\r
315// (This is a helper function for SetFileInfo.)\r
316STATIC\r
317EFI_STATUS\r
318SetFileSize (\r
319 IN BOOTMON_FS_INSTANCE *Instance,\r
320 IN BOOTMON_FS_FILE *BootMonFsFile,\r
35d3b52d 321 IN UINTN NewSize\r
94e0955d
OM
322 )\r
323{\r
324 UINT64 StoredPosition;\r
325 EFI_STATUS Status;\r
326 EFI_FILE_PROTOCOL *File;\r
327 CHAR8 Buffer;\r
328 UINTN BufferSize;\r
35d3b52d
BJ
329 UINT32 OldSize;\r
330\r
331 OldSize = BootMonFsFile->HwDescription.Region[0].Size;\r
332\r
333 if (OldSize == NewSize) {\r
334 return EFI_SUCCESS;\r
335 }\r
94e0955d
OM
336\r
337 Buffer = 0;\r
338 BufferSize = sizeof (Buffer);\r
339\r
340 File = &BootMonFsFile->File;\r
341\r
342 if (!(BootMonFsFile->OpenMode & EFI_FILE_MODE_WRITE)) {\r
343 return EFI_ACCESS_DENIED;\r
344 }\r
345\r
35d3b52d
BJ
346 if (NewSize <= OldSize) {\r
347 OldSize = NewSize;\r
94e0955d
OM
348 } else {\r
349 // Increasing a file's size is potentially complicated as it may require\r
350 // moving the image description on media. The simplest way to do it is to\r
351 // seek past the end of the file (which is valid in UEFI) and perform a\r
352 // Write.\r
353\r
354 // Save position\r
355 Status = File->GetPosition (File, &StoredPosition);\r
356 if (EFI_ERROR (Status)) {\r
357 return Status;\r
358 }\r
359\r
35d3b52d 360 Status = File->SetPosition (File, NewSize - 1);\r
94e0955d
OM
361 if (EFI_ERROR (Status)) {\r
362 return Status;\r
363 }\r
364 Status = File->Write (File, &BufferSize, &Buffer);\r
365 if (EFI_ERROR (Status)) {\r
366 return Status;\r
367 }\r
368\r
369 // Restore saved position\r
35d3b52d 370 Status = File->SetPosition (File, NewSize - 1);\r
94e0955d
OM
371 if (EFI_ERROR (Status)) {\r
372 return Status;\r
373 }\r
374\r
375 Status = File->Flush (File);\r
376 if (EFI_ERROR (Status)) {\r
377 return Status;\r
378 }\r
379 }\r
380 return EFI_SUCCESS;\r
381}\r
382\r
383EFI_STATUS\r
384SetFileInfo (\r
385 IN BOOTMON_FS_INSTANCE *Instance,\r
386 IN BOOTMON_FS_FILE *File,\r
387 IN UINTN BufferSize,\r
388 IN EFI_FILE_INFO *Info\r
389 )\r
390{\r
391 EFI_STATUS Status;\r
392 EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
393 UINT8 *DataBuffer;\r
394 UINTN BlockSize;\r
395\r
396 Status = EFI_SUCCESS;\r
397 BlockIo = Instance->BlockIo;\r
398\r
399 // Note that a call to this function on a file opened read-only is only\r
400 // invalid if it actually changes fields, so we don't immediately fail if the\r
401 // OpenMode is wrong.\r
402 // Also note that the only fields supported are filename and size, others are\r
403 // ignored.\r
404\r
405 if (File != Instance->RootFile) {\r
406 if (!(File->OpenMode & EFI_FILE_MODE_WRITE)) {\r
407 return EFI_ACCESS_DENIED;\r
408 }\r
409\r
35d3b52d 410 Status = SetFileName (File, Info->FileName);\r
94e0955d
OM
411 if (EFI_ERROR (Status)) {\r
412 return Status;\r
413 }\r
414\r
415 // Update file size\r
416 Status = SetFileSize (Instance, File, Info->FileSize);\r
417 if (EFI_ERROR (Status)) {\r
418 return Status;\r
419 }\r
420\r
421 //\r
422 // Update the last block\r
423 //\r
424 BlockSize = BlockIo->Media->BlockSize;\r
425 DataBuffer = AllocatePool (BlockSize);\r
426 if (DataBuffer == NULL) {\r
427 return EFI_OUT_OF_RESOURCES;\r
428 }\r
429 Status = BlockIo->ReadBlocks (BlockIo, Instance->Media->MediaId,\r
430 File->HwDescription.BlockEnd, BlockSize, DataBuffer);\r
431 if (EFI_ERROR (Status)) {\r
432 FreePool (DataBuffer);\r
433 return Status;\r
434 }\r
435 CopyMem (DataBuffer + BlockSize - sizeof (File->HwDescription), &File->HwDescription, sizeof (File->HwDescription));\r
436 Status = BlockIo->WriteBlocks (BlockIo, Instance->Media->MediaId,\r
437 File->HwDescription.BlockEnd, BlockSize, DataBuffer);\r
438 FreePool (DataBuffer);\r
439 }\r
440 return Status;\r
441}\r
442\r
443EFIAPI\r
444EFI_STATUS\r
445BootMonFsGetInfo (\r
446 IN EFI_FILE_PROTOCOL *This,\r
447 IN EFI_GUID *InformationType,\r
448 IN OUT UINTN *BufferSize,\r
449 OUT VOID *Buffer\r
450 )\r
451{\r
452 EFI_STATUS Status;\r
453 BOOTMON_FS_FILE *File;\r
454 BOOTMON_FS_INSTANCE *Instance;\r
455\r
456 File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
457 if (File == NULL) {\r
458 return EFI_DEVICE_ERROR;\r
459 }\r
460\r
461 Instance = File->Instance;\r
462\r
463 // If the instance has not been initialized yet then do it ...\r
464 if (!Instance->Initialized) {\r
465 Status = BootMonFsInitialize (Instance);\r
466 } else {\r
467 Status = EFI_SUCCESS;\r
468 }\r
469\r
470 if (!EFI_ERROR (Status)) {\r
471 if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)\r
472 != 0) {\r
473 Status = GetFileSystemVolumeLabelInfo (Instance, BufferSize, Buffer);\r
474 } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) != 0) {\r
475 Status = GetFilesystemInfo (Instance, BufferSize, Buffer);\r
476 } else if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) {\r
477 Status = GetFileInfo (Instance, File, BufferSize, Buffer);\r
478 } else {\r
479 Status = EFI_UNSUPPORTED;\r
480 }\r
481 }\r
482\r
483 return Status;\r
484}\r
485\r
486EFIAPI\r
487EFI_STATUS\r
488BootMonFsSetInfo (\r
489 IN EFI_FILE_PROTOCOL *This,\r
490 IN EFI_GUID *InformationType,\r
491 IN UINTN BufferSize,\r
492 IN VOID *Buffer\r
493 )\r
494{\r
495 EFI_STATUS Status;\r
496 BOOTMON_FS_FILE *File;\r
497 BOOTMON_FS_INSTANCE *Instance;\r
498\r
499 File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
500 if (File == NULL) {\r
501 return EFI_DEVICE_ERROR;\r
502 }\r
503\r
504 Instance = File->Instance;\r
505\r
506 if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) {\r
507 Status = SetFileInfo (Instance, File, BufferSize, (EFI_FILE_INFO *) Buffer);\r
508 } else {\r
509 // The only writable field in the other two information types\r
510 // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the\r
511 // filesystem volume label. This can be retrieved with GetInfo, but it is\r
512 // hard-coded into this driver, not stored on media.\r
513 Status = EFI_UNSUPPORTED;\r
514 }\r
515\r
516 return Status;\r
517}\r
518\r
519EFIAPI\r
520EFI_STATUS\r
521BootMonFsReadDirectory (\r
522 IN EFI_FILE_PROTOCOL *This,\r
523 IN OUT UINTN *BufferSize,\r
524 OUT VOID *Buffer\r
525 )\r
526{\r
527 BOOTMON_FS_INSTANCE *Instance;\r
528 BOOTMON_FS_FILE *RootFile;\r
529 BOOTMON_FS_FILE *File;\r
530 EFI_FILE_INFO *Info;\r
531 UINTN NameSize;\r
532 UINTN ResultSize;\r
533 EFI_STATUS Status;\r
534 UINTN Index;\r
535\r
536 RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
537 if (RootFile == NULL) {\r
538 return EFI_INVALID_PARAMETER;\r
539 }\r
540\r
541 Instance = RootFile->Instance;\r
542 Status = BootMonGetFileFromPosition (Instance, RootFile->Position, &File);\r
543 if (EFI_ERROR (Status)) {\r
544 // No more file\r
545 *BufferSize = 0;\r
546 return EFI_SUCCESS;\r
547 }\r
548\r
549 NameSize = AsciiStrLen (File->HwDescription.Footer.Filename) + 1;\r
550 ResultSize = SIZE_OF_EFI_FILE_INFO + (NameSize * sizeof (CHAR16));\r
551 if (*BufferSize < ResultSize) {\r
552 *BufferSize = ResultSize;\r
553 return EFI_BUFFER_TOO_SMALL;\r
554 }\r
555\r
556 // Zero out the structure\r
557 Info = Buffer;\r
558 ZeroMem (Info, ResultSize);\r
559\r
560 // Fill in the structure\r
561 Info->Size = ResultSize;\r
562 Info->FileSize = BootMonFsGetImageLength (File);\r
563 Info->PhysicalSize = BootMonFsGetPhysicalSize (File);\r
564 for (Index = 0; Index < NameSize; Index++) {\r
565 Info->FileName[Index] = File->HwDescription.Footer.Filename[Index];\r
566 }\r
567\r
568 *BufferSize = ResultSize;\r
569 RootFile->Position++;\r
570\r
571 return EFI_SUCCESS;\r
572}\r
573\r
574EFIAPI\r
575EFI_STATUS\r
576BootMonFsFlushDirectory (\r
577 IN EFI_FILE_PROTOCOL *This\r
578 )\r
579{\r
580 BOOTMON_FS_FILE *RootFile;\r
581 LIST_ENTRY *ListFiles;\r
582 LIST_ENTRY *Link;\r
583 BOOTMON_FS_FILE *File;\r
584\r
585 RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This);\r
586 if (RootFile == NULL) {\r
587 return EFI_INVALID_PARAMETER;\r
588 }\r
589\r
590 ListFiles = &RootFile->Link;\r
591\r
592 if (IsListEmpty (ListFiles)) {\r
593 return EFI_SUCCESS;\r
594 }\r
595\r
596 //\r
597 // Flush all the files that need to be flushed\r
598 //\r
599\r
600 // Go through all the list of files to flush them\r
601 for (Link = GetFirstNode (ListFiles);\r
602 !IsNull (ListFiles, Link);\r
603 Link = GetNextNode (ListFiles, Link)\r
604 )\r
605 {\r
606 File = BOOTMON_FS_FILE_FROM_LINK_THIS (Link);\r
607 File->File.Flush (&File->File);\r
608 }\r
609\r
610 return EFI_SUCCESS;\r
611}\r