summaryrefslogtreecommitdiffstats
path: root/fs/hfs/part_tbl.c
blob: 2211572262bded02c41301a93dee08f2877e0ecc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/*
 * 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;
}