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