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