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