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