xref: /haiku/src/tools/fs_shell/vfs.cpp (revision d9cebac2b77547b7064f22497514eecd2d047160)
1 /*
2  * Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6  * Distributed under the terms of the NewOS License.
7  */
8 
9 /* Virtual File System and File System Interface Layer */
10 
11 #include "vfs.h"
12 
13 #include <stdlib.h>
14 
15 #include "fd.h"
16 #include "fssh_atomic.h"
17 #include "fssh_defs.h"
18 #include "fssh_dirent.h"
19 #include "fssh_errno.h"
20 #include "fssh_fcntl.h"
21 #include "fssh_fs_info.h"
22 #include "fssh_fs_volume.h"
23 #include "fssh_kernel_export.h"
24 #include "fssh_module.h"
25 #include "fssh_stat.h"
26 #include "fssh_stdio.h"
27 #include "fssh_string.h"
28 #include "fssh_unistd.h"
29 #include "hash.h"
30 #include "KPath.h"
31 #include "posix_compatibility.h"
32 #include "syscalls.h"
33 
34 
35 //#define TRACE_VFS
36 #ifdef TRACE_VFS
37 #	define TRACE(x) fssh_dprintf x
38 #	define FUNCTION(x) fssh_dprintf x
39 #else
40 #	define TRACE(x) ;
41 #	define FUNCTION(x) ;
42 #endif
43 
44 #define ADD_DEBUGGER_COMMANDS
45 
46 #define ASSERT_LOCKED_MUTEX(x)
47 #define ASSERT(x)
48 
49 namespace FSShell {
50 
51 
52 const static uint32_t kMaxUnusedVnodes = 16;
53 	// This is the maximum number of unused vnodes that the system
54 	// will keep around (weak limit, if there is enough memory left,
55 	// they won't get flushed even when hitting that limit).
56 	// It may be chosen with respect to the available memory or enhanced
57 	// by some timestamp/frequency heurism.
58 
59 struct vnode {
60 	struct vnode	*next;
61 	vm_cache_ref	*cache;
62 	fssh_mount_id	device;
63 	list_link		mount_link;
64 	list_link		unused_link;
65 	fssh_vnode_id	id;
66 	fssh_fs_vnode		private_node;
67 	struct fs_mount	*mount;
68 	struct vnode	*covered_by;
69 	int32_t			ref_count;
70 	uint8_t			remove : 1;
71 	uint8_t			busy : 1;
72 	uint8_t			unpublished : 1;
73 	struct file_descriptor *mandatory_locked_by;
74 };
75 
76 struct vnode_hash_key {
77 	fssh_mount_id	device;
78 	fssh_vnode_id	vnode;
79 };
80 
81 #define FS_CALL(vnode, op) (vnode->mount->fs->op)
82 #define FS_MOUNT_CALL(mount, op) (mount->fs->op)
83 
84 /**	\brief Structure to manage a mounted file system
85 
86 	Note: The root_vnode and covers_vnode fields (what others?) are
87 	initialized in fs_mount() and not changed afterwards. That is as soon
88 	as the mount is mounted and it is made sure it won't be unmounted
89 	(e.g. by holding a reference to a vnode of that mount) (read) access
90 	to those fields is always safe, even without additional locking. Morever
91 	while mounted the mount holds a reference to the covers_vnode, and thus
92 	making the access path vnode->mount->covers_vnode->mount->... safe if a
93 	reference to vnode is held (note that for the root mount covers_vnode
94 	is NULL, though).
95  */
96 struct fs_mount {
97 	struct fs_mount	*next;
98 	fssh_file_system_module_info *fs;
99 	fssh_mount_id		id;
100 	void			*cookie;
101 	char			*device_name;
102 	char			*fs_name;
103 	recursive_lock	rlock;	// guards the vnodes list
104 	struct vnode	*root_vnode;
105 	struct vnode	*covers_vnode;
106 	struct list		vnodes;
107 	bool			unmounting;
108 	bool			owns_file_device;
109 };
110 
111 static mutex sFileSystemsMutex;
112 
113 /**	\brief Guards sMountsTable.
114  *
115  *	The holder is allowed to read/write access the sMountsTable.
116  *	Manipulation of the fs_mount structures themselves
117  *	(and their destruction) requires different locks though.
118  */
119 static mutex sMountMutex;
120 
121 /**	\brief Guards mount/unmount operations.
122  *
123  *	The fs_mount() and fs_unmount() hold the lock during their whole operation.
124  *	That is locking the lock ensures that no FS is mounted/unmounted. In
125  *	particular this means that
126  *	- sMountsTable will not be modified,
127  *	- the fields immutable after initialization of the fs_mount structures in
128  *	  sMountsTable will not be modified,
129  *	- vnode::covered_by of any vnode in sVnodeTable will not be modified.
130  *
131  *	The thread trying to lock the lock must not hold sVnodeMutex or
132  *	sMountMutex.
133  */
134 static recursive_lock sMountOpLock;
135 
136 /**	\brief Guards the vnode::covered_by field of any vnode
137  *
138  *	The holder is allowed to read access the vnode::covered_by field of any
139  *	vnode. Additionally holding sMountOpLock allows for write access.
140  *
141  *	The thread trying to lock the must not hold sVnodeMutex.
142  */
143 static mutex sVnodeCoveredByMutex;
144 
145 /**	\brief Guards sVnodeTable.
146  *
147  *	The holder is allowed to read/write access sVnodeTable and to
148  *	to any unbusy vnode in that table, save
149  *	to the immutable fields (device, id, private_node, mount) to which
150  *	only read-only access is allowed, and to the field covered_by, which is
151  *	guarded by sMountOpLock and sVnodeCoveredByMutex.
152  *
153  *	The thread trying to lock the mutex must not hold sMountMutex.
154  *	You must not have this mutex held when calling create_sem(), as this
155  *	might call vfs_free_unused_vnodes().
156  */
157 static mutex sVnodeMutex;
158 
159 #define VNODE_HASH_TABLE_SIZE 1024
160 static hash_table *sVnodeTable;
161 static list sUnusedVnodeList;
162 static uint32_t sUnusedVnodes = 0;
163 static struct vnode *sRoot;
164 
165 #define MOUNTS_HASH_TABLE_SIZE 16
166 static hash_table *sMountsTable;
167 static fssh_mount_id sNextMountID = 1;
168 
169 fssh_mode_t __fssh_gUmask = 022;
170 
171 /* function declarations */
172 
173 // file descriptor operation prototypes
174 static fssh_status_t file_read(struct file_descriptor *, fssh_off_t pos,
175 			void *buffer, fssh_size_t *);
176 static fssh_status_t file_write(struct file_descriptor *, fssh_off_t pos,
177 			const void *buffer, fssh_size_t *);
178 static fssh_off_t file_seek(struct file_descriptor *, fssh_off_t pos,
179 			int seek_type);
180 static void file_free_fd(struct file_descriptor *);
181 static fssh_status_t file_close(struct file_descriptor *);
182 static fssh_status_t dir_read(struct file_descriptor *,
183 			struct fssh_dirent *buffer, fssh_size_t bufferSize,
184 			uint32_t *_count);
185 static fssh_status_t dir_read(struct vnode *vnode, fssh_fs_cookie cookie,
186 			struct fssh_dirent *buffer, fssh_size_t bufferSize,
187 			uint32_t *_count);
188 static fssh_status_t dir_rewind(struct file_descriptor *);
189 static void dir_free_fd(struct file_descriptor *);
190 static fssh_status_t dir_close(struct file_descriptor *);
191 static fssh_status_t attr_dir_read(struct file_descriptor *,
192 			struct fssh_dirent *buffer, fssh_size_t bufferSize,
193 			uint32_t *_count);
194 static fssh_status_t attr_dir_rewind(struct file_descriptor *);
195 static void attr_dir_free_fd(struct file_descriptor *);
196 static fssh_status_t attr_dir_close(struct file_descriptor *);
197 static fssh_status_t attr_read(struct file_descriptor *, fssh_off_t pos,
198 			void *buffer, fssh_size_t *);
199 static fssh_status_t attr_write(struct file_descriptor *, fssh_off_t pos,
200 			const void *buffer, fssh_size_t *);
201 static fssh_off_t attr_seek(struct file_descriptor *, fssh_off_t pos,
202 			int seek_type);
203 static void attr_free_fd(struct file_descriptor *);
204 static fssh_status_t attr_close(struct file_descriptor *);
205 static fssh_status_t attr_read_stat(struct file_descriptor *,
206 			struct fssh_stat *);
207 static fssh_status_t attr_write_stat(struct file_descriptor *,
208 			const struct fssh_stat *, int statMask);
209 static fssh_status_t index_dir_read(struct file_descriptor *,
210 			struct fssh_dirent *buffer, fssh_size_t bufferSize,
211 			uint32_t *_count);
212 static fssh_status_t index_dir_rewind(struct file_descriptor *);
213 static void index_dir_free_fd(struct file_descriptor *);
214 static fssh_status_t index_dir_close(struct file_descriptor *);
215 static fssh_status_t query_read(struct file_descriptor *,
216 			struct fssh_dirent *buffer, fssh_size_t bufferSize,
217 			uint32_t *_count);
218 static fssh_status_t query_rewind(struct file_descriptor *);
219 static void query_free_fd(struct file_descriptor *);
220 static fssh_status_t query_close(struct file_descriptor *);
221 
222 static fssh_status_t common_ioctl(struct file_descriptor *, uint32_t, void *buf,
223 			fssh_size_t len);
224 static fssh_status_t common_read_stat(struct file_descriptor *,
225 			struct fssh_stat *);
226 static fssh_status_t common_write_stat(struct file_descriptor *,
227 			const struct fssh_stat *, int statMask);
228 
229 static fssh_status_t vnode_path_to_vnode(struct vnode *vnode, char *path,
230 			bool traverseLeafLink, int count, struct vnode **_vnode,
231 			fssh_vnode_id *_parentID, int *_type);
232 static fssh_status_t dir_vnode_to_path(struct vnode *vnode, char *buffer,
233 			fssh_size_t bufferSize);
234 static fssh_status_t fd_and_path_to_vnode(int fd, char *path,
235 			bool traverseLeafLink, struct vnode **_vnode,
236 			fssh_vnode_id *_parentID, bool kernel);
237 static void inc_vnode_ref_count(struct vnode *vnode);
238 static fssh_status_t dec_vnode_ref_count(struct vnode *vnode, bool reenter);
239 static inline void put_vnode(struct vnode *vnode);
240 
241 static struct fd_ops sFileOps = {
242 	file_read,
243 	file_write,
244 	file_seek,
245 	common_ioctl,
246 	NULL,
247 	NULL,
248 	NULL,		// read_dir()
249 	NULL,		// rewind_dir()
250 	common_read_stat,
251 	common_write_stat,
252 	file_close,
253 	file_free_fd
254 };
255 
256 static struct fd_ops sDirectoryOps = {
257 	NULL,		// read()
258 	NULL,		// write()
259 	NULL,		// seek()
260 	common_ioctl,
261 	NULL,		// select()
262 	NULL,		// deselect()
263 	dir_read,
264 	dir_rewind,
265 	common_read_stat,
266 	common_write_stat,
267 	dir_close,
268 	dir_free_fd
269 };
270 
271 static struct fd_ops sAttributeDirectoryOps = {
272 	NULL,		// read()
273 	NULL,		// write()
274 	NULL,		// seek()
275 	common_ioctl,
276 	NULL,		// select()
277 	NULL,		// deselect()
278 	attr_dir_read,
279 	attr_dir_rewind,
280 	common_read_stat,
281 	common_write_stat,
282 	attr_dir_close,
283 	attr_dir_free_fd
284 };
285 
286 static struct fd_ops sAttributeOps = {
287 	attr_read,
288 	attr_write,
289 	attr_seek,
290 	common_ioctl,
291 	NULL,		// select()
292 	NULL,		// deselect()
293 	NULL,		// read_dir()
294 	NULL,		// rewind_dir()
295 	attr_read_stat,
296 	attr_write_stat,
297 	attr_close,
298 	attr_free_fd
299 };
300 
301 static struct fd_ops sIndexDirectoryOps = {
302 	NULL,		// read()
303 	NULL,		// write()
304 	NULL,		// seek()
305 	NULL,		// ioctl()
306 	NULL,		// select()
307 	NULL,		// deselect()
308 	index_dir_read,
309 	index_dir_rewind,
310 	NULL,		// read_stat()
311 	NULL,		// write_stat()
312 	index_dir_close,
313 	index_dir_free_fd
314 };
315 
316 #if 0
317 static struct fd_ops sIndexOps = {
318 	NULL,		// read()
319 	NULL,		// write()
320 	NULL,		// seek()
321 	NULL,		// ioctl()
322 	NULL,		// select()
323 	NULL,		// deselect()
324 	NULL,		// dir_read()
325 	NULL,		// dir_rewind()
326 	index_read_stat,	// read_stat()
327 	NULL,		// write_stat()
328 	NULL,		// dir_close()
329 	NULL		// free_fd()
330 };
331 #endif
332 
333 static struct fd_ops sQueryOps = {
334 	NULL,		// read()
335 	NULL,		// write()
336 	NULL,		// seek()
337 	NULL,		// ioctl()
338 	NULL,		// select()
339 	NULL,		// deselect()
340 	query_read,
341 	query_rewind,
342 	NULL,		// read_stat()
343 	NULL,		// write_stat()
344 	query_close,
345 	query_free_fd
346 };
347 
348 
349 // VNodePutter
350 class VNodePutter {
351 public:
352 	VNodePutter(struct vnode *vnode = NULL) : fVNode(vnode) {}
353 
354 	~VNodePutter()
355 	{
356 		Put();
357 	}
358 
359 	void SetTo(struct vnode *vnode)
360 	{
361 		Put();
362 		fVNode = vnode;
363 	}
364 
365 	void Put()
366 	{
367 		if (fVNode) {
368 			put_vnode(fVNode);
369 			fVNode = NULL;
370 		}
371 	}
372 
373 	struct vnode *Detach()
374 	{
375 		struct vnode *vnode = fVNode;
376 		fVNode = NULL;
377 		return vnode;
378 	}
379 
380 private:
381 	struct vnode *fVNode;
382 };
383 
384 
385 static int
386 mount_compare(void *_m, const void *_key)
387 {
388 	struct fs_mount *mount = (fs_mount *)_m;
389 	const fssh_mount_id *id = (fssh_mount_id *)_key;
390 
391 	if (mount->id == *id)
392 		return 0;
393 
394 	return -1;
395 }
396 
397 
398 static uint32_t
399 mount_hash(void *_m, const void *_key, uint32_t range)
400 {
401 	struct fs_mount *mount = (fs_mount *)_m;
402 	const fssh_mount_id *id = (fssh_mount_id *)_key;
403 
404 	if (mount)
405 		return mount->id % range;
406 
407 	return (uint32_t)*id % range;
408 }
409 
410 
411 /** Finds the mounted device (the fs_mount structure) with the given ID.
412  *	Note, you must hold the gMountMutex lock when you call this function.
413  */
414 
415 static struct fs_mount *
416 find_mount(fssh_mount_id id)
417 {
418 	ASSERT_LOCKED_MUTEX(&sMountMutex);
419 
420 	return (fs_mount *)hash_lookup(sMountsTable, (void *)&id);
421 }
422 
423 
424 static fssh_status_t
425 get_mount(fssh_mount_id id, struct fs_mount **_mount)
426 {
427 	struct fs_mount *mount;
428 	fssh_status_t status;
429 
430 	mutex_lock(&sMountMutex);
431 
432 	mount = find_mount(id);
433 	if (mount) {
434 		// ToDo: the volume is locked (against removal) by locking
435 		//	its root node - investigate if that's a good idea
436 		if (mount->root_vnode)
437 			inc_vnode_ref_count(mount->root_vnode);
438 		else {
439 			// might have been called during a mount operation in which
440 			// case the root node may still be NULL
441 			mount = NULL;
442 		}
443 	} else
444 		status = FSSH_B_BAD_VALUE;
445 
446 	mutex_unlock(&sMountMutex);
447 
448 	if (mount == NULL)
449 		return FSSH_B_BUSY;
450 
451 	*_mount = mount;
452 	return FSSH_B_OK;
453 }
454 
455 
456 static void
457 put_mount(struct fs_mount *mount)
458 {
459 	if (mount)
460 		put_vnode(mount->root_vnode);
461 }
462 
463 
464 static fssh_status_t
465 put_file_system(fssh_file_system_module_info *fs)
466 {
467 	return fssh_put_module(fs->info.name);
468 }
469 
470 
471 /**	Tries to open the specified file system module.
472  *	Accepts a file system name of the form "bfs" or "file_systems/bfs/v1".
473  *	Returns a pointer to file system module interface, or NULL if it
474  *	could not open the module.
475  */
476 
477 static fssh_file_system_module_info *
478 get_file_system(const char *fsName)
479 {
480 	char name[FSSH_B_FILE_NAME_LENGTH];
481 	if (fssh_strncmp(fsName, "file_systems/", fssh_strlen("file_systems/"))) {
482 		// construct module name if we didn't get one
483 		// (we currently support only one API)
484 		fssh_snprintf(name, sizeof(name), "file_systems/%s/v1", fsName);
485 		fsName = NULL;
486 	}
487 
488 	fssh_file_system_module_info *info;
489 	if (fssh_get_module(fsName ? fsName : name, (fssh_module_info **)&info) != FSSH_B_OK)
490 		return NULL;
491 
492 	return info;
493 }
494 
495 
496 /**	Accepts a file system name of the form "bfs" or "file_systems/bfs/v1"
497  *	and returns a compatible fs_info.fsh_name name ("bfs" in both cases).
498  *	The name is allocated for you, and you have to free() it when you're
499  *	done with it.
500  *	Returns NULL if the required memory is no available.
501  */
502 
503 static char *
504 get_file_system_name(const char *fsName)
505 {
506 	const fssh_size_t length = fssh_strlen("file_systems/");
507 
508 	if (fssh_strncmp(fsName, "file_systems/", length)) {
509 		// the name already seems to be the module's file name
510 		return fssh_strdup(fsName);
511 	}
512 
513 	fsName += length;
514 	const char *end = fssh_strchr(fsName, '/');
515 	if (end == NULL) {
516 		// this doesn't seem to be a valid name, but well...
517 		return fssh_strdup(fsName);
518 	}
519 
520 	// cut off the trailing /v1
521 
522 	char *name = (char *)malloc(end + 1 - fsName);
523 	if (name == NULL)
524 		return NULL;
525 
526 	fssh_strlcpy(name, fsName, end + 1 - fsName);
527 	return name;
528 }
529 
530 
531 static int
532 vnode_compare(void *_vnode, const void *_key)
533 {
534 	struct vnode *vnode = (struct vnode *)_vnode;
535 	const struct vnode_hash_key *key = (vnode_hash_key *)_key;
536 
537 	if (vnode->device == key->device && vnode->id == key->vnode)
538 		return 0;
539 
540 	return -1;
541 }
542 
543 
544 static uint32_t
545 vnode_hash(void *_vnode, const void *_key, uint32_t range)
546 {
547 	struct vnode *vnode = (struct vnode *)_vnode;
548 	const struct vnode_hash_key *key = (vnode_hash_key *)_key;
549 
550 #define VHASH(mountid, vnodeid) (((uint32_t)((vnodeid) >> 32) + (uint32_t)(vnodeid)) ^ (uint32_t)(mountid))
551 
552 	if (vnode != NULL)
553 		return VHASH(vnode->device, vnode->id) % range;
554 
555 	return VHASH(key->device, key->vnode) % range;
556 
557 #undef VHASH
558 }
559 
560 
561 static void
562 add_vnode_to_mount_list(struct vnode *vnode, struct fs_mount *mount)
563 {
564 	recursive_lock_lock(&mount->rlock);
565 
566 	list_add_link_to_head(&mount->vnodes, &vnode->mount_link);
567 
568 	recursive_lock_unlock(&mount->rlock);
569 }
570 
571 
572 static void
573 remove_vnode_from_mount_list(struct vnode *vnode, struct fs_mount *mount)
574 {
575 	recursive_lock_lock(&mount->rlock);
576 
577 	list_remove_link(&vnode->mount_link);
578 	vnode->mount_link.next = vnode->mount_link.prev = NULL;
579 
580 	recursive_lock_unlock(&mount->rlock);
581 }
582 
583 
584 static fssh_status_t
585 create_new_vnode(struct vnode **_vnode, fssh_mount_id mountID, fssh_vnode_id vnodeID)
586 {
587 	FUNCTION(("create_new_vnode()\n"));
588 
589 	struct vnode *vnode = (struct vnode *)malloc(sizeof(struct vnode));
590 	if (vnode == NULL)
591 		return FSSH_B_NO_MEMORY;
592 
593 	// initialize basic values
594 	fssh_memset(vnode, 0, sizeof(struct vnode));
595 	vnode->device = mountID;
596 	vnode->id = vnodeID;
597 
598 	// add the vnode to the mount structure
599 	mutex_lock(&sMountMutex);
600 	vnode->mount = find_mount(mountID);
601 	if (!vnode->mount || vnode->mount->unmounting) {
602 		mutex_unlock(&sMountMutex);
603 		free(vnode);
604 		return FSSH_B_ENTRY_NOT_FOUND;
605 	}
606 
607 	hash_insert(sVnodeTable, vnode);
608 	add_vnode_to_mount_list(vnode, vnode->mount);
609 
610 	mutex_unlock(&sMountMutex);
611 
612 	vnode->ref_count = 1;
613 	*_vnode = vnode;
614 
615 	return FSSH_B_OK;
616 }
617 
618 
619 /**	Frees the vnode and all resources it has acquired, and removes
620  *	it from the vnode hash as well as from its mount structure.
621  *	Will also make sure that any cache modifications are written back.
622  */
623 
624 static void
625 free_vnode(struct vnode *vnode, bool reenter)
626 {
627 	ASSERT(vnode->ref_count == 0 && vnode->busy);
628 
629 	// write back any changes in this vnode's cache -- but only
630 	// if the vnode won't be deleted, in which case the changes
631 	// will be discarded
632 
633 	if (!vnode->remove && FS_CALL(vnode, fsync) != NULL)
634 		FS_CALL(vnode, fsync)(vnode->mount->cookie, vnode->private_node);
635 
636 	if (!vnode->unpublished) {
637 		if (vnode->remove)
638 			FS_CALL(vnode, remove_vnode)(vnode->mount->cookie, vnode->private_node, reenter);
639 		else
640 			FS_CALL(vnode, put_vnode)(vnode->mount->cookie, vnode->private_node, reenter);
641 	}
642 
643 	// The file system has removed the resources of the vnode now, so we can
644 	// make it available again (and remove the busy vnode from the hash)
645 	mutex_lock(&sVnodeMutex);
646 	hash_remove(sVnodeTable, vnode);
647 	mutex_unlock(&sVnodeMutex);
648 
649 	remove_vnode_from_mount_list(vnode, vnode->mount);
650 
651 	free(vnode);
652 }
653 
654 
655 /**	\brief Decrements the reference counter of the given vnode and deletes it,
656  *	if the counter dropped to 0.
657  *
658  *	The caller must, of course, own a reference to the vnode to call this
659  *	function.
660  *	The caller must not hold the sVnodeMutex or the sMountMutex.
661  *
662  *	\param vnode the vnode.
663  *	\param reenter \c true, if this function is called (indirectly) from within
664  *		   a file system.
665  *	\return \c FSSH_B_OK, if everything went fine, an error code otherwise.
666  */
667 
668 static fssh_status_t
669 dec_vnode_ref_count(struct vnode *vnode, bool reenter)
670 {
671 	mutex_lock(&sVnodeMutex);
672 
673 	int32_t oldRefCount = fssh_atomic_add(&vnode->ref_count, -1);
674 
675 	TRACE(("dec_vnode_ref_count: vnode %p, ref now %ld\n", vnode, vnode->ref_count));
676 
677 	if (oldRefCount == 1) {
678 		if (vnode->busy)
679 			fssh_panic("dec_vnode_ref_count: called on busy vnode %p\n", vnode);
680 
681 		bool freeNode = false;
682 
683 		// Just insert the vnode into an unused list if we don't need
684 		// to delete it
685 		if (vnode->remove) {
686 			vnode->busy = true;
687 			freeNode = true;
688 		} else {
689 			list_add_item(&sUnusedVnodeList, vnode);
690 			if (++sUnusedVnodes > kMaxUnusedVnodes) {
691 				// there are too many unused vnodes so we free the oldest one
692 				// ToDo: evaluate this mechanism
693 				vnode = (struct vnode *)list_remove_head_item(&sUnusedVnodeList);
694 				vnode->busy = true;
695 				freeNode = true;
696 				sUnusedVnodes--;
697 			}
698 		}
699 
700 		mutex_unlock(&sVnodeMutex);
701 
702 		if (freeNode)
703 			free_vnode(vnode, reenter);
704 	} else
705 		mutex_unlock(&sVnodeMutex);
706 
707 	return FSSH_B_OK;
708 }
709 
710 
711 /**	\brief Increments the reference counter of the given vnode.
712  *
713  *	The caller must either already have a reference to the vnode or hold
714  *	the sVnodeMutex.
715  *
716  *	\param vnode the vnode.
717  */
718 
719 static void
720 inc_vnode_ref_count(struct vnode *vnode)
721 {
722 	fssh_atomic_add(&vnode->ref_count, 1);
723 	TRACE(("inc_vnode_ref_count: vnode %p, ref now %ld\n", vnode, vnode->ref_count));
724 }
725 
726 
727 /**	\brief Looks up a vnode by mount and node ID in the sVnodeTable.
728  *
729  *	The caller must hold the sVnodeMutex.
730  *
731  *	\param mountID the mount ID.
732  *	\param vnodeID the node ID.
733  *
734  *	\return The vnode structure, if it was found in the hash table, \c NULL
735  *			otherwise.
736  */
737 
738 static struct vnode *
739 lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID)
740 {
741 	struct vnode_hash_key key;
742 
743 	key.device = mountID;
744 	key.vnode = vnodeID;
745 
746 	return (vnode *)hash_lookup(sVnodeTable, &key);
747 }
748 
749 
750 /**	\brief Retrieves a vnode for a given mount ID, node ID pair.
751  *
752  *	If the node is not yet in memory, it will be loaded.
753  *
754  *	The caller must not hold the sVnodeMutex or the sMountMutex.
755  *
756  *	\param mountID the mount ID.
757  *	\param vnodeID the node ID.
758  *	\param _vnode Pointer to a vnode* variable into which the pointer to the
759  *		   retrieved vnode structure shall be written.
760  *	\param reenter \c true, if this function is called (indirectly) from within
761  *		   a file system.
762  *	\return \c FSSH_B_OK, if everything when fine, an error code otherwise.
763  */
764 
765 static fssh_status_t
766 get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, struct vnode **_vnode, int reenter)
767 {
768 	FUNCTION(("get_vnode: mountid %ld vnid 0x%Lx %p\n", mountID, vnodeID, _vnode));
769 
770 	mutex_lock(&sVnodeMutex);
771 
772 	int32_t tries = 300;
773 		// try for 3 secs
774 restart:
775 	struct vnode *vnode = lookup_vnode(mountID, vnodeID);
776 	if (vnode && vnode->busy) {
777 		mutex_unlock(&sVnodeMutex);
778 		if (--tries < 0) {
779 			// vnode doesn't seem to become unbusy
780 			fssh_panic("vnode %d:%lld is not becoming unbusy!\n", (int)mountID, vnodeID);
781 			return FSSH_B_BUSY;
782 		}
783 		fssh_snooze(10000); // 10 ms
784 		mutex_lock(&sVnodeMutex);
785 		goto restart;
786 	}
787 
788 	TRACE(("get_vnode: tried to lookup vnode, got %p\n", vnode));
789 
790 	fssh_status_t status;
791 
792 	if (vnode) {
793 		if (vnode->ref_count == 0) {
794 			// this vnode has been unused before
795 			list_remove_item(&sUnusedVnodeList, vnode);
796 			sUnusedVnodes--;
797 		}
798 		inc_vnode_ref_count(vnode);
799 	} else {
800 		// we need to create a new vnode and read it in
801 		status = create_new_vnode(&vnode, mountID, vnodeID);
802 		if (status < FSSH_B_OK)
803 			goto err;
804 
805 		vnode->busy = true;
806 		mutex_unlock(&sVnodeMutex);
807 
808 		status = FS_CALL(vnode, get_vnode)(vnode->mount->cookie, vnodeID, &vnode->private_node, reenter);
809 		if (status == FSSH_B_OK && vnode->private_node == NULL)
810 			status = FSSH_B_BAD_VALUE;
811 
812 		mutex_lock(&sVnodeMutex);
813 
814 		if (status < FSSH_B_OK)
815 			goto err1;
816 
817 		vnode->busy = false;
818 	}
819 
820 	mutex_unlock(&sVnodeMutex);
821 
822 	TRACE(("get_vnode: returning %p\n", vnode));
823 
824 	*_vnode = vnode;
825 	return FSSH_B_OK;
826 
827 err1:
828 	hash_remove(sVnodeTable, vnode);
829 	remove_vnode_from_mount_list(vnode, vnode->mount);
830 err:
831 	mutex_unlock(&sVnodeMutex);
832 	if (vnode)
833 		free(vnode);
834 
835 	return status;
836 }
837 
838 
839 /**	\brief Decrements the reference counter of the given vnode and deletes it,
840  *	if the counter dropped to 0.
841  *
842  *	The caller must, of course, own a reference to the vnode to call this
843  *	function.
844  *	The caller must not hold the sVnodeMutex or the sMountMutex.
845  *
846  *	\param vnode the vnode.
847  */
848 
849 static inline void
850 put_vnode(struct vnode *vnode)
851 {
852 	dec_vnode_ref_count(vnode, false);
853 }
854 
855 
856 /**	Disconnects all file descriptors that are associated with the
857  *	\a vnodeToDisconnect, or if this is NULL, all vnodes of the specified
858  *	\a mount object.
859  *
860  *	Note, after you've called this function, there might still be ongoing
861  *	accesses - they won't be interrupted if they already happened before.
862  *	However, any subsequent access will fail.
863  *
864  *	This is not a cheap function and should be used with care and rarely.
865  *	TODO: there is currently no means to stop a blocking read/write!
866  */
867 
868 void
869 disconnect_mount_or_vnode_fds(struct fs_mount *mount,
870 	struct vnode *vnodeToDisconnect)
871 {
872 }
873 
874 
875 /**	\brief Resolves a mount point vnode to the volume root vnode it is covered
876  *		   by.
877  *
878  *	Given an arbitrary vnode, the function checks, whether the node is covered
879  *	by the root of a volume. If it is the function obtains a reference to the
880  *	volume root node and returns it.
881  *
882  *	\param vnode The vnode in question.
883  *	\return The volume root vnode the vnode cover is covered by, if it is
884  *			indeed a mount point, or \c NULL otherwise.
885  */
886 
887 static struct vnode *
888 resolve_mount_point_to_volume_root(struct vnode *vnode)
889 {
890 	if (!vnode)
891 		return NULL;
892 
893 	struct vnode *volumeRoot = NULL;
894 
895 	mutex_lock(&sVnodeCoveredByMutex);
896 	if (vnode->covered_by) {
897 		volumeRoot = vnode->covered_by;
898 		inc_vnode_ref_count(volumeRoot);
899 	}
900 	mutex_unlock(&sVnodeCoveredByMutex);
901 
902 	return volumeRoot;
903 }
904 
905 
906 /**	\brief Resolves a mount point vnode to the volume root vnode it is covered
907  *		   by.
908  *
909  *	Given an arbitrary vnode (identified by mount and node ID), the function
910  *	checks, whether the node is covered by the root of a volume. If it is the
911  *	function returns the mount and node ID of the volume root node. Otherwise
912  *	it simply returns the supplied mount and node ID.
913  *
914  *	In case of error (e.g. the supplied node could not be found) the variables
915  *	for storing the resolved mount and node ID remain untouched and an error
916  *	code is returned.
917  *
918  *	\param mountID The mount ID of the vnode in question.
919  *	\param nodeID The node ID of the vnode in question.
920  *	\param resolvedMountID Pointer to storage for the resolved mount ID.
921  *	\param resolvedNodeID Pointer to storage for the resolved node ID.
922  *	\return
923  *	- \c FSSH_B_OK, if everything went fine,
924  *	- another error code, if something went wrong.
925  */
926 
927 fssh_status_t
928 resolve_mount_point_to_volume_root(fssh_mount_id mountID, fssh_vnode_id nodeID,
929 	fssh_mount_id *resolvedMountID, fssh_vnode_id *resolvedNodeID)
930 {
931 	// get the node
932 	struct vnode *node;
933 	fssh_status_t error = get_vnode(mountID, nodeID, &node, false);
934 	if (error != FSSH_B_OK)
935 		return error;
936 
937 	// resolve the node
938 	struct vnode *resolvedNode = resolve_mount_point_to_volume_root(node);
939 	if (resolvedNode) {
940 		put_vnode(node);
941 		node = resolvedNode;
942 	}
943 
944 	// set the return values
945 	*resolvedMountID = node->device;
946 	*resolvedNodeID = node->id;
947 
948 	put_vnode(node);
949 
950 	return FSSH_B_OK;
951 }
952 
953 
954 /**	\brief Resolves a volume root vnode to the underlying mount point vnode.
955  *
956  *	Given an arbitrary vnode, the function checks, whether the node is the
957  *	root of a volume. If it is (and if it is not "/"), the function obtains
958  *	a reference to the underlying mount point node and returns it.
959  *
960  *	\param vnode The vnode in question (caller must have a reference).
961  *	\return The mount point vnode the vnode covers, if it is indeed a volume
962  *			root and not "/", or \c NULL otherwise.
963  */
964 
965 static struct vnode *
966 resolve_volume_root_to_mount_point(struct vnode *vnode)
967 {
968 	if (!vnode)
969 		return NULL;
970 
971 	struct vnode *mountPoint = NULL;
972 
973 	struct fs_mount *mount = vnode->mount;
974 	if (vnode == mount->root_vnode && mount->covers_vnode) {
975 		mountPoint = mount->covers_vnode;
976 		inc_vnode_ref_count(mountPoint);
977 	}
978 
979 	return mountPoint;
980 }
981 
982 
983 /**	\brief Gets the directory path and leaf name for a given path.
984  *
985  *	The supplied \a path is transformed to refer to the directory part of
986  *	the entry identified by the original path, and into the buffer \a filename
987  *	the leaf name of the original entry is written.
988  *	Neither the returned path nor the leaf name can be expected to be
989  *	canonical.
990  *
991  *	\param path The path to be analyzed. Must be able to store at least one
992  *		   additional character.
993  *	\param filename The buffer into which the leaf name will be written.
994  *		   Must be of size FSSH_B_FILE_NAME_LENGTH at least.
995  *	\return \c FSSH_B_OK, if everything went fine, \c FSSH_B_NAME_TOO_LONG, if the leaf
996  *		   name is longer than \c FSSH_B_FILE_NAME_LENGTH.
997  */
998 
999 static fssh_status_t
1000 get_dir_path_and_leaf(char *path, char *filename)
1001 {
1002 	char *p = fssh_strrchr(path, '/');
1003 		// '/' are not allowed in file names!
1004 
1005 	FUNCTION(("get_dir_path_and_leaf(path = %s)\n", path));
1006 
1007 	if (!p) {
1008 		// this path is single segment with no '/' in it
1009 		// ex. "foo"
1010 		if (fssh_strlcpy(filename, path, FSSH_B_FILE_NAME_LENGTH) >= FSSH_B_FILE_NAME_LENGTH)
1011 			return FSSH_B_NAME_TOO_LONG;
1012 		fssh_strcpy(path, ".");
1013 	} else {
1014 		p++;
1015 		if (*p == '\0') {
1016 			// special case: the path ends in '/'
1017 			fssh_strcpy(filename, ".");
1018 		} else {
1019 			// normal leaf: replace the leaf portion of the path with a '.'
1020 			if (fssh_strlcpy(filename, p, FSSH_B_FILE_NAME_LENGTH)
1021 				>= FSSH_B_FILE_NAME_LENGTH) {
1022 				return FSSH_B_NAME_TOO_LONG;
1023 			}
1024 		}
1025 		p[0] = '.';
1026 		p[1] = '\0';
1027 	}
1028 	return FSSH_B_OK;
1029 }
1030 
1031 
1032 static fssh_status_t
1033 entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, struct vnode **_vnode)
1034 {
1035 	char clonedName[FSSH_B_FILE_NAME_LENGTH + 1];
1036 	if (fssh_strlcpy(clonedName, name, FSSH_B_FILE_NAME_LENGTH) >= FSSH_B_FILE_NAME_LENGTH)
1037 		return FSSH_B_NAME_TOO_LONG;
1038 
1039 	// get the directory vnode and let vnode_path_to_vnode() do the rest
1040 	struct vnode *directory;
1041 
1042 	fssh_status_t status = get_vnode(mountID, directoryID, &directory, false);
1043 	if (status < 0)
1044 		return status;
1045 
1046 	return vnode_path_to_vnode(directory, clonedName, false, 0, _vnode, NULL, NULL);
1047 }
1048 
1049 
1050 /**	Returns the vnode for the relative path starting at the specified \a vnode.
1051  *	\a path must not be NULL.
1052  *	If it returns successfully, \a path contains the name of the last path
1053  *	component.
1054  *	Note, this reduces the ref_count of the starting \a vnode, no matter if
1055  *	it is successful or not!
1056  */
1057 
1058 static fssh_status_t
1059 vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink,
1060 	int count, struct vnode **_vnode, fssh_vnode_id *_parentID, int *_type)
1061 {
1062 	fssh_status_t status = 0;
1063 	fssh_vnode_id lastParentID = vnode->id;
1064 	int type = 0;
1065 
1066 	FUNCTION(("vnode_path_to_vnode(vnode = %p, path = %s)\n", vnode, path));
1067 
1068 	if (path == NULL) {
1069 		put_vnode(vnode);
1070 		return FSSH_B_BAD_VALUE;
1071 	}
1072 
1073 	while (true) {
1074 		struct vnode *nextVnode;
1075 		fssh_vnode_id vnodeID;
1076 		char *nextPath;
1077 
1078 		TRACE(("vnode_path_to_vnode: top of loop. p = %p, p = '%s'\n", path, path));
1079 
1080 		// done?
1081 		if (path[0] == '\0')
1082 			break;
1083 
1084 		// walk to find the next path component ("path" will point to a single
1085 		// path component), and filter out multiple slashes
1086 		for (nextPath = path + 1; *nextPath != '\0' && *nextPath != '/'; nextPath++);
1087 
1088 		if (*nextPath == '/') {
1089 			*nextPath = '\0';
1090 			do
1091 				nextPath++;
1092 			while (*nextPath == '/');
1093 		}
1094 
1095 		// See if the '..' is at the root of a mount and move to the covered
1096 		// vnode so we pass the '..' path to the underlying filesystem
1097 		if (!fssh_strcmp("..", path)
1098 			&& vnode->mount->root_vnode == vnode
1099 			&& vnode->mount->covers_vnode) {
1100 			nextVnode = vnode->mount->covers_vnode;
1101 			inc_vnode_ref_count(nextVnode);
1102 			put_vnode(vnode);
1103 			vnode = nextVnode;
1104 		}
1105 
1106 		// Check if we have the right to search the current directory vnode.
1107 		// If a file system doesn't have the access() function, we assume that
1108 		// searching a directory is always allowed
1109 		if (FS_CALL(vnode, access))
1110 			status = FS_CALL(vnode, access)(vnode->mount->cookie, vnode->private_node, FSSH_X_OK);
1111 
1112 		// Tell the filesystem to get the vnode of this path component (if we got the
1113 		// permission from the call above)
1114 		if (status >= FSSH_B_OK)
1115 			status = FS_CALL(vnode, lookup)(vnode->mount->cookie, vnode->private_node, path, &vnodeID, &type);
1116 
1117 		if (status < FSSH_B_OK) {
1118 			put_vnode(vnode);
1119 			return status;
1120 		}
1121 
1122 		// Lookup the vnode, the call to fs_lookup should have caused a get_vnode to be called
1123 		// from inside the filesystem, thus the vnode would have to be in the list and it's
1124 		// ref count incremented at this point
1125 		mutex_lock(&sVnodeMutex);
1126 		nextVnode = lookup_vnode(vnode->device, vnodeID);
1127 		mutex_unlock(&sVnodeMutex);
1128 
1129 		if (!nextVnode) {
1130 			// pretty screwed up here - the file system found the vnode, but the hash
1131 			// lookup failed, so our internal structures are messed up
1132 			fssh_panic("vnode_path_to_vnode: could not lookup vnode (mountid 0x%x vnid 0x%llx)\n",
1133 				(int)vnode->device, vnodeID);
1134 			put_vnode(vnode);
1135 			return FSSH_B_ENTRY_NOT_FOUND;
1136 		}
1137 
1138 		// If the new node is a symbolic link, resolve it (if we've been told to do it)
1139 		if (FSSH_S_ISLNK(type) && !(!traverseLeafLink && nextPath[0] == '\0')) {
1140 			fssh_size_t bufferSize;
1141 			char *buffer;
1142 
1143 			TRACE(("traverse link\n"));
1144 
1145 			// it's not exactly nice style using goto in this way, but hey, it works :-/
1146 			if (count + 1 > FSSH_B_MAX_SYMLINKS) {
1147 				status = FSSH_B_LINK_LIMIT;
1148 				goto resolve_link_error;
1149 			}
1150 
1151 			buffer = (char *)malloc(bufferSize = FSSH_B_PATH_NAME_LENGTH);
1152 			if (buffer == NULL) {
1153 				status = FSSH_B_NO_MEMORY;
1154 				goto resolve_link_error;
1155 			}
1156 
1157 			if (FS_CALL(nextVnode, read_symlink) != NULL) {
1158 				status = FS_CALL(nextVnode, read_symlink)(
1159 					nextVnode->mount->cookie, nextVnode->private_node, buffer,
1160 					&bufferSize);
1161 			} else
1162 				status = FSSH_B_BAD_VALUE;
1163 
1164 			if (status < FSSH_B_OK) {
1165 				free(buffer);
1166 
1167 		resolve_link_error:
1168 				put_vnode(vnode);
1169 				put_vnode(nextVnode);
1170 
1171 				return status;
1172 			}
1173 			put_vnode(nextVnode);
1174 
1175 			// Check if we start from the root directory or the current
1176 			// directory ("vnode" still points to that one).
1177 			// Cut off all leading slashes if it's the root directory
1178 			path = buffer;
1179 			if (path[0] == '/') {
1180 				// we don't need the old directory anymore
1181 				put_vnode(vnode);
1182 
1183 				while (*++path == '/')
1184 					;
1185 				vnode = sRoot;
1186 				inc_vnode_ref_count(vnode);
1187 			}
1188 			inc_vnode_ref_count(vnode);
1189 				// balance the next recursion - we will decrement the ref_count
1190 				// of the vnode, no matter if we succeeded or not
1191 
1192 			status = vnode_path_to_vnode(vnode, path, traverseLeafLink, count + 1,
1193 				&nextVnode, &lastParentID, _type);
1194 
1195 			free(buffer);
1196 
1197 			if (status < FSSH_B_OK) {
1198 				put_vnode(vnode);
1199 				return status;
1200 			}
1201 		} else
1202 			lastParentID = vnode->id;
1203 
1204 		// decrease the ref count on the old dir we just looked up into
1205 		put_vnode(vnode);
1206 
1207 		path = nextPath;
1208 		vnode = nextVnode;
1209 
1210 		// see if we hit a mount point
1211 		struct vnode *mountPoint = resolve_mount_point_to_volume_root(vnode);
1212 		if (mountPoint) {
1213 			put_vnode(vnode);
1214 			vnode = mountPoint;
1215 		}
1216 	}
1217 
1218 	*_vnode = vnode;
1219 	if (_type)
1220 		*_type = type;
1221 	if (_parentID)
1222 		*_parentID = lastParentID;
1223 
1224 	return FSSH_B_OK;
1225 }
1226 
1227 
1228 static fssh_status_t
1229 path_to_vnode(char *path, bool traverseLink, struct vnode **_vnode,
1230 	fssh_vnode_id *_parentID, bool kernel)
1231 {
1232 	struct vnode *start = NULL;
1233 
1234 	FUNCTION(("path_to_vnode(path = \"%s\")\n", path));
1235 
1236 	if (!path)
1237 		return FSSH_B_BAD_VALUE;
1238 
1239 	// figure out if we need to start at root or at cwd
1240 	if (*path == '/') {
1241 		if (sRoot == NULL) {
1242 			// we're a bit early, aren't we?
1243 			return FSSH_B_ERROR;
1244 		}
1245 
1246 		while (*++path == '/')
1247 			;
1248 		start = sRoot;
1249 		inc_vnode_ref_count(start);
1250 	} else {
1251 		struct io_context *context = get_current_io_context(kernel);
1252 
1253 		mutex_lock(&context->io_mutex);
1254 		start = context->cwd;
1255 		if (start != NULL)
1256 			inc_vnode_ref_count(start);
1257 		mutex_unlock(&context->io_mutex);
1258 
1259 		if (start == NULL)
1260 			return FSSH_B_ERROR;
1261 	}
1262 
1263 	return vnode_path_to_vnode(start, path, traverseLink, 0, _vnode, _parentID, NULL);
1264 }
1265 
1266 
1267 /** Returns the vnode in the next to last segment of the path, and returns
1268  *	the last portion in filename.
1269  *	The path buffer must be able to store at least one additional character.
1270  */
1271 
1272 static fssh_status_t
1273 path_to_dir_vnode(char *path, struct vnode **_vnode, char *filename, bool kernel)
1274 {
1275 	fssh_status_t status = get_dir_path_and_leaf(path, filename);
1276 	if (status != FSSH_B_OK)
1277 		return status;
1278 
1279 	return path_to_vnode(path, true, _vnode, NULL, kernel);
1280 }
1281 
1282 
1283 /**	\brief Retrieves the directory vnode and the leaf name of an entry referred
1284  *		   to by a FD + path pair.
1285  *
1286  *	\a path must be given in either case. \a fd might be omitted, in which
1287  *	case \a path is either an absolute path or one relative to the current
1288  *	directory. If both a supplied and \a path is relative it is reckoned off
1289  *	of the directory referred to by \a fd. If \a path is absolute \a fd is
1290  *	ignored.
1291  *
1292  *	The caller has the responsibility to call put_vnode() on the returned
1293  *	directory vnode.
1294  *
1295  *	\param fd The FD. May be < 0.
1296  *	\param path The absolute or relative path. Must not be \c NULL. The buffer
1297  *	       is modified by this function. It must have at least room for a
1298  *	       string one character longer than the path it contains.
1299  *	\param _vnode A pointer to a variable the directory vnode shall be written
1300  *		   into.
1301  *	\param filename A buffer of size FSSH_B_FILE_NAME_LENGTH or larger into which
1302  *		   the leaf name of the specified entry will be written.
1303  *	\param kernel \c true, if invoked from inside the kernel, \c false if
1304  *		   invoked from userland.
1305  *	\return \c FSSH_B_OK, if everything went fine, another error code otherwise.
1306  */
1307 
1308 static fssh_status_t
1309 fd_and_path_to_dir_vnode(int fd, char *path, struct vnode **_vnode,
1310 	char *filename, bool kernel)
1311 {
1312 	if (!path)
1313 		return FSSH_B_BAD_VALUE;
1314 	if (fd < 0)
1315 		return path_to_dir_vnode(path, _vnode, filename, kernel);
1316 
1317 	fssh_status_t status = get_dir_path_and_leaf(path, filename);
1318 	if (status != FSSH_B_OK)
1319 		return status;
1320 
1321 	return fd_and_path_to_vnode(fd, path, true, _vnode, NULL, kernel);
1322 }
1323 
1324 
1325 /** Returns a vnode's name in the d_name field of a supplied dirent buffer.
1326  */
1327 
1328 static fssh_status_t
1329 get_vnode_name(struct vnode *vnode, struct vnode *parent,
1330 	struct fssh_dirent *buffer, fssh_size_t bufferSize)
1331 {
1332 	if (bufferSize < sizeof(struct fssh_dirent))
1333 		return FSSH_B_BAD_VALUE;
1334 
1335 	// See if vnode is the root of a mount and move to the covered
1336 	// vnode so we get the underlying file system
1337 	VNodePutter vnodePutter;
1338 	if (vnode->mount->root_vnode == vnode && vnode->mount->covers_vnode != NULL) {
1339 		vnode = vnode->mount->covers_vnode;
1340 		inc_vnode_ref_count(vnode);
1341 		vnodePutter.SetTo(vnode);
1342 	}
1343 
1344 	if (FS_CALL(vnode, get_vnode_name)) {
1345 		// The FS supports getting the name of a vnode.
1346 		return FS_CALL(vnode, get_vnode_name)(vnode->mount->cookie,
1347 			vnode->private_node, buffer->d_name,
1348 			(char*)buffer + bufferSize - buffer->d_name);
1349 	}
1350 
1351 	// The FS doesn't support getting the name of a vnode. So we search the
1352 	// parent directory for the vnode, if the caller let us.
1353 
1354 	if (parent == NULL)
1355 		return FSSH_EOPNOTSUPP;
1356 
1357 	fssh_fs_cookie cookie;
1358 
1359 	fssh_status_t status = FS_CALL(parent, open_dir)(parent->mount->cookie,
1360 		parent->private_node, &cookie);
1361 	if (status >= FSSH_B_OK) {
1362 		while (true) {
1363 			uint32_t num = 1;
1364 			status = dir_read(parent, cookie, buffer, bufferSize, &num);
1365 			if (status < FSSH_B_OK)
1366 				break;
1367 			if (num == 0) {
1368 				status = FSSH_B_ENTRY_NOT_FOUND;
1369 				break;
1370 			}
1371 
1372 			if (vnode->id == buffer->d_ino) {
1373 				// found correct entry!
1374 				break;
1375 			}
1376 		}
1377 
1378 		FS_CALL(vnode, close_dir)(vnode->mount->cookie, vnode->private_node,
1379 			cookie);
1380 		FS_CALL(vnode, free_dir_cookie)(vnode->mount->cookie,
1381 			vnode->private_node, cookie);
1382 	}
1383 	return status;
1384 }
1385 
1386 
1387 static fssh_status_t
1388 get_vnode_name(struct vnode *vnode, struct vnode *parent, char *name,
1389 	fssh_size_t nameSize)
1390 {
1391 	char buffer[sizeof(struct fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
1392 	struct fssh_dirent *dirent = (struct fssh_dirent *)buffer;
1393 
1394 	fssh_status_t status = get_vnode_name(vnode, parent, buffer, sizeof(buffer));
1395 	if (status != FSSH_B_OK)
1396 		return status;
1397 
1398 	if (fssh_strlcpy(name, dirent->d_name, nameSize) >= nameSize)
1399 		return FSSH_B_BUFFER_OVERFLOW;
1400 
1401 	return FSSH_B_OK;
1402 }
1403 
1404 
1405 /**	Gets the full path to a given directory vnode.
1406  *	It uses the fs_get_vnode_name() call to get the name of a vnode; if a
1407  *	file system doesn't support this call, it will fall back to iterating
1408  *	through the parent directory to get the name of the child.
1409  *
1410  *	To protect against circular loops, it supports a maximum tree depth
1411  *	of 256 levels.
1412  *
1413  *	Note that the path may not be correct the time this function returns!
1414  *	It doesn't use any locking to prevent returning the correct path, as
1415  *	paths aren't safe anyway: the path to a file can change at any time.
1416  *
1417  *	It might be a good idea, though, to check if the returned path exists
1418  *	in the calling function (it's not done here because of efficiency)
1419  */
1420 
1421 static fssh_status_t
1422 dir_vnode_to_path(struct vnode *vnode, char *buffer, fssh_size_t bufferSize)
1423 {
1424 	FUNCTION(("dir_vnode_to_path(%p, %p, %lu)\n", vnode, buffer, bufferSize));
1425 
1426 	if (vnode == NULL || buffer == NULL)
1427 		return FSSH_B_BAD_VALUE;
1428 
1429 	/* this implementation is currently bound to FSSH_B_PATH_NAME_LENGTH */
1430 	KPath pathBuffer;
1431 	if (pathBuffer.InitCheck() != FSSH_B_OK)
1432 		return FSSH_B_NO_MEMORY;
1433 
1434 	char *path = pathBuffer.LockBuffer();
1435 	int32_t insert = pathBuffer.BufferSize();
1436 	int32_t maxLevel = 256;
1437 	int32_t length;
1438 	fssh_status_t status;
1439 
1440 	// we don't use get_vnode() here because this call is more
1441 	// efficient and does all we need from get_vnode()
1442 	inc_vnode_ref_count(vnode);
1443 
1444 	// resolve a volume root to its mount point
1445 	struct vnode *mountPoint = resolve_volume_root_to_mount_point(vnode);
1446 	if (mountPoint) {
1447 		put_vnode(vnode);
1448 		vnode = mountPoint;
1449 	}
1450 
1451 	path[--insert] = '\0';
1452 
1453 	while (true) {
1454 		// the name buffer is also used for fs_read_dir()
1455 		char nameBuffer[sizeof(struct fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
1456 		char *name = &((struct fssh_dirent *)nameBuffer)->d_name[0];
1457 		struct vnode *parentVnode;
1458 		fssh_vnode_id parentID;
1459 		int type;
1460 
1461 		// lookup the parent vnode
1462 		status = FS_CALL(vnode, lookup)(vnode->mount->cookie, vnode->private_node, "..",
1463 			&parentID, &type);
1464 		if (status < FSSH_B_OK)
1465 			goto out;
1466 
1467 		mutex_lock(&sVnodeMutex);
1468 		parentVnode = lookup_vnode(vnode->device, parentID);
1469 		mutex_unlock(&sVnodeMutex);
1470 
1471 		if (parentVnode == NULL) {
1472 			fssh_panic("dir_vnode_to_path: could not lookup vnode (mountid 0x%x vnid 0x%llx)\n",
1473 				(int)vnode->device, parentID);
1474 			status = FSSH_B_ENTRY_NOT_FOUND;
1475 			goto out;
1476 		}
1477 
1478 		// get the node's name
1479 		status = get_vnode_name(vnode, parentVnode,
1480 			(struct fssh_dirent*)nameBuffer, sizeof(nameBuffer));
1481 
1482 		// resolve a volume root to its mount point
1483 		mountPoint = resolve_volume_root_to_mount_point(parentVnode);
1484 		if (mountPoint) {
1485 			put_vnode(parentVnode);
1486 			parentVnode = mountPoint;
1487 			parentID = parentVnode->id;
1488 		}
1489 
1490 		bool hitRoot = (parentVnode == vnode);
1491 
1492 		// release the current vnode, we only need its parent from now on
1493 		put_vnode(vnode);
1494 		vnode = parentVnode;
1495 
1496 		if (status < FSSH_B_OK)
1497 			goto out;
1498 
1499 		if (hitRoot) {
1500 			// we have reached "/", which means we have constructed the full
1501 			// path
1502 			break;
1503 		}
1504 
1505 		// ToDo: add an explicit check for loops in about 10 levels to do
1506 		// real loop detection
1507 
1508 		// don't go deeper as 'maxLevel' to prevent circular loops
1509 		if (maxLevel-- < 0) {
1510 			status = FSSH_ELOOP;
1511 			goto out;
1512 		}
1513 
1514 		// add the name in front of the current path
1515 		name[FSSH_B_FILE_NAME_LENGTH - 1] = '\0';
1516 		length = fssh_strlen(name);
1517 		insert -= length;
1518 		if (insert <= 0) {
1519 			status = FSSH_ENOBUFS;
1520 			goto out;
1521 		}
1522 		fssh_memcpy(path + insert, name, length);
1523 		path[--insert] = '/';
1524 	}
1525 
1526 	// the root dir will result in an empty path: fix it
1527 	if (path[insert] == '\0')
1528 		path[--insert] = '/';
1529 
1530 	TRACE(("  path is: %s\n", path + insert));
1531 
1532 	// copy the path to the output buffer
1533 	length = pathBuffer.BufferSize() - insert;
1534 	if (length <= (int)bufferSize)
1535 		fssh_memcpy(buffer, path + insert, length);
1536 	else
1537 		status = FSSH_ENOBUFS;
1538 
1539 out:
1540 	put_vnode(vnode);
1541 	return status;
1542 }
1543 
1544 
1545 /**	Checks the length of every path component, and adds a '.'
1546  *	if the path ends in a slash.
1547  *	The given path buffer must be able to store at least one
1548  *	additional character.
1549  */
1550 
1551 static fssh_status_t
1552 check_path(char *to)
1553 {
1554 	int32_t length = 0;
1555 
1556 	// check length of every path component
1557 
1558 	while (*to) {
1559 		char *begin;
1560 		if (*to == '/')
1561 			to++, length++;
1562 
1563 		begin = to;
1564 		while (*to != '/' && *to)
1565 			to++, length++;
1566 
1567 		if (to - begin > FSSH_B_FILE_NAME_LENGTH)
1568 			return FSSH_B_NAME_TOO_LONG;
1569 	}
1570 
1571 	if (length == 0)
1572 		return FSSH_B_ENTRY_NOT_FOUND;
1573 
1574 	// complete path if there is a slash at the end
1575 
1576 	if (*(to - 1) == '/') {
1577 		if (length > FSSH_B_PATH_NAME_LENGTH - 2)
1578 			return FSSH_B_NAME_TOO_LONG;
1579 
1580 		to[0] = '.';
1581 		to[1] = '\0';
1582 	}
1583 
1584 	return FSSH_B_OK;
1585 }
1586 
1587 
1588 static struct file_descriptor *
1589 get_fd_and_vnode(int fd, struct vnode **_vnode, bool kernel)
1590 {
1591 	struct file_descriptor *descriptor = get_fd(get_current_io_context(kernel), fd);
1592 	if (descriptor == NULL)
1593 		return NULL;
1594 
1595 	if (fd_vnode(descriptor) == NULL) {
1596 		put_fd(descriptor);
1597 		return NULL;
1598 	}
1599 
1600 	// ToDo: when we can close a file descriptor at any point, investigate
1601 	//	if this is still valid to do (accessing the vnode without ref_count
1602 	//	or locking)
1603 	*_vnode = descriptor->u.vnode;
1604 	return descriptor;
1605 }
1606 
1607 
1608 static struct vnode *
1609 get_vnode_from_fd(int fd, bool kernel)
1610 {
1611 	struct file_descriptor *descriptor;
1612 	struct vnode *vnode;
1613 
1614 	descriptor = get_fd(get_current_io_context(kernel), fd);
1615 	if (descriptor == NULL)
1616 		return NULL;
1617 
1618 	vnode = fd_vnode(descriptor);
1619 	if (vnode != NULL)
1620 		inc_vnode_ref_count(vnode);
1621 
1622 	put_fd(descriptor);
1623 	return vnode;
1624 }
1625 
1626 
1627 /**	Gets the vnode from an FD + path combination. If \a fd is lower than zero,
1628  *	only the path will be considered. In this case, the \a path must not be
1629  *	NULL.
1630  *	If \a fd is a valid file descriptor, \a path may be NULL for directories,
1631  *	and should be NULL for files.
1632  */
1633 
1634 static fssh_status_t
1635 fd_and_path_to_vnode(int fd, char *path, bool traverseLeafLink,
1636 	struct vnode **_vnode, fssh_vnode_id *_parentID, bool kernel)
1637 {
1638 	if (fd < 0 && !path)
1639 		return FSSH_B_BAD_VALUE;
1640 
1641 	if (fd < 0 || (path != NULL && path[0] == '/')) {
1642 		// no FD or absolute path
1643 		return path_to_vnode(path, traverseLeafLink, _vnode, _parentID, kernel);
1644 	}
1645 
1646 	// FD only, or FD + relative path
1647 	struct vnode *vnode = get_vnode_from_fd(fd, kernel);
1648 	if (!vnode)
1649 		return FSSH_B_FILE_ERROR;
1650 
1651 	if (path != NULL) {
1652 		return vnode_path_to_vnode(vnode, path, traverseLeafLink, 0,
1653 			_vnode, _parentID, NULL);
1654 	}
1655 
1656 	// there is no relative path to take into account
1657 
1658 	*_vnode = vnode;
1659 	if (_parentID)
1660 		*_parentID = -1;
1661 
1662 	return FSSH_B_OK;
1663 }
1664 
1665 
1666 static int
1667 get_new_fd(int type, struct fs_mount *mount, struct vnode *vnode,
1668 	fssh_fs_cookie cookie, int openMode, bool kernel)
1669 {
1670 	struct file_descriptor *descriptor;
1671 	int fd;
1672 
1673 	// if the vnode is locked, we don't allow creating a new file descriptor for it
1674 	if (vnode && vnode->mandatory_locked_by != NULL)
1675 		return FSSH_B_BUSY;
1676 
1677 	descriptor = alloc_fd();
1678 	if (!descriptor)
1679 		return FSSH_B_NO_MEMORY;
1680 
1681 	if (vnode)
1682 		descriptor->u.vnode = vnode;
1683 	else
1684 		descriptor->u.mount = mount;
1685 	descriptor->cookie = cookie;
1686 
1687 	switch (type) {
1688 		// vnode types
1689 		case FDTYPE_FILE:
1690 			descriptor->ops = &sFileOps;
1691 			break;
1692 		case FDTYPE_DIR:
1693 			descriptor->ops = &sDirectoryOps;
1694 			break;
1695 		case FDTYPE_ATTR:
1696 			descriptor->ops = &sAttributeOps;
1697 			break;
1698 		case FDTYPE_ATTR_DIR:
1699 			descriptor->ops = &sAttributeDirectoryOps;
1700 			break;
1701 
1702 		// mount types
1703 		case FDTYPE_INDEX_DIR:
1704 			descriptor->ops = &sIndexDirectoryOps;
1705 			break;
1706 		case FDTYPE_QUERY:
1707 			descriptor->ops = &sQueryOps;
1708 			break;
1709 
1710 		default:
1711 			fssh_panic("get_new_fd() called with unknown type %d\n", type);
1712 			break;
1713 	}
1714 	descriptor->type = type;
1715 	descriptor->open_mode = openMode;
1716 
1717 	fd = new_fd(get_current_io_context(kernel), descriptor);
1718 	if (fd < 0) {
1719 		free(descriptor);
1720 		return FSSH_B_NO_MORE_FDS;
1721 	}
1722 
1723 	return fd;
1724 }
1725 
1726 
1727 //	#pragma mark - public VFS API
1728 
1729 
1730 extern "C" fssh_status_t
1731 fssh_new_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID,
1732 	fssh_fs_vnode privateNode)
1733 {
1734 	FUNCTION(("new_vnode(mountID = %ld, vnodeID = %Ld, node = %p)\n",
1735 		mountID, vnodeID, privateNode));
1736 
1737 	if (privateNode == NULL)
1738 		return FSSH_B_BAD_VALUE;
1739 
1740 	mutex_lock(&sVnodeMutex);
1741 
1742 	// file system integrity check:
1743 	// test if the vnode already exists and bail out if this is the case!
1744 
1745 	// ToDo: the R5 implementation obviously checks for a different cookie
1746 	//	and doesn't panic if they are equal
1747 
1748 	struct vnode *vnode = lookup_vnode(mountID, vnodeID);
1749 	if (vnode != NULL)
1750 		fssh_panic("vnode %d:%lld already exists (node = %p, vnode->node = %p)!", (int)mountID, vnodeID, privateNode, vnode->private_node);
1751 
1752 	fssh_status_t status = create_new_vnode(&vnode, mountID, vnodeID);
1753 	if (status == FSSH_B_OK) {
1754 		vnode->private_node = privateNode;
1755 		vnode->busy = true;
1756 		vnode->unpublished = true;
1757 	}
1758 
1759 	TRACE(("returns: %s\n", strerror(status)));
1760 
1761 	mutex_unlock(&sVnodeMutex);
1762 	return status;
1763 }
1764 
1765 
1766 extern "C" fssh_status_t
1767 fssh_publish_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID,
1768 	fssh_fs_vnode privateNode)
1769 {
1770 	FUNCTION(("publish_vnode()\n"));
1771 
1772 	mutex_lock(&sVnodeMutex);
1773 
1774 	struct vnode *vnode = lookup_vnode(mountID, vnodeID);
1775 	fssh_status_t status = FSSH_B_OK;
1776 
1777 	if (vnode != NULL && vnode->busy && vnode->unpublished
1778 		&& vnode->private_node == privateNode) {
1779 		vnode->busy = false;
1780 		vnode->unpublished = false;
1781 	} else if (vnode == NULL && privateNode != NULL) {
1782 		status = create_new_vnode(&vnode, mountID, vnodeID);
1783 		if (status == FSSH_B_OK)
1784 			vnode->private_node = privateNode;
1785 	} else
1786 		status = FSSH_B_BAD_VALUE;
1787 
1788 	TRACE(("returns: %s\n", strerror(status)));
1789 
1790 	mutex_unlock(&sVnodeMutex);
1791 	return status;
1792 }
1793 
1794 
1795 extern "C" fssh_status_t
1796 fssh_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID,
1797 	fssh_fs_vnode *_fsNode)
1798 {
1799 	struct vnode *vnode;
1800 
1801 	fssh_status_t status = get_vnode(mountID, vnodeID, &vnode, true);
1802 	if (status < FSSH_B_OK)
1803 		return status;
1804 
1805 	*_fsNode = vnode->private_node;
1806 	return FSSH_B_OK;
1807 }
1808 
1809 
1810 extern "C" fssh_status_t
1811 fssh_put_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID)
1812 {
1813 	struct vnode *vnode;
1814 
1815 	mutex_lock(&sVnodeMutex);
1816 	vnode = lookup_vnode(mountID, vnodeID);
1817 	mutex_unlock(&sVnodeMutex);
1818 
1819 	if (vnode)
1820 		dec_vnode_ref_count(vnode, true);
1821 
1822 	return FSSH_B_OK;
1823 }
1824 
1825 
1826 extern "C" fssh_status_t
1827 fssh_remove_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID)
1828 {
1829 	struct vnode *vnode;
1830 	bool remove = false;
1831 
1832 	mutex_lock(&sVnodeMutex);
1833 
1834 	vnode = lookup_vnode(mountID, vnodeID);
1835 	if (vnode != NULL) {
1836 		if (vnode->covered_by != NULL) {
1837 			// this vnode is in use
1838 			mutex_unlock(&sVnodeMutex);
1839 			return FSSH_B_BUSY;
1840 		}
1841 
1842 		vnode->remove = true;
1843 		if (vnode->unpublished) {
1844 			// prepare the vnode for deletion
1845 			vnode->busy = true;
1846 			remove = true;
1847 		}
1848 	}
1849 
1850 	mutex_unlock(&sVnodeMutex);
1851 
1852 	if (remove) {
1853 		// if the vnode hasn't been published yet, we delete it here
1854 		fssh_atomic_add(&vnode->ref_count, -1);
1855 		free_vnode(vnode, true);
1856 	}
1857 
1858 	return FSSH_B_OK;
1859 }
1860 
1861 
1862 extern "C" fssh_status_t
1863 fssh_unremove_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID)
1864 {
1865 	struct vnode *vnode;
1866 
1867 	mutex_lock(&sVnodeMutex);
1868 
1869 	vnode = lookup_vnode(mountID, vnodeID);
1870 	if (vnode)
1871 		vnode->remove = false;
1872 
1873 	mutex_unlock(&sVnodeMutex);
1874 	return FSSH_B_OK;
1875 }
1876 
1877 
1878 extern "C" fssh_status_t
1879 fssh_get_vnode_removed(fssh_mount_id mountID, fssh_vnode_id vnodeID,
1880 	bool* removed)
1881 {
1882 	mutex_lock(&sVnodeMutex);
1883 
1884 	fssh_status_t result;
1885 
1886 	if (struct vnode* vnode = lookup_vnode(mountID, vnodeID)) {
1887 		if (removed)
1888 			*removed = vnode->remove;
1889 		result = FSSH_B_OK;
1890 	} else
1891 		result = FSSH_B_BAD_VALUE;
1892 
1893 	mutex_unlock(&sVnodeMutex);
1894 	return result;
1895 }
1896 
1897 
1898 //	#pragma mark - private VFS API
1899 //	Functions the VFS exports for other parts of the kernel
1900 
1901 
1902 /** Acquires another reference to the vnode that has to be released
1903  *	by calling vfs_put_vnode().
1904  */
1905 
1906 void
1907 vfs_acquire_vnode(void *_vnode)
1908 {
1909 	inc_vnode_ref_count((struct vnode *)_vnode);
1910 }
1911 
1912 
1913 /** This is currently called from file_cache_create() only.
1914  *	It's probably a temporary solution as long as devfs requires that
1915  *	fs_read_pages()/fs_write_pages() are called with the standard
1916  *	open cookie and not with a device cookie.
1917  *	If that's done differently, remove this call; it has no other
1918  *	purpose.
1919  */
1920 
1921 fssh_status_t
1922 vfs_get_cookie_from_fd(int fd, void **_cookie)
1923 {
1924 	struct file_descriptor *descriptor;
1925 
1926 	descriptor = get_fd(get_current_io_context(true), fd);
1927 	if (descriptor == NULL)
1928 		return FSSH_B_FILE_ERROR;
1929 
1930 	*_cookie = descriptor->cookie;
1931 	return FSSH_B_OK;
1932 }
1933 
1934 
1935 int
1936 vfs_get_vnode_from_fd(int fd, bool kernel, void **vnode)
1937 {
1938 	*vnode = get_vnode_from_fd(fd, kernel);
1939 
1940 	if (*vnode == NULL)
1941 		return FSSH_B_FILE_ERROR;
1942 
1943 	return FSSH_B_NO_ERROR;
1944 }
1945 
1946 
1947 fssh_status_t
1948 vfs_get_vnode_from_path(const char *path, bool kernel, void **_vnode)
1949 {
1950 	TRACE(("vfs_get_vnode_from_path: entry. path = '%s', kernel %d\n", path, kernel));
1951 
1952 	KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
1953 	if (pathBuffer.InitCheck() != FSSH_B_OK)
1954 		return FSSH_B_NO_MEMORY;
1955 
1956 	char *buffer = pathBuffer.LockBuffer();
1957 	fssh_strlcpy(buffer, path, pathBuffer.BufferSize());
1958 
1959 	struct vnode *vnode;
1960 	fssh_status_t status = path_to_vnode(buffer, true, &vnode, NULL, kernel);
1961 	if (status < FSSH_B_OK)
1962 		return status;
1963 
1964 	*_vnode = vnode;
1965 	return FSSH_B_OK;
1966 }
1967 
1968 
1969 fssh_status_t
1970 vfs_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void **_vnode)
1971 {
1972 	struct vnode *vnode;
1973 
1974 	fssh_status_t status = get_vnode(mountID, vnodeID, &vnode, false);
1975 	if (status < FSSH_B_OK)
1976 		return status;
1977 
1978 	*_vnode = vnode;
1979 	return FSSH_B_OK;
1980 }
1981 
1982 
1983 fssh_status_t
1984 vfs_entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID,
1985 	const char *name, void **_vnode)
1986 {
1987 	return entry_ref_to_vnode(mountID, directoryID, name, (struct vnode **)_vnode);
1988 }
1989 
1990 
1991 void
1992 vfssh_fs_vnode_to_node_ref(void *_vnode, fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID)
1993 {
1994 	struct vnode *vnode = (struct vnode *)_vnode;
1995 
1996 	*_mountID = vnode->device;
1997 	*_vnodeID = vnode->id;
1998 }
1999 
2000 
2001 /**	Looks up a vnode with the given mount and vnode ID.
2002  *	Must only be used with "in-use" vnodes as it doesn't grab a reference
2003  *	to the node.
2004  *	It's currently only be used by file_cache_create().
2005  */
2006 
2007 fssh_status_t
2008 vfs_lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void **_vnode)
2009 {
2010 	mutex_lock(&sVnodeMutex);
2011 	struct vnode *vnode = lookup_vnode(mountID, vnodeID);
2012 	mutex_unlock(&sVnodeMutex);
2013 
2014 	if (vnode == NULL)
2015 		return FSSH_B_ERROR;
2016 
2017 	*_vnode = vnode;
2018 	return FSSH_B_OK;
2019 }
2020 
2021 
2022 fssh_status_t
2023 vfs_get_fs_node_from_path(fssh_mount_id mountID, const char *path, bool kernel, void **_node)
2024 {
2025 	TRACE(("vfs_get_fs_node_from_path(mountID = %ld, path = \"%s\", kernel %d)\n",
2026 		mountID, path, kernel));
2027 
2028 	KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
2029 	if (pathBuffer.InitCheck() != FSSH_B_OK)
2030 		return FSSH_B_NO_MEMORY;
2031 
2032 	fs_mount *mount;
2033 	fssh_status_t status = get_mount(mountID, &mount);
2034 	if (status < FSSH_B_OK)
2035 		return status;
2036 
2037 	char *buffer = pathBuffer.LockBuffer();
2038 	fssh_strlcpy(buffer, path, pathBuffer.BufferSize());
2039 
2040 	struct vnode *vnode = mount->root_vnode;
2041 
2042 	if (buffer[0] == '/')
2043 		status = path_to_vnode(buffer, true, &vnode, NULL, true);
2044 	else {
2045 		inc_vnode_ref_count(vnode);
2046 			// vnode_path_to_vnode() releases a reference to the starting vnode
2047 		status = vnode_path_to_vnode(vnode, buffer, true, 0, &vnode, NULL, NULL);
2048 	}
2049 
2050 	put_mount(mount);
2051 
2052 	if (status < FSSH_B_OK)
2053 		return status;
2054 
2055 	if (vnode->device != mountID) {
2056 		// wrong mount ID - must not gain access on foreign file system nodes
2057 		put_vnode(vnode);
2058 		return FSSH_B_BAD_VALUE;
2059 	}
2060 
2061 	*_node = vnode->private_node;
2062 	return FSSH_B_OK;
2063 }
2064 
2065 
2066 /**	Finds the full path to the file that contains the module \a moduleName,
2067  *	puts it into \a pathBuffer, and returns FSSH_B_OK for success.
2068  *	If \a pathBuffer was too small, it returns \c FSSH_B_BUFFER_OVERFLOW,
2069  *	\c FSSH_B_ENTRY_NOT_FOUNT if no file could be found.
2070  *	\a pathBuffer is clobbered in any case and must not be relied on if this
2071  *	functions returns unsuccessfully.
2072  */
2073 
2074 fssh_status_t
2075 vfs_get_module_path(const char *basePath, const char *moduleName, char *pathBuffer,
2076 	fssh_size_t bufferSize)
2077 {
2078 	struct vnode *dir, *file;
2079 	fssh_status_t status;
2080 	fssh_size_t length;
2081 	char *path;
2082 
2083 	if (bufferSize == 0 || fssh_strlcpy(pathBuffer, basePath, bufferSize) >= bufferSize)
2084 		return FSSH_B_BUFFER_OVERFLOW;
2085 
2086 	status = path_to_vnode(pathBuffer, true, &dir, NULL, true);
2087 	if (status < FSSH_B_OK)
2088 		return status;
2089 
2090 	// the path buffer had been clobbered by the above call
2091 	length = fssh_strlcpy(pathBuffer, basePath, bufferSize);
2092 	if (pathBuffer[length - 1] != '/')
2093 		pathBuffer[length++] = '/';
2094 
2095 	path = pathBuffer + length;
2096 	bufferSize -= length;
2097 
2098 	while (moduleName) {
2099 		int type;
2100 
2101 		char *nextPath = fssh_strchr(moduleName, '/');
2102 		if (nextPath == NULL)
2103 			length = fssh_strlen(moduleName);
2104 		else {
2105 			length = nextPath - moduleName;
2106 			nextPath++;
2107 		}
2108 
2109 		if (length + 1 >= bufferSize) {
2110 			status = FSSH_B_BUFFER_OVERFLOW;
2111 			goto err;
2112 		}
2113 
2114 		fssh_memcpy(path, moduleName, length);
2115 		path[length] = '\0';
2116 		moduleName = nextPath;
2117 
2118 		status = vnode_path_to_vnode(dir, path, true, 0, &file, NULL, &type);
2119 		if (status < FSSH_B_OK) {
2120 			// vnode_path_to_vnode() has already released the reference to dir
2121 			return status;
2122 		}
2123 
2124 		if (FSSH_S_ISDIR(type)) {
2125 			// goto the next directory
2126 			path[length] = '/';
2127 			path[length + 1] = '\0';
2128 			path += length + 1;
2129 			bufferSize -= length + 1;
2130 
2131 			dir = file;
2132 		} else if (FSSH_S_ISREG(type)) {
2133 			// it's a file so it should be what we've searched for
2134 			put_vnode(file);
2135 
2136 			return FSSH_B_OK;
2137 		} else {
2138 			TRACE(("vfs_get_module_path(): something is strange here: %d...\n", type));
2139 			status = FSSH_B_ERROR;
2140 			dir = file;
2141 			goto err;
2142 		}
2143 	}
2144 
2145 	// if we got here, the moduleName just pointed to a directory, not to
2146 	// a real module - what should we do in this case?
2147 	status = FSSH_B_ENTRY_NOT_FOUND;
2148 
2149 err:
2150 	put_vnode(dir);
2151 	return status;
2152 }
2153 
2154 
2155 /**	\brief Normalizes a given path.
2156  *
2157  *	The path must refer to an existing or non-existing entry in an existing
2158  *	directory, that is chopping off the leaf component the remaining path must
2159  *	refer to an existing directory.
2160  *
2161  *	The returned will be canonical in that it will be absolute, will not
2162  *	contain any "." or ".." components or duplicate occurrences of '/'s,
2163  *	and none of the directory components will by symbolic links.
2164  *
2165  *	Any two paths referring to the same entry, will result in the same
2166  *	normalized path (well, that is pretty much the definition of `normalized',
2167  *	isn't it :-).
2168  *
2169  *	\param path The path to be normalized.
2170  *	\param buffer The buffer into which the normalized path will be written.
2171  *	\param bufferSize The size of \a buffer.
2172  *	\param kernel \c true, if the IO context of the kernel shall be used,
2173  *		   otherwise that of the team this thread belongs to. Only relevant,
2174  *		   if the path is relative (to get the CWD).
2175  *	\return \c FSSH_B_OK if everything went fine, another error code otherwise.
2176  */
2177 
2178 fssh_status_t
2179 vfs_normalize_path(const char *path, char *buffer, fssh_size_t bufferSize,
2180 	bool kernel)
2181 {
2182 	if (!path || !buffer || bufferSize < 1)
2183 		return FSSH_B_BAD_VALUE;
2184 
2185 	TRACE(("vfs_normalize_path(`%s')\n", path));
2186 
2187 	// copy the supplied path to the stack, so it can be modified
2188 	KPath mutablePathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
2189 	if (mutablePathBuffer.InitCheck() != FSSH_B_OK)
2190 		return FSSH_B_NO_MEMORY;
2191 
2192 	char *mutablePath = mutablePathBuffer.LockBuffer();
2193 	if (fssh_strlcpy(mutablePath, path, FSSH_B_PATH_NAME_LENGTH) >= FSSH_B_PATH_NAME_LENGTH)
2194 		return FSSH_B_NAME_TOO_LONG;
2195 
2196 	// get the dir vnode and the leaf name
2197 	struct vnode *dirNode;
2198 	char leaf[FSSH_B_FILE_NAME_LENGTH];
2199 	fssh_status_t error = path_to_dir_vnode(mutablePath, &dirNode, leaf, kernel);
2200 	if (error != FSSH_B_OK) {
2201 		TRACE(("vfs_normalize_path(): failed to get dir vnode: %s\n", strerror(error)));
2202 		return error;
2203 	}
2204 
2205 	// if the leaf is "." or "..", we directly get the correct directory
2206 	// vnode and ignore the leaf later
2207 	bool isDir = (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0);
2208 	if (isDir)
2209 		error = vnode_path_to_vnode(dirNode, leaf, false, 0, &dirNode, NULL, NULL);
2210 	if (error != FSSH_B_OK) {
2211 		TRACE(("vfs_normalize_path(): failed to get dir vnode for \".\" or \"..\": %s\n",
2212 			strerror(error)));
2213 		return error;
2214 	}
2215 
2216 	// get the directory path
2217 	error = dir_vnode_to_path(dirNode, buffer, bufferSize);
2218 	put_vnode(dirNode);
2219 	if (error < FSSH_B_OK) {
2220 		TRACE(("vfs_normalize_path(): failed to get dir path: %s\n", strerror(error)));
2221 		return error;
2222 	}
2223 
2224 	// append the leaf name
2225 	if (!isDir) {
2226 		// insert a directory separator only if this is not the file system root
2227 		if ((fssh_strcmp(buffer, "/") != 0
2228 			 && fssh_strlcat(buffer, "/", bufferSize) >= bufferSize)
2229 			|| fssh_strlcat(buffer, leaf, bufferSize) >= bufferSize) {
2230 			return FSSH_B_NAME_TOO_LONG;
2231 		}
2232 	}
2233 
2234 	TRACE(("vfs_normalize_path() -> `%s'\n", buffer));
2235 	return FSSH_B_OK;
2236 }
2237 
2238 
2239 void
2240 vfs_put_vnode(void *_vnode)
2241 {
2242 	put_vnode((struct vnode *)_vnode);
2243 }
2244 
2245 
2246 fssh_status_t
2247 vfs_get_cwd(fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID)
2248 {
2249 	// Get current working directory from io context
2250 	struct io_context *context = get_current_io_context(false);
2251 	fssh_status_t status = FSSH_B_OK;
2252 
2253 	mutex_lock(&context->io_mutex);
2254 
2255 	if (context->cwd != NULL) {
2256 		*_mountID = context->cwd->device;
2257 		*_vnodeID = context->cwd->id;
2258 	} else
2259 		status = FSSH_B_ERROR;
2260 
2261 	mutex_unlock(&context->io_mutex);
2262 	return status;
2263 }
2264 
2265 
2266 fssh_status_t
2267 vfs_get_file_map(void *_vnode, fssh_off_t offset, fssh_size_t size,
2268 	fssh_file_io_vec *vecs, fssh_size_t *_count)
2269 {
2270 	struct vnode *vnode = (struct vnode *)_vnode;
2271 
2272 	FUNCTION(("vfs_get_file_map: vnode %p, vecs %p, offset %lld, size = %u\n", vnode, vecs, offset, (unsigned)size));
2273 
2274 	return FS_CALL(vnode, get_file_map)(vnode->mount->cookie, vnode->private_node, offset, size, vecs, _count);
2275 }
2276 
2277 
2278 fssh_status_t
2279 vfs_stat_vnode(void *_vnode, struct fssh_stat *stat)
2280 {
2281 	struct vnode *vnode = (struct vnode *)_vnode;
2282 
2283 	fssh_status_t status = FS_CALL(vnode, read_stat)(vnode->mount->cookie,
2284 		vnode->private_node, stat);
2285 
2286 	// fill in the st_dev and st_ino fields
2287 	if (status == FSSH_B_OK) {
2288 		stat->fssh_st_dev = vnode->device;
2289 		stat->fssh_st_ino = vnode->id;
2290 	}
2291 
2292 	return status;
2293 }
2294 
2295 
2296 fssh_status_t
2297 vfs_get_vnode_name(void *_vnode, char *name, fssh_size_t nameSize)
2298 {
2299 	return get_vnode_name((struct vnode *)_vnode, NULL, name, nameSize);
2300 }
2301 
2302 
2303 /**	If the given descriptor locked its vnode, that lock will be released.
2304  */
2305 
2306 void
2307 vfs_unlock_vnode_if_locked(struct file_descriptor *descriptor)
2308 {
2309 	struct vnode *vnode = fd_vnode(descriptor);
2310 
2311 	if (vnode != NULL && vnode->mandatory_locked_by == descriptor)
2312 		vnode->mandatory_locked_by = NULL;
2313 }
2314 
2315 
2316 /**	Closes all file descriptors of the specified I/O context that
2317  *	don't have the FSSH_O_CLOEXEC flag set.
2318  */
2319 
2320 void
2321 vfs_exec_io_context(void *_context)
2322 {
2323 	struct io_context *context = (struct io_context *)_context;
2324 	uint32_t i;
2325 
2326 	for (i = 0; i < context->table_size; i++) {
2327 		mutex_lock(&context->io_mutex);
2328 
2329 		struct file_descriptor *descriptor = context->fds[i];
2330 		bool remove = false;
2331 
2332 		if (descriptor != NULL && fd_close_on_exec(context, i)) {
2333 			context->fds[i] = NULL;
2334 			context->num_used_fds--;
2335 
2336 			remove = true;
2337 		}
2338 
2339 		mutex_unlock(&context->io_mutex);
2340 
2341 		if (remove) {
2342 			close_fd(descriptor);
2343 			put_fd(descriptor);
2344 		}
2345 	}
2346 }
2347 
2348 
2349 /** Sets up a new io_control structure, and inherits the properties
2350  *	of the parent io_control if it is given.
2351  */
2352 
2353 void *
2354 vfs_new_io_context(void *_parentContext)
2355 {
2356 	fssh_size_t tableSize;
2357 	struct io_context *context;
2358 	struct io_context *parentContext;
2359 
2360 	context = (io_context *)malloc(sizeof(struct io_context));
2361 	if (context == NULL)
2362 		return NULL;
2363 
2364 	fssh_memset(context, 0, sizeof(struct io_context));
2365 
2366 	parentContext = (struct io_context *)_parentContext;
2367 	if (parentContext)
2368 		tableSize = parentContext->table_size;
2369 	else
2370 		tableSize = DEFAULT_FD_TABLE_SIZE;
2371 
2372 	// allocate space for FDs and their close-on-exec flag
2373 	context->fds = (file_descriptor **)malloc(sizeof(struct file_descriptor *) * tableSize
2374 		+ (tableSize + 7) / 8);
2375 	if (context->fds == NULL) {
2376 		free(context);
2377 		return NULL;
2378 	}
2379 
2380 	fssh_memset(context->fds, 0, sizeof(struct file_descriptor *) * tableSize
2381 		+ (tableSize + 7) / 8);
2382 	context->fds_close_on_exec = (uint8_t *)(context->fds + tableSize);
2383 
2384 	if (mutex_init(&context->io_mutex, "I/O context") < 0) {
2385 		free(context->fds);
2386 		free(context);
2387 		return NULL;
2388 	}
2389 
2390 	// Copy all parent files which don't have the FSSH_O_CLOEXEC flag set
2391 
2392 	if (parentContext) {
2393 		fssh_size_t i;
2394 
2395 		mutex_lock(&parentContext->io_mutex);
2396 
2397 		context->cwd = parentContext->cwd;
2398 		if (context->cwd)
2399 			inc_vnode_ref_count(context->cwd);
2400 
2401 		for (i = 0; i < tableSize; i++) {
2402 			struct file_descriptor *descriptor = parentContext->fds[i];
2403 
2404 			if (descriptor != NULL && !fd_close_on_exec(parentContext, i)) {
2405 				context->fds[i] = descriptor;
2406 				context->num_used_fds++;
2407 				fssh_atomic_add(&descriptor->ref_count, 1);
2408 				fssh_atomic_add(&descriptor->open_count, 1);
2409 			}
2410 		}
2411 
2412 		mutex_unlock(&parentContext->io_mutex);
2413 	} else {
2414 		context->cwd = sRoot;
2415 
2416 		if (context->cwd)
2417 			inc_vnode_ref_count(context->cwd);
2418 	}
2419 
2420 	context->table_size = tableSize;
2421 
2422 	return context;
2423 }
2424 
2425 
2426 fssh_status_t
2427 vfs_free_io_context(void *_ioContext)
2428 {
2429 	struct io_context *context = (struct io_context *)_ioContext;
2430 	uint32_t i;
2431 
2432 	if (context->cwd)
2433 		dec_vnode_ref_count(context->cwd, false);
2434 
2435 	mutex_lock(&context->io_mutex);
2436 
2437 	for (i = 0; i < context->table_size; i++) {
2438 		if (struct file_descriptor *descriptor = context->fds[i]) {
2439 			close_fd(descriptor);
2440 			put_fd(descriptor);
2441 		}
2442 	}
2443 
2444 	mutex_destroy(&context->io_mutex);
2445 
2446 	free(context->fds);
2447 	free(context);
2448 
2449 	return FSSH_B_OK;
2450 }
2451 
2452 
2453 fssh_status_t
2454 vfs_init(kernel_args *args)
2455 {
2456 	sVnodeTable = hash_init(VNODE_HASH_TABLE_SIZE, fssh_offsetof(struct vnode, next),
2457 		&vnode_compare, &vnode_hash);
2458 	if (sVnodeTable == NULL)
2459 		fssh_panic("vfs_init: error creating vnode hash table\n");
2460 
2461 	list_init_etc(&sUnusedVnodeList, fssh_offsetof(struct vnode, unused_link));
2462 
2463 	sMountsTable = hash_init(MOUNTS_HASH_TABLE_SIZE, fssh_offsetof(struct fs_mount, next),
2464 		&mount_compare, &mount_hash);
2465 	if (sMountsTable == NULL)
2466 		fssh_panic("vfs_init: error creating mounts hash table\n");
2467 
2468 	sRoot = NULL;
2469 
2470 	if (mutex_init(&sFileSystemsMutex, "vfs_lock") < 0)
2471 		fssh_panic("vfs_init: error allocating file systems lock\n");
2472 
2473 	if (recursive_lock_init(&sMountOpLock, "vfs_mount_op_lock") < 0)
2474 		fssh_panic("vfs_init: error allocating mount op lock\n");
2475 
2476 	if (mutex_init(&sMountMutex, "vfs_mount_lock") < 0)
2477 		fssh_panic("vfs_init: error allocating mount lock\n");
2478 
2479 	if (mutex_init(&sVnodeCoveredByMutex, "vfs_vnode_covered_by_lock") < 0)
2480 		fssh_panic("vfs_init: error allocating vnode::covered_by lock\n");
2481 
2482 	if (mutex_init(&sVnodeMutex, "vfs_vnode_lock") < 0)
2483 		fssh_panic("vfs_init: error allocating vnode lock\n");
2484 
2485 	if (block_cache_init() != FSSH_B_OK)
2486 		return FSSH_B_ERROR;
2487 
2488 	return file_cache_init();
2489 }
2490 
2491 
2492 //	#pragma mark -
2493 //	The filetype-dependent implementations (fd_ops + open/create/rename/remove, ...)
2494 
2495 
2496 /** Calls fs_open() on the given vnode and returns a new
2497  *	file descriptor for it
2498  */
2499 
2500 static int
2501 create_vnode(struct vnode *directory, const char *name, int openMode, int perms, bool kernel)
2502 {
2503 	struct vnode *vnode;
2504 	fssh_fs_cookie cookie;
2505 	fssh_vnode_id newID;
2506 	int status;
2507 
2508 	if (FS_CALL(directory, create) == NULL)
2509 		return FSSH_EROFS;
2510 
2511 	status = FS_CALL(directory, create)(directory->mount->cookie, directory->private_node, name, openMode, perms, &cookie, &newID);
2512 	if (status < FSSH_B_OK)
2513 		return status;
2514 
2515 	mutex_lock(&sVnodeMutex);
2516 	vnode = lookup_vnode(directory->device, newID);
2517 	mutex_unlock(&sVnodeMutex);
2518 
2519 	if (vnode == NULL) {
2520 		fssh_dprintf("vfs: fs_create() returned success but there is no vnode!");
2521 		return FSSH_EINVAL;
2522 	}
2523 
2524 	if ((status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel)) >= 0)
2525 		return status;
2526 
2527 	// something went wrong, clean up
2528 
2529 	FS_CALL(vnode, close)(vnode->mount->cookie, vnode->private_node, cookie);
2530 	FS_CALL(vnode, free_cookie)(vnode->mount->cookie, vnode->private_node, cookie);
2531 	put_vnode(vnode);
2532 
2533 	FS_CALL(directory, unlink)(directory->mount->cookie, directory->private_node, name);
2534 
2535 	return status;
2536 }
2537 
2538 
2539 /** Calls fs_open() on the given vnode and returns a new
2540  *	file descriptor for it
2541  */
2542 
2543 static int
2544 open_vnode(struct vnode *vnode, int openMode, bool kernel)
2545 {
2546 	fssh_fs_cookie cookie;
2547 	int status;
2548 
2549 	status = FS_CALL(vnode, open)(vnode->mount->cookie, vnode->private_node, openMode, &cookie);
2550 	if (status < 0)
2551 		return status;
2552 
2553 	status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel);
2554 	if (status < 0) {
2555 		FS_CALL(vnode, close)(vnode->mount->cookie, vnode->private_node, cookie);
2556 		FS_CALL(vnode, free_cookie)(vnode->mount->cookie, vnode->private_node, cookie);
2557 	}
2558 	return status;
2559 }
2560 
2561 
2562 /** Calls fs open_dir() on the given vnode and returns a new
2563  *	file descriptor for it
2564  */
2565 
2566 static int
2567 open_dir_vnode(struct vnode *vnode, bool kernel)
2568 {
2569 	fssh_fs_cookie cookie;
2570 	int status;
2571 
2572 	status = FS_CALL(vnode, open_dir)(vnode->mount->cookie, vnode->private_node, &cookie);
2573 	if (status < FSSH_B_OK)
2574 		return status;
2575 
2576 	// file is opened, create a fd
2577 	status = get_new_fd(FDTYPE_DIR, NULL, vnode, cookie, 0, kernel);
2578 	if (status >= 0)
2579 		return status;
2580 
2581 	FS_CALL(vnode, close_dir)(vnode->mount->cookie, vnode->private_node, cookie);
2582 	FS_CALL(vnode, free_dir_cookie)(vnode->mount->cookie, vnode->private_node, cookie);
2583 
2584 	return status;
2585 }
2586 
2587 
2588 /** Calls fs open_attr_dir() on the given vnode and returns a new
2589  *	file descriptor for it.
2590  *	Used by attr_dir_open(), and attr_dir_open_fd().
2591  */
2592 
2593 static int
2594 open_attr_dir_vnode(struct vnode *vnode, bool kernel)
2595 {
2596 	fssh_fs_cookie cookie;
2597 	int status;
2598 
2599 	if (FS_CALL(vnode, open_attr_dir) == NULL)
2600 		return FSSH_EOPNOTSUPP;
2601 
2602 	status = FS_CALL(vnode, open_attr_dir)(vnode->mount->cookie, vnode->private_node, &cookie);
2603 	if (status < 0)
2604 		return status;
2605 
2606 	// file is opened, create a fd
2607 	status = get_new_fd(FDTYPE_ATTR_DIR, NULL, vnode, cookie, 0, kernel);
2608 	if (status >= 0)
2609 		return status;
2610 
2611 	FS_CALL(vnode, close_attr_dir)(vnode->mount->cookie, vnode->private_node, cookie);
2612 	FS_CALL(vnode, free_attr_dir_cookie)(vnode->mount->cookie, vnode->private_node, cookie);
2613 
2614 	return status;
2615 }
2616 
2617 
2618 static int
2619 file_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, int perms, bool kernel)
2620 {
2621 	struct vnode *directory;
2622 	int status;
2623 
2624 	FUNCTION(("file_create_entry_ref: name = '%s', omode %x, perms %d, kernel %d\n", name, openMode, perms, kernel));
2625 
2626 	// get directory to put the new file in
2627 	status = get_vnode(mountID, directoryID, &directory, false);
2628 	if (status < FSSH_B_OK)
2629 		return status;
2630 
2631 	status = create_vnode(directory, name, openMode, perms, kernel);
2632 	put_vnode(directory);
2633 
2634 	return status;
2635 }
2636 
2637 
2638 static int
2639 file_create(int fd, char *path, int openMode, int perms, bool kernel)
2640 {
2641 	char name[FSSH_B_FILE_NAME_LENGTH];
2642 	struct vnode *directory;
2643 	int status;
2644 
2645 	FUNCTION(("file_create: path '%s', omode %x, perms %d, kernel %d\n", path, openMode, perms, kernel));
2646 
2647 	// get directory to put the new file in
2648 	status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel);
2649 	if (status < 0)
2650 		return status;
2651 
2652 	status = create_vnode(directory, name, openMode, perms, kernel);
2653 
2654 	put_vnode(directory);
2655 	return status;
2656 }
2657 
2658 
2659 static int
2660 file_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, bool kernel)
2661 {
2662 	struct vnode *vnode;
2663 	int status;
2664 
2665 	if (name == NULL || *name == '\0')
2666 		return FSSH_B_BAD_VALUE;
2667 
2668 	FUNCTION(("file_open_entry_ref(ref = (%ld, %Ld, %s), openMode = %d)\n",
2669 		mountID, directoryID, name, openMode));
2670 
2671 	// get the vnode matching the entry_ref
2672 	status = entry_ref_to_vnode(mountID, directoryID, name, &vnode);
2673 	if (status < FSSH_B_OK)
2674 		return status;
2675 
2676 	status = open_vnode(vnode, openMode, kernel);
2677 	if (status < FSSH_B_OK)
2678 		put_vnode(vnode);
2679 
2680 	return status;
2681 }
2682 
2683 
2684 static int
2685 file_open(int fd, char *path, int openMode, bool kernel)
2686 {
2687 	int status = FSSH_B_OK;
2688 	bool traverse = ((openMode & FSSH_O_NOTRAVERSE) == 0);
2689 
2690 	FUNCTION(("file_open: fd: %d, entry path = '%s', omode %d, kernel %d\n",
2691 		fd, path, openMode, kernel));
2692 
2693 	// get the vnode matching the vnode + path combination
2694 	struct vnode *vnode = NULL;
2695 	fssh_vnode_id parentID;
2696 	status = fd_and_path_to_vnode(fd, path, traverse, &vnode, &parentID, kernel);
2697 	if (status != FSSH_B_OK)
2698 		return status;
2699 
2700 	// open the vnode
2701 	status = open_vnode(vnode, openMode, kernel);
2702 	// put only on error -- otherwise our reference was transferred to the FD
2703 	if (status < FSSH_B_OK)
2704 		put_vnode(vnode);
2705 
2706 	return status;
2707 }
2708 
2709 
2710 static fssh_status_t
2711 file_close(struct file_descriptor *descriptor)
2712 {
2713 	struct vnode *vnode = descriptor->u.vnode;
2714 	fssh_status_t status = FSSH_B_OK;
2715 
2716 	FUNCTION(("file_close(descriptor = %p)\n", descriptor));
2717 
2718 	if (FS_CALL(vnode, close))
2719 		status = FS_CALL(vnode, close)(vnode->mount->cookie, vnode->private_node, descriptor->cookie);
2720 
2721 	return status;
2722 }
2723 
2724 
2725 static void
2726 file_free_fd(struct file_descriptor *descriptor)
2727 {
2728 	struct vnode *vnode = descriptor->u.vnode;
2729 
2730 	if (vnode != NULL) {
2731 		FS_CALL(vnode, free_cookie)(vnode->mount->cookie, vnode->private_node, descriptor->cookie);
2732 		put_vnode(vnode);
2733 	}
2734 }
2735 
2736 
2737 static fssh_status_t
2738 file_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length)
2739 {
2740 	struct vnode *vnode = descriptor->u.vnode;
2741 
2742 	FUNCTION(("file_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length));
2743 	return FS_CALL(vnode, read)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, pos, buffer, length);
2744 }
2745 
2746 
2747 static fssh_status_t
2748 file_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length)
2749 {
2750 	struct vnode *vnode = descriptor->u.vnode;
2751 
2752 	FUNCTION(("file_write: buf %p, pos %Ld, len %p\n", buffer, pos, length));
2753 	return FS_CALL(vnode, write)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, pos, buffer, length);
2754 }
2755 
2756 
2757 static fssh_off_t
2758 file_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType)
2759 {
2760 	fssh_off_t offset;
2761 
2762 	FUNCTION(("file_seek(pos = %Ld, seekType = %d)\n", pos, seekType));
2763 	// ToDo: seek should fail for pipes and FIFOs...
2764 
2765 	switch (seekType) {
2766 		case FSSH_SEEK_SET:
2767 			offset = 0;
2768 			break;
2769 		case FSSH_SEEK_CUR:
2770 			offset = descriptor->pos;
2771 			break;
2772 		case FSSH_SEEK_END:
2773 		{
2774 			struct vnode *vnode = descriptor->u.vnode;
2775 			struct fssh_stat stat;
2776 			fssh_status_t status;
2777 
2778 			if (FS_CALL(vnode, read_stat) == NULL)
2779 				return FSSH_EOPNOTSUPP;
2780 
2781 			status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, vnode->private_node, &stat);
2782 			if (status < FSSH_B_OK)
2783 				return status;
2784 
2785 			offset = stat.fssh_st_size;
2786 			break;
2787 		}
2788 		default:
2789 			return FSSH_B_BAD_VALUE;
2790 	}
2791 
2792 	// assumes fssh_off_t is 64 bits wide
2793 	if (offset > 0 && LLONG_MAX - offset < pos)
2794 		return FSSH_EOVERFLOW;
2795 
2796 	pos += offset;
2797 	if (pos < 0)
2798 		return FSSH_B_BAD_VALUE;
2799 
2800 	return descriptor->pos = pos;
2801 }
2802 
2803 
2804 static fssh_status_t
2805 dir_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, int perms, bool kernel)
2806 {
2807 	struct vnode *vnode;
2808 	fssh_vnode_id newID;
2809 	fssh_status_t status;
2810 
2811 	if (name == NULL || *name == '\0')
2812 		return FSSH_B_BAD_VALUE;
2813 
2814 	FUNCTION(("dir_create_entry_ref(dev = %ld, ino = %Ld, name = '%s', perms = %d)\n", mountID, parentID, name, perms));
2815 
2816 	status = get_vnode(mountID, parentID, &vnode, kernel);
2817 	if (status < FSSH_B_OK)
2818 		return status;
2819 
2820 	if (FS_CALL(vnode, create_dir))
2821 		status = FS_CALL(vnode, create_dir)(vnode->mount->cookie, vnode->private_node, name, perms, &newID);
2822 	else
2823 		status = FSSH_EROFS;
2824 
2825 	put_vnode(vnode);
2826 	return status;
2827 }
2828 
2829 
2830 static fssh_status_t
2831 dir_create(int fd, char *path, int perms, bool kernel)
2832 {
2833 	char filename[FSSH_B_FILE_NAME_LENGTH];
2834 	struct vnode *vnode;
2835 	fssh_vnode_id newID;
2836 	fssh_status_t status;
2837 
2838 	FUNCTION(("dir_create: path '%s', perms %d, kernel %d\n", path, perms, kernel));
2839 
2840 	status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel);
2841 	if (status < 0)
2842 		return status;
2843 
2844 	if (FS_CALL(vnode, create_dir))
2845 		status = FS_CALL(vnode, create_dir)(vnode->mount->cookie, vnode->private_node, filename, perms, &newID);
2846 	else
2847 		status = FSSH_EROFS;
2848 
2849 	put_vnode(vnode);
2850 	return status;
2851 }
2852 
2853 
2854 static int
2855 dir_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, bool kernel)
2856 {
2857 	struct vnode *vnode;
2858 	int status;
2859 
2860 	FUNCTION(("dir_open_entry_ref()\n"));
2861 
2862 	if (name && *name == '\0')
2863 		return FSSH_B_BAD_VALUE;
2864 
2865 	// get the vnode matching the entry_ref/node_ref
2866 	if (name)
2867 		status = entry_ref_to_vnode(mountID, parentID, name, &vnode);
2868 	else
2869 		status = get_vnode(mountID, parentID, &vnode, false);
2870 	if (status < FSSH_B_OK)
2871 		return status;
2872 
2873 	status = open_dir_vnode(vnode, kernel);
2874 	if (status < FSSH_B_OK)
2875 		put_vnode(vnode);
2876 
2877 	return status;
2878 }
2879 
2880 
2881 static int
2882 dir_open(int fd, char *path, bool kernel)
2883 {
2884 	int status = FSSH_B_OK;
2885 
2886 	FUNCTION(("dir_open: fd: %d, entry path = '%s', kernel %d\n", fd, path, kernel));
2887 
2888 	// get the vnode matching the vnode + path combination
2889 	struct vnode *vnode = NULL;
2890 	fssh_vnode_id parentID;
2891 	status = fd_and_path_to_vnode(fd, path, true, &vnode, &parentID, kernel);
2892 	if (status != FSSH_B_OK)
2893 		return status;
2894 
2895 	// open the dir
2896 	status = open_dir_vnode(vnode, kernel);
2897 	if (status < FSSH_B_OK)
2898 		put_vnode(vnode);
2899 
2900 	return status;
2901 }
2902 
2903 
2904 static fssh_status_t
2905 dir_close(struct file_descriptor *descriptor)
2906 {
2907 	struct vnode *vnode = descriptor->u.vnode;
2908 
2909 	FUNCTION(("dir_close(descriptor = %p)\n", descriptor));
2910 
2911 	if (FS_CALL(vnode, close_dir))
2912 		return FS_CALL(vnode, close_dir)(vnode->mount->cookie, vnode->private_node, descriptor->cookie);
2913 
2914 	return FSSH_B_OK;
2915 }
2916 
2917 
2918 static void
2919 dir_free_fd(struct file_descriptor *descriptor)
2920 {
2921 	struct vnode *vnode = descriptor->u.vnode;
2922 
2923 	if (vnode != NULL) {
2924 		FS_CALL(vnode, free_dir_cookie)(vnode->mount->cookie, vnode->private_node, descriptor->cookie);
2925 		put_vnode(vnode);
2926 	}
2927 }
2928 
2929 
2930 static fssh_status_t
2931 dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
2932 	fssh_size_t bufferSize, uint32_t *_count)
2933 {
2934 	return dir_read(descriptor->u.vnode, descriptor->cookie, buffer, bufferSize, _count);
2935 }
2936 
2937 
2938 static void
2939 fix_dirent(struct vnode *parent, struct fssh_dirent *entry)
2940 {
2941 	// set d_pdev and d_pino
2942 	entry->d_pdev = parent->device;
2943 	entry->d_pino = parent->id;
2944 
2945 	// If this is the ".." entry and the directory is the root of a FS,
2946 	// we need to replace d_dev and d_ino with the actual values.
2947 	if (fssh_strcmp(entry->d_name, "..") == 0
2948 		&& parent->mount->root_vnode == parent
2949 		&& parent->mount->covers_vnode) {
2950 		inc_vnode_ref_count(parent);
2951 			// vnode_path_to_vnode() puts the node
2952 
2953 		struct vnode *vnode;
2954 		fssh_status_t status = vnode_path_to_vnode(parent, "..", false, 0, &vnode,
2955 			NULL, NULL);
2956 
2957 		if (status == FSSH_B_OK) {
2958 			entry->d_dev = vnode->device;
2959 			entry->d_ino = vnode->id;
2960 		}
2961 	} else {
2962 		// resolve mount points
2963 		struct vnode *vnode = NULL;
2964 		fssh_status_t status = get_vnode(entry->d_dev, entry->d_ino, &vnode, false);
2965 		if (status != FSSH_B_OK)
2966 			return;
2967 
2968 		mutex_lock(&sVnodeCoveredByMutex);
2969 		if (vnode->covered_by) {
2970 			entry->d_dev = vnode->covered_by->device;
2971 			entry->d_ino = vnode->covered_by->id;
2972 		}
2973 		mutex_unlock(&sVnodeCoveredByMutex);
2974 
2975 		put_vnode(vnode);
2976 	}
2977 }
2978 
2979 
2980 static fssh_status_t
2981 dir_read(struct vnode *vnode, fssh_fs_cookie cookie, struct fssh_dirent *buffer,
2982 	fssh_size_t bufferSize, uint32_t *_count)
2983 {
2984 	if (!FS_CALL(vnode, read_dir))
2985 		return FSSH_EOPNOTSUPP;
2986 
2987 	fssh_status_t error = FS_CALL(vnode, read_dir)(vnode->mount->cookie,vnode->private_node,cookie,buffer,bufferSize,_count);
2988 	if (error != FSSH_B_OK)
2989 		return error;
2990 
2991 	// we need to adjust the read dirents
2992 	if (*_count > 0) {
2993 		// XXX: Currently reading only one dirent is supported. Make this a loop!
2994 		fix_dirent(vnode, buffer);
2995 	}
2996 
2997 	return error;
2998 }
2999 
3000 
3001 static fssh_status_t
3002 dir_rewind(struct file_descriptor *descriptor)
3003 {
3004 	struct vnode *vnode = descriptor->u.vnode;
3005 
3006 	if (FS_CALL(vnode, rewind_dir))
3007 		return FS_CALL(vnode, rewind_dir)(vnode->mount->cookie,vnode->private_node,descriptor->cookie);
3008 
3009 	return FSSH_EOPNOTSUPP;
3010 }
3011 
3012 
3013 static fssh_status_t
3014 dir_remove(int fd, char *path, bool kernel)
3015 {
3016 	char name[FSSH_B_FILE_NAME_LENGTH];
3017 	struct vnode *directory;
3018 	fssh_status_t status;
3019 
3020 	if (path != NULL) {
3021 		// we need to make sure our path name doesn't stop with "/", ".", or ".."
3022 		char *lastSlash = fssh_strrchr(path, '/');
3023 		if (lastSlash != NULL) {
3024 			char *leaf = lastSlash + 1;
3025 			if (!fssh_strcmp(leaf, ".."))
3026 				return FSSH_B_NOT_ALLOWED;
3027 
3028 			// omit multiple slashes
3029 			while (lastSlash > path && lastSlash[-1] == '/') {
3030 				lastSlash--;
3031 			}
3032 
3033 			if (!leaf[0]
3034 				|| !fssh_strcmp(leaf, ".")) {
3035 				// "name/" -> "name", or "name/." -> "name"
3036 				lastSlash[0] = '\0';
3037 			}
3038 		} else if (!fssh_strcmp(path, ".."))
3039 			return FSSH_B_NOT_ALLOWED;
3040 	}
3041 
3042 	status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel);
3043 	if (status < FSSH_B_OK)
3044 		return status;
3045 
3046 	if (FS_CALL(directory, remove_dir)) {
3047 		status = FS_CALL(directory, remove_dir)(directory->mount->cookie,
3048 			directory->private_node, name);
3049 	} else
3050 		status = FSSH_EROFS;
3051 
3052 	put_vnode(directory);
3053 	return status;
3054 }
3055 
3056 
3057 static fssh_status_t
3058 common_ioctl(struct file_descriptor *descriptor, uint32_t op, void *buffer,
3059 	fssh_size_t length)
3060 {
3061 	struct vnode *vnode = descriptor->u.vnode;
3062 
3063 	if (FS_CALL(vnode, ioctl)) {
3064 		return FS_CALL(vnode, ioctl)(vnode->mount->cookie, vnode->private_node,
3065 			descriptor->cookie, op, buffer, length);
3066 	}
3067 
3068 	return FSSH_EOPNOTSUPP;
3069 }
3070 
3071 
3072 static fssh_status_t
3073 common_fcntl(int fd, int op, uint32_t argument, bool kernel)
3074 {
3075 	struct file_descriptor *descriptor;
3076 	struct vnode *vnode;
3077 	fssh_status_t status;
3078 
3079 	FUNCTION(("common_fcntl(fd = %d, op = %d, argument = %lx, %s)\n",
3080 		fd, op, argument, kernel ? "kernel" : "user"));
3081 
3082 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3083 	if (descriptor == NULL)
3084 		return FSSH_B_FILE_ERROR;
3085 
3086 	switch (op) {
3087 		case FSSH_F_SETFD:
3088 		{
3089 			struct io_context *context = get_current_io_context(kernel);
3090 			// Set file descriptor flags
3091 
3092 			// FSSH_O_CLOEXEC is the only flag available at this time
3093 			mutex_lock(&context->io_mutex);
3094 			fd_set_close_on_exec(context, fd, argument == FSSH_FD_CLOEXEC);
3095 			mutex_unlock(&context->io_mutex);
3096 
3097 			status = FSSH_B_OK;
3098 			break;
3099 		}
3100 
3101 		case FSSH_F_GETFD:
3102 		{
3103 			struct io_context *context = get_current_io_context(kernel);
3104 
3105 			// Get file descriptor flags
3106 			mutex_lock(&context->io_mutex);
3107 			status = fd_close_on_exec(context, fd) ? FSSH_FD_CLOEXEC : 0;
3108 			mutex_unlock(&context->io_mutex);
3109 			break;
3110 		}
3111 
3112 		case FSSH_F_SETFL:
3113 			// Set file descriptor open mode
3114 			if (FS_CALL(vnode, set_flags)) {
3115 				// we only accept changes to FSSH_O_APPEND and FSSH_O_NONBLOCK
3116 				argument &= FSSH_O_APPEND | FSSH_O_NONBLOCK;
3117 
3118 				status = FS_CALL(vnode, set_flags)(vnode->mount->cookie,
3119 					vnode->private_node, descriptor->cookie, (int)argument);
3120 				if (status == FSSH_B_OK) {
3121 					// update this descriptor's open_mode field
3122 					descriptor->open_mode = (descriptor->open_mode & ~(FSSH_O_APPEND | FSSH_O_NONBLOCK))
3123 						| argument;
3124 				}
3125 			} else
3126 				status = FSSH_EOPNOTSUPP;
3127 			break;
3128 
3129 		case FSSH_F_GETFL:
3130 			// Get file descriptor open mode
3131 			status = descriptor->open_mode;
3132 			break;
3133 
3134 		case FSSH_F_DUPFD:
3135 		{
3136 			struct io_context *context = get_current_io_context(kernel);
3137 
3138 			status = new_fd_etc(context, descriptor, (int)argument);
3139 			if (status >= 0) {
3140 				mutex_lock(&context->io_mutex);
3141 				fd_set_close_on_exec(context, fd, false);
3142 				mutex_unlock(&context->io_mutex);
3143 
3144 				fssh_atomic_add(&descriptor->ref_count, 1);
3145 			}
3146 			break;
3147 		}
3148 
3149 		case FSSH_F_GETLK:
3150 		case FSSH_F_SETLK:
3151 		case FSSH_F_SETLKW:
3152 			status = FSSH_B_BAD_VALUE;
3153 			break;
3154 
3155 		// ToDo: add support for more ops?
3156 
3157 		default:
3158 			status = FSSH_B_BAD_VALUE;
3159 	}
3160 
3161 	put_fd(descriptor);
3162 	return status;
3163 }
3164 
3165 
3166 static fssh_status_t
3167 common_sync(int fd, bool kernel)
3168 {
3169 	struct file_descriptor *descriptor;
3170 	struct vnode *vnode;
3171 	fssh_status_t status;
3172 
3173 	FUNCTION(("common_fsync: entry. fd %d kernel %d\n", fd, kernel));
3174 
3175 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3176 	if (descriptor == NULL)
3177 		return FSSH_B_FILE_ERROR;
3178 
3179 	if (FS_CALL(vnode, fsync) != NULL)
3180 		status = FS_CALL(vnode, fsync)(vnode->mount->cookie, vnode->private_node);
3181 	else
3182 		status = FSSH_EOPNOTSUPP;
3183 
3184 	put_fd(descriptor);
3185 	return status;
3186 }
3187 
3188 
3189 static fssh_status_t
3190 common_lock_node(int fd, bool kernel)
3191 {
3192 	struct file_descriptor *descriptor;
3193 	struct vnode *vnode;
3194 
3195 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3196 	if (descriptor == NULL)
3197 		return FSSH_B_FILE_ERROR;
3198 
3199 	fssh_status_t status = FSSH_B_OK;
3200 
3201 	// We need to set the locking atomically - someone
3202 	// else might set one at the same time
3203 #ifdef __x86_64__
3204 	if (fssh_atomic_test_and_set64((vint64_t *)&vnode->mandatory_locked_by,
3205 			(fssh_addr_t)descriptor, 0) != 0)
3206 #else
3207 	if (fssh_atomic_test_and_set((vint32_t *)&vnode->mandatory_locked_by,
3208 			(fssh_addr_t)descriptor, 0) != 0)
3209 #endif
3210 		status = FSSH_B_BUSY;
3211 
3212 	put_fd(descriptor);
3213 	return status;
3214 }
3215 
3216 
3217 static fssh_status_t
3218 common_unlock_node(int fd, bool kernel)
3219 {
3220 	struct file_descriptor *descriptor;
3221 	struct vnode *vnode;
3222 
3223 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3224 	if (descriptor == NULL)
3225 		return FSSH_B_FILE_ERROR;
3226 
3227 	fssh_status_t status = FSSH_B_OK;
3228 
3229 	// We need to set the locking atomically - someone
3230 	// else might set one at the same time
3231 #ifdef __x86_64__
3232 	if (fssh_atomic_test_and_set64((vint64_t *)&vnode->mandatory_locked_by,
3233 			0, (fssh_addr_t)descriptor) != (int64_t)descriptor)
3234 #else
3235 	if (fssh_atomic_test_and_set((vint32_t *)&vnode->mandatory_locked_by,
3236 			0, (fssh_addr_t)descriptor) != (int32_t)descriptor)
3237 #endif
3238 		status = FSSH_B_BAD_VALUE;
3239 
3240 	put_fd(descriptor);
3241 	return status;
3242 }
3243 
3244 
3245 static fssh_status_t
3246 common_read_link(int fd, char *path, char *buffer, fssh_size_t *_bufferSize,
3247 	bool kernel)
3248 {
3249 	struct vnode *vnode;
3250 	fssh_status_t status;
3251 
3252 	status = fd_and_path_to_vnode(fd, path, false, &vnode, NULL, kernel);
3253 	if (status < FSSH_B_OK)
3254 		return status;
3255 
3256 	if (FS_CALL(vnode, read_symlink) != NULL) {
3257 		status = FS_CALL(vnode, read_symlink)(vnode->mount->cookie,
3258 			vnode->private_node, buffer, _bufferSize);
3259 	} else
3260 		status = FSSH_B_BAD_VALUE;
3261 
3262 	put_vnode(vnode);
3263 	return status;
3264 }
3265 
3266 
3267 static fssh_status_t
3268 common_create_symlink(int fd, char *path, const char *toPath, int mode,
3269 	bool kernel)
3270 {
3271 	// path validity checks have to be in the calling function!
3272 	char name[FSSH_B_FILE_NAME_LENGTH];
3273 	struct vnode *vnode;
3274 	fssh_status_t status;
3275 
3276 	FUNCTION(("common_create_symlink(fd = %d, path = %s, toPath = %s, mode = %d, kernel = %d)\n", fd, path, toPath, mode, kernel));
3277 
3278 	status = fd_and_path_to_dir_vnode(fd, path, &vnode, name, kernel);
3279 	if (status < FSSH_B_OK)
3280 		return status;
3281 
3282 	if (FS_CALL(vnode, create_symlink) != NULL)
3283 		status = FS_CALL(vnode, create_symlink)(vnode->mount->cookie, vnode->private_node, name, toPath, mode);
3284 	else
3285 		status = FSSH_EROFS;
3286 
3287 	put_vnode(vnode);
3288 
3289 	return status;
3290 }
3291 
3292 
3293 static fssh_status_t
3294 common_create_link(char *path, char *toPath, bool kernel)
3295 {
3296 	// path validity checks have to be in the calling function!
3297 	char name[FSSH_B_FILE_NAME_LENGTH];
3298 	struct vnode *directory, *vnode;
3299 	fssh_status_t status;
3300 
3301 	FUNCTION(("common_create_link(path = %s, toPath = %s, kernel = %d)\n", path, toPath, kernel));
3302 
3303 	status = path_to_dir_vnode(path, &directory, name, kernel);
3304 	if (status < FSSH_B_OK)
3305 		return status;
3306 
3307 	status = path_to_vnode(toPath, true, &vnode, NULL, kernel);
3308 	if (status < FSSH_B_OK)
3309 		goto err;
3310 
3311 	if (directory->mount != vnode->mount) {
3312 		status = FSSH_B_CROSS_DEVICE_LINK;
3313 		goto err1;
3314 	}
3315 
3316 	if (FS_CALL(vnode, link) != NULL)
3317 		status = FS_CALL(vnode, link)(directory->mount->cookie, directory->private_node, name, vnode->private_node);
3318 	else
3319 		status = FSSH_EROFS;
3320 
3321 err1:
3322 	put_vnode(vnode);
3323 err:
3324 	put_vnode(directory);
3325 
3326 	return status;
3327 }
3328 
3329 
3330 static fssh_status_t
3331 common_unlink(int fd, char *path, bool kernel)
3332 {
3333 	char filename[FSSH_B_FILE_NAME_LENGTH];
3334 	struct vnode *vnode;
3335 	fssh_status_t status;
3336 
3337 	FUNCTION(("common_unlink: fd: %d, path '%s', kernel %d\n", fd, path, kernel));
3338 
3339 	status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel);
3340 	if (status < 0)
3341 		return status;
3342 
3343 	if (FS_CALL(vnode, unlink) != NULL)
3344 		status = FS_CALL(vnode, unlink)(vnode->mount->cookie, vnode->private_node, filename);
3345 	else
3346 		status = FSSH_EROFS;
3347 
3348 	put_vnode(vnode);
3349 
3350 	return status;
3351 }
3352 
3353 
3354 static fssh_status_t
3355 common_access(char *path, int mode, bool kernel)
3356 {
3357 	struct vnode *vnode;
3358 	fssh_status_t status;
3359 
3360 	status = path_to_vnode(path, true, &vnode, NULL, kernel);
3361 	if (status < FSSH_B_OK)
3362 		return status;
3363 
3364 	if (FS_CALL(vnode, access) != NULL)
3365 		status = FS_CALL(vnode, access)(vnode->mount->cookie, vnode->private_node, mode);
3366 	else
3367 		status = FSSH_B_OK;
3368 
3369 	put_vnode(vnode);
3370 
3371 	return status;
3372 }
3373 
3374 
3375 static fssh_status_t
3376 common_rename(int fd, char *path, int newFD, char *newPath, bool kernel)
3377 {
3378 	struct vnode *fromVnode, *toVnode;
3379 	char fromName[FSSH_B_FILE_NAME_LENGTH];
3380 	char toName[FSSH_B_FILE_NAME_LENGTH];
3381 	fssh_status_t status;
3382 
3383 	FUNCTION(("common_rename(fd = %d, path = %s, newFD = %d, newPath = %s, kernel = %d)\n", fd, path, newFD, newPath, kernel));
3384 
3385 	status = fd_and_path_to_dir_vnode(fd, path, &fromVnode, fromName, kernel);
3386 	if (status < 0)
3387 		return status;
3388 
3389 	status = fd_and_path_to_dir_vnode(newFD, newPath, &toVnode, toName, kernel);
3390 	if (status < 0)
3391 		goto err;
3392 
3393 	if (fromVnode->device != toVnode->device) {
3394 		status = FSSH_B_CROSS_DEVICE_LINK;
3395 		goto err1;
3396 	}
3397 
3398 	if (FS_CALL(fromVnode, rename) != NULL)
3399 		status = FS_CALL(fromVnode, rename)(fromVnode->mount->cookie, fromVnode->private_node, fromName, toVnode->private_node, toName);
3400 	else
3401 		status = FSSH_EROFS;
3402 
3403 err1:
3404 	put_vnode(toVnode);
3405 err:
3406 	put_vnode(fromVnode);
3407 
3408 	return status;
3409 }
3410 
3411 
3412 static fssh_status_t
3413 common_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat)
3414 {
3415 	struct vnode *vnode = descriptor->u.vnode;
3416 
3417 	FUNCTION(("common_read_stat: stat %p\n", stat));
3418 
3419 	fssh_status_t status = FS_CALL(vnode, read_stat)(vnode->mount->cookie,
3420 		vnode->private_node, stat);
3421 
3422 	// fill in the st_dev and st_ino fields
3423 	if (status == FSSH_B_OK) {
3424 		stat->fssh_st_dev = vnode->device;
3425 		stat->fssh_st_ino = vnode->id;
3426 	}
3427 
3428 	return status;
3429 }
3430 
3431 
3432 static fssh_status_t
3433 common_write_stat(struct file_descriptor *descriptor,
3434 	const struct fssh_stat *stat, int statMask)
3435 {
3436 	struct vnode *vnode = descriptor->u.vnode;
3437 
3438 	FUNCTION(("common_write_stat(vnode = %p, stat = %p, statMask = %d)\n", vnode, stat, statMask));
3439 	if (!FS_CALL(vnode, write_stat))
3440 		return FSSH_EROFS;
3441 
3442 	return FS_CALL(vnode, write_stat)(vnode->mount->cookie, vnode->private_node, stat, statMask);
3443 }
3444 
3445 
3446 static fssh_status_t
3447 common_path_read_stat(int fd, char *path, bool traverseLeafLink,
3448 	struct fssh_stat *stat, bool kernel)
3449 {
3450 	struct vnode *vnode;
3451 	fssh_status_t status;
3452 
3453 	FUNCTION(("common_path_read_stat: fd: %d, path '%s', stat %p,\n", fd, path, stat));
3454 
3455 	status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel);
3456 	if (status < 0)
3457 		return status;
3458 
3459 	status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, vnode->private_node, stat);
3460 
3461 	// fill in the st_dev and st_ino fields
3462 	if (status == FSSH_B_OK) {
3463 		stat->fssh_st_dev = vnode->device;
3464 		stat->fssh_st_ino = vnode->id;
3465 	}
3466 
3467 	put_vnode(vnode);
3468 	return status;
3469 }
3470 
3471 
3472 static fssh_status_t
3473 common_path_write_stat(int fd, char *path, bool traverseLeafLink,
3474 	const struct fssh_stat *stat, int statMask, bool kernel)
3475 {
3476 	struct vnode *vnode;
3477 	fssh_status_t status;
3478 
3479 	FUNCTION(("common_write_stat: fd: %d, path '%s', stat %p, stat_mask %d, kernel %d\n", fd, path, stat, statMask, kernel));
3480 
3481 	status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel);
3482 	if (status < 0)
3483 		return status;
3484 
3485 	if (FS_CALL(vnode, write_stat))
3486 		status = FS_CALL(vnode, write_stat)(vnode->mount->cookie, vnode->private_node, stat, statMask);
3487 	else
3488 		status = FSSH_EROFS;
3489 
3490 	put_vnode(vnode);
3491 
3492 	return status;
3493 }
3494 
3495 
3496 static int
3497 attr_dir_open(int fd, char *path, bool kernel)
3498 {
3499 	struct vnode *vnode;
3500 	int status;
3501 
3502 	FUNCTION(("attr_dir_open(fd = %d, path = '%s', kernel = %d)\n", fd, path, kernel));
3503 
3504 	status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel);
3505 	if (status < FSSH_B_OK)
3506 		return status;
3507 
3508 	status = open_attr_dir_vnode(vnode, kernel);
3509 	if (status < 0)
3510 		put_vnode(vnode);
3511 
3512 	return status;
3513 }
3514 
3515 
3516 static fssh_status_t
3517 attr_dir_close(struct file_descriptor *descriptor)
3518 {
3519 	struct vnode *vnode = descriptor->u.vnode;
3520 
3521 	FUNCTION(("attr_dir_close(descriptor = %p)\n", descriptor));
3522 
3523 	if (FS_CALL(vnode, close_attr_dir))
3524 		return FS_CALL(vnode, close_attr_dir)(vnode->mount->cookie, vnode->private_node, descriptor->cookie);
3525 
3526 	return FSSH_B_OK;
3527 }
3528 
3529 
3530 static void
3531 attr_dir_free_fd(struct file_descriptor *descriptor)
3532 {
3533 	struct vnode *vnode = descriptor->u.vnode;
3534 
3535 	if (vnode != NULL) {
3536 		FS_CALL(vnode, free_attr_dir_cookie)(vnode->mount->cookie, vnode->private_node, descriptor->cookie);
3537 		put_vnode(vnode);
3538 	}
3539 }
3540 
3541 
3542 static fssh_status_t
3543 attr_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
3544 	fssh_size_t bufferSize, uint32_t *_count)
3545 {
3546 	struct vnode *vnode = descriptor->u.vnode;
3547 
3548 	FUNCTION(("attr_dir_read(descriptor = %p)\n", descriptor));
3549 
3550 	if (FS_CALL(vnode, read_attr_dir))
3551 		return FS_CALL(vnode, read_attr_dir)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, buffer, bufferSize, _count);
3552 
3553 	return FSSH_EOPNOTSUPP;
3554 }
3555 
3556 
3557 static fssh_status_t
3558 attr_dir_rewind(struct file_descriptor *descriptor)
3559 {
3560 	struct vnode *vnode = descriptor->u.vnode;
3561 
3562 	FUNCTION(("attr_dir_rewind(descriptor = %p)\n", descriptor));
3563 
3564 	if (FS_CALL(vnode, rewind_attr_dir))
3565 		return FS_CALL(vnode, rewind_attr_dir)(vnode->mount->cookie, vnode->private_node, descriptor->cookie);
3566 
3567 	return FSSH_EOPNOTSUPP;
3568 }
3569 
3570 
3571 static int
3572 attr_create(int fd, const char *name, uint32_t type, int openMode, bool kernel)
3573 {
3574 	struct vnode *vnode;
3575 	fssh_fs_cookie cookie;
3576 	int status;
3577 
3578 	if (name == NULL || *name == '\0')
3579 		return FSSH_B_BAD_VALUE;
3580 
3581 	vnode = get_vnode_from_fd(fd, kernel);
3582 	if (vnode == NULL)
3583 		return FSSH_B_FILE_ERROR;
3584 
3585 	if (FS_CALL(vnode, create_attr) == NULL) {
3586 		status = FSSH_EROFS;
3587 		goto err;
3588 	}
3589 
3590 	status = FS_CALL(vnode, create_attr)(vnode->mount->cookie, vnode->private_node, name, type, openMode, &cookie);
3591 	if (status < FSSH_B_OK)
3592 		goto err;
3593 
3594 	if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0)
3595 		return status;
3596 
3597 	FS_CALL(vnode, close_attr)(vnode->mount->cookie, vnode->private_node, cookie);
3598 	FS_CALL(vnode, free_attr_cookie)(vnode->mount->cookie, vnode->private_node, cookie);
3599 
3600 	FS_CALL(vnode, remove_attr)(vnode->mount->cookie, vnode->private_node, name);
3601 
3602 err:
3603 	put_vnode(vnode);
3604 
3605 	return status;
3606 }
3607 
3608 
3609 static int
3610 attr_open(int fd, const char *name, int openMode, bool kernel)
3611 {
3612 	struct vnode *vnode;
3613 	fssh_fs_cookie cookie;
3614 	int status;
3615 
3616 	if (name == NULL || *name == '\0')
3617 		return FSSH_B_BAD_VALUE;
3618 
3619 	vnode = get_vnode_from_fd(fd, kernel);
3620 	if (vnode == NULL)
3621 		return FSSH_B_FILE_ERROR;
3622 
3623 	if (FS_CALL(vnode, open_attr) == NULL) {
3624 		status = FSSH_EOPNOTSUPP;
3625 		goto err;
3626 	}
3627 
3628 	status = FS_CALL(vnode, open_attr)(vnode->mount->cookie, vnode->private_node, name, openMode, &cookie);
3629 	if (status < FSSH_B_OK)
3630 		goto err;
3631 
3632 	// now we only need a file descriptor for this attribute and we're done
3633 	if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0)
3634 		return status;
3635 
3636 	FS_CALL(vnode, close_attr)(vnode->mount->cookie, vnode->private_node, cookie);
3637 	FS_CALL(vnode, free_attr_cookie)(vnode->mount->cookie, vnode->private_node, cookie);
3638 
3639 err:
3640 	put_vnode(vnode);
3641 
3642 	return status;
3643 }
3644 
3645 
3646 static fssh_status_t
3647 attr_close(struct file_descriptor *descriptor)
3648 {
3649 	struct vnode *vnode = descriptor->u.vnode;
3650 
3651 	FUNCTION(("attr_close(descriptor = %p)\n", descriptor));
3652 
3653 	if (FS_CALL(vnode, close_attr))
3654 		return FS_CALL(vnode, close_attr)(vnode->mount->cookie, vnode->private_node, descriptor->cookie);
3655 
3656 	return FSSH_B_OK;
3657 }
3658 
3659 
3660 static void
3661 attr_free_fd(struct file_descriptor *descriptor)
3662 {
3663 	struct vnode *vnode = descriptor->u.vnode;
3664 
3665 	if (vnode != NULL) {
3666 		FS_CALL(vnode, free_attr_cookie)(vnode->mount->cookie, vnode->private_node, descriptor->cookie);
3667 		put_vnode(vnode);
3668 	}
3669 }
3670 
3671 
3672 static fssh_status_t
3673 attr_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length)
3674 {
3675 	struct vnode *vnode = descriptor->u.vnode;
3676 
3677 	FUNCTION(("attr_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length));
3678 	if (!FS_CALL(vnode, read_attr))
3679 		return FSSH_EOPNOTSUPP;
3680 
3681 	return FS_CALL(vnode, read_attr)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, pos, buffer, length);
3682 }
3683 
3684 
3685 static fssh_status_t
3686 attr_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length)
3687 {
3688 	struct vnode *vnode = descriptor->u.vnode;
3689 
3690 	FUNCTION(("attr_write: buf %p, pos %Ld, len %p\n", buffer, pos, length));
3691 	if (!FS_CALL(vnode, write_attr))
3692 		return FSSH_EOPNOTSUPP;
3693 
3694 	return FS_CALL(vnode, write_attr)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, pos, buffer, length);
3695 }
3696 
3697 
3698 static fssh_off_t
3699 attr_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType)
3700 {
3701 	fssh_off_t offset;
3702 
3703 	switch (seekType) {
3704 		case FSSH_SEEK_SET:
3705 			offset = 0;
3706 			break;
3707 		case FSSH_SEEK_CUR:
3708 			offset = descriptor->pos;
3709 			break;
3710 		case FSSH_SEEK_END:
3711 		{
3712 			struct vnode *vnode = descriptor->u.vnode;
3713 			struct fssh_stat stat;
3714 			fssh_status_t status;
3715 
3716 			if (FS_CALL(vnode, read_stat) == NULL)
3717 				return FSSH_EOPNOTSUPP;
3718 
3719 			status = FS_CALL(vnode, read_attr_stat)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, &stat);
3720 			if (status < FSSH_B_OK)
3721 				return status;
3722 
3723 			offset = stat.fssh_st_size;
3724 			break;
3725 		}
3726 		default:
3727 			return FSSH_B_BAD_VALUE;
3728 	}
3729 
3730 	// assumes fssh_off_t is 64 bits wide
3731 	if (offset > 0 && LLONG_MAX - offset < pos)
3732 		return FSSH_EOVERFLOW;
3733 
3734 	pos += offset;
3735 	if (pos < 0)
3736 		return FSSH_B_BAD_VALUE;
3737 
3738 	return descriptor->pos = pos;
3739 }
3740 
3741 
3742 static fssh_status_t
3743 attr_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat)
3744 {
3745 	struct vnode *vnode = descriptor->u.vnode;
3746 
3747 	FUNCTION(("attr_read_stat: stat 0x%p\n", stat));
3748 
3749 	if (!FS_CALL(vnode, read_attr_stat))
3750 		return FSSH_EOPNOTSUPP;
3751 
3752 	return FS_CALL(vnode, read_attr_stat)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, stat);
3753 }
3754 
3755 
3756 static fssh_status_t
3757 attr_write_stat(struct file_descriptor *descriptor,
3758 	const struct fssh_stat *stat, int statMask)
3759 {
3760 	struct vnode *vnode = descriptor->u.vnode;
3761 
3762 	FUNCTION(("attr_write_stat: stat = %p, statMask %d\n", stat, statMask));
3763 
3764 	if (!FS_CALL(vnode, write_attr_stat))
3765 		return FSSH_EROFS;
3766 
3767 	return FS_CALL(vnode, write_attr_stat)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, stat, statMask);
3768 }
3769 
3770 
3771 static fssh_status_t
3772 attr_remove(int fd, const char *name, bool kernel)
3773 {
3774 	struct file_descriptor *descriptor;
3775 	struct vnode *vnode;
3776 	fssh_status_t status;
3777 
3778 	if (name == NULL || *name == '\0')
3779 		return FSSH_B_BAD_VALUE;
3780 
3781 	FUNCTION(("attr_remove: fd = %d, name = \"%s\", kernel %d\n", fd, name, kernel));
3782 
3783 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3784 	if (descriptor == NULL)
3785 		return FSSH_B_FILE_ERROR;
3786 
3787 	if (FS_CALL(vnode, remove_attr))
3788 		status = FS_CALL(vnode, remove_attr)(vnode->mount->cookie, vnode->private_node, name);
3789 	else
3790 		status = FSSH_EROFS;
3791 
3792 	put_fd(descriptor);
3793 
3794 	return status;
3795 }
3796 
3797 
3798 static fssh_status_t
3799 attr_rename(int fromfd, const char *fromName, int tofd, const char *toName, bool kernel)
3800 {
3801 	struct file_descriptor *fromDescriptor, *toDescriptor;
3802 	struct vnode *fromVnode, *toVnode;
3803 	fssh_status_t status;
3804 
3805 	if (fromName == NULL || *fromName == '\0' || toName == NULL || *toName == '\0')
3806 		return FSSH_B_BAD_VALUE;
3807 
3808 	FUNCTION(("attr_rename: from fd = %d, from name = \"%s\", to fd = %d, to name = \"%s\", kernel %d\n", fromfd, fromName, tofd, toName, kernel));
3809 
3810 	fromDescriptor = get_fd_and_vnode(fromfd, &fromVnode, kernel);
3811 	if (fromDescriptor == NULL)
3812 		return FSSH_B_FILE_ERROR;
3813 
3814 	toDescriptor = get_fd_and_vnode(tofd, &toVnode, kernel);
3815 	if (toDescriptor == NULL) {
3816 		status = FSSH_B_FILE_ERROR;
3817 		goto err;
3818 	}
3819 
3820 	// are the files on the same volume?
3821 	if (fromVnode->device != toVnode->device) {
3822 		status = FSSH_B_CROSS_DEVICE_LINK;
3823 		goto err1;
3824 	}
3825 
3826 	if (FS_CALL(fromVnode, rename_attr))
3827 		status = FS_CALL(fromVnode, rename_attr)(fromVnode->mount->cookie, fromVnode->private_node, fromName, toVnode->private_node, toName);
3828 	else
3829 		status = FSSH_EROFS;
3830 
3831 err1:
3832 	put_fd(toDescriptor);
3833 err:
3834 	put_fd(fromDescriptor);
3835 
3836 	return status;
3837 }
3838 
3839 
3840 static fssh_status_t
3841 index_dir_open(fssh_mount_id mountID, bool kernel)
3842 {
3843 	struct fs_mount *mount;
3844 	fssh_fs_cookie cookie;
3845 
3846 	FUNCTION(("index_dir_open(mountID = %ld, kernel = %d)\n", mountID, kernel));
3847 
3848 	fssh_status_t status = get_mount(mountID, &mount);
3849 	if (status < FSSH_B_OK)
3850 		return status;
3851 
3852 	if (FS_MOUNT_CALL(mount, open_index_dir) == NULL) {
3853 		status = FSSH_EOPNOTSUPP;
3854 		goto out;
3855 	}
3856 
3857 	status = FS_MOUNT_CALL(mount, open_index_dir)(mount->cookie, &cookie);
3858 	if (status < FSSH_B_OK)
3859 		goto out;
3860 
3861 	// get fd for the index directory
3862 	status = get_new_fd(FDTYPE_INDEX_DIR, mount, NULL, cookie, 0, kernel);
3863 	if (status >= 0)
3864 		goto out;
3865 
3866 	// something went wrong
3867 	FS_MOUNT_CALL(mount, close_index_dir)(mount->cookie, cookie);
3868 	FS_MOUNT_CALL(mount, free_index_dir_cookie)(mount->cookie, cookie);
3869 
3870 out:
3871 	put_mount(mount);
3872 	return status;
3873 }
3874 
3875 
3876 static fssh_status_t
3877 index_dir_close(struct file_descriptor *descriptor)
3878 {
3879 	struct fs_mount *mount = descriptor->u.mount;
3880 
3881 	FUNCTION(("index_dir_close(descriptor = %p)\n", descriptor));
3882 
3883 	if (FS_MOUNT_CALL(mount, close_index_dir))
3884 		return FS_MOUNT_CALL(mount, close_index_dir)(mount->cookie, descriptor->cookie);
3885 
3886 	return FSSH_B_OK;
3887 }
3888 
3889 
3890 static void
3891 index_dir_free_fd(struct file_descriptor *descriptor)
3892 {
3893 	struct fs_mount *mount = descriptor->u.mount;
3894 
3895 	if (mount != NULL) {
3896 		FS_MOUNT_CALL(mount, free_index_dir_cookie)(mount->cookie, descriptor->cookie);
3897 		// ToDo: find a replacement ref_count object - perhaps the root dir?
3898 		//put_vnode(vnode);
3899 	}
3900 }
3901 
3902 
3903 static fssh_status_t
3904 index_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
3905 	fssh_size_t bufferSize, uint32_t *_count)
3906 {
3907 	struct fs_mount *mount = descriptor->u.mount;
3908 
3909 	if (FS_MOUNT_CALL(mount, read_index_dir))
3910 		return FS_MOUNT_CALL(mount, read_index_dir)(mount->cookie, descriptor->cookie, buffer, bufferSize, _count);
3911 
3912 	return FSSH_EOPNOTSUPP;
3913 }
3914 
3915 
3916 static fssh_status_t
3917 index_dir_rewind(struct file_descriptor *descriptor)
3918 {
3919 	struct fs_mount *mount = descriptor->u.mount;
3920 
3921 	if (FS_MOUNT_CALL(mount, rewind_index_dir))
3922 		return FS_MOUNT_CALL(mount, rewind_index_dir)(mount->cookie, descriptor->cookie);
3923 
3924 	return FSSH_EOPNOTSUPP;
3925 }
3926 
3927 
3928 static fssh_status_t
3929 index_create(fssh_mount_id mountID, const char *name, uint32_t type, uint32_t flags, bool kernel)
3930 {
3931 	FUNCTION(("index_create(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
3932 
3933 	struct fs_mount *mount;
3934 	fssh_status_t status = get_mount(mountID, &mount);
3935 	if (status < FSSH_B_OK)
3936 		return status;
3937 
3938 	if (FS_MOUNT_CALL(mount, create_index) == NULL) {
3939 		status = FSSH_EROFS;
3940 		goto out;
3941 	}
3942 
3943 	status = FS_MOUNT_CALL(mount, create_index)(mount->cookie, name, type, flags);
3944 
3945 out:
3946 	put_mount(mount);
3947 	return status;
3948 }
3949 
3950 
3951 static fssh_status_t
3952 index_name_read_stat(fssh_mount_id mountID, const char *name,
3953 	struct fssh_stat *stat, bool kernel)
3954 {
3955 	FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
3956 
3957 	struct fs_mount *mount;
3958 	fssh_status_t status = get_mount(mountID, &mount);
3959 	if (status < FSSH_B_OK)
3960 		return status;
3961 
3962 	if (FS_MOUNT_CALL(mount, read_index_stat) == NULL) {
3963 		status = FSSH_EOPNOTSUPP;
3964 		goto out;
3965 	}
3966 
3967 	status = FS_MOUNT_CALL(mount, read_index_stat)(mount->cookie, name, stat);
3968 
3969 out:
3970 	put_mount(mount);
3971 	return status;
3972 }
3973 
3974 
3975 static fssh_status_t
3976 index_remove(fssh_mount_id mountID, const char *name, bool kernel)
3977 {
3978 	FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
3979 
3980 	struct fs_mount *mount;
3981 	fssh_status_t status = get_mount(mountID, &mount);
3982 	if (status < FSSH_B_OK)
3983 		return status;
3984 
3985 	if (FS_MOUNT_CALL(mount, remove_index) == NULL) {
3986 		status = FSSH_EROFS;
3987 		goto out;
3988 	}
3989 
3990 	status = FS_MOUNT_CALL(mount, remove_index)(mount->cookie, name);
3991 
3992 out:
3993 	put_mount(mount);
3994 	return status;
3995 }
3996 
3997 
3998 static fssh_status_t
3999 query_close(struct file_descriptor *descriptor)
4000 {
4001 	struct fs_mount *mount = descriptor->u.mount;
4002 
4003 	FUNCTION(("query_close(descriptor = %p)\n", descriptor));
4004 
4005 	if (FS_MOUNT_CALL(mount, close_query))
4006 		return FS_MOUNT_CALL(mount, close_query)(mount->cookie, descriptor->cookie);
4007 
4008 	return FSSH_B_OK;
4009 }
4010 
4011 
4012 static void
4013 query_free_fd(struct file_descriptor *descriptor)
4014 {
4015 	struct fs_mount *mount = descriptor->u.mount;
4016 
4017 	if (mount != NULL) {
4018 		FS_MOUNT_CALL(mount, free_query_cookie)(mount->cookie, descriptor->cookie);
4019 		// ToDo: find a replacement ref_count object - perhaps the root dir?
4020 		//put_vnode(vnode);
4021 	}
4022 }
4023 
4024 
4025 static fssh_status_t
4026 query_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
4027 	fssh_size_t bufferSize, uint32_t *_count)
4028 {
4029 	struct fs_mount *mount = descriptor->u.mount;
4030 
4031 	if (FS_MOUNT_CALL(mount, read_query))
4032 		return FS_MOUNT_CALL(mount, read_query)(mount->cookie, descriptor->cookie, buffer, bufferSize, _count);
4033 
4034 	return FSSH_EOPNOTSUPP;
4035 }
4036 
4037 
4038 static fssh_status_t
4039 query_rewind(struct file_descriptor *descriptor)
4040 {
4041 	struct fs_mount *mount = descriptor->u.mount;
4042 
4043 	if (FS_MOUNT_CALL(mount, rewind_query))
4044 		return FS_MOUNT_CALL(mount, rewind_query)(mount->cookie, descriptor->cookie);
4045 
4046 	return FSSH_EOPNOTSUPP;
4047 }
4048 
4049 
4050 //	#pragma mark -
4051 //	General File System functions
4052 
4053 
4054 static fssh_dev_t
4055 fs_mount(char *path, const char *device, const char *fsName, uint32_t flags,
4056 	const char *args, bool kernel)
4057 {
4058 	struct fs_mount *mount;
4059 	fssh_status_t status = 0;
4060 
4061 	FUNCTION(("fs_mount: entry. path = '%s', fs_name = '%s'\n", path, fsName));
4062 
4063 	// The path is always safe, we just have to make sure that fsName is
4064 	// almost valid - we can't make any assumptions about args, though.
4065 	// A NULL fsName is OK, if a device was given and the FS is not virtual.
4066 	// We'll get it from the DDM later.
4067 	if (fsName == NULL) {
4068 		if (!device || flags & FSSH_B_MOUNT_VIRTUAL_DEVICE)
4069 			return FSSH_B_BAD_VALUE;
4070 	} else if (fsName[0] == '\0')
4071 		return FSSH_B_BAD_VALUE;
4072 
4073 	RecursiveLocker mountOpLocker(sMountOpLock);
4074 
4075 	// If the file system is not a "virtual" one, the device argument should
4076 	// point to a real file/device (if given at all).
4077 	// get the partition
4078 	KPath normalizedDevice;
4079 
4080 	if (!(flags & FSSH_B_MOUNT_VIRTUAL_DEVICE) && device) {
4081 		// normalize the device path
4082 //		status = normalizedDevice.SetTo(device, true);
4083 // NOTE: normalizing works only in our namespace.
4084 		status = normalizedDevice.SetTo(device, false);
4085 		if (status != FSSH_B_OK)
4086 			return status;
4087 
4088 		device = normalizedDevice.Path();
4089 			// correct path to file device
4090 	}
4091 
4092 	mount = (struct fs_mount *)malloc(sizeof(struct fs_mount));
4093 	if (mount == NULL)
4094 		return FSSH_B_NO_MEMORY;
4095 
4096 	list_init_etc(&mount->vnodes, fssh_offsetof(struct vnode, mount_link));
4097 
4098 	mount->fs_name = get_file_system_name(fsName);
4099 	if (mount->fs_name == NULL) {
4100 		status = FSSH_B_NO_MEMORY;
4101 		goto err1;
4102 	}
4103 
4104 	mount->device_name = fssh_strdup(device);
4105 		// "device" can be NULL
4106 
4107 	mount->fs = get_file_system(fsName);
4108 	if (mount->fs == NULL) {
4109 		status = FSSH_ENODEV;
4110 		goto err3;
4111 	}
4112 
4113 	status = recursive_lock_init(&mount->rlock, "mount rlock");
4114 	if (status < FSSH_B_OK)
4115 		goto err4;
4116 
4117 	// initialize structure
4118 	mount->id = sNextMountID++;
4119 	mount->root_vnode = NULL;
4120 	mount->covers_vnode = NULL;
4121 	mount->cookie = NULL;
4122 	mount->unmounting = false;
4123 	mount->owns_file_device = false;
4124 
4125 	// insert mount struct into list before we call FS's mount() function
4126 	// so that vnodes can be created for this mount
4127 	mutex_lock(&sMountMutex);
4128 	hash_insert(sMountsTable, mount);
4129 	mutex_unlock(&sMountMutex);
4130 
4131 	fssh_vnode_id rootID;
4132 
4133 	if (!sRoot) {
4134 		// we haven't mounted anything yet
4135 		if (fssh_strcmp(path, "/") != 0) {
4136 			status = FSSH_B_ERROR;
4137 			goto err5;
4138 		}
4139 
4140 		status = FS_MOUNT_CALL(mount, mount)(mount->id, device, flags, args, &mount->cookie, &rootID);
4141 		if (status < 0) {
4142 			// ToDo: why should we hide the error code from the file system here?
4143 			//status = ERR_VFS_GENERAL;
4144 			goto err5;
4145 		}
4146 	} else {
4147 		struct vnode *coveredVnode;
4148 		status = path_to_vnode(path, true, &coveredVnode, NULL, kernel);
4149 		if (status < FSSH_B_OK)
4150 			goto err5;
4151 
4152 		// make sure covered_vnode is a DIR
4153 		struct fssh_stat coveredNodeStat;
4154 		status = FS_CALL(coveredVnode, read_stat)(coveredVnode->mount->cookie,
4155 			coveredVnode->private_node, &coveredNodeStat);
4156 		if (status < FSSH_B_OK)
4157 			goto err5;
4158 
4159 		if (!FSSH_S_ISDIR(coveredNodeStat.fssh_st_mode)) {
4160 			status = FSSH_B_NOT_A_DIRECTORY;
4161 			goto err5;
4162 		}
4163 
4164 		if (coveredVnode->mount->root_vnode == coveredVnode) {
4165 			// this is already a mount point
4166 			status = FSSH_B_BUSY;
4167 			goto err5;
4168 		}
4169 
4170 		mount->covers_vnode = coveredVnode;
4171 
4172 		// mount it
4173 		status = FS_MOUNT_CALL(mount, mount)(mount->id, device, flags, args, &mount->cookie, &rootID);
4174 		if (status < FSSH_B_OK)
4175 			goto err6;
4176 	}
4177 
4178 	// the root node is supposed to be owned by the file system - it must
4179 	// exist at this point
4180 	mount->root_vnode = lookup_vnode(mount->id, rootID);
4181 	if (mount->root_vnode == NULL || mount->root_vnode->ref_count != 1) {
4182 		fssh_panic("fs_mount: file system does not own its root node!\n");
4183 		status = FSSH_B_ERROR;
4184 		goto err7;
4185 	}
4186 
4187 	// No race here, since fs_mount() is the only function changing
4188 	// covers_vnode (and holds sMountOpLock at that time).
4189 	mutex_lock(&sVnodeCoveredByMutex);
4190 	if (mount->covers_vnode)
4191 		mount->covers_vnode->covered_by = mount->root_vnode;
4192 	mutex_unlock(&sVnodeCoveredByMutex);
4193 
4194 	if (!sRoot)
4195 		sRoot = mount->root_vnode;
4196 
4197 	return mount->id;
4198 
4199 err7:
4200 	FS_MOUNT_CALL(mount, unmount)(mount->cookie);
4201 err6:
4202 	if (mount->covers_vnode)
4203 		put_vnode(mount->covers_vnode);
4204 err5:
4205 	mutex_lock(&sMountMutex);
4206 	hash_remove(sMountsTable, mount);
4207 	mutex_unlock(&sMountMutex);
4208 
4209 	recursive_lock_destroy(&mount->rlock);
4210 err4:
4211 	put_file_system(mount->fs);
4212 	free(mount->device_name);
4213 err3:
4214 	free(mount->fs_name);
4215 err1:
4216 	free(mount);
4217 
4218 	return status;
4219 }
4220 
4221 
4222 static fssh_status_t
4223 fs_unmount(char *path, uint32_t flags, bool kernel)
4224 {
4225 	struct fs_mount *mount;
4226 	struct vnode *vnode;
4227 	fssh_status_t err;
4228 
4229 	FUNCTION(("vfs_unmount: entry. path = '%s', kernel %d\n", path, kernel));
4230 
4231 	err = path_to_vnode(path, true, &vnode, NULL, kernel);
4232 	if (err < 0)
4233 		return FSSH_B_ENTRY_NOT_FOUND;
4234 
4235 	RecursiveLocker mountOpLocker(sMountOpLock);
4236 
4237 	mount = find_mount(vnode->device);
4238 	if (!mount)
4239 		fssh_panic("vfs_unmount: find_mount() failed on root vnode @%p of mount\n", vnode);
4240 
4241 	if (mount->root_vnode != vnode) {
4242 		// not mountpoint
4243 		put_vnode(vnode);
4244 		return FSSH_B_BAD_VALUE;
4245 	}
4246 
4247 	// grab the vnode master mutex to keep someone from creating
4248 	// a vnode while we're figuring out if we can continue
4249 	mutex_lock(&sVnodeMutex);
4250 
4251 	bool disconnectedDescriptors = false;
4252 
4253 	while (true) {
4254 		bool busy = false;
4255 
4256 		// cycle through the list of vnodes associated with this mount and
4257 		// make sure all of them are not busy or have refs on them
4258 		vnode = NULL;
4259 		while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) {
4260 			// The root vnode ref_count needs to be 2 here: one for the file
4261 			// system, one from the path_to_vnode() call above
4262 			if (vnode->busy
4263 				|| ((vnode->ref_count != 0 && mount->root_vnode != vnode)
4264 					|| (vnode->ref_count != 2 && mount->root_vnode == vnode))) {
4265 				// there are still vnodes in use on this mount, so we cannot
4266 				// unmount yet
4267 				busy = true;
4268 				break;
4269 			}
4270 		}
4271 
4272 		if (!busy)
4273 			break;
4274 
4275 		if ((flags & FSSH_B_FORCE_UNMOUNT) == 0) {
4276 			mutex_unlock(&sVnodeMutex);
4277 			put_vnode(mount->root_vnode);
4278 
4279 			return FSSH_B_BUSY;
4280 		}
4281 
4282 		if (disconnectedDescriptors) {
4283 			// wait a bit until the last access is finished, and then try again
4284 			mutex_unlock(&sVnodeMutex);
4285 			fssh_snooze(100000);
4286 			// TODO: if there is some kind of bug that prevents the ref counts
4287 			//	from getting back to zero, this will fall into an endless loop...
4288 			mutex_lock(&sVnodeMutex);
4289 			continue;
4290 		}
4291 
4292 		// the file system is still busy - but we're forced to unmount it,
4293 		// so let's disconnect all open file descriptors
4294 
4295 		mount->unmounting = true;
4296 			// prevent new vnodes from being created
4297 
4298 		mutex_unlock(&sVnodeMutex);
4299 
4300 		disconnect_mount_or_vnode_fds(mount, NULL);
4301 		disconnectedDescriptors = true;
4302 
4303 		mutex_lock(&sVnodeMutex);
4304 	}
4305 
4306 	// we can safely continue, mark all of the vnodes busy and this mount
4307 	// structure in unmounting state
4308 	mount->unmounting = true;
4309 
4310 	while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) {
4311 		vnode->busy = true;
4312 
4313 		if (vnode->ref_count == 0) {
4314 			// this vnode has been unused before
4315 			list_remove_item(&sUnusedVnodeList, vnode);
4316 			sUnusedVnodes--;
4317 		}
4318 	}
4319 
4320 	// The ref_count of the root node is 2 at this point, see above why this is
4321 	mount->root_vnode->ref_count -= 2;
4322 
4323 	mutex_unlock(&sVnodeMutex);
4324 
4325 	mutex_lock(&sVnodeCoveredByMutex);
4326 	mount->covers_vnode->covered_by = NULL;
4327 	mutex_unlock(&sVnodeCoveredByMutex);
4328 	put_vnode(mount->covers_vnode);
4329 
4330 	// Free all vnodes associated with this mount.
4331 	// They will be removed from the mount list by free_vnode(), so
4332 	// we don't have to do this.
4333 	while ((vnode = (struct vnode *)list_get_first_item(&mount->vnodes)) != NULL) {
4334 		free_vnode(vnode, false);
4335 	}
4336 
4337 	// remove the mount structure from the hash table
4338 	mutex_lock(&sMountMutex);
4339 	hash_remove(sMountsTable, mount);
4340 	mutex_unlock(&sMountMutex);
4341 
4342 	mountOpLocker.Unlock();
4343 
4344 	FS_MOUNT_CALL(mount, unmount)(mount->cookie);
4345 
4346 	// release the file system
4347 	put_file_system(mount->fs);
4348 
4349 	free(mount->device_name);
4350 	free(mount->fs_name);
4351 	free(mount);
4352 
4353 	return FSSH_B_OK;
4354 }
4355 
4356 
4357 static fssh_status_t
4358 fs_sync(fssh_dev_t device)
4359 {
4360 	struct fs_mount *mount;
4361 	fssh_status_t status = get_mount(device, &mount);
4362 	if (status < FSSH_B_OK)
4363 		return status;
4364 
4365 	mutex_lock(&sMountMutex);
4366 
4367 	if (FS_MOUNT_CALL(mount, sync))
4368 		status = FS_MOUNT_CALL(mount, sync)(mount->cookie);
4369 
4370 	mutex_unlock(&sMountMutex);
4371 
4372 	struct vnode *previousVnode = NULL;
4373 	while (true) {
4374 		// synchronize access to vnode list
4375 		recursive_lock_lock(&mount->rlock);
4376 
4377 		struct vnode *vnode = (struct vnode *)list_get_next_item(&mount->vnodes,
4378 			previousVnode);
4379 
4380 		fssh_vnode_id id = -1;
4381 		if (vnode != NULL)
4382 			id = vnode->id;
4383 
4384 		recursive_lock_unlock(&mount->rlock);
4385 
4386 		if (vnode == NULL)
4387 			break;
4388 
4389 		// acquire a reference to the vnode
4390 
4391 		if (get_vnode(mount->id, id, &vnode, true) == FSSH_B_OK) {
4392 			if (previousVnode != NULL)
4393 				put_vnode(previousVnode);
4394 
4395 			if (FS_CALL(vnode, fsync) != NULL)
4396 				FS_CALL(vnode, fsync)(vnode->mount->cookie, vnode->private_node);
4397 
4398 			// the next vnode might change until we lock the vnode list again,
4399 			// but this vnode won't go away since we keep a reference to it.
4400 			previousVnode = vnode;
4401 		} else {
4402 			fssh_dprintf("syncing of mount %d stopped due to vnode %lld.\n", (int)mount->id, id);
4403 			break;
4404 		}
4405 	}
4406 
4407 	if (previousVnode != NULL)
4408 		put_vnode(previousVnode);
4409 
4410 	put_mount(mount);
4411 	return status;
4412 }
4413 
4414 
4415 static fssh_status_t
4416 fs_read_info(fssh_dev_t device, struct fssh_fs_info *info)
4417 {
4418 	struct fs_mount *mount;
4419 	fssh_status_t status = get_mount(device, &mount);
4420 	if (status < FSSH_B_OK)
4421 		return status;
4422 
4423 	fssh_memset(info, 0, sizeof(struct fssh_fs_info));
4424 
4425 	if (FS_MOUNT_CALL(mount, read_fs_info))
4426 		status = FS_MOUNT_CALL(mount, read_fs_info)(mount->cookie, info);
4427 
4428 	// fill in info the file system doesn't (have to) know about
4429 	if (status == FSSH_B_OK) {
4430 		info->dev = mount->id;
4431 		info->root = mount->root_vnode->id;
4432 		fssh_strlcpy(info->fsh_name, mount->fs_name, sizeof(info->fsh_name));
4433 		if (mount->device_name != NULL) {
4434 			fssh_strlcpy(info->device_name, mount->device_name,
4435 				sizeof(info->device_name));
4436 		}
4437 	}
4438 
4439 	// if the call is not supported by the file system, there are still
4440 	// the parts that we filled out ourselves
4441 
4442 	put_mount(mount);
4443 	return status;
4444 }
4445 
4446 
4447 static fssh_status_t
4448 fs_write_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask)
4449 {
4450 	struct fs_mount *mount;
4451 	fssh_status_t status = get_mount(device, &mount);
4452 	if (status < FSSH_B_OK)
4453 		return status;
4454 
4455 	if (FS_MOUNT_CALL(mount, write_fs_info))
4456 		status = FS_MOUNT_CALL(mount, write_fs_info)(mount->cookie, info, mask);
4457 	else
4458 		status = FSSH_EROFS;
4459 
4460 	put_mount(mount);
4461 	return status;
4462 }
4463 
4464 
4465 static fssh_dev_t
4466 fs_next_device(int32_t *_cookie)
4467 {
4468 	struct fs_mount *mount = NULL;
4469 	fssh_dev_t device = *_cookie;
4470 
4471 	mutex_lock(&sMountMutex);
4472 
4473 	// Since device IDs are assigned sequentially, this algorithm
4474 	// does work good enough. It makes sure that the device list
4475 	// returned is sorted, and that no device is skipped when an
4476 	// already visited device got unmounted.
4477 
4478 	while (device < sNextMountID) {
4479 		mount = find_mount(device++);
4480 		if (mount != NULL && mount->cookie != NULL)
4481 			break;
4482 	}
4483 
4484 	*_cookie = device;
4485 
4486 	if (mount != NULL)
4487 		device = mount->id;
4488 	else
4489 		device = FSSH_B_BAD_VALUE;
4490 
4491 	mutex_unlock(&sMountMutex);
4492 
4493 	return device;
4494 }
4495 
4496 
4497 static fssh_status_t
4498 get_cwd(char *buffer, fssh_size_t size, bool kernel)
4499 {
4500 	// Get current working directory from io context
4501 	struct io_context *context = get_current_io_context(kernel);
4502 	fssh_status_t status;
4503 
4504 	FUNCTION(("vfs_get_cwd: buf %p, size %ld\n", buffer, size));
4505 
4506 	mutex_lock(&context->io_mutex);
4507 
4508 	if (context->cwd)
4509 		status = dir_vnode_to_path(context->cwd, buffer, size);
4510 	else
4511 		status = FSSH_B_ERROR;
4512 
4513 	mutex_unlock(&context->io_mutex);
4514 	return status;
4515 }
4516 
4517 
4518 static fssh_status_t
4519 set_cwd(int fd, char *path, bool kernel)
4520 {
4521 	struct io_context *context;
4522 	struct vnode *vnode = NULL;
4523 	struct vnode *oldDirectory;
4524 	struct fssh_stat stat;
4525 	fssh_status_t status;
4526 
4527 	FUNCTION(("set_cwd: path = \'%s\'\n", path));
4528 
4529 	// Get vnode for passed path, and bail if it failed
4530 	status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel);
4531 	if (status < 0)
4532 		return status;
4533 
4534 	status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, vnode->private_node, &stat);
4535 	if (status < 0)
4536 		goto err;
4537 
4538 	if (!FSSH_S_ISDIR(stat.fssh_st_mode)) {
4539 		// nope, can't cwd to here
4540 		status = FSSH_B_NOT_A_DIRECTORY;
4541 		goto err;
4542 	}
4543 
4544 	// Get current io context and lock
4545 	context = get_current_io_context(kernel);
4546 	mutex_lock(&context->io_mutex);
4547 
4548 	// save the old current working directory first
4549 	oldDirectory = context->cwd;
4550 	context->cwd = vnode;
4551 
4552 	mutex_unlock(&context->io_mutex);
4553 
4554 	if (oldDirectory)
4555 		put_vnode(oldDirectory);
4556 
4557 	return FSSH_B_NO_ERROR;
4558 
4559 err:
4560 	put_vnode(vnode);
4561 	return status;
4562 }
4563 
4564 
4565 //	#pragma mark -
4566 //	Calls from within the kernel
4567 
4568 
4569 fssh_dev_t
4570 _kern_mount(const char *path, const char *device, const char *fsName,
4571 	uint32_t flags, const char *args, fssh_size_t argsLength)
4572 {
4573 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
4574 	if (pathBuffer.InitCheck() != FSSH_B_OK)
4575 		return FSSH_B_NO_MEMORY;
4576 
4577 	return fs_mount(pathBuffer.LockBuffer(), device, fsName, flags, args, true);
4578 }
4579 
4580 
4581 fssh_status_t
4582 _kern_unmount(const char *path, uint32_t flags)
4583 {
4584 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
4585 	if (pathBuffer.InitCheck() != FSSH_B_OK)
4586 		return FSSH_B_NO_MEMORY;
4587 
4588 	return fs_unmount(pathBuffer.LockBuffer(), flags, true);
4589 }
4590 
4591 
4592 fssh_status_t
4593 _kern_read_fs_info(fssh_dev_t device, struct fssh_fs_info *info)
4594 {
4595 	if (info == NULL)
4596 		return FSSH_B_BAD_VALUE;
4597 
4598 	return fs_read_info(device, info);
4599 }
4600 
4601 
4602 fssh_status_t
4603 _kern_write_fs_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask)
4604 {
4605 	if (info == NULL)
4606 		return FSSH_B_BAD_VALUE;
4607 
4608 	return fs_write_info(device, info, mask);
4609 }
4610 
4611 
4612 fssh_status_t
4613 _kern_sync(void)
4614 {
4615 	// Note: _kern_sync() is also called from _user_sync()
4616 	int32_t cookie = 0;
4617 	fssh_dev_t device;
4618 	while ((device = fs_next_device(&cookie)) >= 0) {
4619 		fssh_status_t status = fs_sync(device);
4620 		if (status != FSSH_B_OK && status != FSSH_B_BAD_VALUE)
4621 			fssh_dprintf("sync: device %d couldn't sync: %s\n", (int)device, fssh_strerror(status));
4622 	}
4623 
4624 	return FSSH_B_OK;
4625 }
4626 
4627 
4628 fssh_dev_t
4629 _kern_next_device(int32_t *_cookie)
4630 {
4631 	return fs_next_device(_cookie);
4632 }
4633 
4634 
4635 int
4636 _kern_open_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int openMode, int perms)
4637 {
4638 	if (openMode & FSSH_O_CREAT)
4639 		return file_create_entry_ref(device, inode, name, openMode, perms, true);
4640 
4641 	return file_open_entry_ref(device, inode, name, openMode, true);
4642 }
4643 
4644 
4645 /**	\brief Opens a node specified by a FD + path pair.
4646  *
4647  *	At least one of \a fd and \a path must be specified.
4648  *	If only \a fd is given, the function opens the node identified by this
4649  *	FD. If only a path is given, this path is opened. If both are given and
4650  *	the path is absolute, \a fd is ignored; a relative path is reckoned off
4651  *	of the directory (!) identified by \a fd.
4652  *
4653  *	\param fd The FD. May be < 0.
4654  *	\param path The absolute or relative path. May be \c NULL.
4655  *	\param openMode The open mode.
4656  *	\return A FD referring to the newly opened node, or an error code,
4657  *			if an error occurs.
4658  */
4659 
4660 int
4661 _kern_open(int fd, const char *path, int openMode, int perms)
4662 {
4663 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
4664 	if (pathBuffer.InitCheck() != FSSH_B_OK)
4665 		return FSSH_B_NO_MEMORY;
4666 
4667 	if (openMode & FSSH_O_CREAT)
4668 		return file_create(fd, pathBuffer.LockBuffer(), openMode, perms, true);
4669 
4670 	return file_open(fd, pathBuffer.LockBuffer(), openMode, true);
4671 }
4672 
4673 
4674 /**	\brief Opens a directory specified by entry_ref or node_ref.
4675  *
4676  *	The supplied name may be \c NULL, in which case directory identified
4677  *	by \a device and \a inode will be opened. Otherwise \a device and
4678  *	\a inode identify the parent directory of the directory to be opened
4679  *	and \a name its entry name.
4680  *
4681  *	\param device If \a name is specified the ID of the device the parent
4682  *		   directory of the directory to be opened resides on, otherwise
4683  *		   the device of the directory itself.
4684  *	\param inode If \a name is specified the node ID of the parent
4685  *		   directory of the directory to be opened, otherwise node ID of the
4686  *		   directory itself.
4687  *	\param name The entry name of the directory to be opened. If \c NULL,
4688  *		   the \a device + \a inode pair identify the node to be opened.
4689  *	\return The FD of the newly opened directory or an error code, if
4690  *			something went wrong.
4691  */
4692 
4693 int
4694 _kern_open_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name)
4695 {
4696 	return dir_open_entry_ref(device, inode, name, true);
4697 }
4698 
4699 
4700 /**	\brief Opens a directory specified by a FD + path pair.
4701  *
4702  *	At least one of \a fd and \a path must be specified.
4703  *	If only \a fd is given, the function opens the directory identified by this
4704  *	FD. If only a path is given, this path is opened. If both are given and
4705  *	the path is absolute, \a fd is ignored; a relative path is reckoned off
4706  *	of the directory (!) identified by \a fd.
4707  *
4708  *	\param fd The FD. May be < 0.
4709  *	\param path The absolute or relative path. May be \c NULL.
4710  *	\return A FD referring to the newly opened directory, or an error code,
4711  *			if an error occurs.
4712  */
4713 
4714 int
4715 _kern_open_dir(int fd, const char *path)
4716 {
4717 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
4718 	if (pathBuffer.InitCheck() != FSSH_B_OK)
4719 		return FSSH_B_NO_MEMORY;
4720 
4721 	return dir_open(fd, pathBuffer.LockBuffer(), true);
4722 }
4723 
4724 
4725 fssh_status_t
4726 _kern_fcntl(int fd, int op, uint32_t argument)
4727 {
4728 	return common_fcntl(fd, op, argument, true);
4729 }
4730 
4731 
4732 fssh_status_t
4733 _kern_fsync(int fd)
4734 {
4735 	return common_sync(fd, true);
4736 }
4737 
4738 
4739 fssh_status_t
4740 _kern_lock_node(int fd)
4741 {
4742 	return common_lock_node(fd, true);
4743 }
4744 
4745 
4746 fssh_status_t
4747 _kern_unlock_node(int fd)
4748 {
4749 	return common_unlock_node(fd, true);
4750 }
4751 
4752 
4753 fssh_status_t
4754 _kern_create_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int perms)
4755 {
4756 	return dir_create_entry_ref(device, inode, name, perms, true);
4757 }
4758 
4759 
4760 /**	\brief Creates a directory specified by a FD + path pair.
4761  *
4762  *	\a path must always be specified (it contains the name of the new directory
4763  *	at least). If only a path is given, this path identifies the location at
4764  *	which the directory shall be created. If both \a fd and \a path are given and
4765  *	the path is absolute, \a fd is ignored; a relative path is reckoned off
4766  *	of the directory (!) identified by \a fd.
4767  *
4768  *	\param fd The FD. May be < 0.
4769  *	\param path The absolute or relative path. Must not be \c NULL.
4770  *	\param perms The access permissions the new directory shall have.
4771  *	\return \c FSSH_B_OK, if the directory has been created successfully, another
4772  *			error code otherwise.
4773  */
4774 
4775 fssh_status_t
4776 _kern_create_dir(int fd, const char *path, int perms)
4777 {
4778 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
4779 	if (pathBuffer.InitCheck() != FSSH_B_OK)
4780 		return FSSH_B_NO_MEMORY;
4781 
4782 	return dir_create(fd, pathBuffer.LockBuffer(), perms, true);
4783 }
4784 
4785 
4786 fssh_status_t
4787 _kern_remove_dir(int fd, const char *path)
4788 {
4789 	if (path) {
4790 		KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
4791 		if (pathBuffer.InitCheck() != FSSH_B_OK)
4792 			return FSSH_B_NO_MEMORY;
4793 
4794 		return dir_remove(fd, pathBuffer.LockBuffer(), true);
4795 	}
4796 
4797 	return dir_remove(fd, NULL, true);
4798 }
4799 
4800 
4801 /**	\brief Reads the contents of a symlink referred to by a FD + path pair.
4802  *
4803  *	At least one of \a fd and \a path must be specified.
4804  *	If only \a fd is given, the function the symlink to be read is the node
4805  *	identified by this FD. If only a path is given, this path identifies the
4806  *	symlink to be read. If both are given and the path is absolute, \a fd is
4807  *	ignored; a relative path is reckoned off of the directory (!) identified
4808  *	by \a fd.
4809  *	If this function fails with FSSH_B_BUFFER_OVERFLOW, the \a _bufferSize pointer
4810  *	will still be updated to reflect the required buffer size.
4811  *
4812  *	\param fd The FD. May be < 0.
4813  *	\param path The absolute or relative path. May be \c NULL.
4814  *	\param buffer The buffer into which the contents of the symlink shall be
4815  *		   written.
4816  *	\param _bufferSize A pointer to the size of the supplied buffer.
4817  *	\return The length of the link on success or an appropriate error code
4818  */
4819 
4820 fssh_status_t
4821 _kern_read_link(int fd, const char *path, char *buffer, fssh_size_t *_bufferSize)
4822 {
4823 	if (path) {
4824 		KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
4825 		if (pathBuffer.InitCheck() != FSSH_B_OK)
4826 			return FSSH_B_NO_MEMORY;
4827 
4828 		return common_read_link(fd, pathBuffer.LockBuffer(),
4829 			buffer, _bufferSize, true);
4830 	}
4831 
4832 	return common_read_link(fd, NULL, buffer, _bufferSize, true);
4833 }
4834 
4835 
4836 /**	\brief Creates a symlink specified by a FD + path pair.
4837  *
4838  *	\a path must always be specified (it contains the name of the new symlink
4839  *	at least). If only a path is given, this path identifies the location at
4840  *	which the symlink shall be created. If both \a fd and \a path are given and
4841  *	the path is absolute, \a fd is ignored; a relative path is reckoned off
4842  *	of the directory (!) identified by \a fd.
4843  *
4844  *	\param fd The FD. May be < 0.
4845  *	\param toPath The absolute or relative path. Must not be \c NULL.
4846  *	\param mode The access permissions the new symlink shall have.
4847  *	\return \c FSSH_B_OK, if the symlink has been created successfully, another
4848  *			error code otherwise.
4849  */
4850 
4851 fssh_status_t
4852 _kern_create_symlink(int fd, const char *path, const char *toPath, int mode)
4853 {
4854 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
4855 	KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
4856 	if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK)
4857 		return FSSH_B_NO_MEMORY;
4858 
4859 	char *toBuffer = toPathBuffer.LockBuffer();
4860 
4861 	fssh_status_t status = check_path(toBuffer);
4862 	if (status < FSSH_B_OK)
4863 		return status;
4864 
4865 	return common_create_symlink(fd, pathBuffer.LockBuffer(),
4866 		toBuffer, mode, true);
4867 }
4868 
4869 
4870 fssh_status_t
4871 _kern_create_link(const char *path, const char *toPath)
4872 {
4873 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
4874 	KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
4875 	if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK)
4876 		return FSSH_B_NO_MEMORY;
4877 
4878 	return common_create_link(pathBuffer.LockBuffer(),
4879 		toPathBuffer.LockBuffer(), true);
4880 }
4881 
4882 
4883 /**	\brief Removes an entry specified by a FD + path pair from its directory.
4884  *
4885  *	\a path must always be specified (it contains at least the name of the entry
4886  *	to be deleted). If only a path is given, this path identifies the entry
4887  *	directly. If both \a fd and \a path are given and the path is absolute,
4888  *	\a fd is ignored; a relative path is reckoned off of the directory (!)
4889  *	identified by \a fd.
4890  *
4891  *	\param fd The FD. May be < 0.
4892  *	\param path The absolute or relative path. Must not be \c NULL.
4893  *	\return \c FSSH_B_OK, if the entry has been removed successfully, another
4894  *			error code otherwise.
4895  */
4896 
4897 fssh_status_t
4898 _kern_unlink(int fd, const char *path)
4899 {
4900 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
4901 	if (pathBuffer.InitCheck() != FSSH_B_OK)
4902 		return FSSH_B_NO_MEMORY;
4903 
4904 	return common_unlink(fd, pathBuffer.LockBuffer(), true);
4905 }
4906 
4907 
4908 /**	\brief Moves an entry specified by a FD + path pair to a an entry specified
4909  *		   by another FD + path pair.
4910  *
4911  *	\a oldPath and \a newPath must always be specified (they contain at least
4912  *	the name of the entry). If only a path is given, this path identifies the
4913  *	entry directly. If both a FD and a path are given and the path is absolute,
4914  *	the FD is ignored; a relative path is reckoned off of the directory (!)
4915  *	identified by the respective FD.
4916  *
4917  *	\param oldFD The FD of the old location. May be < 0.
4918  *	\param oldPath The absolute or relative path of the old location. Must not
4919  *		   be \c NULL.
4920  *	\param newFD The FD of the new location. May be < 0.
4921  *	\param newPath The absolute or relative path of the new location. Must not
4922  *		   be \c NULL.
4923  *	\return \c FSSH_B_OK, if the entry has been moved successfully, another
4924  *			error code otherwise.
4925  */
4926 
4927 fssh_status_t
4928 _kern_rename(int oldFD, const char *oldPath, int newFD, const char *newPath)
4929 {
4930 	KPath oldPathBuffer(oldPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
4931 	KPath newPathBuffer(newPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
4932 	if (oldPathBuffer.InitCheck() != FSSH_B_OK || newPathBuffer.InitCheck() != FSSH_B_OK)
4933 		return FSSH_B_NO_MEMORY;
4934 
4935 	return common_rename(oldFD, oldPathBuffer.LockBuffer(),
4936 		newFD, newPathBuffer.LockBuffer(), true);
4937 }
4938 
4939 
4940 fssh_status_t
4941 _kern_access(const char *path, int mode)
4942 {
4943 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
4944 	if (pathBuffer.InitCheck() != FSSH_B_OK)
4945 		return FSSH_B_NO_MEMORY;
4946 
4947 	return common_access(pathBuffer.LockBuffer(), mode, true);
4948 }
4949 
4950 
4951 /**	\brief Reads stat data of an entity specified by a FD + path pair.
4952  *
4953  *	If only \a fd is given, the stat operation associated with the type
4954  *	of the FD (node, attr, attr dir etc.) is performed. If only \a path is
4955  *	given, this path identifies the entry for whose node to retrieve the
4956  *	stat data. If both \a fd and \a path are given and the path is absolute,
4957  *	\a fd is ignored; a relative path is reckoned off of the directory (!)
4958  *	identified by \a fd and specifies the entry whose stat data shall be
4959  *	retrieved.
4960  *
4961  *	\param fd The FD. May be < 0.
4962  *	\param path The absolute or relative path. Must not be \c NULL.
4963  *	\param traverseLeafLink If \a path is given, \c true specifies that the
4964  *		   function shall not stick to symlinks, but traverse them.
4965  *	\param stat The buffer the stat data shall be written into.
4966  *	\param statSize The size of the supplied stat buffer.
4967  *	\return \c FSSH_B_OK, if the the stat data have been read successfully, another
4968  *			error code otherwise.
4969  */
4970 
4971 fssh_status_t
4972 _kern_read_stat(int fd, const char *path, bool traverseLeafLink,
4973 	fssh_struct_stat *stat, fssh_size_t statSize)
4974 {
4975 	fssh_struct_stat completeStat;
4976 	fssh_struct_stat *originalStat = NULL;
4977 	fssh_status_t status;
4978 
4979 	if (statSize > sizeof(fssh_struct_stat))
4980 		return FSSH_B_BAD_VALUE;
4981 
4982 	// this supports different stat extensions
4983 	if (statSize < sizeof(fssh_struct_stat)) {
4984 		originalStat = stat;
4985 		stat = &completeStat;
4986 	}
4987 
4988 	if (path) {
4989 		// path given: get the stat of the node referred to by (fd, path)
4990 		KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
4991 		if (pathBuffer.InitCheck() != FSSH_B_OK)
4992 			return FSSH_B_NO_MEMORY;
4993 
4994 		status = common_path_read_stat(fd, pathBuffer.LockBuffer(),
4995 			traverseLeafLink, stat, true);
4996 	} else {
4997 		// no path given: get the FD and use the FD operation
4998 		struct file_descriptor *descriptor
4999 			= get_fd(get_current_io_context(true), fd);
5000 		if (descriptor == NULL)
5001 			return FSSH_B_FILE_ERROR;
5002 
5003 		if (descriptor->ops->fd_read_stat)
5004 			status = descriptor->ops->fd_read_stat(descriptor, stat);
5005 		else
5006 			status = FSSH_EOPNOTSUPP;
5007 
5008 		put_fd(descriptor);
5009 	}
5010 
5011 	if (status == FSSH_B_OK && originalStat != NULL)
5012 		fssh_memcpy(originalStat, stat, statSize);
5013 
5014 	return status;
5015 }
5016 
5017 
5018 /**	\brief Writes stat data of an entity specified by a FD + path pair.
5019  *
5020  *	If only \a fd is given, the stat operation associated with the type
5021  *	of the FD (node, attr, attr dir etc.) is performed. If only \a path is
5022  *	given, this path identifies the entry for whose node to write the
5023  *	stat data. If both \a fd and \a path are given and the path is absolute,
5024  *	\a fd is ignored; a relative path is reckoned off of the directory (!)
5025  *	identified by \a fd and specifies the entry whose stat data shall be
5026  *	written.
5027  *
5028  *	\param fd The FD. May be < 0.
5029  *	\param path The absolute or relative path. Must not be \c NULL.
5030  *	\param traverseLeafLink If \a path is given, \c true specifies that the
5031  *		   function shall not stick to symlinks, but traverse them.
5032  *	\param stat The buffer containing the stat data to be written.
5033  *	\param statSize The size of the supplied stat buffer.
5034  *	\param statMask A mask specifying which parts of the stat data shall be
5035  *		   written.
5036  *	\return \c FSSH_B_OK, if the the stat data have been written successfully,
5037  *			another error code otherwise.
5038  */
5039 
5040 fssh_status_t
5041 _kern_write_stat(int fd, const char *path, bool traverseLeafLink,
5042 	const fssh_struct_stat *stat, fssh_size_t statSize, int statMask)
5043 {
5044 	fssh_struct_stat completeStat;
5045 
5046 	if (statSize > sizeof(fssh_struct_stat))
5047 		return FSSH_B_BAD_VALUE;
5048 
5049 	// this supports different stat extensions
5050 	if (statSize < sizeof(fssh_struct_stat)) {
5051 		fssh_memset((uint8_t *)&completeStat + statSize, 0, sizeof(fssh_struct_stat) - statSize);
5052 		fssh_memcpy(&completeStat, stat, statSize);
5053 		stat = &completeStat;
5054 	}
5055 
5056 	fssh_status_t status;
5057 
5058 	if (path) {
5059 		// path given: write the stat of the node referred to by (fd, path)
5060 		KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5061 		if (pathBuffer.InitCheck() != FSSH_B_OK)
5062 			return FSSH_B_NO_MEMORY;
5063 
5064 		status = common_path_write_stat(fd, pathBuffer.LockBuffer(),
5065 			traverseLeafLink, stat, statMask, true);
5066 	} else {
5067 		// no path given: get the FD and use the FD operation
5068 		struct file_descriptor *descriptor
5069 			= get_fd(get_current_io_context(true), fd);
5070 		if (descriptor == NULL)
5071 			return FSSH_B_FILE_ERROR;
5072 
5073 		if (descriptor->ops->fd_write_stat)
5074 			status = descriptor->ops->fd_write_stat(descriptor, stat, statMask);
5075 		else
5076 			status = FSSH_EOPNOTSUPP;
5077 
5078 		put_fd(descriptor);
5079 	}
5080 
5081 	return status;
5082 }
5083 
5084 
5085 int
5086 _kern_open_attr_dir(int fd, const char *path)
5087 {
5088 	KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
5089 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5090 		return FSSH_B_NO_MEMORY;
5091 
5092 	if (path != NULL)
5093 		pathBuffer.SetTo(path);
5094 
5095 	return attr_dir_open(fd, path ? pathBuffer.LockBuffer() : NULL, true);
5096 }
5097 
5098 
5099 int
5100 _kern_create_attr(int fd, const char *name, uint32_t type, int openMode)
5101 {
5102 	return attr_create(fd, name, type, openMode, true);
5103 }
5104 
5105 
5106 int
5107 _kern_open_attr(int fd, const char *name, int openMode)
5108 {
5109 	return attr_open(fd, name, openMode, true);
5110 }
5111 
5112 
5113 fssh_status_t
5114 _kern_remove_attr(int fd, const char *name)
5115 {
5116 	return attr_remove(fd, name, true);
5117 }
5118 
5119 
5120 fssh_status_t
5121 _kern_rename_attr(int fromFile, const char *fromName, int toFile, const char *toName)
5122 {
5123 	return attr_rename(fromFile, fromName, toFile, toName, true);
5124 }
5125 
5126 
5127 int
5128 _kern_open_index_dir(fssh_dev_t device)
5129 {
5130 	return index_dir_open(device, true);
5131 }
5132 
5133 
5134 fssh_status_t
5135 _kern_create_index(fssh_dev_t device, const char *name, uint32_t type, uint32_t flags)
5136 {
5137 	return index_create(device, name, type, flags, true);
5138 }
5139 
5140 
5141 fssh_status_t
5142 _kern_read_index_stat(fssh_dev_t device, const char *name, fssh_struct_stat *stat)
5143 {
5144 	return index_name_read_stat(device, name, stat, true);
5145 }
5146 
5147 
5148 fssh_status_t
5149 _kern_remove_index(fssh_dev_t device, const char *name)
5150 {
5151 	return index_remove(device, name, true);
5152 }
5153 
5154 
5155 fssh_status_t
5156 _kern_getcwd(char *buffer, fssh_size_t size)
5157 {
5158 	TRACE(("_kern_getcwd: buf %p, %ld\n", buffer, size));
5159 
5160 	// Call vfs to get current working directory
5161 	return get_cwd(buffer, size, true);
5162 }
5163 
5164 
5165 fssh_status_t
5166 _kern_setcwd(int fd, const char *path)
5167 {
5168 	KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
5169 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5170 		return FSSH_B_NO_MEMORY;
5171 
5172 	if (path != NULL)
5173 		pathBuffer.SetTo(path);
5174 
5175 	return set_cwd(fd, path != NULL ? pathBuffer.LockBuffer() : NULL, true);
5176 }
5177 
5178 
5179 fssh_status_t
5180 _kern_initialize_volume(const char* fsName, const char *partition,
5181 	const char *name, const char *parameters)
5182 {
5183 	if (!fsName || ! partition)
5184 		return FSSH_B_BAD_VALUE;
5185 
5186 	// The partition argument should point to a real file/device.
5187 
5188 	// open partition
5189 	int fd = fssh_open(partition, FSSH_O_RDWR);
5190 	if (fd < 0)
5191 		return fssh_errno;
5192 
5193 	// get the file system module
5194 	fssh_file_system_module_info* fsModule = get_file_system(fsName);
5195 	if (fsModule == NULL) {
5196 		fssh_close(fd);
5197 		return FSSH_ENODEV;
5198 	}
5199 
5200 	// initialize
5201 	fssh_status_t status;
5202 	if (fsModule->initialize) {
5203 		status = (*fsModule->initialize)(fd, -1, name, parameters, 0, -1);
5204 			// We've got no partition or job IDs -- the FS will hopefully
5205 			// ignore that.
5206 			// TODO: Get the actual size!
5207 	} else
5208 		status = FSSH_B_NOT_SUPPORTED;
5209 
5210 	// put the file system module, close partition
5211 	put_file_system(fsModule);
5212 	fssh_close(fd);
5213 
5214 	return status;
5215 }
5216 
5217 }	// namespace FSShell
5218