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
27using 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
37class 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
66NodeList gBootDevices;
67NodeList gPartitions;
68RootFileSystem *gRoot;
69static Descriptor *sDescriptors[MAX_VFS_DESCRIPTORS];
70static Node *sBootDevice;
71
72
73Node::Node()
74	:
75	fRefCount(1)
76{
77}
78
79
80Node::~Node()
81{
82}
83
84
85status_t
86Node::Open(void **_cookie, int mode)
87{
88	TRACE(("%p::Open()\n", this));
89	return Acquire();
90}
91
92
93status_t
94Node::Close(void *cookie)
95{
96	TRACE(("%p::Close()\n", this));
97	return Release();
98}
99
100
101status_t
102Node::GetName(char *nameBuffer, size_t bufferSize) const
103{
104	return B_ERROR;
105}
106
107
108status_t
109Node::GetFileMap(struct file_map_run *runs, int32 *count)
110{
111	return B_ERROR;
112}
113
114
115int32
116Node::Type() const
117{
118	return 0;
119}
120
121
122off_t
123Node::Size() const
124{
125	return 0LL;
126}
127
128
129ino_t
130Node::Inode() const
131{
132	return 0;
133}
134
135
136status_t
137Node::Acquire()
138{
139	fRefCount++;
140	TRACE(("%p::Acquire(), fRefCount = %ld\n", this, fRefCount));
141	return B_OK;
142}
143
144
145status_t
146Node::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
162ConsoleNode::ConsoleNode()
163	: Node()
164{
165}
166
167
168ssize_t
169ConsoleNode::Read(void *buffer, size_t bufferSize)
170{
171	return ReadAt(NULL, -1, buffer, bufferSize);
172}
173
174
175ssize_t
176ConsoleNode::Write(const void *buffer, size_t bufferSize)
177{
178	return WriteAt(NULL, -1, buffer, bufferSize);
179}
180
181
182//	#pragma mark -
183
184
185Directory::Directory()
186	: Node()
187{
188}
189
190
191ssize_t
192Directory::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
193{
194	return B_ERROR;
195}
196
197
198ssize_t
199Directory::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
200{
201	return B_ERROR;
202}
203
204
205int32
206Directory::Type() const
207{
208	return S_IFDIR;
209}
210
211
212status_t
213Directory::CreateFile(const char *name, mode_t permissions, Node **_node)
214{
215	return EROFS;
216}
217
218
219//	#pragma mark -
220
221
222MemoryDisk::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
231ssize_t
232MemoryDisk::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
245ssize_t
246MemoryDisk::WriteAt(void* cookie, off_t pos, const void* buffer,
247	size_t bufferSize)
248{
249	return B_NOT_ALLOWED;
250}
251
252
253off_t
254MemoryDisk::Size() const
255{
256	return fSize;
257}
258
259
260status_t
261MemoryDisk::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
274Descriptor::Descriptor(Node *node, void *cookie)
275	:
276	fNode(node),
277	fCookie(cookie),
278	fOffset(0),
279	fRefCount(1)
280{
281}
282
283
284Descriptor::~Descriptor()
285{
286}
287
288
289ssize_t
290Descriptor::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
300ssize_t
301Descriptor::ReadAt(off_t pos, void *buffer, size_t bufferSize)
302{
303	return fNode->ReadAt(fCookie, pos, buffer, bufferSize);
304}
305
306
307ssize_t
308Descriptor::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
318ssize_t
319Descriptor::WriteAt(off_t pos, const void *buffer, size_t bufferSize)
320{
321	return fNode->WriteAt(fCookie, pos, buffer, bufferSize);
322}
323
324
325status_t
326Descriptor::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
336status_t
337Descriptor::Acquire()
338{
339	fRefCount++;
340	return B_OK;
341}
342
343
344status_t
345Descriptor::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
360status_t
361vfs_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
371status_t
372register_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
400Directory *
401get_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
437status_t
438mount_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*/
515static status_t
516get_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
562static Descriptor *
563get_descriptor(int fd)
564{
565	if (fd < 0 || fd >= MAX_VFS_DESCRIPTORS)
566		return NULL;
567
568	return sDescriptors[fd];
569}
570
571
572static void
573free_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
587int
588open_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
624int
625dup(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
636ssize_t
637read_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
647ssize_t
648read(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
658ssize_t
659write_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
669ssize_t
670write(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
680ssize_t
681writev(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
700int
701open(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
717int
718open_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
784Node *
785get_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
795int
796close(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
811int
812#if defined(fstat) && !defined(main)
813_fstat(int fd, struct stat *stat, size_t /*statSize*/)
814#else
815fstat(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