325 lines
11 KiB
C
325 lines
11 KiB
C
|
#include "devices/partition.h"
|
||
|
#include <packed.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <stdio.h>
|
||
|
#include "devices/block.h"
|
||
|
#include "threads/malloc.h"
|
||
|
|
||
|
/* A partition of a block device. */
|
||
|
struct partition
|
||
|
{
|
||
|
struct block *block; /* Underlying block device. */
|
||
|
block_sector_t start; /* First sector within device. */
|
||
|
};
|
||
|
|
||
|
static struct block_operations partition_operations;
|
||
|
|
||
|
static void read_partition_table (struct block *, block_sector_t sector,
|
||
|
block_sector_t primary_extended_sector,
|
||
|
int *part_nr);
|
||
|
static void found_partition (struct block *, uint8_t type,
|
||
|
block_sector_t start, block_sector_t size,
|
||
|
int part_nr);
|
||
|
static const char *partition_type_name (uint8_t);
|
||
|
|
||
|
/* Scans BLOCK for partitions of interest to Pintos. */
|
||
|
void
|
||
|
partition_scan (struct block *block)
|
||
|
{
|
||
|
int part_nr = 0;
|
||
|
read_partition_table (block, 0, 0, &part_nr);
|
||
|
if (part_nr == 0)
|
||
|
printf ("%s: Device contains no partitions\n", block_name (block));
|
||
|
}
|
||
|
|
||
|
/* Reads the partition table in the given SECTOR of BLOCK and
|
||
|
scans it for partitions of interest to Pintos.
|
||
|
|
||
|
If SECTOR is 0, so that this is the top-level partition table
|
||
|
on BLOCK, then PRIMARY_EXTENDED_SECTOR is not meaningful;
|
||
|
otherwise, it should designate the sector of the top-level
|
||
|
extended partition table that was traversed to arrive at
|
||
|
SECTOR, for use in finding logical partitions (see the large
|
||
|
comment below).
|
||
|
|
||
|
PART_NR points to the number of non-empty primary or logical
|
||
|
partitions already encountered on BLOCK. It is incremented as
|
||
|
partitions are found. */
|
||
|
static void
|
||
|
read_partition_table (struct block *block, block_sector_t sector,
|
||
|
block_sector_t primary_extended_sector,
|
||
|
int *part_nr)
|
||
|
{
|
||
|
/* Format of a partition table entry. See [Partitions]. */
|
||
|
struct partition_table_entry
|
||
|
{
|
||
|
uint8_t bootable; /* 0x00=not bootable, 0x80=bootable. */
|
||
|
uint8_t start_chs[3]; /* Encoded starting cylinder, head, sector. */
|
||
|
uint8_t type; /* Partition type (see partition_type_name). */
|
||
|
uint8_t end_chs[3]; /* Encoded ending cylinder, head, sector. */
|
||
|
uint32_t offset; /* Start sector offset from partition table. */
|
||
|
uint32_t size; /* Number of sectors. */
|
||
|
}
|
||
|
PACKED;
|
||
|
|
||
|
/* Partition table sector. */
|
||
|
struct partition_table
|
||
|
{
|
||
|
uint8_t loader[446]; /* Loader, in top-level partition table. */
|
||
|
struct partition_table_entry partitions[4]; /* Table entries. */
|
||
|
uint16_t signature; /* Should be 0xaa55. */
|
||
|
}
|
||
|
PACKED;
|
||
|
|
||
|
struct partition_table *pt;
|
||
|
size_t i;
|
||
|
|
||
|
/* Check SECTOR validity. */
|
||
|
if (sector >= block_size (block))
|
||
|
{
|
||
|
printf ("%s: Partition table at sector %"PRDSNu" past end of device.\n",
|
||
|
block_name (block), sector);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Read sector. */
|
||
|
ASSERT (sizeof *pt == BLOCK_SECTOR_SIZE);
|
||
|
pt = malloc (sizeof *pt);
|
||
|
if (pt == NULL)
|
||
|
PANIC ("Failed to allocate memory for partition table.");
|
||
|
block_read (block, 0, pt);
|
||
|
|
||
|
/* Check signature. */
|
||
|
if (pt->signature != 0xaa55)
|
||
|
{
|
||
|
if (primary_extended_sector == 0)
|
||
|
printf ("%s: Invalid partition table signature\n", block_name (block));
|
||
|
else
|
||
|
printf ("%s: Invalid extended partition table in sector %"PRDSNu"\n",
|
||
|
block_name (block), sector);
|
||
|
free (pt);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Parse partitions. */
|
||
|
for (i = 0; i < sizeof pt->partitions / sizeof *pt->partitions; i++)
|
||
|
{
|
||
|
struct partition_table_entry *e = &pt->partitions[i];
|
||
|
|
||
|
if (e->size == 0 || e->type == 0)
|
||
|
{
|
||
|
/* Ignore empty partition. */
|
||
|
}
|
||
|
else if (e->type == 0x05 /* Extended partition. */
|
||
|
|| e->type == 0x0f /* Windows 98 extended partition. */
|
||
|
|| e->type == 0x85 /* Linux extended partition. */
|
||
|
|| e->type == 0xc5) /* DR-DOS extended partition. */
|
||
|
{
|
||
|
printf ("%s: Extended partition in sector %"PRDSNu"\n",
|
||
|
block_name (block), sector);
|
||
|
|
||
|
/* The interpretation of the offset field for extended
|
||
|
partitions is bizarre. When the extended partition
|
||
|
table entry is in the master boot record, that is,
|
||
|
the device's primary partition table in sector 0, then
|
||
|
the offset is an absolute sector number. Otherwise,
|
||
|
no matter how deep the partition table we're reading
|
||
|
is nested, the offset is relative to the start of
|
||
|
the extended partition that the MBR points to. */
|
||
|
if (sector == 0)
|
||
|
read_partition_table (block, e->offset, e->offset, part_nr);
|
||
|
else
|
||
|
read_partition_table (block, e->offset + primary_extended_sector,
|
||
|
primary_extended_sector, part_nr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
++*part_nr;
|
||
|
|
||
|
found_partition (block, e->type, e->offset + sector,
|
||
|
e->size, *part_nr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
free (pt);
|
||
|
}
|
||
|
|
||
|
/* We have found a primary or logical partition of the given TYPE
|
||
|
on BLOCK, starting at sector START and continuing for SIZE
|
||
|
sectors, which we are giving the partition number PART_NR.
|
||
|
Check whether this is a partition of interest to Pintos, and
|
||
|
if so then add it to the proper element of partitions[]. */
|
||
|
static void
|
||
|
found_partition (struct block *block, uint8_t part_type,
|
||
|
block_sector_t start, block_sector_t size,
|
||
|
int part_nr)
|
||
|
{
|
||
|
if (start >= block_size (block))
|
||
|
printf ("%s%d: Partition starts past end of device (sector %"PRDSNu")\n",
|
||
|
block_name (block), part_nr, start);
|
||
|
else if (start + size < start || start + size > block_size (block))
|
||
|
printf ("%s%d: Partition end (%"PRDSNu") past end of device (%"PRDSNu")\n",
|
||
|
block_name (block), part_nr, start + size, block_size (block));
|
||
|
else
|
||
|
{
|
||
|
enum block_type type = (part_type == 0x20 ? BLOCK_KERNEL
|
||
|
: part_type == 0x21 ? BLOCK_FILESYS
|
||
|
: part_type == 0x22 ? BLOCK_SCRATCH
|
||
|
: part_type == 0x23 ? BLOCK_SWAP
|
||
|
: BLOCK_FOREIGN);
|
||
|
struct partition *p;
|
||
|
char extra_info[128];
|
||
|
char name[16];
|
||
|
|
||
|
p = malloc (sizeof *p);
|
||
|
if (p == NULL)
|
||
|
PANIC ("Failed to allocate memory for partition descriptor");
|
||
|
p->block = block;
|
||
|
p->start = start;
|
||
|
|
||
|
snprintf (name, sizeof name, "%s%d", block_name (block), part_nr);
|
||
|
snprintf (extra_info, sizeof extra_info, "%s (%02x)",
|
||
|
partition_type_name (part_type), part_type);
|
||
|
block_register (name, type, extra_info, size, &partition_operations, p);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Returns a human-readable name for the given partition TYPE. */
|
||
|
static const char *
|
||
|
partition_type_name (uint8_t type)
|
||
|
{
|
||
|
/* Name of each known type of partition.
|
||
|
From util-linux-2.12r/fdisk/i386_sys_types.c.
|
||
|
This initializer makes use of a C99 feature that allows
|
||
|
array elements to be initialized by index. */
|
||
|
static const char *type_names[256] =
|
||
|
{
|
||
|
[0x00] = "Empty",
|
||
|
[0x01] = "FAT12",
|
||
|
[0x02] = "XENIX root",
|
||
|
[0x03] = "XENIX usr",
|
||
|
[0x04] = "FAT16 <32M",
|
||
|
[0x05] = "Extended",
|
||
|
[0x06] = "FAT16",
|
||
|
[0x07] = "HPFS/NTFS",
|
||
|
[0x08] = "AIX",
|
||
|
[0x09] = "AIX bootable",
|
||
|
[0x0a] = "OS/2 Boot Manager",
|
||
|
[0x0b] = "W95 FAT32",
|
||
|
[0x0c] = "W95 FAT32 (LBA)",
|
||
|
[0x0e] = "W95 FAT16 (LBA)",
|
||
|
[0x0f] = "W95 Ext'd (LBA)",
|
||
|
[0x10] = "OPUS",
|
||
|
[0x11] = "Hidden FAT12",
|
||
|
[0x12] = "Compaq diagnostics",
|
||
|
[0x14] = "Hidden FAT16 <32M",
|
||
|
[0x16] = "Hidden FAT16",
|
||
|
[0x17] = "Hidden HPFS/NTFS",
|
||
|
[0x18] = "AST SmartSleep",
|
||
|
[0x1b] = "Hidden W95 FAT32",
|
||
|
[0x1c] = "Hidden W95 FAT32 (LBA)",
|
||
|
[0x1e] = "Hidden W95 FAT16 (LBA)",
|
||
|
[0x20] = "Pintos OS kernel",
|
||
|
[0x21] = "Pintos file system",
|
||
|
[0x22] = "Pintos scratch",
|
||
|
[0x23] = "Pintos swap",
|
||
|
[0x24] = "NEC DOS",
|
||
|
[0x39] = "Plan 9",
|
||
|
[0x3c] = "PartitionMagic recovery",
|
||
|
[0x40] = "Venix 80286",
|
||
|
[0x41] = "PPC PReP Boot",
|
||
|
[0x42] = "SFS",
|
||
|
[0x4d] = "QNX4.x",
|
||
|
[0x4e] = "QNX4.x 2nd part",
|
||
|
[0x4f] = "QNX4.x 3rd part",
|
||
|
[0x50] = "OnTrack DM",
|
||
|
[0x51] = "OnTrack DM6 Aux1",
|
||
|
[0x52] = "CP/M",
|
||
|
[0x53] = "OnTrack DM6 Aux3",
|
||
|
[0x54] = "OnTrackDM6",
|
||
|
[0x55] = "EZ-Drive",
|
||
|
[0x56] = "Golden Bow",
|
||
|
[0x5c] = "Priam Edisk",
|
||
|
[0x61] = "SpeedStor",
|
||
|
[0x63] = "GNU HURD or SysV",
|
||
|
[0x64] = "Novell Netware 286",
|
||
|
[0x65] = "Novell Netware 386",
|
||
|
[0x70] = "DiskSecure Multi-Boot",
|
||
|
[0x75] = "PC/IX",
|
||
|
[0x80] = "Old Minix",
|
||
|
[0x81] = "Minix / old Linux",
|
||
|
[0x82] = "Linux swap / Solaris",
|
||
|
[0x83] = "Linux",
|
||
|
[0x84] = "OS/2 hidden C: drive",
|
||
|
[0x85] = "Linux extended",
|
||
|
[0x86] = "NTFS volume set",
|
||
|
[0x87] = "NTFS volume set",
|
||
|
[0x88] = "Linux plaintext",
|
||
|
[0x8e] = "Linux LVM",
|
||
|
[0x93] = "Amoeba",
|
||
|
[0x94] = "Amoeba BBT",
|
||
|
[0x9f] = "BSD/OS",
|
||
|
[0xa0] = "IBM Thinkpad hibernation",
|
||
|
[0xa5] = "FreeBSD",
|
||
|
[0xa6] = "OpenBSD",
|
||
|
[0xa7] = "NeXTSTEP",
|
||
|
[0xa8] = "Darwin UFS",
|
||
|
[0xa9] = "NetBSD",
|
||
|
[0xab] = "Darwin boot",
|
||
|
[0xb7] = "BSDI fs",
|
||
|
[0xb8] = "BSDI swap",
|
||
|
[0xbb] = "Boot Wizard hidden",
|
||
|
[0xbe] = "Solaris boot",
|
||
|
[0xbf] = "Solaris",
|
||
|
[0xc1] = "DRDOS/sec (FAT-12)",
|
||
|
[0xc4] = "DRDOS/sec (FAT-16 < 32M)",
|
||
|
[0xc6] = "DRDOS/sec (FAT-16)",
|
||
|
[0xc7] = "Syrinx",
|
||
|
[0xda] = "Non-FS data",
|
||
|
[0xdb] = "CP/M / CTOS / ...",
|
||
|
[0xde] = "Dell Utility",
|
||
|
[0xdf] = "BootIt",
|
||
|
[0xe1] = "DOS access",
|
||
|
[0xe3] = "DOS R/O",
|
||
|
[0xe4] = "SpeedStor",
|
||
|
[0xeb] = "BeOS fs",
|
||
|
[0xee] = "EFI GPT",
|
||
|
[0xef] = "EFI (FAT-12/16/32)",
|
||
|
[0xf0] = "Linux/PA-RISC boot",
|
||
|
[0xf1] = "SpeedStor",
|
||
|
[0xf4] = "SpeedStor",
|
||
|
[0xf2] = "DOS secondary",
|
||
|
[0xfd] = "Linux raid autodetect",
|
||
|
[0xfe] = "LANstep",
|
||
|
[0xff] = "BBT",
|
||
|
};
|
||
|
|
||
|
return type_names[type] != NULL ? type_names[type] : "Unknown";
|
||
|
}
|
||
|
|
||
|
/* Reads sector SECTOR from partition P into BUFFER, which must
|
||
|
have room for BLOCK_SECTOR_SIZE bytes. */
|
||
|
static void
|
||
|
partition_read (void *p_, block_sector_t sector, void *buffer)
|
||
|
{
|
||
|
struct partition *p = p_;
|
||
|
block_read (p->block, p->start + sector, buffer);
|
||
|
}
|
||
|
|
||
|
/* Write sector SECTOR to partition P from BUFFER, which must
|
||
|
contain BLOCK_SECTOR_SIZE bytes. Returns after the block has
|
||
|
acknowledged receiving the data. */
|
||
|
static void
|
||
|
partition_write (void *p_, block_sector_t sector, const void *buffer)
|
||
|
{
|
||
|
struct partition *p = p_;
|
||
|
block_write (p->block, p->start + sector, buffer);
|
||
|
}
|
||
|
|
||
|
static struct block_operations partition_operations =
|
||
|
{
|
||
|
partition_read,
|
||
|
partition_write
|
||
|
};
|