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