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