1 /** @file
2   This library parses the INI configuration file.
3 
4   The INI file format is:
5     ================
6     [SectionName]
7     EntryName=EntryValue
8     ================
9 
10     Where:
11       1) SectionName is an ASCII string. The valid format is [A-Za-z0-9_]+
12       2) EntryName is an ASCII string. The valid format is [A-Za-z0-9_]+
13       3) EntryValue can be:
14          3.1) an ASCII String. The valid format is [A-Za-z0-9_]+
15          3.2) a GUID. The valid format is xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, where x is [A-Fa-f0-9]
16          3.3) a decimal value. The valid format is [0-9]+
17          3.4) a heximal value. The valid format is 0x[A-Fa-f0-9]+
18       4) '#' or ';' can be used as comment at anywhere.
19       5) TAB(0x20) or SPACE(0x9) can be used as separator.
20       6) LF(\n, 0xA) or CR(\r, 0xD) can be used as line break.
21 
22   Caution: This module requires additional review when modified.
23   This driver will have external input - INI data file.
24 
25   OpenIniFile(), PreProcessDataFile(), ProfileGetSection(), ProfileGetEntry()
26   will receive untrusted input and do basic validation.
27 
28   Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>
29 
30   SPDX-License-Identifier: BSD-2-Clause-Patent
31 
32 **/
33 
34 #include <Uefi.h>
35 #include <Library/BaseLib.h>
36 #include <Library/BaseMemoryLib.h>
37 #include <Library/DebugLib.h>
38 #include <Library/MemoryAllocationLib.h>
39 
40 #define IS_HYPHEN(a)               ((a) == '-')
41 #define IS_NULL(a)                 ((a) == '\0')
42 
43 // This is default allocation. Reallocation will happen if it is not enough.
44 #define MAX_LINE_LENGTH           512
45 
46 typedef struct _INI_SECTION_ITEM SECTION_ITEM;
47 struct _INI_SECTION_ITEM {
48   CHAR8                           *PtrSection;
49   UINTN                           SecNameLen;
50   CHAR8                           *PtrEntry;
51   CHAR8                           *PtrValue;
52   SECTION_ITEM                    *PtrNext;
53 };
54 
55 typedef struct _INI_COMMENT_LINE COMMENT_LINE;
56 struct _INI_COMMENT_LINE {
57   CHAR8                           *PtrComment;
58   COMMENT_LINE                    *PtrNext;
59 };
60 
61 typedef struct {
62   SECTION_ITEM                  *SectionHead;
63   COMMENT_LINE                  *CommentHead;
64 } INI_PARSING_LIB_CONTEXT;
65 
66 /**
67   Return if the digital char is valid.
68 
69   @param[in] DigitalChar    The digital char to be checked.
70   @param[in] IncludeHex     If it include HEX char.
71 
72   @retval TRUE   The digital char is valid.
73   @retval FALSE  The digital char is invalid.
74 **/
75 BOOLEAN
IsValidDigitalChar(IN CHAR8 DigitalChar,IN BOOLEAN IncludeHex)76 IsValidDigitalChar (
77   IN CHAR8    DigitalChar,
78   IN BOOLEAN  IncludeHex
79   )
80 {
81   if (DigitalChar >= '0' && DigitalChar <= '9') {
82     return TRUE;
83   }
84   if (IncludeHex) {
85     if (DigitalChar >= 'a' && DigitalChar <= 'f') {
86       return TRUE;
87     }
88     if (DigitalChar >= 'A' && DigitalChar <= 'F') {
89       return TRUE;
90     }
91   }
92   return FALSE;
93 }
94 
95 /**
96   Return if the name char is valid.
97 
98   @param[in] NameChar    The name char to be checked.
99 
100   @retval TRUE   The name char is valid.
101   @retval FALSE  The name char is invalid.
102 **/
103 BOOLEAN
IsValidNameChar(IN CHAR8 NameChar)104 IsValidNameChar (
105   IN CHAR8  NameChar
106   )
107 {
108   if (NameChar >= 'a' && NameChar <= 'z') {
109     return TRUE;
110   }
111   if (NameChar >= 'A' && NameChar <= 'Z') {
112     return TRUE;
113   }
114   if (NameChar >= '0' && NameChar <= '9') {
115     return TRUE;
116   }
117   if (NameChar == '_') {
118     return TRUE;
119   }
120   return FALSE;
121 }
122 
123 /**
124   Return if the digital string is valid.
125 
126   @param[in] Digital        The digital to be checked.
127   @param[in] Length         The length of digital string in bytes.
128   @param[in] IncludeHex     If it include HEX char.
129 
130   @retval TRUE   The digital string is valid.
131   @retval FALSE  The digital string is invalid.
132 **/
133 BOOLEAN
IsValidDigital(IN CHAR8 * Digital,IN UINTN Length,IN BOOLEAN IncludeHex)134 IsValidDigital (
135   IN CHAR8    *Digital,
136   IN UINTN    Length,
137   IN BOOLEAN  IncludeHex
138   )
139 {
140   UINTN  Index;
141   for (Index = 0; Index < Length; Index++) {
142     if (!IsValidDigitalChar(Digital[Index], IncludeHex)) {
143       return FALSE;
144     }
145   }
146   return TRUE;
147 }
148 
149 /**
150   Return if the decimal string is valid.
151 
152   @param[in] Decimal The decimal string to be checked.
153   @param[in] Length  The length of decimal string in bytes.
154 
155   @retval TRUE   The decimal string is valid.
156   @retval FALSE  The decimal string is invalid.
157 **/
158 BOOLEAN
IsValidDecimalString(IN CHAR8 * Decimal,IN UINTN Length)159 IsValidDecimalString (
160   IN CHAR8  *Decimal,
161   IN UINTN  Length
162   )
163 {
164   return IsValidDigital(Decimal, Length, FALSE);
165 }
166 
167 /**
168   Return if the heximal string is valid.
169 
170   @param[in] Hex     The heximal string to be checked.
171   @param[in] Length  The length of heximal string in bytes.
172 
173   @retval TRUE   The heximal string is valid.
174   @retval FALSE  The heximal string is invalid.
175 **/
176 BOOLEAN
IsValidHexString(IN CHAR8 * Hex,IN UINTN Length)177 IsValidHexString (
178   IN CHAR8  *Hex,
179   IN UINTN  Length
180   )
181 {
182   if (Length <= 2) {
183     return FALSE;
184   }
185   if (Hex[0] != '0') {
186     return FALSE;
187   }
188   if (Hex[1] != 'x' && Hex[1] != 'X') {
189     return FALSE;
190   }
191   return IsValidDigital(&Hex[2], Length - 2, TRUE);
192 }
193 
194 /**
195   Return if the name string is valid.
196 
197   @param[in] Name    The name to be checked.
198   @param[in] Length  The length of name string in bytes.
199 
200   @retval TRUE   The name string is valid.
201   @retval FALSE  The name string is invalid.
202 **/
203 BOOLEAN
IsValidName(IN CHAR8 * Name,IN UINTN Length)204 IsValidName (
205   IN CHAR8  *Name,
206   IN UINTN  Length
207   )
208 {
209   UINTN  Index;
210   for (Index = 0; Index < Length; Index++) {
211     if (!IsValidNameChar(Name[Index])) {
212       return FALSE;
213     }
214   }
215   return TRUE;
216 }
217 
218 /**
219   Return if the value string is valid GUID.
220 
221   @param[in] Value   The value to be checked.
222   @param[in] Length  The length of value string in bytes.
223 
224   @retval TRUE   The value string is valid GUID.
225   @retval FALSE  The value string is invalid GUID.
226 **/
227 BOOLEAN
IsValidGuid(IN CHAR8 * Value,IN UINTN Length)228 IsValidGuid (
229   IN CHAR8  *Value,
230   IN UINTN  Length
231   )
232 {
233   if (Length != sizeof("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") - 1) {
234     return FALSE;
235   }
236   if (!IS_HYPHEN(Value[8])) {
237     return FALSE;
238   }
239   if (!IS_HYPHEN(Value[13])) {
240     return FALSE;
241   }
242   if (!IS_HYPHEN(Value[18])) {
243     return FALSE;
244   }
245   if (!IS_HYPHEN(Value[23])) {
246     return FALSE;
247   }
248   if (!IsValidDigital(&Value[0], 8, TRUE)) {
249     return FALSE;
250   }
251   if (!IsValidDigital(&Value[9], 4, TRUE)) {
252     return FALSE;
253   }
254   if (!IsValidDigital(&Value[14], 4, TRUE)) {
255     return FALSE;
256   }
257   if (!IsValidDigital(&Value[19], 4, TRUE)) {
258     return FALSE;
259   }
260   if (!IsValidDigital(&Value[24], 12, TRUE)) {
261     return FALSE;
262   }
263   return TRUE;
264 }
265 
266 /**
267   Return if the value string is valid.
268 
269   @param[in] Value    The value to be checked.
270   @param[in] Length  The length of value string in bytes.
271 
272   @retval TRUE   The name string is valid.
273   @retval FALSE  The name string is invalid.
274 **/
275 BOOLEAN
IsValidValue(IN CHAR8 * Value,IN UINTN Length)276 IsValidValue (
277   IN CHAR8  *Value,
278   IN UINTN  Length
279   )
280 {
281   if (IsValidName(Value, Length) || IsValidGuid(Value, Length)) {
282     return TRUE;
283   }
284   return FALSE;
285 }
286 
287 /**
288   Dump an INI config file context.
289 
290   @param[in] Context         INI Config file context.
291 **/
292 VOID
DumpIniSection(IN VOID * Context)293 DumpIniSection (
294   IN VOID  *Context
295   )
296 {
297   INI_PARSING_LIB_CONTEXT               *IniContext;
298   SECTION_ITEM                          *PtrSection;
299   SECTION_ITEM                          *Section;
300 
301   if (Context == NULL) {
302     return;
303   }
304 
305   IniContext = Context;
306   Section = IniContext->SectionHead;
307 
308   while (Section != NULL) {
309     PtrSection = Section;
310     Section = Section->PtrNext;
311     if (PtrSection->PtrSection != NULL) {
312       DEBUG((DEBUG_VERBOSE, "Section - %a\n", PtrSection->PtrSection));
313     }
314     if (PtrSection->PtrEntry != NULL) {
315       DEBUG ((DEBUG_VERBOSE, "  Entry - %a\n", PtrSection->PtrEntry));
316     }
317     if (PtrSection->PtrValue != NULL) {
318       DEBUG((DEBUG_VERBOSE, "  Value - %a\n", PtrSection->PtrValue));
319     }
320   }
321 }
322 
323 /**
324   Copy one line data from buffer data to the line buffer.
325 
326   @param[in]      Buffer          Buffer data.
327   @param[in]      BufferSize      Buffer Size.
328   @param[in, out] LineBuffer      Line buffer to store the found line data.
329   @param[in, out] LineSize        On input, size of the input line buffer.
330                                   On output, size of the actual line buffer.
331 
332   @retval EFI_BUFFER_TOO_SMALL  The size of input line buffer is not enough.
333   @retval EFI_SUCCESS           Copy line data into the line buffer.
334 
335 **/
336 EFI_STATUS
ProfileGetLine(IN UINT8 * Buffer,IN UINTN BufferSize,IN OUT UINT8 * LineBuffer,IN OUT UINTN * LineSize)337 ProfileGetLine (
338   IN      UINT8                         *Buffer,
339   IN      UINTN                         BufferSize,
340   IN OUT  UINT8                         *LineBuffer,
341   IN OUT  UINTN                         *LineSize
342   )
343 {
344   UINTN                                 Length;
345   UINT8                                 *PtrBuf;
346   UINTN                                 PtrEnd;
347 
348   PtrBuf      = Buffer;
349   PtrEnd      = (UINTN)Buffer + BufferSize;
350 
351   //
352   // 0x0D indicates a line break. Otherwise there is no line break
353   //
354   while ((UINTN)PtrBuf < PtrEnd) {
355     if (*PtrBuf == 0x0D || *PtrBuf == 0x0A) {
356       break;
357     }
358     PtrBuf++;
359   }
360 
361   if ((UINTN)PtrBuf >= (PtrEnd - 1)) {
362     //
363     // The buffer ends without any line break
364     // or it is the last character of the buffer
365     //
366     Length    = BufferSize;
367   } else if (*(PtrBuf + 1) == 0x0A) {
368     //
369     // Further check if a 0x0A follows. If yes, count 0xA
370     //
371     Length    = (UINTN) PtrBuf - (UINTN) Buffer + 2;
372   } else {
373     Length    = (UINTN) PtrBuf - (UINTN) Buffer + 1;
374   }
375 
376   if (Length > (*LineSize)) {
377     *LineSize = Length;
378     return EFI_BUFFER_TOO_SMALL;
379   }
380 
381   SetMem (LineBuffer, *LineSize, 0x0);
382   *LineSize   = Length;
383   CopyMem (LineBuffer, Buffer, Length);
384 
385   return EFI_SUCCESS;
386 }
387 
388 /**
389   Trim Buffer by removing all CR, LF, TAB, and SPACE chars in its head and tail.
390 
391   @param[in, out] Buffer          On input,  buffer data to be trimed.
392                                   On output, the trimmed buffer.
393   @param[in, out] BufferSize      On input,  size of original buffer data.
394                                   On output, size of the trimmed buffer.
395 
396 **/
397 VOID
ProfileTrim(IN OUT UINT8 * Buffer,IN OUT UINTN * BufferSize)398 ProfileTrim (
399   IN OUT  UINT8                         *Buffer,
400   IN OUT  UINTN                         *BufferSize
401   )
402 {
403   UINTN                                 Length;
404   UINT8                                 *PtrBuf;
405   UINT8                                 *PtrEnd;
406 
407   if (*BufferSize == 0) {
408     return;
409   }
410 
411   //
412   // Trim the tail first, include CR, LF, TAB, and SPACE.
413   //
414   Length          = *BufferSize;
415   PtrBuf          = (UINT8 *) ((UINTN) Buffer + Length - 1);
416   while (PtrBuf >= Buffer) {
417     if ((*PtrBuf != 0x0D) && (*PtrBuf != 0x0A )
418       && (*PtrBuf != 0x20) && (*PtrBuf != 0x09)) {
419       break;
420     }
421     PtrBuf --;
422   }
423 
424   //
425   // all spaces, a blank line, return directly;
426   //
427   if (PtrBuf < Buffer) {
428     *BufferSize   = 0;
429     return;
430   }
431 
432   Length          = (UINTN)PtrBuf - (UINTN)Buffer + 1;
433   PtrEnd          = PtrBuf;
434   PtrBuf          = Buffer;
435 
436   //
437   // Now skip the heading CR, LF, TAB and SPACE
438   //
439   while (PtrBuf <= PtrEnd) {
440     if ((*PtrBuf != 0x0D) && (*PtrBuf != 0x0A )
441       && (*PtrBuf != 0x20) && (*PtrBuf != 0x09)) {
442       break;
443     }
444     PtrBuf++;
445   }
446 
447   //
448   // If no heading CR, LF, TAB or SPACE, directly return
449   //
450   if (PtrBuf == Buffer) {
451     *BufferSize   = Length;
452     return;
453   }
454 
455   *BufferSize     = (UINTN)PtrEnd - (UINTN)PtrBuf + 1;
456 
457   //
458   // The first Buffer..PtrBuf characters are CR, LF, TAB or SPACE.
459   // Now move out all these characters.
460   //
461   while (PtrBuf <= PtrEnd) {
462     *Buffer       = *PtrBuf;
463     Buffer++;
464     PtrBuf++;
465   }
466 
467   return;
468 }
469 
470 /**
471   Insert new comment item into comment head.
472 
473   @param[in]      Buffer          Comment buffer to be added.
474   @param[in]      BufferSize      Size of comment buffer.
475   @param[in, out] CommentHead     Comment Item head entry.
476 
477   @retval EFI_OUT_OF_RESOURCES   No enough memory is allocated.
478   @retval EFI_SUCCESS            New comment item is inserted.
479 
480 **/
481 EFI_STATUS
ProfileGetComments(IN UINT8 * Buffer,IN UINTN BufferSize,IN OUT COMMENT_LINE ** CommentHead)482 ProfileGetComments (
483   IN      UINT8                         *Buffer,
484   IN      UINTN                         BufferSize,
485   IN OUT  COMMENT_LINE                  **CommentHead
486   )
487 {
488   COMMENT_LINE                          *CommentItem;
489 
490   CommentItem = NULL;
491   CommentItem = AllocatePool (sizeof (COMMENT_LINE));
492   if (CommentItem == NULL) {
493     return EFI_OUT_OF_RESOURCES;
494   }
495 
496   CommentItem->PtrNext  = *CommentHead;
497   *CommentHead          = CommentItem;
498 
499   //
500   // Add a trailing '\0'
501   //
502   CommentItem->PtrComment = AllocatePool (BufferSize + 1);
503   if (CommentItem->PtrComment == NULL) {
504     FreePool (CommentItem);
505     return EFI_OUT_OF_RESOURCES;
506   }
507   CopyMem (CommentItem->PtrComment, Buffer, BufferSize);
508   *(CommentItem->PtrComment + BufferSize) = '\0';
509 
510   return EFI_SUCCESS;
511 }
512 
513 /**
514   Add new section item into Section head.
515 
516   @param[in]      Buffer          Section item data buffer.
517   @param[in]      BufferSize      Size of section item.
518   @param[in, out] SectionHead     Section item head entry.
519 
520   @retval EFI_OUT_OF_RESOURCES   No enough memory is allocated.
521   @retval EFI_SUCCESS            Section item is NULL or Section item is added.
522 
523 **/
524 EFI_STATUS
ProfileGetSection(IN UINT8 * Buffer,IN UINTN BufferSize,IN OUT SECTION_ITEM ** SectionHead)525 ProfileGetSection (
526   IN      UINT8                         *Buffer,
527   IN      UINTN                         BufferSize,
528   IN OUT  SECTION_ITEM                  **SectionHead
529   )
530 {
531   SECTION_ITEM                          *SectionItem;
532   UINTN                                 Length;
533   UINT8                                 *PtrBuf;
534   UINT8                                 *PtrEnd;
535 
536   ASSERT(BufferSize >= 1);
537   //
538   // The first character of Buffer is '[', now we want for ']'
539   //
540   PtrEnd      = (UINT8 *)((UINTN)Buffer + BufferSize - 1);
541   PtrBuf      = (UINT8 *)((UINTN)Buffer + 1);
542   while (PtrBuf <= PtrEnd) {
543     if (*PtrBuf == ']') {
544       break;
545     }
546     PtrBuf ++;
547   }
548   if (PtrBuf > PtrEnd) {
549     //
550     // Not found. Invalid line
551     //
552     return EFI_NOT_FOUND;
553   }
554   if (PtrBuf <= Buffer + 1) {
555     // Empty name
556     return EFI_NOT_FOUND;
557   }
558 
559   //
560   // excluding the heading '[' and tailing ']'
561   //
562   Length      = PtrBuf - Buffer - 1;
563   ProfileTrim (
564     Buffer + 1,
565     &Length
566   );
567 
568   //
569   // Invalid line if the section name is null
570   //
571   if (Length == 0) {
572     return EFI_NOT_FOUND;
573   }
574 
575   if (!IsValidName((CHAR8 *)Buffer + 1, Length)) {
576     return EFI_INVALID_PARAMETER;
577   }
578 
579   SectionItem = AllocatePool (sizeof (SECTION_ITEM));
580   if (SectionItem == NULL) {
581     return EFI_OUT_OF_RESOURCES;
582   }
583 
584   SectionItem->PtrSection = NULL;
585   SectionItem->SecNameLen = Length;
586   SectionItem->PtrEntry   = NULL;
587   SectionItem->PtrValue   = NULL;
588   SectionItem->PtrNext    = *SectionHead;
589   *SectionHead            = SectionItem;
590 
591   //
592   // Add a trailing '\0'
593   //
594   SectionItem->PtrSection = AllocatePool (Length + 1);
595   if (SectionItem->PtrSection == NULL) {
596     return EFI_OUT_OF_RESOURCES;
597   }
598 
599   //
600   // excluding the heading '['
601   //
602   CopyMem (SectionItem->PtrSection, Buffer + 1, Length);
603   *(SectionItem->PtrSection + Length) = '\0';
604 
605   return EFI_SUCCESS;
606 }
607 
608 /**
609   Add new section entry and entry value into Section head.
610 
611   @param[in]      Buffer          Section entry data buffer.
612   @param[in]      BufferSize      Size of section entry.
613   @param[in, out] SectionHead     Section item head entry.
614 
615   @retval EFI_OUT_OF_RESOURCES   No enough memory is allocated.
616   @retval EFI_SUCCESS            Section entry is added.
617   @retval EFI_NOT_FOUND          Section entry is not found.
618   @retval EFI_INVALID_PARAMETER  Section entry is invalid.
619 
620 **/
621 EFI_STATUS
ProfileGetEntry(IN UINT8 * Buffer,IN UINTN BufferSize,IN OUT SECTION_ITEM ** SectionHead)622 ProfileGetEntry (
623   IN      UINT8                         *Buffer,
624   IN      UINTN                         BufferSize,
625   IN OUT  SECTION_ITEM                  **SectionHead
626   )
627 {
628   EFI_STATUS                            Status;
629   SECTION_ITEM                          *SectionItem;
630   SECTION_ITEM                          *PtrSection;
631   UINTN                                 Length;
632   UINT8                                 *PtrBuf;
633   UINT8                                 *PtrEnd;
634 
635   Status      = EFI_SUCCESS;
636   PtrBuf      = Buffer;
637   PtrEnd      = (UINT8 *) ((UINTN)Buffer + BufferSize - 1);
638 
639   //
640   // First search for '='
641   //
642   while (PtrBuf <= PtrEnd) {
643     if (*PtrBuf == '=') {
644       break;
645     }
646     PtrBuf++;
647   }
648   if (PtrBuf > PtrEnd) {
649     //
650     // Not found. Invalid line
651     //
652     return EFI_NOT_FOUND;
653   }
654   if (PtrBuf <= Buffer) {
655     // Empty name
656     return EFI_NOT_FOUND;
657   }
658 
659   //
660   // excluding the tailing '='
661   //
662   Length      = PtrBuf - Buffer;
663   ProfileTrim (
664     Buffer,
665     &Length
666   );
667 
668   //
669   // Invalid line if the entry name is null
670   //
671   if (Length == 0) {
672     return EFI_NOT_FOUND;
673   }
674 
675   if (!IsValidName((CHAR8 *)Buffer, Length)) {
676     return EFI_INVALID_PARAMETER;
677   }
678 
679   //
680   // Omit this line if no section header has been found before
681   //
682   if (*SectionHead == NULL) {
683     return Status;
684   }
685   PtrSection  = *SectionHead;
686 
687   SectionItem = AllocatePool (sizeof (SECTION_ITEM));
688   if (SectionItem == NULL) {
689     return EFI_OUT_OF_RESOURCES;
690   }
691 
692   SectionItem->PtrSection = NULL;
693   SectionItem->PtrEntry   = NULL;
694   SectionItem->PtrValue   = NULL;
695   SectionItem->SecNameLen = PtrSection->SecNameLen;
696   SectionItem->PtrNext    = *SectionHead;
697   *SectionHead            = SectionItem;
698 
699   //
700   // SectionName, add a trailing '\0'
701   //
702   SectionItem->PtrSection = AllocatePool (PtrSection->SecNameLen + 1);
703   if (SectionItem->PtrSection == NULL) {
704     return EFI_OUT_OF_RESOURCES;
705   }
706   CopyMem (SectionItem->PtrSection, PtrSection->PtrSection, PtrSection->SecNameLen + 1);
707 
708   //
709   // EntryName, add a trailing '\0'
710   //
711   SectionItem->PtrEntry = AllocatePool (Length + 1);
712   if (SectionItem->PtrEntry == NULL) {
713     FreePool(SectionItem->PtrSection);
714     return EFI_OUT_OF_RESOURCES;
715   }
716   CopyMem (SectionItem->PtrEntry, Buffer, Length);
717   *(SectionItem->PtrEntry + Length) = '\0';
718 
719   //
720   // Next search for '#' or ';'
721   //
722   PtrBuf      = PtrBuf + 1;
723   Buffer      = PtrBuf;
724   while (PtrBuf <= PtrEnd) {
725     if (*PtrBuf == '#' || *PtrBuf == ';') {
726       break;
727     }
728     PtrBuf++;
729   }
730   if (PtrBuf <= Buffer) {
731     // Empty name
732     FreePool(SectionItem->PtrEntry);
733     FreePool(SectionItem->PtrSection);
734     return EFI_NOT_FOUND;
735   }
736   Length      = PtrBuf - Buffer;
737   ProfileTrim (
738     Buffer,
739     &Length
740   );
741 
742   //
743   // Invalid line if the entry value is null
744   //
745   if (Length == 0) {
746     FreePool(SectionItem->PtrEntry);
747     FreePool(SectionItem->PtrSection);
748     return EFI_NOT_FOUND;
749   }
750 
751   if (!IsValidValue((CHAR8 *)Buffer, Length)) {
752     FreePool(SectionItem->PtrEntry);
753     FreePool(SectionItem->PtrSection);
754     return EFI_INVALID_PARAMETER;
755   }
756 
757   //
758   // EntryValue, add a trailing '\0'
759   //
760   SectionItem->PtrValue = AllocatePool (Length + 1);
761   if (SectionItem->PtrValue == NULL) {
762     FreePool(SectionItem->PtrEntry);
763     FreePool(SectionItem->PtrSection);
764     return EFI_OUT_OF_RESOURCES;
765   }
766   CopyMem (SectionItem->PtrValue, Buffer, Length);
767   *(SectionItem->PtrValue + Length) = '\0';
768 
769   return EFI_SUCCESS;
770 }
771 
772 /**
773   Free all comment entry and section entry.
774 
775   @param[in] Section         Section entry list.
776   @param[in] Comment         Comment entry list.
777 
778 **/
779 VOID
FreeAllList(IN SECTION_ITEM * Section,IN COMMENT_LINE * Comment)780 FreeAllList (
781   IN      SECTION_ITEM                  *Section,
782   IN      COMMENT_LINE                  *Comment
783   )
784 {
785   SECTION_ITEM                          *PtrSection;
786   COMMENT_LINE                          *PtrComment;
787 
788   while (Section != NULL) {
789     PtrSection    = Section;
790     Section       = Section->PtrNext;
791     if (PtrSection->PtrEntry != NULL) {
792       FreePool (PtrSection->PtrEntry);
793     }
794     if (PtrSection->PtrSection != NULL) {
795       FreePool (PtrSection->PtrSection);
796     }
797     if (PtrSection->PtrValue != NULL) {
798       FreePool (PtrSection->PtrValue);
799     }
800     FreePool (PtrSection);
801   }
802 
803   while (Comment != NULL) {
804     PtrComment    = Comment;
805     Comment       = Comment->PtrNext;
806     if (PtrComment->PtrComment != NULL) {
807       FreePool (PtrComment->PtrComment);
808     }
809     FreePool (PtrComment);
810   }
811 
812   return;
813 }
814 
815 /**
816   Get section entry value.
817 
818   @param[in]  Section         Section entry list.
819   @param[in]  SectionName     Section name.
820   @param[in]  EntryName       Section entry name.
821   @param[out] EntryValue      Point to the got entry value.
822 
823   @retval EFI_NOT_FOUND  Section is not found.
824   @retval EFI_SUCCESS    Section entry value is got.
825 
826 **/
827 EFI_STATUS
UpdateGetProfileString(IN SECTION_ITEM * Section,IN CHAR8 * SectionName,IN CHAR8 * EntryName,OUT CHAR8 ** EntryValue)828 UpdateGetProfileString (
829   IN      SECTION_ITEM                  *Section,
830   IN      CHAR8                         *SectionName,
831   IN      CHAR8                         *EntryName,
832   OUT     CHAR8                         **EntryValue
833   )
834 {
835   *EntryValue   = NULL;
836 
837   while (Section != NULL) {
838     if (AsciiStrCmp ((CONST CHAR8 *) Section->PtrSection, (CONST CHAR8 *) SectionName) == 0) {
839       if (Section->PtrEntry != NULL) {
840         if (AsciiStrCmp ((CONST CHAR8 *) Section->PtrEntry, (CONST CHAR8 *) EntryName) == 0) {
841           break;
842         }
843       }
844     }
845     Section     = Section->PtrNext;
846   }
847 
848   if (Section == NULL) {
849     return EFI_NOT_FOUND;
850   }
851 
852   *EntryValue   = Section->PtrValue;
853 
854   return EFI_SUCCESS;
855 }
856 
857 /**
858   Pre process config data buffer into Section entry list and Comment entry list.
859 
860   @param[in]      DataBuffer      Config raw file buffer.
861   @param[in]      BufferSize      Size of raw buffer.
862   @param[in, out] SectionHead     Pointer to the section entry list.
863   @param[in, out] CommentHead     Pointer to the comment entry list.
864 
865   @retval EFI_OUT_OF_RESOURCES  No enough memory is allocated.
866   @retval EFI_SUCCESS           Config data buffer is preprocessed.
867   @retval EFI_NOT_FOUND         Config data buffer is invalid, because Section or Entry is not found.
868   @retval EFI_INVALID_PARAMETER Config data buffer is invalid, because Section or Entry is invalid.
869 
870 **/
871 EFI_STATUS
PreProcessDataFile(IN UINT8 * DataBuffer,IN UINTN BufferSize,IN OUT SECTION_ITEM ** SectionHead,IN OUT COMMENT_LINE ** CommentHead)872 PreProcessDataFile (
873   IN      UINT8                         *DataBuffer,
874   IN      UINTN                         BufferSize,
875   IN OUT  SECTION_ITEM                  **SectionHead,
876   IN OUT  COMMENT_LINE                  **CommentHead
877   )
878 {
879   EFI_STATUS                            Status;
880   CHAR8                                 *Source;
881   CHAR8                                 *CurrentPtr;
882   CHAR8                                 *BufferEnd;
883   CHAR8                                 *PtrLine;
884   UINTN                                 LineLength;
885   UINTN                                 SourceLength;
886   UINTN                                 MaxLineLength;
887 
888   *SectionHead          = NULL;
889   *CommentHead          = NULL;
890   BufferEnd             = (CHAR8 *) ( (UINTN) DataBuffer + BufferSize);
891   CurrentPtr            = (CHAR8 *) DataBuffer;
892   MaxLineLength         = MAX_LINE_LENGTH;
893   Status                = EFI_SUCCESS;
894 
895   PtrLine = AllocatePool (MaxLineLength);
896   if (PtrLine == NULL) {
897     return EFI_OUT_OF_RESOURCES;
898   }
899 
900   while (CurrentPtr < BufferEnd) {
901     Source              = CurrentPtr;
902     SourceLength        = (UINTN)BufferEnd - (UINTN)CurrentPtr;
903     LineLength          = MaxLineLength;
904     //
905     // With the assumption that line length is less than 512
906     // characters. Otherwise BUFFER_TOO_SMALL will be returned.
907     //
908     Status              = ProfileGetLine (
909                             (UINT8 *) Source,
910                             SourceLength,
911                             (UINT8 *) PtrLine,
912                             &LineLength
913                             );
914     if (EFI_ERROR (Status)) {
915       if (Status == EFI_BUFFER_TOO_SMALL) {
916         //
917         // If buffer too small, re-allocate the buffer according
918         // to the returned LineLength and try again.
919         //
920         FreePool (PtrLine);
921         PtrLine         = NULL;
922         PtrLine = AllocatePool (LineLength);
923         if (PtrLine == NULL) {
924           Status        = EFI_OUT_OF_RESOURCES;
925           break;
926         }
927         SourceLength    = LineLength;
928         Status          = ProfileGetLine (
929                             (UINT8 *) Source,
930                             SourceLength,
931                             (UINT8 *) PtrLine,
932                             &LineLength
933                             );
934         if (EFI_ERROR (Status)) {
935           break;
936         }
937         MaxLineLength   = LineLength;
938       } else {
939         break;
940       }
941     }
942     CurrentPtr          = (CHAR8 *) ( (UINTN) CurrentPtr + LineLength);
943 
944     //
945     // Line got. Trim the line before processing it.
946     //
947     ProfileTrim (
948       (UINT8 *) PtrLine,
949       &LineLength
950    );
951 
952     //
953     // Blank line
954     //
955     if (LineLength == 0) {
956       continue;
957     }
958 
959     if (PtrLine[0] == '#' || PtrLine[0] == ';') {
960       Status            = ProfileGetComments (
961                             (UINT8 *) PtrLine,
962                             LineLength,
963                             CommentHead
964                             );
965     } else if (PtrLine[0] == '[') {
966       Status            = ProfileGetSection (
967                             (UINT8 *) PtrLine,
968                             LineLength,
969                             SectionHead
970                             );
971     } else {
972       Status            = ProfileGetEntry (
973                             (UINT8 *) PtrLine,
974                             LineLength,
975                             SectionHead
976                             );
977     }
978 
979     if (EFI_ERROR (Status)) {
980       break;
981     }
982   }
983 
984   //
985   // Free buffer
986   //
987   FreePool (PtrLine);
988 
989   return Status;
990 }
991 
992 /**
993   Open an INI config file and return a context.
994 
995   @param[in] DataBuffer      Config raw file buffer.
996   @param[in] BufferSize      Size of raw buffer.
997 
998   @return       Config data buffer is opened and context is returned.
999   @retval NULL  No enough memory is allocated.
1000   @retval NULL  Config data buffer is invalid.
1001 **/
1002 VOID *
1003 EFIAPI
OpenIniFile(IN UINT8 * DataBuffer,IN UINTN BufferSize)1004 OpenIniFile (
1005   IN      UINT8                         *DataBuffer,
1006   IN      UINTN                         BufferSize
1007   )
1008 {
1009   EFI_STATUS                            Status;
1010   INI_PARSING_LIB_CONTEXT               *IniContext;
1011 
1012   if (DataBuffer == NULL || BufferSize == 0) {
1013     return NULL;
1014   }
1015 
1016   IniContext = AllocateZeroPool(sizeof(INI_PARSING_LIB_CONTEXT));
1017   if (IniContext == NULL) {
1018     return NULL;
1019   }
1020 
1021   //
1022   // First process the data buffer and get all sections and entries
1023   //
1024   Status = PreProcessDataFile (
1025              DataBuffer,
1026              BufferSize,
1027              &IniContext->SectionHead,
1028              &IniContext->CommentHead
1029              );
1030   if (EFI_ERROR(Status)) {
1031     FreePool(IniContext);
1032     return NULL;
1033   }
1034   DEBUG_CODE_BEGIN ();
1035     DumpIniSection(IniContext);
1036   DEBUG_CODE_END ();
1037   return IniContext;
1038 }
1039 
1040 /**
1041   Get section entry string value.
1042 
1043   @param[in]  Context         INI Config file context.
1044   @param[in]  SectionName     Section name.
1045   @param[in]  EntryName       Section entry name.
1046   @param[out] EntryValue      Point to the got entry string value.
1047 
1048   @retval EFI_SUCCESS    Section entry string value is got.
1049   @retval EFI_NOT_FOUND  Section is not found.
1050 **/
1051 EFI_STATUS
1052 EFIAPI
GetStringFromDataFile(IN VOID * Context,IN CHAR8 * SectionName,IN CHAR8 * EntryName,OUT CHAR8 ** EntryValue)1053 GetStringFromDataFile(
1054   IN      VOID                          *Context,
1055   IN      CHAR8                         *SectionName,
1056   IN      CHAR8                         *EntryName,
1057   OUT     CHAR8                         **EntryValue
1058   )
1059 {
1060   INI_PARSING_LIB_CONTEXT               *IniContext;
1061   EFI_STATUS                            Status;
1062 
1063   if (Context == NULL || SectionName == NULL || EntryName == NULL || EntryValue == NULL) {
1064     return EFI_INVALID_PARAMETER;
1065   }
1066 
1067   IniContext = Context;
1068 
1069   *EntryValue  = NULL;
1070   Status = UpdateGetProfileString (
1071              IniContext->SectionHead,
1072              SectionName,
1073              EntryName,
1074              EntryValue
1075              );
1076   return Status;
1077 }
1078 
1079 /**
1080   Get section entry GUID value.
1081 
1082   @param[in]  Context         INI Config file context.
1083   @param[in]  SectionName     Section name.
1084   @param[in]  EntryName       Section entry name.
1085   @param[out] Guid            Point to the got GUID value.
1086 
1087   @retval EFI_SUCCESS    Section entry GUID value is got.
1088   @retval EFI_NOT_FOUND  Section is not found.
1089 **/
1090 EFI_STATUS
1091 EFIAPI
GetGuidFromDataFile(IN VOID * Context,IN CHAR8 * SectionName,IN CHAR8 * EntryName,OUT EFI_GUID * Guid)1092 GetGuidFromDataFile (
1093   IN      VOID                          *Context,
1094   IN      CHAR8                         *SectionName,
1095   IN      CHAR8                         *EntryName,
1096   OUT     EFI_GUID                      *Guid
1097   )
1098 {
1099   CHAR8                                 *Value;
1100   EFI_STATUS                            Status;
1101   RETURN_STATUS                         RStatus;
1102 
1103   if (Context == NULL || SectionName == NULL || EntryName == NULL || Guid == NULL) {
1104     return EFI_INVALID_PARAMETER;
1105   }
1106 
1107   Status = GetStringFromDataFile(
1108              Context,
1109              SectionName,
1110              EntryName,
1111              &Value
1112              );
1113   if (EFI_ERROR(Status)) {
1114     return EFI_NOT_FOUND;
1115   }
1116   ASSERT (Value != NULL);
1117   RStatus = AsciiStrToGuid (Value, Guid);
1118   if (RETURN_ERROR (RStatus) || (Value[GUID_STRING_LENGTH] != '\0')) {
1119     return EFI_NOT_FOUND;
1120   }
1121   return EFI_SUCCESS;
1122 }
1123 
1124 /**
1125   Get section entry decimal UINTN value.
1126 
1127   @param[in]  Context         INI Config file context.
1128   @param[in]  SectionName     Section name.
1129   @param[in]  EntryName       Section entry name.
1130   @param[out] Data            Point to the got decimal UINTN value.
1131 
1132   @retval EFI_SUCCESS    Section entry decimal UINTN value is got.
1133   @retval EFI_NOT_FOUND  Section is not found.
1134 **/
1135 EFI_STATUS
1136 EFIAPI
GetDecimalUintnFromDataFile(IN VOID * Context,IN CHAR8 * SectionName,IN CHAR8 * EntryName,OUT UINTN * Data)1137 GetDecimalUintnFromDataFile (
1138   IN      VOID                          *Context,
1139   IN      CHAR8                         *SectionName,
1140   IN      CHAR8                         *EntryName,
1141   OUT     UINTN                         *Data
1142   )
1143 {
1144   CHAR8                                 *Value;
1145   EFI_STATUS                            Status;
1146 
1147   if (Context == NULL || SectionName == NULL || EntryName == NULL || Data == NULL) {
1148     return EFI_INVALID_PARAMETER;
1149   }
1150 
1151   Status = GetStringFromDataFile(
1152              Context,
1153              SectionName,
1154              EntryName,
1155              &Value
1156              );
1157   if (EFI_ERROR(Status)) {
1158     return EFI_NOT_FOUND;
1159   }
1160   ASSERT (Value != NULL);
1161   if (!IsValidDecimalString(Value, AsciiStrLen(Value))) {
1162     return EFI_NOT_FOUND;
1163   }
1164   *Data = AsciiStrDecimalToUintn(Value);
1165   return EFI_SUCCESS;
1166 }
1167 
1168 /**
1169   Get section entry heximal UINTN value.
1170 
1171   @param[in]  Context         INI Config file context.
1172   @param[in]  SectionName     Section name.
1173   @param[in]  EntryName       Section entry name.
1174   @param[out] Data            Point to the got heximal UINTN value.
1175 
1176   @retval EFI_SUCCESS    Section entry heximal UINTN value is got.
1177   @retval EFI_NOT_FOUND  Section is not found.
1178 **/
1179 EFI_STATUS
1180 EFIAPI
GetHexUintnFromDataFile(IN VOID * Context,IN CHAR8 * SectionName,IN CHAR8 * EntryName,OUT UINTN * Data)1181 GetHexUintnFromDataFile (
1182   IN      VOID                          *Context,
1183   IN      CHAR8                         *SectionName,
1184   IN      CHAR8                         *EntryName,
1185   OUT     UINTN                         *Data
1186   )
1187 {
1188   CHAR8                                 *Value;
1189   EFI_STATUS                            Status;
1190 
1191   if (Context == NULL || SectionName == NULL || EntryName == NULL || Data == NULL) {
1192     return EFI_INVALID_PARAMETER;
1193   }
1194 
1195   Status = GetStringFromDataFile(
1196              Context,
1197              SectionName,
1198              EntryName,
1199              &Value
1200              );
1201   if (EFI_ERROR(Status)) {
1202     return EFI_NOT_FOUND;
1203   }
1204   ASSERT (Value != NULL);
1205   if (!IsValidHexString(Value, AsciiStrLen(Value))) {
1206     return EFI_NOT_FOUND;
1207   }
1208   *Data = AsciiStrHexToUintn(Value);
1209   return EFI_SUCCESS;
1210 }
1211 
1212 /**
1213   Get section entry heximal UINT64 value.
1214 
1215   @param[in]  Context         INI Config file context.
1216   @param[in]  SectionName     Section name.
1217   @param[in]  EntryName       Section entry name.
1218   @param[out] Data            Point to the got heximal UINT64 value.
1219 
1220   @retval EFI_SUCCESS    Section entry heximal UINT64 value is got.
1221   @retval EFI_NOT_FOUND  Section is not found.
1222 **/
1223 EFI_STATUS
1224 EFIAPI
GetHexUint64FromDataFile(IN VOID * Context,IN CHAR8 * SectionName,IN CHAR8 * EntryName,OUT UINT64 * Data)1225 GetHexUint64FromDataFile (
1226   IN      VOID                          *Context,
1227   IN      CHAR8                         *SectionName,
1228   IN      CHAR8                         *EntryName,
1229   OUT     UINT64                        *Data
1230   )
1231 {
1232   CHAR8                                 *Value;
1233   EFI_STATUS                            Status;
1234 
1235   if (Context == NULL || SectionName == NULL || EntryName == NULL || Data == NULL) {
1236     return EFI_INVALID_PARAMETER;
1237   }
1238 
1239   Status = GetStringFromDataFile(
1240              Context,
1241              SectionName,
1242              EntryName,
1243              &Value
1244              );
1245   if (EFI_ERROR(Status)) {
1246     return EFI_NOT_FOUND;
1247   }
1248   ASSERT (Value != NULL);
1249   if (!IsValidHexString(Value, AsciiStrLen(Value))) {
1250     return EFI_NOT_FOUND;
1251   }
1252   *Data = AsciiStrHexToUint64(Value);
1253   return EFI_SUCCESS;
1254 }
1255 
1256 /**
1257   Close an INI config file and free the context.
1258 
1259   @param[in] Context         INI Config file context.
1260 **/
1261 VOID
1262 EFIAPI
CloseIniFile(IN VOID * Context)1263 CloseIniFile (
1264   IN      VOID                          *Context
1265   )
1266 {
1267   INI_PARSING_LIB_CONTEXT               *IniContext;
1268 
1269   if (Context == NULL) {
1270     return ;
1271   }
1272 
1273   IniContext = Context;
1274   FreeAllList(IniContext->SectionHead, IniContext->CommentHead);
1275 
1276   return;
1277 }
1278