xref: /haiku/src/system/boot/loader/partitions.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 /*
2  * Copyright 2003-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "RootFileSystem.h"
8 
9 #include <boot/partitions.h>
10 #include <boot/vfs.h>
11 #include <boot/platform.h>
12 #include <boot/stage2.h>
13 #include <boot/stdio.h>
14 #include <ddm_modules.h>
15 #include <util/kernel_cpp.h>
16 
17 #include <unistd.h>
18 #include <string.h>
19 
20 using namespace boot;
21 
22 //#define TRACE_PARTITIONS
23 #ifdef TRACE_PARTITIONS
24 #	define TRACE(x) dprintf x
25 #else
26 #	define TRACE(x) ;
27 #endif
28 
29 
30 /* supported partition modules */
31 
32 static const partition_module_info *sPartitionModules[] = {
33 #ifdef BOOT_SUPPORT_PARTITION_AMIGA
34 	&gAmigaPartitionModule,
35 #endif
36 #ifdef BOOT_SUPPORT_PARTITION_INTEL
37 	&gIntelPartitionMapModule,
38 	&gIntelExtendedPartitionModule,
39 #endif
40 #ifdef BOOT_SUPPORT_PARTITION_APPLE
41 	&gApplePartitionModule,
42 #endif
43 };
44 static const int32 sNumPartitionModules = sizeof(sPartitionModules) / sizeof(partition_module_info *);
45 
46 /* supported file system modules */
47 
48 static file_system_module_info *sFileSystemModules[] = {
49 #ifdef BOOT_SUPPORT_FILE_SYSTEM_BFS
50 	&gBFSFileSystemModule,
51 #endif
52 #ifdef BOOT_SUPPORT_FILE_SYSTEM_AMIGA_FFS
53 	&gAmigaFFSFileSystemModule,
54 #endif
55 #ifdef BOOT_SUPPORT_FILE_SYSTEM_FAT
56 	&gFATFileSystemModule,
57 #endif
58 #ifdef BOOT_SUPPORT_FILE_SYSTEM_HFS_PLUS
59 	&gHFSPlusFileSystemModule,
60 #endif
61 #ifdef BOOT_SUPPORT_FILE_SYSTEM_TARFS
62 	&gTarFileSystemModule,
63 #endif
64 };
65 static const int32 sNumFileSystemModules = sizeof(sFileSystemModules) / sizeof(file_system_module_info *);
66 
67 extern NodeList gPartitions;
68 
69 
70 namespace boot {
71 
72 /** A convenience class to automatically close a
73  *	file descriptor upon deconstruction.
74  */
75 
76 class NodeOpener {
77 	public:
78 		NodeOpener(Node *node, int mode)
79 		{
80 			fFD = open_node(node, mode);
81 		}
82 
83 		~NodeOpener()
84 		{
85 			close(fFD);
86 		}
87 
88 		int Descriptor() const { return fFD; }
89 
90 	private:
91 		int		fFD;
92 };
93 
94 
95 //	#pragma mark -
96 
97 
98 Partition::Partition(int fd)
99 	:
100 	fParent(NULL),
101 	fIsFileSystem(false),
102 	fIsPartitioningSystem(false)
103 {
104 	memset((partition_data *)this, 0, sizeof(partition_data));
105 	id = (partition_id)this;
106 
107 	// it's safe to close the file
108 	fFD = dup(fd);
109 }
110 
111 
112 Partition::~Partition()
113 {
114 	close(fFD);
115 }
116 
117 
118 ssize_t
119 Partition::ReadAt(void *cookie, off_t position, void *buffer, size_t bufferSize)
120 {
121 	if (position > this->size)
122 		return 0;
123 	if (position < 0)
124 		return B_BAD_VALUE;
125 
126 	if (position + bufferSize > this->size)
127 		bufferSize = this->size - position;
128 
129 	return read_pos(fFD, this->offset + position, buffer, bufferSize);
130 }
131 
132 
133 ssize_t
134 Partition::WriteAt(void *cookie, off_t position, const void *buffer, size_t bufferSize)
135 {
136 	if (position > this->size)
137 		return 0;
138 	if (position < 0)
139 		return B_BAD_VALUE;
140 
141 	if (position + bufferSize > this->size)
142 		bufferSize = this->size - position;
143 
144 	return write_pos(fFD, this->offset + position, buffer, bufferSize);
145 }
146 
147 
148 off_t
149 Partition::Size() const
150 {
151 	struct stat stat;
152 	if (fstat(fFD, &stat) == B_OK)
153 		return stat.st_size;
154 
155 	return Node::Size();
156 }
157 
158 
159 int32
160 Partition::Type() const
161 {
162 	struct stat stat;
163 	if (fstat(fFD, &stat) == B_OK)
164 		return stat.st_mode;
165 
166 	return Node::Type();
167 }
168 
169 
170 Partition *
171 Partition::AddChild()
172 {
173 	Partition *child = new Partition(fFD);
174 	if (child == NULL)
175 		return NULL;
176 
177 	child->SetParent(this);
178 	child_count++;
179 	fChildren.Add(child);
180 
181 	return child;
182 }
183 
184 
185 status_t
186 Partition::_Mount(file_system_module_info *module, Directory **_fileSystem)
187 {
188 	TRACE(("check for file_system: %s\n", module->pretty_name));
189 
190 	Directory *fileSystem;
191 	if (module->get_file_system(this, &fileSystem) == B_OK) {
192 		gRoot->AddVolume(fileSystem, this);
193 		if (_fileSystem)
194 			*_fileSystem = fileSystem;
195 
196 		// remember the module name that mounted us
197 		fModuleName = module->module_name;
198 
199 		fIsFileSystem = true;
200 		return B_OK;
201 	}
202 
203 	return B_BAD_VALUE;
204 }
205 
206 
207 status_t
208 Partition::Mount(Directory **_fileSystem, bool isBootDevice)
209 {
210 	if (isBootDevice && gKernelArgs.boot_disk.booted_from_image)
211 		return _Mount(&gTarFileSystemModule, _fileSystem);
212 
213 	for (int32 i = 0; i < sNumFileSystemModules; i++) {
214 		status_t status = _Mount(sFileSystemModules[i], _fileSystem);
215 		if (status == B_OK)
216 			return B_OK;
217 	}
218 
219 	return B_ENTRY_NOT_FOUND;
220 }
221 
222 
223 status_t
224 Partition::Scan(bool mountFileSystems, bool isBootDevice)
225 {
226 	// scan for partitions first (recursively all eventual children as well)
227 
228 	TRACE(("Partition::Scan()\n"));
229 
230 	// if we were not booted from the real boot device, we won't scan
231 	// the device we were booted from (which is likely to be a slow
232 	// floppy or CD)
233 	if (isBootDevice && gKernelArgs.boot_disk.booted_from_image)
234 		return B_ENTRY_NOT_FOUND;
235 
236 	const partition_module_info *bestModule = NULL;
237 	void *bestCookie = NULL;
238 	float bestPriority = -1;
239 
240 	for (int32 i = 0; i < sNumPartitionModules; i++) {
241 		const partition_module_info *module = sPartitionModules[i];
242 		void *cookie = NULL;
243 		NodeOpener opener(this, O_RDONLY);
244 
245 		TRACE(("check for partitioning_system: %s\n", module->pretty_name));
246 
247 		float priority
248 			= module->identify_partition(opener.Descriptor(), this, &cookie);
249 		if (priority < 0.0)
250 			continue;
251 
252 		if (priority <= bestPriority) {
253 			// the disk system recognized the partition worse than the currently
254 			// best one
255 			module->free_identify_partition_cookie(this, cookie);
256 			continue;
257 		}
258 
259 		// a new winner, replace the previous one
260 		if (bestModule)
261 			bestModule->free_identify_partition_cookie(this, bestCookie);
262 		bestModule = module;
263 		bestCookie = cookie;
264 		bestPriority = priority;
265 	}
266 
267 	// now let the best matching disk system scan the partition
268 	if (bestModule) {
269 		NodeOpener opener(this, O_RDONLY);
270 		status_t status = bestModule->scan_partition(opener.Descriptor(), this,
271 			bestCookie);
272 		bestModule->free_identify_partition_cookie(this, bestCookie);
273 
274 		if (status != B_OK) {
275 			dprintf("Partitioning module `%s' recognized the partition, but "
276 				"failed to scan it\n", bestModule->pretty_name);
277 			return status;
278 		}
279 
280 		fIsPartitioningSystem = true;
281 
282 		content_type = bestModule->pretty_name;
283 		flags |= B_PARTITION_PARTITIONING_SYSTEM;
284 
285 		// now that we've found something, check our children
286 		// out as well!
287 
288 		NodeIterator iterator = fChildren.GetIterator();
289 		Partition *child = NULL;
290 
291 		while ((child = (Partition *)iterator.Next()) != NULL) {
292 			TRACE(("*** scan child %p (start = %Ld, size = %Ld, parent = %p)!\n",
293 				child, child->offset, child->size, child->Parent()));
294 
295 			child->Scan(mountFileSystems);
296 
297 			if (!mountFileSystems || child->IsFileSystem()) {
298 				// move the partitions containing file systems to the partition list
299 				fChildren.Remove(child);
300 				gPartitions.Add(child);
301 			}
302 		}
303 
304 		// remove all unused children (we keep only file systems)
305 
306 		while ((child = (Partition *)fChildren.Head()) != NULL) {
307 			fChildren.Remove(child);
308 			delete child;
309 		}
310 
311 		// remember the module name that identified us
312 		fModuleName = bestModule->module.name;
313 
314 		return B_OK;
315 	}
316 
317 	// scan for file systems
318 
319 	if (mountFileSystems)
320 		return Mount();
321 
322 	return B_ENTRY_NOT_FOUND;
323 }
324 
325 }	// namespace boot
326 
327 
328 //	#pragma mark -
329 
330 
331 /**	Scans the device passed in for partitioning systems. If none are found,
332  *	a partition containing the whole device is created.
333  *	All created partitions are added to the gPartitions list.
334  */
335 
336 status_t
337 add_partitions_for(int fd, bool mountFileSystems, bool isBootDevice)
338 {
339 	TRACE(("add_partitions_for(fd = %d, mountFS = %s)\n", fd, mountFileSystems ? "yes" : "no"));
340 
341 	Partition *partition = new Partition(fd);
342 
343 	// set some magic/default values
344 	partition->block_size = 512;
345 	partition->size = partition->Size();
346 
347 	// add this partition to the list of partitions, if it contains
348 	// or might contain a file system
349 	if ((partition->Scan(mountFileSystems, isBootDevice) == B_OK && partition->IsFileSystem())
350 		|| (!partition->IsPartitioningSystem() && !mountFileSystems)) {
351 		gPartitions.Add(partition);
352 		return B_OK;
353 	}
354 
355 	// if not, we'll need to tell the children that their parent is gone
356 
357 	NodeIterator iterator = gPartitions.GetIterator();
358 	Partition *child = NULL;
359 
360 	while ((child = (Partition *)iterator.Next()) != NULL) {
361 		if (child->Parent() == partition)
362 			child->SetParent(NULL);
363 	}
364 
365 	delete partition;
366 	return B_OK;
367 }
368 
369 
370 status_t
371 add_partitions_for(Node *device, bool mountFileSystems, bool isBootDevice)
372 {
373 	TRACE(("add_partitions_for(%p, mountFS = %s)\n", device, mountFileSystems ? "yes" : "no"));
374 
375 	int fd = open_node(device, O_RDONLY);
376 	if (fd < B_OK)
377 		return fd;
378 
379 	status_t status = add_partitions_for(fd, mountFileSystems, isBootDevice);
380 	if (status < B_OK)
381 		dprintf("add_partitions_for(%d) failed: %ld\n", fd, status);
382 
383 	close(fd);
384 	return B_OK;
385 }
386 
387 
388 partition_data *
389 create_child_partition(partition_id id, int32 index, partition_id childID)
390 {
391 	Partition &partition = *(Partition *)id;
392 	Partition *child = partition.AddChild();
393 	if (child == NULL) {
394 		dprintf("creating partition failed: no memory\n");
395 		return NULL;
396 	}
397 
398 	// we cannot do anything with the child here, because it was not
399 	// yet initialized by the partition module.
400 	TRACE(("new child partition!\n"));
401 
402 	return child;
403 }
404 
405 
406 partition_data *
407 get_child_partition(partition_id id, int32 index)
408 {
409 	//Partition &partition = *(Partition *)id;
410 
411 	// ToDo: do we really have to implement this?
412 	//	The intel partition module doesn't really needs this for our mission...
413 
414 	return NULL;
415 }
416 
417 
418 partition_data *
419 get_parent_partition(partition_id id)
420 {
421 	Partition &partition = *(Partition *)id;
422 
423 	return partition.Parent();
424 }
425 
426