xref: /haiku/src/tools/fs_shell/vfs.cpp (revision d06cbe081b7ea043aea2012359744091de6d604d)
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 		fssh_vnode_id parentID;
1468 
1469 		// lookup the parent vnode
1470 		status = lookup_dir_entry(vnode, "..", &parentVnode);
1471 		if (status < FSSH_B_OK)
1472 			goto out;
1473 
1474 		// get the node's name
1475 		status = get_vnode_name(vnode, parentVnode,
1476 			(struct fssh_dirent*)nameBuffer, sizeof(nameBuffer));
1477 
1478 		// resolve a volume root to its mount point
1479 		mountPoint = resolve_volume_root_to_mount_point(parentVnode);
1480 		if (mountPoint) {
1481 			put_vnode(parentVnode);
1482 			parentVnode = mountPoint;
1483 			parentID = parentVnode->id;
1484 		}
1485 
1486 		bool hitRoot = (parentVnode == vnode);
1487 
1488 		// release the current vnode, we only need its parent from now on
1489 		put_vnode(vnode);
1490 		vnode = parentVnode;
1491 
1492 		if (status < FSSH_B_OK)
1493 			goto out;
1494 
1495 		if (hitRoot) {
1496 			// we have reached "/", which means we have constructed the full
1497 			// path
1498 			break;
1499 		}
1500 
1501 		// ToDo: add an explicit check for loops in about 10 levels to do
1502 		// real loop detection
1503 
1504 		// don't go deeper as 'maxLevel' to prevent circular loops
1505 		if (maxLevel-- < 0) {
1506 			status = FSSH_ELOOP;
1507 			goto out;
1508 		}
1509 
1510 		// add the name in front of the current path
1511 		name[FSSH_B_FILE_NAME_LENGTH - 1] = '\0';
1512 		length = fssh_strlen(name);
1513 		insert -= length;
1514 		if (insert <= 0) {
1515 			status = FSSH_ENOBUFS;
1516 			goto out;
1517 		}
1518 		fssh_memcpy(path + insert, name, length);
1519 		path[--insert] = '/';
1520 	}
1521 
1522 	// the root dir will result in an empty path: fix it
1523 	if (path[insert] == '\0')
1524 		path[--insert] = '/';
1525 
1526 	TRACE(("  path is: %s\n", path + insert));
1527 
1528 	// copy the path to the output buffer
1529 	length = pathBuffer.BufferSize() - insert;
1530 	if (length <= (int)bufferSize)
1531 		fssh_memcpy(buffer, path + insert, length);
1532 	else
1533 		status = FSSH_ENOBUFS;
1534 
1535 out:
1536 	put_vnode(vnode);
1537 	return status;
1538 }
1539 
1540 
1541 /**	Checks the length of every path component, and adds a '.'
1542  *	if the path ends in a slash.
1543  *	The given path buffer must be able to store at least one
1544  *	additional character.
1545  */
1546 
1547 static fssh_status_t
1548 check_path(char *to)
1549 {
1550 	int32_t length = 0;
1551 
1552 	// check length of every path component
1553 
1554 	while (*to) {
1555 		char *begin;
1556 		if (*to == '/')
1557 			to++, length++;
1558 
1559 		begin = to;
1560 		while (*to != '/' && *to)
1561 			to++, length++;
1562 
1563 		if (to - begin > FSSH_B_FILE_NAME_LENGTH)
1564 			return FSSH_B_NAME_TOO_LONG;
1565 	}
1566 
1567 	if (length == 0)
1568 		return FSSH_B_ENTRY_NOT_FOUND;
1569 
1570 	// complete path if there is a slash at the end
1571 
1572 	if (*(to - 1) == '/') {
1573 		if (length > FSSH_B_PATH_NAME_LENGTH - 2)
1574 			return FSSH_B_NAME_TOO_LONG;
1575 
1576 		to[0] = '.';
1577 		to[1] = '\0';
1578 	}
1579 
1580 	return FSSH_B_OK;
1581 }
1582 
1583 
1584 static struct file_descriptor *
1585 get_fd_and_vnode(int fd, struct vnode **_vnode, bool kernel)
1586 {
1587 	struct file_descriptor *descriptor = get_fd(get_current_io_context(kernel), fd);
1588 	if (descriptor == NULL)
1589 		return NULL;
1590 
1591 	if (fd_vnode(descriptor) == NULL) {
1592 		put_fd(descriptor);
1593 		return NULL;
1594 	}
1595 
1596 	// ToDo: when we can close a file descriptor at any point, investigate
1597 	//	if this is still valid to do (accessing the vnode without ref_count
1598 	//	or locking)
1599 	*_vnode = descriptor->u.vnode;
1600 	return descriptor;
1601 }
1602 
1603 
1604 static struct vnode *
1605 get_vnode_from_fd(int fd, bool kernel)
1606 {
1607 	struct file_descriptor *descriptor;
1608 	struct vnode *vnode;
1609 
1610 	descriptor = get_fd(get_current_io_context(kernel), fd);
1611 	if (descriptor == NULL)
1612 		return NULL;
1613 
1614 	vnode = fd_vnode(descriptor);
1615 	if (vnode != NULL)
1616 		inc_vnode_ref_count(vnode);
1617 
1618 	put_fd(descriptor);
1619 	return vnode;
1620 }
1621 
1622 
1623 /**	Gets the vnode from an FD + path combination. If \a fd is lower than zero,
1624  *	only the path will be considered. In this case, the \a path must not be
1625  *	NULL.
1626  *	If \a fd is a valid file descriptor, \a path may be NULL for directories,
1627  *	and should be NULL for files.
1628  */
1629 
1630 static fssh_status_t
1631 fd_and_path_to_vnode(int fd, char *path, bool traverseLeafLink,
1632 	struct vnode **_vnode, fssh_vnode_id *_parentID, bool kernel)
1633 {
1634 	if (fd < 0 && !path)
1635 		return FSSH_B_BAD_VALUE;
1636 
1637 	if (fd < 0 || (path != NULL && path[0] == '/')) {
1638 		// no FD or absolute path
1639 		return path_to_vnode(path, traverseLeafLink, _vnode, _parentID, kernel);
1640 	}
1641 
1642 	// FD only, or FD + relative path
1643 	struct vnode *vnode = get_vnode_from_fd(fd, kernel);
1644 	if (!vnode)
1645 		return FSSH_B_FILE_ERROR;
1646 
1647 	if (path != NULL) {
1648 		return vnode_path_to_vnode(vnode, path, traverseLeafLink, 0,
1649 			_vnode, _parentID);
1650 	}
1651 
1652 	// there is no relative path to take into account
1653 
1654 	*_vnode = vnode;
1655 	if (_parentID)
1656 		*_parentID = -1;
1657 
1658 	return FSSH_B_OK;
1659 }
1660 
1661 
1662 static int
1663 get_new_fd(int type, struct fs_mount *mount, struct vnode *vnode,
1664 	void *cookie, int openMode, bool kernel)
1665 {
1666 	struct file_descriptor *descriptor;
1667 	int fd;
1668 
1669 	// if the vnode is locked, we don't allow creating a new file descriptor for it
1670 	if (vnode && vnode->mandatory_locked_by != NULL)
1671 		return FSSH_B_BUSY;
1672 
1673 	descriptor = alloc_fd();
1674 	if (!descriptor)
1675 		return FSSH_B_NO_MEMORY;
1676 
1677 	if (vnode)
1678 		descriptor->u.vnode = vnode;
1679 	else
1680 		descriptor->u.mount = mount;
1681 	descriptor->cookie = cookie;
1682 
1683 	switch (type) {
1684 		// vnode types
1685 		case FDTYPE_FILE:
1686 			descriptor->ops = &sFileOps;
1687 			break;
1688 		case FDTYPE_DIR:
1689 			descriptor->ops = &sDirectoryOps;
1690 			break;
1691 		case FDTYPE_ATTR:
1692 			descriptor->ops = &sAttributeOps;
1693 			break;
1694 		case FDTYPE_ATTR_DIR:
1695 			descriptor->ops = &sAttributeDirectoryOps;
1696 			break;
1697 
1698 		// mount types
1699 		case FDTYPE_INDEX_DIR:
1700 			descriptor->ops = &sIndexDirectoryOps;
1701 			break;
1702 		case FDTYPE_QUERY:
1703 			descriptor->ops = &sQueryOps;
1704 			break;
1705 
1706 		default:
1707 			fssh_panic("get_new_fd() called with unknown type %d\n", type);
1708 			break;
1709 	}
1710 	descriptor->type = type;
1711 	descriptor->open_mode = openMode;
1712 
1713 	fd = new_fd(get_current_io_context(kernel), descriptor);
1714 	if (fd < 0) {
1715 		free(descriptor);
1716 		return FSSH_B_NO_MORE_FDS;
1717 	}
1718 
1719 	return fd;
1720 }
1721 
1722 
1723 /*!	Does the dirty work of combining the file_io_vecs with the iovecs
1724 	and calls the file system hooks to read/write the request to disk.
1725 */
1726 static fssh_status_t
1727 common_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs,
1728 	fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount,
1729 	uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_numBytes,
1730 	bool doWrite)
1731 {
1732 	if (fileVecCount == 0) {
1733 		// There are no file vecs at this offset, so we're obviously trying
1734 		// to access the file outside of its bounds
1735 		return FSSH_B_BAD_VALUE;
1736 	}
1737 
1738 	fssh_size_t numBytes = *_numBytes;
1739 	uint32_t fileVecIndex;
1740 	fssh_size_t vecOffset = *_vecOffset;
1741 	uint32_t vecIndex = *_vecIndex;
1742 	fssh_status_t status;
1743 	fssh_size_t size;
1744 
1745 	if (!doWrite && vecOffset == 0) {
1746 		// now directly read the data from the device
1747 		// the first file_io_vec can be read directly
1748 
1749 		size = fileVecs[0].length;
1750 		if (size > numBytes)
1751 			size = numBytes;
1752 
1753 		status = fssh_read_pages(fd, fileVecs[0].offset, &vecs[vecIndex],
1754 			vecCount - vecIndex, &size);
1755 		if (status < FSSH_B_OK)
1756 			return status;
1757 
1758 		// TODO: this is a work-around for buggy device drivers!
1759 		//	When our own drivers honour the length, we can:
1760 		//	a) also use this direct I/O for writes (otherwise, it would
1761 		//	   overwrite precious data)
1762 		//	b) panic if the term below is true (at least for writes)
1763 		if ((uint64_t)size > (uint64_t)fileVecs[0].length) {
1764 			//dprintf("warning: device driver %p doesn't respect total length in read_pages() call!\n", ref->device);
1765 			size = fileVecs[0].length;
1766 		}
1767 
1768 		ASSERT(size <= fileVecs[0].length);
1769 
1770 		// If the file portion was contiguous, we're already done now
1771 		if (size == numBytes)
1772 			return FSSH_B_OK;
1773 
1774 		// if we reached the end of the file, we can return as well
1775 		if ((uint64_t)size != (uint64_t)fileVecs[0].length) {
1776 			*_numBytes = size;
1777 			return FSSH_B_OK;
1778 		}
1779 
1780 		fileVecIndex = 1;
1781 
1782 		// first, find out where we have to continue in our iovecs
1783 		for (; vecIndex < vecCount; vecIndex++) {
1784 			if (size < vecs[vecIndex].iov_len)
1785 				break;
1786 
1787 			size -= vecs[vecIndex].iov_len;
1788 		}
1789 
1790 		vecOffset = size;
1791 	} else {
1792 		fileVecIndex = 0;
1793 		size = 0;
1794 	}
1795 
1796 	// Too bad, let's process the rest of the file_io_vecs
1797 
1798 	fssh_size_t totalSize = size;
1799 	fssh_size_t bytesLeft = numBytes - size;
1800 
1801 	for (; fileVecIndex < fileVecCount; fileVecIndex++) {
1802 		const fssh_file_io_vec &fileVec = fileVecs[fileVecIndex];
1803 		fssh_off_t fileOffset = fileVec.offset;
1804 		fssh_off_t fileLeft = fssh_min_c((uint64_t)fileVec.length, (uint64_t)bytesLeft);
1805 
1806 		TRACE(("FILE VEC [%lu] length %Ld\n", fileVecIndex, fileLeft));
1807 
1808 		// process the complete fileVec
1809 		while (fileLeft > 0) {
1810 			fssh_iovec tempVecs[MAX_TEMP_IO_VECS];
1811 			uint32_t tempCount = 0;
1812 
1813 			// size tracks how much of what is left of the current fileVec
1814 			// (fileLeft) has been assigned to tempVecs
1815 			size = 0;
1816 
1817 			// assign what is left of the current fileVec to the tempVecs
1818 			for (size = 0; (uint64_t)size < (uint64_t)fileLeft && vecIndex < vecCount
1819 					&& tempCount < MAX_TEMP_IO_VECS;) {
1820 				// try to satisfy one iovec per iteration (or as much as
1821 				// possible)
1822 
1823 				// bytes left of the current iovec
1824 				fssh_size_t vecLeft = vecs[vecIndex].iov_len - vecOffset;
1825 				if (vecLeft == 0) {
1826 					vecOffset = 0;
1827 					vecIndex++;
1828 					continue;
1829 				}
1830 
1831 				TRACE(("fill vec %ld, offset = %lu, size = %lu\n",
1832 					vecIndex, vecOffset, size));
1833 
1834 				// actually available bytes
1835 				fssh_size_t tempVecSize = fssh_min_c(vecLeft, fileLeft - size);
1836 
1837 				tempVecs[tempCount].iov_base
1838 					= (void *)((fssh_addr_t)vecs[vecIndex].iov_base + vecOffset);
1839 				tempVecs[tempCount].iov_len = tempVecSize;
1840 				tempCount++;
1841 
1842 				size += tempVecSize;
1843 				vecOffset += tempVecSize;
1844 			}
1845 
1846 			fssh_size_t bytes = size;
1847 			if (doWrite) {
1848 				status = fssh_write_pages(fd, fileOffset, tempVecs,
1849 					tempCount, &bytes);
1850 			} else {
1851 				status = fssh_read_pages(fd, fileOffset, tempVecs,
1852 					tempCount, &bytes);
1853 			}
1854 			if (status < FSSH_B_OK)
1855 				return status;
1856 
1857 			totalSize += bytes;
1858 			bytesLeft -= size;
1859 			fileOffset += size;
1860 			fileLeft -= size;
1861 
1862 			if (size != bytes || vecIndex >= vecCount) {
1863 				// there are no more bytes or iovecs, let's bail out
1864 				*_numBytes = totalSize;
1865 				return FSSH_B_OK;
1866 			}
1867 		}
1868 	}
1869 
1870 	*_vecIndex = vecIndex;
1871 	*_vecOffset = vecOffset;
1872 	*_numBytes = totalSize;
1873 	return FSSH_B_OK;
1874 }
1875 
1876 
1877 //	#pragma mark - public VFS API
1878 
1879 
1880 extern "C" fssh_status_t
1881 fssh_new_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID,
1882 	void *privateNode, fssh_fs_vnode_ops *ops)
1883 {
1884 	FUNCTION(("new_vnode(volume = %p (%ld), vnodeID = %Ld, node = %p)\n",
1885 		volume, volume->id, vnodeID, privateNode));
1886 
1887 	if (privateNode == NULL)
1888 		return FSSH_B_BAD_VALUE;
1889 
1890 	fssh_mutex_lock(&sVnodeMutex);
1891 
1892 	// file system integrity check:
1893 	// test if the vnode already exists and bail out if this is the case!
1894 
1895 	// ToDo: the R5 implementation obviously checks for a different cookie
1896 	//	and doesn't panic if they are equal
1897 
1898 	struct vnode *vnode = lookup_vnode(volume->id, vnodeID);
1899 	if (vnode != NULL) {
1900 		fssh_panic("vnode %d:%" FSSH_B_PRIdINO " already exists (node = %p, "
1901 			"vnode->node = %p)!", (int)volume->id, vnodeID, privateNode,
1902 			vnode->private_node);
1903 	}
1904 
1905 	fssh_status_t status = create_new_vnode(&vnode, volume->id, vnodeID);
1906 	if (status == FSSH_B_OK) {
1907 		vnode->private_node = privateNode;
1908 		vnode->ops = ops;
1909 		vnode->busy = true;
1910 		vnode->unpublished = true;
1911 	}
1912 
1913 	TRACE(("returns: %s\n", strerror(status)));
1914 
1915 	fssh_mutex_unlock(&sVnodeMutex);
1916 	return status;
1917 }
1918 
1919 
1920 extern "C" fssh_status_t
1921 fssh_publish_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID,
1922 	void *privateNode, fssh_fs_vnode_ops *ops, int type, uint32_t flags)
1923 {
1924 	FUNCTION(("publish_vnode()\n"));
1925 
1926 	MutexLocker locker(sVnodeMutex);
1927 
1928 	struct vnode *vnode = lookup_vnode(volume->id, vnodeID);
1929 	fssh_status_t status = FSSH_B_OK;
1930 
1931 	if (vnode != NULL && vnode->busy && vnode->unpublished
1932 		&& vnode->private_node == privateNode) {
1933 		// already known, but not published
1934 	} else if (vnode == NULL && privateNode != NULL) {
1935 		status = create_new_vnode(&vnode, volume->id, vnodeID);
1936 		if (status == FSSH_B_OK) {
1937 			vnode->private_node = privateNode;
1938 			vnode->ops = ops;
1939 			vnode->busy = true;
1940 			vnode->unpublished = true;
1941 		}
1942 	} else
1943 		status = FSSH_B_BAD_VALUE;
1944 
1945 	// create sub vnodes, if necessary
1946 	if (status == FSSH_B_OK && volume->sub_volume != NULL) {
1947 		locker.Unlock();
1948 
1949 		fssh_fs_volume *subVolume = volume;
1950 		while (status == FSSH_B_OK && subVolume->sub_volume != NULL) {
1951 			subVolume = subVolume->sub_volume;
1952 			status = subVolume->ops->create_sub_vnode(subVolume, vnodeID,
1953 				vnode);
1954 		}
1955 
1956 		if (status != FSSH_B_OK) {
1957 			// error -- clean up the created sub vnodes
1958 			while (subVolume->super_volume != volume) {
1959 				subVolume = subVolume->super_volume;
1960 				subVolume->ops->delete_sub_vnode(subVolume, vnode);
1961 			}
1962 		}
1963 
1964 		locker.Lock();
1965 	}
1966 
1967 	if (status == FSSH_B_OK) {
1968 		vnode->type = type;
1969 		vnode->busy = false;
1970 		vnode->unpublished = false;
1971 	}
1972 
1973 	TRACE(("returns: %s\n", strerror(status)));
1974 
1975 	return status;
1976 }
1977 
1978 
1979 extern "C" fssh_status_t
1980 fssh_get_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID,
1981 	void **privateNode)
1982 {
1983 	struct vnode *vnode;
1984 
1985 	if (volume == NULL)
1986 		return FSSH_B_BAD_VALUE;
1987 
1988 	fssh_status_t status = get_vnode(volume->id, vnodeID, &vnode, true);
1989 	if (status < FSSH_B_OK)
1990 		return status;
1991 
1992 	// If this is a layered FS, we need to get the node cookie for the requested
1993 	// layer.
1994 	if (HAS_FS_CALL(vnode, get_super_vnode)) {
1995 		fssh_fs_vnode resolvedNode;
1996 		fssh_status_t status = FS_CALL(vnode, get_super_vnode, volume,
1997 			&resolvedNode);
1998 		if (status != FSSH_B_OK) {
1999 			fssh_panic("get_vnode(): Failed to get super node for vnode %p, "
2000 				"volume: %p", vnode, volume);
2001 			put_vnode(vnode);
2002 			return status;
2003 		}
2004 
2005 		if (privateNode != NULL)
2006 			*privateNode = resolvedNode.private_node;
2007 	} else if (privateNode != NULL)
2008 		*privateNode = vnode->private_node;
2009 
2010 	return FSSH_B_OK;
2011 }
2012 
2013 
2014 extern "C" fssh_status_t
2015 fssh_acquire_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID)
2016 {
2017 	struct vnode *vnode;
2018 
2019 	fssh_mutex_lock(&sVnodeMutex);
2020 	vnode = lookup_vnode(volume->id, vnodeID);
2021 	fssh_mutex_unlock(&sVnodeMutex);
2022 
2023 	if (vnode == NULL)
2024 		return FSSH_B_BAD_VALUE;
2025 
2026 	inc_vnode_ref_count(vnode);
2027 	return FSSH_B_OK;
2028 }
2029 
2030 
2031 extern "C" fssh_status_t
2032 fssh_put_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID)
2033 {
2034 	struct vnode *vnode;
2035 
2036 	fssh_mutex_lock(&sVnodeMutex);
2037 	vnode = lookup_vnode(volume->id, vnodeID);
2038 	fssh_mutex_unlock(&sVnodeMutex);
2039 
2040 	if (vnode == NULL)
2041 		return FSSH_B_BAD_VALUE;
2042 
2043 	dec_vnode_ref_count(vnode, true);
2044 	return FSSH_B_OK;
2045 }
2046 
2047 
2048 extern "C" fssh_status_t
2049 fssh_remove_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID)
2050 {
2051 	struct vnode *vnode;
2052 	bool remove = false;
2053 
2054 	MutexLocker locker(sVnodeMutex);
2055 
2056 	vnode = lookup_vnode(volume->id, vnodeID);
2057 	if (vnode == NULL)
2058 		return FSSH_B_ENTRY_NOT_FOUND;
2059 
2060 	if (vnode->covered_by != NULL) {
2061 		// this vnode is in use
2062 		fssh_mutex_unlock(&sVnodeMutex);
2063 		return FSSH_B_BUSY;
2064 	}
2065 
2066 	vnode->remove = true;
2067 	if (vnode->unpublished) {
2068 		// prepare the vnode for deletion
2069 		vnode->busy = true;
2070 		remove = true;
2071 	}
2072 
2073 	locker.Unlock();
2074 
2075 	if (remove) {
2076 		// if the vnode hasn't been published yet, we delete it here
2077 		fssh_atomic_add(&vnode->ref_count, -1);
2078 		free_vnode(vnode, true);
2079 	}
2080 
2081 	return FSSH_B_OK;
2082 }
2083 
2084 
2085 extern "C" fssh_status_t
2086 fssh_unremove_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID)
2087 {
2088 	struct vnode *vnode;
2089 
2090 	fssh_mutex_lock(&sVnodeMutex);
2091 
2092 	vnode = lookup_vnode(volume->id, vnodeID);
2093 	if (vnode)
2094 		vnode->remove = false;
2095 
2096 	fssh_mutex_unlock(&sVnodeMutex);
2097 	return FSSH_B_OK;
2098 }
2099 
2100 
2101 extern "C" fssh_status_t
2102 fssh_get_vnode_removed(fssh_fs_volume *volume, fssh_vnode_id vnodeID, bool* removed)
2103 {
2104 	fssh_mutex_lock(&sVnodeMutex);
2105 
2106 	fssh_status_t result;
2107 
2108 	if (struct vnode* vnode = lookup_vnode(volume->id, vnodeID)) {
2109 		if (removed)
2110 			*removed = vnode->remove;
2111 		result = FSSH_B_OK;
2112 	} else
2113 		result = FSSH_B_BAD_VALUE;
2114 
2115 	fssh_mutex_unlock(&sVnodeMutex);
2116 	return result;
2117 }
2118 
2119 
2120 extern "C" fssh_fs_volume*
2121 fssh_volume_for_vnode(fssh_fs_vnode *_vnode)
2122 {
2123 	if (_vnode == NULL)
2124 		return NULL;
2125 
2126 	struct vnode* vnode = static_cast<struct vnode*>(_vnode);
2127 	return vnode->mount->volume;
2128 }
2129 
2130 
2131 //! Works directly on the host's file system
2132 extern "C" fssh_status_t
2133 fssh_read_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs,
2134 	fssh_size_t count, fssh_size_t *_numBytes)
2135 {
2136 	// check how much the iovecs allow us to read
2137 	fssh_size_t toRead = 0;
2138 	for (fssh_size_t i = 0; i < count; i++)
2139 		toRead += vecs[i].iov_len;
2140 
2141 	fssh_iovec* newVecs = NULL;
2142 	if (*_numBytes < toRead) {
2143 		// We're supposed to read less than specified by the vecs. Since
2144 		// readv_pos() doesn't support this, we need to clone the vecs.
2145 		newVecs = new(std::nothrow) fssh_iovec[count];
2146 		if (!newVecs)
2147 			return FSSH_B_NO_MEMORY;
2148 
2149 		fssh_size_t newCount = 0;
2150 		for (fssh_size_t i = 0; i < count && toRead > 0; i++) {
2151 			fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toRead);
2152 			newVecs[i].iov_base = vecs[i].iov_base;
2153 			newVecs[i].iov_len = vecLen;
2154 			toRead -= vecLen;
2155 			newCount++;
2156 		}
2157 
2158 		vecs = newVecs;
2159 		count = newCount;
2160 	}
2161 
2162 	fssh_ssize_t bytesRead = fssh_readv_pos(fd, pos, vecs, count);
2163 	delete[] newVecs;
2164 	if (bytesRead < 0)
2165 		return fssh_get_errno();
2166 
2167 	*_numBytes = bytesRead;
2168 	return FSSH_B_OK;
2169 }
2170 
2171 
2172 //! Works directly on the host's file system
2173 extern "C" fssh_status_t
2174 fssh_write_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs,
2175 	fssh_size_t count, fssh_size_t *_numBytes)
2176 {
2177 	// check how much the iovecs allow us to write
2178 	fssh_size_t toWrite = 0;
2179 	for (fssh_size_t i = 0; i < count; i++)
2180 		toWrite += vecs[i].iov_len;
2181 
2182 	fssh_iovec* newVecs = NULL;
2183 	if (*_numBytes < toWrite) {
2184 		// We're supposed to write less than specified by the vecs. Since
2185 		// writev_pos() doesn't support this, we need to clone the vecs.
2186 		newVecs = new(std::nothrow) fssh_iovec[count];
2187 		if (!newVecs)
2188 			return FSSH_B_NO_MEMORY;
2189 
2190 		fssh_size_t newCount = 0;
2191 		for (fssh_size_t i = 0; i < count && toWrite > 0; i++) {
2192 			fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toWrite);
2193 			newVecs[i].iov_base = vecs[i].iov_base;
2194 			newVecs[i].iov_len = vecLen;
2195 			toWrite -= vecLen;
2196 			newCount++;
2197 		}
2198 
2199 		vecs = newVecs;
2200 		count = newCount;
2201 	}
2202 
2203 	fssh_ssize_t bytesWritten = fssh_writev_pos(fd, pos, vecs, count);
2204 	delete[] newVecs;
2205 	if (bytesWritten < 0)
2206 		return fssh_get_errno();
2207 
2208 	*_numBytes = bytesWritten;
2209 	return FSSH_B_OK;
2210 }
2211 
2212 
2213 //! Works directly on the host's file system
2214 extern "C" fssh_status_t
2215 fssh_read_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs,
2216 	fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount,
2217 	uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_bytes)
2218 {
2219 	return common_file_io_vec_pages(fd, fileVecs, fileVecCount,
2220 		vecs, vecCount, _vecIndex, _vecOffset, _bytes, false);
2221 }
2222 
2223 
2224 //! Works directly on the host's file system
2225 extern "C" fssh_status_t
2226 fssh_write_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs,
2227 	fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount,
2228 	uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_bytes)
2229 {
2230 	return common_file_io_vec_pages(fd, fileVecs, fileVecCount,
2231 		vecs, vecCount, _vecIndex, _vecOffset, _bytes, true);
2232 }
2233 
2234 
2235 extern "C" fssh_status_t
2236 fssh_entry_cache_add(fssh_dev_t mountID, fssh_ino_t dirID, const char* name,
2237 	fssh_ino_t nodeID)
2238 {
2239 	// We don't implement an entry cache in the FS shell.
2240 	return FSSH_B_OK;
2241 }
2242 
2243 
2244 extern "C" fssh_status_t
2245 fssh_entry_cache_remove(fssh_dev_t mountID, fssh_ino_t dirID, const char* name)
2246 {
2247 	// We don't implement an entry cache in the FS shell.
2248 	return FSSH_B_ENTRY_NOT_FOUND;
2249 }
2250 
2251 
2252 //	#pragma mark - private VFS API
2253 //	Functions the VFS exports for other parts of the kernel
2254 
2255 
2256 /** Acquires another reference to the vnode that has to be released
2257  *	by calling vfs_put_vnode().
2258  */
2259 
2260 void
2261 vfs_acquire_vnode(void *_vnode)
2262 {
2263 	inc_vnode_ref_count((struct vnode *)_vnode);
2264 }
2265 
2266 
2267 /** This is currently called from file_cache_create() only.
2268  *	It's probably a temporary solution as long as devfs requires that
2269  *	fs_read_pages()/fs_write_pages() are called with the standard
2270  *	open cookie and not with a device cookie.
2271  *	If that's done differently, remove this call; it has no other
2272  *	purpose.
2273  */
2274 
2275 fssh_status_t
2276 vfs_get_cookie_from_fd(int fd, void **_cookie)
2277 {
2278 	struct file_descriptor *descriptor;
2279 
2280 	descriptor = get_fd(get_current_io_context(true), fd);
2281 	if (descriptor == NULL)
2282 		return FSSH_B_FILE_ERROR;
2283 
2284 	*_cookie = descriptor->cookie;
2285 	return FSSH_B_OK;
2286 }
2287 
2288 
2289 int
2290 vfs_get_vnode_from_fd(int fd, bool kernel, void **vnode)
2291 {
2292 	*vnode = get_vnode_from_fd(fd, kernel);
2293 
2294 	if (*vnode == NULL)
2295 		return FSSH_B_FILE_ERROR;
2296 
2297 	return FSSH_B_NO_ERROR;
2298 }
2299 
2300 
2301 fssh_status_t
2302 vfs_get_vnode_from_path(const char *path, bool kernel, void **_vnode)
2303 {
2304 	TRACE(("vfs_get_vnode_from_path: entry. path = '%s', kernel %d\n", path, kernel));
2305 
2306 	KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
2307 	if (pathBuffer.InitCheck() != FSSH_B_OK)
2308 		return FSSH_B_NO_MEMORY;
2309 
2310 	char *buffer = pathBuffer.LockBuffer();
2311 	fssh_strlcpy(buffer, path, pathBuffer.BufferSize());
2312 
2313 	struct vnode *vnode;
2314 	fssh_status_t status = path_to_vnode(buffer, true, &vnode, NULL, kernel);
2315 	if (status < FSSH_B_OK)
2316 		return status;
2317 
2318 	*_vnode = vnode;
2319 	return FSSH_B_OK;
2320 }
2321 
2322 
2323 fssh_status_t
2324 vfs_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void **_vnode)
2325 {
2326 	struct vnode *vnode;
2327 
2328 	fssh_status_t status = get_vnode(mountID, vnodeID, &vnode, false);
2329 	if (status < FSSH_B_OK)
2330 		return status;
2331 
2332 	*_vnode = vnode;
2333 	return FSSH_B_OK;
2334 }
2335 
2336 
2337 fssh_status_t
2338 vfs_read_pages(void *_vnode, void *cookie, fssh_off_t pos,
2339 	const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes)
2340 {
2341 	struct vnode *vnode = (struct vnode *)_vnode;
2342 
2343 	return FS_CALL(vnode, read_pages,
2344 		cookie, pos, vecs, count, _numBytes);
2345 }
2346 
2347 
2348 fssh_status_t
2349 vfs_write_pages(void *_vnode, void *cookie, fssh_off_t pos,
2350 	const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes)
2351 {
2352 	struct vnode *vnode = (struct vnode *)_vnode;
2353 
2354 	return FS_CALL(vnode, write_pages,
2355 		cookie, pos, vecs, count, _numBytes);
2356 }
2357 
2358 
2359 fssh_status_t
2360 vfs_entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID,
2361 	const char *name, void **_vnode)
2362 {
2363 	return entry_ref_to_vnode(mountID, directoryID, name,
2364 		(struct vnode **)_vnode);
2365 }
2366 
2367 
2368 void
2369 vfs_fs_vnode_to_node_ref(void *_vnode, fssh_mount_id *_mountID,
2370 	fssh_vnode_id *_vnodeID)
2371 {
2372 	struct vnode *vnode = (struct vnode *)_vnode;
2373 
2374 	*_mountID = vnode->device;
2375 	*_vnodeID = vnode->id;
2376 }
2377 
2378 
2379 /**	Looks up a vnode with the given mount and vnode ID.
2380  *	Must only be used with "in-use" vnodes as it doesn't grab a reference
2381  *	to the node.
2382  *	It's currently only be used by file_cache_create().
2383  */
2384 
2385 fssh_status_t
2386 vfs_lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID,
2387 	struct vnode **_vnode)
2388 {
2389 	fssh_mutex_lock(&sVnodeMutex);
2390 	struct vnode *vnode = lookup_vnode(mountID, vnodeID);
2391 	fssh_mutex_unlock(&sVnodeMutex);
2392 
2393 	if (vnode == NULL)
2394 		return FSSH_B_ERROR;
2395 
2396 	*_vnode = vnode;
2397 	return FSSH_B_OK;
2398 }
2399 
2400 
2401 fssh_status_t
2402 vfs_get_fs_node_from_path(fssh_fs_volume *volume, const char *path,
2403 	bool kernel, void **_node)
2404 {
2405 	TRACE(("vfs_get_fs_node_from_path(volume = %p (%ld), path = \"%s\", "
2406 		"kernel %d)\n", volume, volume->id, path, kernel));
2407 
2408 	KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
2409 	if (pathBuffer.InitCheck() != FSSH_B_OK)
2410 		return FSSH_B_NO_MEMORY;
2411 
2412 	fs_mount *mount;
2413 	fssh_status_t status = get_mount(volume->id, &mount);
2414 	if (status < FSSH_B_OK)
2415 		return status;
2416 
2417 	char *buffer = pathBuffer.LockBuffer();
2418 	fssh_strlcpy(buffer, path, pathBuffer.BufferSize());
2419 
2420 	struct vnode *vnode = mount->root_vnode;
2421 
2422 	if (buffer[0] == '/')
2423 		status = path_to_vnode(buffer, true, &vnode, NULL, true);
2424 	else {
2425 		inc_vnode_ref_count(vnode);
2426 			// vnode_path_to_vnode() releases a reference to the starting vnode
2427 		status = vnode_path_to_vnode(vnode, buffer, true, 0, &vnode, NULL);
2428 	}
2429 
2430 	put_mount(mount);
2431 
2432 	if (status < FSSH_B_OK)
2433 		return status;
2434 
2435 	if (vnode->device != volume->id) {
2436 		// wrong mount ID - must not gain access on foreign file system nodes
2437 		put_vnode(vnode);
2438 		return FSSH_B_BAD_VALUE;
2439 	}
2440 
2441 	// Use get_vnode() to resolve the cookie for the right layer.
2442 	status = ::fssh_get_vnode(volume, vnode->id, _node);
2443 	put_vnode(vnode);
2444 
2445 	return FSSH_B_OK;
2446 }
2447 
2448 
2449 /**	Finds the full path to the file that contains the module \a moduleName,
2450  *	puts it into \a pathBuffer, and returns FSSH_B_OK for success.
2451  *	If \a pathBuffer was too small, it returns \c FSSH_B_BUFFER_OVERFLOW,
2452  *	\c FSSH_B_ENTRY_NOT_FOUNT if no file could be found.
2453  *	\a pathBuffer is clobbered in any case and must not be relied on if this
2454  *	functions returns unsuccessfully.
2455  */
2456 
2457 fssh_status_t
2458 vfs_get_module_path(const char *basePath, const char *moduleName, char *pathBuffer,
2459 	fssh_size_t bufferSize)
2460 {
2461 	struct vnode *dir, *file;
2462 	fssh_status_t status;
2463 	fssh_size_t length;
2464 	char *path;
2465 
2466 	if (bufferSize == 0 || fssh_strlcpy(pathBuffer, basePath, bufferSize) >= bufferSize)
2467 		return FSSH_B_BUFFER_OVERFLOW;
2468 
2469 	status = path_to_vnode(pathBuffer, true, &dir, NULL, true);
2470 	if (status < FSSH_B_OK)
2471 		return status;
2472 
2473 	// the path buffer had been clobbered by the above call
2474 	length = fssh_strlcpy(pathBuffer, basePath, bufferSize);
2475 	if (pathBuffer[length - 1] != '/')
2476 		pathBuffer[length++] = '/';
2477 
2478 	path = pathBuffer + length;
2479 	bufferSize -= length;
2480 
2481 	while (moduleName) {
2482 		char *nextPath = fssh_strchr(moduleName, '/');
2483 		if (nextPath == NULL)
2484 			length = fssh_strlen(moduleName);
2485 		else {
2486 			length = nextPath - moduleName;
2487 			nextPath++;
2488 		}
2489 
2490 		if (length + 1 >= bufferSize) {
2491 			status = FSSH_B_BUFFER_OVERFLOW;
2492 			goto err;
2493 		}
2494 
2495 		fssh_memcpy(path, moduleName, length);
2496 		path[length] = '\0';
2497 		moduleName = nextPath;
2498 
2499 		status = vnode_path_to_vnode(dir, path, true, 0, &file, NULL);
2500 		if (status < FSSH_B_OK) {
2501 			// vnode_path_to_vnode() has already released the reference to dir
2502 			return status;
2503 		}
2504 
2505 		if (FSSH_S_ISDIR(file->type)) {
2506 			// goto the next directory
2507 			path[length] = '/';
2508 			path[length + 1] = '\0';
2509 			path += length + 1;
2510 			bufferSize -= length + 1;
2511 
2512 			dir = file;
2513 		} else if (FSSH_S_ISREG(file->type)) {
2514 			// it's a file so it should be what we've searched for
2515 			put_vnode(file);
2516 
2517 			return FSSH_B_OK;
2518 		} else {
2519 			TRACE(("vfs_get_module_path(): something is strange here: %d...\n", file->type));
2520 			status = FSSH_B_ERROR;
2521 			dir = file;
2522 			goto err;
2523 		}
2524 	}
2525 
2526 	// if we got here, the moduleName just pointed to a directory, not to
2527 	// a real module - what should we do in this case?
2528 	status = FSSH_B_ENTRY_NOT_FOUND;
2529 
2530 err:
2531 	put_vnode(dir);
2532 	return status;
2533 }
2534 
2535 
2536 /**	\brief Normalizes a given path.
2537  *
2538  *	The path must refer to an existing or non-existing entry in an existing
2539  *	directory, that is chopping off the leaf component the remaining path must
2540  *	refer to an existing directory.
2541  *
2542  *	The returned will be canonical in that it will be absolute, will not
2543  *	contain any "." or ".." components or duplicate occurrences of '/'s,
2544  *	and none of the directory components will by symbolic links.
2545  *
2546  *	Any two paths referring to the same entry, will result in the same
2547  *	normalized path (well, that is pretty much the definition of `normalized',
2548  *	isn't it :-).
2549  *
2550  *	\param path The path to be normalized.
2551  *	\param buffer The buffer into which the normalized path will be written.
2552  *	\param bufferSize The size of \a buffer.
2553  *	\param kernel \c true, if the IO context of the kernel shall be used,
2554  *		   otherwise that of the team this thread belongs to. Only relevant,
2555  *		   if the path is relative (to get the CWD).
2556  *	\return \c FSSH_B_OK if everything went fine, another error code otherwise.
2557  */
2558 
2559 fssh_status_t
2560 vfs_normalize_path(const char *path, char *buffer, fssh_size_t bufferSize,
2561 	bool kernel)
2562 {
2563 	if (!path || !buffer || bufferSize < 1)
2564 		return FSSH_B_BAD_VALUE;
2565 
2566 	TRACE(("vfs_normalize_path(`%s')\n", path));
2567 
2568 	// copy the supplied path to the stack, so it can be modified
2569 	KPath mutablePathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
2570 	if (mutablePathBuffer.InitCheck() != FSSH_B_OK)
2571 		return FSSH_B_NO_MEMORY;
2572 
2573 	char *mutablePath = mutablePathBuffer.LockBuffer();
2574 	if (fssh_strlcpy(mutablePath, path, FSSH_B_PATH_NAME_LENGTH) >= FSSH_B_PATH_NAME_LENGTH)
2575 		return FSSH_B_NAME_TOO_LONG;
2576 
2577 	// get the dir vnode and the leaf name
2578 	struct vnode *dirNode;
2579 	char leaf[FSSH_B_FILE_NAME_LENGTH];
2580 	fssh_status_t error = path_to_dir_vnode(mutablePath, &dirNode, leaf, kernel);
2581 	if (error != FSSH_B_OK) {
2582 		TRACE(("vfs_normalize_path(): failed to get dir vnode: %s\n", strerror(error)));
2583 		return error;
2584 	}
2585 
2586 	// if the leaf is "." or "..", we directly get the correct directory
2587 	// vnode and ignore the leaf later
2588 	bool isDir = (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0);
2589 	if (isDir)
2590 		error = vnode_path_to_vnode(dirNode, leaf, false, 0, &dirNode, NULL);
2591 	if (error != FSSH_B_OK) {
2592 		TRACE(("vfs_normalize_path(): failed to get dir vnode for \".\" or \"..\": %s\n",
2593 			strerror(error)));
2594 		return error;
2595 	}
2596 
2597 	// get the directory path
2598 	error = dir_vnode_to_path(dirNode, buffer, bufferSize);
2599 	put_vnode(dirNode);
2600 	if (error < FSSH_B_OK) {
2601 		TRACE(("vfs_normalize_path(): failed to get dir path: %s\n", strerror(error)));
2602 		return error;
2603 	}
2604 
2605 	// append the leaf name
2606 	if (!isDir) {
2607 		// insert a directory separator only if this is not the file system root
2608 		if ((fssh_strcmp(buffer, "/") != 0
2609 			 && fssh_strlcat(buffer, "/", bufferSize) >= bufferSize)
2610 			|| fssh_strlcat(buffer, leaf, bufferSize) >= bufferSize) {
2611 			return FSSH_B_NAME_TOO_LONG;
2612 		}
2613 	}
2614 
2615 	TRACE(("vfs_normalize_path() -> `%s'\n", buffer));
2616 	return FSSH_B_OK;
2617 }
2618 
2619 
2620 void
2621 vfs_put_vnode(void *_vnode)
2622 {
2623 	put_vnode((struct vnode *)_vnode);
2624 }
2625 
2626 
2627 fssh_status_t
2628 vfs_get_cwd(fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID)
2629 {
2630 	// Get current working directory from io context
2631 	struct io_context *context = get_current_io_context(false);
2632 	fssh_status_t status = FSSH_B_OK;
2633 
2634 	fssh_mutex_lock(&context->io_mutex);
2635 
2636 	if (context->cwd != NULL) {
2637 		*_mountID = context->cwd->device;
2638 		*_vnodeID = context->cwd->id;
2639 	} else
2640 		status = FSSH_B_ERROR;
2641 
2642 	fssh_mutex_unlock(&context->io_mutex);
2643 	return status;
2644 }
2645 
2646 
2647 fssh_status_t
2648 vfs_get_file_map(void *_vnode, fssh_off_t offset, fssh_size_t size,
2649 	fssh_file_io_vec *vecs, fssh_size_t *_count)
2650 {
2651 	struct vnode *vnode = (struct vnode *)_vnode;
2652 
2653 	FUNCTION(("vfs_get_file_map: vnode %p, vecs %p, offset %lld, size = %u\n", vnode, vecs, offset, (unsigned)size));
2654 
2655 	return FS_CALL(vnode, get_file_map, offset, size, vecs, _count);
2656 }
2657 
2658 
2659 fssh_status_t
2660 vfs_stat_vnode(void *_vnode, struct fssh_stat *stat)
2661 {
2662 	struct vnode *vnode = (struct vnode *)_vnode;
2663 
2664 	fssh_status_t status = FS_CALL(vnode, read_stat, stat);
2665 
2666 	// fill in the st_dev and st_ino fields
2667 	if (status == FSSH_B_OK) {
2668 		stat->fssh_st_dev = vnode->device;
2669 		stat->fssh_st_ino = vnode->id;
2670 	}
2671 
2672 	return status;
2673 }
2674 
2675 
2676 fssh_status_t
2677 vfs_get_vnode_name(void *_vnode, char *name, fssh_size_t nameSize)
2678 {
2679 	return get_vnode_name((struct vnode *)_vnode, NULL, name, nameSize);
2680 }
2681 
2682 
2683 fssh_status_t
2684 vfs_entry_ref_to_path(fssh_dev_t device, fssh_ino_t inode, const char *leaf,
2685 	bool kernel, char *path, fssh_size_t pathLength)
2686 {
2687 	struct vnode *vnode;
2688 	fssh_status_t status;
2689 
2690 	// filter invalid leaf names
2691 	if (leaf != NULL && (leaf[0] == '\0' || fssh_strchr(leaf, '/')))
2692 		return FSSH_B_BAD_VALUE;
2693 
2694 	// get the vnode matching the dir's node_ref
2695 	if (leaf && (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0)) {
2696 		// special cases "." and "..": we can directly get the vnode of the
2697 		// referenced directory
2698 		status = entry_ref_to_vnode(device, inode, leaf, &vnode);
2699 		leaf = NULL;
2700 	} else
2701 		status = get_vnode(device, inode, &vnode, false);
2702 	if (status < FSSH_B_OK)
2703 		return status;
2704 
2705 	// get the directory path
2706 	status = dir_vnode_to_path(vnode, path, pathLength);
2707 	put_vnode(vnode);
2708 		// we don't need the vnode anymore
2709 	if (status < FSSH_B_OK)
2710 		return status;
2711 
2712 	// append the leaf name
2713 	if (leaf) {
2714 		// insert a directory separator if this is not the file system root
2715 		if ((fssh_strcmp(path, "/") && fssh_strlcat(path, "/", pathLength)
2716 				>= pathLength)
2717 			|| fssh_strlcat(path, leaf, pathLength) >= pathLength) {
2718 			return FSSH_B_NAME_TOO_LONG;
2719 		}
2720 	}
2721 
2722 	return FSSH_B_OK;
2723 }
2724 
2725 
2726 /**	If the given descriptor locked its vnode, that lock will be released.
2727  */
2728 
2729 void
2730 vfs_unlock_vnode_if_locked(struct file_descriptor *descriptor)
2731 {
2732 	struct vnode *vnode = fd_vnode(descriptor);
2733 
2734 	if (vnode != NULL && vnode->mandatory_locked_by == descriptor)
2735 		vnode->mandatory_locked_by = NULL;
2736 }
2737 
2738 
2739 /**	Closes all file descriptors of the specified I/O context that
2740  *	don't have the FSSH_O_CLOEXEC flag set.
2741  */
2742 
2743 void
2744 vfs_exec_io_context(void *_context)
2745 {
2746 	struct io_context *context = (struct io_context *)_context;
2747 	uint32_t i;
2748 
2749 	for (i = 0; i < context->table_size; i++) {
2750 		fssh_mutex_lock(&context->io_mutex);
2751 
2752 		struct file_descriptor *descriptor = context->fds[i];
2753 		bool remove = false;
2754 
2755 		if (descriptor != NULL && fd_close_on_exec(context, i)) {
2756 			context->fds[i] = NULL;
2757 			context->num_used_fds--;
2758 
2759 			remove = true;
2760 		}
2761 
2762 		fssh_mutex_unlock(&context->io_mutex);
2763 
2764 		if (remove) {
2765 			close_fd(descriptor);
2766 			put_fd(descriptor);
2767 		}
2768 	}
2769 }
2770 
2771 
2772 /** Sets up a new io_control structure, and inherits the properties
2773  *	of the parent io_control if it is given.
2774  */
2775 
2776 void *
2777 vfs_new_io_context(void *_parentContext)
2778 {
2779 	fssh_size_t tableSize;
2780 	struct io_context *context;
2781 	struct io_context *parentContext;
2782 
2783 	context = (io_context *)malloc(sizeof(struct io_context));
2784 	if (context == NULL)
2785 		return NULL;
2786 
2787 	fssh_memset(context, 0, sizeof(struct io_context));
2788 
2789 	parentContext = (struct io_context *)_parentContext;
2790 	if (parentContext)
2791 		tableSize = parentContext->table_size;
2792 	else
2793 		tableSize = DEFAULT_FD_TABLE_SIZE;
2794 
2795 	// allocate space for FDs and their close-on-exec flag
2796 	context->fds = (file_descriptor **)malloc(sizeof(struct file_descriptor *) * tableSize
2797 		+ (tableSize + 7) / 8);
2798 	if (context->fds == NULL) {
2799 		free(context);
2800 		return NULL;
2801 	}
2802 
2803 	fssh_memset(context->fds, 0, sizeof(struct file_descriptor *) * tableSize
2804 		+ (tableSize + 7) / 8);
2805 	context->fds_close_on_exec = (uint8_t *)(context->fds + tableSize);
2806 
2807 	fssh_mutex_init(&context->io_mutex, "I/O context");
2808 
2809 	// Copy all parent files which don't have the FSSH_O_CLOEXEC flag set
2810 
2811 	if (parentContext) {
2812 		fssh_size_t i;
2813 
2814 		fssh_mutex_lock(&parentContext->io_mutex);
2815 
2816 		context->cwd = parentContext->cwd;
2817 		if (context->cwd)
2818 			inc_vnode_ref_count(context->cwd);
2819 
2820 		for (i = 0; i < tableSize; i++) {
2821 			struct file_descriptor *descriptor = parentContext->fds[i];
2822 
2823 			if (descriptor != NULL && !fd_close_on_exec(parentContext, i)) {
2824 				context->fds[i] = descriptor;
2825 				context->num_used_fds++;
2826 				fssh_atomic_add(&descriptor->ref_count, 1);
2827 				fssh_atomic_add(&descriptor->open_count, 1);
2828 			}
2829 		}
2830 
2831 		fssh_mutex_unlock(&parentContext->io_mutex);
2832 	} else {
2833 		context->cwd = sRoot;
2834 
2835 		if (context->cwd)
2836 			inc_vnode_ref_count(context->cwd);
2837 	}
2838 
2839 	context->table_size = tableSize;
2840 
2841 	return context;
2842 }
2843 
2844 
2845 fssh_status_t
2846 vfs_free_io_context(void *_ioContext)
2847 {
2848 	struct io_context *context = (struct io_context *)_ioContext;
2849 	uint32_t i;
2850 
2851 	if (context->cwd)
2852 		dec_vnode_ref_count(context->cwd, false);
2853 
2854 	fssh_mutex_lock(&context->io_mutex);
2855 
2856 	for (i = 0; i < context->table_size; i++) {
2857 		if (struct file_descriptor *descriptor = context->fds[i]) {
2858 			close_fd(descriptor);
2859 			put_fd(descriptor);
2860 		}
2861 	}
2862 
2863 	fssh_mutex_destroy(&context->io_mutex);
2864 
2865 	free(context->fds);
2866 	free(context);
2867 
2868 	return FSSH_B_OK;
2869 }
2870 
2871 
2872 fssh_status_t
2873 vfs_init(kernel_args *args)
2874 {
2875 	sVnodeTable = hash_init(VNODE_HASH_TABLE_SIZE, fssh_offsetof(struct vnode, next),
2876 		&vnode_compare, &vnode_hash);
2877 	if (sVnodeTable == NULL)
2878 		fssh_panic("vfs_init: error creating vnode hash table\n");
2879 
2880 	list_init_etc(&sUnusedVnodeList, fssh_offsetof(struct vnode, unused_link));
2881 
2882 	sMountsTable = hash_init(MOUNTS_HASH_TABLE_SIZE, fssh_offsetof(struct fs_mount, next),
2883 		&mount_compare, &mount_hash);
2884 	if (sMountsTable == NULL)
2885 		fssh_panic("vfs_init: error creating mounts hash table\n");
2886 
2887 	sRoot = NULL;
2888 
2889 	fssh_mutex_init(&sFileSystemsMutex, "vfs_lock");
2890 	fssh_recursive_lock_init(&sMountOpLock, "vfs_mount_op_lock");
2891 	fssh_mutex_init(&sMountMutex, "vfs_mount_lock");
2892 	fssh_mutex_init(&sVnodeCoveredByMutex, "vfs_vnode_covered_by_lock");
2893 	fssh_mutex_init(&sVnodeMutex, "vfs_vnode_lock");
2894 
2895 	if (block_cache_init() != FSSH_B_OK)
2896 		return FSSH_B_ERROR;
2897 
2898 	return file_cache_init();
2899 }
2900 
2901 
2902 //	#pragma mark -
2903 //	The filetype-dependent implementations (fd_ops + open/create/rename/remove, ...)
2904 
2905 
2906 /** Calls fs_open() on the given vnode and returns a new
2907  *	file descriptor for it
2908  */
2909 
2910 static int
2911 create_vnode(struct vnode *directory, const char *name, int openMode, int perms, bool kernel)
2912 {
2913 	struct vnode *vnode;
2914 	void *cookie;
2915 	fssh_vnode_id newID;
2916 	int status;
2917 
2918 	if (!HAS_FS_CALL(directory, create))
2919 		return FSSH_EROFS;
2920 
2921 	status = FS_CALL(directory, create, name, openMode, perms, &cookie, &newID);
2922 	if (status < FSSH_B_OK)
2923 		return status;
2924 
2925 	fssh_mutex_lock(&sVnodeMutex);
2926 	vnode = lookup_vnode(directory->device, newID);
2927 	fssh_mutex_unlock(&sVnodeMutex);
2928 
2929 	if (vnode == NULL) {
2930 		fssh_dprintf("vfs: fs_create() returned success but there is no vnode!");
2931 		return FSSH_EINVAL;
2932 	}
2933 
2934 	if ((status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel)) >= 0)
2935 		return status;
2936 
2937 	// something went wrong, clean up
2938 
2939 	FS_CALL(vnode, close, cookie);
2940 	FS_CALL(vnode, free_cookie, cookie);
2941 	put_vnode(vnode);
2942 
2943 	FS_CALL(directory, unlink, name);
2944 
2945 	return status;
2946 }
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 open_vnode(struct vnode *vnode, int openMode, bool kernel)
2955 {
2956 	void *cookie;
2957 	int status;
2958 
2959 	status = FS_CALL(vnode, open, openMode, &cookie);
2960 	if (status < 0)
2961 		return status;
2962 
2963 	status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel);
2964 	if (status < 0) {
2965 		FS_CALL(vnode, close, cookie);
2966 		FS_CALL(vnode, free_cookie, cookie);
2967 	}
2968 	return status;
2969 }
2970 
2971 
2972 /** Calls fs open_dir() on the given vnode and returns a new
2973  *	file descriptor for it
2974  */
2975 
2976 static int
2977 open_dir_vnode(struct vnode *vnode, bool kernel)
2978 {
2979 	void *cookie;
2980 	int status;
2981 
2982 	status = FS_CALL(vnode, open_dir, &cookie);
2983 	if (status < FSSH_B_OK)
2984 		return status;
2985 
2986 	// file is opened, create a fd
2987 	status = get_new_fd(FDTYPE_DIR, NULL, vnode, cookie, 0, kernel);
2988 	if (status >= 0)
2989 		return status;
2990 
2991 	FS_CALL(vnode, close_dir, cookie);
2992 	FS_CALL(vnode, free_dir_cookie, cookie);
2993 
2994 	return status;
2995 }
2996 
2997 
2998 /** Calls fs open_attr_dir() on the given vnode and returns a new
2999  *	file descriptor for it.
3000  *	Used by attr_dir_open(), and attr_dir_open_fd().
3001  */
3002 
3003 static int
3004 open_attr_dir_vnode(struct vnode *vnode, bool kernel)
3005 {
3006 	void *cookie;
3007 	int status;
3008 
3009 	if (!HAS_FS_CALL(vnode, open_attr_dir))
3010 		return FSSH_EOPNOTSUPP;
3011 
3012 	status = FS_CALL(vnode, open_attr_dir, &cookie);
3013 	if (status < 0)
3014 		return status;
3015 
3016 	// file is opened, create a fd
3017 	status = get_new_fd(FDTYPE_ATTR_DIR, NULL, vnode, cookie, 0, kernel);
3018 	if (status >= 0)
3019 		return status;
3020 
3021 	FS_CALL(vnode, close_attr_dir, cookie);
3022 	FS_CALL(vnode, free_attr_dir_cookie, cookie);
3023 
3024 	return status;
3025 }
3026 
3027 
3028 static int
3029 file_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, int perms, bool kernel)
3030 {
3031 	struct vnode *directory;
3032 	int status;
3033 
3034 	FUNCTION(("file_create_entry_ref: name = '%s', omode %x, perms %d, kernel %d\n", name, openMode, perms, kernel));
3035 
3036 	// get directory to put the new file in
3037 	status = get_vnode(mountID, directoryID, &directory, false);
3038 	if (status < FSSH_B_OK)
3039 		return status;
3040 
3041 	status = create_vnode(directory, name, openMode, perms, kernel);
3042 	put_vnode(directory);
3043 
3044 	return status;
3045 }
3046 
3047 
3048 static int
3049 file_create(int fd, char *path, int openMode, int perms, bool kernel)
3050 {
3051 	char name[FSSH_B_FILE_NAME_LENGTH];
3052 	struct vnode *directory;
3053 	int status;
3054 
3055 	FUNCTION(("file_create: path '%s', omode %x, perms %d, kernel %d\n", path, openMode, perms, kernel));
3056 
3057 	// get directory to put the new file in
3058 	status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel);
3059 	if (status < 0)
3060 		return status;
3061 
3062 	status = create_vnode(directory, name, openMode, perms, kernel);
3063 
3064 	put_vnode(directory);
3065 	return status;
3066 }
3067 
3068 
3069 static int
3070 file_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, bool kernel)
3071 {
3072 	struct vnode *vnode;
3073 	int status;
3074 
3075 	if (name == NULL || *name == '\0')
3076 		return FSSH_B_BAD_VALUE;
3077 
3078 	FUNCTION(("file_open_entry_ref(ref = (%ld, %Ld, %s), openMode = %d)\n",
3079 		mountID, directoryID, name, openMode));
3080 
3081 	// get the vnode matching the entry_ref
3082 	status = entry_ref_to_vnode(mountID, directoryID, name, &vnode);
3083 	if (status < FSSH_B_OK)
3084 		return status;
3085 
3086 	status = open_vnode(vnode, openMode, kernel);
3087 	if (status < FSSH_B_OK)
3088 		put_vnode(vnode);
3089 
3090 	return status;
3091 }
3092 
3093 
3094 static int
3095 file_open(int fd, char *path, int openMode, bool kernel)
3096 {
3097 	int status = FSSH_B_OK;
3098 	bool traverse = ((openMode & FSSH_O_NOTRAVERSE) == 0);
3099 
3100 	FUNCTION(("file_open: fd: %d, entry path = '%s', omode %d, kernel %d\n",
3101 		fd, path, openMode, kernel));
3102 
3103 	// get the vnode matching the vnode + path combination
3104 	struct vnode *vnode = NULL;
3105 	fssh_vnode_id parentID;
3106 	status = fd_and_path_to_vnode(fd, path, traverse, &vnode, &parentID, kernel);
3107 	if (status != FSSH_B_OK)
3108 		return status;
3109 
3110 	// open the vnode
3111 	status = open_vnode(vnode, openMode, kernel);
3112 	// put only on error -- otherwise our reference was transferred to the FD
3113 	if (status < FSSH_B_OK)
3114 		put_vnode(vnode);
3115 
3116 	return status;
3117 }
3118 
3119 
3120 static fssh_status_t
3121 file_close(struct file_descriptor *descriptor)
3122 {
3123 	struct vnode *vnode = descriptor->u.vnode;
3124 	fssh_status_t status = FSSH_B_OK;
3125 
3126 	FUNCTION(("file_close(descriptor = %p)\n", descriptor));
3127 
3128 	if (HAS_FS_CALL(vnode, close))
3129 		status = FS_CALL(vnode, close, descriptor->cookie);
3130 
3131 	return status;
3132 }
3133 
3134 
3135 static void
3136 file_free_fd(struct file_descriptor *descriptor)
3137 {
3138 	struct vnode *vnode = descriptor->u.vnode;
3139 
3140 	if (vnode != NULL) {
3141 		FS_CALL(vnode, free_cookie, descriptor->cookie);
3142 		put_vnode(vnode);
3143 	}
3144 }
3145 
3146 
3147 static fssh_status_t
3148 file_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length)
3149 {
3150 	struct vnode *vnode = descriptor->u.vnode;
3151 
3152 	FUNCTION(("file_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length));
3153 	return FS_CALL(vnode, read, descriptor->cookie, pos, buffer, length);
3154 }
3155 
3156 
3157 static fssh_status_t
3158 file_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length)
3159 {
3160 	struct vnode *vnode = descriptor->u.vnode;
3161 
3162 	FUNCTION(("file_write: buf %p, pos %Ld, len %p\n", buffer, pos, length));
3163 	return FS_CALL(vnode, write, descriptor->cookie, pos, buffer, length);
3164 }
3165 
3166 
3167 static fssh_off_t
3168 file_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType)
3169 {
3170 	fssh_off_t offset;
3171 
3172 	FUNCTION(("file_seek(pos = %Ld, seekType = %d)\n", pos, seekType));
3173 	// ToDo: seek should fail for pipes and FIFOs...
3174 
3175 	switch (seekType) {
3176 		case FSSH_SEEK_SET:
3177 			offset = 0;
3178 			break;
3179 		case FSSH_SEEK_CUR:
3180 			offset = descriptor->pos;
3181 			break;
3182 		case FSSH_SEEK_END:
3183 		{
3184 			struct vnode *vnode = descriptor->u.vnode;
3185 			struct fssh_stat stat;
3186 			fssh_status_t status;
3187 
3188 			if (!HAS_FS_CALL(vnode, read_stat))
3189 				return FSSH_EOPNOTSUPP;
3190 
3191 			status = FS_CALL(vnode, read_stat, &stat);
3192 			if (status < FSSH_B_OK)
3193 				return status;
3194 
3195 			offset = stat.fssh_st_size;
3196 			break;
3197 		}
3198 		default:
3199 			return FSSH_B_BAD_VALUE;
3200 	}
3201 
3202 	// assumes fssh_off_t is 64 bits wide
3203 	if (offset > 0 && LLONG_MAX - offset < pos)
3204 		return FSSH_EOVERFLOW;
3205 
3206 	pos += offset;
3207 	if (pos < 0)
3208 		return FSSH_B_BAD_VALUE;
3209 
3210 	return descriptor->pos = pos;
3211 }
3212 
3213 
3214 static fssh_status_t
3215 dir_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, int perms, bool kernel)
3216 {
3217 	struct vnode *vnode;
3218 	fssh_status_t status;
3219 
3220 	if (name == NULL || *name == '\0')
3221 		return FSSH_B_BAD_VALUE;
3222 
3223 	FUNCTION(("dir_create_entry_ref(dev = %ld, ino = %Ld, name = '%s', perms = %d)\n", mountID, parentID, name, perms));
3224 
3225 	status = get_vnode(mountID, parentID, &vnode, kernel);
3226 	if (status < FSSH_B_OK)
3227 		return status;
3228 
3229 	if (HAS_FS_CALL(vnode, create_dir))
3230 		status = FS_CALL(vnode, create_dir, name, perms);
3231 	else
3232 		status = FSSH_EROFS;
3233 
3234 	put_vnode(vnode);
3235 	return status;
3236 }
3237 
3238 
3239 static fssh_status_t
3240 dir_create(int fd, char *path, int perms, bool kernel)
3241 {
3242 	char filename[FSSH_B_FILE_NAME_LENGTH];
3243 	struct vnode *vnode;
3244 	fssh_status_t status;
3245 
3246 	FUNCTION(("dir_create: path '%s', perms %d, kernel %d\n", path, perms, kernel));
3247 
3248 	status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel);
3249 	if (status < 0)
3250 		return status;
3251 
3252 	if (HAS_FS_CALL(vnode, create_dir))
3253 		status = FS_CALL(vnode, create_dir, filename, perms);
3254 	else
3255 		status = FSSH_EROFS;
3256 
3257 	put_vnode(vnode);
3258 	return status;
3259 }
3260 
3261 
3262 static int
3263 dir_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, bool kernel)
3264 {
3265 	struct vnode *vnode;
3266 	int status;
3267 
3268 	FUNCTION(("dir_open_entry_ref()\n"));
3269 
3270 	if (name && *name == '\0')
3271 		return FSSH_B_BAD_VALUE;
3272 
3273 	// get the vnode matching the entry_ref/node_ref
3274 	if (name)
3275 		status = entry_ref_to_vnode(mountID, parentID, name, &vnode);
3276 	else
3277 		status = get_vnode(mountID, parentID, &vnode, false);
3278 	if (status < FSSH_B_OK)
3279 		return status;
3280 
3281 	status = open_dir_vnode(vnode, kernel);
3282 	if (status < FSSH_B_OK)
3283 		put_vnode(vnode);
3284 
3285 	return status;
3286 }
3287 
3288 
3289 static int
3290 dir_open(int fd, char *path, bool kernel)
3291 {
3292 	int status = FSSH_B_OK;
3293 
3294 	FUNCTION(("dir_open: fd: %d, entry path = '%s', kernel %d\n", fd, path, kernel));
3295 
3296 	// get the vnode matching the vnode + path combination
3297 	struct vnode *vnode = NULL;
3298 	fssh_vnode_id parentID;
3299 	status = fd_and_path_to_vnode(fd, path, true, &vnode, &parentID, kernel);
3300 	if (status != FSSH_B_OK)
3301 		return status;
3302 
3303 	// open the dir
3304 	status = open_dir_vnode(vnode, kernel);
3305 	if (status < FSSH_B_OK)
3306 		put_vnode(vnode);
3307 
3308 	return status;
3309 }
3310 
3311 
3312 static fssh_status_t
3313 dir_close(struct file_descriptor *descriptor)
3314 {
3315 	struct vnode *vnode = descriptor->u.vnode;
3316 
3317 	FUNCTION(("dir_close(descriptor = %p)\n", descriptor));
3318 
3319 	if (HAS_FS_CALL(vnode, close_dir))
3320 		return FS_CALL(vnode, close_dir, descriptor->cookie);
3321 
3322 	return FSSH_B_OK;
3323 }
3324 
3325 
3326 static void
3327 dir_free_fd(struct file_descriptor *descriptor)
3328 {
3329 	struct vnode *vnode = descriptor->u.vnode;
3330 
3331 	if (vnode != NULL) {
3332 		FS_CALL(vnode, free_dir_cookie, descriptor->cookie);
3333 		put_vnode(vnode);
3334 	}
3335 }
3336 
3337 
3338 static fssh_status_t
3339 dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
3340 	fssh_size_t bufferSize, uint32_t *_count)
3341 {
3342 	return dir_read(descriptor->u.vnode, descriptor->cookie, buffer, bufferSize, _count);
3343 }
3344 
3345 
3346 static void
3347 fix_dirent(struct vnode *parent, struct fssh_dirent *entry)
3348 {
3349 	// set d_pdev and d_pino
3350 	entry->d_pdev = parent->device;
3351 	entry->d_pino = parent->id;
3352 
3353 	// If this is the ".." entry and the directory is the root of a FS,
3354 	// we need to replace d_dev and d_ino with the actual values.
3355 	if (fssh_strcmp(entry->d_name, "..") == 0
3356 		&& parent->mount->root_vnode == parent
3357 		&& parent->mount->covers_vnode) {
3358 		inc_vnode_ref_count(parent);
3359 			// vnode_path_to_vnode() puts the node
3360 
3361 		// ".." is guaranteed to to be clobbered by this call
3362 		struct vnode *vnode;
3363 		fssh_status_t status = vnode_path_to_vnode(parent, (char*)"..", false,
3364 			0, &vnode, NULL);
3365 
3366 		if (status == FSSH_B_OK) {
3367 			entry->d_dev = vnode->device;
3368 			entry->d_ino = vnode->id;
3369 		}
3370 	} else {
3371 		// resolve mount points
3372 		struct vnode *vnode = NULL;
3373 		fssh_status_t status = get_vnode(entry->d_dev, entry->d_ino, &vnode, false);
3374 		if (status != FSSH_B_OK)
3375 			return;
3376 
3377 		fssh_mutex_lock(&sVnodeCoveredByMutex);
3378 		if (vnode->covered_by) {
3379 			entry->d_dev = vnode->covered_by->device;
3380 			entry->d_ino = vnode->covered_by->id;
3381 		}
3382 		fssh_mutex_unlock(&sVnodeCoveredByMutex);
3383 
3384 		put_vnode(vnode);
3385 	}
3386 }
3387 
3388 
3389 static fssh_status_t
3390 dir_read(struct vnode *vnode, void *cookie, struct fssh_dirent *buffer,
3391 	fssh_size_t bufferSize, uint32_t *_count)
3392 {
3393 	if (!HAS_FS_CALL(vnode, read_dir))
3394 		return FSSH_EOPNOTSUPP;
3395 
3396 	fssh_status_t error = FS_CALL(vnode, read_dir,cookie,buffer,bufferSize,_count);
3397 	if (error != FSSH_B_OK)
3398 		return error;
3399 
3400 	// we need to adjust the read dirents
3401 	if (*_count > 0) {
3402 		// XXX: Currently reading only one dirent is supported. Make this a loop!
3403 		fix_dirent(vnode, buffer);
3404 	}
3405 
3406 	return error;
3407 }
3408 
3409 
3410 static fssh_status_t
3411 dir_rewind(struct file_descriptor *descriptor)
3412 {
3413 	struct vnode *vnode = descriptor->u.vnode;
3414 
3415 	if (HAS_FS_CALL(vnode, rewind_dir))
3416 		return FS_CALL(vnode, rewind_dir,descriptor->cookie);
3417 
3418 	return FSSH_EOPNOTSUPP;
3419 }
3420 
3421 
3422 static fssh_status_t
3423 dir_remove(int fd, char *path, bool kernel)
3424 {
3425 	char name[FSSH_B_FILE_NAME_LENGTH];
3426 	struct vnode *directory;
3427 	fssh_status_t status;
3428 
3429 	if (path != NULL) {
3430 		// we need to make sure our path name doesn't stop with "/", ".", or ".."
3431 		char *lastSlash = fssh_strrchr(path, '/');
3432 		if (lastSlash != NULL) {
3433 			char *leaf = lastSlash + 1;
3434 			if (!fssh_strcmp(leaf, ".."))
3435 				return FSSH_B_NOT_ALLOWED;
3436 
3437 			// omit multiple slashes
3438 			while (lastSlash > path && lastSlash[-1] == '/') {
3439 				lastSlash--;
3440 			}
3441 
3442 			if (!leaf[0]
3443 				|| !fssh_strcmp(leaf, ".")) {
3444 				// "name/" -> "name", or "name/." -> "name"
3445 				lastSlash[0] = '\0';
3446 			}
3447 		} else if (!fssh_strcmp(path, ".."))
3448 			return FSSH_B_NOT_ALLOWED;
3449 	}
3450 
3451 	status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel);
3452 	if (status < FSSH_B_OK)
3453 		return status;
3454 
3455 	if (HAS_FS_CALL(directory, remove_dir)) {
3456 		status = FS_CALL(directory, remove_dir, name);
3457 	} else
3458 		status = FSSH_EROFS;
3459 
3460 	put_vnode(directory);
3461 	return status;
3462 }
3463 
3464 
3465 static fssh_status_t
3466 common_ioctl(struct file_descriptor *descriptor, uint32_t op, void *buffer,
3467 	fssh_size_t length)
3468 {
3469 	struct vnode *vnode = descriptor->u.vnode;
3470 
3471 	if (HAS_FS_CALL(vnode, ioctl)) {
3472 		return FS_CALL(vnode, ioctl,
3473 			descriptor->cookie, op, buffer, length);
3474 	}
3475 
3476 	return FSSH_EOPNOTSUPP;
3477 }
3478 
3479 
3480 static fssh_status_t
3481 common_fcntl(int fd, int op, uint32_t argument, bool kernel)
3482 {
3483 	struct file_descriptor *descriptor;
3484 	struct vnode *vnode;
3485 	fssh_status_t status;
3486 
3487 	FUNCTION(("common_fcntl(fd = %d, op = %d, argument = %lx, %s)\n",
3488 		fd, op, argument, kernel ? "kernel" : "user"));
3489 
3490 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3491 	if (descriptor == NULL)
3492 		return FSSH_B_FILE_ERROR;
3493 
3494 	switch (op) {
3495 		case FSSH_F_SETFD:
3496 		{
3497 			struct io_context *context = get_current_io_context(kernel);
3498 			// Set file descriptor flags
3499 
3500 			// FSSH_O_CLOEXEC is the only flag available at this time
3501 			fssh_mutex_lock(&context->io_mutex);
3502 			fd_set_close_on_exec(context, fd, argument == FSSH_FD_CLOEXEC);
3503 			fssh_mutex_unlock(&context->io_mutex);
3504 
3505 			status = FSSH_B_OK;
3506 			break;
3507 		}
3508 
3509 		case FSSH_F_GETFD:
3510 		{
3511 			struct io_context *context = get_current_io_context(kernel);
3512 
3513 			// Get file descriptor flags
3514 			fssh_mutex_lock(&context->io_mutex);
3515 			status = fd_close_on_exec(context, fd) ? FSSH_FD_CLOEXEC : 0;
3516 			fssh_mutex_unlock(&context->io_mutex);
3517 			break;
3518 		}
3519 
3520 		case FSSH_F_SETFL:
3521 			// Set file descriptor open mode
3522 			if (HAS_FS_CALL(vnode, set_flags)) {
3523 				// we only accept changes to FSSH_O_APPEND and FSSH_O_NONBLOCK
3524 				argument &= FSSH_O_APPEND | FSSH_O_NONBLOCK;
3525 
3526 				status = FS_CALL(vnode, set_flags, descriptor->cookie, (int)argument);
3527 				if (status == FSSH_B_OK) {
3528 					// update this descriptor's open_mode field
3529 					descriptor->open_mode = (descriptor->open_mode & ~(FSSH_O_APPEND | FSSH_O_NONBLOCK))
3530 						| argument;
3531 				}
3532 			} else
3533 				status = FSSH_EOPNOTSUPP;
3534 			break;
3535 
3536 		case FSSH_F_GETFL:
3537 			// Get file descriptor open mode
3538 			status = descriptor->open_mode;
3539 			break;
3540 
3541 		case FSSH_F_DUPFD:
3542 		{
3543 			struct io_context *context = get_current_io_context(kernel);
3544 
3545 			status = new_fd_etc(context, descriptor, (int)argument);
3546 			if (status >= 0) {
3547 				fssh_mutex_lock(&context->io_mutex);
3548 				fd_set_close_on_exec(context, fd, false);
3549 				fssh_mutex_unlock(&context->io_mutex);
3550 
3551 				fssh_atomic_add(&descriptor->ref_count, 1);
3552 			}
3553 			break;
3554 		}
3555 
3556 		case FSSH_F_GETLK:
3557 		case FSSH_F_SETLK:
3558 		case FSSH_F_SETLKW:
3559 			status = FSSH_B_BAD_VALUE;
3560 			break;
3561 
3562 		// ToDo: add support for more ops?
3563 
3564 		default:
3565 			status = FSSH_B_BAD_VALUE;
3566 	}
3567 
3568 	put_fd(descriptor);
3569 	return status;
3570 }
3571 
3572 
3573 static fssh_status_t
3574 common_sync(int fd, bool kernel)
3575 {
3576 	struct file_descriptor *descriptor;
3577 	struct vnode *vnode;
3578 	fssh_status_t status;
3579 
3580 	FUNCTION(("common_fsync: entry. fd %d kernel %d\n", fd, kernel));
3581 
3582 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3583 	if (descriptor == NULL)
3584 		return FSSH_B_FILE_ERROR;
3585 
3586 	if (HAS_FS_CALL(vnode, fsync))
3587 		status = FS_CALL_NO_PARAMS(vnode, fsync);
3588 	else
3589 		status = FSSH_EOPNOTSUPP;
3590 
3591 	put_fd(descriptor);
3592 	return status;
3593 }
3594 
3595 
3596 static fssh_status_t
3597 common_lock_node(int fd, bool kernel)
3598 {
3599 	struct file_descriptor *descriptor;
3600 	struct vnode *vnode;
3601 
3602 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3603 	if (descriptor == NULL)
3604 		return FSSH_B_FILE_ERROR;
3605 
3606 	fssh_status_t status = FSSH_B_OK;
3607 
3608 	// We need to set the locking atomically - someone
3609 	// else might set one at the same time
3610 #ifdef __x86_64__
3611 	if (fssh_atomic_test_and_set64((int64_t *)&vnode->mandatory_locked_by,
3612 			(fssh_addr_t)descriptor, 0) != 0)
3613 #else
3614 	if (fssh_atomic_test_and_set((int32_t *)&vnode->mandatory_locked_by,
3615 			(fssh_addr_t)descriptor, 0) != 0)
3616 #endif
3617 		status = FSSH_B_BUSY;
3618 
3619 	put_fd(descriptor);
3620 	return status;
3621 }
3622 
3623 
3624 static fssh_status_t
3625 common_unlock_node(int fd, bool kernel)
3626 {
3627 	struct file_descriptor *descriptor;
3628 	struct vnode *vnode;
3629 
3630 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3631 	if (descriptor == NULL)
3632 		return FSSH_B_FILE_ERROR;
3633 
3634 	fssh_status_t status = FSSH_B_OK;
3635 
3636 	// We need to set the locking atomically - someone
3637 	// else might set one at the same time
3638 #ifdef __x86_64__
3639 	if (fssh_atomic_test_and_set64((int64_t *)&vnode->mandatory_locked_by,
3640 			0, (fssh_addr_t)descriptor) != (int64_t)descriptor)
3641 #else
3642 	if (fssh_atomic_test_and_set((int32_t *)&vnode->mandatory_locked_by,
3643 			0, (fssh_addr_t)descriptor) != (int32_t)descriptor)
3644 #endif
3645 		status = FSSH_B_BAD_VALUE;
3646 
3647 	put_fd(descriptor);
3648 	return status;
3649 }
3650 
3651 
3652 static fssh_status_t
3653 common_read_link(int fd, char *path, char *buffer, fssh_size_t *_bufferSize,
3654 	bool kernel)
3655 {
3656 	struct vnode *vnode;
3657 	fssh_status_t status;
3658 
3659 	status = fd_and_path_to_vnode(fd, path, false, &vnode, NULL, kernel);
3660 	if (status < FSSH_B_OK)
3661 		return status;
3662 
3663 	if (HAS_FS_CALL(vnode, read_symlink)) {
3664 		status = FS_CALL(vnode, read_symlink, buffer, _bufferSize);
3665 	} else
3666 		status = FSSH_B_BAD_VALUE;
3667 
3668 	put_vnode(vnode);
3669 	return status;
3670 }
3671 
3672 
3673 static fssh_status_t
3674 common_create_symlink(int fd, char *path, const char *toPath, int mode,
3675 	bool kernel)
3676 {
3677 	// path validity checks have to be in the calling function!
3678 	char name[FSSH_B_FILE_NAME_LENGTH];
3679 	struct vnode *vnode;
3680 	fssh_status_t status;
3681 
3682 	FUNCTION(("common_create_symlink(fd = %d, path = %s, toPath = %s, mode = %d, kernel = %d)\n", fd, path, toPath, mode, kernel));
3683 
3684 	status = fd_and_path_to_dir_vnode(fd, path, &vnode, name, kernel);
3685 	if (status < FSSH_B_OK)
3686 		return status;
3687 
3688 	if (HAS_FS_CALL(vnode, create_symlink))
3689 		status = FS_CALL(vnode, create_symlink, name, toPath, mode);
3690 	else
3691 		status = FSSH_EROFS;
3692 
3693 	put_vnode(vnode);
3694 
3695 	return status;
3696 }
3697 
3698 
3699 static fssh_status_t
3700 common_create_link(char *path, char *toPath, bool kernel)
3701 {
3702 	// path validity checks have to be in the calling function!
3703 	char name[FSSH_B_FILE_NAME_LENGTH];
3704 	struct vnode *directory, *vnode;
3705 	fssh_status_t status;
3706 
3707 	FUNCTION(("common_create_link(path = %s, toPath = %s, kernel = %d)\n", path, toPath, kernel));
3708 
3709 	status = path_to_dir_vnode(path, &directory, name, kernel);
3710 	if (status < FSSH_B_OK)
3711 		return status;
3712 
3713 	status = path_to_vnode(toPath, true, &vnode, NULL, kernel);
3714 	if (status < FSSH_B_OK)
3715 		goto err;
3716 
3717 	if (directory->mount != vnode->mount) {
3718 		status = FSSH_B_CROSS_DEVICE_LINK;
3719 		goto err1;
3720 	}
3721 
3722 	if (HAS_FS_CALL(directory, link))
3723 		status = FS_CALL(directory, link, name, vnode);
3724 	else
3725 		status = FSSH_EROFS;
3726 
3727 err1:
3728 	put_vnode(vnode);
3729 err:
3730 	put_vnode(directory);
3731 
3732 	return status;
3733 }
3734 
3735 
3736 static fssh_status_t
3737 common_unlink(int fd, char *path, bool kernel)
3738 {
3739 	char filename[FSSH_B_FILE_NAME_LENGTH];
3740 	struct vnode *vnode;
3741 	fssh_status_t status;
3742 
3743 	FUNCTION(("common_unlink: fd: %d, path '%s', kernel %d\n", fd, path, kernel));
3744 
3745 	status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel);
3746 	if (status < 0)
3747 		return status;
3748 
3749 	if (HAS_FS_CALL(vnode, unlink))
3750 		status = FS_CALL(vnode, unlink, filename);
3751 	else
3752 		status = FSSH_EROFS;
3753 
3754 	put_vnode(vnode);
3755 
3756 	return status;
3757 }
3758 
3759 
3760 static fssh_status_t
3761 common_access(char *path, int mode, bool kernel)
3762 {
3763 	struct vnode *vnode;
3764 	fssh_status_t status;
3765 
3766 	status = path_to_vnode(path, true, &vnode, NULL, kernel);
3767 	if (status < FSSH_B_OK)
3768 		return status;
3769 
3770 	if (HAS_FS_CALL(vnode, access))
3771 		status = FS_CALL(vnode, access, mode);
3772 	else
3773 		status = FSSH_B_OK;
3774 
3775 	put_vnode(vnode);
3776 
3777 	return status;
3778 }
3779 
3780 
3781 static fssh_status_t
3782 common_rename(int fd, char *path, int newFD, char *newPath, bool kernel)
3783 {
3784 	struct vnode *fromVnode, *toVnode;
3785 	char fromName[FSSH_B_FILE_NAME_LENGTH];
3786 	char toName[FSSH_B_FILE_NAME_LENGTH];
3787 	fssh_status_t status;
3788 
3789 	FUNCTION(("common_rename(fd = %d, path = %s, newFD = %d, newPath = %s, kernel = %d)\n", fd, path, newFD, newPath, kernel));
3790 
3791 	status = fd_and_path_to_dir_vnode(fd, path, &fromVnode, fromName, kernel);
3792 	if (status < 0)
3793 		return status;
3794 
3795 	status = fd_and_path_to_dir_vnode(newFD, newPath, &toVnode, toName, kernel);
3796 	if (status < 0)
3797 		goto err;
3798 
3799 	if (fromVnode->device != toVnode->device) {
3800 		status = FSSH_B_CROSS_DEVICE_LINK;
3801 		goto err1;
3802 	}
3803 
3804 	if (HAS_FS_CALL(fromVnode, rename))
3805 		status = FS_CALL(fromVnode, rename, fromName, toVnode, toName);
3806 	else
3807 		status = FSSH_EROFS;
3808 
3809 err1:
3810 	put_vnode(toVnode);
3811 err:
3812 	put_vnode(fromVnode);
3813 
3814 	return status;
3815 }
3816 
3817 
3818 static fssh_status_t
3819 common_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat)
3820 {
3821 	struct vnode *vnode = descriptor->u.vnode;
3822 
3823 	FUNCTION(("common_read_stat: stat %p\n", stat));
3824 
3825 	stat->fssh_st_atim.tv_nsec = 0;
3826 	stat->fssh_st_mtim.tv_nsec = 0;
3827 	stat->fssh_st_ctim.tv_nsec = 0;
3828 	stat->fssh_st_crtim.tv_nsec = 0;
3829 
3830 	fssh_status_t status = FS_CALL(vnode, read_stat, stat);
3831 
3832 	// fill in the st_dev and st_ino fields
3833 	if (status == FSSH_B_OK) {
3834 		stat->fssh_st_dev = vnode->device;
3835 		stat->fssh_st_ino = vnode->id;
3836 	}
3837 
3838 	return status;
3839 }
3840 
3841 
3842 static fssh_status_t
3843 common_write_stat(struct file_descriptor *descriptor,
3844 	const struct fssh_stat *stat, int statMask)
3845 {
3846 	struct vnode *vnode = descriptor->u.vnode;
3847 
3848 	FUNCTION(("common_write_stat(vnode = %p, stat = %p, statMask = %d)\n", vnode, stat, statMask));
3849 	if (!HAS_FS_CALL(vnode, write_stat))
3850 		return FSSH_EROFS;
3851 
3852 	return FS_CALL(vnode, write_stat, stat, statMask);
3853 }
3854 
3855 
3856 static fssh_status_t
3857 common_path_read_stat(int fd, char *path, bool traverseLeafLink,
3858 	struct fssh_stat *stat, bool kernel)
3859 {
3860 	struct vnode *vnode;
3861 	fssh_status_t status;
3862 
3863 	FUNCTION(("common_path_read_stat: fd: %d, path '%s', stat %p,\n", fd, path, stat));
3864 
3865 	status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel);
3866 	if (status < 0)
3867 		return status;
3868 
3869 	status = FS_CALL(vnode, read_stat, stat);
3870 
3871 	// fill in the st_dev and st_ino fields
3872 	if (status == FSSH_B_OK) {
3873 		stat->fssh_st_dev = vnode->device;
3874 		stat->fssh_st_ino = vnode->id;
3875 	}
3876 
3877 	put_vnode(vnode);
3878 	return status;
3879 }
3880 
3881 
3882 static fssh_status_t
3883 common_path_write_stat(int fd, char *path, bool traverseLeafLink,
3884 	const struct fssh_stat *stat, int statMask, bool kernel)
3885 {
3886 	struct vnode *vnode;
3887 	fssh_status_t status;
3888 
3889 	FUNCTION(("common_write_stat: fd: %d, path '%s', stat %p, stat_mask %d, kernel %d\n", fd, path, stat, statMask, kernel));
3890 
3891 	status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel);
3892 	if (status < 0)
3893 		return status;
3894 
3895 	if (HAS_FS_CALL(vnode, write_stat))
3896 		status = FS_CALL(vnode, write_stat, stat, statMask);
3897 	else
3898 		status = FSSH_EROFS;
3899 
3900 	put_vnode(vnode);
3901 
3902 	return status;
3903 }
3904 
3905 
3906 static int
3907 attr_dir_open(int fd, char *path, bool kernel)
3908 {
3909 	struct vnode *vnode;
3910 	int status;
3911 
3912 	FUNCTION(("attr_dir_open(fd = %d, path = '%s', kernel = %d)\n", fd, path, kernel));
3913 
3914 	status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel);
3915 	if (status < FSSH_B_OK)
3916 		return status;
3917 
3918 	status = open_attr_dir_vnode(vnode, kernel);
3919 	if (status < 0)
3920 		put_vnode(vnode);
3921 
3922 	return status;
3923 }
3924 
3925 
3926 static fssh_status_t
3927 attr_dir_close(struct file_descriptor *descriptor)
3928 {
3929 	struct vnode *vnode = descriptor->u.vnode;
3930 
3931 	FUNCTION(("attr_dir_close(descriptor = %p)\n", descriptor));
3932 
3933 	if (HAS_FS_CALL(vnode, close_attr_dir))
3934 		return FS_CALL(vnode, close_attr_dir, descriptor->cookie);
3935 
3936 	return FSSH_B_OK;
3937 }
3938 
3939 
3940 static void
3941 attr_dir_free_fd(struct file_descriptor *descriptor)
3942 {
3943 	struct vnode *vnode = descriptor->u.vnode;
3944 
3945 	if (vnode != NULL) {
3946 		FS_CALL(vnode, free_attr_dir_cookie, descriptor->cookie);
3947 		put_vnode(vnode);
3948 	}
3949 }
3950 
3951 
3952 static fssh_status_t
3953 attr_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
3954 	fssh_size_t bufferSize, uint32_t *_count)
3955 {
3956 	struct vnode *vnode = descriptor->u.vnode;
3957 
3958 	FUNCTION(("attr_dir_read(descriptor = %p)\n", descriptor));
3959 
3960 	if (HAS_FS_CALL(vnode, read_attr_dir))
3961 		return FS_CALL(vnode, read_attr_dir, descriptor->cookie, buffer, bufferSize, _count);
3962 
3963 	return FSSH_EOPNOTSUPP;
3964 }
3965 
3966 
3967 static fssh_status_t
3968 attr_dir_rewind(struct file_descriptor *descriptor)
3969 {
3970 	struct vnode *vnode = descriptor->u.vnode;
3971 
3972 	FUNCTION(("attr_dir_rewind(descriptor = %p)\n", descriptor));
3973 
3974 	if (HAS_FS_CALL(vnode, rewind_attr_dir))
3975 		return FS_CALL(vnode, rewind_attr_dir, descriptor->cookie);
3976 
3977 	return FSSH_EOPNOTSUPP;
3978 }
3979 
3980 
3981 static int
3982 attr_create(int fd, const char *name, uint32_t type, int openMode, bool kernel)
3983 {
3984 	struct vnode *vnode;
3985 	void *cookie;
3986 	int status;
3987 
3988 	if (name == NULL || *name == '\0')
3989 		return FSSH_B_BAD_VALUE;
3990 
3991 	vnode = get_vnode_from_fd(fd, kernel);
3992 	if (vnode == NULL)
3993 		return FSSH_B_FILE_ERROR;
3994 
3995 	if (!HAS_FS_CALL(vnode, create_attr)) {
3996 		status = FSSH_EROFS;
3997 		goto err;
3998 	}
3999 
4000 	status = FS_CALL(vnode, create_attr, name, type, openMode, &cookie);
4001 	if (status < FSSH_B_OK)
4002 		goto err;
4003 
4004 	if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0)
4005 		return status;
4006 
4007 	FS_CALL(vnode, close_attr, cookie);
4008 	FS_CALL(vnode, free_attr_cookie, cookie);
4009 
4010 	FS_CALL(vnode, remove_attr, name);
4011 
4012 err:
4013 	put_vnode(vnode);
4014 
4015 	return status;
4016 }
4017 
4018 
4019 static int
4020 attr_open(int fd, const char *name, int openMode, bool kernel)
4021 {
4022 	struct vnode *vnode;
4023 	void *cookie;
4024 	int status;
4025 
4026 	if (name == NULL || *name == '\0')
4027 		return FSSH_B_BAD_VALUE;
4028 
4029 	vnode = get_vnode_from_fd(fd, kernel);
4030 	if (vnode == NULL)
4031 		return FSSH_B_FILE_ERROR;
4032 
4033 	if (!HAS_FS_CALL(vnode, open_attr)) {
4034 		status = FSSH_EOPNOTSUPP;
4035 		goto err;
4036 	}
4037 
4038 	status = FS_CALL(vnode, open_attr, name, openMode, &cookie);
4039 	if (status < FSSH_B_OK)
4040 		goto err;
4041 
4042 	// now we only need a file descriptor for this attribute and we're done
4043 	if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0)
4044 		return status;
4045 
4046 	FS_CALL(vnode, close_attr, cookie);
4047 	FS_CALL(vnode, free_attr_cookie, cookie);
4048 
4049 err:
4050 	put_vnode(vnode);
4051 
4052 	return status;
4053 }
4054 
4055 
4056 static fssh_status_t
4057 attr_close(struct file_descriptor *descriptor)
4058 {
4059 	struct vnode *vnode = descriptor->u.vnode;
4060 
4061 	FUNCTION(("attr_close(descriptor = %p)\n", descriptor));
4062 
4063 	if (HAS_FS_CALL(vnode, close_attr))
4064 		return FS_CALL(vnode, close_attr, descriptor->cookie);
4065 
4066 	return FSSH_B_OK;
4067 }
4068 
4069 
4070 static void
4071 attr_free_fd(struct file_descriptor *descriptor)
4072 {
4073 	struct vnode *vnode = descriptor->u.vnode;
4074 
4075 	if (vnode != NULL) {
4076 		FS_CALL(vnode, free_attr_cookie, descriptor->cookie);
4077 		put_vnode(vnode);
4078 	}
4079 }
4080 
4081 
4082 static fssh_status_t
4083 attr_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length)
4084 {
4085 	struct vnode *vnode = descriptor->u.vnode;
4086 
4087 	FUNCTION(("attr_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length));
4088 	if (!HAS_FS_CALL(vnode, read_attr))
4089 		return FSSH_EOPNOTSUPP;
4090 
4091 	return FS_CALL(vnode, read_attr, descriptor->cookie, pos, buffer, length);
4092 }
4093 
4094 
4095 static fssh_status_t
4096 attr_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length)
4097 {
4098 	struct vnode *vnode = descriptor->u.vnode;
4099 
4100 	FUNCTION(("attr_write: buf %p, pos %Ld, len %p\n", buffer, pos, length));
4101 	if (!HAS_FS_CALL(vnode, write_attr))
4102 		return FSSH_EOPNOTSUPP;
4103 
4104 	return FS_CALL(vnode, write_attr, descriptor->cookie, pos, buffer, length);
4105 }
4106 
4107 
4108 static fssh_off_t
4109 attr_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType)
4110 {
4111 	fssh_off_t offset;
4112 
4113 	switch (seekType) {
4114 		case FSSH_SEEK_SET:
4115 			offset = 0;
4116 			break;
4117 		case FSSH_SEEK_CUR:
4118 			offset = descriptor->pos;
4119 			break;
4120 		case FSSH_SEEK_END:
4121 		{
4122 			struct vnode *vnode = descriptor->u.vnode;
4123 			struct fssh_stat stat;
4124 			fssh_status_t status;
4125 
4126 			if (!HAS_FS_CALL(vnode, read_stat))
4127 				return FSSH_EOPNOTSUPP;
4128 
4129 			status = FS_CALL(vnode, read_attr_stat, descriptor->cookie, &stat);
4130 			if (status < FSSH_B_OK)
4131 				return status;
4132 
4133 			offset = stat.fssh_st_size;
4134 			break;
4135 		}
4136 		default:
4137 			return FSSH_B_BAD_VALUE;
4138 	}
4139 
4140 	// assumes fssh_off_t is 64 bits wide
4141 	if (offset > 0 && LLONG_MAX - offset < pos)
4142 		return FSSH_EOVERFLOW;
4143 
4144 	pos += offset;
4145 	if (pos < 0)
4146 		return FSSH_B_BAD_VALUE;
4147 
4148 	return descriptor->pos = pos;
4149 }
4150 
4151 
4152 static fssh_status_t
4153 attr_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat)
4154 {
4155 	struct vnode *vnode = descriptor->u.vnode;
4156 
4157 	FUNCTION(("attr_read_stat: stat 0x%p\n", stat));
4158 
4159 	if (!HAS_FS_CALL(vnode, read_attr_stat))
4160 		return FSSH_EOPNOTSUPP;
4161 
4162 	return FS_CALL(vnode, read_attr_stat, descriptor->cookie, stat);
4163 }
4164 
4165 
4166 static fssh_status_t
4167 attr_write_stat(struct file_descriptor *descriptor,
4168 	const struct fssh_stat *stat, int statMask)
4169 {
4170 	struct vnode *vnode = descriptor->u.vnode;
4171 
4172 	FUNCTION(("attr_write_stat: stat = %p, statMask %d\n", stat, statMask));
4173 
4174 	if (!HAS_FS_CALL(vnode, write_attr_stat))
4175 		return FSSH_EROFS;
4176 
4177 	return FS_CALL(vnode, write_attr_stat, descriptor->cookie, stat, statMask);
4178 }
4179 
4180 
4181 static fssh_status_t
4182 attr_remove(int fd, const char *name, bool kernel)
4183 {
4184 	struct file_descriptor *descriptor;
4185 	struct vnode *vnode;
4186 	fssh_status_t status;
4187 
4188 	if (name == NULL || *name == '\0')
4189 		return FSSH_B_BAD_VALUE;
4190 
4191 	FUNCTION(("attr_remove: fd = %d, name = \"%s\", kernel %d\n", fd, name, kernel));
4192 
4193 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
4194 	if (descriptor == NULL)
4195 		return FSSH_B_FILE_ERROR;
4196 
4197 	if (HAS_FS_CALL(vnode, remove_attr))
4198 		status = FS_CALL(vnode, remove_attr, name);
4199 	else
4200 		status = FSSH_EROFS;
4201 
4202 	put_fd(descriptor);
4203 
4204 	return status;
4205 }
4206 
4207 
4208 static fssh_status_t
4209 attr_rename(int fromfd, const char *fromName, int tofd, const char *toName, bool kernel)
4210 {
4211 	struct file_descriptor *fromDescriptor, *toDescriptor;
4212 	struct vnode *fromVnode, *toVnode;
4213 	fssh_status_t status;
4214 
4215 	if (fromName == NULL || *fromName == '\0' || toName == NULL || *toName == '\0')
4216 		return FSSH_B_BAD_VALUE;
4217 
4218 	FUNCTION(("attr_rename: from fd = %d, from name = \"%s\", to fd = %d, to name = \"%s\", kernel %d\n", fromfd, fromName, tofd, toName, kernel));
4219 
4220 	fromDescriptor = get_fd_and_vnode(fromfd, &fromVnode, kernel);
4221 	if (fromDescriptor == NULL)
4222 		return FSSH_B_FILE_ERROR;
4223 
4224 	toDescriptor = get_fd_and_vnode(tofd, &toVnode, kernel);
4225 	if (toDescriptor == NULL) {
4226 		status = FSSH_B_FILE_ERROR;
4227 		goto err;
4228 	}
4229 
4230 	// are the files on the same volume?
4231 	if (fromVnode->device != toVnode->device) {
4232 		status = FSSH_B_CROSS_DEVICE_LINK;
4233 		goto err1;
4234 	}
4235 
4236 	if (HAS_FS_CALL(fromVnode, rename_attr))
4237 		status = FS_CALL(fromVnode, rename_attr, fromName, toVnode, toName);
4238 	else
4239 		status = FSSH_EROFS;
4240 
4241 err1:
4242 	put_fd(toDescriptor);
4243 err:
4244 	put_fd(fromDescriptor);
4245 
4246 	return status;
4247 }
4248 
4249 
4250 static fssh_status_t
4251 index_dir_open(fssh_mount_id mountID, bool kernel)
4252 {
4253 	struct fs_mount *mount;
4254 	void *cookie;
4255 
4256 	FUNCTION(("index_dir_open(mountID = %ld, kernel = %d)\n", mountID, kernel));
4257 
4258 	fssh_status_t status = get_mount(mountID, &mount);
4259 	if (status < FSSH_B_OK)
4260 		return status;
4261 
4262 	if (!HAS_FS_MOUNT_CALL(mount, open_index_dir)) {
4263 		status = FSSH_EOPNOTSUPP;
4264 		goto out;
4265 	}
4266 
4267 	status = FS_MOUNT_CALL(mount, open_index_dir, &cookie);
4268 	if (status < FSSH_B_OK)
4269 		goto out;
4270 
4271 	// get fd for the index directory
4272 	status = get_new_fd(FDTYPE_INDEX_DIR, mount, NULL, cookie, 0, kernel);
4273 	if (status >= 0)
4274 		goto out;
4275 
4276 	// something went wrong
4277 	FS_MOUNT_CALL(mount, close_index_dir, cookie);
4278 	FS_MOUNT_CALL(mount, free_index_dir_cookie, cookie);
4279 
4280 out:
4281 	put_mount(mount);
4282 	return status;
4283 }
4284 
4285 
4286 static fssh_status_t
4287 index_dir_close(struct file_descriptor *descriptor)
4288 {
4289 	struct fs_mount *mount = descriptor->u.mount;
4290 
4291 	FUNCTION(("index_dir_close(descriptor = %p)\n", descriptor));
4292 
4293 	if (HAS_FS_MOUNT_CALL(mount, close_index_dir))
4294 		return FS_MOUNT_CALL(mount, close_index_dir, descriptor->cookie);
4295 
4296 	return FSSH_B_OK;
4297 }
4298 
4299 
4300 static void
4301 index_dir_free_fd(struct file_descriptor *descriptor)
4302 {
4303 	struct fs_mount *mount = descriptor->u.mount;
4304 
4305 	if (mount != NULL) {
4306 		FS_MOUNT_CALL(mount, free_index_dir_cookie, descriptor->cookie);
4307 		// ToDo: find a replacement ref_count object - perhaps the root dir?
4308 		//put_vnode(vnode);
4309 	}
4310 }
4311 
4312 
4313 static fssh_status_t
4314 index_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
4315 	fssh_size_t bufferSize, uint32_t *_count)
4316 {
4317 	struct fs_mount *mount = descriptor->u.mount;
4318 
4319 	if (HAS_FS_MOUNT_CALL(mount, read_index_dir))
4320 		return FS_MOUNT_CALL(mount, read_index_dir, descriptor->cookie, buffer, bufferSize, _count);
4321 
4322 	return FSSH_EOPNOTSUPP;
4323 }
4324 
4325 
4326 static fssh_status_t
4327 index_dir_rewind(struct file_descriptor *descriptor)
4328 {
4329 	struct fs_mount *mount = descriptor->u.mount;
4330 
4331 	if (HAS_FS_MOUNT_CALL(mount, rewind_index_dir))
4332 		return FS_MOUNT_CALL(mount, rewind_index_dir, descriptor->cookie);
4333 
4334 	return FSSH_EOPNOTSUPP;
4335 }
4336 
4337 
4338 static fssh_status_t
4339 index_create(fssh_mount_id mountID, const char *name, uint32_t type, uint32_t flags, bool kernel)
4340 {
4341 	FUNCTION(("index_create(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
4342 
4343 	struct fs_mount *mount;
4344 	fssh_status_t status = get_mount(mountID, &mount);
4345 	if (status < FSSH_B_OK)
4346 		return status;
4347 
4348 	if (!HAS_FS_MOUNT_CALL(mount, create_index)) {
4349 		status = FSSH_EROFS;
4350 		goto out;
4351 	}
4352 
4353 	status = FS_MOUNT_CALL(mount, create_index, name, type, flags);
4354 
4355 out:
4356 	put_mount(mount);
4357 	return status;
4358 }
4359 
4360 
4361 static fssh_status_t
4362 index_name_read_stat(fssh_mount_id mountID, const char *name,
4363 	struct fssh_stat *stat, bool kernel)
4364 {
4365 	FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
4366 
4367 	struct fs_mount *mount;
4368 	fssh_status_t status = get_mount(mountID, &mount);
4369 	if (status < FSSH_B_OK)
4370 		return status;
4371 
4372 	if (!HAS_FS_MOUNT_CALL(mount, read_index_stat)) {
4373 		status = FSSH_EOPNOTSUPP;
4374 		goto out;
4375 	}
4376 
4377 	status = FS_MOUNT_CALL(mount, read_index_stat, name, stat);
4378 
4379 out:
4380 	put_mount(mount);
4381 	return status;
4382 }
4383 
4384 
4385 static fssh_status_t
4386 index_remove(fssh_mount_id mountID, const char *name, bool kernel)
4387 {
4388 	FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
4389 
4390 	struct fs_mount *mount;
4391 	fssh_status_t status = get_mount(mountID, &mount);
4392 	if (status < FSSH_B_OK)
4393 		return status;
4394 
4395 	if (!HAS_FS_MOUNT_CALL(mount, remove_index)) {
4396 		status = FSSH_EROFS;
4397 		goto out;
4398 	}
4399 
4400 	status = FS_MOUNT_CALL(mount, remove_index, name);
4401 
4402 out:
4403 	put_mount(mount);
4404 	return status;
4405 }
4406 
4407 
4408 /*!	ToDo: the query FS API is still the pretty much the same as in R5.
4409 		It would be nice if the FS would find some more kernel support
4410 		for them.
4411 		For example, query parsing should be moved into the kernel.
4412 */
4413 static int
4414 query_open(fssh_dev_t device, const char *query, uint32_t flags,
4415 	fssh_port_id port, int32_t token, bool kernel)
4416 {
4417 	struct fs_mount *mount;
4418 	void *cookie;
4419 
4420 	FUNCTION(("query_open(device = %ld, query = \"%s\", kernel = %d)\n", device, query, kernel));
4421 
4422 	fssh_status_t status = get_mount(device, &mount);
4423 	if (status < FSSH_B_OK)
4424 		return status;
4425 
4426 	if (!HAS_FS_MOUNT_CALL(mount, open_query)) {
4427 		status = FSSH_EOPNOTSUPP;
4428 		goto out;
4429 	}
4430 
4431 	status = FS_MOUNT_CALL(mount, open_query, query, flags, port, token, &cookie);
4432 	if (status < FSSH_B_OK)
4433 		goto out;
4434 
4435 	// get fd for the index directory
4436 	status = get_new_fd(FDTYPE_QUERY, mount, NULL, cookie, 0, kernel);
4437 	if (status >= 0)
4438 		goto out;
4439 
4440 	// something went wrong
4441 	FS_MOUNT_CALL(mount, close_query, cookie);
4442 	FS_MOUNT_CALL(mount, free_query_cookie, cookie);
4443 
4444 out:
4445 	put_mount(mount);
4446 	return status;
4447 }
4448 
4449 
4450 static fssh_status_t
4451 query_close(struct file_descriptor *descriptor)
4452 {
4453 	struct fs_mount *mount = descriptor->u.mount;
4454 
4455 	FUNCTION(("query_close(descriptor = %p)\n", descriptor));
4456 
4457 	if (HAS_FS_MOUNT_CALL(mount, close_query))
4458 		return FS_MOUNT_CALL(mount, close_query, descriptor->cookie);
4459 
4460 	return FSSH_B_OK;
4461 }
4462 
4463 
4464 static void
4465 query_free_fd(struct file_descriptor *descriptor)
4466 {
4467 	struct fs_mount *mount = descriptor->u.mount;
4468 
4469 	if (mount != NULL) {
4470 		FS_MOUNT_CALL(mount, free_query_cookie, descriptor->cookie);
4471 		// ToDo: find a replacement ref_count object - perhaps the root dir?
4472 		//put_vnode(vnode);
4473 	}
4474 }
4475 
4476 
4477 static fssh_status_t
4478 query_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
4479 	fssh_size_t bufferSize, uint32_t *_count)
4480 {
4481 	struct fs_mount *mount = descriptor->u.mount;
4482 
4483 	if (HAS_FS_MOUNT_CALL(mount, read_query))
4484 		return FS_MOUNT_CALL(mount, read_query, descriptor->cookie, buffer, bufferSize, _count);
4485 
4486 	return FSSH_EOPNOTSUPP;
4487 }
4488 
4489 
4490 static fssh_status_t
4491 query_rewind(struct file_descriptor *descriptor)
4492 {
4493 	struct fs_mount *mount = descriptor->u.mount;
4494 
4495 	if (HAS_FS_MOUNT_CALL(mount, rewind_query))
4496 		return FS_MOUNT_CALL(mount, rewind_query, descriptor->cookie);
4497 
4498 	return FSSH_EOPNOTSUPP;
4499 }
4500 
4501 
4502 //	#pragma mark -
4503 //	General File System functions
4504 
4505 
4506 static fssh_dev_t
4507 fs_mount(char *path, const char *device, const char *fsName, uint32_t flags,
4508 	const char *args, bool kernel)
4509 {
4510 	struct fs_mount *mount;
4511 	fssh_status_t status = 0;
4512 
4513 	FUNCTION(("fs_mount: entry. path = '%s', fs_name = '%s'\n", path, fsName));
4514 
4515 	// The path is always safe, we just have to make sure that fsName is
4516 	// almost valid - we can't make any assumptions about args, though.
4517 	// A NULL fsName is OK, if a device was given and the FS is not virtual.
4518 	// We'll get it from the DDM later.
4519 	if (fsName == NULL) {
4520 		if (!device || flags & FSSH_B_MOUNT_VIRTUAL_DEVICE)
4521 			return FSSH_B_BAD_VALUE;
4522 	} else if (fsName[0] == '\0')
4523 		return FSSH_B_BAD_VALUE;
4524 
4525 	RecursiveLocker mountOpLocker(sMountOpLock);
4526 
4527 	// If the file system is not a "virtual" one, the device argument should
4528 	// point to a real file/device (if given at all).
4529 	// get the partition
4530 	KPath normalizedDevice;
4531 
4532 	if (!(flags & FSSH_B_MOUNT_VIRTUAL_DEVICE) && device) {
4533 		// normalize the device path
4534 //		status = normalizedDevice.SetTo(device, true);
4535 // NOTE: normalizing works only in our namespace.
4536 		status = normalizedDevice.SetTo(device, false);
4537 		if (status != FSSH_B_OK)
4538 			return status;
4539 
4540 		device = normalizedDevice.Path();
4541 			// correct path to file device
4542 	}
4543 
4544 	mount = (struct fs_mount *)malloc(sizeof(struct fs_mount));
4545 	if (mount == NULL)
4546 		return FSSH_B_NO_MEMORY;
4547 
4548 	mount->volume = (fssh_fs_volume*)malloc(sizeof(fssh_fs_volume));
4549 	if (mount->volume == NULL) {
4550 		free(mount);
4551 		return FSSH_B_NO_MEMORY;
4552 	}
4553 
4554 	list_init_etc(&mount->vnodes, fssh_offsetof(struct vnode, mount_link));
4555 
4556 	mount->fs_name = get_file_system_name(fsName);
4557 	if (mount->fs_name == NULL) {
4558 		status = FSSH_B_NO_MEMORY;
4559 		goto err1;
4560 	}
4561 
4562 	mount->device_name = fssh_strdup(device);
4563 		// "device" can be NULL
4564 
4565 	mount->fs = get_file_system(fsName);
4566 	if (mount->fs == NULL) {
4567 		status = FSSH_ENODEV;
4568 		goto err3;
4569 	}
4570 
4571 	fssh_recursive_lock_init(&mount->rlock, "mount rlock");
4572 
4573 	// initialize structure
4574 	mount->id = sNextMountID++;
4575 	mount->root_vnode = NULL;
4576 	mount->covers_vnode = NULL;
4577 	mount->unmounting = false;
4578 	mount->owns_file_device = false;
4579 
4580 	mount->volume->id = mount->id;
4581 	mount->volume->layer = 0;
4582 	mount->volume->private_volume = NULL;
4583 	mount->volume->ops = NULL;
4584 	mount->volume->sub_volume = NULL;
4585 	mount->volume->super_volume = NULL;
4586 
4587 	// insert mount struct into list before we call FS's mount() function
4588 	// so that vnodes can be created for this mount
4589 	fssh_mutex_lock(&sMountMutex);
4590 	hash_insert(sMountsTable, mount);
4591 	fssh_mutex_unlock(&sMountMutex);
4592 
4593 	fssh_vnode_id rootID;
4594 
4595 	if (!sRoot) {
4596 		// we haven't mounted anything yet
4597 		if (fssh_strcmp(path, "/") != 0) {
4598 			status = FSSH_B_ERROR;
4599 			goto err4;
4600 		}
4601 
4602 		status = mount->fs->mount(mount->volume, device, flags, args, &rootID);
4603 		if (status < 0) {
4604 			// ToDo: why should we hide the error code from the file system here?
4605 			//status = ERR_VFS_GENERAL;
4606 			goto err4;
4607 		}
4608 	} else {
4609 		struct vnode *coveredVnode;
4610 		status = path_to_vnode(path, true, &coveredVnode, NULL, kernel);
4611 		if (status < FSSH_B_OK)
4612 			goto err4;
4613 
4614 		// make sure covered_vnode is a DIR
4615 		struct fssh_stat coveredNodeStat;
4616 		status = FS_CALL(coveredVnode, read_stat, &coveredNodeStat);
4617 		if (status < FSSH_B_OK)
4618 			goto err4;
4619 
4620 		if (!FSSH_S_ISDIR(coveredNodeStat.fssh_st_mode)) {
4621 			status = FSSH_B_NOT_A_DIRECTORY;
4622 			goto err4;
4623 		}
4624 
4625 		if (coveredVnode->mount->root_vnode == coveredVnode) {
4626 			// this is already a mount point
4627 			status = FSSH_B_BUSY;
4628 			goto err4;
4629 		}
4630 
4631 		mount->covers_vnode = coveredVnode;
4632 
4633 		// mount it
4634 		status = mount->fs->mount(mount->volume, device, flags, args, &rootID);
4635 		if (status < FSSH_B_OK)
4636 			goto err5;
4637 	}
4638 
4639 	// the root node is supposed to be owned by the file system - it must
4640 	// exist at this point
4641 	mount->root_vnode = lookup_vnode(mount->id, rootID);
4642 	if (mount->root_vnode == NULL || mount->root_vnode->ref_count != 1) {
4643 		fssh_panic("fs_mount: file system does not own its root node!\n");
4644 		status = FSSH_B_ERROR;
4645 		goto err6;
4646 	}
4647 
4648 	// No race here, since fs_mount() is the only function changing
4649 	// covers_vnode (and holds sMountOpLock at that time).
4650 	fssh_mutex_lock(&sVnodeCoveredByMutex);
4651 	if (mount->covers_vnode)
4652 		mount->covers_vnode->covered_by = mount->root_vnode;
4653 	fssh_mutex_unlock(&sVnodeCoveredByMutex);
4654 
4655 	if (!sRoot)
4656 		sRoot = mount->root_vnode;
4657 
4658 	return mount->id;
4659 
4660 err6:
4661 	FS_MOUNT_CALL_NO_PARAMS(mount, unmount);
4662 err5:
4663 	if (mount->covers_vnode)
4664 		put_vnode(mount->covers_vnode);
4665 
4666 err4:
4667 	fssh_mutex_lock(&sMountMutex);
4668 	hash_remove(sMountsTable, mount);
4669 	fssh_mutex_unlock(&sMountMutex);
4670 
4671 	fssh_recursive_lock_destroy(&mount->rlock);
4672 
4673 	put_file_system(mount->fs);
4674 	free(mount->device_name);
4675 err3:
4676 	free(mount->fs_name);
4677 err1:
4678 	free(mount->volume);
4679 	free(mount);
4680 
4681 	return status;
4682 }
4683 
4684 
4685 static fssh_status_t
4686 fs_unmount(char *path, uint32_t flags, bool kernel)
4687 {
4688 	struct fs_mount *mount;
4689 	struct vnode *vnode;
4690 	fssh_status_t err;
4691 
4692 	FUNCTION(("vfs_unmount: entry. path = '%s', kernel %d\n", path, kernel));
4693 
4694 	err = path_to_vnode(path, true, &vnode, NULL, kernel);
4695 	if (err < 0)
4696 		return FSSH_B_ENTRY_NOT_FOUND;
4697 
4698 	RecursiveLocker mountOpLocker(sMountOpLock);
4699 
4700 	mount = find_mount(vnode->device);
4701 	if (!mount)
4702 		fssh_panic("vfs_unmount: find_mount() failed on root vnode @%p of mount\n", vnode);
4703 
4704 	if (mount->root_vnode != vnode) {
4705 		// not mountpoint
4706 		put_vnode(vnode);
4707 		return FSSH_B_BAD_VALUE;
4708 	}
4709 
4710 	// grab the vnode master mutex to keep someone from creating
4711 	// a vnode while we're figuring out if we can continue
4712 	fssh_mutex_lock(&sVnodeMutex);
4713 
4714 	bool disconnectedDescriptors = false;
4715 
4716 	while (true) {
4717 		bool busy = false;
4718 
4719 		// cycle through the list of vnodes associated with this mount and
4720 		// make sure all of them are not busy or have refs on them
4721 		vnode = NULL;
4722 		while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) {
4723 			// The root vnode ref_count needs to be 2 here: one for the file
4724 			// system, one from the path_to_vnode() call above
4725 			if (vnode->busy
4726 				|| ((vnode->ref_count != 0 && mount->root_vnode != vnode)
4727 					|| (vnode->ref_count != 2 && mount->root_vnode == vnode))) {
4728 				// there are still vnodes in use on this mount, so we cannot
4729 				// unmount yet
4730 				busy = true;
4731 				break;
4732 			}
4733 		}
4734 
4735 		if (!busy)
4736 			break;
4737 
4738 		if ((flags & FSSH_B_FORCE_UNMOUNT) == 0) {
4739 			fssh_mutex_unlock(&sVnodeMutex);
4740 			put_vnode(mount->root_vnode);
4741 
4742 			return FSSH_B_BUSY;
4743 		}
4744 
4745 		if (disconnectedDescriptors) {
4746 			// wait a bit until the last access is finished, and then try again
4747 			fssh_mutex_unlock(&sVnodeMutex);
4748 			fssh_snooze(100000);
4749 			// TODO: if there is some kind of bug that prevents the ref counts
4750 			//	from getting back to zero, this will fall into an endless loop...
4751 			fssh_mutex_lock(&sVnodeMutex);
4752 			continue;
4753 		}
4754 
4755 		// the file system is still busy - but we're forced to unmount it,
4756 		// so let's disconnect all open file descriptors
4757 
4758 		mount->unmounting = true;
4759 			// prevent new vnodes from being created
4760 
4761 		fssh_mutex_unlock(&sVnodeMutex);
4762 
4763 		disconnect_mount_or_vnode_fds(mount, NULL);
4764 		disconnectedDescriptors = true;
4765 
4766 		fssh_mutex_lock(&sVnodeMutex);
4767 	}
4768 
4769 	// we can safely continue, mark all of the vnodes busy and this mount
4770 	// structure in unmounting state
4771 	mount->unmounting = true;
4772 
4773 	while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) {
4774 		vnode->busy = true;
4775 
4776 		if (vnode->ref_count == 0) {
4777 			// this vnode has been unused before
4778 			list_remove_item(&sUnusedVnodeList, vnode);
4779 			sUnusedVnodes--;
4780 		}
4781 	}
4782 
4783 	// The ref_count of the root node is 2 at this point, see above why this is
4784 	mount->root_vnode->ref_count -= 2;
4785 
4786 	fssh_mutex_unlock(&sVnodeMutex);
4787 
4788 	fssh_mutex_lock(&sVnodeCoveredByMutex);
4789 	mount->covers_vnode->covered_by = NULL;
4790 	fssh_mutex_unlock(&sVnodeCoveredByMutex);
4791 	put_vnode(mount->covers_vnode);
4792 
4793 	// Free all vnodes associated with this mount.
4794 	// They will be removed from the mount list by free_vnode(), so
4795 	// we don't have to do this.
4796 	while ((vnode = (struct vnode *)list_get_first_item(&mount->vnodes)) != NULL) {
4797 		free_vnode(vnode, false);
4798 	}
4799 
4800 	// remove the mount structure from the hash table
4801 	fssh_mutex_lock(&sMountMutex);
4802 	hash_remove(sMountsTable, mount);
4803 	fssh_mutex_unlock(&sMountMutex);
4804 
4805 	mountOpLocker.Unlock();
4806 
4807 	FS_MOUNT_CALL_NO_PARAMS(mount, unmount);
4808 
4809 	// release the file system
4810 	put_file_system(mount->fs);
4811 
4812 	free(mount->device_name);
4813 	free(mount->fs_name);
4814 	free(mount);
4815 
4816 	return FSSH_B_OK;
4817 }
4818 
4819 
4820 static fssh_status_t
4821 fs_sync(fssh_dev_t device)
4822 {
4823 	struct fs_mount *mount;
4824 	fssh_status_t status = get_mount(device, &mount);
4825 	if (status < FSSH_B_OK)
4826 		return status;
4827 
4828 	fssh_mutex_lock(&sMountMutex);
4829 
4830 	if (HAS_FS_MOUNT_CALL(mount, sync))
4831 		status = FS_MOUNT_CALL_NO_PARAMS(mount, sync);
4832 
4833 	fssh_mutex_unlock(&sMountMutex);
4834 
4835 	struct vnode *previousVnode = NULL;
4836 	while (true) {
4837 		// synchronize access to vnode list
4838 		fssh_recursive_lock_lock(&mount->rlock);
4839 
4840 		struct vnode *vnode = (struct vnode *)list_get_next_item(&mount->vnodes,
4841 			previousVnode);
4842 
4843 		fssh_vnode_id id = -1;
4844 		if (vnode != NULL)
4845 			id = vnode->id;
4846 
4847 		fssh_recursive_lock_unlock(&mount->rlock);
4848 
4849 		if (vnode == NULL)
4850 			break;
4851 
4852 		// acquire a reference to the vnode
4853 
4854 		if (get_vnode(mount->id, id, &vnode, true) == FSSH_B_OK) {
4855 			if (previousVnode != NULL)
4856 				put_vnode(previousVnode);
4857 
4858 			if (HAS_FS_CALL(vnode, fsync))
4859 				FS_CALL_NO_PARAMS(vnode, fsync);
4860 
4861 			// the next vnode might change until we lock the vnode list again,
4862 			// but this vnode won't go away since we keep a reference to it.
4863 			previousVnode = vnode;
4864 		} else {
4865 			fssh_dprintf("syncing of mount %d stopped due to vnode %"
4866 				FSSH_B_PRIdINO ".\n", (int)mount->id, id);
4867 			break;
4868 		}
4869 	}
4870 
4871 	if (previousVnode != NULL)
4872 		put_vnode(previousVnode);
4873 
4874 	put_mount(mount);
4875 	return status;
4876 }
4877 
4878 
4879 static fssh_status_t
4880 fs_read_info(fssh_dev_t device, struct fssh_fs_info *info)
4881 {
4882 	struct fs_mount *mount;
4883 	fssh_status_t status = get_mount(device, &mount);
4884 	if (status < FSSH_B_OK)
4885 		return status;
4886 
4887 	fssh_memset(info, 0, sizeof(struct fssh_fs_info));
4888 
4889 	if (HAS_FS_MOUNT_CALL(mount, read_fs_info))
4890 		status = FS_MOUNT_CALL(mount, read_fs_info, info);
4891 
4892 	// fill in info the file system doesn't (have to) know about
4893 	if (status == FSSH_B_OK) {
4894 		info->dev = mount->id;
4895 		info->root = mount->root_vnode->id;
4896 		fssh_strlcpy(info->fsh_name, mount->fs_name, sizeof(info->fsh_name));
4897 		if (mount->device_name != NULL) {
4898 			fssh_strlcpy(info->device_name, mount->device_name,
4899 				sizeof(info->device_name));
4900 		}
4901 	}
4902 
4903 	// if the call is not supported by the file system, there are still
4904 	// the parts that we filled out ourselves
4905 
4906 	put_mount(mount);
4907 	return status;
4908 }
4909 
4910 
4911 static fssh_status_t
4912 fs_write_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask)
4913 {
4914 	struct fs_mount *mount;
4915 	fssh_status_t status = get_mount(device, &mount);
4916 	if (status < FSSH_B_OK)
4917 		return status;
4918 
4919 	if (HAS_FS_MOUNT_CALL(mount, write_fs_info))
4920 		status = FS_MOUNT_CALL(mount, write_fs_info, info, mask);
4921 	else
4922 		status = FSSH_EROFS;
4923 
4924 	put_mount(mount);
4925 	return status;
4926 }
4927 
4928 
4929 static fssh_dev_t
4930 fs_next_device(int32_t *_cookie)
4931 {
4932 	struct fs_mount *mount = NULL;
4933 	fssh_dev_t device = *_cookie;
4934 
4935 	fssh_mutex_lock(&sMountMutex);
4936 
4937 	// Since device IDs are assigned sequentially, this algorithm
4938 	// does work good enough. It makes sure that the device list
4939 	// returned is sorted, and that no device is skipped when an
4940 	// already visited device got unmounted.
4941 
4942 	while (device < sNextMountID) {
4943 		mount = find_mount(device++);
4944 		if (mount != NULL && mount->volume->private_volume != NULL)
4945 			break;
4946 	}
4947 
4948 	*_cookie = device;
4949 
4950 	if (mount != NULL)
4951 		device = mount->id;
4952 	else
4953 		device = FSSH_B_BAD_VALUE;
4954 
4955 	fssh_mutex_unlock(&sMountMutex);
4956 
4957 	return device;
4958 }
4959 
4960 
4961 static fssh_status_t
4962 get_cwd(char *buffer, fssh_size_t size, bool kernel)
4963 {
4964 	// Get current working directory from io context
4965 	struct io_context *context = get_current_io_context(kernel);
4966 	fssh_status_t status;
4967 
4968 	FUNCTION(("vfs_get_cwd: buf %p, size %ld\n", buffer, size));
4969 
4970 	fssh_mutex_lock(&context->io_mutex);
4971 
4972 	if (context->cwd)
4973 		status = dir_vnode_to_path(context->cwd, buffer, size);
4974 	else
4975 		status = FSSH_B_ERROR;
4976 
4977 	fssh_mutex_unlock(&context->io_mutex);
4978 	return status;
4979 }
4980 
4981 
4982 static fssh_status_t
4983 set_cwd(int fd, char *path, bool kernel)
4984 {
4985 	struct io_context *context;
4986 	struct vnode *vnode = NULL;
4987 	struct vnode *oldDirectory;
4988 	struct fssh_stat stat;
4989 	fssh_status_t status;
4990 
4991 	FUNCTION(("set_cwd: path = \'%s\'\n", path));
4992 
4993 	// Get vnode for passed path, and bail if it failed
4994 	status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel);
4995 	if (status < 0)
4996 		return status;
4997 
4998 	status = FS_CALL(vnode, read_stat, &stat);
4999 	if (status < 0)
5000 		goto err;
5001 
5002 	if (!FSSH_S_ISDIR(stat.fssh_st_mode)) {
5003 		// nope, can't cwd to here
5004 		status = FSSH_B_NOT_A_DIRECTORY;
5005 		goto err;
5006 	}
5007 
5008 	// Get current io context and lock
5009 	context = get_current_io_context(kernel);
5010 	fssh_mutex_lock(&context->io_mutex);
5011 
5012 	// save the old current working directory first
5013 	oldDirectory = context->cwd;
5014 	context->cwd = vnode;
5015 
5016 	fssh_mutex_unlock(&context->io_mutex);
5017 
5018 	if (oldDirectory)
5019 		put_vnode(oldDirectory);
5020 
5021 	return FSSH_B_NO_ERROR;
5022 
5023 err:
5024 	put_vnode(vnode);
5025 	return status;
5026 }
5027 
5028 
5029 //	#pragma mark -
5030 //	Calls from within the kernel
5031 
5032 
5033 fssh_dev_t
5034 _kern_mount(const char *path, const char *device, const char *fsName,
5035 	uint32_t flags, const char *args, fssh_size_t argsLength)
5036 {
5037 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5038 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5039 		return FSSH_B_NO_MEMORY;
5040 
5041 	return fs_mount(pathBuffer.LockBuffer(), device, fsName, flags, args, true);
5042 }
5043 
5044 
5045 fssh_status_t
5046 _kern_unmount(const char *path, uint32_t flags)
5047 {
5048 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5049 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5050 		return FSSH_B_NO_MEMORY;
5051 
5052 	return fs_unmount(pathBuffer.LockBuffer(), flags, true);
5053 }
5054 
5055 
5056 fssh_status_t
5057 _kern_read_fs_info(fssh_dev_t device, struct fssh_fs_info *info)
5058 {
5059 	if (info == NULL)
5060 		return FSSH_B_BAD_VALUE;
5061 
5062 	return fs_read_info(device, info);
5063 }
5064 
5065 
5066 fssh_status_t
5067 _kern_write_fs_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask)
5068 {
5069 	if (info == NULL)
5070 		return FSSH_B_BAD_VALUE;
5071 
5072 	return fs_write_info(device, info, mask);
5073 }
5074 
5075 
5076 fssh_status_t
5077 _kern_sync(void)
5078 {
5079 	// Note: _kern_sync() is also called from _user_sync()
5080 	int32_t cookie = 0;
5081 	fssh_dev_t device;
5082 	while ((device = fs_next_device(&cookie)) >= 0) {
5083 		fssh_status_t status = fs_sync(device);
5084 		if (status != FSSH_B_OK && status != FSSH_B_BAD_VALUE)
5085 			fssh_dprintf("sync: device %d couldn't sync: %s\n", (int)device, fssh_strerror(status));
5086 	}
5087 
5088 	return FSSH_B_OK;
5089 }
5090 
5091 
5092 fssh_dev_t
5093 _kern_next_device(int32_t *_cookie)
5094 {
5095 	return fs_next_device(_cookie);
5096 }
5097 
5098 
5099 int
5100 _kern_open_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int openMode, int perms)
5101 {
5102 	if (openMode & FSSH_O_CREAT)
5103 		return file_create_entry_ref(device, inode, name, openMode, perms, true);
5104 
5105 	return file_open_entry_ref(device, inode, name, openMode, true);
5106 }
5107 
5108 
5109 /**	\brief Opens a node specified by a FD + path pair.
5110  *
5111  *	At least one of \a fd and \a path must be specified.
5112  *	If only \a fd is given, the function opens the node identified by this
5113  *	FD. If only a path is given, this path is opened. If both are given and
5114  *	the path is absolute, \a fd is ignored; a relative path is reckoned off
5115  *	of the directory (!) identified by \a fd.
5116  *
5117  *	\param fd The FD. May be < 0.
5118  *	\param path The absolute or relative path. May be \c NULL.
5119  *	\param openMode The open mode.
5120  *	\return A FD referring to the newly opened node, or an error code,
5121  *			if an error occurs.
5122  */
5123 
5124 int
5125 _kern_open(int fd, const char *path, int openMode, int perms)
5126 {
5127 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5128 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5129 		return FSSH_B_NO_MEMORY;
5130 
5131 	if (openMode & FSSH_O_CREAT)
5132 		return file_create(fd, pathBuffer.LockBuffer(), openMode, perms, true);
5133 
5134 	return file_open(fd, pathBuffer.LockBuffer(), openMode, true);
5135 }
5136 
5137 
5138 /**	\brief Opens a directory specified by entry_ref or node_ref.
5139  *
5140  *	The supplied name may be \c NULL, in which case directory identified
5141  *	by \a device and \a inode will be opened. Otherwise \a device and
5142  *	\a inode identify the parent directory of the directory to be opened
5143  *	and \a name its entry name.
5144  *
5145  *	\param device If \a name is specified the ID of the device the parent
5146  *		   directory of the directory to be opened resides on, otherwise
5147  *		   the device of the directory itself.
5148  *	\param inode If \a name is specified the node ID of the parent
5149  *		   directory of the directory to be opened, otherwise node ID of the
5150  *		   directory itself.
5151  *	\param name The entry name of the directory to be opened. If \c NULL,
5152  *		   the \a device + \a inode pair identify the node to be opened.
5153  *	\return The FD of the newly opened directory or an error code, if
5154  *			something went wrong.
5155  */
5156 
5157 int
5158 _kern_open_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name)
5159 {
5160 	return dir_open_entry_ref(device, inode, name, true);
5161 }
5162 
5163 
5164 /**	\brief Opens a directory specified by a FD + path pair.
5165  *
5166  *	At least one of \a fd and \a path must be specified.
5167  *	If only \a fd is given, the function opens the directory identified by this
5168  *	FD. If only a path is given, this path is opened. If both are given and
5169  *	the path is absolute, \a fd is ignored; a relative path is reckoned off
5170  *	of the directory (!) identified by \a fd.
5171  *
5172  *	\param fd The FD. May be < 0.
5173  *	\param path The absolute or relative path. May be \c NULL.
5174  *	\return A FD referring to the newly opened directory, or an error code,
5175  *			if an error occurs.
5176  */
5177 
5178 int
5179 _kern_open_dir(int fd, const char *path)
5180 {
5181 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5182 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5183 		return FSSH_B_NO_MEMORY;
5184 
5185 	return dir_open(fd, pathBuffer.LockBuffer(), true);
5186 }
5187 
5188 
5189 fssh_status_t
5190 _kern_fcntl(int fd, int op, uint32_t argument)
5191 {
5192 	return common_fcntl(fd, op, argument, true);
5193 }
5194 
5195 
5196 fssh_status_t
5197 _kern_fsync(int fd)
5198 {
5199 	return common_sync(fd, true);
5200 }
5201 
5202 
5203 fssh_status_t
5204 _kern_lock_node(int fd)
5205 {
5206 	return common_lock_node(fd, true);
5207 }
5208 
5209 
5210 fssh_status_t
5211 _kern_unlock_node(int fd)
5212 {
5213 	return common_unlock_node(fd, true);
5214 }
5215 
5216 
5217 fssh_status_t
5218 _kern_create_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int perms)
5219 {
5220 	return dir_create_entry_ref(device, inode, name, perms, true);
5221 }
5222 
5223 
5224 /**	\brief Creates a directory specified by a FD + path pair.
5225  *
5226  *	\a path must always be specified (it contains the name of the new directory
5227  *	at least). If only a path is given, this path identifies the location at
5228  *	which the directory shall be created. If both \a fd and \a path are given and
5229  *	the path is absolute, \a fd is ignored; a relative path is reckoned off
5230  *	of the directory (!) identified by \a fd.
5231  *
5232  *	\param fd The FD. May be < 0.
5233  *	\param path The absolute or relative path. Must not be \c NULL.
5234  *	\param perms The access permissions the new directory shall have.
5235  *	\return \c FSSH_B_OK, if the directory has been created successfully, another
5236  *			error code otherwise.
5237  */
5238 
5239 fssh_status_t
5240 _kern_create_dir(int fd, const char *path, int perms)
5241 {
5242 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5243 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5244 		return FSSH_B_NO_MEMORY;
5245 
5246 	return dir_create(fd, pathBuffer.LockBuffer(), perms, true);
5247 }
5248 
5249 
5250 fssh_status_t
5251 _kern_remove_dir(int fd, const char *path)
5252 {
5253 	if (path) {
5254 		KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5255 		if (pathBuffer.InitCheck() != FSSH_B_OK)
5256 			return FSSH_B_NO_MEMORY;
5257 
5258 		return dir_remove(fd, pathBuffer.LockBuffer(), true);
5259 	}
5260 
5261 	return dir_remove(fd, NULL, true);
5262 }
5263 
5264 
5265 /**	\brief Reads the contents of a symlink referred to by a FD + path pair.
5266  *
5267  *	At least one of \a fd and \a path must be specified.
5268  *	If only \a fd is given, the function the symlink to be read is the node
5269  *	identified by this FD. If only a path is given, this path identifies the
5270  *	symlink to be read. If both are given and the path is absolute, \a fd is
5271  *	ignored; a relative path is reckoned off of the directory (!) identified
5272  *	by \a fd.
5273  *	If this function fails with FSSH_B_BUFFER_OVERFLOW, the \a _bufferSize pointer
5274  *	will still be updated to reflect the required buffer size.
5275  *
5276  *	\param fd The FD. May be < 0.
5277  *	\param path The absolute or relative path. May be \c NULL.
5278  *	\param buffer The buffer into which the contents of the symlink shall be
5279  *		   written.
5280  *	\param _bufferSize A pointer to the size of the supplied buffer.
5281  *	\return The length of the link on success or an appropriate error code
5282  */
5283 
5284 fssh_status_t
5285 _kern_read_link(int fd, const char *path, char *buffer, fssh_size_t *_bufferSize)
5286 {
5287 	if (path) {
5288 		KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5289 		if (pathBuffer.InitCheck() != FSSH_B_OK)
5290 			return FSSH_B_NO_MEMORY;
5291 
5292 		return common_read_link(fd, pathBuffer.LockBuffer(),
5293 			buffer, _bufferSize, true);
5294 	}
5295 
5296 	return common_read_link(fd, NULL, buffer, _bufferSize, true);
5297 }
5298 
5299 
5300 /**	\brief Creates a symlink specified by a FD + path pair.
5301  *
5302  *	\a path must always be specified (it contains the name of the new symlink
5303  *	at least). If only a path is given, this path identifies the location at
5304  *	which the symlink shall be created. If both \a fd and \a path are given and
5305  *	the path is absolute, \a fd is ignored; a relative path is reckoned off
5306  *	of the directory (!) identified by \a fd.
5307  *
5308  *	\param fd The FD. May be < 0.
5309  *	\param toPath The absolute or relative path. Must not be \c NULL.
5310  *	\param mode The access permissions the new symlink shall have.
5311  *	\return \c FSSH_B_OK, if the symlink has been created successfully, another
5312  *			error code otherwise.
5313  */
5314 
5315 fssh_status_t
5316 _kern_create_symlink(int fd, const char *path, const char *toPath, int mode)
5317 {
5318 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5319 	KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
5320 	if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK)
5321 		return FSSH_B_NO_MEMORY;
5322 
5323 	char *toBuffer = toPathBuffer.LockBuffer();
5324 
5325 	fssh_status_t status = check_path(toBuffer);
5326 	if (status < FSSH_B_OK)
5327 		return status;
5328 
5329 	return common_create_symlink(fd, pathBuffer.LockBuffer(),
5330 		toBuffer, mode, true);
5331 }
5332 
5333 
5334 fssh_status_t
5335 _kern_create_link(const char *path, const char *toPath)
5336 {
5337 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5338 	KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
5339 	if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK)
5340 		return FSSH_B_NO_MEMORY;
5341 
5342 	return common_create_link(pathBuffer.LockBuffer(),
5343 		toPathBuffer.LockBuffer(), true);
5344 }
5345 
5346 
5347 /**	\brief Removes an entry specified by a FD + path pair from its directory.
5348  *
5349  *	\a path must always be specified (it contains at least the name of the entry
5350  *	to be deleted). If only a path is given, this path identifies the entry
5351  *	directly. If both \a fd and \a path are given and the path is absolute,
5352  *	\a fd is ignored; a relative path is reckoned off of the directory (!)
5353  *	identified by \a fd.
5354  *
5355  *	\param fd The FD. May be < 0.
5356  *	\param path The absolute or relative path. Must not be \c NULL.
5357  *	\return \c FSSH_B_OK, if the entry has been removed successfully, another
5358  *			error code otherwise.
5359  */
5360 
5361 fssh_status_t
5362 _kern_unlink(int fd, const char *path)
5363 {
5364 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5365 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5366 		return FSSH_B_NO_MEMORY;
5367 
5368 	return common_unlink(fd, pathBuffer.LockBuffer(), true);
5369 }
5370 
5371 
5372 /**	\brief Moves an entry specified by a FD + path pair to a an entry specified
5373  *		   by another FD + path pair.
5374  *
5375  *	\a oldPath and \a newPath must always be specified (they contain at least
5376  *	the name of the entry). If only a path is given, this path identifies the
5377  *	entry directly. If both a FD and a path are given and the path is absolute,
5378  *	the FD is ignored; a relative path is reckoned off of the directory (!)
5379  *	identified by the respective FD.
5380  *
5381  *	\param oldFD The FD of the old location. May be < 0.
5382  *	\param oldPath The absolute or relative path of the old location. Must not
5383  *		   be \c NULL.
5384  *	\param newFD The FD of the new location. May be < 0.
5385  *	\param newPath The absolute or relative path of the new location. Must not
5386  *		   be \c NULL.
5387  *	\return \c FSSH_B_OK, if the entry has been moved successfully, another
5388  *			error code otherwise.
5389  */
5390 
5391 fssh_status_t
5392 _kern_rename(int oldFD, const char *oldPath, int newFD, const char *newPath)
5393 {
5394 	KPath oldPathBuffer(oldPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
5395 	KPath newPathBuffer(newPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
5396 	if (oldPathBuffer.InitCheck() != FSSH_B_OK || newPathBuffer.InitCheck() != FSSH_B_OK)
5397 		return FSSH_B_NO_MEMORY;
5398 
5399 	return common_rename(oldFD, oldPathBuffer.LockBuffer(),
5400 		newFD, newPathBuffer.LockBuffer(), true);
5401 }
5402 
5403 
5404 fssh_status_t
5405 _kern_access(const char *path, int mode)
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_access(pathBuffer.LockBuffer(), mode, true);
5412 }
5413 
5414 
5415 /**	\brief Reads stat data of an entity specified by a FD + path pair.
5416  *
5417  *	If only \a fd is given, the stat operation associated with the type
5418  *	of the FD (node, attr, attr dir etc.) is performed. If only \a path is
5419  *	given, this path identifies the entry for whose node to retrieve the
5420  *	stat data. If both \a fd and \a path are given and the path is absolute,
5421  *	\a fd is ignored; a relative path is reckoned off of the directory (!)
5422  *	identified by \a fd and specifies the entry whose stat data shall be
5423  *	retrieved.
5424  *
5425  *	\param fd The FD. May be < 0.
5426  *	\param path The absolute or relative path. Must not be \c NULL.
5427  *	\param traverseLeafLink If \a path is given, \c true specifies that the
5428  *		   function shall not stick to symlinks, but traverse them.
5429  *	\param stat The buffer the stat data shall be written into.
5430  *	\param statSize The size of the supplied stat buffer.
5431  *	\return \c FSSH_B_OK, if the the stat data have been read successfully, another
5432  *			error code otherwise.
5433  */
5434 
5435 fssh_status_t
5436 _kern_read_stat(int fd, const char *path, bool traverseLeafLink,
5437 	fssh_struct_stat *stat, fssh_size_t statSize)
5438 {
5439 	fssh_struct_stat completeStat;
5440 	fssh_struct_stat *originalStat = NULL;
5441 	fssh_status_t status;
5442 
5443 	if (statSize > sizeof(fssh_struct_stat))
5444 		return FSSH_B_BAD_VALUE;
5445 
5446 	// this supports different stat extensions
5447 	if (statSize < sizeof(fssh_struct_stat)) {
5448 		originalStat = stat;
5449 		stat = &completeStat;
5450 	}
5451 
5452 	if (path) {
5453 		// path given: get the stat of the node referred to by (fd, path)
5454 		KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5455 		if (pathBuffer.InitCheck() != FSSH_B_OK)
5456 			return FSSH_B_NO_MEMORY;
5457 
5458 		status = common_path_read_stat(fd, pathBuffer.LockBuffer(),
5459 			traverseLeafLink, stat, true);
5460 	} else {
5461 		// no path given: get the FD and use the FD operation
5462 		struct file_descriptor *descriptor
5463 			= get_fd(get_current_io_context(true), fd);
5464 		if (descriptor == NULL)
5465 			return FSSH_B_FILE_ERROR;
5466 
5467 		if (descriptor->ops->fd_read_stat)
5468 			status = descriptor->ops->fd_read_stat(descriptor, stat);
5469 		else
5470 			status = FSSH_EOPNOTSUPP;
5471 
5472 		put_fd(descriptor);
5473 	}
5474 
5475 	if (status == FSSH_B_OK && originalStat != NULL)
5476 		fssh_memcpy(originalStat, stat, statSize);
5477 
5478 	return status;
5479 }
5480 
5481 
5482 /**	\brief Writes stat data of an entity specified by a FD + path pair.
5483  *
5484  *	If only \a fd is given, the stat operation associated with the type
5485  *	of the FD (node, attr, attr dir etc.) is performed. If only \a path is
5486  *	given, this path identifies the entry for whose node to write the
5487  *	stat data. If both \a fd and \a path are given and the path is absolute,
5488  *	\a fd is ignored; a relative path is reckoned off of the directory (!)
5489  *	identified by \a fd and specifies the entry whose stat data shall be
5490  *	written.
5491  *
5492  *	\param fd The FD. May be < 0.
5493  *	\param path The absolute or relative path. Must not be \c NULL.
5494  *	\param traverseLeafLink If \a path is given, \c true specifies that the
5495  *		   function shall not stick to symlinks, but traverse them.
5496  *	\param stat The buffer containing the stat data to be written.
5497  *	\param statSize The size of the supplied stat buffer.
5498  *	\param statMask A mask specifying which parts of the stat data shall be
5499  *		   written.
5500  *	\return \c FSSH_B_OK, if the the stat data have been written successfully,
5501  *			another error code otherwise.
5502  */
5503 
5504 fssh_status_t
5505 _kern_write_stat(int fd, const char *path, bool traverseLeafLink,
5506 	const fssh_struct_stat *stat, fssh_size_t statSize, int statMask)
5507 {
5508 	fssh_struct_stat completeStat;
5509 
5510 	if (statSize > sizeof(fssh_struct_stat))
5511 		return FSSH_B_BAD_VALUE;
5512 
5513 	// this supports different stat extensions
5514 	if (statSize < sizeof(fssh_struct_stat)) {
5515 		fssh_memset((uint8_t *)&completeStat + statSize, 0, sizeof(fssh_struct_stat) - statSize);
5516 		fssh_memcpy(&completeStat, stat, statSize);
5517 		stat = &completeStat;
5518 	}
5519 
5520 	fssh_status_t status;
5521 
5522 	if (path) {
5523 		// path given: write the stat of the node referred to by (fd, path)
5524 		KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5525 		if (pathBuffer.InitCheck() != FSSH_B_OK)
5526 			return FSSH_B_NO_MEMORY;
5527 
5528 		status = common_path_write_stat(fd, pathBuffer.LockBuffer(),
5529 			traverseLeafLink, stat, statMask, true);
5530 	} else {
5531 		// no path given: get the FD and use the FD operation
5532 		struct file_descriptor *descriptor
5533 			= get_fd(get_current_io_context(true), fd);
5534 		if (descriptor == NULL)
5535 			return FSSH_B_FILE_ERROR;
5536 
5537 		if (descriptor->ops->fd_write_stat)
5538 			status = descriptor->ops->fd_write_stat(descriptor, stat, statMask);
5539 		else
5540 			status = FSSH_EOPNOTSUPP;
5541 
5542 		put_fd(descriptor);
5543 	}
5544 
5545 	return status;
5546 }
5547 
5548 
5549 int
5550 _kern_open_attr_dir(int fd, const char *path)
5551 {
5552 	KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
5553 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5554 		return FSSH_B_NO_MEMORY;
5555 
5556 	if (path != NULL)
5557 		pathBuffer.SetTo(path);
5558 
5559 	return attr_dir_open(fd, path ? pathBuffer.LockBuffer() : NULL, true);
5560 }
5561 
5562 
5563 int
5564 _kern_create_attr(int fd, const char *name, uint32_t type, int openMode)
5565 {
5566 	return attr_create(fd, name, type, openMode, true);
5567 }
5568 
5569 
5570 int
5571 _kern_open_attr(int fd, const char *name, int openMode)
5572 {
5573 	return attr_open(fd, name, openMode, true);
5574 }
5575 
5576 
5577 fssh_status_t
5578 _kern_remove_attr(int fd, const char *name)
5579 {
5580 	return attr_remove(fd, name, true);
5581 }
5582 
5583 
5584 fssh_status_t
5585 _kern_rename_attr(int fromFile, const char *fromName, int toFile, const char *toName)
5586 {
5587 	return attr_rename(fromFile, fromName, toFile, toName, true);
5588 }
5589 
5590 
5591 int
5592 _kern_open_index_dir(fssh_dev_t device)
5593 {
5594 	return index_dir_open(device, true);
5595 }
5596 
5597 
5598 fssh_status_t
5599 _kern_create_index(fssh_dev_t device, const char *name, uint32_t type, uint32_t flags)
5600 {
5601 	return index_create(device, name, type, flags, true);
5602 }
5603 
5604 
5605 fssh_status_t
5606 _kern_read_index_stat(fssh_dev_t device, const char *name, fssh_struct_stat *stat)
5607 {
5608 	return index_name_read_stat(device, name, stat, true);
5609 }
5610 
5611 
5612 fssh_status_t
5613 _kern_remove_index(fssh_dev_t device, const char *name)
5614 {
5615 	return index_remove(device, name, true);
5616 }
5617 
5618 
5619 fssh_status_t
5620 _kern_getcwd(char *buffer, fssh_size_t size)
5621 {
5622 	TRACE(("_kern_getcwd: buf %p, %ld\n", buffer, size));
5623 
5624 	// Call vfs to get current working directory
5625 	return get_cwd(buffer, size, true);
5626 }
5627 
5628 
5629 fssh_status_t
5630 _kern_setcwd(int fd, const char *path)
5631 {
5632 	KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
5633 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5634 		return FSSH_B_NO_MEMORY;
5635 
5636 	if (path != NULL)
5637 		pathBuffer.SetTo(path);
5638 
5639 	return set_cwd(fd, path != NULL ? pathBuffer.LockBuffer() : NULL, true);
5640 }
5641 
5642 
5643 fssh_status_t
5644 _kern_initialize_volume(const char* fsName, const char *partition,
5645 	const char *name, const char *parameters)
5646 {
5647 	if (!fsName || ! partition)
5648 		return FSSH_B_BAD_VALUE;
5649 
5650 	// The partition argument should point to a real file/device.
5651 
5652 	// open partition
5653 	int fd = fssh_open(partition, FSSH_O_RDWR);
5654 	if (fd < 0)
5655 		return fssh_errno;
5656 
5657 	// get the file system module
5658 	fssh_file_system_module_info* fsModule = get_file_system(fsName);
5659 	if (fsModule == NULL) {
5660 		fssh_close(fd);
5661 		return FSSH_ENODEV;
5662 	}
5663 
5664 	// initialize
5665 	fssh_status_t status;
5666 	if (fsModule->initialize) {
5667 		status = (*fsModule->initialize)(fd, -1, name, parameters, 0, -1);
5668 			// We've got no partition or job IDs -- the FS will hopefully
5669 			// ignore that.
5670 			// TODO: Get the actual size!
5671 	} else
5672 		status = FSSH_B_NOT_SUPPORTED;
5673 
5674 	// put the file system module, close partition
5675 	put_file_system(fsModule);
5676 	fssh_close(fd);
5677 
5678 	return status;
5679 }
5680 
5681 
5682 fssh_status_t
5683 _kern_entry_ref_to_path(fssh_dev_t device, fssh_ino_t inode, const char *leaf,
5684 	char* path, fssh_size_t pathLength)
5685 {
5686 	return vfs_entry_ref_to_path(device, inode, leaf, true, path, pathLength);
5687 }
5688 
5689 
5690 int
5691 _kern_open_query(fssh_dev_t device, const char *query, fssh_size_t queryLength,
5692 	uint32_t flags, fssh_port_id port, int32_t token)
5693 {
5694 	return query_open(device, query, flags, port, token, false);
5695 }
5696 
5697 
5698 }	// namespace FSShell
5699 
5700 
5701 #include "vfs_request_io.cpp"
5702