xref: /haiku/src/add-ons/kernel/partitioning_systems/intel/PartitionMapParser.cpp (revision 0562493379cd52eb7103531f895f10bb8e77c085)
1 /*
2  * Copyright 2003-2008, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Ingo Weinhold, bonefish@cs.tu-berlin.de
7  */
8 
9 
10 #ifndef _USER_MODE
11 #	include <KernelExport.h>
12 #endif
13 
14 #include <errno.h>
15 #include <stdio.h>
16 #include <unistd.h>
17 #include <string.h>
18 
19 #include <new>
20 
21 #include "PartitionMap.h"
22 #include "PartitionMapParser.h"
23 
24 
25 //#define TRACE_ENABLED
26 #ifdef TRACE_ENABLED
27 #	ifdef _USER_MODE
28 #		define TRACE(x) printf x
29 #	else
30 #		define TRACE(x) dprintf x
31 #	endif
32 #else
33 #	define TRACE(x) ;
34 #endif
35 
36 using std::nothrow;
37 
38 // Maximal number of logical partitions per extended partition we allow.
39 static const int32 kMaxLogicalPartitionCount = 128;
40 
41 // constructor
42 PartitionMapParser::PartitionMapParser(int deviceFD, off_t sessionOffset,
43 		off_t sessionSize)
44 	: fDeviceFD(deviceFD),
45 	  fSessionOffset(sessionOffset),
46 	  fSessionSize(sessionSize),
47 	  fPTS(NULL),
48 	  fMap(NULL)
49 {
50 }
51 
52 // destructor
53 PartitionMapParser::~PartitionMapParser()
54 {
55 }
56 
57 // Parse
58 status_t
59 PartitionMapParser::Parse(const uint8 *block, PartitionMap *map)
60 {
61 	status_t error = (map ? B_OK : B_BAD_VALUE);
62 	if (error == B_OK) {
63 		fMap = map;
64 		fMap->Unset();
65 		if (block) {
66 			const partition_table_sector *pts
67 				= (const partition_table_sector*)block;
68 			error = _ParsePrimary(pts);
69 		} else {
70 			partition_table_sector pts;
71 			error = _ReadPTS(0, &pts);
72 			if (error == B_OK)
73 				error = _ParsePrimary(&pts);
74 		}
75 
76 		if (error == B_OK && !fMap->Check(fSessionSize))
77 			error = B_BAD_DATA;
78 
79 		fMap = NULL;
80 	}
81 	return error;
82 }
83 
84 // _ParsePrimary
85 status_t
86 PartitionMapParser::_ParsePrimary(const partition_table_sector *pts)
87 {
88 	if (pts == NULL)
89 		return B_BAD_VALUE;
90 
91 	// check the signature
92 	if (pts->signature != kPartitionTableSectorSignature) {
93 		TRACE(("intel: _ParsePrimary(): invalid PTS signature: %lx\n",
94 			(uint32)pts->signature));
95 		return B_BAD_DATA;
96 	}
97 
98 	// examine the table
99 	for (int32 i = 0; i < 4; i++) {
100 		const partition_descriptor *descriptor = &pts->table[i];
101 		PrimaryPartition *partition = fMap->PrimaryPartitionAt(i);
102 		partition->SetTo(descriptor, 0);
103 
104 #ifdef _BOOT_MODE
105 		// work-around potential BIOS problems
106 		partition->AdjustSize(fSessionSize);
107 #endif
108 		// ignore, if location is bad
109 		if (!partition->CheckLocation(fSessionSize)) {
110 			TRACE(("intel: _ParsePrimary(): partition %ld: bad location, "
111 				"ignoring\n", i));
112 			partition->Unset();
113 		}
114 	}
115 
116 	// allocate a PTS buffer
117 	fPTS = new(nothrow) partition_table_sector;
118 	if (fPTS == NULL)
119 		return B_NO_MEMORY;
120 
121 	// parse extended partitions
122 	status_t error = B_OK;
123 	for (int32 i = 0; error == B_OK && i < 4; i++) {
124 		PrimaryPartition *primary = fMap->PrimaryPartitionAt(i);
125 		if (primary->IsExtended())
126 			error = _ParseExtended(primary, primary->Offset());
127 	}
128 
129 	// cleanup
130 	delete fPTS;
131 	fPTS = NULL;
132 
133 	return error;
134 }
135 
136 // _ParseExtended
137 status_t
138 PartitionMapParser::_ParseExtended(PrimaryPartition *primary, off_t offset)
139 {
140 	status_t error = B_OK;
141 	int32 partitionCount = 0;
142 	while (error == B_OK) {
143 		// check for cycles
144 		if (++partitionCount > kMaxLogicalPartitionCount) {
145 			TRACE(("intel: _ParseExtended(): Maximal number of logical "
146 				   "partitions for extended partition reached. Cycle?\n"));
147 			error = B_BAD_DATA;
148 		}
149 
150 		// read the PTS
151 		if (error == B_OK)
152 			error = _ReadPTS(offset);
153 
154 		// check the signature
155 		if (error == B_OK
156 			&& fPTS->signature != kPartitionTableSectorSignature) {
157 			TRACE(("intel: _ParseExtended(): invalid PTS signature: %lx\n",
158 				(uint32)fPTS->signature));
159 			error = B_BAD_DATA;
160 		}
161 
162 		// ignore the PTS, if any error occured till now
163 		if (error != B_OK) {
164 			TRACE(("intel: _ParseExtended(): ignoring this PTS\n"));
165 			error = B_OK;
166 			break;
167 		}
168 
169 		// Examine the table, there is exactly one extended and one
170 		// non-extended logical partition. All four table entries are
171 		// examined though. If there is no inner extended partition,
172 		// the end of the linked list is reached.
173 		// The first PTS describing both an "inner extended" parition and a
174 		// "data" partition (non extended and not empty) is the start sector
175 		// of the primary extended partition. The next PTS in the linked list
176 		// is the start sector of the inner extended partition described in
177 		// this PTS.
178 		LogicalPartition extended;
179 		LogicalPartition nonExtended;
180 		for (int32 i = 0; error == B_OK && i < 4; i++) {
181 			const partition_descriptor *descriptor = &fPTS->table[i];
182 			if (descriptor->is_empty())
183 				continue;
184 
185 			LogicalPartition *partition = NULL;
186 			if (descriptor->is_extended()) {
187 				if (extended.IsEmpty()) {
188 					extended.SetTo(descriptor, offset, primary);
189 					partition = &extended;
190 				} else {
191 					// only one extended partition allowed
192 					error = B_BAD_DATA;
193 					TRACE(("intel: _ParseExtended(): "
194 						   "only one extended partition allowed\n"));
195 				}
196 			} else {
197 				if (nonExtended.IsEmpty()) {
198 					nonExtended.SetTo(descriptor, offset, primary);
199 					partition = &nonExtended;
200 				} else {
201 					// only one non-extended partition allowed
202 					error = B_BAD_DATA;
203 					TRACE(("intel: _ParseExtended(): only one "
204 						   "non-extended partition allowed\n"));
205 				}
206 			}
207 			if (partition == NULL)
208 				break;
209 #ifdef _BOOT_MODE
210 			// work-around potential BIOS problems
211 			partition->AdjustSize(fSessionSize);
212 #endif
213 			// check the partition's location
214 			if (!partition->CheckLocation(fSessionSize)) {
215 				error = B_BAD_DATA;
216 				TRACE(("intel: _ParseExtended(): Invalid partition "
217 					"location: pts: %lld, offset: %lld, size: %lld\n",
218 					partition->PTSOffset(), partition->Offset(),
219 					partition->Size()));
220 			}
221 		}
222 
223 		// add non-extended partition to list
224 		if (error == B_OK && !nonExtended.IsEmpty()) {
225 			LogicalPartition *partition
226 				= new(nothrow) LogicalPartition(nonExtended);
227 			if (partition)
228 				primary->AddLogicalPartition(partition);
229 			else
230 				error = B_NO_MEMORY;
231 		}
232 
233 		// prepare to parse next extended/non-extended partition pair
234 		if (error == B_OK && !extended.IsEmpty())
235 			offset = extended.Offset();
236 		else
237 			break;
238 	}
239 
240 	return error;
241 }
242 
243 // _ReadPTS
244 status_t
245 PartitionMapParser::_ReadPTS(off_t offset, partition_table_sector *pts)
246 {
247 	status_t error = B_OK;
248 	if (!pts)
249 		pts = fPTS;
250 	int32 toRead = sizeof(partition_table_sector);
251 	// check the offset
252 	if (offset < 0 || offset + toRead > fSessionSize) {
253 		error = B_BAD_VALUE;
254 		TRACE(("intel: _ReadPTS(): bad offset: %Ld\n", offset));
255 	// read
256 	} else if (read_pos(fDeviceFD, fSessionOffset + offset, pts, toRead)
257 						!= toRead) {
258 #ifndef _BOOT_MODE
259 		error = errno;
260 		if (error == B_OK)
261 			error = B_IO_ERROR;
262 #else
263 		error = B_IO_ERROR;
264 #endif
265 		TRACE(("intel: _ReadPTS(): reading the PTS failed: %lx\n", error));
266 	}
267 	return error;
268 }
269 
270