Fix FreeBSD build.
[haiku.git] / src / tools / fs_shell / vfs.cpp
blob5137ddd3643cdada68a5ba36f373f7dd0ba1d1d0
1 /*
2 * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6 * Distributed under the terms of the NewOS License.
7 */
9 /*! Virtual File System and File System Interface Layer */
11 #include "vfs.h"
13 #include <new>
14 #include <stdlib.h>
15 #include <string.h>
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"
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
46 #define ADD_DEBUGGER_COMMANDS
48 #define ASSERT_LOCKED_MUTEX(x)
49 #define ASSERT(x)
51 namespace FSShell {
54 #define HAS_FS_CALL(vnode, op) (vnode->ops->op != NULL)
55 #define HAS_FS_MOUNT_CALL(mount, op) (mount->volume->ops->op != NULL)
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)
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.
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;
93 struct vnode_hash_key {
94 fssh_mount_id device;
95 fssh_vnode_id vnode;
98 /** \brief Structure to manage a mounted file system
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).
110 struct fs_mount {
111 struct fs_mount *next;
112 fssh_file_system_module_info *fs;
113 fssh_mount_id id;
114 fssh_fs_volume *volume;
115 char *device_name;
116 char *fs_name;
117 fssh_recursive_lock rlock; // guards the vnodes list
118 struct vnode *root_vnode;
119 struct vnode *covers_vnode;
120 struct list vnodes;
121 bool unmounting;
122 bool owns_file_device;
125 static fssh_mutex sFileSystemsMutex;
127 /** \brief Guards sMountsTable.
129 * The holder is allowed to read/write access the sMountsTable.
130 * Manipulation of the fs_mount structures themselves
131 * (and their destruction) requires different locks though.
133 static fssh_mutex sMountMutex;
135 /** \brief Guards mount/unmount operations.
137 * The fs_mount() and fs_unmount() hold the lock during their whole operation.
138 * That is locking the lock ensures that no FS is mounted/unmounted. In
139 * particular this means that
140 * - sMountsTable will not be modified,
141 * - the fields immutable after initialization of the fs_mount structures in
142 * sMountsTable will not be modified,
143 * - vnode::covered_by of any vnode in sVnodeTable will not be modified.
145 * The thread trying to lock the lock must not hold sVnodeMutex or
146 * sMountMutex.
148 static fssh_recursive_lock sMountOpLock;
150 /** \brief Guards the vnode::covered_by field of any vnode
152 * The holder is allowed to read access the vnode::covered_by field of any
153 * vnode. Additionally holding sMountOpLock allows for write access.
155 * The thread trying to lock the must not hold sVnodeMutex.
157 static fssh_mutex sVnodeCoveredByMutex;
159 /** \brief Guards sVnodeTable.
161 * The holder is allowed to read/write access sVnodeTable and to
162 * to any unbusy vnode in that table, save
163 * to the immutable fields (device, id, private_node, mount) to which
164 * only read-only access is allowed, and to the field covered_by, which is
165 * guarded by sMountOpLock and sVnodeCoveredByMutex.
167 * The thread trying to lock the mutex must not hold sMountMutex.
168 * You must not have this mutex held when calling create_sem(), as this
169 * might call vfs_free_unused_vnodes().
171 static fssh_mutex sVnodeMutex;
173 #define VNODE_HASH_TABLE_SIZE 1024
174 static hash_table *sVnodeTable;
175 static list sUnusedVnodeList;
176 static uint32_t sUnusedVnodes = 0;
177 static struct vnode *sRoot;
179 #define MOUNTS_HASH_TABLE_SIZE 16
180 static hash_table *sMountsTable;
181 static fssh_mount_id sNextMountID = 1;
183 #define MAX_TEMP_IO_VECS 8
185 fssh_mode_t __fssh_gUmask = 022;
187 /* function declarations */
189 // file descriptor operation prototypes
190 static fssh_status_t file_read(struct file_descriptor *, fssh_off_t pos,
191 void *buffer, fssh_size_t *);
192 static fssh_status_t file_write(struct file_descriptor *, fssh_off_t pos,
193 const void *buffer, fssh_size_t *);
194 static fssh_off_t file_seek(struct file_descriptor *, fssh_off_t pos,
195 int seek_type);
196 static void file_free_fd(struct file_descriptor *);
197 static fssh_status_t file_close(struct file_descriptor *);
198 static fssh_status_t dir_read(struct file_descriptor *,
199 struct fssh_dirent *buffer, fssh_size_t bufferSize,
200 uint32_t *_count);
201 static fssh_status_t dir_read(struct vnode *vnode, void *cookie,
202 struct fssh_dirent *buffer, fssh_size_t bufferSize,
203 uint32_t *_count);
204 static fssh_status_t dir_rewind(struct file_descriptor *);
205 static void dir_free_fd(struct file_descriptor *);
206 static fssh_status_t dir_close(struct file_descriptor *);
207 static fssh_status_t attr_dir_read(struct file_descriptor *,
208 struct fssh_dirent *buffer, fssh_size_t bufferSize,
209 uint32_t *_count);
210 static fssh_status_t attr_dir_rewind(struct file_descriptor *);
211 static void attr_dir_free_fd(struct file_descriptor *);
212 static fssh_status_t attr_dir_close(struct file_descriptor *);
213 static fssh_status_t attr_read(struct file_descriptor *, fssh_off_t pos,
214 void *buffer, fssh_size_t *);
215 static fssh_status_t attr_write(struct file_descriptor *, fssh_off_t pos,
216 const void *buffer, fssh_size_t *);
217 static fssh_off_t attr_seek(struct file_descriptor *, fssh_off_t pos,
218 int seek_type);
219 static void attr_free_fd(struct file_descriptor *);
220 static fssh_status_t attr_close(struct file_descriptor *);
221 static fssh_status_t attr_read_stat(struct file_descriptor *,
222 struct fssh_stat *);
223 static fssh_status_t attr_write_stat(struct file_descriptor *,
224 const struct fssh_stat *, int statMask);
225 static fssh_status_t index_dir_read(struct file_descriptor *,
226 struct fssh_dirent *buffer, fssh_size_t bufferSize,
227 uint32_t *_count);
228 static fssh_status_t index_dir_rewind(struct file_descriptor *);
229 static void index_dir_free_fd(struct file_descriptor *);
230 static fssh_status_t index_dir_close(struct file_descriptor *);
231 static fssh_status_t query_read(struct file_descriptor *,
232 struct fssh_dirent *buffer, fssh_size_t bufferSize,
233 uint32_t *_count);
234 static fssh_status_t query_rewind(struct file_descriptor *);
235 static void query_free_fd(struct file_descriptor *);
236 static fssh_status_t query_close(struct file_descriptor *);
238 static fssh_status_t common_ioctl(struct file_descriptor *, uint32_t, void *buf,
239 fssh_size_t len);
240 static fssh_status_t common_read_stat(struct file_descriptor *,
241 struct fssh_stat *);
242 static fssh_status_t common_write_stat(struct file_descriptor *,
243 const struct fssh_stat *, int statMask);
245 static fssh_status_t vnode_path_to_vnode(struct vnode *vnode, char *path,
246 bool traverseLeafLink, int count, struct vnode **_vnode,
247 fssh_vnode_id *_parentID);
248 static fssh_status_t dir_vnode_to_path(struct vnode *vnode, char *buffer,
249 fssh_size_t bufferSize);
250 static fssh_status_t fd_and_path_to_vnode(int fd, char *path,
251 bool traverseLeafLink, struct vnode **_vnode,
252 fssh_vnode_id *_parentID, bool kernel);
253 static void inc_vnode_ref_count(struct vnode *vnode);
254 static fssh_status_t dec_vnode_ref_count(struct vnode *vnode, bool reenter);
255 static inline void put_vnode(struct vnode *vnode);
257 static struct fd_ops sFileOps = {
258 file_read,
259 file_write,
260 file_seek,
261 common_ioctl,
262 NULL,
263 NULL,
264 NULL, // read_dir()
265 NULL, // rewind_dir()
266 common_read_stat,
267 common_write_stat,
268 file_close,
269 file_free_fd
272 static struct fd_ops sDirectoryOps = {
273 NULL, // read()
274 NULL, // write()
275 NULL, // seek()
276 common_ioctl,
277 NULL, // select()
278 NULL, // deselect()
279 dir_read,
280 dir_rewind,
281 common_read_stat,
282 common_write_stat,
283 dir_close,
284 dir_free_fd
287 static struct fd_ops sAttributeDirectoryOps = {
288 NULL, // read()
289 NULL, // write()
290 NULL, // seek()
291 common_ioctl,
292 NULL, // select()
293 NULL, // deselect()
294 attr_dir_read,
295 attr_dir_rewind,
296 common_read_stat,
297 common_write_stat,
298 attr_dir_close,
299 attr_dir_free_fd
302 static struct fd_ops sAttributeOps = {
303 attr_read,
304 attr_write,
305 attr_seek,
306 common_ioctl,
307 NULL, // select()
308 NULL, // deselect()
309 NULL, // read_dir()
310 NULL, // rewind_dir()
311 attr_read_stat,
312 attr_write_stat,
313 attr_close,
314 attr_free_fd
317 static struct fd_ops sIndexDirectoryOps = {
318 NULL, // read()
319 NULL, // write()
320 NULL, // seek()
321 NULL, // ioctl()
322 NULL, // select()
323 NULL, // deselect()
324 index_dir_read,
325 index_dir_rewind,
326 NULL, // read_stat()
327 NULL, // write_stat()
328 index_dir_close,
329 index_dir_free_fd
332 #if 0
333 static struct fd_ops sIndexOps = {
334 NULL, // read()
335 NULL, // write()
336 NULL, // seek()
337 NULL, // ioctl()
338 NULL, // select()
339 NULL, // deselect()
340 NULL, // dir_read()
341 NULL, // dir_rewind()
342 index_read_stat, // read_stat()
343 NULL, // write_stat()
344 NULL, // dir_close()
345 NULL // free_fd()
347 #endif
349 static struct fd_ops sQueryOps = {
350 NULL, // read()
351 NULL, // write()
352 NULL, // seek()
353 NULL, // ioctl()
354 NULL, // select()
355 NULL, // deselect()
356 query_read,
357 query_rewind,
358 NULL, // read_stat()
359 NULL, // write_stat()
360 query_close,
361 query_free_fd
365 // VNodePutter
366 class VNodePutter {
367 public:
368 VNodePutter(struct vnode *vnode = NULL) : fVNode(vnode) {}
370 ~VNodePutter()
372 Put();
375 void SetTo(struct vnode *vnode)
377 Put();
378 fVNode = vnode;
381 void Put()
383 if (fVNode) {
384 put_vnode(fVNode);
385 fVNode = NULL;
389 struct vnode *Detach()
391 struct vnode *vnode = fVNode;
392 fVNode = NULL;
393 return vnode;
396 private:
397 struct vnode *fVNode;
401 static int
402 mount_compare(void *_m, const void *_key)
404 struct fs_mount *mount = (fs_mount *)_m;
405 const fssh_mount_id *id = (fssh_mount_id *)_key;
407 if (mount->id == *id)
408 return 0;
410 return -1;
414 static uint32_t
415 mount_hash(void *_m, const void *_key, uint32_t range)
417 struct fs_mount *mount = (fs_mount *)_m;
418 const fssh_mount_id *id = (fssh_mount_id *)_key;
420 if (mount)
421 return mount->id % range;
423 return (uint32_t)*id % range;
427 /** Finds the mounted device (the fs_mount structure) with the given ID.
428 * Note, you must hold the gMountMutex lock when you call this function.
431 static struct fs_mount *
432 find_mount(fssh_mount_id id)
434 ASSERT_LOCKED_MUTEX(&sMountMutex);
436 return (fs_mount *)hash_lookup(sMountsTable, (void *)&id);
440 static fssh_status_t
441 get_mount(fssh_mount_id id, struct fs_mount **_mount)
443 MutexLocker locker(&sMountMutex);
445 struct fs_mount *mount = find_mount(id);
446 if (mount == NULL)
447 return FSSH_B_BAD_VALUE;
449 if (mount->root_vnode == NULL) {
450 // might have been called during a mount operation in which
451 // case the root node may still be NULL
452 return FSSH_B_BUSY;
455 inc_vnode_ref_count(mount->root_vnode);
456 *_mount = mount;
458 return FSSH_B_OK;
462 static void
463 put_mount(struct fs_mount *mount)
465 if (mount)
466 put_vnode(mount->root_vnode);
470 static fssh_status_t
471 put_file_system(fssh_file_system_module_info *fs)
473 return fssh_put_module(fs->info.name);
477 /** Tries to open the specified file system module.
478 * Accepts a file system name of the form "bfs" or "file_systems/bfs/v1".
479 * Returns a pointer to file system module interface, or NULL if it
480 * could not open the module.
483 static fssh_file_system_module_info *
484 get_file_system(const char *fsName)
486 char name[FSSH_B_FILE_NAME_LENGTH];
487 if (fssh_strncmp(fsName, "file_systems/", fssh_strlen("file_systems/"))) {
488 // construct module name if we didn't get one
489 // (we currently support only one API)
490 fssh_snprintf(name, sizeof(name), "file_systems/%s/v1", fsName);
491 fsName = NULL;
494 fssh_file_system_module_info *info;
495 if (fssh_get_module(fsName ? fsName : name, (fssh_module_info **)&info) != FSSH_B_OK)
496 return NULL;
498 return info;
502 /** Accepts a file system name of the form "bfs" or "file_systems/bfs/v1"
503 * and returns a compatible fs_info.fsh_name name ("bfs" in both cases).
504 * The name is allocated for you, and you have to free() it when you're
505 * done with it.
506 * Returns NULL if the required memory is no available.
509 static char *
510 get_file_system_name(const char *fsName)
512 const fssh_size_t length = fssh_strlen("file_systems/");
514 if (fssh_strncmp(fsName, "file_systems/", length)) {
515 // the name already seems to be the module's file name
516 return fssh_strdup(fsName);
519 fsName += length;
520 const char *end = fssh_strchr(fsName, '/');
521 if (end == NULL) {
522 // this doesn't seem to be a valid name, but well...
523 return fssh_strdup(fsName);
526 // cut off the trailing /v1
528 char *name = (char *)malloc(end + 1 - fsName);
529 if (name == NULL)
530 return NULL;
532 fssh_strlcpy(name, fsName, end + 1 - fsName);
533 return name;
537 static int
538 vnode_compare(void *_vnode, const void *_key)
540 struct vnode *vnode = (struct vnode *)_vnode;
541 const struct vnode_hash_key *key = (vnode_hash_key *)_key;
543 if (vnode->device == key->device && vnode->id == key->vnode)
544 return 0;
546 return -1;
550 static uint32_t
551 vnode_hash(void *_vnode, const void *_key, uint32_t range)
553 struct vnode *vnode = (struct vnode *)_vnode;
554 const struct vnode_hash_key *key = (vnode_hash_key *)_key;
556 #define VHASH(mountid, vnodeid) (((uint32_t)((vnodeid) >> 32) + (uint32_t)(vnodeid)) ^ (uint32_t)(mountid))
558 if (vnode != NULL)
559 return VHASH(vnode->device, vnode->id) % range;
561 return VHASH(key->device, key->vnode) % range;
563 #undef VHASH
567 static void
568 add_vnode_to_mount_list(struct vnode *vnode, struct fs_mount *mount)
570 fssh_recursive_lock_lock(&mount->rlock);
572 list_add_link_to_head(&mount->vnodes, &vnode->mount_link);
574 fssh_recursive_lock_unlock(&mount->rlock);
578 static void
579 remove_vnode_from_mount_list(struct vnode *vnode, struct fs_mount *mount)
581 fssh_recursive_lock_lock(&mount->rlock);
583 list_remove_link(&vnode->mount_link);
584 vnode->mount_link.next = vnode->mount_link.prev = NULL;
586 fssh_recursive_lock_unlock(&mount->rlock);
590 static fssh_status_t
591 create_new_vnode(struct vnode **_vnode, fssh_mount_id mountID, fssh_vnode_id vnodeID)
593 FUNCTION(("create_new_vnode()\n"));
595 struct vnode *vnode = (struct vnode *)malloc(sizeof(struct vnode));
596 if (vnode == NULL)
597 return FSSH_B_NO_MEMORY;
599 // initialize basic values
600 fssh_memset(vnode, 0, sizeof(struct vnode));
601 vnode->device = mountID;
602 vnode->id = vnodeID;
604 // add the vnode to the mount structure
605 fssh_mutex_lock(&sMountMutex);
606 vnode->mount = find_mount(mountID);
607 if (!vnode->mount || vnode->mount->unmounting) {
608 fssh_mutex_unlock(&sMountMutex);
609 free(vnode);
610 return FSSH_B_ENTRY_NOT_FOUND;
613 hash_insert(sVnodeTable, vnode);
614 add_vnode_to_mount_list(vnode, vnode->mount);
616 fssh_mutex_unlock(&sMountMutex);
618 vnode->ref_count = 1;
619 *_vnode = vnode;
621 return FSSH_B_OK;
625 /** Frees the vnode and all resources it has acquired, and removes
626 * it from the vnode hash as well as from its mount structure.
627 * Will also make sure that any cache modifications are written back.
630 static void
631 free_vnode(struct vnode *vnode, bool reenter)
633 ASSERT(vnode->ref_count == 0 && vnode->busy);
635 // write back any changes in this vnode's cache -- but only
636 // if the vnode won't be deleted, in which case the changes
637 // will be discarded
639 if (!vnode->remove && HAS_FS_CALL(vnode, fsync))
640 FS_CALL_NO_PARAMS(vnode, fsync);
642 if (!vnode->unpublished) {
643 if (vnode->remove)
644 FS_CALL(vnode, remove_vnode, reenter);
645 else
646 FS_CALL(vnode, put_vnode, reenter);
649 // The file system has removed the resources of the vnode now, so we can
650 // make it available again (and remove the busy vnode from the hash)
651 fssh_mutex_lock(&sVnodeMutex);
652 hash_remove(sVnodeTable, vnode);
653 fssh_mutex_unlock(&sVnodeMutex);
655 remove_vnode_from_mount_list(vnode, vnode->mount);
657 free(vnode);
661 /** \brief Decrements the reference counter of the given vnode and deletes it,
662 * if the counter dropped to 0.
664 * The caller must, of course, own a reference to the vnode to call this
665 * function.
666 * The caller must not hold the sVnodeMutex or the sMountMutex.
668 * \param vnode the vnode.
669 * \param reenter \c true, if this function is called (indirectly) from within
670 * a file system.
671 * \return \c FSSH_B_OK, if everything went fine, an error code otherwise.
674 static fssh_status_t
675 dec_vnode_ref_count(struct vnode *vnode, bool reenter)
677 fssh_mutex_lock(&sVnodeMutex);
679 int32_t oldRefCount = fssh_atomic_add(&vnode->ref_count, -1);
681 TRACE(("dec_vnode_ref_count: vnode %p, ref now %ld\n", vnode, vnode->ref_count));
683 if (oldRefCount == 1) {
684 if (vnode->busy)
685 fssh_panic("dec_vnode_ref_count: called on busy vnode %p\n", vnode);
687 bool freeNode = false;
689 // Just insert the vnode into an unused list if we don't need
690 // to delete it
691 if (vnode->remove) {
692 vnode->busy = true;
693 freeNode = true;
694 } else {
695 list_add_item(&sUnusedVnodeList, vnode);
696 if (++sUnusedVnodes > kMaxUnusedVnodes) {
697 // there are too many unused vnodes so we free the oldest one
698 // ToDo: evaluate this mechanism
699 vnode = (struct vnode *)list_remove_head_item(&sUnusedVnodeList);
700 vnode->busy = true;
701 freeNode = true;
702 sUnusedVnodes--;
706 fssh_mutex_unlock(&sVnodeMutex);
708 if (freeNode)
709 free_vnode(vnode, reenter);
710 } else
711 fssh_mutex_unlock(&sVnodeMutex);
713 return FSSH_B_OK;
717 /** \brief Increments the reference counter of the given vnode.
719 * The caller must either already have a reference to the vnode or hold
720 * the sVnodeMutex.
722 * \param vnode the vnode.
725 static void
726 inc_vnode_ref_count(struct vnode *vnode)
728 fssh_atomic_add(&vnode->ref_count, 1);
729 TRACE(("inc_vnode_ref_count: vnode %p, ref now %ld\n", vnode, vnode->ref_count));
733 /** \brief Looks up a vnode by mount and node ID in the sVnodeTable.
735 * The caller must hold the sVnodeMutex.
737 * \param mountID the mount ID.
738 * \param vnodeID the node ID.
740 * \return The vnode structure, if it was found in the hash table, \c NULL
741 * otherwise.
744 static struct vnode *
745 lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID)
747 struct vnode_hash_key key;
749 key.device = mountID;
750 key.vnode = vnodeID;
752 return (vnode *)hash_lookup(sVnodeTable, &key);
756 /** \brief Retrieves a vnode for a given mount ID, node ID pair.
758 * If the node is not yet in memory, it will be loaded.
760 * The caller must not hold the sVnodeMutex or the sMountMutex.
762 * \param mountID the mount ID.
763 * \param vnodeID the node ID.
764 * \param _vnode Pointer to a vnode* variable into which the pointer to the
765 * retrieved vnode structure shall be written.
766 * \param reenter \c true, if this function is called (indirectly) from within
767 * a file system.
768 * \return \c FSSH_B_OK, if everything when fine, an error code otherwise.
771 static fssh_status_t
772 get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, struct vnode **_vnode, int reenter)
774 FUNCTION(("get_vnode: mountid %ld vnid 0x%Lx %p\n", mountID, vnodeID, _vnode));
776 fssh_mutex_lock(&sVnodeMutex);
778 int32_t tries = 300;
779 // try for 3 secs
780 restart:
781 struct vnode *vnode = lookup_vnode(mountID, vnodeID);
782 if (vnode && vnode->busy) {
783 fssh_mutex_unlock(&sVnodeMutex);
784 if (--tries < 0) {
785 // vnode doesn't seem to become unbusy
786 fssh_panic("vnode %d:%" FSSH_B_PRIdINO " is not becoming unbusy!\n",
787 (int)mountID, vnodeID);
788 return FSSH_B_BUSY;
790 fssh_snooze(10000); // 10 ms
791 fssh_mutex_lock(&sVnodeMutex);
792 goto restart;
795 TRACE(("get_vnode: tried to lookup vnode, got %p\n", vnode));
797 fssh_status_t status;
799 if (vnode) {
800 if (vnode->ref_count == 0) {
801 // this vnode has been unused before
802 list_remove_item(&sUnusedVnodeList, vnode);
803 sUnusedVnodes--;
805 inc_vnode_ref_count(vnode);
806 } else {
807 // we need to create a new vnode and read it in
808 status = create_new_vnode(&vnode, mountID, vnodeID);
809 if (status < FSSH_B_OK)
810 goto err;
812 vnode->busy = true;
813 fssh_mutex_unlock(&sVnodeMutex);
815 int type;
816 uint32_t flags;
817 status = FS_MOUNT_CALL(vnode->mount, get_vnode, vnodeID, vnode, &type,
818 &flags, reenter);
819 if (status == FSSH_B_OK && vnode->private_node == NULL)
820 status = FSSH_B_BAD_VALUE;
822 fssh_mutex_lock(&sVnodeMutex);
824 if (status < FSSH_B_OK)
825 goto err1;
827 vnode->type = type;
828 vnode->busy = false;
831 fssh_mutex_unlock(&sVnodeMutex);
833 TRACE(("get_vnode: returning %p\n", vnode));
835 *_vnode = vnode;
836 return FSSH_B_OK;
838 err1:
839 hash_remove(sVnodeTable, vnode);
840 remove_vnode_from_mount_list(vnode, vnode->mount);
841 err:
842 fssh_mutex_unlock(&sVnodeMutex);
843 if (vnode)
844 free(vnode);
846 return status;
850 /** \brief Decrements the reference counter of the given vnode and deletes it,
851 * if the counter dropped to 0.
853 * The caller must, of course, own a reference to the vnode to call this
854 * function.
855 * The caller must not hold the sVnodeMutex or the sMountMutex.
857 * \param vnode the vnode.
860 static inline void
861 put_vnode(struct vnode *vnode)
863 dec_vnode_ref_count(vnode, false);
867 /** Disconnects all file descriptors that are associated with the
868 * \a vnodeToDisconnect, or if this is NULL, all vnodes of the specified
869 * \a mount object.
871 * Note, after you've called this function, there might still be ongoing
872 * accesses - they won't be interrupted if they already happened before.
873 * However, any subsequent access will fail.
875 * This is not a cheap function and should be used with care and rarely.
876 * TODO: there is currently no means to stop a blocking read/write!
879 void
880 disconnect_mount_or_vnode_fds(struct fs_mount *mount,
881 struct vnode *vnodeToDisconnect)
886 /** \brief Resolves a mount point vnode to the volume root vnode it is covered
887 * by.
889 * Given an arbitrary vnode, the function checks, whether the node is covered
890 * by the root of a volume. If it is the function obtains a reference to the
891 * volume root node and returns it.
893 * \param vnode The vnode in question.
894 * \return The volume root vnode the vnode cover is covered by, if it is
895 * indeed a mount point, or \c NULL otherwise.
898 static struct vnode *
899 resolve_mount_point_to_volume_root(struct vnode *vnode)
901 if (!vnode)
902 return NULL;
904 struct vnode *volumeRoot = NULL;
906 fssh_mutex_lock(&sVnodeCoveredByMutex);
907 if (vnode->covered_by) {
908 volumeRoot = vnode->covered_by;
909 inc_vnode_ref_count(volumeRoot);
911 fssh_mutex_unlock(&sVnodeCoveredByMutex);
913 return volumeRoot;
917 /** \brief Resolves a mount point vnode to the volume root vnode it is covered
918 * by.
920 * Given an arbitrary vnode (identified by mount and node ID), the function
921 * checks, whether the node is covered by the root of a volume. If it is the
922 * function returns the mount and node ID of the volume root node. Otherwise
923 * it simply returns the supplied mount and node ID.
925 * In case of error (e.g. the supplied node could not be found) the variables
926 * for storing the resolved mount and node ID remain untouched and an error
927 * code is returned.
929 * \param mountID The mount ID of the vnode in question.
930 * \param nodeID The node ID of the vnode in question.
931 * \param resolvedMountID Pointer to storage for the resolved mount ID.
932 * \param resolvedNodeID Pointer to storage for the resolved node ID.
933 * \return
934 * - \c FSSH_B_OK, if everything went fine,
935 * - another error code, if something went wrong.
938 fssh_status_t
939 resolve_mount_point_to_volume_root(fssh_mount_id mountID, fssh_vnode_id nodeID,
940 fssh_mount_id *resolvedMountID, fssh_vnode_id *resolvedNodeID)
942 // get the node
943 struct vnode *node;
944 fssh_status_t error = get_vnode(mountID, nodeID, &node, false);
945 if (error != FSSH_B_OK)
946 return error;
948 // resolve the node
949 struct vnode *resolvedNode = resolve_mount_point_to_volume_root(node);
950 if (resolvedNode) {
951 put_vnode(node);
952 node = resolvedNode;
955 // set the return values
956 *resolvedMountID = node->device;
957 *resolvedNodeID = node->id;
959 put_vnode(node);
961 return FSSH_B_OK;
965 /** \brief Resolves a volume root vnode to the underlying mount point vnode.
967 * Given an arbitrary vnode, the function checks, whether the node is the
968 * root of a volume. If it is (and if it is not "/"), the function obtains
969 * a reference to the underlying mount point node and returns it.
971 * \param vnode The vnode in question (caller must have a reference).
972 * \return The mount point vnode the vnode covers, if it is indeed a volume
973 * root and not "/", or \c NULL otherwise.
976 static struct vnode *
977 resolve_volume_root_to_mount_point(struct vnode *vnode)
979 if (!vnode)
980 return NULL;
982 struct vnode *mountPoint = NULL;
984 struct fs_mount *mount = vnode->mount;
985 if (vnode == mount->root_vnode && mount->covers_vnode) {
986 mountPoint = mount->covers_vnode;
987 inc_vnode_ref_count(mountPoint);
990 return mountPoint;
994 /** \brief Gets the directory path and leaf name for a given path.
996 * The supplied \a path is transformed to refer to the directory part of
997 * the entry identified by the original path, and into the buffer \a filename
998 * the leaf name of the original entry is written.
999 * Neither the returned path nor the leaf name can be expected to be
1000 * canonical.
1002 * \param path The path to be analyzed. Must be able to store at least one
1003 * additional character.
1004 * \param filename The buffer into which the leaf name will be written.
1005 * Must be of size FSSH_B_FILE_NAME_LENGTH at least.
1006 * \return \c FSSH_B_OK, if everything went fine, \c FSSH_B_NAME_TOO_LONG, if the leaf
1007 * name is longer than \c FSSH_B_FILE_NAME_LENGTH.
1010 static fssh_status_t
1011 get_dir_path_and_leaf(char *path, char *filename)
1013 char *p = fssh_strrchr(path, '/');
1014 // '/' are not allowed in file names!
1016 FUNCTION(("get_dir_path_and_leaf(path = %s)\n", path));
1018 if (!p) {
1019 // this path is single segment with no '/' in it
1020 // ex. "foo"
1021 if (fssh_strlcpy(filename, path, FSSH_B_FILE_NAME_LENGTH) >= FSSH_B_FILE_NAME_LENGTH)
1022 return FSSH_B_NAME_TOO_LONG;
1023 fssh_strcpy(path, ".");
1024 } else {
1025 p++;
1026 if (*p == '\0') {
1027 // special case: the path ends in '/'
1028 fssh_strcpy(filename, ".");
1029 } else {
1030 // normal leaf: replace the leaf portion of the path with a '.'
1031 if (fssh_strlcpy(filename, p, FSSH_B_FILE_NAME_LENGTH)
1032 >= FSSH_B_FILE_NAME_LENGTH) {
1033 return FSSH_B_NAME_TOO_LONG;
1036 p[0] = '.';
1037 p[1] = '\0';
1039 return FSSH_B_OK;
1043 static fssh_status_t
1044 entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, struct vnode **_vnode)
1046 char clonedName[FSSH_B_FILE_NAME_LENGTH + 1];
1047 if (fssh_strlcpy(clonedName, name, FSSH_B_FILE_NAME_LENGTH) >= FSSH_B_FILE_NAME_LENGTH)
1048 return FSSH_B_NAME_TOO_LONG;
1050 // get the directory vnode and let vnode_path_to_vnode() do the rest
1051 struct vnode *directory;
1053 fssh_status_t status = get_vnode(mountID, directoryID, &directory, false);
1054 if (status < 0)
1055 return status;
1057 return vnode_path_to_vnode(directory, clonedName, false, 0, _vnode, NULL);
1061 static fssh_status_t
1062 lookup_dir_entry(struct vnode* dir, const char* name, struct vnode** _vnode)
1064 fssh_ino_t id;
1065 fssh_status_t status = FS_CALL(dir, lookup, name, &id);
1066 if (status < FSSH_B_OK)
1067 return status;
1069 fssh_mutex_lock(&sVnodeMutex);
1070 *_vnode = lookup_vnode(dir->device, id);
1071 fssh_mutex_unlock(&sVnodeMutex);
1073 if (*_vnode == NULL) {
1074 fssh_panic("lookup_dir_entry(): could not lookup vnode (mountid %d "
1075 "vnid %" FSSH_B_PRIdINO ")\n", (int)dir->device, id);
1076 return FSSH_B_ENTRY_NOT_FOUND;
1079 return FSSH_B_OK;
1083 /*! Returns the vnode for the relative path starting at the specified \a vnode.
1084 \a path must not be NULL.
1085 If it returns successfully, \a path contains the name of the last path
1086 component. This function clobbers the buffer pointed to by \a path only
1087 if it does contain more than one component.
1088 Note, this reduces the ref_count of the starting \a vnode, no matter if
1089 it is successful or not!
1091 static fssh_status_t
1092 vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink,
1093 int count, struct vnode **_vnode, fssh_vnode_id *_parentID)
1095 fssh_status_t status = 0;
1096 fssh_vnode_id lastParentID = vnode->id;
1098 FUNCTION(("vnode_path_to_vnode(vnode = %p, path = %s)\n", vnode, path));
1100 if (path == NULL) {
1101 put_vnode(vnode);
1102 return FSSH_B_BAD_VALUE;
1105 while (true) {
1106 struct vnode *nextVnode;
1107 char *nextPath;
1109 TRACE(("vnode_path_to_vnode: top of loop. p = %p, p = '%s'\n", path, path));
1111 // done?
1112 if (path[0] == '\0')
1113 break;
1115 // walk to find the next path component ("path" will point to a single
1116 // path component), and filter out multiple slashes
1117 for (nextPath = path + 1; *nextPath != '\0' && *nextPath != '/'; nextPath++);
1119 if (*nextPath == '/') {
1120 *nextPath = '\0';
1122 nextPath++;
1123 while (*nextPath == '/');
1126 // See if the '..' is at the root of a mount and move to the covered
1127 // vnode so we pass the '..' path to the underlying filesystem
1128 if (!fssh_strcmp("..", path)
1129 && vnode->mount->root_vnode == vnode
1130 && vnode->mount->covers_vnode) {
1131 nextVnode = vnode->mount->covers_vnode;
1132 inc_vnode_ref_count(nextVnode);
1133 put_vnode(vnode);
1134 vnode = nextVnode;
1137 // Check if we have the right to search the current directory vnode.
1138 // If a file system doesn't have the access() function, we assume that
1139 // searching a directory is always allowed
1140 if (HAS_FS_CALL(vnode, access))
1141 status = FS_CALL(vnode, access, FSSH_X_OK);
1143 // Tell the filesystem to get the vnode of this path component (if we got the
1144 // permission from the call above)
1145 if (status >= FSSH_B_OK)
1146 status = lookup_dir_entry(vnode, path, &nextVnode);
1148 if (status < FSSH_B_OK) {
1149 put_vnode(vnode);
1150 return status;
1153 // If the new node is a symbolic link, resolve it (if we've been told to do it)
1154 if (FSSH_S_ISLNK(vnode->type)
1155 && !(!traverseLeafLink && nextPath[0] == '\0')) {
1156 fssh_size_t bufferSize;
1157 char *buffer;
1159 TRACE(("traverse link\n"));
1161 // it's not exactly nice style using goto in this way, but hey, it works :-/
1162 if (count + 1 > FSSH_B_MAX_SYMLINKS) {
1163 status = FSSH_B_LINK_LIMIT;
1164 goto resolve_link_error;
1167 buffer = (char *)malloc(bufferSize = FSSH_B_PATH_NAME_LENGTH);
1168 if (buffer == NULL) {
1169 status = FSSH_B_NO_MEMORY;
1170 goto resolve_link_error;
1173 if (HAS_FS_CALL(nextVnode, read_symlink)) {
1174 status = FS_CALL(nextVnode, read_symlink, buffer,
1175 &bufferSize);
1176 } else
1177 status = FSSH_B_BAD_VALUE;
1179 if (status < FSSH_B_OK) {
1180 free(buffer);
1182 resolve_link_error:
1183 put_vnode(vnode);
1184 put_vnode(nextVnode);
1186 return status;
1188 put_vnode(nextVnode);
1190 // Check if we start from the root directory or the current
1191 // directory ("vnode" still points to that one).
1192 // Cut off all leading slashes if it's the root directory
1193 path = buffer;
1194 if (path[0] == '/') {
1195 // we don't need the old directory anymore
1196 put_vnode(vnode);
1198 while (*++path == '/')
1200 vnode = sRoot;
1201 inc_vnode_ref_count(vnode);
1203 inc_vnode_ref_count(vnode);
1204 // balance the next recursion - we will decrement the ref_count
1205 // of the vnode, no matter if we succeeded or not
1207 status = vnode_path_to_vnode(vnode, path, traverseLeafLink, count + 1,
1208 &nextVnode, &lastParentID);
1210 free(buffer);
1212 if (status < FSSH_B_OK) {
1213 put_vnode(vnode);
1214 return status;
1216 } else
1217 lastParentID = vnode->id;
1219 // decrease the ref count on the old dir we just looked up into
1220 put_vnode(vnode);
1222 path = nextPath;
1223 vnode = nextVnode;
1225 // see if we hit a mount point
1226 struct vnode *mountPoint = resolve_mount_point_to_volume_root(vnode);
1227 if (mountPoint) {
1228 put_vnode(vnode);
1229 vnode = mountPoint;
1233 *_vnode = vnode;
1234 if (_parentID)
1235 *_parentID = lastParentID;
1237 return FSSH_B_OK;
1241 static fssh_status_t
1242 path_to_vnode(char *path, bool traverseLink, struct vnode **_vnode,
1243 fssh_vnode_id *_parentID, bool kernel)
1245 struct vnode *start = NULL;
1247 FUNCTION(("path_to_vnode(path = \"%s\")\n", path));
1249 if (!path)
1250 return FSSH_B_BAD_VALUE;
1252 // figure out if we need to start at root or at cwd
1253 if (*path == '/') {
1254 if (sRoot == NULL) {
1255 // we're a bit early, aren't we?
1256 return FSSH_B_ERROR;
1259 while (*++path == '/')
1261 start = sRoot;
1262 inc_vnode_ref_count(start);
1263 } else {
1264 struct io_context *context = get_current_io_context(kernel);
1266 fssh_mutex_lock(&context->io_mutex);
1267 start = context->cwd;
1268 if (start != NULL)
1269 inc_vnode_ref_count(start);
1270 fssh_mutex_unlock(&context->io_mutex);
1272 if (start == NULL)
1273 return FSSH_B_ERROR;
1276 return vnode_path_to_vnode(start, path, traverseLink, 0, _vnode, _parentID);
1280 /** Returns the vnode in the next to last segment of the path, and returns
1281 * the last portion in filename.
1282 * The path buffer must be able to store at least one additional character.
1285 static fssh_status_t
1286 path_to_dir_vnode(char *path, struct vnode **_vnode, char *filename, bool kernel)
1288 fssh_status_t status = get_dir_path_and_leaf(path, filename);
1289 if (status != FSSH_B_OK)
1290 return status;
1292 return path_to_vnode(path, true, _vnode, NULL, kernel);
1296 /** \brief Retrieves the directory vnode and the leaf name of an entry referred
1297 * to by a FD + path pair.
1299 * \a path must be given in either case. \a fd might be omitted, in which
1300 * case \a path is either an absolute path or one relative to the current
1301 * directory. If both a supplied and \a path is relative it is reckoned off
1302 * of the directory referred to by \a fd. If \a path is absolute \a fd is
1303 * ignored.
1305 * The caller has the responsibility to call put_vnode() on the returned
1306 * directory vnode.
1308 * \param fd The FD. May be < 0.
1309 * \param path The absolute or relative path. Must not be \c NULL. The buffer
1310 * is modified by this function. It must have at least room for a
1311 * string one character longer than the path it contains.
1312 * \param _vnode A pointer to a variable the directory vnode shall be written
1313 * into.
1314 * \param filename A buffer of size FSSH_B_FILE_NAME_LENGTH or larger into which
1315 * the leaf name of the specified entry will be written.
1316 * \param kernel \c true, if invoked from inside the kernel, \c false if
1317 * invoked from userland.
1318 * \return \c FSSH_B_OK, if everything went fine, another error code otherwise.
1321 static fssh_status_t
1322 fd_and_path_to_dir_vnode(int fd, char *path, struct vnode **_vnode,
1323 char *filename, bool kernel)
1325 if (!path)
1326 return FSSH_B_BAD_VALUE;
1327 if (fd < 0)
1328 return path_to_dir_vnode(path, _vnode, filename, kernel);
1330 fssh_status_t status = get_dir_path_and_leaf(path, filename);
1331 if (status != FSSH_B_OK)
1332 return status;
1334 return fd_and_path_to_vnode(fd, path, true, _vnode, NULL, kernel);
1338 /** Returns a vnode's name in the d_name field of a supplied dirent buffer.
1341 static fssh_status_t
1342 get_vnode_name(struct vnode *vnode, struct vnode *parent,
1343 struct fssh_dirent *buffer, fssh_size_t bufferSize)
1345 if (bufferSize < sizeof(struct fssh_dirent))
1346 return FSSH_B_BAD_VALUE;
1348 // See if vnode is the root of a mount and move to the covered
1349 // vnode so we get the underlying file system
1350 VNodePutter vnodePutter;
1351 if (vnode->mount->root_vnode == vnode && vnode->mount->covers_vnode != NULL) {
1352 vnode = vnode->mount->covers_vnode;
1353 inc_vnode_ref_count(vnode);
1354 vnodePutter.SetTo(vnode);
1357 if (HAS_FS_CALL(vnode, get_vnode_name)) {
1358 // The FS supports getting the name of a vnode.
1359 return FS_CALL(vnode, get_vnode_name, buffer->d_name,
1360 (char*)buffer + bufferSize - buffer->d_name);
1363 // The FS doesn't support getting the name of a vnode. So we search the
1364 // parent directory for the vnode, if the caller let us.
1366 if (parent == NULL)
1367 return FSSH_EOPNOTSUPP;
1369 void *cookie;
1371 fssh_status_t status = FS_CALL(parent, open_dir, &cookie);
1372 if (status >= FSSH_B_OK) {
1373 while (true) {
1374 uint32_t num = 1;
1375 status = dir_read(parent, cookie, buffer, bufferSize, &num);
1376 if (status < FSSH_B_OK)
1377 break;
1378 if (num == 0) {
1379 status = FSSH_B_ENTRY_NOT_FOUND;
1380 break;
1383 if (vnode->id == buffer->d_ino) {
1384 // found correct entry!
1385 break;
1389 FS_CALL(vnode, close_dir, cookie);
1390 FS_CALL(vnode, free_dir_cookie, cookie);
1392 return status;
1396 static fssh_status_t
1397 get_vnode_name(struct vnode *vnode, struct vnode *parent, char *name,
1398 fssh_size_t nameSize)
1400 char buffer[sizeof(struct fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
1401 struct fssh_dirent *dirent = (struct fssh_dirent *)buffer;
1403 fssh_status_t status = get_vnode_name(vnode, parent, buffer, sizeof(buffer));
1404 if (status != FSSH_B_OK)
1405 return status;
1407 if (fssh_strlcpy(name, dirent->d_name, nameSize) >= nameSize)
1408 return FSSH_B_BUFFER_OVERFLOW;
1410 return FSSH_B_OK;
1414 /** Gets the full path to a given directory vnode.
1415 * It uses the fs_get_vnode_name() call to get the name of a vnode; if a
1416 * file system doesn't support this call, it will fall back to iterating
1417 * through the parent directory to get the name of the child.
1419 * To protect against circular loops, it supports a maximum tree depth
1420 * of 256 levels.
1422 * Note that the path may not be correct the time this function returns!
1423 * It doesn't use any locking to prevent returning the correct path, as
1424 * paths aren't safe anyway: the path to a file can change at any time.
1426 * It might be a good idea, though, to check if the returned path exists
1427 * in the calling function (it's not done here because of efficiency)
1430 static fssh_status_t
1431 dir_vnode_to_path(struct vnode *vnode, char *buffer, fssh_size_t bufferSize)
1433 FUNCTION(("dir_vnode_to_path(%p, %p, %lu)\n", vnode, buffer, bufferSize));
1435 if (vnode == NULL || buffer == NULL)
1436 return FSSH_B_BAD_VALUE;
1438 /* this implementation is currently bound to FSSH_B_PATH_NAME_LENGTH */
1439 KPath pathBuffer;
1440 if (pathBuffer.InitCheck() != FSSH_B_OK)
1441 return FSSH_B_NO_MEMORY;
1443 char *path = pathBuffer.LockBuffer();
1444 int32_t insert = pathBuffer.BufferSize();
1445 int32_t maxLevel = 256;
1446 int32_t length;
1447 fssh_status_t status;
1449 // we don't use get_vnode() here because this call is more
1450 // efficient and does all we need from get_vnode()
1451 inc_vnode_ref_count(vnode);
1453 // resolve a volume root to its mount point
1454 struct vnode *mountPoint = resolve_volume_root_to_mount_point(vnode);
1455 if (mountPoint) {
1456 put_vnode(vnode);
1457 vnode = mountPoint;
1460 path[--insert] = '\0';
1462 while (true) {
1463 // the name buffer is also used for fs_read_dir()
1464 char nameBuffer[sizeof(struct fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
1465 char *name = &((struct fssh_dirent *)nameBuffer)->d_name[0];
1466 struct vnode *parentVnode;
1467 fssh_vnode_id parentID;
1469 // lookup the parent vnode
1470 status = lookup_dir_entry(vnode, "..", &parentVnode);
1471 if (status < FSSH_B_OK)
1472 goto out;
1474 // get the node's name
1475 status = get_vnode_name(vnode, parentVnode,
1476 (struct fssh_dirent*)nameBuffer, sizeof(nameBuffer));
1478 // resolve a volume root to its mount point
1479 mountPoint = resolve_volume_root_to_mount_point(parentVnode);
1480 if (mountPoint) {
1481 put_vnode(parentVnode);
1482 parentVnode = mountPoint;
1483 parentID = parentVnode->id;
1486 bool hitRoot = (parentVnode == vnode);
1488 // release the current vnode, we only need its parent from now on
1489 put_vnode(vnode);
1490 vnode = parentVnode;
1492 if (status < FSSH_B_OK)
1493 goto out;
1495 if (hitRoot) {
1496 // we have reached "/", which means we have constructed the full
1497 // path
1498 break;
1501 // ToDo: add an explicit check for loops in about 10 levels to do
1502 // real loop detection
1504 // don't go deeper as 'maxLevel' to prevent circular loops
1505 if (maxLevel-- < 0) {
1506 status = FSSH_ELOOP;
1507 goto out;
1510 // add the name in front of the current path
1511 name[FSSH_B_FILE_NAME_LENGTH - 1] = '\0';
1512 length = fssh_strlen(name);
1513 insert -= length;
1514 if (insert <= 0) {
1515 status = FSSH_ENOBUFS;
1516 goto out;
1518 fssh_memcpy(path + insert, name, length);
1519 path[--insert] = '/';
1522 // the root dir will result in an empty path: fix it
1523 if (path[insert] == '\0')
1524 path[--insert] = '/';
1526 TRACE((" path is: %s\n", path + insert));
1528 // copy the path to the output buffer
1529 length = pathBuffer.BufferSize() - insert;
1530 if (length <= (int)bufferSize)
1531 fssh_memcpy(buffer, path + insert, length);
1532 else
1533 status = FSSH_ENOBUFS;
1535 out:
1536 put_vnode(vnode);
1537 return status;
1541 /** Checks the length of every path component, and adds a '.'
1542 * if the path ends in a slash.
1543 * The given path buffer must be able to store at least one
1544 * additional character.
1547 static fssh_status_t
1548 check_path(char *to)
1550 int32_t length = 0;
1552 // check length of every path component
1554 while (*to) {
1555 char *begin;
1556 if (*to == '/')
1557 to++, length++;
1559 begin = to;
1560 while (*to != '/' && *to)
1561 to++, length++;
1563 if (to - begin > FSSH_B_FILE_NAME_LENGTH)
1564 return FSSH_B_NAME_TOO_LONG;
1567 if (length == 0)
1568 return FSSH_B_ENTRY_NOT_FOUND;
1570 // complete path if there is a slash at the end
1572 if (*(to - 1) == '/') {
1573 if (length > FSSH_B_PATH_NAME_LENGTH - 2)
1574 return FSSH_B_NAME_TOO_LONG;
1576 to[0] = '.';
1577 to[1] = '\0';
1580 return FSSH_B_OK;
1584 static struct file_descriptor *
1585 get_fd_and_vnode(int fd, struct vnode **_vnode, bool kernel)
1587 struct file_descriptor *descriptor = get_fd(get_current_io_context(kernel), fd);
1588 if (descriptor == NULL)
1589 return NULL;
1591 if (fd_vnode(descriptor) == NULL) {
1592 put_fd(descriptor);
1593 return NULL;
1596 // ToDo: when we can close a file descriptor at any point, investigate
1597 // if this is still valid to do (accessing the vnode without ref_count
1598 // or locking)
1599 *_vnode = descriptor->u.vnode;
1600 return descriptor;
1604 static struct vnode *
1605 get_vnode_from_fd(int fd, bool kernel)
1607 struct file_descriptor *descriptor;
1608 struct vnode *vnode;
1610 descriptor = get_fd(get_current_io_context(kernel), fd);
1611 if (descriptor == NULL)
1612 return NULL;
1614 vnode = fd_vnode(descriptor);
1615 if (vnode != NULL)
1616 inc_vnode_ref_count(vnode);
1618 put_fd(descriptor);
1619 return vnode;
1623 /** Gets the vnode from an FD + path combination. If \a fd is lower than zero,
1624 * only the path will be considered. In this case, the \a path must not be
1625 * NULL.
1626 * If \a fd is a valid file descriptor, \a path may be NULL for directories,
1627 * and should be NULL for files.
1630 static fssh_status_t
1631 fd_and_path_to_vnode(int fd, char *path, bool traverseLeafLink,
1632 struct vnode **_vnode, fssh_vnode_id *_parentID, bool kernel)
1634 if (fd < 0 && !path)
1635 return FSSH_B_BAD_VALUE;
1637 if (fd < 0 || (path != NULL && path[0] == '/')) {
1638 // no FD or absolute path
1639 return path_to_vnode(path, traverseLeafLink, _vnode, _parentID, kernel);
1642 // FD only, or FD + relative path
1643 struct vnode *vnode = get_vnode_from_fd(fd, kernel);
1644 if (!vnode)
1645 return FSSH_B_FILE_ERROR;
1647 if (path != NULL) {
1648 return vnode_path_to_vnode(vnode, path, traverseLeafLink, 0,
1649 _vnode, _parentID);
1652 // there is no relative path to take into account
1654 *_vnode = vnode;
1655 if (_parentID)
1656 *_parentID = -1;
1658 return FSSH_B_OK;
1662 static int
1663 get_new_fd(int type, struct fs_mount *mount, struct vnode *vnode,
1664 void *cookie, int openMode, bool kernel)
1666 struct file_descriptor *descriptor;
1667 int fd;
1669 // if the vnode is locked, we don't allow creating a new file descriptor for it
1670 if (vnode && vnode->mandatory_locked_by != NULL)
1671 return FSSH_B_BUSY;
1673 descriptor = alloc_fd();
1674 if (!descriptor)
1675 return FSSH_B_NO_MEMORY;
1677 if (vnode)
1678 descriptor->u.vnode = vnode;
1679 else
1680 descriptor->u.mount = mount;
1681 descriptor->cookie = cookie;
1683 switch (type) {
1684 // vnode types
1685 case FDTYPE_FILE:
1686 descriptor->ops = &sFileOps;
1687 break;
1688 case FDTYPE_DIR:
1689 descriptor->ops = &sDirectoryOps;
1690 break;
1691 case FDTYPE_ATTR:
1692 descriptor->ops = &sAttributeOps;
1693 break;
1694 case FDTYPE_ATTR_DIR:
1695 descriptor->ops = &sAttributeDirectoryOps;
1696 break;
1698 // mount types
1699 case FDTYPE_INDEX_DIR:
1700 descriptor->ops = &sIndexDirectoryOps;
1701 break;
1702 case FDTYPE_QUERY:
1703 descriptor->ops = &sQueryOps;
1704 break;
1706 default:
1707 fssh_panic("get_new_fd() called with unknown type %d\n", type);
1708 break;
1710 descriptor->type = type;
1711 descriptor->open_mode = openMode;
1713 fd = new_fd(get_current_io_context(kernel), descriptor);
1714 if (fd < 0) {
1715 free(descriptor);
1716 return FSSH_B_NO_MORE_FDS;
1719 return fd;
1723 /*! Does the dirty work of combining the file_io_vecs with the iovecs
1724 and calls the file system hooks to read/write the request to disk.
1726 static fssh_status_t
1727 common_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs,
1728 fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount,
1729 uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_numBytes,
1730 bool doWrite)
1732 if (fileVecCount == 0) {
1733 // There are no file vecs at this offset, so we're obviously trying
1734 // to access the file outside of its bounds
1735 return FSSH_B_BAD_VALUE;
1738 fssh_size_t numBytes = *_numBytes;
1739 uint32_t fileVecIndex;
1740 fssh_size_t vecOffset = *_vecOffset;
1741 uint32_t vecIndex = *_vecIndex;
1742 fssh_status_t status;
1743 fssh_size_t size;
1745 if (!doWrite && vecOffset == 0) {
1746 // now directly read the data from the device
1747 // the first file_io_vec can be read directly
1749 size = fileVecs[0].length;
1750 if (size > numBytes)
1751 size = numBytes;
1753 status = fssh_read_pages(fd, fileVecs[0].offset, &vecs[vecIndex],
1754 vecCount - vecIndex, &size);
1755 if (status < FSSH_B_OK)
1756 return status;
1758 // TODO: this is a work-around for buggy device drivers!
1759 // When our own drivers honour the length, we can:
1760 // a) also use this direct I/O for writes (otherwise, it would
1761 // overwrite precious data)
1762 // b) panic if the term below is true (at least for writes)
1763 if ((uint64_t)size > (uint64_t)fileVecs[0].length) {
1764 //dprintf("warning: device driver %p doesn't respect total length in read_pages() call!\n", ref->device);
1765 size = fileVecs[0].length;
1768 ASSERT(size <= fileVecs[0].length);
1770 // If the file portion was contiguous, we're already done now
1771 if (size == numBytes)
1772 return FSSH_B_OK;
1774 // if we reached the end of the file, we can return as well
1775 if ((uint64_t)size != (uint64_t)fileVecs[0].length) {
1776 *_numBytes = size;
1777 return FSSH_B_OK;
1780 fileVecIndex = 1;
1782 // first, find out where we have to continue in our iovecs
1783 for (; vecIndex < vecCount; vecIndex++) {
1784 if (size < vecs[vecIndex].iov_len)
1785 break;
1787 size -= vecs[vecIndex].iov_len;
1790 vecOffset = size;
1791 } else {
1792 fileVecIndex = 0;
1793 size = 0;
1796 // Too bad, let's process the rest of the file_io_vecs
1798 fssh_size_t totalSize = size;
1799 fssh_size_t bytesLeft = numBytes - size;
1801 for (; fileVecIndex < fileVecCount; fileVecIndex++) {
1802 const fssh_file_io_vec &fileVec = fileVecs[fileVecIndex];
1803 fssh_off_t fileOffset = fileVec.offset;
1804 fssh_off_t fileLeft = fssh_min_c((uint64_t)fileVec.length, (uint64_t)bytesLeft);
1806 TRACE(("FILE VEC [%lu] length %Ld\n", fileVecIndex, fileLeft));
1808 // process the complete fileVec
1809 while (fileLeft > 0) {
1810 fssh_iovec tempVecs[MAX_TEMP_IO_VECS];
1811 uint32_t tempCount = 0;
1813 // size tracks how much of what is left of the current fileVec
1814 // (fileLeft) has been assigned to tempVecs
1815 size = 0;
1817 // assign what is left of the current fileVec to the tempVecs
1818 for (size = 0; (uint64_t)size < (uint64_t)fileLeft && vecIndex < vecCount
1819 && tempCount < MAX_TEMP_IO_VECS;) {
1820 // try to satisfy one iovec per iteration (or as much as
1821 // possible)
1823 // bytes left of the current iovec
1824 fssh_size_t vecLeft = vecs[vecIndex].iov_len - vecOffset;
1825 if (vecLeft == 0) {
1826 vecOffset = 0;
1827 vecIndex++;
1828 continue;
1831 TRACE(("fill vec %ld, offset = %lu, size = %lu\n",
1832 vecIndex, vecOffset, size));
1834 // actually available bytes
1835 fssh_size_t tempVecSize = fssh_min_c(vecLeft, fileLeft - size);
1837 tempVecs[tempCount].iov_base
1838 = (void *)((fssh_addr_t)vecs[vecIndex].iov_base + vecOffset);
1839 tempVecs[tempCount].iov_len = tempVecSize;
1840 tempCount++;
1842 size += tempVecSize;
1843 vecOffset += tempVecSize;
1846 fssh_size_t bytes = size;
1847 if (doWrite) {
1848 status = fssh_write_pages(fd, fileOffset, tempVecs,
1849 tempCount, &bytes);
1850 } else {
1851 status = fssh_read_pages(fd, fileOffset, tempVecs,
1852 tempCount, &bytes);
1854 if (status < FSSH_B_OK)
1855 return status;
1857 totalSize += bytes;
1858 bytesLeft -= size;
1859 fileOffset += size;
1860 fileLeft -= size;
1862 if (size != bytes || vecIndex >= vecCount) {
1863 // there are no more bytes or iovecs, let's bail out
1864 *_numBytes = totalSize;
1865 return FSSH_B_OK;
1870 *_vecIndex = vecIndex;
1871 *_vecOffset = vecOffset;
1872 *_numBytes = totalSize;
1873 return FSSH_B_OK;
1877 // #pragma mark - public VFS API
1880 extern "C" fssh_status_t
1881 fssh_new_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID,
1882 void *privateNode, fssh_fs_vnode_ops *ops)
1884 FUNCTION(("new_vnode(volume = %p (%ld), vnodeID = %Ld, node = %p)\n",
1885 volume, volume->id, vnodeID, privateNode));
1887 if (privateNode == NULL)
1888 return FSSH_B_BAD_VALUE;
1890 fssh_mutex_lock(&sVnodeMutex);
1892 // file system integrity check:
1893 // test if the vnode already exists and bail out if this is the case!
1895 // ToDo: the R5 implementation obviously checks for a different cookie
1896 // and doesn't panic if they are equal
1898 struct vnode *vnode = lookup_vnode(volume->id, vnodeID);
1899 if (vnode != NULL) {
1900 fssh_panic("vnode %d:%" FSSH_B_PRIdINO " already exists (node = %p, "
1901 "vnode->node = %p)!", (int)volume->id, vnodeID, privateNode,
1902 vnode->private_node);
1905 fssh_status_t status = create_new_vnode(&vnode, volume->id, vnodeID);
1906 if (status == FSSH_B_OK) {
1907 vnode->private_node = privateNode;
1908 vnode->ops = ops;
1909 vnode->busy = true;
1910 vnode->unpublished = true;
1913 TRACE(("returns: %s\n", strerror(status)));
1915 fssh_mutex_unlock(&sVnodeMutex);
1916 return status;
1920 extern "C" fssh_status_t
1921 fssh_publish_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID,
1922 void *privateNode, fssh_fs_vnode_ops *ops, int type, uint32_t flags)
1924 FUNCTION(("publish_vnode()\n"));
1926 MutexLocker locker(sVnodeMutex);
1928 struct vnode *vnode = lookup_vnode(volume->id, vnodeID);
1929 fssh_status_t status = FSSH_B_OK;
1931 if (vnode != NULL && vnode->busy && vnode->unpublished
1932 && vnode->private_node == privateNode) {
1933 // already known, but not published
1934 } else if (vnode == NULL && privateNode != NULL) {
1935 status = create_new_vnode(&vnode, volume->id, vnodeID);
1936 if (status == FSSH_B_OK) {
1937 vnode->private_node = privateNode;
1938 vnode->ops = ops;
1939 vnode->busy = true;
1940 vnode->unpublished = true;
1942 } else
1943 status = FSSH_B_BAD_VALUE;
1945 // create sub vnodes, if necessary
1946 if (status == FSSH_B_OK && volume->sub_volume != NULL) {
1947 locker.Unlock();
1949 fssh_fs_volume *subVolume = volume;
1950 while (status == FSSH_B_OK && subVolume->sub_volume != NULL) {
1951 subVolume = subVolume->sub_volume;
1952 status = subVolume->ops->create_sub_vnode(subVolume, vnodeID,
1953 vnode);
1956 if (status != FSSH_B_OK) {
1957 // error -- clean up the created sub vnodes
1958 while (subVolume->super_volume != volume) {
1959 subVolume = subVolume->super_volume;
1960 subVolume->ops->delete_sub_vnode(subVolume, vnode);
1964 locker.Lock();
1967 if (status == FSSH_B_OK) {
1968 vnode->type = type;
1969 vnode->busy = false;
1970 vnode->unpublished = false;
1973 TRACE(("returns: %s\n", strerror(status)));
1975 return status;
1979 extern "C" fssh_status_t
1980 fssh_get_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID,
1981 void **privateNode)
1983 struct vnode *vnode;
1985 if (volume == NULL)
1986 return FSSH_B_BAD_VALUE;
1988 fssh_status_t status = get_vnode(volume->id, vnodeID, &vnode, true);
1989 if (status < FSSH_B_OK)
1990 return status;
1992 // If this is a layered FS, we need to get the node cookie for the requested
1993 // layer.
1994 if (HAS_FS_CALL(vnode, get_super_vnode)) {
1995 fssh_fs_vnode resolvedNode;
1996 fssh_status_t status = FS_CALL(vnode, get_super_vnode, volume,
1997 &resolvedNode);
1998 if (status != FSSH_B_OK) {
1999 fssh_panic("get_vnode(): Failed to get super node for vnode %p, "
2000 "volume: %p", vnode, volume);
2001 put_vnode(vnode);
2002 return status;
2005 if (privateNode != NULL)
2006 *privateNode = resolvedNode.private_node;
2007 } else if (privateNode != NULL)
2008 *privateNode = vnode->private_node;
2010 return FSSH_B_OK;
2014 extern "C" fssh_status_t
2015 fssh_acquire_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID)
2017 struct vnode *vnode;
2019 fssh_mutex_lock(&sVnodeMutex);
2020 vnode = lookup_vnode(volume->id, vnodeID);
2021 fssh_mutex_unlock(&sVnodeMutex);
2023 if (vnode == NULL)
2024 return FSSH_B_BAD_VALUE;
2026 inc_vnode_ref_count(vnode);
2027 return FSSH_B_OK;
2031 extern "C" fssh_status_t
2032 fssh_put_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID)
2034 struct vnode *vnode;
2036 fssh_mutex_lock(&sVnodeMutex);
2037 vnode = lookup_vnode(volume->id, vnodeID);
2038 fssh_mutex_unlock(&sVnodeMutex);
2040 if (vnode == NULL)
2041 return FSSH_B_BAD_VALUE;
2043 dec_vnode_ref_count(vnode, true);
2044 return FSSH_B_OK;
2048 extern "C" fssh_status_t
2049 fssh_remove_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID)
2051 struct vnode *vnode;
2052 bool remove = false;
2054 MutexLocker locker(sVnodeMutex);
2056 vnode = lookup_vnode(volume->id, vnodeID);
2057 if (vnode == NULL)
2058 return FSSH_B_ENTRY_NOT_FOUND;
2060 if (vnode->covered_by != NULL) {
2061 // this vnode is in use
2062 fssh_mutex_unlock(&sVnodeMutex);
2063 return FSSH_B_BUSY;
2066 vnode->remove = true;
2067 if (vnode->unpublished) {
2068 // prepare the vnode for deletion
2069 vnode->busy = true;
2070 remove = true;
2073 locker.Unlock();
2075 if (remove) {
2076 // if the vnode hasn't been published yet, we delete it here
2077 fssh_atomic_add(&vnode->ref_count, -1);
2078 free_vnode(vnode, true);
2081 return FSSH_B_OK;
2085 extern "C" fssh_status_t
2086 fssh_unremove_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID)
2088 struct vnode *vnode;
2090 fssh_mutex_lock(&sVnodeMutex);
2092 vnode = lookup_vnode(volume->id, vnodeID);
2093 if (vnode)
2094 vnode->remove = false;
2096 fssh_mutex_unlock(&sVnodeMutex);
2097 return FSSH_B_OK;
2101 extern "C" fssh_status_t
2102 fssh_get_vnode_removed(fssh_fs_volume *volume, fssh_vnode_id vnodeID, bool* removed)
2104 fssh_mutex_lock(&sVnodeMutex);
2106 fssh_status_t result;
2108 if (struct vnode* vnode = lookup_vnode(volume->id, vnodeID)) {
2109 if (removed)
2110 *removed = vnode->remove;
2111 result = FSSH_B_OK;
2112 } else
2113 result = FSSH_B_BAD_VALUE;
2115 fssh_mutex_unlock(&sVnodeMutex);
2116 return result;
2120 extern "C" fssh_fs_volume*
2121 fssh_volume_for_vnode(fssh_fs_vnode *_vnode)
2123 if (_vnode == NULL)
2124 return NULL;
2126 struct vnode* vnode = static_cast<struct vnode*>(_vnode);
2127 return vnode->mount->volume;
2131 extern "C" fssh_status_t
2132 fssh_check_access_permissions(int accessMode, fssh_mode_t mode,
2133 fssh_gid_t nodeGroupID, fssh_uid_t nodeUserID)
2135 // get node permissions
2136 int userPermissions = (mode & FSSH_S_IRWXU) >> 6;
2137 int groupPermissions = (mode & FSSH_S_IRWXG) >> 3;
2138 int otherPermissions = mode & FSSH_S_IRWXO;
2140 // get the node permissions for this uid/gid
2141 int permissions = 0;
2142 fssh_uid_t uid = fssh_geteuid();
2144 if (uid == 0) {
2145 // user is root
2146 // root has always read/write permission, but at least one of the
2147 // X bits must be set for execute permission
2148 permissions = userPermissions | groupPermissions | otherPermissions
2149 | FSSH_S_IROTH | FSSH_S_IWOTH;
2150 if (FSSH_S_ISDIR(mode))
2151 permissions |= FSSH_S_IXOTH;
2152 } else if (uid == nodeUserID) {
2153 // user is node owner
2154 permissions = userPermissions;
2155 } else if (fssh_getegid() == nodeGroupID) {
2156 // user is in owning group
2157 permissions = groupPermissions;
2158 } else {
2159 // user is one of the others
2160 permissions = otherPermissions;
2163 return (accessMode & ~permissions) == 0 ? FSSH_B_OK : FSSH_B_NOT_ALLOWED;
2167 //! Works directly on the host's file system
2168 extern "C" fssh_status_t
2169 fssh_read_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs,
2170 fssh_size_t count, fssh_size_t *_numBytes)
2172 // check how much the iovecs allow us to read
2173 fssh_size_t toRead = 0;
2174 for (fssh_size_t i = 0; i < count; i++)
2175 toRead += vecs[i].iov_len;
2177 fssh_iovec* newVecs = NULL;
2178 if (*_numBytes < toRead) {
2179 // We're supposed to read less than specified by the vecs. Since
2180 // readv_pos() doesn't support this, we need to clone the vecs.
2181 newVecs = new(std::nothrow) fssh_iovec[count];
2182 if (!newVecs)
2183 return FSSH_B_NO_MEMORY;
2185 fssh_size_t newCount = 0;
2186 for (fssh_size_t i = 0; i < count && toRead > 0; i++) {
2187 fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toRead);
2188 newVecs[i].iov_base = vecs[i].iov_base;
2189 newVecs[i].iov_len = vecLen;
2190 toRead -= vecLen;
2191 newCount++;
2194 vecs = newVecs;
2195 count = newCount;
2198 fssh_ssize_t bytesRead = fssh_readv_pos(fd, pos, vecs, count);
2199 delete[] newVecs;
2200 if (bytesRead < 0)
2201 return fssh_get_errno();
2203 *_numBytes = bytesRead;
2204 return FSSH_B_OK;
2208 //! Works directly on the host's file system
2209 extern "C" fssh_status_t
2210 fssh_write_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs,
2211 fssh_size_t count, fssh_size_t *_numBytes)
2213 // check how much the iovecs allow us to write
2214 fssh_size_t toWrite = 0;
2215 for (fssh_size_t i = 0; i < count; i++)
2216 toWrite += vecs[i].iov_len;
2218 fssh_iovec* newVecs = NULL;
2219 if (*_numBytes < toWrite) {
2220 // We're supposed to write less than specified by the vecs. Since
2221 // writev_pos() doesn't support this, we need to clone the vecs.
2222 newVecs = new(std::nothrow) fssh_iovec[count];
2223 if (!newVecs)
2224 return FSSH_B_NO_MEMORY;
2226 fssh_size_t newCount = 0;
2227 for (fssh_size_t i = 0; i < count && toWrite > 0; i++) {
2228 fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toWrite);
2229 newVecs[i].iov_base = vecs[i].iov_base;
2230 newVecs[i].iov_len = vecLen;
2231 toWrite -= vecLen;
2232 newCount++;
2235 vecs = newVecs;
2236 count = newCount;
2239 fssh_ssize_t bytesWritten = fssh_writev_pos(fd, pos, vecs, count);
2240 delete[] newVecs;
2241 if (bytesWritten < 0)
2242 return fssh_get_errno();
2244 *_numBytes = bytesWritten;
2245 return FSSH_B_OK;
2249 //! Works directly on the host's file system
2250 extern "C" fssh_status_t
2251 fssh_read_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs,
2252 fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount,
2253 uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_bytes)
2255 return common_file_io_vec_pages(fd, fileVecs, fileVecCount,
2256 vecs, vecCount, _vecIndex, _vecOffset, _bytes, false);
2260 //! Works directly on the host's file system
2261 extern "C" fssh_status_t
2262 fssh_write_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs,
2263 fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount,
2264 uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_bytes)
2266 return common_file_io_vec_pages(fd, fileVecs, fileVecCount,
2267 vecs, vecCount, _vecIndex, _vecOffset, _bytes, true);
2271 extern "C" fssh_status_t
2272 fssh_entry_cache_add(fssh_dev_t mountID, fssh_ino_t dirID, const char* name,
2273 fssh_ino_t nodeID)
2275 // We don't implement an entry cache in the FS shell.
2276 return FSSH_B_OK;
2280 extern "C" fssh_status_t
2281 fssh_entry_cache_add_missing(fssh_dev_t mountID, fssh_ino_t dirID,
2282 const char* name)
2284 // We don't implement an entry cache in the FS shell.
2285 return FSSH_B_OK;
2289 extern "C" fssh_status_t
2290 fssh_entry_cache_remove(fssh_dev_t mountID, fssh_ino_t dirID, const char* name)
2292 // We don't implement an entry cache in the FS shell.
2293 return FSSH_B_ENTRY_NOT_FOUND;
2297 // #pragma mark - private VFS API
2298 // Functions the VFS exports for other parts of the kernel
2301 /** Acquires another reference to the vnode that has to be released
2302 * by calling vfs_put_vnode().
2305 void
2306 vfs_acquire_vnode(void *_vnode)
2308 inc_vnode_ref_count((struct vnode *)_vnode);
2312 /** This is currently called from file_cache_create() only.
2313 * It's probably a temporary solution as long as devfs requires that
2314 * fs_read_pages()/fs_write_pages() are called with the standard
2315 * open cookie and not with a device cookie.
2316 * If that's done differently, remove this call; it has no other
2317 * purpose.
2320 fssh_status_t
2321 vfs_get_cookie_from_fd(int fd, void **_cookie)
2323 struct file_descriptor *descriptor;
2325 descriptor = get_fd(get_current_io_context(true), fd);
2326 if (descriptor == NULL)
2327 return FSSH_B_FILE_ERROR;
2329 *_cookie = descriptor->cookie;
2330 return FSSH_B_OK;
2335 vfs_get_vnode_from_fd(int fd, bool kernel, void **vnode)
2337 *vnode = get_vnode_from_fd(fd, kernel);
2339 if (*vnode == NULL)
2340 return FSSH_B_FILE_ERROR;
2342 return FSSH_B_NO_ERROR;
2346 fssh_status_t
2347 vfs_get_vnode_from_path(const char *path, bool kernel, void **_vnode)
2349 TRACE(("vfs_get_vnode_from_path: entry. path = '%s', kernel %d\n", path, kernel));
2351 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
2352 if (pathBuffer.InitCheck() != FSSH_B_OK)
2353 return FSSH_B_NO_MEMORY;
2355 char *buffer = pathBuffer.LockBuffer();
2356 fssh_strlcpy(buffer, path, pathBuffer.BufferSize());
2358 struct vnode *vnode;
2359 fssh_status_t status = path_to_vnode(buffer, true, &vnode, NULL, kernel);
2360 if (status < FSSH_B_OK)
2361 return status;
2363 *_vnode = vnode;
2364 return FSSH_B_OK;
2368 fssh_status_t
2369 vfs_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void **_vnode)
2371 struct vnode *vnode;
2373 fssh_status_t status = get_vnode(mountID, vnodeID, &vnode, false);
2374 if (status < FSSH_B_OK)
2375 return status;
2377 *_vnode = vnode;
2378 return FSSH_B_OK;
2382 fssh_status_t
2383 vfs_read_pages(void *_vnode, void *cookie, fssh_off_t pos,
2384 const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes)
2386 struct vnode *vnode = (struct vnode *)_vnode;
2388 return FS_CALL(vnode, read_pages,
2389 cookie, pos, vecs, count, _numBytes);
2393 fssh_status_t
2394 vfs_write_pages(void *_vnode, void *cookie, fssh_off_t pos,
2395 const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes)
2397 struct vnode *vnode = (struct vnode *)_vnode;
2399 return FS_CALL(vnode, write_pages,
2400 cookie, pos, vecs, count, _numBytes);
2404 fssh_status_t
2405 vfs_entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID,
2406 const char *name, void **_vnode)
2408 return entry_ref_to_vnode(mountID, directoryID, name,
2409 (struct vnode **)_vnode);
2413 void
2414 vfs_fs_vnode_to_node_ref(void *_vnode, fssh_mount_id *_mountID,
2415 fssh_vnode_id *_vnodeID)
2417 struct vnode *vnode = (struct vnode *)_vnode;
2419 *_mountID = vnode->device;
2420 *_vnodeID = vnode->id;
2424 /** Looks up a vnode with the given mount and vnode ID.
2425 * Must only be used with "in-use" vnodes as it doesn't grab a reference
2426 * to the node.
2427 * It's currently only be used by file_cache_create().
2430 fssh_status_t
2431 vfs_lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID,
2432 struct vnode **_vnode)
2434 fssh_mutex_lock(&sVnodeMutex);
2435 struct vnode *vnode = lookup_vnode(mountID, vnodeID);
2436 fssh_mutex_unlock(&sVnodeMutex);
2438 if (vnode == NULL)
2439 return FSSH_B_ERROR;
2441 *_vnode = vnode;
2442 return FSSH_B_OK;
2446 fssh_status_t
2447 vfs_get_fs_node_from_path(fssh_fs_volume *volume, const char *path,
2448 bool kernel, void **_node)
2450 TRACE(("vfs_get_fs_node_from_path(volume = %p (%ld), path = \"%s\", "
2451 "kernel %d)\n", volume, volume->id, path, kernel));
2453 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
2454 if (pathBuffer.InitCheck() != FSSH_B_OK)
2455 return FSSH_B_NO_MEMORY;
2457 fs_mount *mount;
2458 fssh_status_t status = get_mount(volume->id, &mount);
2459 if (status < FSSH_B_OK)
2460 return status;
2462 char *buffer = pathBuffer.LockBuffer();
2463 fssh_strlcpy(buffer, path, pathBuffer.BufferSize());
2465 struct vnode *vnode = mount->root_vnode;
2467 if (buffer[0] == '/')
2468 status = path_to_vnode(buffer, true, &vnode, NULL, true);
2469 else {
2470 inc_vnode_ref_count(vnode);
2471 // vnode_path_to_vnode() releases a reference to the starting vnode
2472 status = vnode_path_to_vnode(vnode, buffer, true, 0, &vnode, NULL);
2475 put_mount(mount);
2477 if (status < FSSH_B_OK)
2478 return status;
2480 if (vnode->device != volume->id) {
2481 // wrong mount ID - must not gain access on foreign file system nodes
2482 put_vnode(vnode);
2483 return FSSH_B_BAD_VALUE;
2486 // Use get_vnode() to resolve the cookie for the right layer.
2487 status = ::fssh_get_vnode(volume, vnode->id, _node);
2488 put_vnode(vnode);
2490 return FSSH_B_OK;
2494 /** Finds the full path to the file that contains the module \a moduleName,
2495 * puts it into \a pathBuffer, and returns FSSH_B_OK for success.
2496 * If \a pathBuffer was too small, it returns \c FSSH_B_BUFFER_OVERFLOW,
2497 * \c FSSH_B_ENTRY_NOT_FOUNT if no file could be found.
2498 * \a pathBuffer is clobbered in any case and must not be relied on if this
2499 * functions returns unsuccessfully.
2502 fssh_status_t
2503 vfs_get_module_path(const char *basePath, const char *moduleName, char *pathBuffer,
2504 fssh_size_t bufferSize)
2506 struct vnode *dir, *file;
2507 fssh_status_t status;
2508 fssh_size_t length;
2509 char *path;
2511 if (bufferSize == 0 || fssh_strlcpy(pathBuffer, basePath, bufferSize) >= bufferSize)
2512 return FSSH_B_BUFFER_OVERFLOW;
2514 status = path_to_vnode(pathBuffer, true, &dir, NULL, true);
2515 if (status < FSSH_B_OK)
2516 return status;
2518 // the path buffer had been clobbered by the above call
2519 length = fssh_strlcpy(pathBuffer, basePath, bufferSize);
2520 if (pathBuffer[length - 1] != '/')
2521 pathBuffer[length++] = '/';
2523 path = pathBuffer + length;
2524 bufferSize -= length;
2526 while (moduleName) {
2527 char *nextPath = fssh_strchr(moduleName, '/');
2528 if (nextPath == NULL)
2529 length = fssh_strlen(moduleName);
2530 else {
2531 length = nextPath - moduleName;
2532 nextPath++;
2535 if (length + 1 >= bufferSize) {
2536 status = FSSH_B_BUFFER_OVERFLOW;
2537 goto err;
2540 fssh_memcpy(path, moduleName, length);
2541 path[length] = '\0';
2542 moduleName = nextPath;
2544 status = vnode_path_to_vnode(dir, path, true, 0, &file, NULL);
2545 if (status < FSSH_B_OK) {
2546 // vnode_path_to_vnode() has already released the reference to dir
2547 return status;
2550 if (FSSH_S_ISDIR(file->type)) {
2551 // goto the next directory
2552 path[length] = '/';
2553 path[length + 1] = '\0';
2554 path += length + 1;
2555 bufferSize -= length + 1;
2557 dir = file;
2558 } else if (FSSH_S_ISREG(file->type)) {
2559 // it's a file so it should be what we've searched for
2560 put_vnode(file);
2562 return FSSH_B_OK;
2563 } else {
2564 TRACE(("vfs_get_module_path(): something is strange here: %d...\n", file->type));
2565 status = FSSH_B_ERROR;
2566 dir = file;
2567 goto err;
2571 // if we got here, the moduleName just pointed to a directory, not to
2572 // a real module - what should we do in this case?
2573 status = FSSH_B_ENTRY_NOT_FOUND;
2575 err:
2576 put_vnode(dir);
2577 return status;
2581 /** \brief Normalizes a given path.
2583 * The path must refer to an existing or non-existing entry in an existing
2584 * directory, that is chopping off the leaf component the remaining path must
2585 * refer to an existing directory.
2587 * The returned will be canonical in that it will be absolute, will not
2588 * contain any "." or ".." components or duplicate occurrences of '/'s,
2589 * and none of the directory components will by symbolic links.
2591 * Any two paths referring to the same entry, will result in the same
2592 * normalized path (well, that is pretty much the definition of `normalized',
2593 * isn't it :-).
2595 * \param path The path to be normalized.
2596 * \param buffer The buffer into which the normalized path will be written.
2597 * \param bufferSize The size of \a buffer.
2598 * \param kernel \c true, if the IO context of the kernel shall be used,
2599 * otherwise that of the team this thread belongs to. Only relevant,
2600 * if the path is relative (to get the CWD).
2601 * \return \c FSSH_B_OK if everything went fine, another error code otherwise.
2604 fssh_status_t
2605 vfs_normalize_path(const char *path, char *buffer, fssh_size_t bufferSize,
2606 bool kernel)
2608 if (!path || !buffer || bufferSize < 1)
2609 return FSSH_B_BAD_VALUE;
2611 TRACE(("vfs_normalize_path(`%s')\n", path));
2613 // copy the supplied path to the stack, so it can be modified
2614 KPath mutablePathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
2615 if (mutablePathBuffer.InitCheck() != FSSH_B_OK)
2616 return FSSH_B_NO_MEMORY;
2618 char *mutablePath = mutablePathBuffer.LockBuffer();
2619 if (fssh_strlcpy(mutablePath, path, FSSH_B_PATH_NAME_LENGTH) >= FSSH_B_PATH_NAME_LENGTH)
2620 return FSSH_B_NAME_TOO_LONG;
2622 // get the dir vnode and the leaf name
2623 struct vnode *dirNode;
2624 char leaf[FSSH_B_FILE_NAME_LENGTH];
2625 fssh_status_t error = path_to_dir_vnode(mutablePath, &dirNode, leaf, kernel);
2626 if (error != FSSH_B_OK) {
2627 TRACE(("vfs_normalize_path(): failed to get dir vnode: %s\n", strerror(error)));
2628 return error;
2631 // if the leaf is "." or "..", we directly get the correct directory
2632 // vnode and ignore the leaf later
2633 bool isDir = (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0);
2634 if (isDir)
2635 error = vnode_path_to_vnode(dirNode, leaf, false, 0, &dirNode, NULL);
2636 if (error != FSSH_B_OK) {
2637 TRACE(("vfs_normalize_path(): failed to get dir vnode for \".\" or \"..\": %s\n",
2638 strerror(error)));
2639 return error;
2642 // get the directory path
2643 error = dir_vnode_to_path(dirNode, buffer, bufferSize);
2644 put_vnode(dirNode);
2645 if (error < FSSH_B_OK) {
2646 TRACE(("vfs_normalize_path(): failed to get dir path: %s\n", strerror(error)));
2647 return error;
2650 // append the leaf name
2651 if (!isDir) {
2652 // insert a directory separator only if this is not the file system root
2653 if ((fssh_strcmp(buffer, "/") != 0
2654 && fssh_strlcat(buffer, "/", bufferSize) >= bufferSize)
2655 || fssh_strlcat(buffer, leaf, bufferSize) >= bufferSize) {
2656 return FSSH_B_NAME_TOO_LONG;
2660 TRACE(("vfs_normalize_path() -> `%s'\n", buffer));
2661 return FSSH_B_OK;
2665 void
2666 vfs_put_vnode(void *_vnode)
2668 put_vnode((struct vnode *)_vnode);
2672 fssh_status_t
2673 vfs_get_cwd(fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID)
2675 // Get current working directory from io context
2676 struct io_context *context = get_current_io_context(false);
2677 fssh_status_t status = FSSH_B_OK;
2679 fssh_mutex_lock(&context->io_mutex);
2681 if (context->cwd != NULL) {
2682 *_mountID = context->cwd->device;
2683 *_vnodeID = context->cwd->id;
2684 } else
2685 status = FSSH_B_ERROR;
2687 fssh_mutex_unlock(&context->io_mutex);
2688 return status;
2692 fssh_status_t
2693 vfs_get_file_map(void *_vnode, fssh_off_t offset, fssh_size_t size,
2694 fssh_file_io_vec *vecs, fssh_size_t *_count)
2696 struct vnode *vnode = (struct vnode *)_vnode;
2698 FUNCTION(("vfs_get_file_map: vnode %p, vecs %p, offset %lld, size = %u\n", vnode, vecs, offset, (unsigned)size));
2700 return FS_CALL(vnode, get_file_map, offset, size, vecs, _count);
2704 fssh_status_t
2705 vfs_stat_vnode(void *_vnode, struct fssh_stat *stat)
2707 struct vnode *vnode = (struct vnode *)_vnode;
2709 fssh_status_t status = FS_CALL(vnode, read_stat, stat);
2711 // fill in the st_dev and st_ino fields
2712 if (status == FSSH_B_OK) {
2713 stat->fssh_st_dev = vnode->device;
2714 stat->fssh_st_ino = vnode->id;
2717 return status;
2721 fssh_status_t
2722 vfs_get_vnode_name(void *_vnode, char *name, fssh_size_t nameSize)
2724 return get_vnode_name((struct vnode *)_vnode, NULL, name, nameSize);
2728 fssh_status_t
2729 vfs_entry_ref_to_path(fssh_dev_t device, fssh_ino_t inode, const char *leaf,
2730 bool kernel, char *path, fssh_size_t pathLength)
2732 struct vnode *vnode;
2733 fssh_status_t status;
2735 // filter invalid leaf names
2736 if (leaf != NULL && (leaf[0] == '\0' || fssh_strchr(leaf, '/')))
2737 return FSSH_B_BAD_VALUE;
2739 // get the vnode matching the dir's node_ref
2740 if (leaf && (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0)) {
2741 // special cases "." and "..": we can directly get the vnode of the
2742 // referenced directory
2743 status = entry_ref_to_vnode(device, inode, leaf, &vnode);
2744 leaf = NULL;
2745 } else
2746 status = get_vnode(device, inode, &vnode, false);
2747 if (status < FSSH_B_OK)
2748 return status;
2750 // get the directory path
2751 status = dir_vnode_to_path(vnode, path, pathLength);
2752 put_vnode(vnode);
2753 // we don't need the vnode anymore
2754 if (status < FSSH_B_OK)
2755 return status;
2757 // append the leaf name
2758 if (leaf) {
2759 // insert a directory separator if this is not the file system root
2760 if ((fssh_strcmp(path, "/") && fssh_strlcat(path, "/", pathLength)
2761 >= pathLength)
2762 || fssh_strlcat(path, leaf, pathLength) >= pathLength) {
2763 return FSSH_B_NAME_TOO_LONG;
2767 return FSSH_B_OK;
2771 /** If the given descriptor locked its vnode, that lock will be released.
2774 void
2775 vfs_unlock_vnode_if_locked(struct file_descriptor *descriptor)
2777 struct vnode *vnode = fd_vnode(descriptor);
2779 if (vnode != NULL && vnode->mandatory_locked_by == descriptor)
2780 vnode->mandatory_locked_by = NULL;
2784 /** Closes all file descriptors of the specified I/O context that
2785 * don't have the FSSH_O_CLOEXEC flag set.
2788 void
2789 vfs_exec_io_context(void *_context)
2791 struct io_context *context = (struct io_context *)_context;
2792 uint32_t i;
2794 for (i = 0; i < context->table_size; i++) {
2795 fssh_mutex_lock(&context->io_mutex);
2797 struct file_descriptor *descriptor = context->fds[i];
2798 bool remove = false;
2800 if (descriptor != NULL && fd_close_on_exec(context, i)) {
2801 context->fds[i] = NULL;
2802 context->num_used_fds--;
2804 remove = true;
2807 fssh_mutex_unlock(&context->io_mutex);
2809 if (remove) {
2810 close_fd(descriptor);
2811 put_fd(descriptor);
2817 /** Sets up a new io_control structure, and inherits the properties
2818 * of the parent io_control if it is given.
2821 void *
2822 vfs_new_io_context(void *_parentContext)
2824 fssh_size_t tableSize;
2825 struct io_context *context;
2826 struct io_context *parentContext;
2828 context = (io_context *)malloc(sizeof(struct io_context));
2829 if (context == NULL)
2830 return NULL;
2832 fssh_memset(context, 0, sizeof(struct io_context));
2834 parentContext = (struct io_context *)_parentContext;
2835 if (parentContext)
2836 tableSize = parentContext->table_size;
2837 else
2838 tableSize = DEFAULT_FD_TABLE_SIZE;
2840 // allocate space for FDs and their close-on-exec flag
2841 context->fds = (file_descriptor **)malloc(sizeof(struct file_descriptor *) * tableSize
2842 + (tableSize + 7) / 8);
2843 if (context->fds == NULL) {
2844 free(context);
2845 return NULL;
2848 fssh_memset(context->fds, 0, sizeof(struct file_descriptor *) * tableSize
2849 + (tableSize + 7) / 8);
2850 context->fds_close_on_exec = (uint8_t *)(context->fds + tableSize);
2852 fssh_mutex_init(&context->io_mutex, "I/O context");
2854 // Copy all parent files which don't have the FSSH_O_CLOEXEC flag set
2856 if (parentContext) {
2857 fssh_size_t i;
2859 fssh_mutex_lock(&parentContext->io_mutex);
2861 context->cwd = parentContext->cwd;
2862 if (context->cwd)
2863 inc_vnode_ref_count(context->cwd);
2865 for (i = 0; i < tableSize; i++) {
2866 struct file_descriptor *descriptor = parentContext->fds[i];
2868 if (descriptor != NULL && !fd_close_on_exec(parentContext, i)) {
2869 context->fds[i] = descriptor;
2870 context->num_used_fds++;
2871 fssh_atomic_add(&descriptor->ref_count, 1);
2872 fssh_atomic_add(&descriptor->open_count, 1);
2876 fssh_mutex_unlock(&parentContext->io_mutex);
2877 } else {
2878 context->cwd = sRoot;
2880 if (context->cwd)
2881 inc_vnode_ref_count(context->cwd);
2884 context->table_size = tableSize;
2886 return context;
2890 fssh_status_t
2891 vfs_free_io_context(void *_ioContext)
2893 struct io_context *context = (struct io_context *)_ioContext;
2894 uint32_t i;
2896 if (context->cwd)
2897 dec_vnode_ref_count(context->cwd, false);
2899 fssh_mutex_lock(&context->io_mutex);
2901 for (i = 0; i < context->table_size; i++) {
2902 if (struct file_descriptor *descriptor = context->fds[i]) {
2903 close_fd(descriptor);
2904 put_fd(descriptor);
2908 fssh_mutex_destroy(&context->io_mutex);
2910 free(context->fds);
2911 free(context);
2913 return FSSH_B_OK;
2917 fssh_status_t
2918 vfs_init(kernel_args *args)
2920 sVnodeTable = hash_init(VNODE_HASH_TABLE_SIZE, fssh_offsetof(struct vnode, next),
2921 &vnode_compare, &vnode_hash);
2922 if (sVnodeTable == NULL)
2923 fssh_panic("vfs_init: error creating vnode hash table\n");
2925 list_init_etc(&sUnusedVnodeList, fssh_offsetof(struct vnode, unused_link));
2927 sMountsTable = hash_init(MOUNTS_HASH_TABLE_SIZE, fssh_offsetof(struct fs_mount, next),
2928 &mount_compare, &mount_hash);
2929 if (sMountsTable == NULL)
2930 fssh_panic("vfs_init: error creating mounts hash table\n");
2932 sRoot = NULL;
2934 fssh_mutex_init(&sFileSystemsMutex, "vfs_lock");
2935 fssh_recursive_lock_init(&sMountOpLock, "vfs_mount_op_lock");
2936 fssh_mutex_init(&sMountMutex, "vfs_mount_lock");
2937 fssh_mutex_init(&sVnodeCoveredByMutex, "vfs_vnode_covered_by_lock");
2938 fssh_mutex_init(&sVnodeMutex, "vfs_vnode_lock");
2940 if (block_cache_init() != FSSH_B_OK)
2941 return FSSH_B_ERROR;
2943 return file_cache_init();
2947 // #pragma mark -
2948 // The filetype-dependent implementations (fd_ops + open/create/rename/remove, ...)
2951 /** Calls fs_open() on the given vnode and returns a new
2952 * file descriptor for it
2955 static int
2956 create_vnode(struct vnode *directory, const char *name, int openMode, int perms, bool kernel)
2958 struct vnode *vnode;
2959 void *cookie;
2960 fssh_vnode_id newID;
2961 int status;
2963 if (!HAS_FS_CALL(directory, create))
2964 return FSSH_EROFS;
2966 status = FS_CALL(directory, create, name, openMode, perms, &cookie, &newID);
2967 if (status < FSSH_B_OK)
2968 return status;
2970 fssh_mutex_lock(&sVnodeMutex);
2971 vnode = lookup_vnode(directory->device, newID);
2972 fssh_mutex_unlock(&sVnodeMutex);
2974 if (vnode == NULL) {
2975 fssh_dprintf("vfs: fs_create() returned success but there is no vnode!");
2976 return FSSH_EINVAL;
2979 if ((status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel)) >= 0)
2980 return status;
2982 // something went wrong, clean up
2984 FS_CALL(vnode, close, cookie);
2985 FS_CALL(vnode, free_cookie, cookie);
2986 put_vnode(vnode);
2988 FS_CALL(directory, unlink, name);
2990 return status;
2994 /** Calls fs_open() on the given vnode and returns a new
2995 * file descriptor for it
2998 static int
2999 open_vnode(struct vnode *vnode, int openMode, bool kernel)
3001 void *cookie;
3002 int status;
3004 status = FS_CALL(vnode, open, openMode, &cookie);
3005 if (status < 0)
3006 return status;
3008 status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel);
3009 if (status < 0) {
3010 FS_CALL(vnode, close, cookie);
3011 FS_CALL(vnode, free_cookie, cookie);
3013 return status;
3017 /** Calls fs open_dir() on the given vnode and returns a new
3018 * file descriptor for it
3021 static int
3022 open_dir_vnode(struct vnode *vnode, bool kernel)
3024 void *cookie;
3025 int status;
3027 status = FS_CALL(vnode, open_dir, &cookie);
3028 if (status < FSSH_B_OK)
3029 return status;
3031 // file is opened, create a fd
3032 status = get_new_fd(FDTYPE_DIR, NULL, vnode, cookie, 0, kernel);
3033 if (status >= 0)
3034 return status;
3036 FS_CALL(vnode, close_dir, cookie);
3037 FS_CALL(vnode, free_dir_cookie, cookie);
3039 return status;
3043 /** Calls fs open_attr_dir() on the given vnode and returns a new
3044 * file descriptor for it.
3045 * Used by attr_dir_open(), and attr_dir_open_fd().
3048 static int
3049 open_attr_dir_vnode(struct vnode *vnode, bool kernel)
3051 void *cookie;
3052 int status;
3054 if (!HAS_FS_CALL(vnode, open_attr_dir))
3055 return FSSH_EOPNOTSUPP;
3057 status = FS_CALL(vnode, open_attr_dir, &cookie);
3058 if (status < 0)
3059 return status;
3061 // file is opened, create a fd
3062 status = get_new_fd(FDTYPE_ATTR_DIR, NULL, vnode, cookie, 0, kernel);
3063 if (status >= 0)
3064 return status;
3066 FS_CALL(vnode, close_attr_dir, cookie);
3067 FS_CALL(vnode, free_attr_dir_cookie, cookie);
3069 return status;
3073 static int
3074 file_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, int perms, bool kernel)
3076 struct vnode *directory;
3077 int status;
3079 FUNCTION(("file_create_entry_ref: name = '%s', omode %x, perms %d, kernel %d\n", name, openMode, perms, kernel));
3081 // get directory to put the new file in
3082 status = get_vnode(mountID, directoryID, &directory, false);
3083 if (status < FSSH_B_OK)
3084 return status;
3086 status = create_vnode(directory, name, openMode, perms, kernel);
3087 put_vnode(directory);
3089 return status;
3093 static int
3094 file_create(int fd, char *path, int openMode, int perms, bool kernel)
3096 char name[FSSH_B_FILE_NAME_LENGTH];
3097 struct vnode *directory;
3098 int status;
3100 FUNCTION(("file_create: path '%s', omode %x, perms %d, kernel %d\n", path, openMode, perms, kernel));
3102 // get directory to put the new file in
3103 status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel);
3104 if (status < 0)
3105 return status;
3107 status = create_vnode(directory, name, openMode, perms, kernel);
3109 put_vnode(directory);
3110 return status;
3114 static int
3115 file_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, bool kernel)
3117 struct vnode *vnode;
3118 int status;
3120 if (name == NULL || *name == '\0')
3121 return FSSH_B_BAD_VALUE;
3123 FUNCTION(("file_open_entry_ref(ref = (%ld, %Ld, %s), openMode = %d)\n",
3124 mountID, directoryID, name, openMode));
3126 // get the vnode matching the entry_ref
3127 status = entry_ref_to_vnode(mountID, directoryID, name, &vnode);
3128 if (status < FSSH_B_OK)
3129 return status;
3131 status = open_vnode(vnode, openMode, kernel);
3132 if (status < FSSH_B_OK)
3133 put_vnode(vnode);
3135 return status;
3139 static int
3140 file_open(int fd, char *path, int openMode, bool kernel)
3142 int status = FSSH_B_OK;
3143 bool traverse = ((openMode & FSSH_O_NOTRAVERSE) == 0);
3145 FUNCTION(("file_open: fd: %d, entry path = '%s', omode %d, kernel %d\n",
3146 fd, path, openMode, kernel));
3148 // get the vnode matching the vnode + path combination
3149 struct vnode *vnode = NULL;
3150 fssh_vnode_id parentID;
3151 status = fd_and_path_to_vnode(fd, path, traverse, &vnode, &parentID, kernel);
3152 if (status != FSSH_B_OK)
3153 return status;
3155 // open the vnode
3156 status = open_vnode(vnode, openMode, kernel);
3157 // put only on error -- otherwise our reference was transferred to the FD
3158 if (status < FSSH_B_OK)
3159 put_vnode(vnode);
3161 return status;
3165 static fssh_status_t
3166 file_close(struct file_descriptor *descriptor)
3168 struct vnode *vnode = descriptor->u.vnode;
3169 fssh_status_t status = FSSH_B_OK;
3171 FUNCTION(("file_close(descriptor = %p)\n", descriptor));
3173 if (HAS_FS_CALL(vnode, close))
3174 status = FS_CALL(vnode, close, descriptor->cookie);
3176 return status;
3180 static void
3181 file_free_fd(struct file_descriptor *descriptor)
3183 struct vnode *vnode = descriptor->u.vnode;
3185 if (vnode != NULL) {
3186 FS_CALL(vnode, free_cookie, descriptor->cookie);
3187 put_vnode(vnode);
3192 static fssh_status_t
3193 file_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length)
3195 struct vnode *vnode = descriptor->u.vnode;
3197 FUNCTION(("file_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length));
3198 return FS_CALL(vnode, read, descriptor->cookie, pos, buffer, length);
3202 static fssh_status_t
3203 file_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length)
3205 struct vnode *vnode = descriptor->u.vnode;
3207 FUNCTION(("file_write: buf %p, pos %Ld, len %p\n", buffer, pos, length));
3208 return FS_CALL(vnode, write, descriptor->cookie, pos, buffer, length);
3212 static fssh_off_t
3213 file_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType)
3215 fssh_off_t offset;
3217 FUNCTION(("file_seek(pos = %Ld, seekType = %d)\n", pos, seekType));
3218 // ToDo: seek should fail for pipes and FIFOs...
3220 switch (seekType) {
3221 case FSSH_SEEK_SET:
3222 offset = 0;
3223 break;
3224 case FSSH_SEEK_CUR:
3225 offset = descriptor->pos;
3226 break;
3227 case FSSH_SEEK_END:
3229 struct vnode *vnode = descriptor->u.vnode;
3230 struct fssh_stat stat;
3231 fssh_status_t status;
3233 if (!HAS_FS_CALL(vnode, read_stat))
3234 return FSSH_EOPNOTSUPP;
3236 status = FS_CALL(vnode, read_stat, &stat);
3237 if (status < FSSH_B_OK)
3238 return status;
3240 offset = stat.fssh_st_size;
3241 break;
3243 default:
3244 return FSSH_B_BAD_VALUE;
3247 // assumes fssh_off_t is 64 bits wide
3248 if (offset > 0 && LLONG_MAX - offset < pos)
3249 return FSSH_EOVERFLOW;
3251 pos += offset;
3252 if (pos < 0)
3253 return FSSH_B_BAD_VALUE;
3255 return descriptor->pos = pos;
3259 static fssh_status_t
3260 dir_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, int perms, bool kernel)
3262 struct vnode *vnode;
3263 fssh_status_t status;
3265 if (name == NULL || *name == '\0')
3266 return FSSH_B_BAD_VALUE;
3268 FUNCTION(("dir_create_entry_ref(dev = %ld, ino = %Ld, name = '%s', perms = %d)\n", mountID, parentID, name, perms));
3270 status = get_vnode(mountID, parentID, &vnode, kernel);
3271 if (status < FSSH_B_OK)
3272 return status;
3274 if (HAS_FS_CALL(vnode, create_dir))
3275 status = FS_CALL(vnode, create_dir, name, perms);
3276 else
3277 status = FSSH_EROFS;
3279 put_vnode(vnode);
3280 return status;
3284 static fssh_status_t
3285 dir_create(int fd, char *path, int perms, bool kernel)
3287 char filename[FSSH_B_FILE_NAME_LENGTH];
3288 struct vnode *vnode;
3289 fssh_status_t status;
3291 FUNCTION(("dir_create: path '%s', perms %d, kernel %d\n", path, perms, kernel));
3293 status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel);
3294 if (status < 0)
3295 return status;
3297 if (HAS_FS_CALL(vnode, create_dir))
3298 status = FS_CALL(vnode, create_dir, filename, perms);
3299 else
3300 status = FSSH_EROFS;
3302 put_vnode(vnode);
3303 return status;
3307 static int
3308 dir_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, bool kernel)
3310 struct vnode *vnode;
3311 int status;
3313 FUNCTION(("dir_open_entry_ref()\n"));
3315 if (name && *name == '\0')
3316 return FSSH_B_BAD_VALUE;
3318 // get the vnode matching the entry_ref/node_ref
3319 if (name)
3320 status = entry_ref_to_vnode(mountID, parentID, name, &vnode);
3321 else
3322 status = get_vnode(mountID, parentID, &vnode, false);
3323 if (status < FSSH_B_OK)
3324 return status;
3326 status = open_dir_vnode(vnode, kernel);
3327 if (status < FSSH_B_OK)
3328 put_vnode(vnode);
3330 return status;
3334 static int
3335 dir_open(int fd, char *path, bool kernel)
3337 int status = FSSH_B_OK;
3339 FUNCTION(("dir_open: fd: %d, entry path = '%s', kernel %d\n", fd, path, kernel));
3341 // get the vnode matching the vnode + path combination
3342 struct vnode *vnode = NULL;
3343 fssh_vnode_id parentID;
3344 status = fd_and_path_to_vnode(fd, path, true, &vnode, &parentID, kernel);
3345 if (status != FSSH_B_OK)
3346 return status;
3348 // open the dir
3349 status = open_dir_vnode(vnode, kernel);
3350 if (status < FSSH_B_OK)
3351 put_vnode(vnode);
3353 return status;
3357 static fssh_status_t
3358 dir_close(struct file_descriptor *descriptor)
3360 struct vnode *vnode = descriptor->u.vnode;
3362 FUNCTION(("dir_close(descriptor = %p)\n", descriptor));
3364 if (HAS_FS_CALL(vnode, close_dir))
3365 return FS_CALL(vnode, close_dir, descriptor->cookie);
3367 return FSSH_B_OK;
3371 static void
3372 dir_free_fd(struct file_descriptor *descriptor)
3374 struct vnode *vnode = descriptor->u.vnode;
3376 if (vnode != NULL) {
3377 FS_CALL(vnode, free_dir_cookie, descriptor->cookie);
3378 put_vnode(vnode);
3383 static fssh_status_t
3384 dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
3385 fssh_size_t bufferSize, uint32_t *_count)
3387 return dir_read(descriptor->u.vnode, descriptor->cookie, buffer, bufferSize, _count);
3391 static void
3392 fix_dirent(struct vnode *parent, struct fssh_dirent *entry)
3394 // set d_pdev and d_pino
3395 entry->d_pdev = parent->device;
3396 entry->d_pino = parent->id;
3398 // If this is the ".." entry and the directory is the root of a FS,
3399 // we need to replace d_dev and d_ino with the actual values.
3400 if (fssh_strcmp(entry->d_name, "..") == 0
3401 && parent->mount->root_vnode == parent
3402 && parent->mount->covers_vnode) {
3403 inc_vnode_ref_count(parent);
3404 // vnode_path_to_vnode() puts the node
3406 // ".." is guaranteed to to be clobbered by this call
3407 struct vnode *vnode;
3408 fssh_status_t status = vnode_path_to_vnode(parent, (char*)"..", false,
3409 0, &vnode, NULL);
3411 if (status == FSSH_B_OK) {
3412 entry->d_dev = vnode->device;
3413 entry->d_ino = vnode->id;
3415 } else {
3416 // resolve mount points
3417 struct vnode *vnode = NULL;
3418 fssh_status_t status = get_vnode(entry->d_dev, entry->d_ino, &vnode, false);
3419 if (status != FSSH_B_OK)
3420 return;
3422 fssh_mutex_lock(&sVnodeCoveredByMutex);
3423 if (vnode->covered_by) {
3424 entry->d_dev = vnode->covered_by->device;
3425 entry->d_ino = vnode->covered_by->id;
3427 fssh_mutex_unlock(&sVnodeCoveredByMutex);
3429 put_vnode(vnode);
3434 static fssh_status_t
3435 dir_read(struct vnode *vnode, void *cookie, struct fssh_dirent *buffer,
3436 fssh_size_t bufferSize, uint32_t *_count)
3438 if (!HAS_FS_CALL(vnode, read_dir))
3439 return FSSH_EOPNOTSUPP;
3441 fssh_status_t error = FS_CALL(vnode, read_dir,cookie,buffer,bufferSize,_count);
3442 if (error != FSSH_B_OK)
3443 return error;
3445 // we need to adjust the read dirents
3446 if (*_count > 0) {
3447 // XXX: Currently reading only one dirent is supported. Make this a loop!
3448 fix_dirent(vnode, buffer);
3451 return error;
3455 static fssh_status_t
3456 dir_rewind(struct file_descriptor *descriptor)
3458 struct vnode *vnode = descriptor->u.vnode;
3460 if (HAS_FS_CALL(vnode, rewind_dir))
3461 return FS_CALL(vnode, rewind_dir,descriptor->cookie);
3463 return FSSH_EOPNOTSUPP;
3467 static fssh_status_t
3468 dir_remove(int fd, char *path, bool kernel)
3470 char name[FSSH_B_FILE_NAME_LENGTH];
3471 struct vnode *directory;
3472 fssh_status_t status;
3474 if (path != NULL) {
3475 // we need to make sure our path name doesn't stop with "/", ".", or ".."
3476 char *lastSlash = fssh_strrchr(path, '/');
3477 if (lastSlash != NULL) {
3478 char *leaf = lastSlash + 1;
3479 if (!fssh_strcmp(leaf, ".."))
3480 return FSSH_B_NOT_ALLOWED;
3482 // omit multiple slashes
3483 while (lastSlash > path && lastSlash[-1] == '/') {
3484 lastSlash--;
3487 if (!leaf[0]
3488 || !fssh_strcmp(leaf, ".")) {
3489 // "name/" -> "name", or "name/." -> "name"
3490 lastSlash[0] = '\0';
3492 } else if (!fssh_strcmp(path, ".."))
3493 return FSSH_B_NOT_ALLOWED;
3496 status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel);
3497 if (status < FSSH_B_OK)
3498 return status;
3500 if (HAS_FS_CALL(directory, remove_dir)) {
3501 status = FS_CALL(directory, remove_dir, name);
3502 } else
3503 status = FSSH_EROFS;
3505 put_vnode(directory);
3506 return status;
3510 static fssh_status_t
3511 common_ioctl(struct file_descriptor *descriptor, uint32_t op, void *buffer,
3512 fssh_size_t length)
3514 struct vnode *vnode = descriptor->u.vnode;
3516 if (HAS_FS_CALL(vnode, ioctl)) {
3517 return FS_CALL(vnode, ioctl,
3518 descriptor->cookie, op, buffer, length);
3521 return FSSH_EOPNOTSUPP;
3525 static fssh_status_t
3526 common_fcntl(int fd, int op, uint32_t argument, bool kernel)
3528 struct file_descriptor *descriptor;
3529 struct vnode *vnode;
3530 fssh_status_t status;
3532 FUNCTION(("common_fcntl(fd = %d, op = %d, argument = %lx, %s)\n",
3533 fd, op, argument, kernel ? "kernel" : "user"));
3535 descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3536 if (descriptor == NULL)
3537 return FSSH_B_FILE_ERROR;
3539 switch (op) {
3540 case FSSH_F_SETFD:
3542 struct io_context *context = get_current_io_context(kernel);
3543 // Set file descriptor flags
3545 // FSSH_O_CLOEXEC is the only flag available at this time
3546 fssh_mutex_lock(&context->io_mutex);
3547 fd_set_close_on_exec(context, fd, argument == FSSH_FD_CLOEXEC);
3548 fssh_mutex_unlock(&context->io_mutex);
3550 status = FSSH_B_OK;
3551 break;
3554 case FSSH_F_GETFD:
3556 struct io_context *context = get_current_io_context(kernel);
3558 // Get file descriptor flags
3559 fssh_mutex_lock(&context->io_mutex);
3560 status = fd_close_on_exec(context, fd) ? FSSH_FD_CLOEXEC : 0;
3561 fssh_mutex_unlock(&context->io_mutex);
3562 break;
3565 case FSSH_F_SETFL:
3566 // Set file descriptor open mode
3567 if (HAS_FS_CALL(vnode, set_flags)) {
3568 // we only accept changes to FSSH_O_APPEND and FSSH_O_NONBLOCK
3569 argument &= FSSH_O_APPEND | FSSH_O_NONBLOCK;
3571 status = FS_CALL(vnode, set_flags, descriptor->cookie, (int)argument);
3572 if (status == FSSH_B_OK) {
3573 // update this descriptor's open_mode field
3574 descriptor->open_mode = (descriptor->open_mode & ~(FSSH_O_APPEND | FSSH_O_NONBLOCK))
3575 | argument;
3577 } else
3578 status = FSSH_EOPNOTSUPP;
3579 break;
3581 case FSSH_F_GETFL:
3582 // Get file descriptor open mode
3583 status = descriptor->open_mode;
3584 break;
3586 case FSSH_F_DUPFD:
3588 struct io_context *context = get_current_io_context(kernel);
3590 status = new_fd_etc(context, descriptor, (int)argument);
3591 if (status >= 0) {
3592 fssh_mutex_lock(&context->io_mutex);
3593 fd_set_close_on_exec(context, fd, false);
3594 fssh_mutex_unlock(&context->io_mutex);
3596 fssh_atomic_add(&descriptor->ref_count, 1);
3598 break;
3601 case FSSH_F_GETLK:
3602 case FSSH_F_SETLK:
3603 case FSSH_F_SETLKW:
3604 status = FSSH_B_BAD_VALUE;
3605 break;
3607 // ToDo: add support for more ops?
3609 default:
3610 status = FSSH_B_BAD_VALUE;
3613 put_fd(descriptor);
3614 return status;
3618 static fssh_status_t
3619 common_sync(int fd, bool kernel)
3621 struct file_descriptor *descriptor;
3622 struct vnode *vnode;
3623 fssh_status_t status;
3625 FUNCTION(("common_fsync: entry. fd %d kernel %d\n", fd, kernel));
3627 descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3628 if (descriptor == NULL)
3629 return FSSH_B_FILE_ERROR;
3631 if (HAS_FS_CALL(vnode, fsync))
3632 status = FS_CALL_NO_PARAMS(vnode, fsync);
3633 else
3634 status = FSSH_EOPNOTSUPP;
3636 put_fd(descriptor);
3637 return status;
3641 static fssh_status_t
3642 common_lock_node(int fd, bool kernel)
3644 struct file_descriptor *descriptor;
3645 struct vnode *vnode;
3647 descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3648 if (descriptor == NULL)
3649 return FSSH_B_FILE_ERROR;
3651 fssh_status_t status = FSSH_B_OK;
3653 // We need to set the locking atomically - someone
3654 // else might set one at the same time
3655 #ifdef __x86_64__
3656 if (fssh_atomic_test_and_set64((int64_t *)&vnode->mandatory_locked_by,
3657 (fssh_addr_t)descriptor, 0) != 0)
3658 #else
3659 if (fssh_atomic_test_and_set((int32_t *)&vnode->mandatory_locked_by,
3660 (fssh_addr_t)descriptor, 0) != 0)
3661 #endif
3662 status = FSSH_B_BUSY;
3664 put_fd(descriptor);
3665 return status;
3669 static fssh_status_t
3670 common_unlock_node(int fd, bool kernel)
3672 struct file_descriptor *descriptor;
3673 struct vnode *vnode;
3675 descriptor = get_fd_and_vnode(fd, &vnode, kernel);
3676 if (descriptor == NULL)
3677 return FSSH_B_FILE_ERROR;
3679 fssh_status_t status = FSSH_B_OK;
3681 // We need to set the locking atomically - someone
3682 // else might set one at the same time
3683 #ifdef __x86_64__
3684 if (fssh_atomic_test_and_set64((int64_t *)&vnode->mandatory_locked_by,
3685 0, (fssh_addr_t)descriptor) != (int64_t)descriptor)
3686 #else
3687 if (fssh_atomic_test_and_set((int32_t *)&vnode->mandatory_locked_by,
3688 0, (fssh_addr_t)descriptor) != (int32_t)descriptor)
3689 #endif
3690 status = FSSH_B_BAD_VALUE;
3692 put_fd(descriptor);
3693 return status;
3697 static fssh_status_t
3698 common_read_link(int fd, char *path, char *buffer, fssh_size_t *_bufferSize,
3699 bool kernel)
3701 struct vnode *vnode;
3702 fssh_status_t status;
3704 status = fd_and_path_to_vnode(fd, path, false, &vnode, NULL, kernel);
3705 if (status < FSSH_B_OK)
3706 return status;
3708 if (HAS_FS_CALL(vnode, read_symlink)) {
3709 status = FS_CALL(vnode, read_symlink, buffer, _bufferSize);
3710 } else
3711 status = FSSH_B_BAD_VALUE;
3713 put_vnode(vnode);
3714 return status;
3718 static fssh_status_t
3719 common_create_symlink(int fd, char *path, const char *toPath, int mode,
3720 bool kernel)
3722 // path validity checks have to be in the calling function!
3723 char name[FSSH_B_FILE_NAME_LENGTH];
3724 struct vnode *vnode;
3725 fssh_status_t status;
3727 FUNCTION(("common_create_symlink(fd = %d, path = %s, toPath = %s, mode = %d, kernel = %d)\n", fd, path, toPath, mode, kernel));
3729 status = fd_and_path_to_dir_vnode(fd, path, &vnode, name, kernel);
3730 if (status < FSSH_B_OK)
3731 return status;
3733 if (HAS_FS_CALL(vnode, create_symlink))
3734 status = FS_CALL(vnode, create_symlink, name, toPath, mode);
3735 else
3736 status = FSSH_EROFS;
3738 put_vnode(vnode);
3740 return status;
3744 static fssh_status_t
3745 common_create_link(char *path, char *toPath, bool kernel)
3747 // path validity checks have to be in the calling function!
3748 char name[FSSH_B_FILE_NAME_LENGTH];
3749 struct vnode *directory, *vnode;
3750 fssh_status_t status;
3752 FUNCTION(("common_create_link(path = %s, toPath = %s, kernel = %d)\n", path, toPath, kernel));
3754 status = path_to_dir_vnode(path, &directory, name, kernel);
3755 if (status < FSSH_B_OK)
3756 return status;
3758 status = path_to_vnode(toPath, true, &vnode, NULL, kernel);
3759 if (status < FSSH_B_OK)
3760 goto err;
3762 if (directory->mount != vnode->mount) {
3763 status = FSSH_B_CROSS_DEVICE_LINK;
3764 goto err1;
3767 if (HAS_FS_CALL(directory, link))
3768 status = FS_CALL(directory, link, name, vnode);
3769 else
3770 status = FSSH_EROFS;
3772 err1:
3773 put_vnode(vnode);
3774 err:
3775 put_vnode(directory);
3777 return status;
3781 static fssh_status_t
3782 common_unlink(int fd, char *path, bool kernel)
3784 char filename[FSSH_B_FILE_NAME_LENGTH];
3785 struct vnode *vnode;
3786 fssh_status_t status;
3788 FUNCTION(("common_unlink: fd: %d, path '%s', kernel %d\n", fd, path, kernel));
3790 status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel);
3791 if (status < 0)
3792 return status;
3794 if (HAS_FS_CALL(vnode, unlink))
3795 status = FS_CALL(vnode, unlink, filename);
3796 else
3797 status = FSSH_EROFS;
3799 put_vnode(vnode);
3801 return status;
3805 static fssh_status_t
3806 common_access(char *path, int mode, bool kernel)
3808 struct vnode *vnode;
3809 fssh_status_t status;
3811 status = path_to_vnode(path, true, &vnode, NULL, kernel);
3812 if (status < FSSH_B_OK)
3813 return status;
3815 if (HAS_FS_CALL(vnode, access))
3816 status = FS_CALL(vnode, access, mode);
3817 else
3818 status = FSSH_B_OK;
3820 put_vnode(vnode);
3822 return status;
3826 static fssh_status_t
3827 common_rename(int fd, char *path, int newFD, char *newPath, bool kernel)
3829 struct vnode *fromVnode, *toVnode;
3830 char fromName[FSSH_B_FILE_NAME_LENGTH];
3831 char toName[FSSH_B_FILE_NAME_LENGTH];
3832 fssh_status_t status;
3834 FUNCTION(("common_rename(fd = %d, path = %s, newFD = %d, newPath = %s, kernel = %d)\n", fd, path, newFD, newPath, kernel));
3836 status = fd_and_path_to_dir_vnode(fd, path, &fromVnode, fromName, kernel);
3837 if (status < 0)
3838 return status;
3840 status = fd_and_path_to_dir_vnode(newFD, newPath, &toVnode, toName, kernel);
3841 if (status < 0)
3842 goto err;
3844 if (fromVnode->device != toVnode->device) {
3845 status = FSSH_B_CROSS_DEVICE_LINK;
3846 goto err1;
3849 if (HAS_FS_CALL(fromVnode, rename))
3850 status = FS_CALL(fromVnode, rename, fromName, toVnode, toName);
3851 else
3852 status = FSSH_EROFS;
3854 err1:
3855 put_vnode(toVnode);
3856 err:
3857 put_vnode(fromVnode);
3859 return status;
3863 static fssh_status_t
3864 common_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat)
3866 struct vnode *vnode = descriptor->u.vnode;
3868 FUNCTION(("common_read_stat: stat %p\n", stat));
3870 stat->fssh_st_atim.tv_nsec = 0;
3871 stat->fssh_st_mtim.tv_nsec = 0;
3872 stat->fssh_st_ctim.tv_nsec = 0;
3873 stat->fssh_st_crtim.tv_nsec = 0;
3875 fssh_status_t status = FS_CALL(vnode, read_stat, stat);
3877 // fill in the st_dev and st_ino fields
3878 if (status == FSSH_B_OK) {
3879 stat->fssh_st_dev = vnode->device;
3880 stat->fssh_st_ino = vnode->id;
3883 return status;
3887 static fssh_status_t
3888 common_write_stat(struct file_descriptor *descriptor,
3889 const struct fssh_stat *stat, int statMask)
3891 struct vnode *vnode = descriptor->u.vnode;
3893 FUNCTION(("common_write_stat(vnode = %p, stat = %p, statMask = %d)\n", vnode, stat, statMask));
3894 if (!HAS_FS_CALL(vnode, write_stat))
3895 return FSSH_EROFS;
3897 return FS_CALL(vnode, write_stat, stat, statMask);
3901 static fssh_status_t
3902 common_path_read_stat(int fd, char *path, bool traverseLeafLink,
3903 struct fssh_stat *stat, bool kernel)
3905 struct vnode *vnode;
3906 fssh_status_t status;
3908 FUNCTION(("common_path_read_stat: fd: %d, path '%s', stat %p,\n", fd, path, stat));
3910 status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel);
3911 if (status < 0)
3912 return status;
3914 status = FS_CALL(vnode, read_stat, stat);
3916 // fill in the st_dev and st_ino fields
3917 if (status == FSSH_B_OK) {
3918 stat->fssh_st_dev = vnode->device;
3919 stat->fssh_st_ino = vnode->id;
3922 put_vnode(vnode);
3923 return status;
3927 static fssh_status_t
3928 common_path_write_stat(int fd, char *path, bool traverseLeafLink,
3929 const struct fssh_stat *stat, int statMask, bool kernel)
3931 struct vnode *vnode;
3932 fssh_status_t status;
3934 FUNCTION(("common_write_stat: fd: %d, path '%s', stat %p, stat_mask %d, kernel %d\n", fd, path, stat, statMask, kernel));
3936 status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel);
3937 if (status < 0)
3938 return status;
3940 if (HAS_FS_CALL(vnode, write_stat))
3941 status = FS_CALL(vnode, write_stat, stat, statMask);
3942 else
3943 status = FSSH_EROFS;
3945 put_vnode(vnode);
3947 return status;
3951 static int
3952 attr_dir_open(int fd, char *path, bool kernel)
3954 struct vnode *vnode;
3955 int status;
3957 FUNCTION(("attr_dir_open(fd = %d, path = '%s', kernel = %d)\n", fd, path, kernel));
3959 status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel);
3960 if (status < FSSH_B_OK)
3961 return status;
3963 status = open_attr_dir_vnode(vnode, kernel);
3964 if (status < 0)
3965 put_vnode(vnode);
3967 return status;
3971 static fssh_status_t
3972 attr_dir_close(struct file_descriptor *descriptor)
3974 struct vnode *vnode = descriptor->u.vnode;
3976 FUNCTION(("attr_dir_close(descriptor = %p)\n", descriptor));
3978 if (HAS_FS_CALL(vnode, close_attr_dir))
3979 return FS_CALL(vnode, close_attr_dir, descriptor->cookie);
3981 return FSSH_B_OK;
3985 static void
3986 attr_dir_free_fd(struct file_descriptor *descriptor)
3988 struct vnode *vnode = descriptor->u.vnode;
3990 if (vnode != NULL) {
3991 FS_CALL(vnode, free_attr_dir_cookie, descriptor->cookie);
3992 put_vnode(vnode);
3997 static fssh_status_t
3998 attr_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
3999 fssh_size_t bufferSize, uint32_t *_count)
4001 struct vnode *vnode = descriptor->u.vnode;
4003 FUNCTION(("attr_dir_read(descriptor = %p)\n", descriptor));
4005 if (HAS_FS_CALL(vnode, read_attr_dir))
4006 return FS_CALL(vnode, read_attr_dir, descriptor->cookie, buffer, bufferSize, _count);
4008 return FSSH_EOPNOTSUPP;
4012 static fssh_status_t
4013 attr_dir_rewind(struct file_descriptor *descriptor)
4015 struct vnode *vnode = descriptor->u.vnode;
4017 FUNCTION(("attr_dir_rewind(descriptor = %p)\n", descriptor));
4019 if (HAS_FS_CALL(vnode, rewind_attr_dir))
4020 return FS_CALL(vnode, rewind_attr_dir, descriptor->cookie);
4022 return FSSH_EOPNOTSUPP;
4026 static int
4027 attr_create(int fd, const char *name, uint32_t type, int openMode, bool kernel)
4029 struct vnode *vnode;
4030 void *cookie;
4031 int status;
4033 if (name == NULL || *name == '\0')
4034 return FSSH_B_BAD_VALUE;
4036 vnode = get_vnode_from_fd(fd, kernel);
4037 if (vnode == NULL)
4038 return FSSH_B_FILE_ERROR;
4040 if (!HAS_FS_CALL(vnode, create_attr)) {
4041 status = FSSH_EROFS;
4042 goto err;
4045 status = FS_CALL(vnode, create_attr, name, type, openMode, &cookie);
4046 if (status < FSSH_B_OK)
4047 goto err;
4049 if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0)
4050 return status;
4052 FS_CALL(vnode, close_attr, cookie);
4053 FS_CALL(vnode, free_attr_cookie, cookie);
4055 FS_CALL(vnode, remove_attr, name);
4057 err:
4058 put_vnode(vnode);
4060 return status;
4064 static int
4065 attr_open(int fd, const char *name, int openMode, bool kernel)
4067 struct vnode *vnode;
4068 void *cookie;
4069 int status;
4071 if (name == NULL || *name == '\0')
4072 return FSSH_B_BAD_VALUE;
4074 vnode = get_vnode_from_fd(fd, kernel);
4075 if (vnode == NULL)
4076 return FSSH_B_FILE_ERROR;
4078 if (!HAS_FS_CALL(vnode, open_attr)) {
4079 status = FSSH_EOPNOTSUPP;
4080 goto err;
4083 status = FS_CALL(vnode, open_attr, name, openMode, &cookie);
4084 if (status < FSSH_B_OK)
4085 goto err;
4087 // now we only need a file descriptor for this attribute and we're done
4088 if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0)
4089 return status;
4091 FS_CALL(vnode, close_attr, cookie);
4092 FS_CALL(vnode, free_attr_cookie, cookie);
4094 err:
4095 put_vnode(vnode);
4097 return status;
4101 static fssh_status_t
4102 attr_close(struct file_descriptor *descriptor)
4104 struct vnode *vnode = descriptor->u.vnode;
4106 FUNCTION(("attr_close(descriptor = %p)\n", descriptor));
4108 if (HAS_FS_CALL(vnode, close_attr))
4109 return FS_CALL(vnode, close_attr, descriptor->cookie);
4111 return FSSH_B_OK;
4115 static void
4116 attr_free_fd(struct file_descriptor *descriptor)
4118 struct vnode *vnode = descriptor->u.vnode;
4120 if (vnode != NULL) {
4121 FS_CALL(vnode, free_attr_cookie, descriptor->cookie);
4122 put_vnode(vnode);
4127 static fssh_status_t
4128 attr_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length)
4130 struct vnode *vnode = descriptor->u.vnode;
4132 FUNCTION(("attr_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length));
4133 if (!HAS_FS_CALL(vnode, read_attr))
4134 return FSSH_EOPNOTSUPP;
4136 return FS_CALL(vnode, read_attr, descriptor->cookie, pos, buffer, length);
4140 static fssh_status_t
4141 attr_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length)
4143 struct vnode *vnode = descriptor->u.vnode;
4145 FUNCTION(("attr_write: buf %p, pos %Ld, len %p\n", buffer, pos, length));
4146 if (!HAS_FS_CALL(vnode, write_attr))
4147 return FSSH_EOPNOTSUPP;
4149 return FS_CALL(vnode, write_attr, descriptor->cookie, pos, buffer, length);
4153 static fssh_off_t
4154 attr_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType)
4156 fssh_off_t offset;
4158 switch (seekType) {
4159 case FSSH_SEEK_SET:
4160 offset = 0;
4161 break;
4162 case FSSH_SEEK_CUR:
4163 offset = descriptor->pos;
4164 break;
4165 case FSSH_SEEK_END:
4167 struct vnode *vnode = descriptor->u.vnode;
4168 struct fssh_stat stat;
4169 fssh_status_t status;
4171 if (!HAS_FS_CALL(vnode, read_stat))
4172 return FSSH_EOPNOTSUPP;
4174 status = FS_CALL(vnode, read_attr_stat, descriptor->cookie, &stat);
4175 if (status < FSSH_B_OK)
4176 return status;
4178 offset = stat.fssh_st_size;
4179 break;
4181 default:
4182 return FSSH_B_BAD_VALUE;
4185 // assumes fssh_off_t is 64 bits wide
4186 if (offset > 0 && LLONG_MAX - offset < pos)
4187 return FSSH_EOVERFLOW;
4189 pos += offset;
4190 if (pos < 0)
4191 return FSSH_B_BAD_VALUE;
4193 return descriptor->pos = pos;
4197 static fssh_status_t
4198 attr_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat)
4200 struct vnode *vnode = descriptor->u.vnode;
4202 FUNCTION(("attr_read_stat: stat 0x%p\n", stat));
4204 if (!HAS_FS_CALL(vnode, read_attr_stat))
4205 return FSSH_EOPNOTSUPP;
4207 return FS_CALL(vnode, read_attr_stat, descriptor->cookie, stat);
4211 static fssh_status_t
4212 attr_write_stat(struct file_descriptor *descriptor,
4213 const struct fssh_stat *stat, int statMask)
4215 struct vnode *vnode = descriptor->u.vnode;
4217 FUNCTION(("attr_write_stat: stat = %p, statMask %d\n", stat, statMask));
4219 if (!HAS_FS_CALL(vnode, write_attr_stat))
4220 return FSSH_EROFS;
4222 return FS_CALL(vnode, write_attr_stat, descriptor->cookie, stat, statMask);
4226 static fssh_status_t
4227 attr_remove(int fd, const char *name, bool kernel)
4229 struct file_descriptor *descriptor;
4230 struct vnode *vnode;
4231 fssh_status_t status;
4233 if (name == NULL || *name == '\0')
4234 return FSSH_B_BAD_VALUE;
4236 FUNCTION(("attr_remove: fd = %d, name = \"%s\", kernel %d\n", fd, name, kernel));
4238 descriptor = get_fd_and_vnode(fd, &vnode, kernel);
4239 if (descriptor == NULL)
4240 return FSSH_B_FILE_ERROR;
4242 if (HAS_FS_CALL(vnode, remove_attr))
4243 status = FS_CALL(vnode, remove_attr, name);
4244 else
4245 status = FSSH_EROFS;
4247 put_fd(descriptor);
4249 return status;
4253 static fssh_status_t
4254 attr_rename(int fromfd, const char *fromName, int tofd, const char *toName, bool kernel)
4256 struct file_descriptor *fromDescriptor, *toDescriptor;
4257 struct vnode *fromVnode, *toVnode;
4258 fssh_status_t status;
4260 if (fromName == NULL || *fromName == '\0' || toName == NULL || *toName == '\0')
4261 return FSSH_B_BAD_VALUE;
4263 FUNCTION(("attr_rename: from fd = %d, from name = \"%s\", to fd = %d, to name = \"%s\", kernel %d\n", fromfd, fromName, tofd, toName, kernel));
4265 fromDescriptor = get_fd_and_vnode(fromfd, &fromVnode, kernel);
4266 if (fromDescriptor == NULL)
4267 return FSSH_B_FILE_ERROR;
4269 toDescriptor = get_fd_and_vnode(tofd, &toVnode, kernel);
4270 if (toDescriptor == NULL) {
4271 status = FSSH_B_FILE_ERROR;
4272 goto err;
4275 // are the files on the same volume?
4276 if (fromVnode->device != toVnode->device) {
4277 status = FSSH_B_CROSS_DEVICE_LINK;
4278 goto err1;
4281 if (HAS_FS_CALL(fromVnode, rename_attr))
4282 status = FS_CALL(fromVnode, rename_attr, fromName, toVnode, toName);
4283 else
4284 status = FSSH_EROFS;
4286 err1:
4287 put_fd(toDescriptor);
4288 err:
4289 put_fd(fromDescriptor);
4291 return status;
4295 static fssh_status_t
4296 index_dir_open(fssh_mount_id mountID, bool kernel)
4298 struct fs_mount *mount;
4299 void *cookie;
4301 FUNCTION(("index_dir_open(mountID = %ld, kernel = %d)\n", mountID, kernel));
4303 fssh_status_t status = get_mount(mountID, &mount);
4304 if (status < FSSH_B_OK)
4305 return status;
4307 if (!HAS_FS_MOUNT_CALL(mount, open_index_dir)) {
4308 status = FSSH_EOPNOTSUPP;
4309 goto out;
4312 status = FS_MOUNT_CALL(mount, open_index_dir, &cookie);
4313 if (status < FSSH_B_OK)
4314 goto out;
4316 // get fd for the index directory
4317 status = get_new_fd(FDTYPE_INDEX_DIR, mount, NULL, cookie, 0, kernel);
4318 if (status >= 0)
4319 goto out;
4321 // something went wrong
4322 FS_MOUNT_CALL(mount, close_index_dir, cookie);
4323 FS_MOUNT_CALL(mount, free_index_dir_cookie, cookie);
4325 out:
4326 put_mount(mount);
4327 return status;
4331 static fssh_status_t
4332 index_dir_close(struct file_descriptor *descriptor)
4334 struct fs_mount *mount = descriptor->u.mount;
4336 FUNCTION(("index_dir_close(descriptor = %p)\n", descriptor));
4338 if (HAS_FS_MOUNT_CALL(mount, close_index_dir))
4339 return FS_MOUNT_CALL(mount, close_index_dir, descriptor->cookie);
4341 return FSSH_B_OK;
4345 static void
4346 index_dir_free_fd(struct file_descriptor *descriptor)
4348 struct fs_mount *mount = descriptor->u.mount;
4350 if (mount != NULL) {
4351 FS_MOUNT_CALL(mount, free_index_dir_cookie, descriptor->cookie);
4352 // ToDo: find a replacement ref_count object - perhaps the root dir?
4353 //put_vnode(vnode);
4358 static fssh_status_t
4359 index_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
4360 fssh_size_t bufferSize, uint32_t *_count)
4362 struct fs_mount *mount = descriptor->u.mount;
4364 if (HAS_FS_MOUNT_CALL(mount, read_index_dir))
4365 return FS_MOUNT_CALL(mount, read_index_dir, descriptor->cookie, buffer, bufferSize, _count);
4367 return FSSH_EOPNOTSUPP;
4371 static fssh_status_t
4372 index_dir_rewind(struct file_descriptor *descriptor)
4374 struct fs_mount *mount = descriptor->u.mount;
4376 if (HAS_FS_MOUNT_CALL(mount, rewind_index_dir))
4377 return FS_MOUNT_CALL(mount, rewind_index_dir, descriptor->cookie);
4379 return FSSH_EOPNOTSUPP;
4383 static fssh_status_t
4384 index_create(fssh_mount_id mountID, const char *name, uint32_t type, uint32_t flags, bool kernel)
4386 FUNCTION(("index_create(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
4388 struct fs_mount *mount;
4389 fssh_status_t status = get_mount(mountID, &mount);
4390 if (status < FSSH_B_OK)
4391 return status;
4393 if (!HAS_FS_MOUNT_CALL(mount, create_index)) {
4394 status = FSSH_EROFS;
4395 goto out;
4398 status = FS_MOUNT_CALL(mount, create_index, name, type, flags);
4400 out:
4401 put_mount(mount);
4402 return status;
4406 static fssh_status_t
4407 index_name_read_stat(fssh_mount_id mountID, const char *name,
4408 struct fssh_stat *stat, bool kernel)
4410 FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
4412 struct fs_mount *mount;
4413 fssh_status_t status = get_mount(mountID, &mount);
4414 if (status < FSSH_B_OK)
4415 return status;
4417 if (!HAS_FS_MOUNT_CALL(mount, read_index_stat)) {
4418 status = FSSH_EOPNOTSUPP;
4419 goto out;
4422 status = FS_MOUNT_CALL(mount, read_index_stat, name, stat);
4424 out:
4425 put_mount(mount);
4426 return status;
4430 static fssh_status_t
4431 index_remove(fssh_mount_id mountID, const char *name, bool kernel)
4433 FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
4435 struct fs_mount *mount;
4436 fssh_status_t status = get_mount(mountID, &mount);
4437 if (status < FSSH_B_OK)
4438 return status;
4440 if (!HAS_FS_MOUNT_CALL(mount, remove_index)) {
4441 status = FSSH_EROFS;
4442 goto out;
4445 status = FS_MOUNT_CALL(mount, remove_index, name);
4447 out:
4448 put_mount(mount);
4449 return status;
4453 /*! ToDo: the query FS API is still the pretty much the same as in R5.
4454 It would be nice if the FS would find some more kernel support
4455 for them.
4456 For example, query parsing should be moved into the kernel.
4458 static int
4459 query_open(fssh_dev_t device, const char *query, uint32_t flags,
4460 fssh_port_id port, int32_t token, bool kernel)
4462 struct fs_mount *mount;
4463 void *cookie;
4465 FUNCTION(("query_open(device = %ld, query = \"%s\", kernel = %d)\n", device, query, kernel));
4467 fssh_status_t status = get_mount(device, &mount);
4468 if (status < FSSH_B_OK)
4469 return status;
4471 if (!HAS_FS_MOUNT_CALL(mount, open_query)) {
4472 status = FSSH_EOPNOTSUPP;
4473 goto out;
4476 status = FS_MOUNT_CALL(mount, open_query, query, flags, port, token, &cookie);
4477 if (status < FSSH_B_OK)
4478 goto out;
4480 // get fd for the index directory
4481 status = get_new_fd(FDTYPE_QUERY, mount, NULL, cookie, 0, kernel);
4482 if (status >= 0)
4483 goto out;
4485 // something went wrong
4486 FS_MOUNT_CALL(mount, close_query, cookie);
4487 FS_MOUNT_CALL(mount, free_query_cookie, cookie);
4489 out:
4490 put_mount(mount);
4491 return status;
4495 static fssh_status_t
4496 query_close(struct file_descriptor *descriptor)
4498 struct fs_mount *mount = descriptor->u.mount;
4500 FUNCTION(("query_close(descriptor = %p)\n", descriptor));
4502 if (HAS_FS_MOUNT_CALL(mount, close_query))
4503 return FS_MOUNT_CALL(mount, close_query, descriptor->cookie);
4505 return FSSH_B_OK;
4509 static void
4510 query_free_fd(struct file_descriptor *descriptor)
4512 struct fs_mount *mount = descriptor->u.mount;
4514 if (mount != NULL) {
4515 FS_MOUNT_CALL(mount, free_query_cookie, descriptor->cookie);
4516 // ToDo: find a replacement ref_count object - perhaps the root dir?
4517 //put_vnode(vnode);
4522 static fssh_status_t
4523 query_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
4524 fssh_size_t bufferSize, uint32_t *_count)
4526 struct fs_mount *mount = descriptor->u.mount;
4528 if (HAS_FS_MOUNT_CALL(mount, read_query))
4529 return FS_MOUNT_CALL(mount, read_query, descriptor->cookie, buffer, bufferSize, _count);
4531 return FSSH_EOPNOTSUPP;
4535 static fssh_status_t
4536 query_rewind(struct file_descriptor *descriptor)
4538 struct fs_mount *mount = descriptor->u.mount;
4540 if (HAS_FS_MOUNT_CALL(mount, rewind_query))
4541 return FS_MOUNT_CALL(mount, rewind_query, descriptor->cookie);
4543 return FSSH_EOPNOTSUPP;
4547 // #pragma mark -
4548 // General File System functions
4551 static fssh_dev_t
4552 fs_mount(char *path, const char *device, const char *fsName, uint32_t flags,
4553 const char *args, bool kernel)
4555 struct fs_mount *mount;
4556 fssh_status_t status = 0;
4558 FUNCTION(("fs_mount: entry. path = '%s', fs_name = '%s'\n", path, fsName));
4560 // The path is always safe, we just have to make sure that fsName is
4561 // almost valid - we can't make any assumptions about args, though.
4562 // A NULL fsName is OK, if a device was given and the FS is not virtual.
4563 // We'll get it from the DDM later.
4564 if (fsName == NULL) {
4565 if (!device || flags & FSSH_B_MOUNT_VIRTUAL_DEVICE)
4566 return FSSH_B_BAD_VALUE;
4567 } else if (fsName[0] == '\0')
4568 return FSSH_B_BAD_VALUE;
4570 RecursiveLocker mountOpLocker(sMountOpLock);
4572 // If the file system is not a "virtual" one, the device argument should
4573 // point to a real file/device (if given at all).
4574 // get the partition
4575 KPath normalizedDevice;
4577 if (!(flags & FSSH_B_MOUNT_VIRTUAL_DEVICE) && device) {
4578 // normalize the device path
4579 // status = normalizedDevice.SetTo(device, true);
4580 // NOTE: normalizing works only in our namespace.
4581 status = normalizedDevice.SetTo(device, false);
4582 if (status != FSSH_B_OK)
4583 return status;
4585 device = normalizedDevice.Path();
4586 // correct path to file device
4589 mount = (struct fs_mount *)malloc(sizeof(struct fs_mount));
4590 if (mount == NULL)
4591 return FSSH_B_NO_MEMORY;
4593 mount->volume = (fssh_fs_volume*)malloc(sizeof(fssh_fs_volume));
4594 if (mount->volume == NULL) {
4595 free(mount);
4596 return FSSH_B_NO_MEMORY;
4599 list_init_etc(&mount->vnodes, fssh_offsetof(struct vnode, mount_link));
4601 mount->fs_name = get_file_system_name(fsName);
4602 if (mount->fs_name == NULL) {
4603 status = FSSH_B_NO_MEMORY;
4604 goto err1;
4607 mount->device_name = fssh_strdup(device);
4608 // "device" can be NULL
4610 mount->fs = get_file_system(fsName);
4611 if (mount->fs == NULL) {
4612 status = FSSH_ENODEV;
4613 goto err3;
4616 fssh_recursive_lock_init(&mount->rlock, "mount rlock");
4618 // initialize structure
4619 mount->id = sNextMountID++;
4620 mount->root_vnode = NULL;
4621 mount->covers_vnode = NULL;
4622 mount->unmounting = false;
4623 mount->owns_file_device = false;
4625 mount->volume->id = mount->id;
4626 mount->volume->layer = 0;
4627 mount->volume->private_volume = NULL;
4628 mount->volume->ops = NULL;
4629 mount->volume->sub_volume = NULL;
4630 mount->volume->super_volume = NULL;
4632 // insert mount struct into list before we call FS's mount() function
4633 // so that vnodes can be created for this mount
4634 fssh_mutex_lock(&sMountMutex);
4635 hash_insert(sMountsTable, mount);
4636 fssh_mutex_unlock(&sMountMutex);
4638 fssh_vnode_id rootID;
4640 if (!sRoot) {
4641 // we haven't mounted anything yet
4642 if (fssh_strcmp(path, "/") != 0) {
4643 status = FSSH_B_ERROR;
4644 goto err4;
4647 status = mount->fs->mount(mount->volume, device, flags, args, &rootID);
4648 if (status < 0) {
4649 // ToDo: why should we hide the error code from the file system here?
4650 //status = ERR_VFS_GENERAL;
4651 goto err4;
4653 } else {
4654 struct vnode *coveredVnode;
4655 status = path_to_vnode(path, true, &coveredVnode, NULL, kernel);
4656 if (status < FSSH_B_OK)
4657 goto err4;
4659 // make sure covered_vnode is a DIR
4660 struct fssh_stat coveredNodeStat;
4661 status = FS_CALL(coveredVnode, read_stat, &coveredNodeStat);
4662 if (status < FSSH_B_OK)
4663 goto err4;
4665 if (!FSSH_S_ISDIR(coveredNodeStat.fssh_st_mode)) {
4666 status = FSSH_B_NOT_A_DIRECTORY;
4667 goto err4;
4670 if (coveredVnode->mount->root_vnode == coveredVnode) {
4671 // this is already a mount point
4672 status = FSSH_B_BUSY;
4673 goto err4;
4676 mount->covers_vnode = coveredVnode;
4678 // mount it
4679 status = mount->fs->mount(mount->volume, device, flags, args, &rootID);
4680 if (status < FSSH_B_OK)
4681 goto err5;
4684 // the root node is supposed to be owned by the file system - it must
4685 // exist at this point
4686 mount->root_vnode = lookup_vnode(mount->id, rootID);
4687 if (mount->root_vnode == NULL || mount->root_vnode->ref_count != 1) {
4688 fssh_panic("fs_mount: file system does not own its root node!\n");
4689 status = FSSH_B_ERROR;
4690 goto err6;
4693 // No race here, since fs_mount() is the only function changing
4694 // covers_vnode (and holds sMountOpLock at that time).
4695 fssh_mutex_lock(&sVnodeCoveredByMutex);
4696 if (mount->covers_vnode)
4697 mount->covers_vnode->covered_by = mount->root_vnode;
4698 fssh_mutex_unlock(&sVnodeCoveredByMutex);
4700 if (!sRoot)
4701 sRoot = mount->root_vnode;
4703 return mount->id;
4705 err6:
4706 FS_MOUNT_CALL_NO_PARAMS(mount, unmount);
4707 err5:
4708 if (mount->covers_vnode)
4709 put_vnode(mount->covers_vnode);
4711 err4:
4712 fssh_mutex_lock(&sMountMutex);
4713 hash_remove(sMountsTable, mount);
4714 fssh_mutex_unlock(&sMountMutex);
4716 fssh_recursive_lock_destroy(&mount->rlock);
4718 put_file_system(mount->fs);
4719 free(mount->device_name);
4720 err3:
4721 free(mount->fs_name);
4722 err1:
4723 free(mount->volume);
4724 free(mount);
4726 return status;
4730 static fssh_status_t
4731 fs_unmount(char *path, uint32_t flags, bool kernel)
4733 struct fs_mount *mount;
4734 struct vnode *vnode;
4735 fssh_status_t err;
4737 FUNCTION(("vfs_unmount: entry. path = '%s', kernel %d\n", path, kernel));
4739 err = path_to_vnode(path, true, &vnode, NULL, kernel);
4740 if (err < 0)
4741 return FSSH_B_ENTRY_NOT_FOUND;
4743 RecursiveLocker mountOpLocker(sMountOpLock);
4745 mount = find_mount(vnode->device);
4746 if (!mount)
4747 fssh_panic("vfs_unmount: find_mount() failed on root vnode @%p of mount\n", vnode);
4749 if (mount->root_vnode != vnode) {
4750 // not mountpoint
4751 put_vnode(vnode);
4752 return FSSH_B_BAD_VALUE;
4755 // grab the vnode master mutex to keep someone from creating
4756 // a vnode while we're figuring out if we can continue
4757 fssh_mutex_lock(&sVnodeMutex);
4759 bool disconnectedDescriptors = false;
4761 while (true) {
4762 bool busy = false;
4764 // cycle through the list of vnodes associated with this mount and
4765 // make sure all of them are not busy or have refs on them
4766 vnode = NULL;
4767 while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) {
4768 // The root vnode ref_count needs to be 2 here: one for the file
4769 // system, one from the path_to_vnode() call above
4770 if (vnode->busy
4771 || ((vnode->ref_count != 0 && mount->root_vnode != vnode)
4772 || (vnode->ref_count != 2 && mount->root_vnode == vnode))) {
4773 // there are still vnodes in use on this mount, so we cannot
4774 // unmount yet
4775 busy = true;
4776 break;
4780 if (!busy)
4781 break;
4783 if ((flags & FSSH_B_FORCE_UNMOUNT) == 0) {
4784 fssh_mutex_unlock(&sVnodeMutex);
4785 put_vnode(mount->root_vnode);
4787 return FSSH_B_BUSY;
4790 if (disconnectedDescriptors) {
4791 // wait a bit until the last access is finished, and then try again
4792 fssh_mutex_unlock(&sVnodeMutex);
4793 fssh_snooze(100000);
4794 // TODO: if there is some kind of bug that prevents the ref counts
4795 // from getting back to zero, this will fall into an endless loop...
4796 fssh_mutex_lock(&sVnodeMutex);
4797 continue;
4800 // the file system is still busy - but we're forced to unmount it,
4801 // so let's disconnect all open file descriptors
4803 mount->unmounting = true;
4804 // prevent new vnodes from being created
4806 fssh_mutex_unlock(&sVnodeMutex);
4808 disconnect_mount_or_vnode_fds(mount, NULL);
4809 disconnectedDescriptors = true;
4811 fssh_mutex_lock(&sVnodeMutex);
4814 // we can safely continue, mark all of the vnodes busy and this mount
4815 // structure in unmounting state
4816 mount->unmounting = true;
4818 while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) {
4819 vnode->busy = true;
4821 if (vnode->ref_count == 0) {
4822 // this vnode has been unused before
4823 list_remove_item(&sUnusedVnodeList, vnode);
4824 sUnusedVnodes--;
4828 // The ref_count of the root node is 2 at this point, see above why this is
4829 mount->root_vnode->ref_count -= 2;
4831 fssh_mutex_unlock(&sVnodeMutex);
4833 fssh_mutex_lock(&sVnodeCoveredByMutex);
4834 mount->covers_vnode->covered_by = NULL;
4835 fssh_mutex_unlock(&sVnodeCoveredByMutex);
4836 put_vnode(mount->covers_vnode);
4838 // Free all vnodes associated with this mount.
4839 // They will be removed from the mount list by free_vnode(), so
4840 // we don't have to do this.
4841 while ((vnode = (struct vnode *)list_get_first_item(&mount->vnodes)) != NULL) {
4842 free_vnode(vnode, false);
4845 // remove the mount structure from the hash table
4846 fssh_mutex_lock(&sMountMutex);
4847 hash_remove(sMountsTable, mount);
4848 fssh_mutex_unlock(&sMountMutex);
4850 mountOpLocker.Unlock();
4852 FS_MOUNT_CALL_NO_PARAMS(mount, unmount);
4854 // release the file system
4855 put_file_system(mount->fs);
4857 free(mount->device_name);
4858 free(mount->fs_name);
4859 free(mount);
4861 return FSSH_B_OK;
4865 static fssh_status_t
4866 fs_sync(fssh_dev_t device)
4868 struct fs_mount *mount;
4869 fssh_status_t status = get_mount(device, &mount);
4870 if (status < FSSH_B_OK)
4871 return status;
4873 fssh_mutex_lock(&sMountMutex);
4875 if (HAS_FS_MOUNT_CALL(mount, sync))
4876 status = FS_MOUNT_CALL_NO_PARAMS(mount, sync);
4878 fssh_mutex_unlock(&sMountMutex);
4880 struct vnode *previousVnode = NULL;
4881 while (true) {
4882 // synchronize access to vnode list
4883 fssh_recursive_lock_lock(&mount->rlock);
4885 struct vnode *vnode = (struct vnode *)list_get_next_item(&mount->vnodes,
4886 previousVnode);
4888 fssh_vnode_id id = -1;
4889 if (vnode != NULL)
4890 id = vnode->id;
4892 fssh_recursive_lock_unlock(&mount->rlock);
4894 if (vnode == NULL)
4895 break;
4897 // acquire a reference to the vnode
4899 if (get_vnode(mount->id, id, &vnode, true) == FSSH_B_OK) {
4900 if (previousVnode != NULL)
4901 put_vnode(previousVnode);
4903 if (HAS_FS_CALL(vnode, fsync))
4904 FS_CALL_NO_PARAMS(vnode, fsync);
4906 // the next vnode might change until we lock the vnode list again,
4907 // but this vnode won't go away since we keep a reference to it.
4908 previousVnode = vnode;
4909 } else {
4910 fssh_dprintf("syncing of mount %d stopped due to vnode %"
4911 FSSH_B_PRIdINO ".\n", (int)mount->id, id);
4912 break;
4916 if (previousVnode != NULL)
4917 put_vnode(previousVnode);
4919 put_mount(mount);
4920 return status;
4924 static fssh_status_t
4925 fs_read_info(fssh_dev_t device, struct fssh_fs_info *info)
4927 struct fs_mount *mount;
4928 fssh_status_t status = get_mount(device, &mount);
4929 if (status < FSSH_B_OK)
4930 return status;
4932 fssh_memset(info, 0, sizeof(struct fssh_fs_info));
4934 if (HAS_FS_MOUNT_CALL(mount, read_fs_info))
4935 status = FS_MOUNT_CALL(mount, read_fs_info, info);
4937 // fill in info the file system doesn't (have to) know about
4938 if (status == FSSH_B_OK) {
4939 info->dev = mount->id;
4940 info->root = mount->root_vnode->id;
4941 fssh_strlcpy(info->fsh_name, mount->fs_name, sizeof(info->fsh_name));
4942 if (mount->device_name != NULL) {
4943 fssh_strlcpy(info->device_name, mount->device_name,
4944 sizeof(info->device_name));
4948 // if the call is not supported by the file system, there are still
4949 // the parts that we filled out ourselves
4951 put_mount(mount);
4952 return status;
4956 static fssh_status_t
4957 fs_write_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask)
4959 struct fs_mount *mount;
4960 fssh_status_t status = get_mount(device, &mount);
4961 if (status < FSSH_B_OK)
4962 return status;
4964 if (HAS_FS_MOUNT_CALL(mount, write_fs_info))
4965 status = FS_MOUNT_CALL(mount, write_fs_info, info, mask);
4966 else
4967 status = FSSH_EROFS;
4969 put_mount(mount);
4970 return status;
4974 static fssh_dev_t
4975 fs_next_device(int32_t *_cookie)
4977 struct fs_mount *mount = NULL;
4978 fssh_dev_t device = *_cookie;
4980 fssh_mutex_lock(&sMountMutex);
4982 // Since device IDs are assigned sequentially, this algorithm
4983 // does work good enough. It makes sure that the device list
4984 // returned is sorted, and that no device is skipped when an
4985 // already visited device got unmounted.
4987 while (device < sNextMountID) {
4988 mount = find_mount(device++);
4989 if (mount != NULL && mount->volume->private_volume != NULL)
4990 break;
4993 *_cookie = device;
4995 if (mount != NULL)
4996 device = mount->id;
4997 else
4998 device = FSSH_B_BAD_VALUE;
5000 fssh_mutex_unlock(&sMountMutex);
5002 return device;
5006 static fssh_status_t
5007 get_cwd(char *buffer, fssh_size_t size, bool kernel)
5009 // Get current working directory from io context
5010 struct io_context *context = get_current_io_context(kernel);
5011 fssh_status_t status;
5013 FUNCTION(("vfs_get_cwd: buf %p, size %ld\n", buffer, size));
5015 fssh_mutex_lock(&context->io_mutex);
5017 if (context->cwd)
5018 status = dir_vnode_to_path(context->cwd, buffer, size);
5019 else
5020 status = FSSH_B_ERROR;
5022 fssh_mutex_unlock(&context->io_mutex);
5023 return status;
5027 static fssh_status_t
5028 set_cwd(int fd, char *path, bool kernel)
5030 struct io_context *context;
5031 struct vnode *vnode = NULL;
5032 struct vnode *oldDirectory;
5033 struct fssh_stat stat;
5034 fssh_status_t status;
5036 FUNCTION(("set_cwd: path = \'%s\'\n", path));
5038 // Get vnode for passed path, and bail if it failed
5039 status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel);
5040 if (status < 0)
5041 return status;
5043 status = FS_CALL(vnode, read_stat, &stat);
5044 if (status < 0)
5045 goto err;
5047 if (!FSSH_S_ISDIR(stat.fssh_st_mode)) {
5048 // nope, can't cwd to here
5049 status = FSSH_B_NOT_A_DIRECTORY;
5050 goto err;
5053 // Get current io context and lock
5054 context = get_current_io_context(kernel);
5055 fssh_mutex_lock(&context->io_mutex);
5057 // save the old current working directory first
5058 oldDirectory = context->cwd;
5059 context->cwd = vnode;
5061 fssh_mutex_unlock(&context->io_mutex);
5063 if (oldDirectory)
5064 put_vnode(oldDirectory);
5066 return FSSH_B_NO_ERROR;
5068 err:
5069 put_vnode(vnode);
5070 return status;
5074 // #pragma mark -
5075 // Calls from within the kernel
5078 fssh_dev_t
5079 _kern_mount(const char *path, const char *device, const char *fsName,
5080 uint32_t flags, const char *args, fssh_size_t argsLength)
5082 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5083 if (pathBuffer.InitCheck() != FSSH_B_OK)
5084 return FSSH_B_NO_MEMORY;
5086 return fs_mount(pathBuffer.LockBuffer(), device, fsName, flags, args, true);
5090 fssh_status_t
5091 _kern_unmount(const char *path, uint32_t flags)
5093 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5094 if (pathBuffer.InitCheck() != FSSH_B_OK)
5095 return FSSH_B_NO_MEMORY;
5097 return fs_unmount(pathBuffer.LockBuffer(), flags, true);
5101 fssh_status_t
5102 _kern_read_fs_info(fssh_dev_t device, struct fssh_fs_info *info)
5104 if (info == NULL)
5105 return FSSH_B_BAD_VALUE;
5107 return fs_read_info(device, info);
5111 fssh_status_t
5112 _kern_write_fs_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask)
5114 if (info == NULL)
5115 return FSSH_B_BAD_VALUE;
5117 return fs_write_info(device, info, mask);
5121 fssh_status_t
5122 _kern_sync(void)
5124 // Note: _kern_sync() is also called from _user_sync()
5125 int32_t cookie = 0;
5126 fssh_dev_t device;
5127 while ((device = fs_next_device(&cookie)) >= 0) {
5128 fssh_status_t status = fs_sync(device);
5129 if (status != FSSH_B_OK && status != FSSH_B_BAD_VALUE)
5130 fssh_dprintf("sync: device %d couldn't sync: %s\n", (int)device, fssh_strerror(status));
5133 return FSSH_B_OK;
5137 fssh_dev_t
5138 _kern_next_device(int32_t *_cookie)
5140 return fs_next_device(_cookie);
5145 _kern_open_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int openMode, int perms)
5147 if (openMode & FSSH_O_CREAT)
5148 return file_create_entry_ref(device, inode, name, openMode, perms, true);
5150 return file_open_entry_ref(device, inode, name, openMode, true);
5154 /** \brief Opens a node specified by a FD + path pair.
5156 * At least one of \a fd and \a path must be specified.
5157 * If only \a fd is given, the function opens the node identified by this
5158 * FD. If only a path is given, this path is opened. If both are given and
5159 * the path is absolute, \a fd is ignored; a relative path is reckoned off
5160 * of the directory (!) identified by \a fd.
5162 * \param fd The FD. May be < 0.
5163 * \param path The absolute or relative path. May be \c NULL.
5164 * \param openMode The open mode.
5165 * \return A FD referring to the newly opened node, or an error code,
5166 * if an error occurs.
5170 _kern_open(int fd, const char *path, int openMode, int perms)
5172 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5173 if (pathBuffer.InitCheck() != FSSH_B_OK)
5174 return FSSH_B_NO_MEMORY;
5176 if (openMode & FSSH_O_CREAT)
5177 return file_create(fd, pathBuffer.LockBuffer(), openMode, perms, true);
5179 return file_open(fd, pathBuffer.LockBuffer(), openMode, true);
5183 /** \brief Opens a directory specified by entry_ref or node_ref.
5185 * The supplied name may be \c NULL, in which case directory identified
5186 * by \a device and \a inode will be opened. Otherwise \a device and
5187 * \a inode identify the parent directory of the directory to be opened
5188 * and \a name its entry name.
5190 * \param device If \a name is specified the ID of the device the parent
5191 * directory of the directory to be opened resides on, otherwise
5192 * the device of the directory itself.
5193 * \param inode If \a name is specified the node ID of the parent
5194 * directory of the directory to be opened, otherwise node ID of the
5195 * directory itself.
5196 * \param name The entry name of the directory to be opened. If \c NULL,
5197 * the \a device + \a inode pair identify the node to be opened.
5198 * \return The FD of the newly opened directory or an error code, if
5199 * something went wrong.
5203 _kern_open_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name)
5205 return dir_open_entry_ref(device, inode, name, true);
5209 /** \brief Opens a directory specified by a FD + path pair.
5211 * At least one of \a fd and \a path must be specified.
5212 * If only \a fd is given, the function opens the directory identified by this
5213 * FD. If only a path is given, this path is opened. If both are given and
5214 * the path is absolute, \a fd is ignored; a relative path is reckoned off
5215 * of the directory (!) identified by \a fd.
5217 * \param fd The FD. May be < 0.
5218 * \param path The absolute or relative path. May be \c NULL.
5219 * \return A FD referring to the newly opened directory, or an error code,
5220 * if an error occurs.
5224 _kern_open_dir(int fd, const char *path)
5226 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5227 if (pathBuffer.InitCheck() != FSSH_B_OK)
5228 return FSSH_B_NO_MEMORY;
5230 return dir_open(fd, pathBuffer.LockBuffer(), true);
5234 fssh_status_t
5235 _kern_fcntl(int fd, int op, uint32_t argument)
5237 return common_fcntl(fd, op, argument, true);
5241 fssh_status_t
5242 _kern_fsync(int fd)
5244 return common_sync(fd, true);
5248 fssh_status_t
5249 _kern_lock_node(int fd)
5251 return common_lock_node(fd, true);
5255 fssh_status_t
5256 _kern_unlock_node(int fd)
5258 return common_unlock_node(fd, true);
5262 fssh_status_t
5263 _kern_create_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int perms)
5265 return dir_create_entry_ref(device, inode, name, perms, true);
5269 /** \brief Creates a directory specified by a FD + path pair.
5271 * \a path must always be specified (it contains the name of the new directory
5272 * at least). If only a path is given, this path identifies the location at
5273 * which the directory shall be created. If both \a fd and \a path are given and
5274 * the path is absolute, \a fd is ignored; a relative path is reckoned off
5275 * of the directory (!) identified by \a fd.
5277 * \param fd The FD. May be < 0.
5278 * \param path The absolute or relative path. Must not be \c NULL.
5279 * \param perms The access permissions the new directory shall have.
5280 * \return \c FSSH_B_OK, if the directory has been created successfully, another
5281 * error code otherwise.
5284 fssh_status_t
5285 _kern_create_dir(int fd, const char *path, int perms)
5287 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5288 if (pathBuffer.InitCheck() != FSSH_B_OK)
5289 return FSSH_B_NO_MEMORY;
5291 return dir_create(fd, pathBuffer.LockBuffer(), perms, true);
5295 fssh_status_t
5296 _kern_remove_dir(int fd, const char *path)
5298 if (path) {
5299 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5300 if (pathBuffer.InitCheck() != FSSH_B_OK)
5301 return FSSH_B_NO_MEMORY;
5303 return dir_remove(fd, pathBuffer.LockBuffer(), true);
5306 return dir_remove(fd, NULL, true);
5310 /** \brief Reads the contents of a symlink referred to by a FD + path pair.
5312 * At least one of \a fd and \a path must be specified.
5313 * If only \a fd is given, the function the symlink to be read is the node
5314 * identified by this FD. If only a path is given, this path identifies the
5315 * symlink to be read. If both are given and the path is absolute, \a fd is
5316 * ignored; a relative path is reckoned off of the directory (!) identified
5317 * by \a fd.
5318 * If this function fails with FSSH_B_BUFFER_OVERFLOW, the \a _bufferSize pointer
5319 * will still be updated to reflect the required buffer size.
5321 * \param fd The FD. May be < 0.
5322 * \param path The absolute or relative path. May be \c NULL.
5323 * \param buffer The buffer into which the contents of the symlink shall be
5324 * written.
5325 * \param _bufferSize A pointer to the size of the supplied buffer.
5326 * \return The length of the link on success or an appropriate error code
5329 fssh_status_t
5330 _kern_read_link(int fd, const char *path, char *buffer, fssh_size_t *_bufferSize)
5332 if (path) {
5333 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5334 if (pathBuffer.InitCheck() != FSSH_B_OK)
5335 return FSSH_B_NO_MEMORY;
5337 return common_read_link(fd, pathBuffer.LockBuffer(),
5338 buffer, _bufferSize, true);
5341 return common_read_link(fd, NULL, buffer, _bufferSize, true);
5345 /** \brief Creates a symlink specified by a FD + path pair.
5347 * \a path must always be specified (it contains the name of the new symlink
5348 * at least). If only a path is given, this path identifies the location at
5349 * which the symlink shall be created. If both \a fd and \a path are given and
5350 * the path is absolute, \a fd is ignored; a relative path is reckoned off
5351 * of the directory (!) identified by \a fd.
5353 * \param fd The FD. May be < 0.
5354 * \param toPath The absolute or relative path. Must not be \c NULL.
5355 * \param mode The access permissions the new symlink shall have.
5356 * \return \c FSSH_B_OK, if the symlink has been created successfully, another
5357 * error code otherwise.
5360 fssh_status_t
5361 _kern_create_symlink(int fd, const char *path, const char *toPath, int mode)
5363 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5364 KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
5365 if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK)
5366 return FSSH_B_NO_MEMORY;
5368 char *toBuffer = toPathBuffer.LockBuffer();
5370 fssh_status_t status = check_path(toBuffer);
5371 if (status < FSSH_B_OK)
5372 return status;
5374 return common_create_symlink(fd, pathBuffer.LockBuffer(),
5375 toBuffer, mode, true);
5379 fssh_status_t
5380 _kern_create_link(const char *path, const char *toPath)
5382 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5383 KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
5384 if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK)
5385 return FSSH_B_NO_MEMORY;
5387 return common_create_link(pathBuffer.LockBuffer(),
5388 toPathBuffer.LockBuffer(), true);
5392 /** \brief Removes an entry specified by a FD + path pair from its directory.
5394 * \a path must always be specified (it contains at least the name of the entry
5395 * to be deleted). If only a path is given, this path identifies the entry
5396 * directly. If both \a fd and \a path are given and the path is absolute,
5397 * \a fd is ignored; a relative path is reckoned off of the directory (!)
5398 * identified by \a fd.
5400 * \param fd The FD. May be < 0.
5401 * \param path The absolute or relative path. Must not be \c NULL.
5402 * \return \c FSSH_B_OK, if the entry has been removed successfully, another
5403 * error code otherwise.
5406 fssh_status_t
5407 _kern_unlink(int fd, const char *path)
5409 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5410 if (pathBuffer.InitCheck() != FSSH_B_OK)
5411 return FSSH_B_NO_MEMORY;
5413 return common_unlink(fd, pathBuffer.LockBuffer(), true);
5417 /** \brief Moves an entry specified by a FD + path pair to a an entry specified
5418 * by another FD + path pair.
5420 * \a oldPath and \a newPath must always be specified (they contain at least
5421 * the name of the entry). If only a path is given, this path identifies the
5422 * entry directly. If both a FD and a path are given and the path is absolute,
5423 * the FD is ignored; a relative path is reckoned off of the directory (!)
5424 * identified by the respective FD.
5426 * \param oldFD The FD of the old location. May be < 0.
5427 * \param oldPath The absolute or relative path of the old location. Must not
5428 * be \c NULL.
5429 * \param newFD The FD of the new location. May be < 0.
5430 * \param newPath The absolute or relative path of the new location. Must not
5431 * be \c NULL.
5432 * \return \c FSSH_B_OK, if the entry has been moved successfully, another
5433 * error code otherwise.
5436 fssh_status_t
5437 _kern_rename(int oldFD, const char *oldPath, int newFD, const char *newPath)
5439 KPath oldPathBuffer(oldPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
5440 KPath newPathBuffer(newPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
5441 if (oldPathBuffer.InitCheck() != FSSH_B_OK || newPathBuffer.InitCheck() != FSSH_B_OK)
5442 return FSSH_B_NO_MEMORY;
5444 return common_rename(oldFD, oldPathBuffer.LockBuffer(),
5445 newFD, newPathBuffer.LockBuffer(), true);
5449 fssh_status_t
5450 _kern_access(const char *path, int mode)
5452 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5453 if (pathBuffer.InitCheck() != FSSH_B_OK)
5454 return FSSH_B_NO_MEMORY;
5456 return common_access(pathBuffer.LockBuffer(), mode, true);
5460 /** \brief Reads stat data of an entity specified by a FD + path pair.
5462 * If only \a fd is given, the stat operation associated with the type
5463 * of the FD (node, attr, attr dir etc.) is performed. If only \a path is
5464 * given, this path identifies the entry for whose node to retrieve the
5465 * stat data. If both \a fd and \a path are given and the path is absolute,
5466 * \a fd is ignored; a relative path is reckoned off of the directory (!)
5467 * identified by \a fd and specifies the entry whose stat data shall be
5468 * retrieved.
5470 * \param fd The FD. May be < 0.
5471 * \param path The absolute or relative path. Must not be \c NULL.
5472 * \param traverseLeafLink If \a path is given, \c true specifies that the
5473 * function shall not stick to symlinks, but traverse them.
5474 * \param stat The buffer the stat data shall be written into.
5475 * \param statSize The size of the supplied stat buffer.
5476 * \return \c FSSH_B_OK, if the the stat data have been read successfully, another
5477 * error code otherwise.
5480 fssh_status_t
5481 _kern_read_stat(int fd, const char *path, bool traverseLeafLink,
5482 fssh_struct_stat *stat, fssh_size_t statSize)
5484 fssh_struct_stat completeStat;
5485 fssh_struct_stat *originalStat = NULL;
5486 fssh_status_t status;
5488 if (statSize > sizeof(fssh_struct_stat))
5489 return FSSH_B_BAD_VALUE;
5491 // this supports different stat extensions
5492 if (statSize < sizeof(fssh_struct_stat)) {
5493 originalStat = stat;
5494 stat = &completeStat;
5497 if (path) {
5498 // path given: get the stat of the node referred to by (fd, path)
5499 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5500 if (pathBuffer.InitCheck() != FSSH_B_OK)
5501 return FSSH_B_NO_MEMORY;
5503 status = common_path_read_stat(fd, pathBuffer.LockBuffer(),
5504 traverseLeafLink, stat, true);
5505 } else {
5506 // no path given: get the FD and use the FD operation
5507 struct file_descriptor *descriptor
5508 = get_fd(get_current_io_context(true), fd);
5509 if (descriptor == NULL)
5510 return FSSH_B_FILE_ERROR;
5512 if (descriptor->ops->fd_read_stat)
5513 status = descriptor->ops->fd_read_stat(descriptor, stat);
5514 else
5515 status = FSSH_EOPNOTSUPP;
5517 put_fd(descriptor);
5520 if (status == FSSH_B_OK && originalStat != NULL)
5521 fssh_memcpy(originalStat, stat, statSize);
5523 return status;
5527 /** \brief Writes stat data of an entity specified by a FD + path pair.
5529 * If only \a fd is given, the stat operation associated with the type
5530 * of the FD (node, attr, attr dir etc.) is performed. If only \a path is
5531 * given, this path identifies the entry for whose node to write the
5532 * stat data. If both \a fd and \a path are given and the path is absolute,
5533 * \a fd is ignored; a relative path is reckoned off of the directory (!)
5534 * identified by \a fd and specifies the entry whose stat data shall be
5535 * written.
5537 * \param fd The FD. May be < 0.
5538 * \param path The absolute or relative path. Must not be \c NULL.
5539 * \param traverseLeafLink If \a path is given, \c true specifies that the
5540 * function shall not stick to symlinks, but traverse them.
5541 * \param stat The buffer containing the stat data to be written.
5542 * \param statSize The size of the supplied stat buffer.
5543 * \param statMask A mask specifying which parts of the stat data shall be
5544 * written.
5545 * \return \c FSSH_B_OK, if the the stat data have been written successfully,
5546 * another error code otherwise.
5549 fssh_status_t
5550 _kern_write_stat(int fd, const char *path, bool traverseLeafLink,
5551 const fssh_struct_stat *stat, fssh_size_t statSize, int statMask)
5553 fssh_struct_stat completeStat;
5555 if (statSize > sizeof(fssh_struct_stat))
5556 return FSSH_B_BAD_VALUE;
5558 // this supports different stat extensions
5559 if (statSize < sizeof(fssh_struct_stat)) {
5560 fssh_memset((uint8_t *)&completeStat + statSize, 0, sizeof(fssh_struct_stat) - statSize);
5561 fssh_memcpy(&completeStat, stat, statSize);
5562 stat = &completeStat;
5565 fssh_status_t status;
5567 if (path) {
5568 // path given: write the stat of the node referred to by (fd, path)
5569 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
5570 if (pathBuffer.InitCheck() != FSSH_B_OK)
5571 return FSSH_B_NO_MEMORY;
5573 status = common_path_write_stat(fd, pathBuffer.LockBuffer(),
5574 traverseLeafLink, stat, statMask, true);
5575 } else {
5576 // no path given: get the FD and use the FD operation
5577 struct file_descriptor *descriptor
5578 = get_fd(get_current_io_context(true), fd);
5579 if (descriptor == NULL)
5580 return FSSH_B_FILE_ERROR;
5582 if (descriptor->ops->fd_write_stat)
5583 status = descriptor->ops->fd_write_stat(descriptor, stat, statMask);
5584 else
5585 status = FSSH_EOPNOTSUPP;
5587 put_fd(descriptor);
5590 return status;
5595 _kern_open_attr_dir(int fd, const char *path)
5597 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
5598 if (pathBuffer.InitCheck() != FSSH_B_OK)
5599 return FSSH_B_NO_MEMORY;
5601 if (path != NULL)
5602 pathBuffer.SetTo(path);
5604 return attr_dir_open(fd, path ? pathBuffer.LockBuffer() : NULL, true);
5609 _kern_create_attr(int fd, const char *name, uint32_t type, int openMode)
5611 return attr_create(fd, name, type, openMode, true);
5616 _kern_open_attr(int fd, const char *name, int openMode)
5618 return attr_open(fd, name, openMode, true);
5622 fssh_status_t
5623 _kern_remove_attr(int fd, const char *name)
5625 return attr_remove(fd, name, true);
5629 fssh_status_t
5630 _kern_rename_attr(int fromFile, const char *fromName, int toFile, const char *toName)
5632 return attr_rename(fromFile, fromName, toFile, toName, true);
5637 _kern_open_index_dir(fssh_dev_t device)
5639 return index_dir_open(device, true);
5643 fssh_status_t
5644 _kern_create_index(fssh_dev_t device, const char *name, uint32_t type, uint32_t flags)
5646 return index_create(device, name, type, flags, true);
5650 fssh_status_t
5651 _kern_read_index_stat(fssh_dev_t device, const char *name, fssh_struct_stat *stat)
5653 return index_name_read_stat(device, name, stat, true);
5657 fssh_status_t
5658 _kern_remove_index(fssh_dev_t device, const char *name)
5660 return index_remove(device, name, true);
5664 fssh_status_t
5665 _kern_getcwd(char *buffer, fssh_size_t size)
5667 TRACE(("_kern_getcwd: buf %p, %ld\n", buffer, size));
5669 // Call vfs to get current working directory
5670 return get_cwd(buffer, size, true);
5674 fssh_status_t
5675 _kern_setcwd(int fd, const char *path)
5677 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
5678 if (pathBuffer.InitCheck() != FSSH_B_OK)
5679 return FSSH_B_NO_MEMORY;
5681 if (path != NULL)
5682 pathBuffer.SetTo(path);
5684 return set_cwd(fd, path != NULL ? pathBuffer.LockBuffer() : NULL, true);
5688 fssh_status_t
5689 _kern_initialize_volume(const char* fsName, const char *partition,
5690 const char *name, const char *parameters)
5692 if (!fsName || ! partition)
5693 return FSSH_B_BAD_VALUE;
5695 // The partition argument should point to a real file/device.
5697 // open partition
5698 int fd = fssh_open(partition, FSSH_O_RDWR);
5699 if (fd < 0)
5700 return fssh_errno;
5702 // get the file system module
5703 fssh_file_system_module_info* fsModule = get_file_system(fsName);
5704 if (fsModule == NULL) {
5705 fssh_close(fd);
5706 return FSSH_ENODEV;
5709 // initialize
5710 fssh_status_t status;
5711 if (fsModule->initialize) {
5712 status = (*fsModule->initialize)(fd, -1, name, parameters, 0, -1);
5713 // We've got no partition or job IDs -- the FS will hopefully
5714 // ignore that.
5715 // TODO: Get the actual size!
5716 } else
5717 status = FSSH_B_NOT_SUPPORTED;
5719 // put the file system module, close partition
5720 put_file_system(fsModule);
5721 fssh_close(fd);
5723 return status;
5727 fssh_status_t
5728 _kern_entry_ref_to_path(fssh_dev_t device, fssh_ino_t inode, const char *leaf,
5729 char* path, fssh_size_t pathLength)
5731 return vfs_entry_ref_to_path(device, inode, leaf, true, path, pathLength);
5736 _kern_open_query(fssh_dev_t device, const char *query, fssh_size_t queryLength,
5737 uint32_t flags, fssh_port_id port, int32_t token)
5739 return query_open(device, query, flags, port, token, false);
5743 } // namespace FSShell
5746 #include "vfs_request_io.cpp"