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