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
28using 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
49bool
50check_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
89PartitionMapWriter::PartitionMapWriter(int deviceFD, uint32 blockSize)
90	:
91	fDeviceFD(deviceFD),
92	fBlockSize(blockSize)
93{
94}
95
96
97PartitionMapWriter::~PartitionMapWriter()
98{
99}
100
101
102status_t
103PartitionMapWriter::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
135status_t
136PartitionMapWriter::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
178status_t
179PartitionMapWriter::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
236status_t
237PartitionMapWriter::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
260status_t
261PartitionMapWriter::_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
280status_t
281PartitionMapWriter::_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