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