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