xref: /haiku/src/add-ons/kernel/partitioning_systems/intel/PartitionMapParser.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
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
170 		LogicalPartition extended;
171 		LogicalPartition nonExtended;
172 		if (error == B_OK) {
173 			for (int32 i = 0; error == B_OK && i < 4; i++) {
174 				const partition_descriptor *descriptor = &fPTS->table[i];
175 				LogicalPartition *partition = NULL;
176 				if (!descriptor->is_empty()) {
177 					if (descriptor->is_extended()) {
178 						if (extended.IsEmpty()) {
179 							extended.SetTo(descriptor, offset, primary);
180 							partition = &extended;
181 						} else {
182 							// only one extended partition allowed
183 							error = B_BAD_DATA;
184 							TRACE(("intel: _ParseExtended(): "
185 								   "only one extended partition allowed\n"));
186 						}
187 					} else {
188 						if (nonExtended.IsEmpty()) {
189 							nonExtended.SetTo(descriptor, offset, primary);
190 							partition = &nonExtended;
191 						} else {
192 							// only one non-extended partition allowed
193 							error = B_BAD_DATA;
194 							TRACE(("intel: _ParseExtended(): only one "
195 								   "non-extended partition allowed\n"));
196 						}
197 					}
198 #ifdef _BOOT_MODE
199 					// work-around potential BIOS problems
200 					if (partition)
201 						partition->AdjustSize(fSessionSize);
202 #endif
203 					// check the partition's location
204 					if (partition && !partition->CheckLocation(fSessionSize)) {
205 						error = B_BAD_DATA;
206 						TRACE(("intel: _ParseExtended(): Invalid partition "
207 							"location: pts: %lld, offset: %lld, size: %lld\n",
208 							partition->PTSOffset(), partition->Offset(),
209 							partition->Size()));
210 					}
211 				}
212 			}
213 		}
214 
215 		// add non-extended partition to list
216 		if (error == B_OK && !nonExtended.IsEmpty()) {
217 			LogicalPartition *partition
218 				= new(nothrow) LogicalPartition(nonExtended);
219 			if (partition)
220 				primary->AddLogicalPartition(partition);
221 			else
222 				error = B_NO_MEMORY;
223 		}
224 
225 		// prepare to parse next extended partition
226 		if (error == B_OK && !extended.IsEmpty())
227 			offset = extended.Offset();
228 		else
229 			break;
230 	}
231 
232 	return error;
233 }
234 
235 // _ReadPTS
236 status_t
237 PartitionMapParser::_ReadPTS(off_t offset, partition_table_sector *pts)
238 {
239 	status_t error = B_OK;
240 	if (!pts)
241 		pts = fPTS;
242 	int32 toRead = sizeof(partition_table_sector);
243 	// check the offset
244 	if (offset < 0 || offset + toRead > fSessionSize) {
245 		error = B_BAD_VALUE;
246 		TRACE(("intel: _ReadPTS(): bad offset: %Ld\n", offset));
247 	// read
248 	} else if (read_pos(fDeviceFD, fSessionOffset + offset, pts, toRead)
249 						!= toRead) {
250 #ifndef _BOOT_MODE
251 		error = errno;
252 		if (error == B_OK)
253 			error = B_IO_ERROR;
254 #else
255 		error = B_IO_ERROR;
256 #endif
257 		TRACE(("intel: _ReadPTS(): reading the PTS failed: %lx\n", error));
258 	}
259 	return error;
260 }
261 
262