1 /*
2  * linux/fs/hfs/part_tbl.c
3  *
4  * Copyright (C) 1996-1997  Paul H. Hargrove
5  * This file may be distributed under the terms of the GNU General Public License.
6  *
7  * Original code to handle the new style Mac partition table based on
8  * a patch contributed by Holger Schemel (aeglos@valinor.owl.de).
9  *
10  * "XXX" in a comment is a note to myself to consider changing something.
11  *
12  * In function preconditions the term "valid" applied to a pointer to
13  * a structure means that the pointer is non-NULL and the structure it
14  * points to has all fields initialized to consistent values.
15  *
16  * The code in this file initializes some structures which contain
17  * pointers by calling memset(&foo, 0, sizeof(foo)).
18  * This produces the desired behavior only due to the non-ANSI
19  * assumption that the machine representation of NULL is all zeros.
20  */
21 
22 #include "hfs.h"
23 
24 /*================ File-local data types ================*/
25 
26 /*
27  * The Macintosh Driver Descriptor Block
28  *
29  * On partitioned Macintosh media this is block 0.
30  * We really only need the "magic number" to check for partitioned media.
31  */
32 struct hfs_drvr_desc {
33 	hfs_word_t	ddSig;		/* The signature word */
34 	/* a bunch more stuff we don't need */
35 };
36 
37 /*
38  * The new style Mac partition map
39  *
40  * For each partition on the media there is a physical block (512-byte
41  * block) containing one of these structures.  These blocks are
42  * contiguous starting at block 1.
43  */
44 struct new_pmap {
45 	hfs_word_t	pmSig;		/* Signature bytes to verify
46 					   that this is a partition
47 					   map block */
48 	hfs_word_t	reSigPad;	/* padding */
49 	hfs_lword_t	pmMapBlkCnt;	/* (At least in block 1) this
50 					   is the number of partition
51 					   map blocks */
52 	hfs_lword_t	pmPyPartStart;	/* The physical block number
53 					   of the first block in this
54 					   partition */
55 	hfs_lword_t	pmPartBlkCnt;	/* The number of physical
56 					   blocks in this partition */
57 	hfs_byte_t	pmPartName[32];	/* (null terminated?) string
58 					   giving the name of this
59 					   partition */
60 	hfs_byte_t	pmPartType[32];	/* (null terminated?) string
61 					   giving the type of this
62 					   partition */
63 	/* a bunch more stuff we don't need */
64 };
65 
66 /*
67  * The old style Mac partition map
68  *
69  * The partition map consists for a 2-byte signature followed by an
70  * array of these structures.  The map is terminated with an all-zero
71  * one of these.
72  */
73 struct old_pmap {
74 	hfs_word_t		pdSig;	/* Signature bytes */
75 	struct 	old_pmap_entry {
76 		hfs_lword_t	pdStart;
77 		hfs_lword_t	pdSize;
78 		hfs_lword_t	pdFSID;
79 	}	pdEntry[42];
80 } __attribute__((packed));
81 
82 /*================ File-local functions ================*/
83 
84 /*
85  * parse_new_part_table()
86  *
87  * Parse a new style partition map looking for the
88  * start and length of the 'part'th HFS partition.
89  */
parse_new_part_table(hfs_sysmdb sys_mdb,hfs_buffer buf,int part,hfs_s32 * size,hfs_s32 * start)90 static int parse_new_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf,
91 				int part, hfs_s32 *size, hfs_s32 *start)
92 {
93 	struct new_pmap *pm = (struct new_pmap *)hfs_buffer_data(buf);
94 	hfs_u32 pmap_entries = hfs_get_hl(pm->pmMapBlkCnt);
95 	int hfs_part = 0;
96 	int entry;
97 
98 	for (entry = 0; (entry < pmap_entries) && !(*start); ++entry) {
99 		if (entry) {
100 			/* read the next partition map entry */
101 			buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK + entry, 1);
102 			if (!hfs_buffer_ok(buf)) {
103 				hfs_warn("hfs_fs: unable to "
104 				         "read partition map.\n");
105 				goto bail;
106 			}
107 			pm = (struct new_pmap *)hfs_buffer_data(buf);
108 			if (hfs_get_ns(pm->pmSig) !=
109 						htons(HFS_NEW_PMAP_MAGIC)) {
110 				hfs_warn("hfs_fs: invalid "
111 				         "entry in partition map\n");
112 				hfs_buffer_put(buf);
113 				goto bail;
114 			}
115 		}
116 
117 		/* look for an HFS partition */
118 		if (!memcmp(pm->pmPartType,"Apple_HFS",9) &&
119 		    ((hfs_part++) == part)) {
120 			/* Found it! */
121 			*start = hfs_get_hl(pm->pmPyPartStart);
122 			*size = hfs_get_hl(pm->pmPartBlkCnt);
123 		}
124 
125 		hfs_buffer_put(buf);
126 	}
127 
128 	return 0;
129 
130 bail:
131 	return 1;
132 }
133 
134 /*
135  * parse_old_part_table()
136  *
137  * Parse a old style partition map looking for the
138  * start and length of the 'part'th HFS partition.
139  */
parse_old_part_table(hfs_sysmdb sys_mdb,hfs_buffer buf,int part,hfs_s32 * size,hfs_s32 * start)140 static int parse_old_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf,
141 				int part, hfs_s32 *size, hfs_s32 *start)
142 {
143 	struct old_pmap *pm = (struct old_pmap *)hfs_buffer_data(buf);
144 	struct old_pmap_entry *p = &pm->pdEntry[0];
145 	int hfs_part = 0;
146 
147 	while ((p->pdStart || p->pdSize || p->pdFSID) && !(*start)) {
148 		/* look for an HFS partition */
149 		if ((hfs_get_nl(p->pdFSID) == htonl(0x54465331)/*"TFS1"*/) &&
150 		    ((hfs_part++) == part)) {
151 			/* Found it! */
152 			*start = hfs_get_hl(p->pdStart);
153 			*size = hfs_get_hl(p->pdSize);
154 		}
155 		++p;
156 	}
157 	hfs_buffer_put(buf);
158 
159 	return 0;
160 }
161 
162 /*================ Global functions ================*/
163 
164 /*
165  * hfs_part_find()
166  *
167  * Parse the partition map looking for the
168  * start and length of the 'part'th HFS partition.
169  */
hfs_part_find(hfs_sysmdb sys_mdb,int part,int silent,hfs_s32 * size,hfs_s32 * start)170 int hfs_part_find(hfs_sysmdb sys_mdb, int part, int silent,
171 		  hfs_s32 *size, hfs_s32 *start)
172 {
173 	hfs_buffer buf;
174 	hfs_u16 sig;
175 	int dd_found = 0;
176 	int retval = 1;
177 
178 	/* Read block 0 to see if this media is partitioned */
179 	buf = hfs_buffer_get(sys_mdb, HFS_DD_BLK, 1);
180 	if (!hfs_buffer_ok(buf)) {
181 		hfs_warn("hfs_fs: Unable to read block 0.\n");
182 		goto done;
183 	}
184 	sig = hfs_get_ns(((struct hfs_drvr_desc *)hfs_buffer_data(buf))->ddSig);
185 	hfs_buffer_put(buf);
186 
187         if (sig == htons(HFS_DRVR_DESC_MAGIC)) {
188 		/* We are definitely on partitioned media. */
189 		dd_found = 1;
190 	}
191 
192 	buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK, 1);
193 	if (!hfs_buffer_ok(buf)) {
194 		hfs_warn("hfs_fs: Unable to read block 1.\n");
195 		goto done;
196 	}
197 
198 	*size = *start = 0;
199 
200 	switch (hfs_get_ns(hfs_buffer_data(buf))) {
201 	case __constant_htons(HFS_OLD_PMAP_MAGIC):
202 		retval = parse_old_part_table(sys_mdb, buf, part, size, start);
203 		break;
204 
205 	case __constant_htons(HFS_NEW_PMAP_MAGIC):
206 		retval = parse_new_part_table(sys_mdb, buf, part, size, start);
207 		break;
208 
209 	default:
210 		if (dd_found) {
211 			/* The media claimed to have a partition map */
212 			if (!silent) {
213 				hfs_warn("hfs_fs: This disk has an "
214 					 "unrecognized partition map type.\n");
215 			}
216 		} else {
217 			/* Conclude that the media is not partitioned */
218 			retval = 0;
219 		}
220 		goto done;
221 	}
222 
223 	if (!retval) {
224 		if (*start == 0) {
225 			if (part) {
226 				hfs_warn("hfs_fs: unable to locate "
227 				         "HFS partition number %d.\n", part);
228 			} else {
229 				hfs_warn("hfs_fs: unable to locate any "
230 					 "HFS partitions.\n");
231 			}
232 			retval = 1;
233 		} else if (*size < 0) {
234 			hfs_warn("hfs_fs: Partition size > 1 Terabyte.\n");
235 			retval = 1;
236 		} else if (*start < 0) {
237 			hfs_warn("hfs_fs: Partition begins beyond 1 "
238 				 "Terabyte.\n");
239 			retval = 1;
240 		}
241 	}
242 done:
243 	return retval;
244 }
245