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