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