xref: /haiku/src/system/boot/loader/vfs.cpp (revision 24159a0c7d6d6dcba9f2a0c1a7c08d2c8167f21b)
1 /*
2  * Copyright 2003-2005, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "RootFileSystem.h"
8 
9 #include <StorageDefs.h>
10 #include <boot/vfs.h>
11 #include <boot/platform.h>
12 #include <boot/partitions.h>
13 #include <boot/stdio.h>
14 #include <boot/stage2.h>
15 #include <util/kernel_cpp.h>
16 
17 #include <unistd.h>
18 #include <string.h>
19 #include <fcntl.h>
20 
21 using namespace boot;
22 
23 //#define TRACE_VFS
24 #ifdef TRACE_VFS
25 #	define TRACE(x) dprintf x
26 #else
27 #	define TRACE(x) ;
28 #endif
29 
30 
31 class Descriptor {
32 	public:
33 		Descriptor(Node *node, void *cookie);
34 		~Descriptor();
35 
36 		ssize_t ReadAt(off_t pos, void *buffer, size_t bufferSize);
37 		ssize_t Read(void *buffer, size_t bufferSize);
38 		ssize_t WriteAt(off_t pos, const void *buffer, size_t bufferSize);
39 		ssize_t Write(const void *buffer, size_t bufferSize);
40 
41 		status_t Stat(struct stat &stat);
42 
43 		off_t Offset() const { return fOffset; }
44 		int32 RefCount() const { return fRefCount; }
45 
46 		status_t Acquire();
47 		status_t Release();
48 
49 		Node *GetNode() const { return fNode; }
50 
51 	private:
52 		Node	*fNode;
53 		void	*fCookie;
54 		off_t	fOffset;
55 		int32	fRefCount;
56 };
57 
58 #define MAX_VFS_DESCRIPTORS 64
59 
60 NodeList gBootDevices;
61 NodeList gPartitions;
62 RootFileSystem *gRoot;
63 static Descriptor *sDescriptors[MAX_VFS_DESCRIPTORS];
64 static Node *sBootDevice;
65 
66 
67 Node::Node()
68 	:
69 	fRefCount(1)
70 {
71 }
72 
73 
74 Node::~Node()
75 {
76 }
77 
78 
79 status_t
80 Node::Open(void **_cookie, int mode)
81 {
82 	TRACE(("%p::Open()\n", this));
83 	return Acquire();
84 }
85 
86 
87 status_t
88 Node::Close(void *cookie)
89 {
90 	TRACE(("%p::Close()\n", this));
91 	return Release();
92 }
93 
94 
95 status_t
96 Node::GetName(char *nameBuffer, size_t bufferSize) const
97 {
98 	return B_ERROR;
99 }
100 
101 
102 int32
103 Node::Type() const
104 {
105 	return 0;
106 }
107 
108 
109 off_t
110 Node::Size() const
111 {
112 	return 0LL;
113 }
114 
115 
116 ino_t
117 Node::Inode() const
118 {
119 	return 0;
120 }
121 
122 
123 status_t
124 Node::Acquire()
125 {
126 	fRefCount++;
127 	TRACE(("%p::Acquire(), fRefCount = %ld\n", this, fRefCount));
128 	return B_OK;
129 }
130 
131 status_t
132 Node::Release()
133 {
134 	TRACE(("%p::Release(), fRefCount = %ld\n", this, fRefCount));
135 	if (--fRefCount == 0) {
136 		TRACE(("delete node: %p\n", this));
137 		delete this;
138 		return 1;
139 	}
140 
141 	return B_OK;
142 }
143 
144 
145 //	#pragma mark -
146 
147 
148 ConsoleNode::ConsoleNode()
149 	: Node()
150 {
151 }
152 
153 
154 ssize_t
155 ConsoleNode::Read(void *buffer, size_t bufferSize)
156 {
157 	return ReadAt(NULL, -1, buffer, bufferSize);
158 }
159 
160 
161 ssize_t
162 ConsoleNode::Write(const void *buffer, size_t bufferSize)
163 {
164 	return WriteAt(NULL, -1, buffer, bufferSize);
165 }
166 
167 
168 //	#pragma mark -
169 
170 
171 Directory::Directory()
172 	: Node()
173 {
174 }
175 
176 
177 ssize_t
178 Directory::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
179 {
180 	return B_ERROR;
181 }
182 
183 
184 ssize_t
185 Directory::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
186 {
187 	return B_ERROR;
188 }
189 
190 
191 int32
192 Directory::Type() const
193 {
194 	return S_IFDIR;
195 }
196 
197 
198 //	#pragma mark -
199 
200 
201 Descriptor::Descriptor(Node *node, void *cookie)
202 	:
203 	fNode(node),
204 	fCookie(cookie),
205 	fOffset(0),
206 	fRefCount(1)
207 {
208 }
209 
210 
211 Descriptor::~Descriptor()
212 {
213 }
214 
215 
216 ssize_t
217 Descriptor::Read(void *buffer, size_t bufferSize)
218 {
219 	ssize_t bytesRead = fNode->ReadAt(fCookie, fOffset, buffer, bufferSize);
220 	if (bytesRead > B_OK)
221 		fOffset += bytesRead;
222 
223 	return bytesRead;
224 }
225 
226 
227 ssize_t
228 Descriptor::ReadAt(off_t pos, void *buffer, size_t bufferSize)
229 {
230 	return fNode->ReadAt(fCookie, pos, buffer, bufferSize);
231 }
232 
233 
234 ssize_t
235 Descriptor::Write(const void *buffer, size_t bufferSize)
236 {
237 	ssize_t bytesWritten = fNode->WriteAt(fCookie, fOffset, buffer, bufferSize);
238 	if (bytesWritten > B_OK)
239 		fOffset += bytesWritten;
240 
241 	return bytesWritten;
242 }
243 
244 
245 ssize_t
246 Descriptor::WriteAt(off_t pos, const void *buffer, size_t bufferSize)
247 {
248 	return fNode->WriteAt(fCookie, pos, buffer, bufferSize);
249 }
250 
251 
252 status_t
253 Descriptor::Stat(struct stat &stat)
254 {
255 	stat.st_mode = fNode->Type();
256 	stat.st_size = fNode->Size();
257 	stat.st_ino = fNode->Inode();
258 
259 	return B_OK;
260 }
261 
262 
263 status_t
264 Descriptor::Acquire()
265 {
266 	fRefCount++;
267 	return B_OK;
268 }
269 
270 
271 status_t
272 Descriptor::Release()
273 {
274 	if (--fRefCount == 0) {
275 		status_t status = fNode->Close(fCookie);
276 		if (status != B_OK)
277 			return status;
278 	}
279 
280 	return B_OK;
281 }
282 
283 
284 //	#pragma mark -
285 
286 
287 status_t
288 vfs_init(stage2_args *args)
289 {
290 	gRoot = new RootFileSystem();
291 	if (gRoot == NULL)
292 		return B_NO_MEMORY;
293 
294 	return B_OK;
295 }
296 
297 
298 status_t
299 register_boot_file_system(Directory *volume)
300 {
301 	gRoot->AddLink("boot", volume);
302 
303 	Partition *partition;
304 	status_t status = gRoot->GetPartitionFor(volume, &partition);
305 	if (status != B_OK) {
306 		dprintf("register_boot_file_system(): could not locate boot volume in root!\n");
307 		return status;
308 	}
309 
310 	gKernelArgs.boot_disk.partition_offset = partition->offset;
311 
312 	Node *device = get_node_from(partition->FD());
313 	if (device == NULL) {
314 		dprintf("register_boot_file_system(): could not get boot device!\n");
315 		return B_ERROR;
316 	}
317 
318 	return platform_register_boot_device(device);
319 }
320 
321 
322 /** Gets the boot device, scans all of its partitions, gets the
323  *	boot partition, and mounts its file system.
324  *	Returns the file system's root node or NULL for failure.
325  */
326 
327 Directory *
328 get_boot_file_system(stage2_args *args)
329 {
330 	Node *device;
331 	if (platform_get_boot_device(args, &device) < B_OK)
332 		return NULL;
333 
334 	// add the boot device to the list of devices
335 	gBootDevices.Add(device);
336 
337 	if (add_partitions_for(device, false, true) < B_OK)
338 		return NULL;
339 
340 	Partition *partition;
341 	if (platform_get_boot_partition(args, device, &gPartitions, &partition) < B_OK)
342 		return NULL;
343 
344 	Directory *fileSystem;
345 	status_t status = partition->Mount(&fileSystem, true);
346 
347 	if (status < B_OK) {
348 		// this partition doesn't contain any known file system; we
349 		// don't need it anymore
350 		gPartitions.Remove(partition);
351 		delete partition;
352 		return NULL;
353 	}
354 
355 	sBootDevice = device;
356 	return fileSystem;
357 }
358 
359 
360 /** Mounts all file systems recognized on the given device by
361  *	calling the add_partitions_for() function on them.
362  */
363 
364 status_t
365 mount_file_systems(stage2_args *args)
366 {
367 	// mount other partitions on boot device (if any)
368 	NodeIterator iterator = gPartitions.GetIterator();
369 
370 	Partition *partition = NULL;
371 	while ((partition = (Partition *)iterator.Next()) != NULL) {
372 		// don't scan known partitions again
373 		if (partition->IsFileSystem())
374 			continue;
375 
376 		// remove the partition if it doesn't contain a (known) file system
377 		if (partition->Scan(true) != B_OK && !partition->IsFileSystem()) {
378 			gPartitions.Remove(partition);
379 			delete partition;
380 		}
381 	}
382 
383 	// add all block devices the platform has for us
384 
385 	status_t status = platform_add_block_devices(args, &gBootDevices);
386 	if (status < B_OK)
387 		return status;
388 
389 	iterator = gBootDevices.GetIterator();
390 	Node *device = NULL, *last = NULL;
391 	while ((device = iterator.Next()) != NULL) {
392 		// don't scan former boot device again
393 		if (device == sBootDevice)
394 			continue;
395 
396 		if (add_partitions_for(device, true) == B_OK) {
397 			// ToDo: we can't delete the object here, because it must
398 			//	be removed from the list before we know that it was
399 			//	deleted.
400 
401 /*			// if the Release() deletes the object, we need to skip it
402 			if (device->Release() > 0) {
403 				list_remove_item(&gBootDevices, device);
404 				device = last;
405 			}
406 */
407 		}
408 		last = device;
409 	}
410 
411 	if (gPartitions.IsEmpty())
412 		return B_ENTRY_NOT_FOUND;
413 
414 #if 0
415 	void *cookie;
416 	if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
417 		Directory *directory;
418 		while (gRoot->GetNextNode(cookie, (Node **)&directory) == B_OK) {
419 			char name[256];
420 			if (directory->GetName(name, sizeof(name)) == B_OK)
421 				printf(":: %s (%p)\n", name, directory);
422 
423 			void *subCookie;
424 			if (directory->Open(&subCookie, O_RDONLY) == B_OK) {
425 				while (directory->GetNextEntry(subCookie, name, sizeof(name)) == B_OK) {
426 					printf("\t%s\n", name);
427 				}
428 				directory->Close(subCookie);
429 			}
430 		}
431 		gRoot->Close(cookie);
432 	}
433 #endif
434 
435 	return B_OK;
436 }
437 
438 
439 static status_t
440 get_node_for_path(Directory *directory, const char *pathName, Node **_node)
441 {
442 	char pathBuffer[B_PATH_NAME_LENGTH];
443 	char *path = pathBuffer;
444 
445 	if (pathName == NULL)
446 		return B_BAD_VALUE;
447 
448 	strlcpy(path, pathName, sizeof(pathBuffer));
449 
450 	directory->Acquire();
451 		// balance Acquire()/Release() calls
452 
453 	while (true) {
454 		Node *nextNode;
455 		char *nextPath;
456 
457 		// walk to find the next path component ("path" will point to a single
458 		// path component), and filter out multiple slashes
459 		for (nextPath = path + 1; nextPath[0] != '\0' && nextPath[0] != '/'; nextPath++);
460 
461 		if (*nextPath == '/') {
462 			*nextPath = '\0';
463 			do
464 				nextPath++;
465 			while (*nextPath == '/');
466 		}
467 
468 		nextNode = directory->Lookup(path, true);
469 		directory->Release();
470 
471 		if (nextNode == NULL)
472 			return B_ENTRY_NOT_FOUND;
473 
474 		path = nextPath;
475 		if (S_ISDIR(nextNode->Type()))
476 			directory = (Directory *)nextNode;
477 		else if (path[0])
478 			return B_NOT_ALLOWED;
479 
480 		// are we done?
481 		if (path[0] == '\0') {
482 			*_node = nextNode;
483 			return B_OK;
484 		}
485 	}
486 
487 	return NULL;
488 }
489 
490 
491 //	#pragma mark -
492 
493 
494 static Descriptor *
495 get_descriptor(int fd)
496 {
497 	if (fd < 0 || fd >= MAX_VFS_DESCRIPTORS)
498 		return NULL;
499 
500 	return sDescriptors[fd];
501 }
502 
503 
504 static void
505 free_descriptor(int fd)
506 {
507 	if (fd >= MAX_VFS_DESCRIPTORS)
508 		return;
509 
510 	delete sDescriptors[fd];
511 	sDescriptors[fd] = NULL;
512 }
513 
514 
515 /**	Reserves an entry of the descriptor table and
516  *	assigns the given node to it.
517  */
518 
519 int
520 open_node(Node *node, int mode)
521 {
522 	if (node == NULL)
523 		return B_ERROR;
524 
525 	// get free descriptor
526 
527 	int fd = 0;
528 	for (; fd < MAX_VFS_DESCRIPTORS; fd++) {
529 		if (sDescriptors[fd] == NULL)
530 			break;
531 	}
532 	if (fd == MAX_VFS_DESCRIPTORS)
533 		return B_ERROR;
534 
535 	TRACE(("got descriptor %d for node %p\n", fd, node));
536 
537 	// we got a free descriptor entry, now try to open the node
538 
539 	void *cookie;
540 	status_t status = node->Open(&cookie, mode);
541 	if (status < B_OK)
542 		return status;
543 
544 	TRACE(("could open node at %p\n", node));
545 
546 	Descriptor *descriptor = new Descriptor(node, cookie);
547 	if (descriptor == NULL)
548 		return B_NO_MEMORY;
549 
550 	sDescriptors[fd] = descriptor;
551 
552 	return fd;
553 }
554 
555 
556 int
557 dup(int fd)
558 {
559 	Descriptor *descriptor = get_descriptor(fd);
560 	if (descriptor == NULL)
561 		return B_FILE_ERROR;
562 
563 	descriptor->Acquire();
564 	return fd;
565 }
566 
567 
568 ssize_t
569 read_pos(int fd, off_t offset, void *buffer, size_t bufferSize)
570 {
571 	Descriptor *descriptor = get_descriptor(fd);
572 	if (descriptor == NULL)
573 		return B_FILE_ERROR;
574 
575 	return descriptor->ReadAt(offset, buffer, bufferSize);
576 }
577 
578 
579 ssize_t
580 read(int fd, void *buffer, size_t bufferSize)
581 {
582 	Descriptor *descriptor = get_descriptor(fd);
583 	if (descriptor == NULL)
584 		return B_FILE_ERROR;
585 
586 	return descriptor->Read(buffer, bufferSize);
587 }
588 
589 
590 ssize_t
591 write_pos(int fd, off_t offset, const void *buffer, size_t bufferSize)
592 {
593 	Descriptor *descriptor = get_descriptor(fd);
594 	if (descriptor == NULL)
595 		return B_FILE_ERROR;
596 
597 	return descriptor->WriteAt(offset, buffer, bufferSize);
598 }
599 
600 
601 ssize_t
602 write(int fd, const void *buffer, size_t bufferSize)
603 {
604 	Descriptor *descriptor = get_descriptor(fd);
605 	if (descriptor == NULL)
606 		return B_FILE_ERROR;
607 
608 	return descriptor->Write(buffer, bufferSize);
609 }
610 
611 
612 int
613 open(const char *name, int mode, ...)
614 {
615 	// we always start at the top (there is no notion of a current directory (yet?))
616 	if (name[0] == '/')
617 		name++;
618 
619 	Node *node;
620 	if (get_node_for_path(gRoot, name, &node) < B_OK)
621 		return B_ENTRY_NOT_FOUND;
622 
623 	int fd = open_node(node, mode);
624 
625 	node->Release();
626 	return fd;
627 }
628 
629 
630 int
631 open_from(Directory *directory, const char *name, int mode)
632 {
633 	if (name[0] == '/') {
634 		// ignore the directory and start from root if we are asked to do that
635 		directory = gRoot;
636 		name++;
637 	}
638 
639 	Node *node;
640 	if (get_node_for_path(directory, name, &node) < B_OK)
641 		return B_ENTRY_NOT_FOUND;
642 
643 	int fd = open_node(node, mode);
644 
645 	node->Release();
646 	return fd;
647 }
648 
649 
650 /** Since we don't have directory functions yet, this
651  *	function is needed to get the contents of a directory.
652  *	It should be removed once readdir() & co. are in place.
653  */
654 
655 Node *
656 get_node_from(int fd)
657 {
658 	Descriptor *descriptor = get_descriptor(fd);
659 	if (descriptor == NULL)
660 		return NULL;
661 
662 	return descriptor->GetNode();
663 }
664 
665 
666 int
667 close(int fd)
668 {
669 	Descriptor *descriptor = get_descriptor(fd);
670 	if (descriptor == NULL)
671 		return B_FILE_ERROR;
672 
673 	status_t status = descriptor->Release();
674 	if (!descriptor->RefCount())
675 		free_descriptor(fd);
676 
677 	return status;
678 }
679 
680 
681 // ToDo: remove this kludge when possible
682 int
683 #if defined(fstat) && !defined(main)
684 _fstat(int fd, struct stat *stat, size_t /*statSize*/)
685 #else
686 fstat(int fd, struct stat *stat)
687 #endif
688 {
689 	if (stat == NULL)
690 		return B_BAD_VALUE;
691 
692 	Descriptor *descriptor = get_descriptor(fd);
693 	if (descriptor == NULL)
694 		return B_FILE_ERROR;
695 
696 	return descriptor->Stat(*stat);
697 }
698