xref: /haiku/src/add-ons/kernel/partitioning_systems/common/PartitionMapWriter.cpp (revision 52c4471a3024d2eb81fe88e2c3982b9f8daa5e56)
1 /*
2  * Copyright 2003-2009, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Bryce Groff, brycegroff@gmail.com
7  */
8 
9 #include "PartitionMapWriter.h"
10 
11 #include <errno.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <unistd.h>
15 
16 #include <new>
17 
18 #ifndef _USER_MODE
19 #include <debug.h>
20 #endif
21 
22 #ifndef _USER_MODE
23 #	include <KernelExport.h>
24 #endif
25 
26 #include "PartitionMap.h"
27 
28 using std::nothrow;
29 
30 
31 #define TRACE_ENABLED
32 #ifdef TRACE_ENABLED
33 #	ifdef _USER_MODE
34 #		define TRACE(x) printf x
35 #	else
36 #		define TRACE(x) dprintf x
37 #	endif
38 #endif
39 
40 
41 #if defined(__i386__) || defined(__x86_64__)
42 #	ifndef _USER_MODE
43 #		define MBR_HEADER "MBR.h"
44 #		include MBR_HEADER
45 #	endif
46 #endif
47 
48 
49 bool
50 check_logical_location(const LogicalPartition* child,
51 	const PrimaryPartition* parent)
52 {
53 	if (child->PartitionTableOffset() % child->BlockSize() != 0) {
54 		TRACE(("check_logical_location() - PartitionTableOffset: %" B_PRId64 " "
55 			"not a multiple of media's block size: %" B_PRId32 "\n",
56 			child->PartitionTableOffset(), child->BlockSize()));
57 		return false;
58 	}
59 	if (child->Offset() % child->BlockSize() != 0) {
60 		TRACE(("check_logical_location() - Parition offset: %" B_PRId64 " "
61 			"is not a multiple of block size: %" B_PRId32 "\n", child->Offset(),
62 			child->BlockSize()));
63 		return false;
64 	}
65 	if (child->Size() % child->BlockSize() != 0) {
66 		TRACE(("check_logical_location() - Size: (%" B_PRId64 ") is not a "
67 			"multiple of block size: (%" B_PRId32 ")\n", child->Size(),
68 			child->BlockSize()));
69 		return false;
70 	}
71 	if (child->PartitionTableOffset() < parent->Offset()
72 		|| child->PartitionTableOffset() >= parent->Offset()
73 		+ parent->Size()) {
74 		TRACE(("check_logical_location() - Partition table: (%" B_PRId64 ") not"
75 			" within extended partition (start: %" B_PRId64 "), (end: "
76 			"%" B_PRId64 ")\n", child->PartitionTableOffset(), parent->Offset(),
77 			parent->Offset() + parent->Size()));
78 		return false;
79 	}
80 	if (child->Offset() + child->Size() > parent->Offset() + parent->Size()) {
81 		TRACE(("check_logical_location() - logical paritition does not lie "
82 			"within extended partition\n"));
83 		return false;
84 	}
85 	return true;
86 }
87 
88 
89 PartitionMapWriter::PartitionMapWriter(int deviceFD, uint32 blockSize)
90 	:
91 	fDeviceFD(deviceFD),
92 	fBlockSize(blockSize)
93 {
94 }
95 
96 
97 PartitionMapWriter::~PartitionMapWriter()
98 {
99 }
100 
101 
102 status_t
103 PartitionMapWriter::WriteMBR(const PartitionMap* map, bool writeBootCode)
104 {
105 	if (map == NULL)
106 		return B_BAD_VALUE;
107 
108 	partition_table partitionTable;
109 	status_t error = _ReadBlock(0, partitionTable);
110 	if (error != B_OK)
111 		return error;
112 #ifdef MBR_HEADER
113 	if (writeBootCode) {
114 		// the boot code must be small enough to fit in the code area
115 		STATIC_ASSERT(kMBRSize <= sizeof(partitionTable.code_area));
116 		partitionTable.clear_code_area();
117 		partitionTable.fill_code_area(kMBR, kMBRSize);
118 	}
119 #endif
120 
121 	partitionTable.signature = kPartitionTableSectorSignature;
122 
123 	for (int i = 0; i < 4; i++) {
124 		partition_descriptor* descriptor = &partitionTable.table[i];
125 		const PrimaryPartition* partition = map->PrimaryPartitionAt(i);
126 
127 		partition->GetPartitionDescriptor(descriptor);
128 	}
129 
130 	error = _WriteBlock(0, partitionTable);
131 	return error;
132 }
133 
134 
135 status_t
136 PartitionMapWriter::WriteLogical(const LogicalPartition* logical,
137 	const PrimaryPartition* primary, bool clearCode)
138 {
139 	if (logical == NULL || primary == NULL)
140 		return B_BAD_VALUE;
141 
142 	if (!check_logical_location(logical, primary))
143 		return B_BAD_DATA;
144 
145 	partition_table partitionTable;
146 	if (clearCode) {
147 		partitionTable.clear_code_area();
148 	} else {
149 		status_t error = _ReadBlock(logical->PartitionTableOffset(),
150 			partitionTable);
151 		if (error != B_OK)
152 			return error;
153 	}
154 
155 	partitionTable.signature = kPartitionTableSectorSignature;
156 
157 	partition_descriptor* descriptor = &partitionTable.table[0];
158 	logical->GetPartitionDescriptor(descriptor);
159 
160 	descriptor = &partitionTable.table[1];
161 	if (logical->Next() != NULL)
162 		logical->Next()->GetPartitionDescriptor(descriptor, true);
163 	else
164 		memset(descriptor, 0, sizeof(partition_descriptor));
165 
166 	// last two descriptors are empty
167 	for (int32 i = 2; i < 4; i++) {
168 		descriptor = &partitionTable.table[i];
169 		memset(descriptor, 0, sizeof(partition_descriptor));
170 	}
171 
172 	status_t error = _WriteBlock(logical->PartitionTableOffset(),
173 		partitionTable);
174 	return error;
175 }
176 
177 
178 status_t
179 PartitionMapWriter::WriteExtendedHead(const LogicalPartition* logical,
180 	const PrimaryPartition* primary, bool clearCode)
181 {
182 	if (primary == NULL)
183 		return B_BAD_VALUE;
184 
185 	partition_table partitionTable;
186 	if (clearCode) {
187 		partitionTable.clear_code_area();
188 	} else {
189 		status_t error = _ReadBlock(primary->Offset(), partitionTable);
190 		if (error != B_OK)
191 			return error;
192 	}
193 
194 	partitionTable.signature = kPartitionTableSectorSignature;
195 	partition_descriptor* descriptor;
196 	if (logical == NULL) {
197 		for (int32 i = 0; i < 4; i++) {
198 			descriptor = &partitionTable.table[i];
199 			memset(descriptor, 0, sizeof(partition_descriptor));
200 		}
201 	} else {
202 		LogicalPartition partition;
203 		partition.SetPartitionTableOffset(primary->Offset());
204 		partition.SetBlockSize(logical->BlockSize());
205 		partition.SetOffset(logical->Offset());
206 		partition.SetSize(logical->Size());
207 		partition.SetType(logical->Type());
208 
209 		// set the logicals partition table to the correct location
210 		descriptor = &partitionTable.table[0];
211 		partition.GetPartitionDescriptor(descriptor);
212 
213 		descriptor = &partitionTable.table[1];
214 		LogicalPartition* next = logical->Next();
215 		if (next != NULL)
216 			next->GetPartitionDescriptor(descriptor, true);
217 		else
218 			memset(descriptor, 0, sizeof(partition_descriptor));
219 
220 		// last two descriptors are empty
221 		for (int32 i = 2; i < 4; i++) {
222 			descriptor = &partitionTable.table[i];
223 			memset(descriptor, 0, sizeof(partition_descriptor));
224 		}
225 	}
226 
227 	status_t error = _WriteBlock(primary->Offset(), partitionTable);
228 	if (error != B_OK)
229 		return error;
230 
231 	return B_OK;
232 }
233 
234 
235 
236 status_t
237 PartitionMapWriter::ClearExtendedHead(const PrimaryPartition* primary)
238 {
239 	if (primary == NULL)
240 		return B_BAD_VALUE;
241 
242 	partition_table partitionTable;
243 	partitionTable.clear_code_area();
244 	partitionTable.signature = kPartitionTableSectorSignature;
245 
246 	partition_descriptor* descriptor;
247 	for (int32 i = 0; i < 4; i++) {
248 		descriptor = &partitionTable.table[i];
249 		memset(descriptor, 0, sizeof(partition_descriptor));
250 	}
251 
252 	status_t error = _WriteBlock(primary->Offset(), partitionTable);
253 	if (error != B_OK)
254 		return error;
255 
256 	return B_OK;
257 }
258 
259 
260 status_t
261 PartitionMapWriter::_ReadBlock(off_t partitionOffset,
262 	partition_table& partitionTable)
263 {
264 	if (partitionOffset < 0)
265 		return B_BAD_VALUE;
266 	// TODO: If fBlockSize > sizeof(partition_table) then stop/read NULL after
267 	if (read_pos(fDeviceFD, partitionOffset, &partitionTable,
268 		sizeof(partitionTable)) != sizeof(partitionTable)) {
269 		status_t error = errno;
270 		if (error == B_OK)
271 			error = B_IO_ERROR;
272 
273 		return error;
274 	}
275 
276 	return B_OK;
277 }
278 
279 
280 status_t
281 PartitionMapWriter::_WriteBlock(off_t partitionOffset,
282 	const partition_table& partitionTable)
283 {
284 	if (partitionOffset < 0)
285 		return B_BAD_VALUE;
286 	// TODO: maybe clear the rest of the block if
287 	// fBlockSize > sizeof(partition_table)?
288 	if (write_pos(fDeviceFD, partitionOffset, &partitionTable,
289 		sizeof(partitionTable)) != sizeof(partitionTable)) {
290 		status_t error = errno;
291 		if (error == B_OK)
292 			error = B_IO_ERROR;
293 
294 		return error;
295 	}
296 
297 	return B_OK;
298 }
299 
300