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