1/*
2 * Copyright 2003-2013, Axel D��rfler, axeld@pinc-software.de.
3 * Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Copyright 2017, Jessica Hamilton, jessica.l.hamilton@gmail.com.
5 * Distributed under the terms of the MIT License.
6 */
7
8
9#include <boot/vfs.h>
10
11#include <errno.h>
12#include <fcntl.h>
13#include <string.h>
14#include <sys/uio.h>
15#include <unistd.h>
16
17#include <StorageDefs.h>
18
19#include <AutoDeleter.h>
20
21#include <boot/platform.h>
22#include <boot/partitions.h>
23#include <boot/stdio.h>
24#include <boot/stage2.h>
25#include <syscall_utils.h>
26
27#include "package_support.h"
28#include "RootFileSystem.h"
29#include "file_systems/packagefs/packagefs.h"
30
31
32using namespace boot;
33
34//#define TRACE_VFS
35#ifdef TRACE_VFS
36#	define TRACE(x) dprintf x
37#else
38#	define TRACE(x) ;
39#endif
40
41
42struct __DIR {
43	Directory*	directory;
44	void*		cookie;
45	dirent		entry;
46	char		nameBuffer[B_FILE_NAME_LENGTH - 1];
47};
48
49
50class Descriptor {
51	public:
52		Descriptor(Node *node, void *cookie);
53		~Descriptor();
54
55		ssize_t ReadAt(off_t pos, void *buffer, size_t bufferSize);
56		ssize_t Read(void *buffer, size_t bufferSize);
57		ssize_t WriteAt(off_t pos, const void *buffer, size_t bufferSize);
58		ssize_t Write(const void *buffer, size_t bufferSize);
59
60		void Stat(struct stat &stat);
61		status_t Seek(off_t position, int mode);
62
63		off_t Offset() const { return fOffset; }
64		int32 RefCount() const { return fRefCount; }
65
66		status_t Acquire();
67		status_t Release();
68
69		Node *GetNode() const { return fNode; }
70
71	private:
72		Node	*fNode;
73		void	*fCookie;
74		off_t	fOffset;
75		int32	fRefCount;
76};
77
78#define MAX_VFS_DESCRIPTORS 64
79
80NodeList gBootDevices;
81NodeList gPartitions;
82RootFileSystem *gRoot;
83static Descriptor *sDescriptors[MAX_VFS_DESCRIPTORS];
84static Node *sBootDevice;
85
86
87Node::Node()
88	:
89	fRefCount(1)
90{
91}
92
93
94Node::~Node()
95{
96}
97
98
99status_t
100Node::Open(void **_cookie, int mode)
101{
102	TRACE(("%p::Open()\n", this));
103	return Acquire();
104}
105
106
107status_t
108Node::Close(void *cookie)
109{
110	TRACE(("%p::Close()\n", this));
111	return Release();
112}
113
114
115status_t
116Node::ReadLink(char* buffer, size_t bufferSize)
117{
118	return B_BAD_VALUE;
119}
120
121
122status_t
123Node::GetName(char *nameBuffer, size_t bufferSize) const
124{
125	return B_ERROR;
126}
127
128
129status_t
130Node::GetFileMap(struct file_map_run *runs, int32 *count)
131{
132	return B_ERROR;
133}
134
135
136int32
137Node::Type() const
138{
139	return 0;
140}
141
142
143off_t
144Node::Size() const
145{
146	return 0LL;
147}
148
149
150ino_t
151Node::Inode() const
152{
153	return 0;
154}
155
156
157void
158Node::Stat(struct stat& stat)
159{
160	stat.st_mode = Type();
161	stat.st_size = Size();
162	stat.st_ino = Inode();
163}
164
165
166status_t
167Node::Acquire()
168{
169	fRefCount++;
170	TRACE(("%p::Acquire(), fRefCount = %ld\n", this, fRefCount));
171	return B_OK;
172}
173
174
175status_t
176Node::Release()
177{
178	TRACE(("%p::Release(), fRefCount = %ld\n", this, fRefCount));
179	if (--fRefCount == 0) {
180		TRACE(("delete node: %p\n", this));
181		delete this;
182		return 1;
183	}
184
185	return B_OK;
186}
187
188
189//	#pragma mark -
190
191
192ConsoleNode::ConsoleNode()
193	: Node()
194{
195}
196
197
198ssize_t
199ConsoleNode::Read(void *buffer, size_t bufferSize)
200{
201	return ReadAt(NULL, -1, buffer, bufferSize);
202}
203
204
205ssize_t
206ConsoleNode::Write(const void *buffer, size_t bufferSize)
207{
208	return WriteAt(NULL, -1, buffer, bufferSize);
209}
210
211
212//	#pragma mark -
213
214
215Directory::Directory()
216	: Node()
217{
218}
219
220
221ssize_t
222Directory::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
223{
224	return B_ERROR;
225}
226
227
228ssize_t
229Directory::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
230{
231	return B_ERROR;
232}
233
234
235int32
236Directory::Type() const
237{
238	return S_IFDIR;
239}
240
241
242Node*
243Directory::Lookup(const char* name, bool traverseLinks)
244{
245	Node* node = LookupDontTraverse(name);
246	if (node == NULL)
247		return NULL;
248
249	if (!traverseLinks || !S_ISLNK(node->Type()))
250		return node;
251
252	// the node is a symbolic link, so we have to resolve the path
253	char linkPath[B_PATH_NAME_LENGTH];
254	status_t error = node->ReadLink(linkPath, sizeof(linkPath));
255
256	node->Release();
257		// we don't need this one anymore
258
259	if (error != B_OK)
260		return NULL;
261
262	// let open_from() do the real work
263	int fd = open_from(this, linkPath, O_RDONLY);
264	if (fd < 0)
265		return NULL;
266
267	node = get_node_from(fd);
268	if (node != NULL)
269		node->Acquire();
270
271	close(fd);
272	return node;
273}
274
275
276status_t
277Directory::CreateFile(const char *name, mode_t permissions, Node **_node)
278{
279	return EROFS;
280}
281
282
283//	#pragma mark -
284
285
286MemoryDisk::MemoryDisk(const uint8* data, size_t size, const char* name)
287	: Node(),
288	  fData(data),
289	  fSize(size)
290{
291	strlcpy(fName, name, sizeof(fName));
292}
293
294
295ssize_t
296MemoryDisk::ReadAt(void* cookie, off_t pos, void* buffer, size_t bufferSize)
297{
298	if (pos < 0)
299		return B_BAD_VALUE;
300	if ((size_t)pos >= fSize)
301		return 0;
302
303	if (pos + bufferSize > fSize)
304		bufferSize = fSize - pos;
305
306	memcpy(buffer, fData + pos, bufferSize);
307	return bufferSize;
308}
309
310
311ssize_t
312MemoryDisk::WriteAt(void* cookie, off_t pos, const void* buffer,
313	size_t bufferSize)
314{
315	return B_NOT_ALLOWED;
316}
317
318
319off_t
320MemoryDisk::Size() const
321{
322	return fSize;
323}
324
325
326status_t
327MemoryDisk::GetName(char *nameBuffer, size_t bufferSize) const
328{
329	if (!nameBuffer)
330		return B_BAD_VALUE;
331
332	strlcpy(nameBuffer, fName, bufferSize);
333	return B_OK;
334}
335
336
337//	#pragma mark -
338
339
340Descriptor::Descriptor(Node *node, void *cookie)
341	:
342	fNode(node),
343	fCookie(cookie),
344	fOffset(0),
345	fRefCount(1)
346{
347}
348
349
350Descriptor::~Descriptor()
351{
352}
353
354
355ssize_t
356Descriptor::Read(void *buffer, size_t bufferSize)
357{
358	ssize_t bytesRead = fNode->ReadAt(fCookie, fOffset, buffer, bufferSize);
359	if (bytesRead > B_OK)
360		fOffset += bytesRead;
361
362	return bytesRead;
363}
364
365
366ssize_t
367Descriptor::ReadAt(off_t pos, void *buffer, size_t bufferSize)
368{
369	return fNode->ReadAt(fCookie, pos, buffer, bufferSize);
370}
371
372
373ssize_t
374Descriptor::Write(const void *buffer, size_t bufferSize)
375{
376	ssize_t bytesWritten = fNode->WriteAt(fCookie, fOffset, buffer, bufferSize);
377	if (bytesWritten > B_OK)
378		fOffset += bytesWritten;
379
380	return bytesWritten;
381}
382
383
384ssize_t
385Descriptor::WriteAt(off_t pos, const void *buffer, size_t bufferSize)
386{
387	return fNode->WriteAt(fCookie, pos, buffer, bufferSize);
388}
389
390
391void
392Descriptor::Stat(struct stat &stat)
393{
394	fNode->Stat(stat);
395}
396
397
398status_t
399Descriptor::Seek(off_t position, int mode)
400{
401	off_t newPosition;
402	switch (mode)
403	{
404		case SEEK_SET:
405			newPosition = position;
406			break;
407		case SEEK_CUR:
408			newPosition = fOffset + position;
409			break;
410		case SEEK_END:
411		{
412			struct stat st;
413			Stat(st);
414			newPosition = st.st_size + position;
415			break;
416		}
417		default:
418			return B_BAD_VALUE;
419	}
420
421	if (newPosition < 0)
422		return B_BAD_VALUE;
423
424	fOffset = newPosition;
425	return B_OK;
426}
427
428
429status_t
430Descriptor::Acquire()
431{
432	fRefCount++;
433	return B_OK;
434}
435
436
437status_t
438Descriptor::Release()
439{
440	if (--fRefCount == 0) {
441		status_t status = fNode->Close(fCookie);
442		if (status != B_OK)
443			return status;
444	}
445
446	return B_OK;
447}
448
449
450//	#pragma mark -
451
452
453BootVolume::BootVolume()
454	:
455	fRootDirectory(NULL),
456	fSystemDirectory(NULL),
457	fPackageVolumeInfo(NULL),
458	fPackageVolumeState(NULL)
459{
460}
461
462
463BootVolume::~BootVolume()
464{
465	Unset();
466}
467
468
469status_t
470BootVolume::SetTo(Directory* rootDirectory,
471	PackageVolumeInfo* packageVolumeInfo,
472	PackageVolumeState* packageVolumeState)
473{
474	Unset();
475
476	status_t error = _SetTo(rootDirectory, packageVolumeInfo,
477		packageVolumeState);
478	if (error != B_OK)
479		Unset();
480
481	return error;
482}
483
484
485void
486BootVolume::Unset()
487{
488	if (fRootDirectory != NULL) {
489		fRootDirectory->Release();
490		fRootDirectory = NULL;
491	}
492
493	if (fSystemDirectory != NULL) {
494		fSystemDirectory->Release();
495		fSystemDirectory = NULL;
496	}
497
498	if (fPackageVolumeInfo != NULL) {
499		fPackageVolumeInfo->ReleaseReference();
500		fPackageVolumeInfo = NULL;
501		fPackageVolumeState = NULL;
502	}
503}
504
505
506status_t
507BootVolume::_SetTo(Directory* rootDirectory,
508	PackageVolumeInfo* packageVolumeInfo,
509	PackageVolumeState* packageVolumeState)
510{
511	Unset();
512
513	if (rootDirectory == NULL)
514		return B_BAD_VALUE;
515
516	fRootDirectory = rootDirectory;
517	fRootDirectory->Acquire();
518
519	// find the system directory
520	Node* systemNode = fRootDirectory->Lookup("system", true);
521	if (systemNode == NULL || !S_ISDIR(systemNode->Type())) {
522		if (systemNode != NULL)
523			systemNode->Release();
524		Unset();
525		return B_ENTRY_NOT_FOUND;
526	}
527
528	fSystemDirectory = static_cast<Directory*>(systemNode);
529
530	if (packageVolumeInfo == NULL) {
531		// get a package volume info
532		BReference<PackageVolumeInfo> packageVolumeInfoReference(
533			new(std::nothrow) PackageVolumeInfo);
534		status_t error = packageVolumeInfoReference->SetTo(fSystemDirectory,
535			"packages");
536		if (error != B_OK) {
537			// apparently not packaged
538			return B_OK;
539		}
540
541		fPackageVolumeInfo = packageVolumeInfoReference.Detach();
542	} else {
543		fPackageVolumeInfo = packageVolumeInfo;
544		fPackageVolumeInfo->AcquireReference();
545	}
546
547	fPackageVolumeState = packageVolumeState != NULL
548		? packageVolumeState : fPackageVolumeInfo->States().Head();
549
550	// try opening the system package
551	int packageFD = _OpenSystemPackage();
552	if (packageFD < 0)
553		return packageFD;
554
555	// mount packagefs
556	Directory* packageRootDirectory;
557	status_t error = packagefs_mount_file(packageFD, fSystemDirectory,
558		packageRootDirectory);
559	close(packageFD);
560	if (error != B_OK) {
561		Unset();
562		return error;
563	}
564
565	fSystemDirectory->Release();
566	fSystemDirectory = packageRootDirectory;
567
568	return B_OK;
569}
570
571
572int
573BootVolume::_OpenSystemPackage()
574{
575	// open the packages directory
576	Node* packagesNode = fSystemDirectory->Lookup("packages", false);
577	if (packagesNode == NULL)
578		return -1;
579	MethodDeleter<Node, status_t> packagesNodeReleaser(packagesNode,
580		&Node::Release);
581
582	if (!S_ISDIR(packagesNode->Type()))
583		return -1;
584	Directory* packageDirectory = (Directory*)packagesNode;
585
586	// open the system package
587	return open_from(packageDirectory, fPackageVolumeState->SystemPackage(),
588		O_RDONLY);
589}
590
591
592//	#pragma mark -
593
594
595status_t
596vfs_init(stage2_args *args)
597{
598	gRoot = new(nothrow) RootFileSystem();
599	if (gRoot == NULL)
600		return B_NO_MEMORY;
601
602	return B_OK;
603}
604
605
606status_t
607register_boot_file_system(BootVolume& bootVolume)
608{
609	Directory* rootDirectory = bootVolume.RootDirectory();
610	gRoot->AddLink("boot", rootDirectory);
611
612	Partition *partition;
613	status_t status = gRoot->GetPartitionFor(rootDirectory, &partition);
614	if (status != B_OK) {
615		dprintf("register_boot_file_system(): could not locate boot volume in "
616			"root!\n");
617		return status;
618	}
619
620	gBootVolume.SetInt64(BOOT_VOLUME_PARTITION_OFFSET,
621		partition->offset);
622
623	if (bootVolume.IsPackaged()) {
624		gBootVolume.SetBool(BOOT_VOLUME_PACKAGED, true);
625		PackageVolumeState* state = bootVolume.GetPackageVolumeState();
626		if (state->Name() != NULL)
627			gBootVolume.AddString(BOOT_VOLUME_PACKAGES_STATE, state->Name());
628	}
629
630	Node *device = get_node_from(partition->FD());
631	if (device == NULL) {
632		dprintf("register_boot_file_system(): could not get boot device!\n");
633		return B_ERROR;
634	}
635
636	return platform_register_boot_device(device);
637}
638
639
640/*! Gets the boot device, scans all of its partitions, gets the
641	boot partition, and mounts its file system.
642
643	\param args The stage 2 arguments.
644	\param _bootVolume On success set to the boot volume.
645	\return \c B_OK on success, another error code otherwise.
646*/
647status_t
648get_boot_file_system(stage2_args* args, BootVolume& _bootVolume)
649{
650	status_t error = platform_add_boot_device(args, &gBootDevices);
651	if (error != B_OK)
652		return error;
653
654	NodeIterator iterator = gBootDevices.GetIterator();
655	while (iterator.HasNext()) {
656		Node *device = iterator.Next();
657
658		error = add_partitions_for(device, false, true);
659		if (error != B_OK)
660			continue;
661
662		NodeList bootPartitions;
663		error = platform_get_boot_partitions(args, device, &gPartitions, &bootPartitions);
664		if (error != B_OK)
665			continue;
666
667		NodeIterator partitionIterator = bootPartitions.GetIterator();
668		while (partitionIterator.HasNext()) {
669			Partition *partition = (Partition*)partitionIterator.Next();
670
671			Directory *fileSystem;
672			error = partition->Mount(&fileSystem, true);
673			if (error != B_OK) {
674				// this partition doesn't contain any known file system; we
675				// don't need it anymore
676				gPartitions.Remove(partition);
677				delete partition;
678				continue;
679			}
680
681			// init the BootVolume
682			error = _bootVolume.SetTo(fileSystem);
683			if (error != B_OK)
684				continue;
685
686			sBootDevice = device;
687			return B_OK;
688		}
689	}
690
691	return B_ERROR;
692}
693
694
695/** Mounts all file systems recognized on the given device by
696 *	calling the add_partitions_for() function on them.
697 */
698
699status_t
700mount_file_systems(stage2_args *args)
701{
702	// mount other partitions on boot device (if any)
703	NodeIterator iterator = gPartitions.GetIterator();
704
705	Partition *partition = NULL;
706	while ((partition = (Partition *)iterator.Next()) != NULL) {
707		// don't scan known partitions again
708		if (partition->IsFileSystem())
709			continue;
710
711		// remove the partition if it doesn't contain a (known) file system
712		if (partition->Scan(true) != B_OK && !partition->IsFileSystem()) {
713			gPartitions.Remove(partition);
714			delete partition;
715		}
716	}
717
718	// add all block devices the platform has for us
719
720	status_t status = platform_add_block_devices(args, &gBootDevices);
721	if (status < B_OK)
722		return status;
723
724	iterator = gBootDevices.GetIterator();
725	Node *device = NULL, *last = NULL;
726	while ((device = iterator.Next()) != NULL) {
727		// don't scan former boot device again
728		if (device == sBootDevice)
729			continue;
730
731		if (add_partitions_for(device, true) == B_OK) {
732			// ToDo: we can't delete the object here, because it must
733			//	be removed from the list before we know that it was
734			//	deleted.
735
736/*			// if the Release() deletes the object, we need to skip it
737			if (device->Release() > 0) {
738				list_remove_item(&gBootDevices, device);
739				device = last;
740			}
741*/
742(void)last;
743		}
744		last = device;
745	}
746
747	if (gPartitions.IsEmpty())
748		return B_ENTRY_NOT_FOUND;
749
750#if 0
751	void *cookie;
752	if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
753		Directory *directory;
754		while (gRoot->GetNextNode(cookie, (Node **)&directory) == B_OK) {
755			char name[256];
756			if (directory->GetName(name, sizeof(name)) == B_OK)
757				printf(":: %s (%p)\n", name, directory);
758
759			void *subCookie;
760			if (directory->Open(&subCookie, O_RDONLY) == B_OK) {
761				while (directory->GetNextEntry(subCookie, name, sizeof(name)) == B_OK) {
762					printf("\t%s\n", name);
763				}
764				directory->Close(subCookie);
765			}
766		}
767		gRoot->Close(cookie);
768	}
769#endif
770
771	return B_OK;
772}
773
774
775/*!	Resolves \a directory + \a path to a node.
776	Note that \a path will be modified by the function.
777*/
778static status_t
779get_node_for_path(Directory *directory, char *path, Node **_node)
780{
781	directory->Acquire();
782		// balance Acquire()/Release() calls
783
784	while (true) {
785		Node *nextNode;
786		char *nextPath;
787
788		// walk to find the next path component ("path" will point to a single
789		// path component), and filter out multiple slashes
790		for (nextPath = path + 1; nextPath[0] != '\0' && nextPath[0] != '/'; nextPath++);
791
792		if (*nextPath == '/') {
793			*nextPath = '\0';
794			do
795				nextPath++;
796			while (*nextPath == '/');
797		}
798
799		nextNode = directory->Lookup(path, true);
800		directory->Release();
801
802		if (nextNode == NULL)
803			return B_ENTRY_NOT_FOUND;
804
805		path = nextPath;
806		if (S_ISDIR(nextNode->Type()))
807			directory = (Directory *)nextNode;
808		else if (path[0])
809			return B_NOT_ALLOWED;
810
811		// are we done?
812		if (path[0] == '\0') {
813			*_node = nextNode;
814			return B_OK;
815		}
816	}
817
818	return B_ENTRY_NOT_FOUND;
819}
820
821
822/*!	Version of get_node_for_path() not modifying \a path.
823 */
824static status_t
825get_node_for_path(Directory* directory, const char* path, Node** _node)
826{
827	char* mutablePath = strdup(path);
828	if (mutablePath == NULL)
829		return B_NO_MEMORY;
830	MemoryDeleter mutablePathDeleter(mutablePath);
831
832	return get_node_for_path(directory, mutablePath, _node);
833}
834
835//	#pragma mark -
836
837
838static Descriptor *
839get_descriptor(int fd)
840{
841	if (fd < 0 || fd >= MAX_VFS_DESCRIPTORS)
842		return NULL;
843
844	return sDescriptors[fd];
845}
846
847
848static void
849free_descriptor(int fd)
850{
851	if (fd >= MAX_VFS_DESCRIPTORS)
852		return;
853
854	delete sDescriptors[fd];
855	sDescriptors[fd] = NULL;
856}
857
858
859/**	Reserves an entry of the descriptor table and
860 *	assigns the given node to it.
861 */
862
863int
864open_node(Node *node, int mode)
865{
866	if (node == NULL)
867		return B_ERROR;
868
869	// get free descriptor
870
871	int fd = 0;
872	for (; fd < MAX_VFS_DESCRIPTORS; fd++) {
873		if (sDescriptors[fd] == NULL)
874			break;
875	}
876	if (fd == MAX_VFS_DESCRIPTORS)
877		return B_ERROR;
878
879	TRACE(("got descriptor %d for node %p\n", fd, node));
880
881	// we got a free descriptor entry, now try to open the node
882
883	void *cookie;
884	status_t status = node->Open(&cookie, mode);
885	if (status < B_OK)
886		return status;
887
888	TRACE(("could open node at %p\n", node));
889
890	Descriptor *descriptor = new(nothrow) Descriptor(node, cookie);
891	if (descriptor == NULL)
892		return B_NO_MEMORY;
893
894	sDescriptors[fd] = descriptor;
895
896	return fd;
897}
898
899
900int
901dup(int fd)
902{
903	Descriptor *descriptor = get_descriptor(fd);
904	if (descriptor == NULL)
905		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
906
907	descriptor->Acquire();
908	RETURN_AND_SET_ERRNO(fd);
909}
910
911
912off_t
913lseek(int fd, off_t offset, int whence)
914{
915	Descriptor* descriptor = get_descriptor(fd);
916	if (descriptor == NULL)
917		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
918
919	status_t error = descriptor->Seek(offset, whence);
920	if (error != B_OK)
921		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
922
923	return descriptor->Offset();
924}
925
926
927int
928ftruncate(int fd, off_t newSize)
929{
930	dprintf("ftruncate() not implemented!\n");
931	RETURN_AND_SET_ERRNO(B_FILE_ERROR);
932}
933
934
935ssize_t
936read_pos(int fd, off_t offset, void *buffer, size_t bufferSize)
937{
938	Descriptor *descriptor = get_descriptor(fd);
939	if (descriptor == NULL)
940		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
941
942	RETURN_AND_SET_ERRNO(descriptor->ReadAt(offset, buffer, bufferSize));
943}
944
945
946ssize_t
947pread(int fd, void* buffer, size_t bufferSize, off_t offset)
948{
949	return read_pos(fd, offset, buffer, bufferSize);
950}
951
952
953ssize_t
954read(int fd, void *buffer, size_t bufferSize)
955{
956	Descriptor *descriptor = get_descriptor(fd);
957	if (descriptor == NULL)
958		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
959
960	RETURN_AND_SET_ERRNO(descriptor->Read(buffer, bufferSize));
961}
962
963
964ssize_t
965write_pos(int fd, off_t offset, const void *buffer, size_t bufferSize)
966{
967	Descriptor *descriptor = get_descriptor(fd);
968	if (descriptor == NULL)
969		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
970
971	RETURN_AND_SET_ERRNO(descriptor->WriteAt(offset, buffer, bufferSize));
972}
973
974
975ssize_t
976pwrite(int fd, const void* buffer, size_t bufferSize, off_t offset)
977{
978	return write_pos(fd, offset, buffer, bufferSize);
979}
980
981
982ssize_t
983write(int fd, const void *buffer, size_t bufferSize)
984{
985	Descriptor *descriptor = get_descriptor(fd);
986	if (descriptor == NULL)
987		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
988
989	RETURN_AND_SET_ERRNO(descriptor->Write(buffer, bufferSize));
990}
991
992
993ssize_t
994writev(int fd, const struct iovec* vecs, size_t count)
995{
996	size_t totalWritten = 0;
997
998	for (size_t i = 0; i < count; i++) {
999		ssize_t written = write(fd, vecs[i].iov_base, vecs[i].iov_len);
1000		if (written < 0)
1001			return totalWritten == 0 ? written : totalWritten;
1002
1003		totalWritten += written;
1004
1005		if ((size_t)written != vecs[i].iov_len)
1006			break;
1007	}
1008
1009	return totalWritten;
1010}
1011
1012
1013int
1014open(const char *name, int mode, ...)
1015{
1016	mode_t permissions = 0;
1017	if ((mode & O_CREAT) != 0) {
1018        va_list args;
1019        va_start(args, mode);
1020        permissions = va_arg(args, int) /*& ~__gUmask*/;
1021            // adapt the permissions as required by POSIX
1022        va_end(args);
1023	}
1024
1025	// we always start at the top (there is no notion of a current directory (yet?))
1026	RETURN_AND_SET_ERRNO(open_from(gRoot, name, mode, permissions));
1027}
1028
1029
1030int
1031open_from(Directory *directory, const char *name, int mode, mode_t permissions)
1032{
1033	if (name[0] == '/') {
1034		// ignore the directory and start from root if we are asked to do that
1035		directory = gRoot;
1036		name++;
1037	}
1038
1039	char path[B_PATH_NAME_LENGTH];
1040	if (strlcpy(path, name, sizeof(path)) >= sizeof(path))
1041		return B_NAME_TOO_LONG;
1042
1043	Node *node;
1044	status_t error = get_node_for_path(directory, path, &node);
1045	if (error != B_OK) {
1046		if (error != B_ENTRY_NOT_FOUND)
1047			return error;
1048
1049		if ((mode & O_CREAT) == 0)
1050			return B_ENTRY_NOT_FOUND;
1051
1052		// try to resolve the parent directory
1053		strlcpy(path, name, sizeof(path));
1054		if (char* lastSlash = strrchr(path, '/')) {
1055			if (lastSlash[1] == '\0')
1056				return B_ENTRY_NOT_FOUND;
1057
1058			*lastSlash = '\0';
1059			name = lastSlash + 1;
1060
1061			// resolve the directory
1062			if (get_node_for_path(directory, path, &node) != B_OK)
1063				return B_ENTRY_NOT_FOUND;
1064
1065			if (node->Type() != S_IFDIR) {
1066				node->Release();
1067				return B_NOT_A_DIRECTORY;
1068			}
1069
1070			directory = static_cast<Directory*>(node);
1071		} else
1072			directory->Acquire();
1073
1074		// create the file
1075		error = directory->CreateFile(name, permissions, &node);
1076		directory->Release();
1077
1078		if (error != B_OK)
1079			return error;
1080	} else if ((mode & O_EXCL) != 0) {
1081		node->Release();
1082		return B_FILE_EXISTS;
1083	}
1084
1085	int fd = open_node(node, mode);
1086
1087	node->Release();
1088	return fd;
1089}
1090
1091
1092/** Since we don't have directory functions yet, this
1093 *	function is needed to get the contents of a directory.
1094 *	It should be removed once readdir() & co. are in place.
1095 */
1096
1097Node *
1098get_node_from(int fd)
1099{
1100	Descriptor *descriptor = get_descriptor(fd);
1101	if (descriptor == NULL)
1102		return NULL;
1103
1104	return descriptor->GetNode();
1105}
1106
1107
1108status_t
1109get_stat(Directory* directory, const char* path, struct stat& st)
1110{
1111	Node* node;
1112	status_t error = get_node_for_path(directory, path, &node);
1113	if (error != B_OK)
1114		return error;
1115
1116	node->Stat(st);
1117	node->Release();
1118	return B_OK;
1119}
1120
1121
1122Directory*
1123directory_from(DIR* dir)
1124{
1125	return dir != NULL ? dir->directory : NULL;
1126}
1127
1128
1129int
1130close(int fd)
1131{
1132	Descriptor *descriptor = get_descriptor(fd);
1133	if (descriptor == NULL)
1134		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
1135
1136	status_t status = descriptor->Release();
1137	if (!descriptor->RefCount())
1138		free_descriptor(fd);
1139
1140	RETURN_AND_SET_ERRNO(status);
1141}
1142
1143
1144// ToDo: remove this kludge when possible
1145int
1146#if defined(fstat) && !defined(main)
1147_fstat(int fd, struct stat *stat, size_t /*statSize*/)
1148#else
1149fstat(int fd, struct stat *stat)
1150#endif
1151{
1152	if (stat == NULL)
1153		RETURN_AND_SET_ERRNO(B_BAD_VALUE);
1154
1155	Descriptor *descriptor = get_descriptor(fd);
1156	if (descriptor == NULL)
1157		RETURN_AND_SET_ERRNO(B_FILE_ERROR);
1158
1159	descriptor->Stat(*stat);
1160	return 0;
1161}
1162
1163
1164DIR*
1165open_directory(Directory* baseDirectory, const char* path)
1166{
1167	DIR* dir = new(std::nothrow) DIR;
1168	if (dir == NULL) {
1169		errno = B_NO_MEMORY;
1170		return NULL;
1171	}
1172	ObjectDeleter<DIR> dirDeleter(dir);
1173
1174	Node* node;
1175	status_t error = get_node_for_path(baseDirectory, path, &node);
1176	if (error != B_OK) {
1177		errno = error;
1178		return NULL;
1179	}
1180	MethodDeleter<Node, status_t> nodeReleaser(node, &Node::Release);
1181
1182	if (!S_ISDIR(node->Type())) {
1183		errno = error;
1184		return NULL;
1185	}
1186
1187	dir->directory = static_cast<Directory*>(node);
1188
1189	error = dir->directory->Open(&dir->cookie, O_RDONLY);
1190	if (error != B_OK) {
1191		errno = error;
1192		return NULL;
1193	}
1194
1195	nodeReleaser.Detach();
1196	return dirDeleter.Detach();
1197}
1198
1199
1200DIR*
1201opendir(const char* dirName)
1202{
1203	return open_directory(gRoot, dirName);
1204}
1205
1206
1207int
1208closedir(DIR* dir)
1209{
1210	if (dir != NULL) {
1211		dir->directory->Close(dir->cookie);
1212		dir->directory->Release();
1213		delete dir;
1214	}
1215
1216	return 0;
1217}
1218
1219
1220struct dirent*
1221readdir(DIR* dir)
1222{
1223	if (dir == NULL) {
1224		errno = B_BAD_VALUE;
1225		return NULL;
1226	}
1227
1228	for (;;) {
1229		status_t error = dir->directory->GetNextEntry(dir->cookie,
1230			dir->entry.d_name, B_FILE_NAME_LENGTH);
1231		if (error != B_OK) {
1232			errno = error;
1233			return NULL;
1234		}
1235
1236		dir->entry.d_pdev = 0;
1237			// not supported
1238		dir->entry.d_pino = dir->directory->Inode();
1239		dir->entry.d_dev = dir->entry.d_pdev;
1240			// not supported
1241
1242		if (strcmp(dir->entry.d_name, ".") == 0
1243				|| strcmp(dir->entry.d_name, "..") == 0) {
1244			// Note: That's obviously not correct for "..", but we can't
1245			// retrieve that information.
1246			dir->entry.d_ino = dir->entry.d_pino;
1247		} else {
1248			Node* node = dir->directory->Lookup(dir->entry.d_name, false);
1249			if (node == NULL)
1250				continue;
1251
1252			dir->entry.d_ino = node->Inode();
1253			node->Release();
1254		}
1255
1256		return &dir->entry;
1257	}
1258}
1259
1260
1261void
1262rewinddir(DIR* dir)
1263{
1264	if (dir == NULL) {
1265		errno = B_BAD_VALUE;
1266		return;
1267	}
1268
1269	status_t error = dir->directory->Rewind(dir->cookie);
1270	if (error != B_OK)
1271		errno = error;
1272}
1273