xref: /haiku/src/system/boot/loader/vfs.cpp (revision ff3409e005096a604d862a776e470aae2089865d)
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 	gBootVolume.SetInt64(BOOT_VOLUME_PARTITION_OFFSET, partition->offset);
384 
385 	Node *device = get_node_from(partition->FD());
386 	if (device == NULL) {
387 		dprintf("register_boot_file_system(): could not get boot device!\n");
388 		return B_ERROR;
389 	}
390 
391 	return platform_register_boot_device(device);
392 }
393 
394 
395 /** Gets the boot device, scans all of its partitions, gets the
396  *	boot partition, and mounts its file system.
397  *	Returns the file system's root node or NULL for failure.
398  */
399 
400 Directory *
401 get_boot_file_system(stage2_args *args)
402 {
403 	Node *device;
404 	if (platform_add_boot_device(args, &gBootDevices) < B_OK)
405 		return NULL;
406 
407 	// the boot device must be the first device in the list
408 	device = gBootDevices.First();
409 
410 	if (add_partitions_for(device, false, true) < B_OK)
411 		return NULL;
412 
413 	Partition *partition;
414 	if (platform_get_boot_partition(args, device, &gPartitions, &partition) < B_OK)
415 		return NULL;
416 
417 	Directory *fileSystem;
418 	status_t status = partition->Mount(&fileSystem, true);
419 
420 	if (status < B_OK) {
421 		// this partition doesn't contain any known file system; we
422 		// don't need it anymore
423 		gPartitions.Remove(partition);
424 		delete partition;
425 		return NULL;
426 	}
427 
428 	sBootDevice = device;
429 	return fileSystem;
430 }
431 
432 
433 /** Mounts all file systems recognized on the given device by
434  *	calling the add_partitions_for() function on them.
435  */
436 
437 status_t
438 mount_file_systems(stage2_args *args)
439 {
440 	// mount other partitions on boot device (if any)
441 	NodeIterator iterator = gPartitions.GetIterator();
442 
443 	Partition *partition = NULL;
444 	while ((partition = (Partition *)iterator.Next()) != NULL) {
445 		// don't scan known partitions again
446 		if (partition->IsFileSystem())
447 			continue;
448 
449 		// remove the partition if it doesn't contain a (known) file system
450 		if (partition->Scan(true) != B_OK && !partition->IsFileSystem()) {
451 			gPartitions.Remove(partition);
452 			delete partition;
453 		}
454 	}
455 
456 	// add all block devices the platform has for us
457 
458 	status_t status = platform_add_block_devices(args, &gBootDevices);
459 	if (status < B_OK)
460 		return status;
461 
462 	iterator = gBootDevices.GetIterator();
463 	Node *device = NULL, *last = NULL;
464 	while ((device = iterator.Next()) != NULL) {
465 		// don't scan former boot device again
466 		if (device == sBootDevice)
467 			continue;
468 
469 		if (add_partitions_for(device, true) == B_OK) {
470 			// ToDo: we can't delete the object here, because it must
471 			//	be removed from the list before we know that it was
472 			//	deleted.
473 
474 /*			// if the Release() deletes the object, we need to skip it
475 			if (device->Release() > 0) {
476 				list_remove_item(&gBootDevices, device);
477 				device = last;
478 			}
479 */
480 		}
481 		last = device;
482 	}
483 
484 	if (gPartitions.IsEmpty())
485 		return B_ENTRY_NOT_FOUND;
486 
487 #if 0
488 	void *cookie;
489 	if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
490 		Directory *directory;
491 		while (gRoot->GetNextNode(cookie, (Node **)&directory) == B_OK) {
492 			char name[256];
493 			if (directory->GetName(name, sizeof(name)) == B_OK)
494 				printf(":: %s (%p)\n", name, directory);
495 
496 			void *subCookie;
497 			if (directory->Open(&subCookie, O_RDONLY) == B_OK) {
498 				while (directory->GetNextEntry(subCookie, name, sizeof(name)) == B_OK) {
499 					printf("\t%s\n", name);
500 				}
501 				directory->Close(subCookie);
502 			}
503 		}
504 		gRoot->Close(cookie);
505 	}
506 #endif
507 
508 	return B_OK;
509 }
510 
511 
512 /*!	Resolves \a directory + \a path to a node.
513 	Note that \a path will be modified by the function.
514 */
515 static status_t
516 get_node_for_path(Directory *directory, char *path, Node **_node)
517 {
518 	directory->Acquire();
519 		// balance Acquire()/Release() calls
520 
521 	while (true) {
522 		Node *nextNode;
523 		char *nextPath;
524 
525 		// walk to find the next path component ("path" will point to a single
526 		// path component), and filter out multiple slashes
527 		for (nextPath = path + 1; nextPath[0] != '\0' && nextPath[0] != '/'; nextPath++);
528 
529 		if (*nextPath == '/') {
530 			*nextPath = '\0';
531 			do
532 				nextPath++;
533 			while (*nextPath == '/');
534 		}
535 
536 		nextNode = directory->Lookup(path, true);
537 		directory->Release();
538 
539 		if (nextNode == NULL)
540 			return B_ENTRY_NOT_FOUND;
541 
542 		path = nextPath;
543 		if (S_ISDIR(nextNode->Type()))
544 			directory = (Directory *)nextNode;
545 		else if (path[0])
546 			return B_NOT_ALLOWED;
547 
548 		// are we done?
549 		if (path[0] == '\0') {
550 			*_node = nextNode;
551 			return B_OK;
552 		}
553 	}
554 
555 	return B_ENTRY_NOT_FOUND;
556 }
557 
558 
559 //	#pragma mark -
560 
561 
562 static Descriptor *
563 get_descriptor(int fd)
564 {
565 	if (fd < 0 || fd >= MAX_VFS_DESCRIPTORS)
566 		return NULL;
567 
568 	return sDescriptors[fd];
569 }
570 
571 
572 static void
573 free_descriptor(int fd)
574 {
575 	if (fd >= MAX_VFS_DESCRIPTORS)
576 		return;
577 
578 	delete sDescriptors[fd];
579 	sDescriptors[fd] = NULL;
580 }
581 
582 
583 /**	Reserves an entry of the descriptor table and
584  *	assigns the given node to it.
585  */
586 
587 int
588 open_node(Node *node, int mode)
589 {
590 	if (node == NULL)
591 		return B_ERROR;
592 
593 	// get free descriptor
594 
595 	int fd = 0;
596 	for (; fd < MAX_VFS_DESCRIPTORS; fd++) {
597 		if (sDescriptors[fd] == NULL)
598 			break;
599 	}
600 	if (fd == MAX_VFS_DESCRIPTORS)
601 		return B_ERROR;
602 
603 	TRACE(("got descriptor %d for node %p\n", fd, node));
604 
605 	// we got a free descriptor entry, now try to open the node
606 
607 	void *cookie;
608 	status_t status = node->Open(&cookie, mode);
609 	if (status < B_OK)
610 		return status;
611 
612 	TRACE(("could open node at %p\n", node));
613 
614 	Descriptor *descriptor = new(nothrow) Descriptor(node, cookie);
615 	if (descriptor == NULL)
616 		return B_NO_MEMORY;
617 
618 	sDescriptors[fd] = descriptor;
619 
620 	return fd;
621 }
622 
623 
624 int
625 dup(int fd)
626 {
627 	Descriptor *descriptor = get_descriptor(fd);
628 	if (descriptor == NULL)
629 		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
630 
631 	descriptor->Acquire();
632 	RETURN_AND_SET_ERRNO(fd);
633 }
634 
635 
636 ssize_t
637 read_pos(int fd, off_t offset, void *buffer, size_t bufferSize)
638 {
639 	Descriptor *descriptor = get_descriptor(fd);
640 	if (descriptor == NULL)
641 		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
642 
643 	RETURN_AND_SET_ERRNO(descriptor->ReadAt(offset, buffer, bufferSize));
644 }
645 
646 
647 ssize_t
648 read(int fd, void *buffer, size_t bufferSize)
649 {
650 	Descriptor *descriptor = get_descriptor(fd);
651 	if (descriptor == NULL)
652 		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
653 
654 	RETURN_AND_SET_ERRNO(descriptor->Read(buffer, bufferSize));
655 }
656 
657 
658 ssize_t
659 write_pos(int fd, off_t offset, const void *buffer, size_t bufferSize)
660 {
661 	Descriptor *descriptor = get_descriptor(fd);
662 	if (descriptor == NULL)
663 		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
664 
665 	RETURN_AND_SET_ERRNO(descriptor->WriteAt(offset, buffer, bufferSize));
666 }
667 
668 
669 ssize_t
670 write(int fd, const void *buffer, size_t bufferSize)
671 {
672 	Descriptor *descriptor = get_descriptor(fd);
673 	if (descriptor == NULL)
674 		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
675 
676 	RETURN_AND_SET_ERRNO(descriptor->Write(buffer, bufferSize));
677 }
678 
679 
680 ssize_t
681 writev(int fd, const struct iovec* vecs, size_t count)
682 {
683 	size_t totalWritten = 0;
684 
685 	for (size_t i = 0; i < count; i++) {
686 		ssize_t written = write(fd, vecs[i].iov_base, vecs[i].iov_len);
687 		if (written < 0)
688 			return totalWritten == 0 ? written : totalWritten;
689 
690 		totalWritten += written;
691 
692 		if ((size_t)written != vecs[i].iov_len)
693 			break;
694 	}
695 
696 	return totalWritten;
697 }
698 
699 
700 int
701 open(const char *name, int mode, ...)
702 {
703 	mode_t permissions = 0;
704 	if ((mode & O_CREAT) != 0) {
705         va_list args;
706         va_start(args, mode);
707         permissions = va_arg(args, int) /*& ~__gUmask*/;
708             // adapt the permissions as required by POSIX
709         va_end(args);
710 	}
711 
712 	// we always start at the top (there is no notion of a current directory (yet?))
713 	RETURN_AND_SET_ERRNO(open_from(gRoot, name, mode, permissions));
714 }
715 
716 
717 int
718 open_from(Directory *directory, const char *name, int mode, mode_t permissions)
719 {
720 	if (name[0] == '/') {
721 		// ignore the directory and start from root if we are asked to do that
722 		directory = gRoot;
723 		name++;
724 	}
725 
726 	char path[B_PATH_NAME_LENGTH];
727 	if (strlcpy(path, name, sizeof(path)) >= sizeof(path))
728 		return B_NAME_TOO_LONG;
729 
730 	Node *node;
731 	status_t error = get_node_for_path(directory, path, &node);
732 	if (error != B_OK) {
733 		if (error != B_ENTRY_NOT_FOUND)
734 			return error;
735 
736 		if ((mode & O_CREAT) == 0)
737 			return B_ENTRY_NOT_FOUND;
738 
739 		// try to resolve the parent directory
740 		strlcpy(path, name, sizeof(path));
741 		if (char* lastSlash = strrchr(path, '/')) {
742 			if (lastSlash[1] == '\0')
743 				return B_ENTRY_NOT_FOUND;
744 
745 			lastSlash = '\0';
746 			name = lastSlash + 1;
747 
748 			// resolve the directory
749 			if (get_node_for_path(directory, path, &node) != B_OK)
750 				return B_ENTRY_NOT_FOUND;
751 
752 			if (node->Type() != S_IFDIR) {
753 				node->Release();
754 				return B_NOT_A_DIRECTORY;
755 			}
756 
757 			directory = static_cast<Directory*>(node);
758 		} else
759 			directory->Acquire();
760 
761 		// create the file
762 		error = directory->CreateFile(name, permissions, &node);
763 		directory->Release();
764 
765 		if (error != B_OK)
766 			return error;
767 	} else if ((mode & O_EXCL) != 0) {
768 		node->Release();
769 		return B_FILE_EXISTS;
770 	}
771 
772 	int fd = open_node(node, mode);
773 
774 	node->Release();
775 	return fd;
776 }
777 
778 
779 /** Since we don't have directory functions yet, this
780  *	function is needed to get the contents of a directory.
781  *	It should be removed once readdir() & co. are in place.
782  */
783 
784 Node *
785 get_node_from(int fd)
786 {
787 	Descriptor *descriptor = get_descriptor(fd);
788 	if (descriptor == NULL)
789 		return NULL;
790 
791 	return descriptor->GetNode();
792 }
793 
794 
795 int
796 close(int fd)
797 {
798 	Descriptor *descriptor = get_descriptor(fd);
799 	if (descriptor == NULL)
800 		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
801 
802 	status_t status = descriptor->Release();
803 	if (!descriptor->RefCount())
804 		free_descriptor(fd);
805 
806 	RETURN_AND_SET_ERRNO(status);
807 }
808 
809 
810 // ToDo: remove this kludge when possible
811 int
812 #if defined(fstat) && !defined(main)
813 _fstat(int fd, struct stat *stat, size_t /*statSize*/)
814 #else
815 fstat(int fd, struct stat *stat)
816 #endif
817 {
818 	if (stat == NULL)
819 		RETURN_AND_SET_ERRNO(B_BAD_VALUE);
820 
821 	Descriptor *descriptor = get_descriptor(fd);
822 	if (descriptor == NULL)
823 		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
824 
825 	RETURN_AND_SET_ERRNO(descriptor->Stat(*stat));
826 }
827