xref: /haiku/src/tools/fs_shell/vfs.cpp (revision 52f7c9389475e19fc21487b38064b4390eeb6fea)
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_fs_volume*
2121 fssh_volume_for_vnode(fssh_fs_vnode *_vnode)
2122 {
2123 	if (_vnode == NULL)
2124 		return NULL;
2125 
2126 	struct vnode* vnode = static_cast<struct vnode*>(_vnode);
2127 	return vnode->mount->volume;
2128 }
2129 
2130 
2131 extern "C" fssh_status_t
2132 fssh_check_access_permissions(int accessMode, fssh_mode_t mode,
2133 	fssh_gid_t nodeGroupID, fssh_uid_t nodeUserID)
2134 {
2135 	// get node permissions
2136 	int userPermissions = (mode & FSSH_S_IRWXU) >> 6;
2137 	int groupPermissions = (mode & FSSH_S_IRWXG) >> 3;
2138 	int otherPermissions = mode & FSSH_S_IRWXO;
2139 
2140 	// get the node permissions for this uid/gid
2141 	int permissions = 0;
2142 	fssh_uid_t uid = fssh_geteuid();
2143 
2144 	if (uid == 0) {
2145 		// user is root
2146 		// root has always read/write permission, but at least one of the
2147 		// X bits must be set for execute permission
2148 		permissions = userPermissions | groupPermissions | otherPermissions
2149 			| FSSH_S_IROTH | FSSH_S_IWOTH;
2150 		if (FSSH_S_ISDIR(mode))
2151 			permissions |= FSSH_S_IXOTH;
2152 	} else if (uid == nodeUserID) {
2153 		// user is node owner
2154 		permissions = userPermissions;
2155 	} else if (fssh_getegid() == nodeGroupID) {
2156 		// user is in owning group
2157 		permissions = groupPermissions;
2158 	} else {
2159 		// user is one of the others
2160 		permissions = otherPermissions;
2161 	}
2162 
2163 	return (accessMode & ~permissions) == 0 ? FSSH_B_OK : FSSH_B_NOT_ALLOWED;
2164 }
2165 
2166 
2167 //! Works directly on the host's file system
2168 extern "C" fssh_status_t
2169 fssh_read_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs,
2170 	fssh_size_t count, fssh_size_t *_numBytes)
2171 {
2172 	// check how much the iovecs allow us to read
2173 	fssh_size_t toRead = 0;
2174 	for (fssh_size_t i = 0; i < count; i++)
2175 		toRead += vecs[i].iov_len;
2176 
2177 	fssh_iovec* newVecs = NULL;
2178 	if (*_numBytes < toRead) {
2179 		// We're supposed to read less than specified by the vecs. Since
2180 		// readv_pos() doesn't support this, we need to clone the vecs.
2181 		newVecs = new(std::nothrow) fssh_iovec[count];
2182 		if (!newVecs)
2183 			return FSSH_B_NO_MEMORY;
2184 
2185 		fssh_size_t newCount = 0;
2186 		for (fssh_size_t i = 0; i < count && toRead > 0; i++) {
2187 			fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toRead);
2188 			newVecs[i].iov_base = vecs[i].iov_base;
2189 			newVecs[i].iov_len = vecLen;
2190 			toRead -= vecLen;
2191 			newCount++;
2192 		}
2193 
2194 		vecs = newVecs;
2195 		count = newCount;
2196 	}
2197 
2198 	fssh_ssize_t bytesRead = fssh_readv_pos(fd, pos, vecs, count);
2199 	delete[] newVecs;
2200 	if (bytesRead < 0)
2201 		return fssh_get_errno();
2202 
2203 	*_numBytes = bytesRead;
2204 	return FSSH_B_OK;
2205 }
2206 
2207 
2208 //! Works directly on the host's file system
2209 extern "C" fssh_status_t
2210 fssh_write_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs,
2211 	fssh_size_t count, fssh_size_t *_numBytes)
2212 {
2213 	// check how much the iovecs allow us to write
2214 	fssh_size_t toWrite = 0;
2215 	for (fssh_size_t i = 0; i < count; i++)
2216 		toWrite += vecs[i].iov_len;
2217 
2218 	fssh_iovec* newVecs = NULL;
2219 	if (*_numBytes < toWrite) {
2220 		// We're supposed to write less than specified by the vecs. Since
2221 		// writev_pos() doesn't support this, we need to clone the vecs.
2222 		newVecs = new(std::nothrow) fssh_iovec[count];
2223 		if (!newVecs)
2224 			return FSSH_B_NO_MEMORY;
2225 
2226 		fssh_size_t newCount = 0;
2227 		for (fssh_size_t i = 0; i < count && toWrite > 0; i++) {
2228 			fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toWrite);
2229 			newVecs[i].iov_base = vecs[i].iov_base;
2230 			newVecs[i].iov_len = vecLen;
2231 			toWrite -= vecLen;
2232 			newCount++;
2233 		}
2234 
2235 		vecs = newVecs;
2236 		count = newCount;
2237 	}
2238 
2239 	fssh_ssize_t bytesWritten = fssh_writev_pos(fd, pos, vecs, count);
2240 	delete[] newVecs;
2241 	if (bytesWritten < 0)
2242 		return fssh_get_errno();
2243 
2244 	*_numBytes = bytesWritten;
2245 	return FSSH_B_OK;
2246 }
2247 
2248 
2249 //! Works directly on the host's file system
2250 extern "C" fssh_status_t
2251 fssh_read_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs,
2252 	fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount,
2253 	uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_bytes)
2254 {
2255 	return common_file_io_vec_pages(fd, fileVecs, fileVecCount,
2256 		vecs, vecCount, _vecIndex, _vecOffset, _bytes, false);
2257 }
2258 
2259 
2260 //! Works directly on the host's file system
2261 extern "C" fssh_status_t
2262 fssh_write_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs,
2263 	fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount,
2264 	uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_bytes)
2265 {
2266 	return common_file_io_vec_pages(fd, fileVecs, fileVecCount,
2267 		vecs, vecCount, _vecIndex, _vecOffset, _bytes, true);
2268 }
2269 
2270 
2271 extern "C" fssh_status_t
2272 fssh_entry_cache_add(fssh_dev_t mountID, fssh_ino_t dirID, const char* name,
2273 	fssh_ino_t nodeID)
2274 {
2275 	// We don't implement an entry cache in the FS shell.
2276 	return FSSH_B_OK;
2277 }
2278 
2279 
2280 extern "C" fssh_status_t
2281 fssh_entry_cache_add_missing(fssh_dev_t mountID, fssh_ino_t dirID,
2282 	const char* name)
2283 {
2284 	// We don't implement an entry cache in the FS shell.
2285 	return FSSH_B_OK;
2286 }
2287 
2288 
2289 extern "C" fssh_status_t
2290 fssh_entry_cache_remove(fssh_dev_t mountID, fssh_ino_t dirID, const char* name)
2291 {
2292 	// We don't implement an entry cache in the FS shell.
2293 	return FSSH_B_ENTRY_NOT_FOUND;
2294 }
2295 
2296 
2297 //	#pragma mark - private VFS API
2298 //	Functions the VFS exports for other parts of the kernel
2299 
2300 
2301 /** Acquires another reference to the vnode that has to be released
2302  *	by calling vfs_put_vnode().
2303  */
2304 
2305 void
2306 vfs_acquire_vnode(void *_vnode)
2307 {
2308 	inc_vnode_ref_count((struct vnode *)_vnode);
2309 }
2310 
2311 
2312 /** This is currently called from file_cache_create() only.
2313  *	It's probably a temporary solution as long as devfs requires that
2314  *	fs_read_pages()/fs_write_pages() are called with the standard
2315  *	open cookie and not with a device cookie.
2316  *	If that's done differently, remove this call; it has no other
2317  *	purpose.
2318  */
2319 
2320 fssh_status_t
2321 vfs_get_cookie_from_fd(int fd, void **_cookie)
2322 {
2323 	struct file_descriptor *descriptor;
2324 
2325 	descriptor = get_fd(get_current_io_context(true), fd);
2326 	if (descriptor == NULL)
2327 		return FSSH_B_FILE_ERROR;
2328 
2329 	*_cookie = descriptor->cookie;
2330 	return FSSH_B_OK;
2331 }
2332 
2333 
2334 int
2335 vfs_get_vnode_from_fd(int fd, bool kernel, void **vnode)
2336 {
2337 	*vnode = get_vnode_from_fd(fd, kernel);
2338 
2339 	if (*vnode == NULL)
2340 		return FSSH_B_FILE_ERROR;
2341 
2342 	return FSSH_B_NO_ERROR;
2343 }
2344 
2345 
2346 fssh_status_t
2347 vfs_get_vnode_from_path(const char *path, bool kernel, void **_vnode)
2348 {
2349 	TRACE(("vfs_get_vnode_from_path: entry. path = '%s', kernel %d\n", path, kernel));
2350 
2351 	KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
2352 	if (pathBuffer.InitCheck() != FSSH_B_OK)
2353 		return FSSH_B_NO_MEMORY;
2354 
2355 	char *buffer = pathBuffer.LockBuffer();
2356 	fssh_strlcpy(buffer, path, pathBuffer.BufferSize());
2357 
2358 	struct vnode *vnode;
2359 	fssh_status_t status = path_to_vnode(buffer, true, &vnode, NULL, kernel);
2360 	if (status < FSSH_B_OK)
2361 		return status;
2362 
2363 	*_vnode = vnode;
2364 	return FSSH_B_OK;
2365 }
2366 
2367 
2368 fssh_status_t
2369 vfs_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void **_vnode)
2370 {
2371 	struct vnode *vnode;
2372 
2373 	fssh_status_t status = get_vnode(mountID, vnodeID, &vnode, false);
2374 	if (status < FSSH_B_OK)
2375 		return status;
2376 
2377 	*_vnode = vnode;
2378 	return FSSH_B_OK;
2379 }
2380 
2381 
2382 fssh_status_t
2383 vfs_read_pages(void *_vnode, void *cookie, fssh_off_t pos,
2384 	const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes)
2385 {
2386 	struct vnode *vnode = (struct vnode *)_vnode;
2387 
2388 	return FS_CALL(vnode, read_pages,
2389 		cookie, pos, vecs, count, _numBytes);
2390 }
2391 
2392 
2393 fssh_status_t
2394 vfs_write_pages(void *_vnode, void *cookie, fssh_off_t pos,
2395 	const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes)
2396 {
2397 	struct vnode *vnode = (struct vnode *)_vnode;
2398 
2399 	return FS_CALL(vnode, write_pages,
2400 		cookie, pos, vecs, count, _numBytes);
2401 }
2402 
2403 
2404 fssh_status_t
2405 vfs_entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID,
2406 	const char *name, void **_vnode)
2407 {
2408 	return entry_ref_to_vnode(mountID, directoryID, name,
2409 		(struct vnode **)_vnode);
2410 }
2411 
2412 
2413 void
2414 vfs_fs_vnode_to_node_ref(void *_vnode, fssh_mount_id *_mountID,
2415 	fssh_vnode_id *_vnodeID)
2416 {
2417 	struct vnode *vnode = (struct vnode *)_vnode;
2418 
2419 	*_mountID = vnode->device;
2420 	*_vnodeID = vnode->id;
2421 }
2422 
2423 
2424 /**	Looks up a vnode with the given mount and vnode ID.
2425  *	Must only be used with "in-use" vnodes as it doesn't grab a reference
2426  *	to the node.
2427  *	It's currently only be used by file_cache_create().
2428  */
2429 
2430 fssh_status_t
2431 vfs_lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID,
2432 	struct vnode **_vnode)
2433 {
2434 	fssh_mutex_lock(&sVnodeMutex);
2435 	struct vnode *vnode = lookup_vnode(mountID, vnodeID);
2436 	fssh_mutex_unlock(&sVnodeMutex);
2437 
2438 	if (vnode == NULL)
2439 		return FSSH_B_ERROR;
2440 
2441 	*_vnode = vnode;
2442 	return FSSH_B_OK;
2443 }
2444 
2445 
2446 fssh_status_t
2447 vfs_get_fs_node_from_path(fssh_fs_volume *volume, const char *path,
2448 	bool kernel, void **_node)
2449 {
2450 	TRACE(("vfs_get_fs_node_from_path(volume = %p (%ld), path = \"%s\", "
2451 		"kernel %d)\n", volume, volume->id, path, kernel));
2452 
2453 	KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
2454 	if (pathBuffer.InitCheck() != FSSH_B_OK)
2455 		return FSSH_B_NO_MEMORY;
2456 
2457 	fs_mount *mount;
2458 	fssh_status_t status = get_mount(volume->id, &mount);
2459 	if (status < FSSH_B_OK)
2460 		return status;
2461 
2462 	char *buffer = pathBuffer.LockBuffer();
2463 	fssh_strlcpy(buffer, path, pathBuffer.BufferSize());
2464 
2465 	struct vnode *vnode = mount->root_vnode;
2466 
2467 	if (buffer[0] == '/')
2468 		status = path_to_vnode(buffer, true, &vnode, NULL, true);
2469 	else {
2470 		inc_vnode_ref_count(vnode);
2471 			// vnode_path_to_vnode() releases a reference to the starting vnode
2472 		status = vnode_path_to_vnode(vnode, buffer, true, 0, &vnode, NULL);
2473 	}
2474 
2475 	put_mount(mount);
2476 
2477 	if (status < FSSH_B_OK)
2478 		return status;
2479 
2480 	if (vnode->device != volume->id) {
2481 		// wrong mount ID - must not gain access on foreign file system nodes
2482 		put_vnode(vnode);
2483 		return FSSH_B_BAD_VALUE;
2484 	}
2485 
2486 	// Use get_vnode() to resolve the cookie for the right layer.
2487 	status = ::fssh_get_vnode(volume, vnode->id, _node);
2488 	put_vnode(vnode);
2489 
2490 	return FSSH_B_OK;
2491 }
2492 
2493 
2494 /**	Finds the full path to the file that contains the module \a moduleName,
2495  *	puts it into \a pathBuffer, and returns FSSH_B_OK for success.
2496  *	If \a pathBuffer was too small, it returns \c FSSH_B_BUFFER_OVERFLOW,
2497  *	\c FSSH_B_ENTRY_NOT_FOUNT if no file could be found.
2498  *	\a pathBuffer is clobbered in any case and must not be relied on if this
2499  *	functions returns unsuccessfully.
2500  */
2501 
2502 fssh_status_t
2503 vfs_get_module_path(const char *basePath, const char *moduleName, char *pathBuffer,
2504 	fssh_size_t bufferSize)
2505 {
2506 	struct vnode *dir, *file;
2507 	fssh_status_t status;
2508 	fssh_size_t length;
2509 	char *path;
2510 
2511 	if (bufferSize == 0 || fssh_strlcpy(pathBuffer, basePath, bufferSize) >= bufferSize)
2512 		return FSSH_B_BUFFER_OVERFLOW;
2513 
2514 	status = path_to_vnode(pathBuffer, true, &dir, NULL, true);
2515 	if (status < FSSH_B_OK)
2516 		return status;
2517 
2518 	// the path buffer had been clobbered by the above call
2519 	length = fssh_strlcpy(pathBuffer, basePath, bufferSize);
2520 	if (pathBuffer[length - 1] != '/')
2521 		pathBuffer[length++] = '/';
2522 
2523 	path = pathBuffer + length;
2524 	bufferSize -= length;
2525 
2526 	while (moduleName) {
2527 		char *nextPath = fssh_strchr(moduleName, '/');
2528 		if (nextPath == NULL)
2529 			length = fssh_strlen(moduleName);
2530 		else {
2531 			length = nextPath - moduleName;
2532 			nextPath++;
2533 		}
2534 
2535 		if (length + 1 >= bufferSize) {
2536 			status = FSSH_B_BUFFER_OVERFLOW;
2537 			goto err;
2538 		}
2539 
2540 		fssh_memcpy(path, moduleName, length);
2541 		path[length] = '\0';
2542 		moduleName = nextPath;
2543 
2544 		status = vnode_path_to_vnode(dir, path, true, 0, &file, NULL);
2545 		if (status < FSSH_B_OK) {
2546 			// vnode_path_to_vnode() has already released the reference to dir
2547 			return status;
2548 		}
2549 
2550 		if (FSSH_S_ISDIR(file->type)) {
2551 			// goto the next directory
2552 			path[length] = '/';
2553 			path[length + 1] = '\0';
2554 			path += length + 1;
2555 			bufferSize -= length + 1;
2556 
2557 			dir = file;
2558 		} else if (FSSH_S_ISREG(file->type)) {
2559 			// it's a file so it should be what we've searched for
2560 			put_vnode(file);
2561 
2562 			return FSSH_B_OK;
2563 		} else {
2564 			TRACE(("vfs_get_module_path(): something is strange here: %d...\n", file->type));
2565 			status = FSSH_B_ERROR;
2566 			dir = file;
2567 			goto err;
2568 		}
2569 	}
2570 
2571 	// if we got here, the moduleName just pointed to a directory, not to
2572 	// a real module - what should we do in this case?
2573 	status = FSSH_B_ENTRY_NOT_FOUND;
2574 
2575 err:
2576 	put_vnode(dir);
2577 	return status;
2578 }
2579 
2580 
2581 /**	\brief Normalizes a given path.
2582  *
2583  *	The path must refer to an existing or non-existing entry in an existing
2584  *	directory, that is chopping off the leaf component the remaining path must
2585  *	refer to an existing directory.
2586  *
2587  *	The returned will be canonical in that it will be absolute, will not
2588  *	contain any "." or ".." components or duplicate occurrences of '/'s,
2589  *	and none of the directory components will by symbolic links.
2590  *
2591  *	Any two paths referring to the same entry, will result in the same
2592  *	normalized path (well, that is pretty much the definition of `normalized',
2593  *	isn't it :-).
2594  *
2595  *	\param path The path to be normalized.
2596  *	\param buffer The buffer into which the normalized path will be written.
2597  *	\param bufferSize The size of \a buffer.
2598  *	\param kernel \c true, if the IO context of the kernel shall be used,
2599  *		   otherwise that of the team this thread belongs to. Only relevant,
2600  *		   if the path is relative (to get the CWD).
2601  *	\return \c FSSH_B_OK if everything went fine, another error code otherwise.
2602  */
2603 
2604 fssh_status_t
2605 vfs_normalize_path(const char *path, char *buffer, fssh_size_t bufferSize,
2606 	bool kernel)
2607 {
2608 	if (!path || !buffer || bufferSize < 1)
2609 		return FSSH_B_BAD_VALUE;
2610 
2611 	TRACE(("vfs_normalize_path(`%s')\n", path));
2612 
2613 	// copy the supplied path to the stack, so it can be modified
2614 	KPath mutablePathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
2615 	if (mutablePathBuffer.InitCheck() != FSSH_B_OK)
2616 		return FSSH_B_NO_MEMORY;
2617 
2618 	char *mutablePath = mutablePathBuffer.LockBuffer();
2619 	if (fssh_strlcpy(mutablePath, path, FSSH_B_PATH_NAME_LENGTH) >= FSSH_B_PATH_NAME_LENGTH)
2620 		return FSSH_B_NAME_TOO_LONG;
2621 
2622 	// get the dir vnode and the leaf name
2623 	struct vnode *dirNode;
2624 	char leaf[FSSH_B_FILE_NAME_LENGTH];
2625 	fssh_status_t error = path_to_dir_vnode(mutablePath, &dirNode, leaf, kernel);
2626 	if (error != FSSH_B_OK) {
2627 		TRACE(("vfs_normalize_path(): failed to get dir vnode: %s\n", strerror(error)));
2628 		return error;
2629 	}
2630 
2631 	// if the leaf is "." or "..", we directly get the correct directory
2632 	// vnode and ignore the leaf later
2633 	bool isDir = (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0);
2634 	if (isDir)
2635 		error = vnode_path_to_vnode(dirNode, leaf, false, 0, &dirNode, NULL);
2636 	if (error != FSSH_B_OK) {
2637 		TRACE(("vfs_normalize_path(): failed to get dir vnode for \".\" or \"..\": %s\n",
2638 			strerror(error)));
2639 		return error;
2640 	}
2641 
2642 	// get the directory path
2643 	error = dir_vnode_to_path(dirNode, buffer, bufferSize);
2644 	put_vnode(dirNode);
2645 	if (error < FSSH_B_OK) {
2646 		TRACE(("vfs_normalize_path(): failed to get dir path: %s\n", strerror(error)));
2647 		return error;
2648 	}
2649 
2650 	// append the leaf name
2651 	if (!isDir) {
2652 		// insert a directory separator only if this is not the file system root
2653 		if ((fssh_strcmp(buffer, "/") != 0
2654 			 && fssh_strlcat(buffer, "/", bufferSize) >= bufferSize)
2655 			|| fssh_strlcat(buffer, leaf, bufferSize) >= bufferSize) {
2656 			return FSSH_B_NAME_TOO_LONG;
2657 		}
2658 	}
2659 
2660 	TRACE(("vfs_normalize_path() -> `%s'\n", buffer));
2661 	return FSSH_B_OK;
2662 }
2663 
2664 
2665 void
2666 vfs_put_vnode(void *_vnode)
2667 {
2668 	put_vnode((struct vnode *)_vnode);
2669 }
2670 
2671 
2672 fssh_status_t
2673 vfs_get_cwd(fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID)
2674 {
2675 	// Get current working directory from io context
2676 	struct io_context *context = get_current_io_context(false);
2677 	fssh_status_t status = FSSH_B_OK;
2678 
2679 	fssh_mutex_lock(&context->io_mutex);
2680 
2681 	if (context->cwd != NULL) {
2682 		*_mountID = context->cwd->device;
2683 		*_vnodeID = context->cwd->id;
2684 	} else
2685 		status = FSSH_B_ERROR;
2686 
2687 	fssh_mutex_unlock(&context->io_mutex);
2688 	return status;
2689 }
2690 
2691 
2692 fssh_status_t
2693 vfs_get_file_map(void *_vnode, fssh_off_t offset, fssh_size_t size,
2694 	fssh_file_io_vec *vecs, fssh_size_t *_count)
2695 {
2696 	struct vnode *vnode = (struct vnode *)_vnode;
2697 
2698 	FUNCTION(("vfs_get_file_map: vnode %p, vecs %p, offset %lld, size = %u\n", vnode, vecs, offset, (unsigned)size));
2699 
2700 	return FS_CALL(vnode, get_file_map, offset, size, vecs, _count);
2701 }
2702 
2703 
2704 fssh_status_t
2705 vfs_stat_vnode(void *_vnode, struct fssh_stat *stat)
2706 {
2707 	struct vnode *vnode = (struct vnode *)_vnode;
2708 
2709 	fssh_status_t status = FS_CALL(vnode, read_stat, stat);
2710 
2711 	// fill in the st_dev and st_ino fields
2712 	if (status == FSSH_B_OK) {
2713 		stat->fssh_st_dev = vnode->device;
2714 		stat->fssh_st_ino = vnode->id;
2715 	}
2716 
2717 	return status;
2718 }
2719 
2720 
2721 fssh_status_t
2722 vfs_get_vnode_name(void *_vnode, char *name, fssh_size_t nameSize)
2723 {
2724 	return get_vnode_name((struct vnode *)_vnode, NULL, name, nameSize);
2725 }
2726 
2727 
2728 fssh_status_t
2729 vfs_entry_ref_to_path(fssh_dev_t device, fssh_ino_t inode, const char *leaf,
2730 	bool kernel, char *path, fssh_size_t pathLength)
2731 {
2732 	struct vnode *vnode;
2733 	fssh_status_t status;
2734 
2735 	// filter invalid leaf names
2736 	if (leaf != NULL && (leaf[0] == '\0' || fssh_strchr(leaf, '/')))
2737 		return FSSH_B_BAD_VALUE;
2738 
2739 	// get the vnode matching the dir's node_ref
2740 	if (leaf && (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0)) {
2741 		// special cases "." and "..": we can directly get the vnode of the
2742 		// referenced directory
2743 		status = entry_ref_to_vnode(device, inode, leaf, &vnode);
2744 		leaf = NULL;
2745 	} else
2746 		status = get_vnode(device, inode, &vnode, false);
2747 	if (status < FSSH_B_OK)
2748 		return status;
2749 
2750 	// get the directory path
2751 	status = dir_vnode_to_path(vnode, path, pathLength);
2752 	put_vnode(vnode);
2753 		// we don't need the vnode anymore
2754 	if (status < FSSH_B_OK)
2755 		return status;
2756 
2757 	// append the leaf name
2758 	if (leaf) {
2759 		// insert a directory separator if this is not the file system root
2760 		if ((fssh_strcmp(path, "/") && fssh_strlcat(path, "/", pathLength)
2761 				>= pathLength)
2762 			|| fssh_strlcat(path, leaf, pathLength) >= pathLength) {
2763 			return FSSH_B_NAME_TOO_LONG;
2764 		}
2765 	}
2766 
2767 	return FSSH_B_OK;
2768 }
2769 
2770 
2771 /**	If the given descriptor locked its vnode, that lock will be released.
2772  */
2773 
2774 void
2775 vfs_unlock_vnode_if_locked(struct file_descriptor *descriptor)
2776 {
2777 	struct vnode *vnode = fd_vnode(descriptor);
2778 
2779 	if (vnode != NULL && vnode->mandatory_locked_by == descriptor)
2780 		vnode->mandatory_locked_by = NULL;
2781 }
2782 
2783 
2784 /**	Closes all file descriptors of the specified I/O context that
2785  *	don't have the FSSH_O_CLOEXEC flag set.
2786  */
2787 
2788 void
2789 vfs_exec_io_context(void *_context)
2790 {
2791 	struct io_context *context = (struct io_context *)_context;
2792 	uint32_t i;
2793 
2794 	for (i = 0; i < context->table_size; i++) {
2795 		fssh_mutex_lock(&context->io_mutex);
2796 
2797 		struct file_descriptor *descriptor = context->fds[i];
2798 		bool remove = false;
2799 
2800 		if (descriptor != NULL && fd_close_on_exec(context, i)) {
2801 			context->fds[i] = NULL;
2802 			context->num_used_fds--;
2803 
2804 			remove = true;
2805 		}
2806 
2807 		fssh_mutex_unlock(&context->io_mutex);
2808 
2809 		if (remove) {
2810 			close_fd(descriptor);
2811 			put_fd(descriptor);
2812 		}
2813 	}
2814 }
2815 
2816 
2817 /** Sets up a new io_control structure, and inherits the properties
2818  *	of the parent io_control if it is given.
2819  */
2820 
2821 void *
2822 vfs_new_io_context(void *_parentContext)
2823 {
2824 	fssh_size_t tableSize;
2825 	struct io_context *context;
2826 	struct io_context *parentContext;
2827 
2828 	context = (io_context *)malloc(sizeof(struct io_context));
2829 	if (context == NULL)
2830 		return NULL;
2831 
2832 	fssh_memset(context, 0, sizeof(struct io_context));
2833 
2834 	parentContext = (struct io_context *)_parentContext;
2835 	if (parentContext)
2836 		tableSize = parentContext->table_size;
2837 	else
2838 		tableSize = DEFAULT_FD_TABLE_SIZE;
2839 
2840 	// allocate space for FDs and their close-on-exec flag
2841 	context->fds = (file_descriptor **)malloc(sizeof(struct file_descriptor *) * tableSize
2842 		+ (tableSize + 7) / 8);
2843 	if (context->fds == NULL) {
2844 		free(context);
2845 		return NULL;
2846 	}
2847 
2848 	fssh_memset(context->fds, 0, sizeof(struct file_descriptor *) * tableSize
2849 		+ (tableSize + 7) / 8);
2850 	context->fds_close_on_exec = (uint8_t *)(context->fds + tableSize);
2851 
2852 	fssh_mutex_init(&context->io_mutex, "I/O context");
2853 
2854 	// Copy all parent files which don't have the FSSH_O_CLOEXEC flag set
2855 
2856 	if (parentContext) {
2857 		fssh_size_t i;
2858 
2859 		fssh_mutex_lock(&parentContext->io_mutex);
2860 
2861 		context->cwd = parentContext->cwd;
2862 		if (context->cwd)
2863 			inc_vnode_ref_count(context->cwd);
2864 
2865 		for (i = 0; i < tableSize; i++) {
2866 			struct file_descriptor *descriptor = parentContext->fds[i];
2867 
2868 			if (descriptor != NULL && !fd_close_on_exec(parentContext, i)) {
2869 				context->fds[i] = descriptor;
2870 				context->num_used_fds++;
2871 				fssh_atomic_add(&descriptor->ref_count, 1);
2872 				fssh_atomic_add(&descriptor->open_count, 1);
2873 			}
2874 		}
2875 
2876 		fssh_mutex_unlock(&parentContext->io_mutex);
2877 	} else {
2878 		context->cwd = sRoot;
2879 
2880 		if (context->cwd)
2881 			inc_vnode_ref_count(context->cwd);
2882 	}
2883 
2884 	context->table_size = tableSize;
2885 
2886 	return context;
2887 }
2888 
2889 
2890 fssh_status_t
2891 vfs_free_io_context(void *_ioContext)
2892 {
2893 	struct io_context *context = (struct io_context *)_ioContext;
2894 	uint32_t i;
2895 
2896 	if (context->cwd)
2897 		dec_vnode_ref_count(context->cwd, false);
2898 
2899 	fssh_mutex_lock(&context->io_mutex);
2900 
2901 	for (i = 0; i < context->table_size; i++) {
2902 		if (struct file_descriptor *descriptor = context->fds[i]) {
2903 			close_fd(descriptor);
2904 			put_fd(descriptor);
2905 		}
2906 	}
2907 
2908 	fssh_mutex_destroy(&context->io_mutex);
2909 
2910 	free(context->fds);
2911 	free(context);
2912 
2913 	return FSSH_B_OK;
2914 }
2915 
2916 
2917 fssh_status_t
2918 vfs_init(kernel_args *args)
2919 {
2920 	sVnodeTable = hash_init(VNODE_HASH_TABLE_SIZE, fssh_offsetof(struct vnode, next),
2921 		&vnode_compare, &vnode_hash);
2922 	if (sVnodeTable == NULL)
2923 		fssh_panic("vfs_init: error creating vnode hash table\n");
2924 
2925 	list_init_etc(&sUnusedVnodeList, fssh_offsetof(struct vnode, unused_link));
2926 
2927 	sMountsTable = hash_init(MOUNTS_HASH_TABLE_SIZE, fssh_offsetof(struct fs_mount, next),
2928 		&mount_compare, &mount_hash);
2929 	if (sMountsTable == NULL)
2930 		fssh_panic("vfs_init: error creating mounts hash table\n");
2931 
2932 	sRoot = NULL;
2933 
2934 	fssh_mutex_init(&sFileSystemsMutex, "vfs_lock");
2935 	fssh_recursive_lock_init(&sMountOpLock, "vfs_mount_op_lock");
2936 	fssh_mutex_init(&sMountMutex, "vfs_mount_lock");
2937 	fssh_mutex_init(&sVnodeCoveredByMutex, "vfs_vnode_covered_by_lock");
2938 	fssh_mutex_init(&sVnodeMutex, "vfs_vnode_lock");
2939 
2940 	if (block_cache_init() != FSSH_B_OK)
2941 		return FSSH_B_ERROR;
2942 
2943 	return file_cache_init();
2944 }
2945 
2946 
2947 //	#pragma mark -
2948 //	The filetype-dependent implementations (fd_ops + open/create/rename/remove, ...)
2949 
2950 
2951 /** Calls fs_open() on the given vnode and returns a new
2952  *	file descriptor for it
2953  */
2954 
2955 static int
2956 create_vnode(struct vnode *directory, const char *name, int openMode, int perms, bool kernel)
2957 {
2958 	struct vnode *vnode;
2959 	void *cookie;
2960 	fssh_vnode_id newID;
2961 	int status;
2962 
2963 	if (!HAS_FS_CALL(directory, create))
2964 		return FSSH_EROFS;
2965 
2966 	status = FS_CALL(directory, create, name, openMode, perms, &cookie, &newID);
2967 	if (status < FSSH_B_OK)
2968 		return status;
2969 
2970 	fssh_mutex_lock(&sVnodeMutex);
2971 	vnode = lookup_vnode(directory->device, newID);
2972 	fssh_mutex_unlock(&sVnodeMutex);
2973 
2974 	if (vnode == NULL) {
2975 		fssh_dprintf("vfs: fs_create() returned success but there is no vnode!");
2976 		return FSSH_EINVAL;
2977 	}
2978 
2979 	if ((status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel)) >= 0)
2980 		return status;
2981 
2982 	// something went wrong, clean up
2983 
2984 	FS_CALL(vnode, close, cookie);
2985 	FS_CALL(vnode, free_cookie, cookie);
2986 	put_vnode(vnode);
2987 
2988 	FS_CALL(directory, unlink, name);
2989 
2990 	return status;
2991 }
2992 
2993 
2994 /** Calls fs_open() on the given vnode and returns a new
2995  *	file descriptor for it
2996  */
2997 
2998 static int
2999 open_vnode(struct vnode *vnode, int openMode, bool kernel)
3000 {
3001 	void *cookie;
3002 	int status;
3003 
3004 	status = FS_CALL(vnode, open, openMode, &cookie);
3005 	if (status < 0)
3006 		return status;
3007 
3008 	status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel);
3009 	if (status < 0) {
3010 		FS_CALL(vnode, close, cookie);
3011 		FS_CALL(vnode, free_cookie, cookie);
3012 	}
3013 	return status;
3014 }
3015 
3016 
3017 /** Calls fs open_dir() on the given vnode and returns a new
3018  *	file descriptor for it
3019  */
3020 
3021 static int
3022 open_dir_vnode(struct vnode *vnode, bool kernel)
3023 {
3024 	void *cookie;
3025 	int status;
3026 
3027 	status = FS_CALL(vnode, open_dir, &cookie);
3028 	if (status < FSSH_B_OK)
3029 		return status;
3030 
3031 	// file is opened, create a fd
3032 	status = get_new_fd(FDTYPE_DIR, NULL, vnode, cookie, 0, kernel);
3033 	if (status >= 0)
3034 		return status;
3035 
3036 	FS_CALL(vnode, close_dir, cookie);
3037 	FS_CALL(vnode, free_dir_cookie, cookie);
3038 
3039 	return status;
3040 }
3041 
3042 
3043 /** Calls fs open_attr_dir() on the given vnode and returns a new
3044  *	file descriptor for it.
3045  *	Used by attr_dir_open(), and attr_dir_open_fd().
3046  */
3047 
3048 static int
3049 open_attr_dir_vnode(struct vnode *vnode, bool kernel)
3050 {
3051 	void *cookie;
3052 	int status;
3053 
3054 	if (!HAS_FS_CALL(vnode, open_attr_dir))
3055 		return FSSH_EOPNOTSUPP;
3056 
3057 	status = FS_CALL(vnode, open_attr_dir, &cookie);
3058 	if (status < 0)
3059 		return status;
3060 
3061 	// file is opened, create a fd
3062 	status = get_new_fd(FDTYPE_ATTR_DIR, NULL, vnode, cookie, 0, kernel);
3063 	if (status >= 0)
3064 		return status;
3065 
3066 	FS_CALL(vnode, close_attr_dir, cookie);
3067 	FS_CALL(vnode, free_attr_dir_cookie, cookie);
3068 
3069 	return status;
3070 }
3071 
3072 
3073 static int
3074 file_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, int perms, bool kernel)
3075 {
3076 	struct vnode *directory;
3077 	int status;
3078 
3079 	FUNCTION(("file_create_entry_ref: name = '%s', omode %x, perms %d, kernel %d\n", name, openMode, perms, kernel));
3080 
3081 	// get directory to put the new file in
3082 	status = get_vnode(mountID, directoryID, &directory, false);
3083 	if (status < FSSH_B_OK)
3084 		return status;
3085 
3086 	status = create_vnode(directory, name, openMode, perms, kernel);
3087 	put_vnode(directory);
3088 
3089 	return status;
3090 }
3091 
3092 
3093 static int
3094 file_create(int fd, char *path, int openMode, int perms, bool kernel)
3095 {
3096 	char name[FSSH_B_FILE_NAME_LENGTH];
3097 	struct vnode *directory;
3098 	int status;
3099 
3100 	FUNCTION(("file_create: path '%s', omode %x, perms %d, kernel %d\n", path, openMode, perms, kernel));
3101 
3102 	// get directory to put the new file in
3103 	status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel);
3104 	if (status < 0)
3105 		return status;
3106 
3107 	status = create_vnode(directory, name, openMode, perms, kernel);
3108 
3109 	put_vnode(directory);
3110 	return status;
3111 }
3112 
3113 
3114 static int
3115 file_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, bool kernel)
3116 {
3117 	struct vnode *vnode;
3118 	int status;
3119 
3120 	if (name == NULL || *name == '\0')
3121 		return FSSH_B_BAD_VALUE;
3122 
3123 	FUNCTION(("file_open_entry_ref(ref = (%ld, %Ld, %s), openMode = %d)\n",
3124 		mountID, directoryID, name, openMode));
3125 
3126 	// get the vnode matching the entry_ref
3127 	status = entry_ref_to_vnode(mountID, directoryID, name, &vnode);
3128 	if (status < FSSH_B_OK)
3129 		return status;
3130 
3131 	status = open_vnode(vnode, openMode, kernel);
3132 	if (status < FSSH_B_OK)
3133 		put_vnode(vnode);
3134 
3135 	return status;
3136 }
3137 
3138 
3139 static int
3140 file_open(int fd, char *path, int openMode, bool kernel)
3141 {
3142 	int status = FSSH_B_OK;
3143 	bool traverse = ((openMode & FSSH_O_NOTRAVERSE) == 0);
3144 
3145 	FUNCTION(("file_open: fd: %d, entry path = '%s', omode %d, kernel %d\n",
3146 		fd, path, openMode, kernel));
3147 
3148 	// get the vnode matching the vnode + path combination
3149 	struct vnode *vnode = NULL;
3150 	fssh_vnode_id parentID;
3151 	status = fd_and_path_to_vnode(fd, path, traverse, &vnode, &parentID, kernel);
3152 	if (status != FSSH_B_OK)
3153 		return status;
3154 
3155 	// open the vnode
3156 	status = open_vnode(vnode, openMode, kernel);
3157 	// put only on error -- otherwise our reference was transferred to the FD
3158 	if (status < FSSH_B_OK)
3159 		put_vnode(vnode);
3160 
3161 	return status;
3162 }
3163 
3164 
3165 static fssh_status_t
3166 file_close(struct file_descriptor *descriptor)
3167 {
3168 	struct vnode *vnode = descriptor->u.vnode;
3169 	fssh_status_t status = FSSH_B_OK;
3170 
3171 	FUNCTION(("file_close(descriptor = %p)\n", descriptor));
3172 
3173 	if (HAS_FS_CALL(vnode, close))
3174 		status = FS_CALL(vnode, close, descriptor->cookie);
3175 
3176 	return status;
3177 }
3178 
3179 
3180 static void
3181 file_free_fd(struct file_descriptor *descriptor)
3182 {
3183 	struct vnode *vnode = descriptor->u.vnode;
3184 
3185 	if (vnode != NULL) {
3186 		FS_CALL(vnode, free_cookie, descriptor->cookie);
3187 		put_vnode(vnode);
3188 	}
3189 }
3190 
3191 
3192 static fssh_status_t
3193 file_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length)
3194 {
3195 	struct vnode *vnode = descriptor->u.vnode;
3196 
3197 	FUNCTION(("file_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length));
3198 	return FS_CALL(vnode, read, descriptor->cookie, pos, buffer, length);
3199 }
3200 
3201 
3202 static fssh_status_t
3203 file_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length)
3204 {
3205 	struct vnode *vnode = descriptor->u.vnode;
3206 
3207 	FUNCTION(("file_write: buf %p, pos %Ld, len %p\n", buffer, pos, length));
3208 	return FS_CALL(vnode, write, descriptor->cookie, pos, buffer, length);
3209 }
3210 
3211 
3212 static fssh_off_t
3213 file_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType)
3214 {
3215 	fssh_off_t offset;
3216 
3217 	FUNCTION(("file_seek(pos = %Ld, seekType = %d)\n", pos, seekType));
3218 	// ToDo: seek should fail for pipes and FIFOs...
3219 
3220 	switch (seekType) {
3221 		case FSSH_SEEK_SET:
3222 			offset = 0;
3223 			break;
3224 		case FSSH_SEEK_CUR:
3225 			offset = descriptor->pos;
3226 			break;
3227 		case FSSH_SEEK_END:
3228 		{
3229 			struct vnode *vnode = descriptor->u.vnode;
3230 			struct fssh_stat stat;
3231 			fssh_status_t status;
3232 
3233 			if (!HAS_FS_CALL(vnode, read_stat))
3234 				return FSSH_EOPNOTSUPP;
3235 
3236 			status = FS_CALL(vnode, read_stat, &stat);
3237 			if (status < FSSH_B_OK)
3238 				return status;
3239 
3240 			offset = stat.fssh_st_size;
3241 			break;
3242 		}
3243 		default:
3244 			return FSSH_B_BAD_VALUE;
3245 	}
3246 
3247 	// assumes fssh_off_t is 64 bits wide
3248 	if (offset > 0 && LLONG_MAX - offset < pos)
3249 		return FSSH_EOVERFLOW;
3250 
3251 	pos += offset;
3252 	if (pos < 0)
3253 		return FSSH_B_BAD_VALUE;
3254 
3255 	return descriptor->pos = pos;
3256 }
3257 
3258 
3259 static fssh_status_t
3260 dir_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, int perms, bool kernel)
3261 {
3262 	struct vnode *vnode;
3263 	fssh_status_t status;
3264 
3265 	if (name == NULL || *name == '\0')
3266 		return FSSH_B_BAD_VALUE;
3267 
3268 	FUNCTION(("dir_create_entry_ref(dev = %ld, ino = %Ld, name = '%s', perms = %d)\n", mountID, parentID, name, perms));
3269 
3270 	status = get_vnode(mountID, parentID, &vnode, kernel);
3271 	if (status < FSSH_B_OK)
3272 		return status;
3273 
3274 	if (HAS_FS_CALL(vnode, create_dir))
3275 		status = FS_CALL(vnode, create_dir, name, perms);
3276 	else
3277 		status = FSSH_EROFS;
3278 
3279 	put_vnode(vnode);
3280 	return status;
3281 }
3282 
3283 
3284 static fssh_status_t
3285 dir_create(int fd, char *path, int perms, bool kernel)
3286 {
3287 	char filename[FSSH_B_FILE_NAME_LENGTH];
3288 	struct vnode *vnode;
3289 	fssh_status_t status;
3290 
3291 	FUNCTION(("dir_create: path '%s', perms %d, kernel %d\n", path, perms, kernel));
3292 
3293 	status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel);
3294 	if (status < 0)
3295 		return status;
3296 
3297 	if (HAS_FS_CALL(vnode, create_dir))
3298 		status = FS_CALL(vnode, create_dir, filename, perms);
3299 	else
3300 		status = FSSH_EROFS;
3301 
3302 	put_vnode(vnode);
3303 	return status;
3304 }
3305 
3306 
3307 static int
3308 dir_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, bool kernel)
3309 {
3310 	struct vnode *vnode;
3311 	int status;
3312 
3313 	FUNCTION(("dir_open_entry_ref()\n"));
3314 
3315 	if (name && *name == '\0')
3316 		return FSSH_B_BAD_VALUE;
3317 
3318 	// get the vnode matching the entry_ref/node_ref
3319 	if (name)
3320 		status = entry_ref_to_vnode(mountID, parentID, name, &vnode);
3321 	else
3322 		status = get_vnode(mountID, parentID, &vnode, false);
3323 	if (status < FSSH_B_OK)
3324 		return status;
3325 
3326 	status = open_dir_vnode(vnode, kernel);
3327 	if (status < FSSH_B_OK)
3328 		put_vnode(vnode);
3329 
3330 	return status;
3331 }
3332 
3333 
3334 static int
3335 dir_open(int fd, char *path, bool kernel)
3336 {
3337 	int status = FSSH_B_OK;
3338 
3339 	FUNCTION(("dir_open: fd: %d, entry path = '%s', kernel %d\n", fd, path, kernel));
3340 
3341 	// get the vnode matching the vnode + path combination
3342 	struct vnode *vnode = NULL;
3343 	fssh_vnode_id parentID;
3344 	status = fd_and_path_to_vnode(fd, path, true, &vnode, &parentID, kernel);
3345 	if (status != FSSH_B_OK)
3346 		return status;
3347 
3348 	// open the dir
3349 	status = open_dir_vnode(vnode, kernel);
3350 	if (status < FSSH_B_OK)
3351 		put_vnode(vnode);
3352 
3353 	return status;
3354 }
3355 
3356 
3357 static fssh_status_t
3358 dir_close(struct file_descriptor *descriptor)
3359 {
3360 	struct vnode *vnode = descriptor->u.vnode;
3361 
3362 	FUNCTION(("dir_close(descriptor = %p)\n", descriptor));
3363 
3364 	if (HAS_FS_CALL(vnode, close_dir))
3365 		return FS_CALL(vnode, close_dir, descriptor->cookie);
3366 
3367 	return FSSH_B_OK;
3368 }
3369 
3370 
3371 static void
3372 dir_free_fd(struct file_descriptor *descriptor)
3373 {
3374 	struct vnode *vnode = descriptor->u.vnode;
3375 
3376 	if (vnode != NULL) {
3377 		FS_CALL(vnode, free_dir_cookie, descriptor->cookie);
3378 		put_vnode(vnode);
3379 	}
3380 }
3381 
3382 
3383 static fssh_status_t
3384 dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
3385 	fssh_size_t bufferSize, uint32_t *_count)
3386 {
3387 	return dir_read(descriptor->u.vnode, descriptor->cookie, buffer, bufferSize, _count);
3388 }
3389 
3390 
3391 static void
3392 fix_dirent(struct vnode *parent, struct fssh_dirent *entry)
3393 {
3394 	// set d_pdev and d_pino
3395 	entry->d_pdev = parent->device;
3396 	entry->d_pino = parent->id;
3397 
3398 	// If this is the ".." entry and the directory is the root of a FS,
3399 	// we need to replace d_dev and d_ino with the actual values.
3400 	if (fssh_strcmp(entry->d_name, "..") == 0
3401 		&& parent->mount->root_vnode == parent
3402 		&& parent->mount->covers_vnode) {
3403 		inc_vnode_ref_count(parent);
3404 			// vnode_path_to_vnode() puts the node
3405 
3406 		// ".." is guaranteed to to be clobbered by this call
3407 		struct vnode *vnode;
3408 		fssh_status_t status = vnode_path_to_vnode(parent, (char*)"..", false,
3409 			0, &vnode, NULL);
3410 
3411 		if (status == FSSH_B_OK) {
3412 			entry->d_dev = vnode->device;
3413 			entry->d_ino = vnode->id;
3414 		}
3415 	} else {
3416 		// resolve mount points
3417 		struct vnode *vnode = NULL;
3418 		fssh_status_t status = get_vnode(entry->d_dev, entry->d_ino, &vnode, false);
3419 		if (status != FSSH_B_OK)
3420 			return;
3421 
3422 		fssh_mutex_lock(&sVnodeCoveredByMutex);
3423 		if (vnode->covered_by) {
3424 			entry->d_dev = vnode->covered_by->device;
3425 			entry->d_ino = vnode->covered_by->id;
3426 		}
3427 		fssh_mutex_unlock(&sVnodeCoveredByMutex);
3428 
3429 		put_vnode(vnode);
3430 	}
3431 }
3432 
3433 
3434 static fssh_status_t
3435 dir_read(struct vnode *vnode, void *cookie, struct fssh_dirent *buffer,
3436 	fssh_size_t bufferSize, uint32_t *_count)
3437 {
3438 	if (!HAS_FS_CALL(vnode, read_dir))
3439 		return FSSH_EOPNOTSUPP;
3440 
3441 	fssh_status_t error = FS_CALL(vnode, read_dir,cookie,buffer,bufferSize,_count);
3442 	if (error != FSSH_B_OK)
3443 		return error;
3444 
3445 	// we need to adjust the read dirents
3446 	if (*_count > 0) {
3447 		// XXX: Currently reading only one dirent is supported. Make this a loop!
3448 		fix_dirent(vnode, buffer);
3449 	}
3450 
3451 	return error;
3452 }
3453 
3454 
3455 static fssh_status_t
3456 dir_rewind(struct file_descriptor *descriptor)
3457 {
3458 	struct vnode *vnode = descriptor->u.vnode;
3459 
3460 	if (HAS_FS_CALL(vnode, rewind_dir))
3461 		return FS_CALL(vnode, rewind_dir,descriptor->cookie);
3462 
3463 	return FSSH_EOPNOTSUPP;
3464 }
3465 
3466 
3467 static fssh_status_t
3468 dir_remove(int fd, char *path, bool kernel)
3469 {
3470 	char name[FSSH_B_FILE_NAME_LENGTH];
3471 	struct vnode *directory;
3472 	fssh_status_t status;
3473 
3474 	if (path != NULL) {
3475 		// we need to make sure our path name doesn't stop with "/", ".", or ".."
3476 		char *lastSlash = fssh_strrchr(path, '/');
3477 		if (lastSlash != NULL) {
3478 			char *leaf = lastSlash + 1;
3479 			if (!fssh_strcmp(leaf, ".."))
3480 				return FSSH_B_NOT_ALLOWED;
3481 
3482 			// omit multiple slashes
3483 			while (lastSlash > path && lastSlash[-1] == '/') {
3484 				lastSlash--;
3485 			}
3486 
3487 			if (!leaf[0]
3488 				|| !fssh_strcmp(leaf, ".")) {
3489 				// "name/" -> "name", or "name/." -> "name"
3490 				lastSlash[0] = '\0';
3491 			}
3492 		} else if (!fssh_strcmp(path, ".."))
3493 			return FSSH_B_NOT_ALLOWED;
3494 	}
3495 
3496 	status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel);
3497 	if (status < FSSH_B_OK)
3498 		return status;
3499 
3500 	if (HAS_FS_CALL(directory, remove_dir)) {
3501 		status = FS_CALL(directory, remove_dir, name);
3502 	} else
3503 		status = FSSH_EROFS;
3504 
3505 	put_vnode(directory);
3506 	return status;
3507 }
3508 
3509 
3510 static fssh_status_t
3511 common_ioctl(struct file_descriptor *descriptor, uint32_t op, void *buffer,
3512 	fssh_size_t length)
3513 {
3514 	struct vnode *vnode = descriptor->u.vnode;
3515 
3516 	if (HAS_FS_CALL(vnode, ioctl)) {
3517 		return FS_CALL(vnode, ioctl,
3518 			descriptor->cookie, op, buffer, length);
3519 	}
3520 
3521 	return FSSH_EOPNOTSUPP;
3522 }
3523 
3524 
3525 static fssh_status_t
3526 common_fcntl(int fd, int op, uint32_t argument, bool kernel)
3527 {
3528 	struct file_descriptor *descriptor;
3529 	struct vnode *vnode;
3530 	fssh_status_t status;
3531 
3532 	FUNCTION(("common_fcntl(fd = %d, op = %d, argument = %lx, %s)\n",
3533 		fd, op, argument, kernel ? "kernel" : "user"));
3534 
3535 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3536 	if (descriptor == NULL)
3537 		return FSSH_B_FILE_ERROR;
3538 
3539 	switch (op) {
3540 		case FSSH_F_SETFD:
3541 		{
3542 			struct io_context *context = get_current_io_context(kernel);
3543 			// Set file descriptor flags
3544 
3545 			// FSSH_O_CLOEXEC is the only flag available at this time
3546 			fssh_mutex_lock(&context->io_mutex);
3547 			fd_set_close_on_exec(context, fd, argument == FSSH_FD_CLOEXEC);
3548 			fssh_mutex_unlock(&context->io_mutex);
3549 
3550 			status = FSSH_B_OK;
3551 			break;
3552 		}
3553 
3554 		case FSSH_F_GETFD:
3555 		{
3556 			struct io_context *context = get_current_io_context(kernel);
3557 
3558 			// Get file descriptor flags
3559 			fssh_mutex_lock(&context->io_mutex);
3560 			status = fd_close_on_exec(context, fd) ? FSSH_FD_CLOEXEC : 0;
3561 			fssh_mutex_unlock(&context->io_mutex);
3562 			break;
3563 		}
3564 
3565 		case FSSH_F_SETFL:
3566 			// Set file descriptor open mode
3567 			if (HAS_FS_CALL(vnode, set_flags)) {
3568 				// we only accept changes to FSSH_O_APPEND and FSSH_O_NONBLOCK
3569 				argument &= FSSH_O_APPEND | FSSH_O_NONBLOCK;
3570 
3571 				status = FS_CALL(vnode, set_flags, descriptor->cookie, (int)argument);
3572 				if (status == FSSH_B_OK) {
3573 					// update this descriptor's open_mode field
3574 					descriptor->open_mode = (descriptor->open_mode & ~(FSSH_O_APPEND | FSSH_O_NONBLOCK))
3575 						| argument;
3576 				}
3577 			} else
3578 				status = FSSH_EOPNOTSUPP;
3579 			break;
3580 
3581 		case FSSH_F_GETFL:
3582 			// Get file descriptor open mode
3583 			status = descriptor->open_mode;
3584 			break;
3585 
3586 		case FSSH_F_DUPFD:
3587 		{
3588 			struct io_context *context = get_current_io_context(kernel);
3589 
3590 			status = new_fd_etc(context, descriptor, (int)argument);
3591 			if (status >= 0) {
3592 				fssh_mutex_lock(&context->io_mutex);
3593 				fd_set_close_on_exec(context, fd, false);
3594 				fssh_mutex_unlock(&context->io_mutex);
3595 
3596 				fssh_atomic_add(&descriptor->ref_count, 1);
3597 			}
3598 			break;
3599 		}
3600 
3601 		case FSSH_F_GETLK:
3602 		case FSSH_F_SETLK:
3603 		case FSSH_F_SETLKW:
3604 			status = FSSH_B_BAD_VALUE;
3605 			break;
3606 
3607 		// ToDo: add support for more ops?
3608 
3609 		default:
3610 			status = FSSH_B_BAD_VALUE;
3611 	}
3612 
3613 	put_fd(descriptor);
3614 	return status;
3615 }
3616 
3617 
3618 static fssh_status_t
3619 common_sync(int fd, bool kernel)
3620 {
3621 	struct file_descriptor *descriptor;
3622 	struct vnode *vnode;
3623 	fssh_status_t status;
3624 
3625 	FUNCTION(("common_fsync: entry. fd %d kernel %d\n", fd, kernel));
3626 
3627 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3628 	if (descriptor == NULL)
3629 		return FSSH_B_FILE_ERROR;
3630 
3631 	if (HAS_FS_CALL(vnode, fsync))
3632 		status = FS_CALL_NO_PARAMS(vnode, fsync);
3633 	else
3634 		status = FSSH_EOPNOTSUPP;
3635 
3636 	put_fd(descriptor);
3637 	return status;
3638 }
3639 
3640 
3641 static fssh_status_t
3642 common_lock_node(int fd, bool kernel)
3643 {
3644 	struct file_descriptor *descriptor;
3645 	struct vnode *vnode;
3646 
3647 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3648 	if (descriptor == NULL)
3649 		return FSSH_B_FILE_ERROR;
3650 
3651 	fssh_status_t status = FSSH_B_OK;
3652 
3653 	// We need to set the locking atomically - someone
3654 	// else might set one at the same time
3655 #if LONG_MAX == INT_MAX
3656 	if (fssh_atomic_test_and_set64((int64_t *)&vnode->mandatory_locked_by,
3657 			(fssh_addr_t)descriptor, 0) != 0)
3658 #else
3659 	if (fssh_atomic_test_and_set((int32_t *)&vnode->mandatory_locked_by,
3660 			(fssh_addr_t)descriptor, 0) != 0)
3661 #endif
3662 		status = FSSH_B_BUSY;
3663 
3664 	put_fd(descriptor);
3665 	return status;
3666 }
3667 
3668 
3669 static fssh_status_t
3670 common_unlock_node(int fd, bool kernel)
3671 {
3672 	struct file_descriptor *descriptor;
3673 	struct vnode *vnode;
3674 
3675 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3676 	if (descriptor == NULL)
3677 		return FSSH_B_FILE_ERROR;
3678 
3679 	fssh_status_t status = FSSH_B_OK;
3680 
3681 	// We need to set the locking atomically - someone
3682 	// else might set one at the same time
3683 #ifdef __x86_64__
3684 	if (fssh_atomic_test_and_set64((int64_t *)&vnode->mandatory_locked_by,
3685 			0, (fssh_addr_t)descriptor) != (int64_t)descriptor)
3686 #else
3687 	if (fssh_atomic_test_and_set((int32_t *)&vnode->mandatory_locked_by,
3688 			0, (fssh_addr_t)descriptor) != (int32_t)descriptor)
3689 #endif
3690 		status = FSSH_B_BAD_VALUE;
3691 
3692 	put_fd(descriptor);
3693 	return status;
3694 }
3695 
3696 
3697 static fssh_status_t
3698 common_read_link(int fd, char *path, char *buffer, fssh_size_t *_bufferSize,
3699 	bool kernel)
3700 {
3701 	struct vnode *vnode;
3702 	fssh_status_t status;
3703 
3704 	status = fd_and_path_to_vnode(fd, path, false, &vnode, NULL, kernel);
3705 	if (status < FSSH_B_OK)
3706 		return status;
3707 
3708 	if (HAS_FS_CALL(vnode, read_symlink)) {
3709 		status = FS_CALL(vnode, read_symlink, buffer, _bufferSize);
3710 	} else
3711 		status = FSSH_B_BAD_VALUE;
3712 
3713 	put_vnode(vnode);
3714 	return status;
3715 }
3716 
3717 
3718 static fssh_status_t
3719 common_create_symlink(int fd, char *path, const char *toPath, int mode,
3720 	bool kernel)
3721 {
3722 	// path validity checks have to be in the calling function!
3723 	char name[FSSH_B_FILE_NAME_LENGTH];
3724 	struct vnode *vnode;
3725 	fssh_status_t status;
3726 
3727 	FUNCTION(("common_create_symlink(fd = %d, path = %s, toPath = %s, mode = %d, kernel = %d)\n", fd, path, toPath, mode, kernel));
3728 
3729 	status = fd_and_path_to_dir_vnode(fd, path, &vnode, name, kernel);
3730 	if (status < FSSH_B_OK)
3731 		return status;
3732 
3733 	if (HAS_FS_CALL(vnode, create_symlink))
3734 		status = FS_CALL(vnode, create_symlink, name, toPath, mode);
3735 	else
3736 		status = FSSH_EROFS;
3737 
3738 	put_vnode(vnode);
3739 
3740 	return status;
3741 }
3742 
3743 
3744 static fssh_status_t
3745 common_create_link(char *path, char *toPath, bool kernel)
3746 {
3747 	// path validity checks have to be in the calling function!
3748 	char name[FSSH_B_FILE_NAME_LENGTH];
3749 	struct vnode *directory, *vnode;
3750 	fssh_status_t status;
3751 
3752 	FUNCTION(("common_create_link(path = %s, toPath = %s, kernel = %d)\n", path, toPath, kernel));
3753 
3754 	status = path_to_dir_vnode(path, &directory, name, kernel);
3755 	if (status < FSSH_B_OK)
3756 		return status;
3757 
3758 	status = path_to_vnode(toPath, true, &vnode, NULL, kernel);
3759 	if (status < FSSH_B_OK)
3760 		goto err;
3761 
3762 	if (directory->mount != vnode->mount) {
3763 		status = FSSH_B_CROSS_DEVICE_LINK;
3764 		goto err1;
3765 	}
3766 
3767 	if (HAS_FS_CALL(directory, link))
3768 		status = FS_CALL(directory, link, name, vnode);
3769 	else
3770 		status = FSSH_EROFS;
3771 
3772 err1:
3773 	put_vnode(vnode);
3774 err:
3775 	put_vnode(directory);
3776 
3777 	return status;
3778 }
3779 
3780 
3781 static fssh_status_t
3782 common_unlink(int fd, char *path, bool kernel)
3783 {
3784 	char filename[FSSH_B_FILE_NAME_LENGTH];
3785 	struct vnode *vnode;
3786 	fssh_status_t status;
3787 
3788 	FUNCTION(("common_unlink: fd: %d, path '%s', kernel %d\n", fd, path, kernel));
3789 
3790 	status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel);
3791 	if (status < 0)
3792 		return status;
3793 
3794 	if (HAS_FS_CALL(vnode, unlink))
3795 		status = FS_CALL(vnode, unlink, filename);
3796 	else
3797 		status = FSSH_EROFS;
3798 
3799 	put_vnode(vnode);
3800 
3801 	return status;
3802 }
3803 
3804 
3805 static fssh_status_t
3806 common_access(char *path, int mode, bool kernel)
3807 {
3808 	struct vnode *vnode;
3809 	fssh_status_t status;
3810 
3811 	status = path_to_vnode(path, true, &vnode, NULL, kernel);
3812 	if (status < FSSH_B_OK)
3813 		return status;
3814 
3815 	if (HAS_FS_CALL(vnode, access))
3816 		status = FS_CALL(vnode, access, mode);
3817 	else
3818 		status = FSSH_B_OK;
3819 
3820 	put_vnode(vnode);
3821 
3822 	return status;
3823 }
3824 
3825 
3826 static fssh_status_t
3827 common_rename(int fd, char *path, int newFD, char *newPath, bool kernel)
3828 {
3829 	struct vnode *fromVnode, *toVnode;
3830 	char fromName[FSSH_B_FILE_NAME_LENGTH];
3831 	char toName[FSSH_B_FILE_NAME_LENGTH];
3832 	fssh_status_t status;
3833 
3834 	FUNCTION(("common_rename(fd = %d, path = %s, newFD = %d, newPath = %s, kernel = %d)\n", fd, path, newFD, newPath, kernel));
3835 
3836 	status = fd_and_path_to_dir_vnode(fd, path, &fromVnode, fromName, kernel);
3837 	if (status < 0)
3838 		return status;
3839 
3840 	status = fd_and_path_to_dir_vnode(newFD, newPath, &toVnode, toName, kernel);
3841 	if (status < 0)
3842 		goto err;
3843 
3844 	if (fromVnode->device != toVnode->device) {
3845 		status = FSSH_B_CROSS_DEVICE_LINK;
3846 		goto err1;
3847 	}
3848 
3849 	if (HAS_FS_CALL(fromVnode, rename))
3850 		status = FS_CALL(fromVnode, rename, fromName, toVnode, toName);
3851 	else
3852 		status = FSSH_EROFS;
3853 
3854 err1:
3855 	put_vnode(toVnode);
3856 err:
3857 	put_vnode(fromVnode);
3858 
3859 	return status;
3860 }
3861 
3862 
3863 static fssh_status_t
3864 common_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat)
3865 {
3866 	struct vnode *vnode = descriptor->u.vnode;
3867 
3868 	FUNCTION(("common_read_stat: stat %p\n", stat));
3869 
3870 	stat->fssh_st_atim.tv_nsec = 0;
3871 	stat->fssh_st_mtim.tv_nsec = 0;
3872 	stat->fssh_st_ctim.tv_nsec = 0;
3873 	stat->fssh_st_crtim.tv_nsec = 0;
3874 
3875 	fssh_status_t status = FS_CALL(vnode, read_stat, stat);
3876 
3877 	// fill in the st_dev and st_ino fields
3878 	if (status == FSSH_B_OK) {
3879 		stat->fssh_st_dev = vnode->device;
3880 		stat->fssh_st_ino = vnode->id;
3881 	}
3882 
3883 	return status;
3884 }
3885 
3886 
3887 static fssh_status_t
3888 common_write_stat(struct file_descriptor *descriptor,
3889 	const struct fssh_stat *stat, int statMask)
3890 {
3891 	struct vnode *vnode = descriptor->u.vnode;
3892 
3893 	FUNCTION(("common_write_stat(vnode = %p, stat = %p, statMask = %d)\n", vnode, stat, statMask));
3894 	if (!HAS_FS_CALL(vnode, write_stat))
3895 		return FSSH_EROFS;
3896 
3897 	return FS_CALL(vnode, write_stat, stat, statMask);
3898 }
3899 
3900 
3901 static fssh_status_t
3902 common_path_read_stat(int fd, char *path, bool traverseLeafLink,
3903 	struct fssh_stat *stat, bool kernel)
3904 {
3905 	struct vnode *vnode;
3906 	fssh_status_t status;
3907 
3908 	FUNCTION(("common_path_read_stat: fd: %d, path '%s', stat %p,\n", fd, path, stat));
3909 
3910 	status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel);
3911 	if (status < 0)
3912 		return status;
3913 
3914 	status = FS_CALL(vnode, read_stat, stat);
3915 
3916 	// fill in the st_dev and st_ino fields
3917 	if (status == FSSH_B_OK) {
3918 		stat->fssh_st_dev = vnode->device;
3919 		stat->fssh_st_ino = vnode->id;
3920 	}
3921 
3922 	put_vnode(vnode);
3923 	return status;
3924 }
3925 
3926 
3927 static fssh_status_t
3928 common_path_write_stat(int fd, char *path, bool traverseLeafLink,
3929 	const struct fssh_stat *stat, int statMask, bool kernel)
3930 {
3931 	struct vnode *vnode;
3932 	fssh_status_t status;
3933 
3934 	FUNCTION(("common_write_stat: fd: %d, path '%s', stat %p, stat_mask %d, kernel %d\n", fd, path, stat, statMask, kernel));
3935 
3936 	status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel);
3937 	if (status < 0)
3938 		return status;
3939 
3940 	if (HAS_FS_CALL(vnode, write_stat))
3941 		status = FS_CALL(vnode, write_stat, stat, statMask);
3942 	else
3943 		status = FSSH_EROFS;
3944 
3945 	put_vnode(vnode);
3946 
3947 	return status;
3948 }
3949 
3950 
3951 static int
3952 attr_dir_open(int fd, char *path, bool kernel)
3953 {
3954 	struct vnode *vnode;
3955 	int status;
3956 
3957 	FUNCTION(("attr_dir_open(fd = %d, path = '%s', kernel = %d)\n", fd, path, kernel));
3958 
3959 	status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel);
3960 	if (status < FSSH_B_OK)
3961 		return status;
3962 
3963 	status = open_attr_dir_vnode(vnode, kernel);
3964 	if (status < 0)
3965 		put_vnode(vnode);
3966 
3967 	return status;
3968 }
3969 
3970 
3971 static fssh_status_t
3972 attr_dir_close(struct file_descriptor *descriptor)
3973 {
3974 	struct vnode *vnode = descriptor->u.vnode;
3975 
3976 	FUNCTION(("attr_dir_close(descriptor = %p)\n", descriptor));
3977 
3978 	if (HAS_FS_CALL(vnode, close_attr_dir))
3979 		return FS_CALL(vnode, close_attr_dir, descriptor->cookie);
3980 
3981 	return FSSH_B_OK;
3982 }
3983 
3984 
3985 static void
3986 attr_dir_free_fd(struct file_descriptor *descriptor)
3987 {
3988 	struct vnode *vnode = descriptor->u.vnode;
3989 
3990 	if (vnode != NULL) {
3991 		FS_CALL(vnode, free_attr_dir_cookie, descriptor->cookie);
3992 		put_vnode(vnode);
3993 	}
3994 }
3995 
3996 
3997 static fssh_status_t
3998 attr_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
3999 	fssh_size_t bufferSize, uint32_t *_count)
4000 {
4001 	struct vnode *vnode = descriptor->u.vnode;
4002 
4003 	FUNCTION(("attr_dir_read(descriptor = %p)\n", descriptor));
4004 
4005 	if (HAS_FS_CALL(vnode, read_attr_dir))
4006 		return FS_CALL(vnode, read_attr_dir, descriptor->cookie, buffer, bufferSize, _count);
4007 
4008 	return FSSH_EOPNOTSUPP;
4009 }
4010 
4011 
4012 static fssh_status_t
4013 attr_dir_rewind(struct file_descriptor *descriptor)
4014 {
4015 	struct vnode *vnode = descriptor->u.vnode;
4016 
4017 	FUNCTION(("attr_dir_rewind(descriptor = %p)\n", descriptor));
4018 
4019 	if (HAS_FS_CALL(vnode, rewind_attr_dir))
4020 		return FS_CALL(vnode, rewind_attr_dir, descriptor->cookie);
4021 
4022 	return FSSH_EOPNOTSUPP;
4023 }
4024 
4025 
4026 static int
4027 attr_create(int fd, const char *name, uint32_t type, int openMode, bool kernel)
4028 {
4029 	struct vnode *vnode;
4030 	void *cookie;
4031 	int status;
4032 
4033 	if (name == NULL || *name == '\0')
4034 		return FSSH_B_BAD_VALUE;
4035 
4036 	vnode = get_vnode_from_fd(fd, kernel);
4037 	if (vnode == NULL)
4038 		return FSSH_B_FILE_ERROR;
4039 
4040 	if (!HAS_FS_CALL(vnode, create_attr)) {
4041 		status = FSSH_EROFS;
4042 		goto err;
4043 	}
4044 
4045 	status = FS_CALL(vnode, create_attr, name, type, openMode, &cookie);
4046 	if (status < FSSH_B_OK)
4047 		goto err;
4048 
4049 	if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0)
4050 		return status;
4051 
4052 	FS_CALL(vnode, close_attr, cookie);
4053 	FS_CALL(vnode, free_attr_cookie, cookie);
4054 
4055 	FS_CALL(vnode, remove_attr, name);
4056 
4057 err:
4058 	put_vnode(vnode);
4059 
4060 	return status;
4061 }
4062 
4063 
4064 static int
4065 attr_open(int fd, const char *name, int openMode, bool kernel)
4066 {
4067 	struct vnode *vnode;
4068 	void *cookie;
4069 	int status;
4070 
4071 	if (name == NULL || *name == '\0')
4072 		return FSSH_B_BAD_VALUE;
4073 
4074 	vnode = get_vnode_from_fd(fd, kernel);
4075 	if (vnode == NULL)
4076 		return FSSH_B_FILE_ERROR;
4077 
4078 	if (!HAS_FS_CALL(vnode, open_attr)) {
4079 		status = FSSH_EOPNOTSUPP;
4080 		goto err;
4081 	}
4082 
4083 	status = FS_CALL(vnode, open_attr, name, openMode, &cookie);
4084 	if (status < FSSH_B_OK)
4085 		goto err;
4086 
4087 	// now we only need a file descriptor for this attribute and we're done
4088 	if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0)
4089 		return status;
4090 
4091 	FS_CALL(vnode, close_attr, cookie);
4092 	FS_CALL(vnode, free_attr_cookie, cookie);
4093 
4094 err:
4095 	put_vnode(vnode);
4096 
4097 	return status;
4098 }
4099 
4100 
4101 static fssh_status_t
4102 attr_close(struct file_descriptor *descriptor)
4103 {
4104 	struct vnode *vnode = descriptor->u.vnode;
4105 
4106 	FUNCTION(("attr_close(descriptor = %p)\n", descriptor));
4107 
4108 	if (HAS_FS_CALL(vnode, close_attr))
4109 		return FS_CALL(vnode, close_attr, descriptor->cookie);
4110 
4111 	return FSSH_B_OK;
4112 }
4113 
4114 
4115 static void
4116 attr_free_fd(struct file_descriptor *descriptor)
4117 {
4118 	struct vnode *vnode = descriptor->u.vnode;
4119 
4120 	if (vnode != NULL) {
4121 		FS_CALL(vnode, free_attr_cookie, descriptor->cookie);
4122 		put_vnode(vnode);
4123 	}
4124 }
4125 
4126 
4127 static fssh_status_t
4128 attr_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length)
4129 {
4130 	struct vnode *vnode = descriptor->u.vnode;
4131 
4132 	FUNCTION(("attr_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length));
4133 	if (!HAS_FS_CALL(vnode, read_attr))
4134 		return FSSH_EOPNOTSUPP;
4135 
4136 	return FS_CALL(vnode, read_attr, descriptor->cookie, pos, buffer, length);
4137 }
4138 
4139 
4140 static fssh_status_t
4141 attr_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length)
4142 {
4143 	struct vnode *vnode = descriptor->u.vnode;
4144 
4145 	FUNCTION(("attr_write: buf %p, pos %Ld, len %p\n", buffer, pos, length));
4146 	if (!HAS_FS_CALL(vnode, write_attr))
4147 		return FSSH_EOPNOTSUPP;
4148 
4149 	return FS_CALL(vnode, write_attr, descriptor->cookie, pos, buffer, length);
4150 }
4151 
4152 
4153 static fssh_off_t
4154 attr_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType)
4155 {
4156 	fssh_off_t offset;
4157 
4158 	switch (seekType) {
4159 		case FSSH_SEEK_SET:
4160 			offset = 0;
4161 			break;
4162 		case FSSH_SEEK_CUR:
4163 			offset = descriptor->pos;
4164 			break;
4165 		case FSSH_SEEK_END:
4166 		{
4167 			struct vnode *vnode = descriptor->u.vnode;
4168 			struct fssh_stat stat;
4169 			fssh_status_t status;
4170 
4171 			if (!HAS_FS_CALL(vnode, read_stat))
4172 				return FSSH_EOPNOTSUPP;
4173 
4174 			status = FS_CALL(vnode, read_attr_stat, descriptor->cookie, &stat);
4175 			if (status < FSSH_B_OK)
4176 				return status;
4177 
4178 			offset = stat.fssh_st_size;
4179 			break;
4180 		}
4181 		default:
4182 			return FSSH_B_BAD_VALUE;
4183 	}
4184 
4185 	// assumes fssh_off_t is 64 bits wide
4186 	if (offset > 0 && LLONG_MAX - offset < pos)
4187 		return FSSH_EOVERFLOW;
4188 
4189 	pos += offset;
4190 	if (pos < 0)
4191 		return FSSH_B_BAD_VALUE;
4192 
4193 	return descriptor->pos = pos;
4194 }
4195 
4196 
4197 static fssh_status_t
4198 attr_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat)
4199 {
4200 	struct vnode *vnode = descriptor->u.vnode;
4201 
4202 	FUNCTION(("attr_read_stat: stat 0x%p\n", stat));
4203 
4204 	if (!HAS_FS_CALL(vnode, read_attr_stat))
4205 		return FSSH_EOPNOTSUPP;
4206 
4207 	return FS_CALL(vnode, read_attr_stat, descriptor->cookie, stat);
4208 }
4209 
4210 
4211 static fssh_status_t
4212 attr_write_stat(struct file_descriptor *descriptor,
4213 	const struct fssh_stat *stat, int statMask)
4214 {
4215 	struct vnode *vnode = descriptor->u.vnode;
4216 
4217 	FUNCTION(("attr_write_stat: stat = %p, statMask %d\n", stat, statMask));
4218 
4219 	if (!HAS_FS_CALL(vnode, write_attr_stat))
4220 		return FSSH_EROFS;
4221 
4222 	return FS_CALL(vnode, write_attr_stat, descriptor->cookie, stat, statMask);
4223 }
4224 
4225 
4226 static fssh_status_t
4227 attr_remove(int fd, const char *name, bool kernel)
4228 {
4229 	struct file_descriptor *descriptor;
4230 	struct vnode *vnode;
4231 	fssh_status_t status;
4232 
4233 	if (name == NULL || *name == '\0')
4234 		return FSSH_B_BAD_VALUE;
4235 
4236 	FUNCTION(("attr_remove: fd = %d, name = \"%s\", kernel %d\n", fd, name, kernel));
4237 
4238 	descriptor = get_fd_and_vnode(fd, &vnode, kernel);
4239 	if (descriptor == NULL)
4240 		return FSSH_B_FILE_ERROR;
4241 
4242 	if (HAS_FS_CALL(vnode, remove_attr))
4243 		status = FS_CALL(vnode, remove_attr, name);
4244 	else
4245 		status = FSSH_EROFS;
4246 
4247 	put_fd(descriptor);
4248 
4249 	return status;
4250 }
4251 
4252 
4253 static fssh_status_t
4254 attr_rename(int fromfd, const char *fromName, int tofd, const char *toName, bool kernel)
4255 {
4256 	struct file_descriptor *fromDescriptor, *toDescriptor;
4257 	struct vnode *fromVnode, *toVnode;
4258 	fssh_status_t status;
4259 
4260 	if (fromName == NULL || *fromName == '\0' || toName == NULL || *toName == '\0')
4261 		return FSSH_B_BAD_VALUE;
4262 
4263 	FUNCTION(("attr_rename: from fd = %d, from name = \"%s\", to fd = %d, to name = \"%s\", kernel %d\n", fromfd, fromName, tofd, toName, kernel));
4264 
4265 	fromDescriptor = get_fd_and_vnode(fromfd, &fromVnode, kernel);
4266 	if (fromDescriptor == NULL)
4267 		return FSSH_B_FILE_ERROR;
4268 
4269 	toDescriptor = get_fd_and_vnode(tofd, &toVnode, kernel);
4270 	if (toDescriptor == NULL) {
4271 		status = FSSH_B_FILE_ERROR;
4272 		goto err;
4273 	}
4274 
4275 	// are the files on the same volume?
4276 	if (fromVnode->device != toVnode->device) {
4277 		status = FSSH_B_CROSS_DEVICE_LINK;
4278 		goto err1;
4279 	}
4280 
4281 	if (HAS_FS_CALL(fromVnode, rename_attr))
4282 		status = FS_CALL(fromVnode, rename_attr, fromName, toVnode, toName);
4283 	else
4284 		status = FSSH_EROFS;
4285 
4286 err1:
4287 	put_fd(toDescriptor);
4288 err:
4289 	put_fd(fromDescriptor);
4290 
4291 	return status;
4292 }
4293 
4294 
4295 static fssh_status_t
4296 index_dir_open(fssh_mount_id mountID, bool kernel)
4297 {
4298 	struct fs_mount *mount;
4299 	void *cookie;
4300 
4301 	FUNCTION(("index_dir_open(mountID = %ld, kernel = %d)\n", mountID, kernel));
4302 
4303 	fssh_status_t status = get_mount(mountID, &mount);
4304 	if (status < FSSH_B_OK)
4305 		return status;
4306 
4307 	if (!HAS_FS_MOUNT_CALL(mount, open_index_dir)) {
4308 		status = FSSH_EOPNOTSUPP;
4309 		goto out;
4310 	}
4311 
4312 	status = FS_MOUNT_CALL(mount, open_index_dir, &cookie);
4313 	if (status < FSSH_B_OK)
4314 		goto out;
4315 
4316 	// get fd for the index directory
4317 	status = get_new_fd(FDTYPE_INDEX_DIR, mount, NULL, cookie, 0, kernel);
4318 	if (status >= 0)
4319 		goto out;
4320 
4321 	// something went wrong
4322 	FS_MOUNT_CALL(mount, close_index_dir, cookie);
4323 	FS_MOUNT_CALL(mount, free_index_dir_cookie, cookie);
4324 
4325 out:
4326 	put_mount(mount);
4327 	return status;
4328 }
4329 
4330 
4331 static fssh_status_t
4332 index_dir_close(struct file_descriptor *descriptor)
4333 {
4334 	struct fs_mount *mount = descriptor->u.mount;
4335 
4336 	FUNCTION(("index_dir_close(descriptor = %p)\n", descriptor));
4337 
4338 	if (HAS_FS_MOUNT_CALL(mount, close_index_dir))
4339 		return FS_MOUNT_CALL(mount, close_index_dir, descriptor->cookie);
4340 
4341 	return FSSH_B_OK;
4342 }
4343 
4344 
4345 static void
4346 index_dir_free_fd(struct file_descriptor *descriptor)
4347 {
4348 	struct fs_mount *mount = descriptor->u.mount;
4349 
4350 	if (mount != NULL) {
4351 		FS_MOUNT_CALL(mount, free_index_dir_cookie, descriptor->cookie);
4352 		// ToDo: find a replacement ref_count object - perhaps the root dir?
4353 		//put_vnode(vnode);
4354 	}
4355 }
4356 
4357 
4358 static fssh_status_t
4359 index_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
4360 	fssh_size_t bufferSize, uint32_t *_count)
4361 {
4362 	struct fs_mount *mount = descriptor->u.mount;
4363 
4364 	if (HAS_FS_MOUNT_CALL(mount, read_index_dir))
4365 		return FS_MOUNT_CALL(mount, read_index_dir, descriptor->cookie, buffer, bufferSize, _count);
4366 
4367 	return FSSH_EOPNOTSUPP;
4368 }
4369 
4370 
4371 static fssh_status_t
4372 index_dir_rewind(struct file_descriptor *descriptor)
4373 {
4374 	struct fs_mount *mount = descriptor->u.mount;
4375 
4376 	if (HAS_FS_MOUNT_CALL(mount, rewind_index_dir))
4377 		return FS_MOUNT_CALL(mount, rewind_index_dir, descriptor->cookie);
4378 
4379 	return FSSH_EOPNOTSUPP;
4380 }
4381 
4382 
4383 static fssh_status_t
4384 index_create(fssh_mount_id mountID, const char *name, uint32_t type, uint32_t flags, bool kernel)
4385 {
4386 	FUNCTION(("index_create(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
4387 
4388 	struct fs_mount *mount;
4389 	fssh_status_t status = get_mount(mountID, &mount);
4390 	if (status < FSSH_B_OK)
4391 		return status;
4392 
4393 	if (!HAS_FS_MOUNT_CALL(mount, create_index)) {
4394 		status = FSSH_EROFS;
4395 		goto out;
4396 	}
4397 
4398 	status = FS_MOUNT_CALL(mount, create_index, name, type, flags);
4399 
4400 out:
4401 	put_mount(mount);
4402 	return status;
4403 }
4404 
4405 
4406 static fssh_status_t
4407 index_name_read_stat(fssh_mount_id mountID, const char *name,
4408 	struct fssh_stat *stat, bool kernel)
4409 {
4410 	FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
4411 
4412 	struct fs_mount *mount;
4413 	fssh_status_t status = get_mount(mountID, &mount);
4414 	if (status < FSSH_B_OK)
4415 		return status;
4416 
4417 	if (!HAS_FS_MOUNT_CALL(mount, read_index_stat)) {
4418 		status = FSSH_EOPNOTSUPP;
4419 		goto out;
4420 	}
4421 
4422 	status = FS_MOUNT_CALL(mount, read_index_stat, name, stat);
4423 
4424 out:
4425 	put_mount(mount);
4426 	return status;
4427 }
4428 
4429 
4430 static fssh_status_t
4431 index_remove(fssh_mount_id mountID, const char *name, bool kernel)
4432 {
4433 	FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
4434 
4435 	struct fs_mount *mount;
4436 	fssh_status_t status = get_mount(mountID, &mount);
4437 	if (status < FSSH_B_OK)
4438 		return status;
4439 
4440 	if (!HAS_FS_MOUNT_CALL(mount, remove_index)) {
4441 		status = FSSH_EROFS;
4442 		goto out;
4443 	}
4444 
4445 	status = FS_MOUNT_CALL(mount, remove_index, name);
4446 
4447 out:
4448 	put_mount(mount);
4449 	return status;
4450 }
4451 
4452 
4453 /*!	ToDo: the query FS API is still the pretty much the same as in R5.
4454 		It would be nice if the FS would find some more kernel support
4455 		for them.
4456 		For example, query parsing should be moved into the kernel.
4457 */
4458 static int
4459 query_open(fssh_dev_t device, const char *query, uint32_t flags,
4460 	fssh_port_id port, int32_t token, bool kernel)
4461 {
4462 	struct fs_mount *mount;
4463 	void *cookie;
4464 
4465 	FUNCTION(("query_open(device = %ld, query = \"%s\", kernel = %d)\n", device, query, kernel));
4466 
4467 	fssh_status_t status = get_mount(device, &mount);
4468 	if (status < FSSH_B_OK)
4469 		return status;
4470 
4471 	if (!HAS_FS_MOUNT_CALL(mount, open_query)) {
4472 		status = FSSH_EOPNOTSUPP;
4473 		goto out;
4474 	}
4475 
4476 	status = FS_MOUNT_CALL(mount, open_query, query, flags, port, token, &cookie);
4477 	if (status < FSSH_B_OK)
4478 		goto out;
4479 
4480 	// get fd for the index directory
4481 	status = get_new_fd(FDTYPE_QUERY, mount, NULL, cookie, 0, kernel);
4482 	if (status >= 0)
4483 		goto out;
4484 
4485 	// something went wrong
4486 	FS_MOUNT_CALL(mount, close_query, cookie);
4487 	FS_MOUNT_CALL(mount, free_query_cookie, cookie);
4488 
4489 out:
4490 	put_mount(mount);
4491 	return status;
4492 }
4493 
4494 
4495 static fssh_status_t
4496 query_close(struct file_descriptor *descriptor)
4497 {
4498 	struct fs_mount *mount = descriptor->u.mount;
4499 
4500 	FUNCTION(("query_close(descriptor = %p)\n", descriptor));
4501 
4502 	if (HAS_FS_MOUNT_CALL(mount, close_query))
4503 		return FS_MOUNT_CALL(mount, close_query, descriptor->cookie);
4504 
4505 	return FSSH_B_OK;
4506 }
4507 
4508 
4509 static void
4510 query_free_fd(struct file_descriptor *descriptor)
4511 {
4512 	struct fs_mount *mount = descriptor->u.mount;
4513 
4514 	if (mount != NULL) {
4515 		FS_MOUNT_CALL(mount, free_query_cookie, descriptor->cookie);
4516 		// ToDo: find a replacement ref_count object - perhaps the root dir?
4517 		//put_vnode(vnode);
4518 	}
4519 }
4520 
4521 
4522 static fssh_status_t
4523 query_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
4524 	fssh_size_t bufferSize, uint32_t *_count)
4525 {
4526 	struct fs_mount *mount = descriptor->u.mount;
4527 
4528 	if (HAS_FS_MOUNT_CALL(mount, read_query))
4529 		return FS_MOUNT_CALL(mount, read_query, descriptor->cookie, buffer, bufferSize, _count);
4530 
4531 	return FSSH_EOPNOTSUPP;
4532 }
4533 
4534 
4535 static fssh_status_t
4536 query_rewind(struct file_descriptor *descriptor)
4537 {
4538 	struct fs_mount *mount = descriptor->u.mount;
4539 
4540 	if (HAS_FS_MOUNT_CALL(mount, rewind_query))
4541 		return FS_MOUNT_CALL(mount, rewind_query, descriptor->cookie);
4542 
4543 	return FSSH_EOPNOTSUPP;
4544 }
4545 
4546 
4547 //	#pragma mark -
4548 //	General File System functions
4549 
4550 
4551 static fssh_dev_t
4552 fs_mount(char *path, const char *device, const char *fsName, uint32_t flags,
4553 	const char *args, bool kernel)
4554 {
4555 	struct fs_mount *mount;
4556 	fssh_status_t status = 0;
4557 
4558 	FUNCTION(("fs_mount: entry. path = '%s', fs_name = '%s'\n", path, fsName));
4559 
4560 	// The path is always safe, we just have to make sure that fsName is
4561 	// almost valid - we can't make any assumptions about args, though.
4562 	// A NULL fsName is OK, if a device was given and the FS is not virtual.
4563 	// We'll get it from the DDM later.
4564 	if (fsName == NULL) {
4565 		if (!device || flags & FSSH_B_MOUNT_VIRTUAL_DEVICE)
4566 			return FSSH_B_BAD_VALUE;
4567 	} else if (fsName[0] == '\0')
4568 		return FSSH_B_BAD_VALUE;
4569 
4570 	RecursiveLocker mountOpLocker(sMountOpLock);
4571 
4572 	// If the file system is not a "virtual" one, the device argument should
4573 	// point to a real file/device (if given at all).
4574 	// get the partition
4575 	KPath normalizedDevice;
4576 
4577 	if (!(flags & FSSH_B_MOUNT_VIRTUAL_DEVICE) && device) {
4578 		// normalize the device path
4579 //		status = normalizedDevice.SetTo(device, true);
4580 // NOTE: normalizing works only in our namespace.
4581 		status = normalizedDevice.SetTo(device, false);
4582 		if (status != FSSH_B_OK)
4583 			return status;
4584 
4585 		device = normalizedDevice.Path();
4586 			// correct path to file device
4587 	}
4588 
4589 	mount = (struct fs_mount *)malloc(sizeof(struct fs_mount));
4590 	if (mount == NULL)
4591 		return FSSH_B_NO_MEMORY;
4592 
4593 	mount->volume = (fssh_fs_volume*)malloc(sizeof(fssh_fs_volume));
4594 	if (mount->volume == NULL) {
4595 		free(mount);
4596 		return FSSH_B_NO_MEMORY;
4597 	}
4598 
4599 	list_init_etc(&mount->vnodes, fssh_offsetof(struct vnode, mount_link));
4600 
4601 	mount->fs_name = get_file_system_name(fsName);
4602 	if (mount->fs_name == NULL) {
4603 		status = FSSH_B_NO_MEMORY;
4604 		goto err1;
4605 	}
4606 
4607 	mount->device_name = fssh_strdup(device);
4608 		// "device" can be NULL
4609 
4610 	mount->fs = get_file_system(fsName);
4611 	if (mount->fs == NULL) {
4612 		status = FSSH_ENODEV;
4613 		goto err3;
4614 	}
4615 
4616 	fssh_recursive_lock_init(&mount->rlock, "mount rlock");
4617 
4618 	// initialize structure
4619 	mount->id = sNextMountID++;
4620 	mount->root_vnode = NULL;
4621 	mount->covers_vnode = NULL;
4622 	mount->unmounting = false;
4623 	mount->owns_file_device = false;
4624 
4625 	mount->volume->id = mount->id;
4626 	mount->volume->layer = 0;
4627 	mount->volume->private_volume = NULL;
4628 	mount->volume->ops = NULL;
4629 	mount->volume->sub_volume = NULL;
4630 	mount->volume->super_volume = NULL;
4631 
4632 	// insert mount struct into list before we call FS's mount() function
4633 	// so that vnodes can be created for this mount
4634 	fssh_mutex_lock(&sMountMutex);
4635 	hash_insert(sMountsTable, mount);
4636 	fssh_mutex_unlock(&sMountMutex);
4637 
4638 	fssh_vnode_id rootID;
4639 
4640 	if (!sRoot) {
4641 		// we haven't mounted anything yet
4642 		if (fssh_strcmp(path, "/") != 0) {
4643 			status = FSSH_B_ERROR;
4644 			goto err4;
4645 		}
4646 
4647 		status = mount->fs->mount(mount->volume, device, flags, args, &rootID);
4648 		if (status < 0) {
4649 			// ToDo: why should we hide the error code from the file system here?
4650 			//status = ERR_VFS_GENERAL;
4651 			goto err4;
4652 		}
4653 	} else {
4654 		struct vnode *coveredVnode;
4655 		status = path_to_vnode(path, true, &coveredVnode, NULL, kernel);
4656 		if (status < FSSH_B_OK)
4657 			goto err4;
4658 
4659 		// make sure covered_vnode is a DIR
4660 		struct fssh_stat coveredNodeStat;
4661 		status = FS_CALL(coveredVnode, read_stat, &coveredNodeStat);
4662 		if (status < FSSH_B_OK)
4663 			goto err4;
4664 
4665 		if (!FSSH_S_ISDIR(coveredNodeStat.fssh_st_mode)) {
4666 			status = FSSH_B_NOT_A_DIRECTORY;
4667 			goto err4;
4668 		}
4669 
4670 		if (coveredVnode->mount->root_vnode == coveredVnode) {
4671 			// this is already a mount point
4672 			status = FSSH_B_BUSY;
4673 			goto err4;
4674 		}
4675 
4676 		mount->covers_vnode = coveredVnode;
4677 
4678 		// mount it
4679 		status = mount->fs->mount(mount->volume, device, flags, args, &rootID);
4680 		if (status < FSSH_B_OK)
4681 			goto err5;
4682 	}
4683 
4684 	// the root node is supposed to be owned by the file system - it must
4685 	// exist at this point
4686 	mount->root_vnode = lookup_vnode(mount->id, rootID);
4687 	if (mount->root_vnode == NULL || mount->root_vnode->ref_count != 1) {
4688 		fssh_panic("fs_mount: file system does not own its root node!\n");
4689 		status = FSSH_B_ERROR;
4690 		goto err6;
4691 	}
4692 
4693 	// No race here, since fs_mount() is the only function changing
4694 	// covers_vnode (and holds sMountOpLock at that time).
4695 	fssh_mutex_lock(&sVnodeCoveredByMutex);
4696 	if (mount->covers_vnode)
4697 		mount->covers_vnode->covered_by = mount->root_vnode;
4698 	fssh_mutex_unlock(&sVnodeCoveredByMutex);
4699 
4700 	if (!sRoot)
4701 		sRoot = mount->root_vnode;
4702 
4703 	return mount->id;
4704 
4705 err6:
4706 	FS_MOUNT_CALL_NO_PARAMS(mount, unmount);
4707 err5:
4708 	if (mount->covers_vnode)
4709 		put_vnode(mount->covers_vnode);
4710 
4711 err4:
4712 	fssh_mutex_lock(&sMountMutex);
4713 	hash_remove(sMountsTable, mount);
4714 	fssh_mutex_unlock(&sMountMutex);
4715 
4716 	fssh_recursive_lock_destroy(&mount->rlock);
4717 
4718 	put_file_system(mount->fs);
4719 	free(mount->device_name);
4720 err3:
4721 	free(mount->fs_name);
4722 err1:
4723 	free(mount->volume);
4724 	free(mount);
4725 
4726 	return status;
4727 }
4728 
4729 
4730 static fssh_status_t
4731 fs_unmount(char *path, uint32_t flags, bool kernel)
4732 {
4733 	struct fs_mount *mount;
4734 	struct vnode *vnode;
4735 	fssh_status_t err;
4736 
4737 	FUNCTION(("vfs_unmount: entry. path = '%s', kernel %d\n", path, kernel));
4738 
4739 	err = path_to_vnode(path, true, &vnode, NULL, kernel);
4740 	if (err < 0)
4741 		return FSSH_B_ENTRY_NOT_FOUND;
4742 
4743 	RecursiveLocker mountOpLocker(sMountOpLock);
4744 
4745 	mount = find_mount(vnode->device);
4746 	if (!mount)
4747 		fssh_panic("vfs_unmount: find_mount() failed on root vnode @%p of mount\n", vnode);
4748 
4749 	if (mount->root_vnode != vnode) {
4750 		// not mountpoint
4751 		put_vnode(vnode);
4752 		return FSSH_B_BAD_VALUE;
4753 	}
4754 
4755 	// grab the vnode master mutex to keep someone from creating
4756 	// a vnode while we're figuring out if we can continue
4757 	fssh_mutex_lock(&sVnodeMutex);
4758 
4759 	bool disconnectedDescriptors = false;
4760 
4761 	while (true) {
4762 		bool busy = false;
4763 
4764 		// cycle through the list of vnodes associated with this mount and
4765 		// make sure all of them are not busy or have refs on them
4766 		vnode = NULL;
4767 		while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) {
4768 			// The root vnode ref_count needs to be 2 here: one for the file
4769 			// system, one from the path_to_vnode() call above
4770 			if (vnode->busy
4771 				|| ((vnode->ref_count != 0 && mount->root_vnode != vnode)
4772 					|| (vnode->ref_count != 2 && mount->root_vnode == vnode))) {
4773 				// there are still vnodes in use on this mount, so we cannot
4774 				// unmount yet
4775 				busy = true;
4776 				break;
4777 			}
4778 		}
4779 
4780 		if (!busy)
4781 			break;
4782 
4783 		if ((flags & FSSH_B_FORCE_UNMOUNT) == 0) {
4784 			fssh_mutex_unlock(&sVnodeMutex);
4785 			put_vnode(mount->root_vnode);
4786 
4787 			return FSSH_B_BUSY;
4788 		}
4789 
4790 		if (disconnectedDescriptors) {
4791 			// wait a bit until the last access is finished, and then try again
4792 			fssh_mutex_unlock(&sVnodeMutex);
4793 			fssh_snooze(100000);
4794 			// TODO: if there is some kind of bug that prevents the ref counts
4795 			//	from getting back to zero, this will fall into an endless loop...
4796 			fssh_mutex_lock(&sVnodeMutex);
4797 			continue;
4798 		}
4799 
4800 		// the file system is still busy - but we're forced to unmount it,
4801 		// so let's disconnect all open file descriptors
4802 
4803 		mount->unmounting = true;
4804 			// prevent new vnodes from being created
4805 
4806 		fssh_mutex_unlock(&sVnodeMutex);
4807 
4808 		disconnect_mount_or_vnode_fds(mount, NULL);
4809 		disconnectedDescriptors = true;
4810 
4811 		fssh_mutex_lock(&sVnodeMutex);
4812 	}
4813 
4814 	// we can safely continue, mark all of the vnodes busy and this mount
4815 	// structure in unmounting state
4816 	mount->unmounting = true;
4817 
4818 	while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) {
4819 		vnode->busy = true;
4820 
4821 		if (vnode->ref_count == 0) {
4822 			// this vnode has been unused before
4823 			list_remove_item(&sUnusedVnodeList, vnode);
4824 			sUnusedVnodes--;
4825 		}
4826 	}
4827 
4828 	// The ref_count of the root node is 2 at this point, see above why this is
4829 	mount->root_vnode->ref_count -= 2;
4830 
4831 	fssh_mutex_unlock(&sVnodeMutex);
4832 
4833 	fssh_mutex_lock(&sVnodeCoveredByMutex);
4834 	mount->covers_vnode->covered_by = NULL;
4835 	fssh_mutex_unlock(&sVnodeCoveredByMutex);
4836 	put_vnode(mount->covers_vnode);
4837 
4838 	// Free all vnodes associated with this mount.
4839 	// They will be removed from the mount list by free_vnode(), so
4840 	// we don't have to do this.
4841 	while ((vnode = (struct vnode *)list_get_first_item(&mount->vnodes)) != NULL) {
4842 		free_vnode(vnode, false);
4843 	}
4844 
4845 	// remove the mount structure from the hash table
4846 	fssh_mutex_lock(&sMountMutex);
4847 	hash_remove(sMountsTable, mount);
4848 	fssh_mutex_unlock(&sMountMutex);
4849 
4850 	mountOpLocker.Unlock();
4851 
4852 	FS_MOUNT_CALL_NO_PARAMS(mount, unmount);
4853 
4854 	// release the file system
4855 	put_file_system(mount->fs);
4856 
4857 	free(mount->device_name);
4858 	free(mount->fs_name);
4859 	free(mount);
4860 
4861 	return FSSH_B_OK;
4862 }
4863 
4864 
4865 static fssh_status_t
4866 fs_sync(fssh_dev_t device)
4867 {
4868 	struct fs_mount *mount;
4869 	fssh_status_t status = get_mount(device, &mount);
4870 	if (status < FSSH_B_OK)
4871 		return status;
4872 
4873 	fssh_mutex_lock(&sMountMutex);
4874 
4875 	if (HAS_FS_MOUNT_CALL(mount, sync))
4876 		status = FS_MOUNT_CALL_NO_PARAMS(mount, sync);
4877 
4878 	fssh_mutex_unlock(&sMountMutex);
4879 
4880 	struct vnode *previousVnode = NULL;
4881 	while (true) {
4882 		// synchronize access to vnode list
4883 		fssh_recursive_lock_lock(&mount->rlock);
4884 
4885 		struct vnode *vnode = (struct vnode *)list_get_next_item(&mount->vnodes,
4886 			previousVnode);
4887 
4888 		fssh_vnode_id id = -1;
4889 		if (vnode != NULL)
4890 			id = vnode->id;
4891 
4892 		fssh_recursive_lock_unlock(&mount->rlock);
4893 
4894 		if (vnode == NULL)
4895 			break;
4896 
4897 		// acquire a reference to the vnode
4898 
4899 		if (get_vnode(mount->id, id, &vnode, true) == FSSH_B_OK) {
4900 			if (previousVnode != NULL)
4901 				put_vnode(previousVnode);
4902 
4903 			if (HAS_FS_CALL(vnode, fsync))
4904 				FS_CALL_NO_PARAMS(vnode, fsync);
4905 
4906 			// the next vnode might change until we lock the vnode list again,
4907 			// but this vnode won't go away since we keep a reference to it.
4908 			previousVnode = vnode;
4909 		} else {
4910 			fssh_dprintf("syncing of mount %d stopped due to vnode %"
4911 				FSSH_B_PRIdINO ".\n", (int)mount->id, id);
4912 			break;
4913 		}
4914 	}
4915 
4916 	if (previousVnode != NULL)
4917 		put_vnode(previousVnode);
4918 
4919 	put_mount(mount);
4920 	return status;
4921 }
4922 
4923 
4924 static fssh_status_t
4925 fs_read_info(fssh_dev_t device, struct fssh_fs_info *info)
4926 {
4927 	struct fs_mount *mount;
4928 	fssh_status_t status = get_mount(device, &mount);
4929 	if (status < FSSH_B_OK)
4930 		return status;
4931 
4932 	fssh_memset(info, 0, sizeof(struct fssh_fs_info));
4933 
4934 	if (HAS_FS_MOUNT_CALL(mount, read_fs_info))
4935 		status = FS_MOUNT_CALL(mount, read_fs_info, info);
4936 
4937 	// fill in info the file system doesn't (have to) know about
4938 	if (status == FSSH_B_OK) {
4939 		info->dev = mount->id;
4940 		info->root = mount->root_vnode->id;
4941 		fssh_strlcpy(info->fsh_name, mount->fs_name, sizeof(info->fsh_name));
4942 		if (mount->device_name != NULL) {
4943 			fssh_strlcpy(info->device_name, mount->device_name,
4944 				sizeof(info->device_name));
4945 		}
4946 	}
4947 
4948 	// if the call is not supported by the file system, there are still
4949 	// the parts that we filled out ourselves
4950 
4951 	put_mount(mount);
4952 	return status;
4953 }
4954 
4955 
4956 static fssh_status_t
4957 fs_write_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask)
4958 {
4959 	struct fs_mount *mount;
4960 	fssh_status_t status = get_mount(device, &mount);
4961 	if (status < FSSH_B_OK)
4962 		return status;
4963 
4964 	if (HAS_FS_MOUNT_CALL(mount, write_fs_info))
4965 		status = FS_MOUNT_CALL(mount, write_fs_info, info, mask);
4966 	else
4967 		status = FSSH_EROFS;
4968 
4969 	put_mount(mount);
4970 	return status;
4971 }
4972 
4973 
4974 static fssh_dev_t
4975 fs_next_device(int32_t *_cookie)
4976 {
4977 	struct fs_mount *mount = NULL;
4978 	fssh_dev_t device = *_cookie;
4979 
4980 	fssh_mutex_lock(&sMountMutex);
4981 
4982 	// Since device IDs are assigned sequentially, this algorithm
4983 	// does work good enough. It makes sure that the device list
4984 	// returned is sorted, and that no device is skipped when an
4985 	// already visited device got unmounted.
4986 
4987 	while (device < sNextMountID) {
4988 		mount = find_mount(device++);
4989 		if (mount != NULL && mount->volume->private_volume != NULL)
4990 			break;
4991 	}
4992 
4993 	*_cookie = device;
4994 
4995 	if (mount != NULL)
4996 		device = mount->id;
4997 	else
4998 		device = FSSH_B_BAD_VALUE;
4999 
5000 	fssh_mutex_unlock(&sMountMutex);
5001 
5002 	return device;
5003 }
5004 
5005 
5006 static fssh_status_t
5007 get_cwd(char *buffer, fssh_size_t size, bool kernel)
5008 {
5009 	// Get current working directory from io context
5010 	struct io_context *context = get_current_io_context(kernel);
5011 	fssh_status_t status;
5012 
5013 	FUNCTION(("vfs_get_cwd: buf %p, size %ld\n", buffer, size));
5014 
5015 	fssh_mutex_lock(&context->io_mutex);
5016 
5017 	if (context->cwd)
5018 		status = dir_vnode_to_path(context->cwd, buffer, size);
5019 	else
5020 		status = FSSH_B_ERROR;
5021 
5022 	fssh_mutex_unlock(&context->io_mutex);
5023 	return status;
5024 }
5025 
5026 
5027 static fssh_status_t
5028 set_cwd(int fd, char *path, bool kernel)
5029 {
5030 	struct io_context *context;
5031 	struct vnode *vnode = NULL;
5032 	struct vnode *oldDirectory;
5033 	struct fssh_stat stat;
5034 	fssh_status_t status;
5035 
5036 	FUNCTION(("set_cwd: path = \'%s\'\n", path));
5037 
5038 	// Get vnode for passed path, and bail if it failed
5039 	status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel);
5040 	if (status < 0)
5041 		return status;
5042 
5043 	status = FS_CALL(vnode, read_stat, &stat);
5044 	if (status < 0)
5045 		goto err;
5046 
5047 	if (!FSSH_S_ISDIR(stat.fssh_st_mode)) {
5048 		// nope, can't cwd to here
5049 		status = FSSH_B_NOT_A_DIRECTORY;
5050 		goto err;
5051 	}
5052 
5053 	// Get current io context and lock
5054 	context = get_current_io_context(kernel);
5055 	fssh_mutex_lock(&context->io_mutex);
5056 
5057 	// save the old current working directory first
5058 	oldDirectory = context->cwd;
5059 	context->cwd = vnode;
5060 
5061 	fssh_mutex_unlock(&context->io_mutex);
5062 
5063 	if (oldDirectory)
5064 		put_vnode(oldDirectory);
5065 
5066 	return FSSH_B_NO_ERROR;
5067 
5068 err:
5069 	put_vnode(vnode);
5070 	return status;
5071 }
5072 
5073 
5074 //	#pragma mark -
5075 //	Calls from within the kernel
5076 
5077 
5078 fssh_dev_t
5079 _kern_mount(const char *path, const char *device, const char *fsName,
5080 	uint32_t flags, const char *args, fssh_size_t argsLength)
5081 {
5082 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5083 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5084 		return FSSH_B_NO_MEMORY;
5085 
5086 	return fs_mount(pathBuffer.LockBuffer(), device, fsName, flags, args, true);
5087 }
5088 
5089 
5090 fssh_status_t
5091 _kern_unmount(const char *path, uint32_t flags)
5092 {
5093 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5094 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5095 		return FSSH_B_NO_MEMORY;
5096 
5097 	return fs_unmount(pathBuffer.LockBuffer(), flags, true);
5098 }
5099 
5100 
5101 fssh_status_t
5102 _kern_read_fs_info(fssh_dev_t device, struct fssh_fs_info *info)
5103 {
5104 	if (info == NULL)
5105 		return FSSH_B_BAD_VALUE;
5106 
5107 	return fs_read_info(device, info);
5108 }
5109 
5110 
5111 fssh_status_t
5112 _kern_write_fs_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask)
5113 {
5114 	if (info == NULL)
5115 		return FSSH_B_BAD_VALUE;
5116 
5117 	return fs_write_info(device, info, mask);
5118 }
5119 
5120 
5121 fssh_status_t
5122 _kern_sync(void)
5123 {
5124 	// Note: _kern_sync() is also called from _user_sync()
5125 	int32_t cookie = 0;
5126 	fssh_dev_t device;
5127 	while ((device = fs_next_device(&cookie)) >= 0) {
5128 		fssh_status_t status = fs_sync(device);
5129 		if (status != FSSH_B_OK && status != FSSH_B_BAD_VALUE)
5130 			fssh_dprintf("sync: device %d couldn't sync: %s\n", (int)device, fssh_strerror(status));
5131 	}
5132 
5133 	return FSSH_B_OK;
5134 }
5135 
5136 
5137 fssh_dev_t
5138 _kern_next_device(int32_t *_cookie)
5139 {
5140 	return fs_next_device(_cookie);
5141 }
5142 
5143 
5144 int
5145 _kern_open_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int openMode, int perms)
5146 {
5147 	if (openMode & FSSH_O_CREAT)
5148 		return file_create_entry_ref(device, inode, name, openMode, perms, true);
5149 
5150 	return file_open_entry_ref(device, inode, name, openMode, true);
5151 }
5152 
5153 
5154 /**	\brief Opens a node specified by a FD + path pair.
5155  *
5156  *	At least one of \a fd and \a path must be specified.
5157  *	If only \a fd is given, the function opens the node identified by this
5158  *	FD. If only a path is given, this path is opened. If both are given and
5159  *	the path is absolute, \a fd is ignored; a relative path is reckoned off
5160  *	of the directory (!) identified by \a fd.
5161  *
5162  *	\param fd The FD. May be < 0.
5163  *	\param path The absolute or relative path. May be \c NULL.
5164  *	\param openMode The open mode.
5165  *	\return A FD referring to the newly opened node, or an error code,
5166  *			if an error occurs.
5167  */
5168 
5169 int
5170 _kern_open(int fd, const char *path, int openMode, int perms)
5171 {
5172 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5173 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5174 		return FSSH_B_NO_MEMORY;
5175 
5176 	if (openMode & FSSH_O_CREAT)
5177 		return file_create(fd, pathBuffer.LockBuffer(), openMode, perms, true);
5178 
5179 	return file_open(fd, pathBuffer.LockBuffer(), openMode, true);
5180 }
5181 
5182 
5183 /**	\brief Opens a directory specified by entry_ref or node_ref.
5184  *
5185  *	The supplied name may be \c NULL, in which case directory identified
5186  *	by \a device and \a inode will be opened. Otherwise \a device and
5187  *	\a inode identify the parent directory of the directory to be opened
5188  *	and \a name its entry name.
5189  *
5190  *	\param device If \a name is specified the ID of the device the parent
5191  *		   directory of the directory to be opened resides on, otherwise
5192  *		   the device of the directory itself.
5193  *	\param inode If \a name is specified the node ID of the parent
5194  *		   directory of the directory to be opened, otherwise node ID of the
5195  *		   directory itself.
5196  *	\param name The entry name of the directory to be opened. If \c NULL,
5197  *		   the \a device + \a inode pair identify the node to be opened.
5198  *	\return The FD of the newly opened directory or an error code, if
5199  *			something went wrong.
5200  */
5201 
5202 int
5203 _kern_open_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name)
5204 {
5205 	return dir_open_entry_ref(device, inode, name, true);
5206 }
5207 
5208 
5209 /**	\brief Opens a directory specified by a FD + path pair.
5210  *
5211  *	At least one of \a fd and \a path must be specified.
5212  *	If only \a fd is given, the function opens the directory identified by this
5213  *	FD. If only a path is given, this path is opened. If both are given and
5214  *	the path is absolute, \a fd is ignored; a relative path is reckoned off
5215  *	of the directory (!) identified by \a fd.
5216  *
5217  *	\param fd The FD. May be < 0.
5218  *	\param path The absolute or relative path. May be \c NULL.
5219  *	\return A FD referring to the newly opened directory, or an error code,
5220  *			if an error occurs.
5221  */
5222 
5223 int
5224 _kern_open_dir(int fd, const char *path)
5225 {
5226 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5227 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5228 		return FSSH_B_NO_MEMORY;
5229 
5230 	return dir_open(fd, pathBuffer.LockBuffer(), true);
5231 }
5232 
5233 
5234 fssh_status_t
5235 _kern_fcntl(int fd, int op, uint32_t argument)
5236 {
5237 	return common_fcntl(fd, op, argument, true);
5238 }
5239 
5240 
5241 fssh_status_t
5242 _kern_fsync(int fd)
5243 {
5244 	return common_sync(fd, true);
5245 }
5246 
5247 
5248 fssh_status_t
5249 _kern_lock_node(int fd)
5250 {
5251 	return common_lock_node(fd, true);
5252 }
5253 
5254 
5255 fssh_status_t
5256 _kern_unlock_node(int fd)
5257 {
5258 	return common_unlock_node(fd, true);
5259 }
5260 
5261 
5262 fssh_status_t
5263 _kern_create_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int perms)
5264 {
5265 	return dir_create_entry_ref(device, inode, name, perms, true);
5266 }
5267 
5268 
5269 /**	\brief Creates a directory specified by a FD + path pair.
5270  *
5271  *	\a path must always be specified (it contains the name of the new directory
5272  *	at least). If only a path is given, this path identifies the location at
5273  *	which the directory shall be created. If both \a fd and \a path are given and
5274  *	the path is absolute, \a fd is ignored; a relative path is reckoned off
5275  *	of the directory (!) identified by \a fd.
5276  *
5277  *	\param fd The FD. May be < 0.
5278  *	\param path The absolute or relative path. Must not be \c NULL.
5279  *	\param perms The access permissions the new directory shall have.
5280  *	\return \c FSSH_B_OK, if the directory has been created successfully, another
5281  *			error code otherwise.
5282  */
5283 
5284 fssh_status_t
5285 _kern_create_dir(int fd, const char *path, int perms)
5286 {
5287 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5288 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5289 		return FSSH_B_NO_MEMORY;
5290 
5291 	return dir_create(fd, pathBuffer.LockBuffer(), perms, true);
5292 }
5293 
5294 
5295 fssh_status_t
5296 _kern_remove_dir(int fd, const char *path)
5297 {
5298 	if (path) {
5299 		KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5300 		if (pathBuffer.InitCheck() != FSSH_B_OK)
5301 			return FSSH_B_NO_MEMORY;
5302 
5303 		return dir_remove(fd, pathBuffer.LockBuffer(), true);
5304 	}
5305 
5306 	return dir_remove(fd, NULL, true);
5307 }
5308 
5309 
5310 /**	\brief Reads the contents of a symlink referred to by a FD + path pair.
5311  *
5312  *	At least one of \a fd and \a path must be specified.
5313  *	If only \a fd is given, the function the symlink to be read is the node
5314  *	identified by this FD. If only a path is given, this path identifies the
5315  *	symlink to be read. If both are given and the path is absolute, \a fd is
5316  *	ignored; a relative path is reckoned off of the directory (!) identified
5317  *	by \a fd.
5318  *	If this function fails with FSSH_B_BUFFER_OVERFLOW, the \a _bufferSize pointer
5319  *	will still be updated to reflect the required buffer size.
5320  *
5321  *	\param fd The FD. May be < 0.
5322  *	\param path The absolute or relative path. May be \c NULL.
5323  *	\param buffer The buffer into which the contents of the symlink shall be
5324  *		   written.
5325  *	\param _bufferSize A pointer to the size of the supplied buffer.
5326  *	\return The length of the link on success or an appropriate error code
5327  */
5328 
5329 fssh_status_t
5330 _kern_read_link(int fd, const char *path, char *buffer, fssh_size_t *_bufferSize)
5331 {
5332 	if (path) {
5333 		KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5334 		if (pathBuffer.InitCheck() != FSSH_B_OK)
5335 			return FSSH_B_NO_MEMORY;
5336 
5337 		return common_read_link(fd, pathBuffer.LockBuffer(),
5338 			buffer, _bufferSize, true);
5339 	}
5340 
5341 	return common_read_link(fd, NULL, buffer, _bufferSize, true);
5342 }
5343 
5344 
5345 /**	\brief Creates a symlink specified by a FD + path pair.
5346  *
5347  *	\a path must always be specified (it contains the name of the new symlink
5348  *	at least). If only a path is given, this path identifies the location at
5349  *	which the symlink shall be created. If both \a fd and \a path are given and
5350  *	the path is absolute, \a fd is ignored; a relative path is reckoned off
5351  *	of the directory (!) identified by \a fd.
5352  *
5353  *	\param fd The FD. May be < 0.
5354  *	\param toPath The absolute or relative path. Must not be \c NULL.
5355  *	\param mode The access permissions the new symlink shall have.
5356  *	\return \c FSSH_B_OK, if the symlink has been created successfully, another
5357  *			error code otherwise.
5358  */
5359 
5360 fssh_status_t
5361 _kern_create_symlink(int fd, const char *path, const char *toPath, int mode)
5362 {
5363 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5364 	KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
5365 	if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK)
5366 		return FSSH_B_NO_MEMORY;
5367 
5368 	char *toBuffer = toPathBuffer.LockBuffer();
5369 
5370 	fssh_status_t status = check_path(toBuffer);
5371 	if (status < FSSH_B_OK)
5372 		return status;
5373 
5374 	return common_create_symlink(fd, pathBuffer.LockBuffer(),
5375 		toBuffer, mode, true);
5376 }
5377 
5378 
5379 fssh_status_t
5380 _kern_create_link(const char *path, const char *toPath)
5381 {
5382 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5383 	KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
5384 	if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK)
5385 		return FSSH_B_NO_MEMORY;
5386 
5387 	return common_create_link(pathBuffer.LockBuffer(),
5388 		toPathBuffer.LockBuffer(), true);
5389 }
5390 
5391 
5392 /**	\brief Removes an entry specified by a FD + path pair from its directory.
5393  *
5394  *	\a path must always be specified (it contains at least the name of the entry
5395  *	to be deleted). If only a path is given, this path identifies the entry
5396  *	directly. If both \a fd and \a path are given and the path is absolute,
5397  *	\a fd is ignored; a relative path is reckoned off of the directory (!)
5398  *	identified by \a fd.
5399  *
5400  *	\param fd The FD. May be < 0.
5401  *	\param path The absolute or relative path. Must not be \c NULL.
5402  *	\return \c FSSH_B_OK, if the entry has been removed successfully, another
5403  *			error code otherwise.
5404  */
5405 
5406 fssh_status_t
5407 _kern_unlink(int fd, const char *path)
5408 {
5409 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5410 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5411 		return FSSH_B_NO_MEMORY;
5412 
5413 	return common_unlink(fd, pathBuffer.LockBuffer(), true);
5414 }
5415 
5416 
5417 /**	\brief Moves an entry specified by a FD + path pair to a an entry specified
5418  *		   by another FD + path pair.
5419  *
5420  *	\a oldPath and \a newPath must always be specified (they contain at least
5421  *	the name of the entry). If only a path is given, this path identifies the
5422  *	entry directly. If both a FD and a path are given and the path is absolute,
5423  *	the FD is ignored; a relative path is reckoned off of the directory (!)
5424  *	identified by the respective FD.
5425  *
5426  *	\param oldFD The FD of the old location. May be < 0.
5427  *	\param oldPath The absolute or relative path of the old location. Must not
5428  *		   be \c NULL.
5429  *	\param newFD The FD of the new location. May be < 0.
5430  *	\param newPath The absolute or relative path of the new location. Must not
5431  *		   be \c NULL.
5432  *	\return \c FSSH_B_OK, if the entry has been moved successfully, another
5433  *			error code otherwise.
5434  */
5435 
5436 fssh_status_t
5437 _kern_rename(int oldFD, const char *oldPath, int newFD, const char *newPath)
5438 {
5439 	KPath oldPathBuffer(oldPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
5440 	KPath newPathBuffer(newPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
5441 	if (oldPathBuffer.InitCheck() != FSSH_B_OK || newPathBuffer.InitCheck() != FSSH_B_OK)
5442 		return FSSH_B_NO_MEMORY;
5443 
5444 	return common_rename(oldFD, oldPathBuffer.LockBuffer(),
5445 		newFD, newPathBuffer.LockBuffer(), true);
5446 }
5447 
5448 
5449 fssh_status_t
5450 _kern_access(const char *path, int mode)
5451 {
5452 	KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5453 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5454 		return FSSH_B_NO_MEMORY;
5455 
5456 	return common_access(pathBuffer.LockBuffer(), mode, true);
5457 }
5458 
5459 
5460 /**	\brief Reads stat data of an entity specified by a FD + path pair.
5461  *
5462  *	If only \a fd is given, the stat operation associated with the type
5463  *	of the FD (node, attr, attr dir etc.) is performed. If only \a path is
5464  *	given, this path identifies the entry for whose node to retrieve the
5465  *	stat data. If both \a fd and \a path are given and the path is absolute,
5466  *	\a fd is ignored; a relative path is reckoned off of the directory (!)
5467  *	identified by \a fd and specifies the entry whose stat data shall be
5468  *	retrieved.
5469  *
5470  *	\param fd The FD. May be < 0.
5471  *	\param path The absolute or relative path. Must not be \c NULL.
5472  *	\param traverseLeafLink If \a path is given, \c true specifies that the
5473  *		   function shall not stick to symlinks, but traverse them.
5474  *	\param stat The buffer the stat data shall be written into.
5475  *	\param statSize The size of the supplied stat buffer.
5476  *	\return \c FSSH_B_OK, if the the stat data have been read successfully, another
5477  *			error code otherwise.
5478  */
5479 
5480 fssh_status_t
5481 _kern_read_stat(int fd, const char *path, bool traverseLeafLink,
5482 	fssh_struct_stat *stat, fssh_size_t statSize)
5483 {
5484 	fssh_struct_stat completeStat;
5485 	fssh_struct_stat *originalStat = NULL;
5486 	fssh_status_t status;
5487 
5488 	if (statSize > sizeof(fssh_struct_stat))
5489 		return FSSH_B_BAD_VALUE;
5490 
5491 	// this supports different stat extensions
5492 	if (statSize < sizeof(fssh_struct_stat)) {
5493 		originalStat = stat;
5494 		stat = &completeStat;
5495 	}
5496 
5497 	if (path) {
5498 		// path given: get the stat of the node referred to by (fd, path)
5499 		KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5500 		if (pathBuffer.InitCheck() != FSSH_B_OK)
5501 			return FSSH_B_NO_MEMORY;
5502 
5503 		status = common_path_read_stat(fd, pathBuffer.LockBuffer(),
5504 			traverseLeafLink, stat, true);
5505 	} else {
5506 		// no path given: get the FD and use the FD operation
5507 		struct file_descriptor *descriptor
5508 			= get_fd(get_current_io_context(true), fd);
5509 		if (descriptor == NULL)
5510 			return FSSH_B_FILE_ERROR;
5511 
5512 		if (descriptor->ops->fd_read_stat)
5513 			status = descriptor->ops->fd_read_stat(descriptor, stat);
5514 		else
5515 			status = FSSH_EOPNOTSUPP;
5516 
5517 		put_fd(descriptor);
5518 	}
5519 
5520 	if (status == FSSH_B_OK && originalStat != NULL)
5521 		fssh_memcpy(originalStat, stat, statSize);
5522 
5523 	return status;
5524 }
5525 
5526 
5527 /**	\brief Writes stat data of an entity specified by a FD + path pair.
5528  *
5529  *	If only \a fd is given, the stat operation associated with the type
5530  *	of the FD (node, attr, attr dir etc.) is performed. If only \a path is
5531  *	given, this path identifies the entry for whose node to write the
5532  *	stat data. If both \a fd and \a path are given and the path is absolute,
5533  *	\a fd is ignored; a relative path is reckoned off of the directory (!)
5534  *	identified by \a fd and specifies the entry whose stat data shall be
5535  *	written.
5536  *
5537  *	\param fd The FD. May be < 0.
5538  *	\param path The absolute or relative path. Must not be \c NULL.
5539  *	\param traverseLeafLink If \a path is given, \c true specifies that the
5540  *		   function shall not stick to symlinks, but traverse them.
5541  *	\param stat The buffer containing the stat data to be written.
5542  *	\param statSize The size of the supplied stat buffer.
5543  *	\param statMask A mask specifying which parts of the stat data shall be
5544  *		   written.
5545  *	\return \c FSSH_B_OK, if the the stat data have been written successfully,
5546  *			another error code otherwise.
5547  */
5548 
5549 fssh_status_t
5550 _kern_write_stat(int fd, const char *path, bool traverseLeafLink,
5551 	const fssh_struct_stat *stat, fssh_size_t statSize, int statMask)
5552 {
5553 	fssh_struct_stat completeStat;
5554 
5555 	if (statSize > sizeof(fssh_struct_stat))
5556 		return FSSH_B_BAD_VALUE;
5557 
5558 	// this supports different stat extensions
5559 	if (statSize < sizeof(fssh_struct_stat)) {
5560 		fssh_memset((uint8_t *)&completeStat + statSize, 0, sizeof(fssh_struct_stat) - statSize);
5561 		fssh_memcpy(&completeStat, stat, statSize);
5562 		stat = &completeStat;
5563 	}
5564 
5565 	fssh_status_t status;
5566 
5567 	if (path) {
5568 		// path given: write the stat of the node referred to by (fd, path)
5569 		KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5570 		if (pathBuffer.InitCheck() != FSSH_B_OK)
5571 			return FSSH_B_NO_MEMORY;
5572 
5573 		status = common_path_write_stat(fd, pathBuffer.LockBuffer(),
5574 			traverseLeafLink, stat, statMask, true);
5575 	} else {
5576 		// no path given: get the FD and use the FD operation
5577 		struct file_descriptor *descriptor
5578 			= get_fd(get_current_io_context(true), fd);
5579 		if (descriptor == NULL)
5580 			return FSSH_B_FILE_ERROR;
5581 
5582 		if (descriptor->ops->fd_write_stat)
5583 			status = descriptor->ops->fd_write_stat(descriptor, stat, statMask);
5584 		else
5585 			status = FSSH_EOPNOTSUPP;
5586 
5587 		put_fd(descriptor);
5588 	}
5589 
5590 	return status;
5591 }
5592 
5593 
5594 int
5595 _kern_open_attr_dir(int fd, const char *path)
5596 {
5597 	KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
5598 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5599 		return FSSH_B_NO_MEMORY;
5600 
5601 	if (path != NULL)
5602 		pathBuffer.SetTo(path);
5603 
5604 	return attr_dir_open(fd, path ? pathBuffer.LockBuffer() : NULL, true);
5605 }
5606 
5607 
5608 int
5609 _kern_create_attr(int fd, const char *name, uint32_t type, int openMode)
5610 {
5611 	return attr_create(fd, name, type, openMode, true);
5612 }
5613 
5614 
5615 int
5616 _kern_open_attr(int fd, const char *name, int openMode)
5617 {
5618 	return attr_open(fd, name, openMode, true);
5619 }
5620 
5621 
5622 fssh_status_t
5623 _kern_remove_attr(int fd, const char *name)
5624 {
5625 	return attr_remove(fd, name, true);
5626 }
5627 
5628 
5629 fssh_status_t
5630 _kern_rename_attr(int fromFile, const char *fromName, int toFile, const char *toName)
5631 {
5632 	return attr_rename(fromFile, fromName, toFile, toName, true);
5633 }
5634 
5635 
5636 int
5637 _kern_open_index_dir(fssh_dev_t device)
5638 {
5639 	return index_dir_open(device, true);
5640 }
5641 
5642 
5643 fssh_status_t
5644 _kern_create_index(fssh_dev_t device, const char *name, uint32_t type, uint32_t flags)
5645 {
5646 	return index_create(device, name, type, flags, true);
5647 }
5648 
5649 
5650 fssh_status_t
5651 _kern_read_index_stat(fssh_dev_t device, const char *name, fssh_struct_stat *stat)
5652 {
5653 	return index_name_read_stat(device, name, stat, true);
5654 }
5655 
5656 
5657 fssh_status_t
5658 _kern_remove_index(fssh_dev_t device, const char *name)
5659 {
5660 	return index_remove(device, name, true);
5661 }
5662 
5663 
5664 fssh_status_t
5665 _kern_getcwd(char *buffer, fssh_size_t size)
5666 {
5667 	TRACE(("_kern_getcwd: buf %p, %ld\n", buffer, size));
5668 
5669 	// Call vfs to get current working directory
5670 	return get_cwd(buffer, size, true);
5671 }
5672 
5673 
5674 fssh_status_t
5675 _kern_setcwd(int fd, const char *path)
5676 {
5677 	KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
5678 	if (pathBuffer.InitCheck() != FSSH_B_OK)
5679 		return FSSH_B_NO_MEMORY;
5680 
5681 	if (path != NULL)
5682 		pathBuffer.SetTo(path);
5683 
5684 	return set_cwd(fd, path != NULL ? pathBuffer.LockBuffer() : NULL, true);
5685 }
5686 
5687 
5688 fssh_status_t
5689 _kern_initialize_volume(const char* fsName, const char *partition,
5690 	const char *name, const char *parameters)
5691 {
5692 	if (!fsName || ! partition)
5693 		return FSSH_B_BAD_VALUE;
5694 
5695 	// The partition argument should point to a real file/device.
5696 
5697 	// open partition
5698 	int fd = fssh_open(partition, FSSH_O_RDWR);
5699 	if (fd < 0)
5700 		return fssh_errno;
5701 
5702 	// get the file system module
5703 	fssh_file_system_module_info* fsModule = get_file_system(fsName);
5704 	if (fsModule == NULL) {
5705 		fssh_close(fd);
5706 		return FSSH_ENODEV;
5707 	}
5708 
5709 	// initialize
5710 	fssh_status_t status;
5711 	if (fsModule->initialize) {
5712 		status = (*fsModule->initialize)(fd, -1, name, parameters, 0, -1);
5713 			// We've got no partition or job IDs -- the FS will hopefully
5714 			// ignore that.
5715 			// TODO: Get the actual size!
5716 	} else
5717 		status = FSSH_B_NOT_SUPPORTED;
5718 
5719 	// put the file system module, close partition
5720 	put_file_system(fsModule);
5721 	fssh_close(fd);
5722 
5723 	return status;
5724 }
5725 
5726 
5727 fssh_status_t
5728 _kern_entry_ref_to_path(fssh_dev_t device, fssh_ino_t inode, const char *leaf,
5729 	char* path, fssh_size_t pathLength)
5730 {
5731 	return vfs_entry_ref_to_path(device, inode, leaf, true, path, pathLength);
5732 }
5733 
5734 
5735 int
5736 _kern_open_query(fssh_dev_t device, const char *query, fssh_size_t queryLength,
5737 	uint32_t flags, fssh_port_id port, int32_t token)
5738 {
5739 	return query_open(device, query, flags, port, token, false);
5740 }
5741 
5742 
5743 }	// namespace FSShell
5744 
5745 
5746 #include "vfs_request_io.cpp"
5747