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