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