1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 
12 /**************************************************************************/
13 /**************************************************************************/
14 /**                                                                       */
15 /** FileX Component                                                       */
16 /**                                                                       */
17 /**   Application Utility                                                 */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define FX_SOURCE_CODE
23 
24 
25 /* Include necessary system files.  */
26 
27 #include "fx_api.h"
28 #include "fx_utility.h"
29 
30 
31 /* Define internal data structures.  */
32 
33 typedef struct FX_MEDIA_PARTITION_STRUCT
34 {
35     ULONG fx_media_part_start;
36     ULONG fx_media_part_size;
37 } FX_MEDIA_PARTITION;
38 
39 /* Define internal partition constants. */
40 
41 #ifndef FX_MAX_PARTITION_COUNT
42 #define FX_MAX_PARTITION_COUNT              16
43 #endif /* FX_MAX_PARTITION_COUNT */
44 
45 #define FX_PARTITION_TABLE_OFFSET           446
46 #define FX_PARTITION_ENTRY_SIZE             16
47 #define FX_PARTITION_TYPE_OFFSET            4
48 #define FX_PARTITION_LBA_OFFSET             8
49 #define FX_PARTITION_SECTORS_OFFSET         12
50 
51 #define FX_PARTITION_TYPE_FREE              0x00
52 #define FX_PARTITION_TYPE_EXTENDED          0x05
53 #define FX_PARTITION_TYPE_EXTENDED_LBA      0x0F
54 
55 
56 /* Define function prototypes for the partition table parsing application
57    utility.  */
58 
59 UINT    _fx_partition_offset_calculate(void  *partition_sector, UINT partition,
60                                      ULONG *partition_start, ULONG *partition_size);
61 UINT    _fx_utility_partition_get(FX_MEDIA_PARTITION *partition_table,
62                                 UINT *count, ULONG sector, UCHAR *sector_buffer);
63 UINT    _fx_partition_offset_calculate_extended(FX_MEDIA *media_ptr, void  *partition_sector, UINT partition,
64                                      ULONG *partition_start, ULONG *partition_size);
65 
66 /**************************************************************************/
67 /*                                                                        */
68 /*  FUNCTION                                               RELEASE        */
69 /*                                                                        */
70 /*    _fx_partition_offset_calculate                      PORTABLE C      */
71 /*                                                           6.1.6        */
72 /*  AUTHOR                                                                */
73 /*                                                                        */
74 /*    William E. Lamie, Microsoft Corporation                             */
75 /*                                                                        */
76 /*  DESCRIPTION                                                           */
77 /*                                                                        */
78 /*    This function calculates the sector offset to the specified         */
79 /*    partition.  The buffer containing the partition table is also       */
80 /*    supplied to this function.  If the buffer supplied is a boot        */
81 /*    record (which could be the case in non-partition systems), this     */
82 /*    function returns an offset of zero, the total sectors, and a        */
83 /*    successful status indicating that the buffer supplied is the boot   */
84 /*    record.  Otherwise, if a partition is found, this function returns  */
85 /*    the sector offset to its boot record along with a successful        */
86 /*    status. If the specified partition is not found or the buffer is    */
87 /*    not a partition table or boot record, this function returns an      */
88 /*    error.                                                              */
89 /*                                                                        */
90 /*    Note: Empty partitions have a FX_SUCCESS return code, however their */
91 /*          starting sector is FX_NULL and the size returned is 0.        */
92 /*                                                                        */
93 /*  INPUT                                                                 */
94 /*                                                                        */
95 /*    partition_sector                      Pointer to buffer containing  */
96 /*                                            either the partition table  */
97 /*                                            or the boot sector          */
98 /*    partition                             Desired partition             */
99 /*    partition_start                       Return partition start        */
100 /*    partition_size                        Return partition size         */
101 /*                                                                        */
102 /*  OUTPUT                                                                */
103 /*                                                                        */
104 /*    return status                                                       */
105 /*                                                                        */
106 /*  CALLS                                                                 */
107 /*                                                                        */
108 /*     _fx_utility_partition_get            Actual partition parsing      */
109 /*                                            routine                     */
110 /*                                                                        */
111 /*  CALLED BY                                                             */
112 /*                                                                        */
113 /*    Application Driver                                                  */
114 /*                                                                        */
115 /*  RELEASE HISTORY                                                       */
116 /*                                                                        */
117 /*    DATE              NAME                      DESCRIPTION             */
118 /*                                                                        */
119 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
120 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
121 /*                                            resulting in version 6.1    */
122 /*  04-02-2021     William E. Lamie         Modified comment(s),          */
123 /*                                            ignored signature check for */
124 /*                                            no partition situation,     */
125 /*                                            resulting in version 6.1.6  */
126 /*                                                                        */
127 /**************************************************************************/
_fx_partition_offset_calculate(void * partition_sector,UINT partition,ULONG * partition_start,ULONG * partition_size)128 UINT  _fx_partition_offset_calculate(void  *partition_sector, UINT partition,
129                                      ULONG *partition_start, ULONG *partition_size)
130 {
131 
132 FX_MEDIA_PARTITION partition_table[4];
133 UINT               count;
134 ULONG64            total_sectors;
135 UCHAR             *partition_sector_ptr;
136 
137 
138     /* Setup working pointer and initialize count.  */
139     partition_sector_ptr =  partition_sector;
140     count =  0;
141 
142     /* Check for a real boot sector instead of a partition table.  */
143     if ((partition_sector_ptr[0] == 0xe9) || ((partition_sector_ptr[0] == 0xeb) && (partition_sector_ptr[2] == 0x90)))
144     {
145 
146         /* Yes, a real boot sector could be present.  */
147 
148         /* See if there are good values for sectors per FAT.  */
149         if (partition_sector_ptr[0x16] || partition_sector_ptr[0x17] || partition_sector_ptr[0x24] || partition_sector_ptr[0x25] || partition_sector_ptr[0x26] || partition_sector_ptr[0x27])
150         {
151 
152             /* There are values for sectors per FAT.  */
153 
154             /* Determine if there is a total sector count.  */
155             total_sectors =  0;
156 
157             if (partition_sector_ptr[0x13] || partition_sector_ptr[0x14])
158             {
159 
160                 /* Calculate the total sectors, FAT12/16.  */
161                 total_sectors =  (((ULONG) partition_sector_ptr[0x14]) << 8) | ((ULONG) partition_sector_ptr[0x13]);
162             }
163             else if (partition_sector_ptr[0x20] || partition_sector_ptr[0x21] || partition_sector_ptr[0x22] || partition_sector_ptr[0x23])
164             {
165 
166                 /* Calculate the total sectors, FAT32.  */
167                 total_sectors =  (((ULONG) partition_sector_ptr[0x23]) << 24) |
168                                  (((ULONG) partition_sector_ptr[0x22]) << 16) |
169                                  (((ULONG) partition_sector_ptr[0x21]) << 8)  |
170                                  ((ULONG) partition_sector_ptr[0x20]);
171             }
172 
173             /* Determine if there is a total sector count.  */
174             if (total_sectors)
175             {
176 
177                 if (partition_start != FX_NULL)
178                 {
179                     /* Return an offset of 0, size of boot record, and a successful status.  */
180                     *partition_start =  0;
181                 }
182 
183                 /* Determine if the total sectors is required.  */
184                 if (partition_size != FX_NULL)
185                 {
186 
187                     /* Return the total sectors.  */
188                     *partition_size =  (ULONG)(total_sectors & 0xFFFFFFFF);
189                 }
190 
191                 /* Return success!  */
192                 return(FX_SUCCESS);
193             }
194         }
195     }
196 
197     /* Check signature to make sure the buffer is valid.  */
198     if ((partition_sector_ptr[510] != 0x55) || (partition_sector_ptr[511] != 0xAA))
199     {
200 
201         /* Invalid, return an error.  */
202         return(FX_NOT_FOUND);
203     }
204 
205     /* Not bootable, look for specific partition.  */
206     _fx_utility_partition_get(partition_table, &count, 0, partition_sector_ptr);
207 
208     /* Determine if return value is valid.  */
209     if (partition >= count)
210     {
211 
212         /* No, return an error.  */
213         return(FX_NOT_FOUND);
214     }
215 
216     /* Return the partition starting sector, if non-NULL.  */
217     if (partition_start != FX_NULL)
218     {
219         *partition_start =  partition_table[partition].fx_media_part_start;
220     }
221 
222     /* Return the partition size, if non-NULL.  */
223     if (partition_size != FX_NULL)
224     {
225         *partition_size =  partition_table[partition].fx_media_part_size;
226     }
227 
228     /* Return successful completion.  */
229     return(FX_SUCCESS);
230 }
231 
232 
233 /**************************************************************************/
234 /*                                                                        */
235 /*  FUNCTION                                               RELEASE        */
236 /*                                                                        */
237 /*    _fx_utility_partition_get                           PORTABLE C      */
238 /*                                                           6.1.6        */
239 /*  AUTHOR                                                                */
240 /*                                                                        */
241 /*    William E. Lamie, Microsoft Corporation                             */
242 /*                                                                        */
243 /*  DESCRIPTION                                                           */
244 /*                                                                        */
245 /*    This function parses the partition sector and completes the         */
246 /*    supplied partition entry structure.                                 */
247 /*                                                                        */
248 /*  INPUT                                                                 */
249 /*                                                                        */
250 /*    partition_table                       Pointer to partition table    */
251 /*    count                                 Number of partitions found    */
252 /*    sector                                Base sector                   */
253 /*    sector_buffer                         Buffer containing partition   */
254 /*                                            table                       */
255 /*                                                                        */
256 /*  OUTPUT                                                                */
257 /*                                                                        */
258 /*    return status                                                       */
259 /*                                                                        */
260 /*  CALLS                                                                 */
261 /*                                                                        */
262 /*    None                                                                */
263 /*                                                                        */
264 /*  CALLED BY                                                             */
265 /*                                                                        */
266 /*    _fx_partition_offset_calculate        Calculate partition offset    */
267 /*                                                                        */
268 /*  RELEASE HISTORY                                                       */
269 /*                                                                        */
270 /*    DATE              NAME                      DESCRIPTION             */
271 /*                                                                        */
272 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
273 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
274 /*                                            resulting in version 6.1    */
275 /*  04-02-2021     William E. Lamie         Modified comment(s),          */
276 /*                                            resulting in version 6.1.6  */
277 /*                                                                        */
278 /**************************************************************************/
_fx_utility_partition_get(FX_MEDIA_PARTITION * partition_table,UINT * count,ULONG sector,UCHAR * sector_buffer)279 UINT  _fx_utility_partition_get(FX_MEDIA_PARTITION *partition_table,
280                                 UINT *count, ULONG sector, UCHAR *sector_buffer)
281 {
282 
283 UINT  i;
284 ULONG base_sector, value;
285 
286     /* This parameter has not been supported yet. */
287     FX_PARAMETER_NOT_USED(sector);
288 
289     /* Initialize base sector.  */
290     base_sector =  0;
291 
292     for(i = 446; i <= 494; i+=16)
293     {
294         if (sector_buffer[i + 4] == 0) /* no partition entry here */
295         {
296 
297             partition_table[*count].fx_media_part_start = 0;
298             partition_table[*count].fx_media_part_size  = 0;
299         }
300         else
301         {
302 
303             value =  (ULONG) sector_buffer[i + 8]; /* little endian start value */
304             value =  (((ULONG) sector_buffer[i + 9]) << 8) | value;
305             value =  (((ULONG) sector_buffer[i + 10]) << 16) | value;
306             value =  (((ULONG) sector_buffer[i + 11]) << 24) | value;
307             partition_table[*count].fx_media_part_start = value + base_sector;
308 
309             value =  (ULONG) sector_buffer[i + 12]; /* little endian size value */
310             value =  (((ULONG) sector_buffer[i + 13]) << 8) | value;
311             value =  (((ULONG) sector_buffer[i + 14]) << 16) | value;
312             value =  (((ULONG) sector_buffer[i + 15]) << 24) | value;
313             partition_table[*count].fx_media_part_size = value;
314         }
315 
316         (*count)++;
317     }
318 
319     /* Return success.  */
320     return(FX_SUCCESS);
321 }
322 
323 /**************************************************************************/
324 /*                                                                        */
325 /*  FUNCTION                                               RELEASE        */
326 /*                                                                        */
327 /*    _fx_partition_offset_calculate_extended             PORTABLE C      */
328 /*                                                           6.2.0        */
329 /*  AUTHOR                                                                */
330 /*                                                                        */
331 /*    Xiuwen Cai, Microsoft Corporation                                   */
332 /*                                                                        */
333 /*  DESCRIPTION                                                           */
334 /*                                                                        */
335 /*    This function calculates the sector offset to the specified         */
336 /*    partition.  The buffer containing the partition table is also       */
337 /*    supplied to this function.  If the buffer supplied is a boot        */
338 /*    record (which could be the case in non-partition systems), this     */
339 /*    function returns an offset of zero, the total sectors, and a        */
340 /*    successful status indicating that the buffer supplied is the boot   */
341 /*    record.  Otherwise, if a partition is found, this function returns  */
342 /*    the sector offset to its boot record along with a successful        */
343 /*    status. If the specified partition is not found or the buffer is    */
344 /*    not a partition table or boot record, this function returns an      */
345 /*    error.                                                              */
346 /*                                                                        */
347 /*    Note: Empty partitions have a FX_NOT_FOUND return code.             */
348 /*      Use partition index 0 to 3 for primary partition and index 4 to   */
349 /*      FX_MAX_PARTITION_COUNT for extended partition.                    */
350 /*                                                                        */
351 /*  INPUT                                                                 */
352 /*                                                                        */
353 /*    media_ptr                             Media control block pointer   */
354 /*    partition_sector                      Pointer to buffer containing  */
355 /*                                            either the partition table  */
356 /*                                            or the boot sector          */
357 /*    partition                             Desired partition             */
358 /*    partition_start                       Return partition start        */
359 /*    partition_size                        Return partition size         */
360 /*                                                                        */
361 /*  OUTPUT                                                                */
362 /*                                                                        */
363 /*    return status                                                       */
364 /*                                                                        */
365 /*  CALLS                                                                 */
366 /*                                                                        */
367 /*    _fx_utility_16_unsigned_read          Read a USHORT from memory     */
368 /*    _fx_utility_32_unsigned_read          Read a ULONG from memory      */
369 /*    _fx_utility_64_unsigned_read          Read a ULONG64 from memory    */
370 /*    Media driver                                                        */
371 /*                                                                        */
372 /*  CALLED BY                                                             */
373 /*                                                                        */
374 /*    Application Driver                                                  */
375 /*                                                                        */
376 /*  RELEASE HISTORY                                                       */
377 /*                                                                        */
378 /*    DATE              NAME                      DESCRIPTION             */
379 /*                                                                        */
380 /*  10-31-2022     Xiuwen Cai               Initial Version 6.2.0        */
381 /*                                                                        */
382 /**************************************************************************/
_fx_partition_offset_calculate_extended(FX_MEDIA * media_ptr,void * partition_sector,UINT partition,ULONG * partition_start,ULONG * partition_size)383 UINT  _fx_partition_offset_calculate_extended(FX_MEDIA *media_ptr, void  *partition_sector, UINT partition,
384                                      ULONG *partition_start, ULONG *partition_size)
385 {
386 
387 ULONG64 total_sectors;
388 UCHAR  *partition_sector_ptr;
389 UCHAR   partition_type;
390 UINT    i;
391 ULONG   base_sector;
392 ULONG   base_sector_extended;
393 
394 
395     /* Setup working pointer.  */
396     partition_sector_ptr =  partition_sector;
397 
398     /* Check for a real boot sector instead of a partition table.  */
399     if ((partition_sector_ptr[0] == 0xe9) || ((partition_sector_ptr[0] == 0xeb) && (partition_sector_ptr[2] == 0x90)))
400     {
401 
402         /* Yes, a real boot sector could be present.  */
403 
404         /* See if there are good values for sectors per FAT.  */
405         if (partition_sector_ptr[0x16] || partition_sector_ptr[0x17] || partition_sector_ptr[0x24] || partition_sector_ptr[0x25] || partition_sector_ptr[0x26] || partition_sector_ptr[0x27])
406         {
407 
408             /* There are values for sectors per FAT.  */
409 
410             /* Get the total sectors, FAT12/16.  */
411             total_sectors =  _fx_utility_16_unsigned_read(&partition_sector_ptr[FX_SECTORS]);
412 
413             if (total_sectors == 0)
414             {
415 
416                 /* Get the total sectors, FAT32.  */
417                 total_sectors = _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_HUGE_SECTORS]);
418             }
419 
420             /* Determine if there is a total sector count.  */
421             if (total_sectors)
422             {
423 
424                 if (partition_start != FX_NULL)
425                 {
426                     /* Return an offset of 0, size of boot record, and a successful status.  */
427                     *partition_start =  0;
428                 }
429 
430                 /* Determine if the total sectors is required.  */
431                 if (partition_size != FX_NULL)
432                 {
433 
434                     /* Return the total sectors.  */
435                     *partition_size =  (ULONG)(total_sectors & 0xFFFFFFFF);
436                 }
437 
438                 /* Return success!  */
439                 return(FX_SUCCESS);
440             }
441         }
442     }
443 
444     /* Check signature to make sure the buffer is valid.  */
445     if ((partition_sector_ptr[510] != FX_SIG_BYTE_1) || (partition_sector_ptr[511] != FX_SIG_BYTE_2))
446     {
447 
448         /* Invalid, return an error.  */
449         return(FX_NOT_FOUND);
450     }
451 
452     /* Not bootable, look for specific partition.  */
453 
454     /* Check if primary partitions are addressed.  */
455     if (partition < 4)
456     {
457 
458         /* Get partition type.  */
459         partition_type =  partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + partition * FX_PARTITION_ENTRY_SIZE + FX_PARTITION_TYPE_OFFSET];
460 
461         /* Check if there is a vaild partition.  */
462         if (partition_type != FX_PARTITION_TYPE_FREE)
463         {
464 
465             /* Return the partition starting sector, if non-NULL.  */
466             if (partition_start != FX_NULL)
467             {
468                 *partition_start = _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + partition * FX_PARTITION_ENTRY_SIZE + FX_PARTITION_LBA_OFFSET]);
469             }
470 
471             /* Return the partition size, if non-NULL.  */
472             if (partition_size != FX_NULL)
473             {
474                 *partition_size =  _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + partition * FX_PARTITION_ENTRY_SIZE + FX_PARTITION_SECTORS_OFFSET]);
475             }
476 
477             /* Return success!  */
478             return(FX_SUCCESS);
479         }
480         else
481         {
482 
483             /* Not partition here.  */
484             return(FX_NOT_FOUND);
485         }
486     }
487 
488     /* Check for invalid parameter.  */
489     if (partition > FX_MAX_PARTITION_COUNT)
490     {
491 
492         /* Return error.  */
493         return(FX_NOT_FOUND);
494     }
495 
496     base_sector = 0;
497 
498     /* Loop to find the extended partition table.  */
499     for(i = FX_PARTITION_TABLE_OFFSET; i <= FX_PARTITION_TABLE_OFFSET + 3 * FX_PARTITION_ENTRY_SIZE; i += FX_PARTITION_ENTRY_SIZE)
500     {
501 
502         /* Get partition type.  */
503         partition_type =  partition_sector_ptr[i + FX_PARTITION_TYPE_OFFSET];
504         if (partition_type == FX_PARTITION_TYPE_EXTENDED || partition_type == FX_PARTITION_TYPE_EXTENDED_LBA)
505         {
506             base_sector =  _fx_utility_32_unsigned_read(&partition_sector_ptr[i + FX_PARTITION_LBA_OFFSET]);
507             break;
508         }
509     }
510 
511     if (base_sector == 0)
512     {
513 
514         /* No extended partition.  */
515         return(FX_NOT_FOUND);
516     }
517 
518     base_sector_extended = base_sector;
519 
520     for (i = 4; i <= partition; i++)
521     {
522 
523         /* Read the partition sector from the device.  Build the read sector
524             command.  */
525         media_ptr -> fx_media_driver_request =          FX_DRIVER_READ;
526         media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
527         media_ptr -> fx_media_driver_buffer =           partition_sector_ptr;
528         media_ptr -> fx_media_driver_logical_sector =   base_sector;
529         media_ptr -> fx_media_driver_sectors =          1;
530         media_ptr -> fx_media_driver_sector_type =      FX_UNKNOWN_SECTOR;
531         media_ptr -> fx_media_hidden_sectors =          0;
532 
533         /* Invoke the driver to read the sector.  */
534         (media_ptr -> fx_media_driver_entry) (media_ptr);
535 
536         /* Determine if the sector was read correctly. */
537         if (media_ptr -> fx_media_driver_status != FX_SUCCESS)
538         {
539 
540             /* Return error.  */
541             return(FX_IO_ERROR);
542         }
543 
544         /* Check signature to make sure the sector is valid.  */
545         if ((partition_sector_ptr[510] != FX_SIG_BYTE_1) || (partition_sector_ptr[511] != FX_SIG_BYTE_2))
546         {
547 
548             /* Invalid, return an error.  */
549             return(FX_NOT_FOUND);
550         }
551 
552         /* Determine if this is the desired partition.  */
553         if (i == partition)
554         {
555 
556             /* Get partition type.  */
557             partition_type =  partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_TYPE_OFFSET];
558             if (partition_type != FX_PARTITION_TYPE_FREE)
559             {
560 
561                 /* Return the partition starting sector, if non-NULL.  */
562                 if (partition_start != FX_NULL)
563                 {
564                     *partition_start = _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_LBA_OFFSET]) + base_sector;
565                 }
566 
567                 /* Return the partition size, if non-NULL.  */
568                 if (partition_size != FX_NULL)
569                 {
570                     *partition_size =  _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_SECTORS_OFFSET]);
571                 }
572 
573                 /* Return success!  */
574                 return(FX_SUCCESS);
575             }
576             else
577             {
578                 /* Not partition here.  */
579                 return(FX_NOT_FOUND);
580             }
581         }
582         else
583         {
584 
585             /* Get partition type.  */
586             partition_type =  partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_ENTRY_SIZE + FX_PARTITION_TYPE_OFFSET];
587             if (partition_type == FX_PARTITION_TYPE_EXTENDED || partition_type == FX_PARTITION_TYPE_EXTENDED_LBA)
588             {
589 
590                 /* Update sector number for next partition table.  */
591                 base_sector =  _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_ENTRY_SIZE + FX_PARTITION_LBA_OFFSET]) + base_sector_extended;
592             }
593             else
594             {
595                 /* No valid partition, get out of the loop.  */
596                 break;
597             }
598         }
599 
600     }
601 
602     /* Return error.  */
603     return(FX_NOT_FOUND);
604 }