218 lines
6.7 KiB
C
218 lines
6.7 KiB
C
/****************************************************************************
|
|
* fs/partition/fs_mbr.c
|
|
*
|
|
* 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;
|
|
}
|