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