xref: /haiku/src/system/boot/loader/vfs.cpp (revision 268f99dd7dc4bd7474a8bd2742d3f1ec1de6752a)
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 
32 using 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 
42 struct __DIR {
43 	Directory*	directory;
44 	void*		cookie;
45 
46 	char		_direntBuffer[sizeof(dirent) + B_FILE_NAME_LENGTH + 1];
entry__DIR47 	dirent*		entry() { return (dirent*)_direntBuffer; }
48 };
49 
50 
51 class 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 
Offset() const64 		off_t Offset() const { return fOffset; }
RefCount() const65 		int32 RefCount() const { return fRefCount; }
66 
67 		status_t Acquire();
68 		status_t Release();
69 
GetNode() const70 		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 
81 NodeList gBootDevices;
82 NodeList gPartitions;
83 RootFileSystem *gRoot;
84 static Descriptor *sDescriptors[MAX_VFS_DESCRIPTORS];
85 static Node *sBootDevice;
86 
87 
Node()88 Node::Node()
89 	:
90 	fRefCount(1)
91 {
92 }
93 
94 
~Node()95 Node::~Node()
96 {
97 }
98 
99 
100 status_t
Open(void ** _cookie,int mode)101 Node::Open(void **_cookie, int mode)
102 {
103 	TRACE(("%p::Open()\n", this));
104 	return Acquire();
105 }
106 
107 
108 status_t
Close(void * cookie)109 Node::Close(void *cookie)
110 {
111 	TRACE(("%p::Close()\n", this));
112 	return Release();
113 }
114 
115 
116 status_t
ReadLink(char * buffer,size_t bufferSize)117 Node::ReadLink(char* buffer, size_t bufferSize)
118 {
119 	return B_BAD_VALUE;
120 }
121 
122 
123 status_t
GetName(char * nameBuffer,size_t bufferSize) const124 Node::GetName(char *nameBuffer, size_t bufferSize) const
125 {
126 	return B_ERROR;
127 }
128 
129 
130 status_t
GetFileMap(struct file_map_run * runs,int32 * count)131 Node::GetFileMap(struct file_map_run *runs, int32 *count)
132 {
133 	return B_ERROR;
134 }
135 
136 
137 int32
Type() const138 Node::Type() const
139 {
140 	return 0;
141 }
142 
143 
144 off_t
Size() const145 Node::Size() const
146 {
147 	return 0LL;
148 }
149 
150 
151 ino_t
Inode() const152 Node::Inode() const
153 {
154 	return 0;
155 }
156 
157 
158 void
Stat(struct stat & stat)159 Node::Stat(struct stat& stat)
160 {
161 	stat.st_mode = Type();
162 	stat.st_size = Size();
163 	stat.st_ino = Inode();
164 }
165 
166 
167 status_t
Acquire()168 Node::Acquire()
169 {
170 	fRefCount++;
171 	TRACE(("%p::Acquire(), fRefCount = %" B_PRId32 "\n", this, fRefCount));
172 	return B_OK;
173 }
174 
175 
176 status_t
Release()177 Node::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 
ConsoleNode()193 ConsoleNode::ConsoleNode()
194 	: Node()
195 {
196 }
197 
198 
199 ssize_t
Read(void * buffer,size_t bufferSize)200 ConsoleNode::Read(void *buffer, size_t bufferSize)
201 {
202 	return ReadAt(NULL, -1, buffer, bufferSize);
203 }
204 
205 
206 ssize_t
Write(const void * buffer,size_t bufferSize)207 ConsoleNode::Write(const void *buffer, size_t bufferSize)
208 {
209 	return WriteAt(NULL, -1, buffer, bufferSize);
210 }
211 
212 
213 //	#pragma mark -
214 
215 
Directory()216 Directory::Directory()
217 	: Node()
218 {
219 }
220 
221 
222 ssize_t
ReadAt(void * cookie,off_t pos,void * buffer,size_t bufferSize)223 Directory::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
224 {
225 	return B_ERROR;
226 }
227 
228 
229 ssize_t
WriteAt(void * cookie,off_t pos,const void * buffer,size_t bufferSize)230 Directory::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
231 {
232 	return B_ERROR;
233 }
234 
235 
236 int32
Type() const237 Directory::Type() const
238 {
239 	return S_IFDIR;
240 }
241 
242 
243 Node*
Lookup(const char * name,bool traverseLinks)244 Directory::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 
287 status_t
CreateFile(const char * name,mode_t permissions,Node ** _node)288 Directory::CreateFile(const char *name, mode_t permissions, Node **_node)
289 {
290 	return EROFS;
291 }
292 
293 
294 //	#pragma mark -
295 
296 
MemoryDisk(const uint8 * data,size_t size,const char * name)297 MemoryDisk::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 
306 ssize_t
ReadAt(void * cookie,off_t pos,void * buffer,size_t bufferSize)307 MemoryDisk::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 
322 ssize_t
WriteAt(void * cookie,off_t pos,const void * buffer,size_t bufferSize)323 MemoryDisk::WriteAt(void* cookie, off_t pos, const void* buffer,
324 	size_t bufferSize)
325 {
326 	return B_NOT_ALLOWED;
327 }
328 
329 
330 off_t
Size() const331 MemoryDisk::Size() const
332 {
333 	return fSize;
334 }
335 
336 
337 status_t
GetName(char * nameBuffer,size_t bufferSize) const338 MemoryDisk::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 
Descriptor(Node * node,void * cookie)351 Descriptor::Descriptor(Node *node, void *cookie)
352 	:
353 	fNode(node),
354 	fCookie(cookie),
355 	fOffset(0),
356 	fRefCount(1)
357 {
358 }
359 
360 
~Descriptor()361 Descriptor::~Descriptor()
362 {
363 }
364 
365 
366 ssize_t
Read(void * buffer,size_t bufferSize)367 Descriptor::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 
377 ssize_t
ReadAt(off_t pos,void * buffer,size_t bufferSize)378 Descriptor::ReadAt(off_t pos, void *buffer, size_t bufferSize)
379 {
380 	return fNode->ReadAt(fCookie, pos, buffer, bufferSize);
381 }
382 
383 
384 ssize_t
Write(const void * buffer,size_t bufferSize)385 Descriptor::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 
395 ssize_t
WriteAt(off_t pos,const void * buffer,size_t bufferSize)396 Descriptor::WriteAt(off_t pos, const void *buffer, size_t bufferSize)
397 {
398 	return fNode->WriteAt(fCookie, pos, buffer, bufferSize);
399 }
400 
401 
402 void
Stat(struct stat & stat)403 Descriptor::Stat(struct stat &stat)
404 {
405 	fNode->Stat(stat);
406 }
407 
408 
409 status_t
Seek(off_t position,int mode)410 Descriptor::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 
440 status_t
Acquire()441 Descriptor::Acquire()
442 {
443 	fRefCount++;
444 	return B_OK;
445 }
446 
447 
448 status_t
Release()449 Descriptor::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 
BootVolume()464 BootVolume::BootVolume()
465 	:
466 	fRootDirectory(NULL),
467 	fSystemDirectory(NULL),
468 	fPackageVolumeInfo(NULL),
469 	fPackageVolumeState(NULL)
470 {
471 }
472 
473 
~BootVolume()474 BootVolume::~BootVolume()
475 {
476 	Unset();
477 }
478 
479 
480 status_t
SetTo(Directory * rootDirectory,PackageVolumeInfo * packageVolumeInfo,PackageVolumeState * packageVolumeState)481 BootVolume::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 
496 void
Unset()497 BootVolume::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 
517 status_t
_SetTo(Directory * rootDirectory,PackageVolumeInfo * packageVolumeInfo,PackageVolumeState * packageVolumeState)518 BootVolume::_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 
583 int
_OpenSystemPackage()584 BootVolume::_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 
606 status_t
vfs_init(stage2_args * args)607 vfs_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 
617 status_t
register_boot_file_system(BootVolume & bootVolume)618 register_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 */
658 status_t
get_boot_file_system(stage2_args * args,BootVolume & _bootVolume)659 get_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 
710 status_t
mount_file_systems(stage2_args * args)711 mount_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 */
789 static status_t
get_node_for_path(Directory * directory,char * path,Node ** _node)790 get_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  */
835 static status_t
get_node_for_path(Directory * directory,const char * path,Node ** _node)836 get_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 
849 static Descriptor *
get_descriptor(int fd)850 get_descriptor(int fd)
851 {
852 	if (fd < 0 || fd >= MAX_VFS_DESCRIPTORS)
853 		return NULL;
854 
855 	return sDescriptors[fd];
856 }
857 
858 
859 static void
free_descriptor(int fd)860 free_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 
874 int
open_node(Node * node,int mode)875 open_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 
911 int
dup(int fd)912 dup(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 
923 off_t
lseek(int fd,off_t offset,int whence)924 lseek(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 
938 int
ftruncate(int fd,off_t newSize)939 ftruncate(int fd, off_t newSize)
940 {
941 	dprintf("ftruncate() not implemented!\n");
942 	RETURN_AND_SET_ERRNO(B_FILE_ERROR);
943 }
944 
945 
946 ssize_t
read_pos(int fd,off_t offset,void * buffer,size_t bufferSize)947 read_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 
957 ssize_t
pread(int fd,void * buffer,size_t bufferSize,off_t offset)958 pread(int fd, void* buffer, size_t bufferSize, off_t offset)
959 {
960 	return read_pos(fd, offset, buffer, bufferSize);
961 }
962 
963 
964 ssize_t
read(int fd,void * buffer,size_t bufferSize)965 read(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 
975 ssize_t
write_pos(int fd,off_t offset,const void * buffer,size_t bufferSize)976 write_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 
986 ssize_t
pwrite(int fd,const void * buffer,size_t bufferSize,off_t offset)987 pwrite(int fd, const void* buffer, size_t bufferSize, off_t offset)
988 {
989 	return write_pos(fd, offset, buffer, bufferSize);
990 }
991 
992 
993 ssize_t
write(int fd,const void * buffer,size_t bufferSize)994 write(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 
1004 ssize_t
writev(int fd,const struct iovec * vecs,int count)1005 writev(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 
1027 int
open(const char * name,int mode,...)1028 open(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 
1044 int
open_from(Directory * directory,const char * name,int mode,mode_t permissions)1045 open_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 
1129 Node *
get_node_from(int fd)1130 get_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 
1140 status_t
get_stat(Directory * directory,const char * path,struct stat & st)1141 get_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 
1154 Directory*
directory_from(DIR * dir)1155 directory_from(DIR* dir)
1156 {
1157 	return dir != NULL ? dir->directory : NULL;
1158 }
1159 
1160 
1161 int
close(int fd)1162 close(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
1177 int
1178 #if defined(fstat) && !defined(main)
_fstat(int fd,struct stat * stat,size_t)1179 _fstat(int fd, struct stat *stat, size_t /*statSize*/)
1180 #else
1181 fstat(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 
1196 DIR*
open_directory(Directory * baseDirectory,const char * path)1197 open_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 
1232 DIR*
opendir(const char * dirName)1233 opendir(const char* dirName)
1234 {
1235 	return open_directory(gRoot, dirName);
1236 }
1237 
1238 
1239 int
closedir(DIR * dir)1240 closedir(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 
1252 struct dirent*
readdir(DIR * dir)1253 readdir(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 
1293 void
rewinddir(DIR * dir)1294 rewinddir(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