xref: /haiku/src/add-ons/kernel/partitioning_systems/intel/PartitionMapParser.cpp (revision 55b40aa53a835472ec7952b138ae4256203d02e4)
1 /*
2  * Copyright 2003-2006, 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 #define TRACE_ENABLED
25 
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 #endif
33 
34 using std::nothrow;
35 
36 // Maximal number of logical partitions per extended partition we allow.
37 static const int32 kMaxLogicalPartitionCount = 128;
38 
39 // constructor
40 PartitionMapParser::PartitionMapParser(int deviceFD, off_t sessionOffset,
41 									   off_t sessionSize, int32 blockSize)
42 	: fDeviceFD(deviceFD),
43 	  fSessionOffset(sessionOffset),
44 	  fSessionSize(sessionSize),
45 	  fBlockSize(blockSize),
46 	  fPTS(NULL),
47 	  fMap(NULL)
48 {
49 }
50 
51 // destructor
52 PartitionMapParser::~PartitionMapParser()
53 {
54 }
55 
56 // Parse
57 status_t
58 PartitionMapParser::Parse(const uint8 *block, PartitionMap *map)
59 {
60 	status_t error = (map ? B_OK : B_BAD_VALUE);
61 	if (error == B_OK) {
62 		fMap = map;
63 		fMap->Unset();
64 		if (block) {
65 			const partition_table_sector *pts
66 				= (const partition_table_sector*)block;
67 			error = _ParsePrimary(pts);
68 		} else {
69 			partition_table_sector pts;
70 			error = _ReadPTS(0, &pts);
71 			if (error == B_OK)
72 				error = _ParsePrimary(&pts);
73 		}
74 
75 		// If we don't have any partitions it might also just be an
76 		// empty partition map, but we still can't do much with it
77 		if (error == B_OK
78 			&& (fMap->CountNonEmptyPartitions() == 0
79 				|| !fMap->Check(fSessionSize, fBlockSize))) {
80 			error = B_BAD_DATA;
81 		}
82 		fMap = NULL;
83 	}
84 	return error;
85 }
86 
87 // _ParsePrimary
88 status_t
89 PartitionMapParser::_ParsePrimary(const partition_table_sector *pts)
90 {
91 	if (pts == NULL)
92 		return B_BAD_VALUE;
93 
94 	// check the signature
95 	if (pts->signature != kPartitionTableSectorSignature) {
96 		TRACE(("intel: _ParsePrimary(): invalid PTS signature\n"));
97 		return B_BAD_DATA;
98 	}
99 
100 	// examine the table
101 	for (int32 i = 0; i < 4; i++) {
102 		const partition_descriptor *descriptor = &pts->table[i];
103 		PrimaryPartition *partition = fMap->PrimaryPartitionAt(i);
104 		partition->SetTo(descriptor, 0, fBlockSize);
105 
106 #ifdef _BOOT_MODE
107 		// work-around potential BIOS problems
108 		partition->AdjustSize(fSessionSize);
109 #endif
110 		// ignore, if location is bad
111 		if (!partition->CheckLocation(fSessionSize, fBlockSize)) {
112 			TRACE(("intel: _ParsePrimary(): partition %ld: bad location, "
113 				"ignoring\n", i));
114 			partition->Unset();
115 		}
116 	}
117 
118 	// allocate a PTS buffer
119 	fPTS = new(nothrow) partition_table_sector;
120 	if (fPTS == NULL)
121 		return B_NO_MEMORY;
122 
123 	// parse extended partitions
124 	status_t error = B_OK;
125 	for (int32 i = 0; error == B_OK && i < 4; i++) {
126 		PrimaryPartition *primary = fMap->PrimaryPartitionAt(i);
127 		if (primary->IsExtended())
128 			error = _ParseExtended(primary, primary->Offset());
129 	}
130 
131 	// cleanup
132 	delete fPTS;
133 	fPTS = NULL;
134 
135 	return error;
136 }
137 
138 // _ParseExtended
139 status_t
140 PartitionMapParser::_ParseExtended(PrimaryPartition *primary, off_t offset)
141 {
142 	status_t error = B_OK;
143 	int32 partitionCount = 0;
144 	while (error == B_OK) {
145 		// check for cycles
146 		if (++partitionCount > kMaxLogicalPartitionCount) {
147 			TRACE(("intel: _ParseExtended(): Maximal number of logical "
148 				   "partitions for extended partition reached. Cycle?\n"));
149 			error = B_BAD_DATA;
150 		}
151 
152 		// read the PTS
153 		if (error == B_OK)
154 			error = _ReadPTS(offset);
155 
156 		// check the signature
157 		if (error == B_OK
158 			&& fPTS->signature != kPartitionTableSectorSignature) {
159 			TRACE(("intel: _ParseExtended(): invalid PTS signature\n"));
160 			error = B_BAD_DATA;
161 		}
162 
163 		// ignore the PTS, if any error occured till now
164 		if (error != B_OK) {
165 			TRACE(("intel: _ParseExtended(): ignoring this PTS\n"));
166 			error = B_OK;
167 			break;
168 		}
169 
170 		// examine the table
171 		LogicalPartition extended;
172 		LogicalPartition nonExtended;
173 		if (error == B_OK) {
174 			for (int32 i = 0; error == B_OK && i < 4; i++) {
175 				const partition_descriptor *descriptor = &fPTS->table[i];
176 				LogicalPartition *partition = NULL;
177 				if (!descriptor->is_empty()) {
178 					if (descriptor->is_extended()) {
179 						if (extended.IsEmpty()) {
180 							extended.SetTo(descriptor, offset, fBlockSize,
181 										   primary);
182 							partition = &extended;
183 						} else {
184 							// only one extended partition allowed
185 							error = B_BAD_DATA;
186 							TRACE(("intel: _ParseExtended(): "
187 								   "only one extended partition allowed\n"));
188 						}
189 					} else {
190 						if (nonExtended.IsEmpty()) {
191 							nonExtended.SetTo(descriptor, offset, fBlockSize,
192 											  primary);
193 							partition = &nonExtended;
194 						} else {
195 							// only one non-extended partition allowed
196 							error = B_BAD_DATA;
197 							TRACE(("intel: _ParseExtended(): only one "
198 								   "non-extended partition allowed\n"));
199 						}
200 					}
201 #ifdef _BOOT_MODE
202 					// work-around potential BIOS problems
203 					if (partition)
204 						partition->AdjustSize(fSessionSize);
205 #endif
206 					// check the partition's location
207 					if (partition && !partition->CheckLocation(fSessionSize,
208 															   fBlockSize)) {
209 						error = B_BAD_DATA;
210 						TRACE(("intel: _ParseExtended(): Invalid partition "
211 							"location: pts: %lld, offset: %lld, size: %lld\n",
212 							partition->PTSOffset(), partition->Offset(),
213 							partition->Size()));
214 					}
215 				}
216 			}
217 		}
218 
219 		// add non-extended partition to list
220 		if (error == B_OK && !nonExtended.IsEmpty()) {
221 			LogicalPartition *partition
222 				= new(nothrow) LogicalPartition(nonExtended);
223 			if (partition)
224 				primary->AddLogicalPartition(partition);
225 			else
226 				error = B_NO_MEMORY;
227 		}
228 
229 		// prepare to parse next extended partition
230 		if (error == B_OK && !extended.IsEmpty())
231 			offset = extended.Offset();
232 		else
233 			break;
234 	}
235 
236 	return error;
237 }
238 
239 // _ReadPTS
240 status_t
241 PartitionMapParser::_ReadPTS(off_t offset, partition_table_sector *pts)
242 {
243 	status_t error = B_OK;
244 	if (!pts)
245 		pts = fPTS;
246 	int32 toRead = sizeof(partition_table_sector);
247 	// check the offset
248 	if (offset < 0 || offset + toRead > fSessionSize) {
249 		error = B_BAD_VALUE;
250 		TRACE(("intel: _ReadPTS(): bad offset: %Ld\n", offset));
251 	// read
252 	} else if (read_pos(fDeviceFD, fSessionOffset + offset, pts, toRead)
253 						!= toRead) {
254 #ifndef _BOOT_MODE
255 		error = errno;
256 		if (error == B_OK)
257 			error = B_IO_ERROR;
258 #else
259 		error = B_IO_ERROR;
260 #endif
261 		TRACE(("intel: _ReadPTS(): reading the PTS failed: %lx\n", error));
262 	}
263 	return error;
264 }
265 
266