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