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