| /**************************************************************************** |
| * fs/partition/fs_mbr.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <debug.h> |
| #include <endian.h> |
| #include <string.h> |
| #include <inttypes.h> |
| |
| #include <nuttx/kmalloc.h> |
| |
| #include "partition.h" |
| #include "fs_heap.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #define MBR_SIZE 512 |
| #define MBR_LBA_TO_BLOCK(lba, blk) (((blkcnt_t)le32toh(lba) * 512 + (blk) - 1) / (blk)) |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* These three have identical behaviour; use the second one if DOS FDISK gets |
| * confused about extended/logical partitions starting past cylinder 1023. |
| */ |
| |
| enum mbr_type_e |
| { |
| DOS_EXTENDED_PARTITION = 5, |
| LINUX_EXTENDED_PARTITION = 0x85, |
| WIN98_EXTENDED_PARTITION = 0x0f, |
| }; |
| |
| /* Description of one partition table entry (D*S type) */ |
| |
| begin_packed_struct struct mbr_entry_s |
| { |
| uint8_t boot_indicator; /* Maybe marked as an active partition */ |
| uint8_t chs_begin[3]; /* Start of the partition in cylinders, heads and sectors */ |
| uint8_t type; /* Filesystem type */ |
| uint8_t chs_end[3]; /* End of the partition in cylinders, heads and sectors */ |
| uint32_t partition_start; /* Start of the partition in LBA notation */ |
| uint32_t partition_size; /* Start of the partition in LBA notation */ |
| } end_packed_struct; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| static inline int is_extended(uint8_t type) |
| { |
| return (type == DOS_EXTENDED_PARTITION || |
| type == WIN98_EXTENDED_PARTITION || |
| type == LINUX_EXTENDED_PARTITION); |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: parse_mbr_partition |
| * |
| * Description: |
| * parse the mbr(Master_boot_record) partition. |
| * |
| * Input Parameters: |
| * state - The partition table state |
| * handler - The function to be called for each found partition |
| * arg - A caller provided value to return with the handler |
| * |
| * Returned Value: |
| * Zero on success; A negated errno value is returned on a failure |
| * |
| ****************************************************************************/ |
| |
| int parse_mbr_partition(FAR struct partition_state_s *state, |
| partition_handler_t handler, |
| FAR void *arg) |
| { |
| struct partition_s pentry; |
| FAR struct mbr_entry_s *table; |
| FAR struct mbr_entry_s *extended = NULL; |
| FAR uint8_t *buffer; |
| int num; |
| int ret; |
| int i; |
| |
| num = (MBR_SIZE + state->blocksize - 1) / state->blocksize; |
| buffer = fs_heap_malloc(num * state->blocksize); |
| if (!buffer) |
| { |
| return -ENOMEM; |
| } |
| |
| ret = read_partition_block(state, buffer, 0, num); |
| if (ret < 0) |
| { |
| fs_heap_free(buffer); |
| return ret; |
| } |
| |
| if (buffer[0x1fe] != 0x55 || buffer[0x1ff] != 0xaa) |
| { |
| fs_heap_free(buffer); |
| return -EINVAL; |
| } |
| |
| memset(&pentry, 0, sizeof(pentry)); |
| table = (FAR struct mbr_entry_s *)&buffer[0x1be]; |
| for (i = 0; i < 4; i++) |
| { |
| pentry.firstblock = MBR_LBA_TO_BLOCK(table[i].partition_start, |
| state->blocksize); |
| pentry.nblocks = MBR_LBA_TO_BLOCK(table[i].partition_size, |
| state->blocksize); |
| pentry.blocksize = state->blocksize; |
| |
| if (pentry.nblocks != 0) |
| { |
| if (!is_extended(table[i].type)) |
| { |
| handler(&pentry, arg); |
| pentry.index++; |
| } |
| else if(!extended) |
| { |
| extended = &table[i]; |
| } |
| } |
| else |
| { |
| finfo("Skipping empty partition %d\n", i); |
| } |
| } |
| |
| if (extended) |
| { |
| uint32_t ebr_block; |
| ebr_block = MBR_LBA_TO_BLOCK(extended->partition_start, |
| state->blocksize); |
| while (1) |
| { |
| ret = read_partition_block(state, buffer, ebr_block, num); |
| if (ret < 0) |
| { |
| goto out; |
| } |
| |
| if (buffer[0x1fe] != 0x55 || buffer[0x1ff] != 0xaa) |
| { |
| ferr("block %" PRIu32 " doesn't contain an EBR signature\n", |
| ebr_block); |
| ret = -EINVAL; |
| goto out; |
| } |
| |
| for (i = 0x1de; i < 0x1fe; ++i) |
| { |
| if (buffer[i]) |
| { |
| ferr("EBR's third or fourth partition non-empty\n"); |
| ret = -EINVAL; |
| goto out; |
| } |
| } |
| |
| /* the first entry defines the extended partition */ |
| |
| pentry.firstblock = ebr_block + MBR_LBA_TO_BLOCK( |
| table[0].partition_start, state->blocksize); |
| pentry.nblocks = MBR_LBA_TO_BLOCK(table[0].partition_size, |
| state->blocksize); |
| handler(&pentry, arg); |
| pentry.index++; |
| |
| /* the second entry defines the start of the next ebr if != 0 */ |
| |
| if (table[1].partition_start) |
| { |
| ebr_block = pentry.firstblock + MBR_LBA_TO_BLOCK( |
| table[1].partition_start, state->blocksize); |
| } |
| else |
| { |
| break; |
| } |
| } |
| } |
| |
| out: |
| fs_heap_free(buffer); |
| return ret; |
| } |