blob: 2211572262bded02c41301a93dee08f2877e0ecc [file] [log] [blame]
/*
* linux/fs/hfs/part_tbl.c
*
* Copyright (C) 1996-1997 Paul H. Hargrove
* This file may be distributed under the terms of the GNU General Public License.
*
* Original code to handle the new style Mac partition table based on
* a patch contributed by Holger Schemel (aeglos@valinor.owl.de).
*
* "XXX" in a comment is a note to myself to consider changing something.
*
* In function preconditions the term "valid" applied to a pointer to
* a structure means that the pointer is non-NULL and the structure it
* points to has all fields initialized to consistent values.
*
* The code in this file initializes some structures which contain
* pointers by calling memset(&foo, 0, sizeof(foo)).
* This produces the desired behavior only due to the non-ANSI
* assumption that the machine representation of NULL is all zeros.
*/
#include "hfs.h"
/*================ File-local data types ================*/
/*
* The Macintosh Driver Descriptor Block
*
* On partitioned Macintosh media this is block 0.
* We really only need the "magic number" to check for partitioned media.
*/
struct hfs_drvr_desc {
hfs_word_t ddSig; /* The signature word */
/* a bunch more stuff we don't need */
};
/*
* The new style Mac partition map
*
* For each partition on the media there is a physical block (512-byte
* block) containing one of these structures. These blocks are
* contiguous starting at block 1.
*/
struct new_pmap {
hfs_word_t pmSig; /* Signature bytes to verify
that this is a partition
map block */
hfs_word_t reSigPad; /* padding */
hfs_lword_t pmMapBlkCnt; /* (At least in block 1) this
is the number of partition
map blocks */
hfs_lword_t pmPyPartStart; /* The physical block number
of the first block in this
partition */
hfs_lword_t pmPartBlkCnt; /* The number of physical
blocks in this partition */
hfs_byte_t pmPartName[32]; /* (null terminated?) string
giving the name of this
partition */
hfs_byte_t pmPartType[32]; /* (null terminated?) string
giving the type of this
partition */
/* a bunch more stuff we don't need */
};
/*
* The old style Mac partition map
*
* The partition map consists for a 2-byte signature followed by an
* array of these structures. The map is terminated with an all-zero
* one of these.
*/
struct old_pmap {
hfs_word_t pdSig; /* Signature bytes */
struct old_pmap_entry {
hfs_lword_t pdStart;
hfs_lword_t pdSize;
hfs_lword_t pdFSID;
} pdEntry[42];
} __attribute__((packed));
/*================ File-local functions ================*/
/*
* parse_new_part_table()
*
* Parse a new style partition map looking for the
* start and length of the 'part'th HFS partition.
*/
static int parse_new_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf,
int part, hfs_s32 *size, hfs_s32 *start)
{
struct new_pmap *pm = (struct new_pmap *)hfs_buffer_data(buf);
hfs_u32 pmap_entries = hfs_get_hl(pm->pmMapBlkCnt);
int hfs_part = 0;
int entry;
for (entry = 0; (entry < pmap_entries) && !(*start); ++entry) {
if (entry) {
/* read the next partition map entry */
buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK + entry, 1);
if (!hfs_buffer_ok(buf)) {
hfs_warn("hfs_fs: unable to "
"read partition map.\n");
goto bail;
}
pm = (struct new_pmap *)hfs_buffer_data(buf);
if (hfs_get_ns(pm->pmSig) !=
htons(HFS_NEW_PMAP_MAGIC)) {
hfs_warn("hfs_fs: invalid "
"entry in partition map\n");
hfs_buffer_put(buf);
goto bail;
}
}
/* look for an HFS partition */
if (!memcmp(pm->pmPartType,"Apple_HFS",9) &&
((hfs_part++) == part)) {
/* Found it! */
*start = hfs_get_hl(pm->pmPyPartStart);
*size = hfs_get_hl(pm->pmPartBlkCnt);
}
hfs_buffer_put(buf);
}
return 0;
bail:
return 1;
}
/*
* parse_old_part_table()
*
* Parse a old style partition map looking for the
* start and length of the 'part'th HFS partition.
*/
static int parse_old_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf,
int part, hfs_s32 *size, hfs_s32 *start)
{
struct old_pmap *pm = (struct old_pmap *)hfs_buffer_data(buf);
struct old_pmap_entry *p = &pm->pdEntry[0];
int hfs_part = 0;
while ((p->pdStart || p->pdSize || p->pdFSID) && !(*start)) {
/* look for an HFS partition */
if ((hfs_get_nl(p->pdFSID) == htonl(0x54465331)/*"TFS1"*/) &&
((hfs_part++) == part)) {
/* Found it! */
*start = hfs_get_hl(p->pdStart);
*size = hfs_get_hl(p->pdSize);
}
++p;
}
hfs_buffer_put(buf);
return 0;
}
/*================ Global functions ================*/
/*
* hfs_part_find()
*
* Parse the partition map looking for the
* start and length of the 'part'th HFS partition.
*/
int hfs_part_find(hfs_sysmdb sys_mdb, int part, int silent,
hfs_s32 *size, hfs_s32 *start)
{
hfs_buffer buf;
hfs_u16 sig;
int dd_found = 0;
int retval = 1;
/* Read block 0 to see if this media is partitioned */
buf = hfs_buffer_get(sys_mdb, HFS_DD_BLK, 1);
if (!hfs_buffer_ok(buf)) {
hfs_warn("hfs_fs: Unable to read block 0.\n");
goto done;
}
sig = hfs_get_ns(((struct hfs_drvr_desc *)hfs_buffer_data(buf))->ddSig);
hfs_buffer_put(buf);
if (sig == htons(HFS_DRVR_DESC_MAGIC)) {
/* We are definitely on partitioned media. */
dd_found = 1;
}
buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK, 1);
if (!hfs_buffer_ok(buf)) {
hfs_warn("hfs_fs: Unable to read block 1.\n");
goto done;
}
*size = *start = 0;
switch (hfs_get_ns(hfs_buffer_data(buf))) {
case __constant_htons(HFS_OLD_PMAP_MAGIC):
retval = parse_old_part_table(sys_mdb, buf, part, size, start);
break;
case __constant_htons(HFS_NEW_PMAP_MAGIC):
retval = parse_new_part_table(sys_mdb, buf, part, size, start);
break;
default:
if (dd_found) {
/* The media claimed to have a partition map */
if (!silent) {
hfs_warn("hfs_fs: This disk has an "
"unrecognized partition map type.\n");
}
} else {
/* Conclude that the media is not partitioned */
retval = 0;
}
goto done;
}
if (!retval) {
if (*start == 0) {
if (part) {
hfs_warn("hfs_fs: unable to locate "
"HFS partition number %d.\n", part);
} else {
hfs_warn("hfs_fs: unable to locate any "
"HFS partitions.\n");
}
retval = 1;
} else if (*size < 0) {
hfs_warn("hfs_fs: Partition size > 1 Terabyte.\n");
retval = 1;
} else if (*start < 0) {
hfs_warn("hfs_fs: Partition begins beyond 1 "
"Terabyte.\n");
retval = 1;
}
}
done:
return retval;
}