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.
9 /*! Virtual File System and File System Interface Layer */
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"
31 #include "fssh_unistd.h"
34 #include "posix_compatibility.h"
39 # define TRACE(x) fssh_dprintf x
40 # define FUNCTION(x) fssh_dprintf x
43 # define FUNCTION(x) ;
46 #define ADD_DEBUGGER_COMMANDS
48 #define ASSERT_LOCKED_MUTEX(x)
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
{
79 list_link unused_link
;
81 struct fs_mount
*mount
;
82 struct vnode
*covered_by
;
85 // TODO: S_INDEX_DIR actually needs another bit.
86 // Better combine this field with the following ones.
89 uint32_t unpublished
: 1;
90 struct file_descriptor
*mandatory_locked_by
;
93 struct vnode_hash_key
{
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
111 struct fs_mount
*next
;
112 fssh_file_system_module_info
*fs
;
114 fssh_fs_volume
*volume
;
117 fssh_recursive_lock rlock
; // guards the vnodes list
118 struct vnode
*root_vnode
;
119 struct vnode
*covers_vnode
;
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
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
,
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
,
201 static fssh_status_t
dir_read(struct vnode
*vnode
, void *cookie
,
202 struct fssh_dirent
*buffer
, fssh_size_t bufferSize
,
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
,
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
,
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
*,
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
,
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
,
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
,
240 static fssh_status_t
common_read_stat(struct file_descriptor
*,
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
= {
265 NULL
, // rewind_dir()
272 static struct fd_ops sDirectoryOps
= {
287 static struct fd_ops sAttributeDirectoryOps
= {
302 static struct fd_ops sAttributeOps
= {
310 NULL
, // rewind_dir()
317 static struct fd_ops sIndexDirectoryOps
= {
327 NULL
, // write_stat()
333 static struct fd_ops sIndexOps
= {
341 NULL
, // dir_rewind()
342 index_read_stat
, // read_stat()
343 NULL
, // write_stat()
349 static struct fd_ops sQueryOps
= {
359 NULL
, // write_stat()
368 VNodePutter(struct vnode
*vnode
= NULL
) : fVNode(vnode
) {}
375 void SetTo(struct vnode
*vnode
)
389 struct vnode
*Detach()
391 struct vnode
*vnode
= fVNode
;
397 struct vnode
*fVNode
;
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
)
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
;
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
);
441 get_mount(fssh_mount_id id
, struct fs_mount
**_mount
)
443 MutexLocker
locker(&sMountMutex
);
445 struct fs_mount
*mount
= find_mount(id
);
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
455 inc_vnode_ref_count(mount
->root_vnode
);
463 put_mount(struct fs_mount
*mount
)
466 put_vnode(mount
->root_vnode
);
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
);
494 fssh_file_system_module_info
*info
;
495 if (fssh_get_module(fsName
? fsName
: name
, (fssh_module_info
**)&info
) != FSSH_B_OK
)
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
506 * Returns NULL if the required memory is no available.
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
);
520 const char *end
= fssh_strchr(fsName
, '/');
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
);
532 fssh_strlcpy(name
, fsName
, end
+ 1 - fsName
);
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
)
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))
559 return VHASH(vnode
->device
, vnode
->id
) % range
;
561 return VHASH(key
->device
, key
->vnode
) % range
;
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
);
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
);
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
));
597 return FSSH_B_NO_MEMORY
;
599 // initialize basic values
600 fssh_memset(vnode
, 0, sizeof(struct vnode
));
601 vnode
->device
= mountID
;
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
);
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;
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.
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
639 if (!vnode
->remove
&& HAS_FS_CALL(vnode
, fsync
))
640 FS_CALL_NO_PARAMS(vnode
, fsync
);
642 if (!vnode
->unpublished
) {
644 FS_CALL(vnode
, remove_vnode
, reenter
);
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
);
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
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
671 * \return \c FSSH_B_OK, if everything went fine, an error code otherwise.
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) {
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
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
);
706 fssh_mutex_unlock(&sVnodeMutex
);
709 free_vnode(vnode
, reenter
);
711 fssh_mutex_unlock(&sVnodeMutex
);
717 /** \brief Increments the reference counter of the given vnode.
719 * The caller must either already have a reference to the vnode or hold
722 * \param vnode the vnode.
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
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
;
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
768 * \return \c FSSH_B_OK, if everything when fine, an error code otherwise.
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
);
781 struct vnode
*vnode
= lookup_vnode(mountID
, vnodeID
);
782 if (vnode
&& vnode
->busy
) {
783 fssh_mutex_unlock(&sVnodeMutex
);
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
);
790 fssh_snooze(10000); // 10 ms
791 fssh_mutex_lock(&sVnodeMutex
);
795 TRACE(("get_vnode: tried to lookup vnode, got %p\n", vnode
));
797 fssh_status_t status
;
800 if (vnode
->ref_count
== 0) {
801 // this vnode has been unused before
802 list_remove_item(&sUnusedVnodeList
, vnode
);
805 inc_vnode_ref_count(vnode
);
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
)
813 fssh_mutex_unlock(&sVnodeMutex
);
817 status
= FS_MOUNT_CALL(vnode
->mount
, get_vnode
, vnodeID
, vnode
, &type
,
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
)
831 fssh_mutex_unlock(&sVnodeMutex
);
833 TRACE(("get_vnode: returning %p\n", vnode
));
839 hash_remove(sVnodeTable
, vnode
);
840 remove_vnode_from_mount_list(vnode
, vnode
->mount
);
842 fssh_mutex_unlock(&sVnodeMutex
);
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
855 * The caller must not hold the sVnodeMutex or the sMountMutex.
857 * \param vnode the vnode.
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
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!
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
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
)
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
);
917 /** \brief Resolves a mount point vnode to the volume root vnode it is covered
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
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.
934 * - \c FSSH_B_OK, if everything went fine,
935 * - another error code, if something went wrong.
939 resolve_mount_point_to_volume_root(fssh_mount_id mountID
, fssh_vnode_id nodeID
,
940 fssh_mount_id
*resolvedMountID
, fssh_vnode_id
*resolvedNodeID
)
944 fssh_status_t error
= get_vnode(mountID
, nodeID
, &node
, false);
945 if (error
!= FSSH_B_OK
)
949 struct vnode
*resolvedNode
= resolve_mount_point_to_volume_root(node
);
955 // set the return values
956 *resolvedMountID
= node
->device
;
957 *resolvedNodeID
= node
->id
;
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
)
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
);
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
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
));
1019 // this path is single segment with no '/' in it
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
, ".");
1027 // special case: the path ends in '/'
1028 fssh_strcpy(filename
, ".");
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
;
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);
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
)
1065 fssh_status_t status
= FS_CALL(dir
, lookup
, name
, &id
);
1066 if (status
< FSSH_B_OK
)
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
;
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
));
1102 return FSSH_B_BAD_VALUE
;
1106 struct vnode
*nextVnode
;
1109 TRACE(("vnode_path_to_vnode: top of loop. p = %p, p = '%s'\n", path
, path
));
1112 if (path
[0] == '\0')
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
== '/') {
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
);
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
) {
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
;
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
,
1177 status
= FSSH_B_BAD_VALUE
;
1179 if (status
< FSSH_B_OK
) {
1184 put_vnode(nextVnode
);
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
1194 if (path
[0] == '/') {
1195 // we don't need the old directory anymore
1198 while (*++path
== '/')
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
);
1212 if (status
< FSSH_B_OK
) {
1217 lastParentID
= vnode
->id
;
1219 // decrease the ref count on the old dir we just looked up into
1225 // see if we hit a mount point
1226 struct vnode
*mountPoint
= resolve_mount_point_to_volume_root(vnode
);
1235 *_parentID
= lastParentID
;
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
));
1250 return FSSH_B_BAD_VALUE
;
1252 // figure out if we need to start at root or at cwd
1254 if (sRoot
== NULL
) {
1255 // we're a bit early, aren't we?
1256 return FSSH_B_ERROR
;
1259 while (*++path
== '/')
1262 inc_vnode_ref_count(start
);
1264 struct io_context
*context
= get_current_io_context(kernel
);
1266 fssh_mutex_lock(&context
->io_mutex
);
1267 start
= context
->cwd
;
1269 inc_vnode_ref_count(start
);
1270 fssh_mutex_unlock(&context
->io_mutex
);
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
)
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
1305 * The caller has the responsibility to call put_vnode() on the returned
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
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
)
1326 return FSSH_B_BAD_VALUE
;
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
)
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.
1367 return FSSH_EOPNOTSUPP
;
1371 fssh_status_t status
= FS_CALL(parent
, open_dir
, &cookie
);
1372 if (status
>= FSSH_B_OK
) {
1375 status
= dir_read(parent
, cookie
, buffer
, bufferSize
, &num
);
1376 if (status
< FSSH_B_OK
)
1379 status
= FSSH_B_ENTRY_NOT_FOUND
;
1383 if (vnode
->id
== buffer
->d_ino
) {
1384 // found correct entry!
1389 FS_CALL(vnode
, close_dir
, cookie
);
1390 FS_CALL(vnode
, free_dir_cookie
, cookie
);
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
)
1407 if (fssh_strlcpy(name
, dirent
->d_name
, nameSize
) >= nameSize
)
1408 return FSSH_B_BUFFER_OVERFLOW
;
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
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 */
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;
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
);
1460 path
[--insert
] = '\0';
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
)
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
);
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
1490 vnode
= parentVnode
;
1492 if (status
< FSSH_B_OK
)
1496 // we have reached "/", which means we have constructed the full
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
;
1510 // add the name in front of the current path
1511 name
[FSSH_B_FILE_NAME_LENGTH
- 1] = '\0';
1512 length
= fssh_strlen(name
);
1515 status
= FSSH_ENOBUFS
;
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
);
1533 status
= FSSH_ENOBUFS
;
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
)
1552 // check length of every path component
1560 while (*to
!= '/' && *to
)
1563 if (to
- begin
> FSSH_B_FILE_NAME_LENGTH
)
1564 return FSSH_B_NAME_TOO_LONG
;
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
;
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
)
1591 if (fd_vnode(descriptor
) == 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
1599 *_vnode
= descriptor
->u
.vnode
;
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
)
1614 vnode
= fd_vnode(descriptor
);
1616 inc_vnode_ref_count(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
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
);
1645 return FSSH_B_FILE_ERROR
;
1648 return vnode_path_to_vnode(vnode
, path
, traverseLeafLink
, 0,
1652 // there is no relative path to take into account
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
;
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
)
1673 descriptor
= alloc_fd();
1675 return FSSH_B_NO_MEMORY
;
1678 descriptor
->u
.vnode
= vnode
;
1680 descriptor
->u
.mount
= mount
;
1681 descriptor
->cookie
= cookie
;
1686 descriptor
->ops
= &sFileOps
;
1689 descriptor
->ops
= &sDirectoryOps
;
1692 descriptor
->ops
= &sAttributeOps
;
1694 case FDTYPE_ATTR_DIR
:
1695 descriptor
->ops
= &sAttributeDirectoryOps
;
1699 case FDTYPE_INDEX_DIR
:
1700 descriptor
->ops
= &sIndexDirectoryOps
;
1703 descriptor
->ops
= &sQueryOps
;
1707 fssh_panic("get_new_fd() called with unknown type %d\n", type
);
1710 descriptor
->type
= type
;
1711 descriptor
->open_mode
= openMode
;
1713 fd
= new_fd(get_current_io_context(kernel
), descriptor
);
1716 return FSSH_B_NO_MORE_FDS
;
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
,
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
;
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
)
1753 status
= fssh_read_pages(fd
, fileVecs
[0].offset
, &vecs
[vecIndex
],
1754 vecCount
- vecIndex
, &size
);
1755 if (status
< FSSH_B_OK
)
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
)
1774 // if we reached the end of the file, we can return as well
1775 if ((uint64_t)size
!= (uint64_t)fileVecs
[0].length
) {
1782 // first, find out where we have to continue in our iovecs
1783 for (; vecIndex
< vecCount
; vecIndex
++) {
1784 if (size
< vecs
[vecIndex
].iov_len
)
1787 size
-= vecs
[vecIndex
].iov_len
;
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
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
1823 // bytes left of the current iovec
1824 fssh_size_t vecLeft
= vecs
[vecIndex
].iov_len
- vecOffset
;
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
;
1842 size
+= tempVecSize
;
1843 vecOffset
+= tempVecSize
;
1846 fssh_size_t bytes
= size
;
1848 status
= fssh_write_pages(fd
, fileOffset
, tempVecs
,
1851 status
= fssh_read_pages(fd
, fileOffset
, tempVecs
,
1854 if (status
< FSSH_B_OK
)
1862 if (size
!= bytes
|| vecIndex
>= vecCount
) {
1863 // there are no more bytes or iovecs, let's bail out
1864 *_numBytes
= totalSize
;
1870 *_vecIndex
= vecIndex
;
1871 *_vecOffset
= vecOffset
;
1872 *_numBytes
= totalSize
;
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
;
1910 vnode
->unpublished
= true;
1913 TRACE(("returns: %s\n", strerror(status
)));
1915 fssh_mutex_unlock(&sVnodeMutex
);
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
;
1940 vnode
->unpublished
= true;
1943 status
= FSSH_B_BAD_VALUE
;
1945 // create sub vnodes, if necessary
1946 if (status
== FSSH_B_OK
&& volume
->sub_volume
!= NULL
) {
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
,
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
);
1967 if (status
== FSSH_B_OK
) {
1969 vnode
->busy
= false;
1970 vnode
->unpublished
= false;
1973 TRACE(("returns: %s\n", strerror(status
)));
1979 extern "C" fssh_status_t
1980 fssh_get_vnode(fssh_fs_volume
*volume
, fssh_vnode_id vnodeID
,
1983 struct vnode
*vnode
;
1986 return FSSH_B_BAD_VALUE
;
1988 fssh_status_t status
= get_vnode(volume
->id
, vnodeID
, &vnode
, true);
1989 if (status
< FSSH_B_OK
)
1992 // If this is a layered FS, we need to get the node cookie for the requested
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
,
1998 if (status
!= FSSH_B_OK
) {
1999 fssh_panic("get_vnode(): Failed to get super node for vnode %p, "
2000 "volume: %p", vnode
, volume
);
2005 if (privateNode
!= NULL
)
2006 *privateNode
= resolvedNode
.private_node
;
2007 } else if (privateNode
!= NULL
)
2008 *privateNode
= vnode
->private_node
;
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
);
2024 return FSSH_B_BAD_VALUE
;
2026 inc_vnode_ref_count(vnode
);
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
);
2041 return FSSH_B_BAD_VALUE
;
2043 dec_vnode_ref_count(vnode
, true);
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
);
2058 return FSSH_B_ENTRY_NOT_FOUND
;
2060 if (vnode
->covered_by
!= NULL
) {
2061 // this vnode is in use
2062 fssh_mutex_unlock(&sVnodeMutex
);
2066 vnode
->remove
= true;
2067 if (vnode
->unpublished
) {
2068 // prepare the vnode for deletion
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);
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
);
2094 vnode
->remove
= false;
2096 fssh_mutex_unlock(&sVnodeMutex
);
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
)) {
2110 *removed
= vnode
->remove
;
2113 result
= FSSH_B_BAD_VALUE
;
2115 fssh_mutex_unlock(&sVnodeMutex
);
2120 extern "C" fssh_fs_volume
*
2121 fssh_volume_for_vnode(fssh_fs_vnode
*_vnode
)
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();
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
;
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
];
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
;
2198 fssh_ssize_t bytesRead
= fssh_readv_pos(fd
, pos
, vecs
, count
);
2201 return fssh_get_errno();
2203 *_numBytes
= bytesRead
;
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
];
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
;
2239 fssh_ssize_t bytesWritten
= fssh_writev_pos(fd
, pos
, vecs
, count
);
2241 if (bytesWritten
< 0)
2242 return fssh_get_errno();
2244 *_numBytes
= bytesWritten
;
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
,
2275 // We don't implement an entry cache in the FS shell.
2280 extern "C" fssh_status_t
2281 fssh_entry_cache_add_missing(fssh_dev_t mountID
, fssh_ino_t dirID
,
2284 // We don't implement an entry cache in the FS shell.
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().
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
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
;
2335 vfs_get_vnode_from_fd(int fd
, bool kernel
, void **vnode
)
2337 *vnode
= get_vnode_from_fd(fd
, kernel
);
2340 return FSSH_B_FILE_ERROR
;
2342 return FSSH_B_NO_ERROR
;
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
)
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
)
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
);
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
);
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
);
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
2427 * It's currently only be used by file_cache_create().
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
);
2439 return FSSH_B_ERROR
;
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
;
2458 fssh_status_t status
= get_mount(volume
->id
, &mount
);
2459 if (status
< FSSH_B_OK
)
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);
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
);
2477 if (status
< FSSH_B_OK
)
2480 if (vnode
->device
!= volume
->id
) {
2481 // wrong mount ID - must not gain access on foreign file system nodes
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
);
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.
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
;
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
)
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
);
2531 length
= nextPath
- moduleName
;
2535 if (length
+ 1 >= bufferSize
) {
2536 status
= FSSH_B_BUFFER_OVERFLOW
;
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
2550 if (FSSH_S_ISDIR(file
->type
)) {
2551 // goto the next directory
2553 path
[length
+ 1] = '\0';
2555 bufferSize
-= length
+ 1;
2558 } else if (FSSH_S_ISREG(file
->type
)) {
2559 // it's a file so it should be what we've searched for
2564 TRACE(("vfs_get_module_path(): something is strange here: %d...\n", file
->type
));
2565 status
= FSSH_B_ERROR
;
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
;
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',
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.
2605 vfs_normalize_path(const char *path
, char *buffer
, fssh_size_t bufferSize
,
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
)));
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);
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",
2642 // get the directory path
2643 error
= dir_vnode_to_path(dirNode
, buffer
, bufferSize
);
2645 if (error
< FSSH_B_OK
) {
2646 TRACE(("vfs_normalize_path(): failed to get dir path: %s\n", strerror(error
)));
2650 // append the leaf name
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
));
2666 vfs_put_vnode(void *_vnode
)
2668 put_vnode((struct vnode
*)_vnode
);
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
;
2685 status
= FSSH_B_ERROR
;
2687 fssh_mutex_unlock(&context
->io_mutex
);
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
);
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
;
2722 vfs_get_vnode_name(void *_vnode
, char *name
, fssh_size_t nameSize
)
2724 return get_vnode_name((struct vnode
*)_vnode
, NULL
, name
, nameSize
);
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
);
2746 status
= get_vnode(device
, inode
, &vnode
, false);
2747 if (status
< FSSH_B_OK
)
2750 // get the directory path
2751 status
= dir_vnode_to_path(vnode
, path
, pathLength
);
2753 // we don't need the vnode anymore
2754 if (status
< FSSH_B_OK
)
2757 // append the leaf name
2759 // insert a directory separator if this is not the file system root
2760 if ((fssh_strcmp(path
, "/") && fssh_strlcat(path
, "/", pathLength
)
2762 || fssh_strlcat(path
, leaf
, pathLength
) >= pathLength
) {
2763 return FSSH_B_NAME_TOO_LONG
;
2771 /** If the given descriptor locked its vnode, that lock will be released.
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.
2789 vfs_exec_io_context(void *_context
)
2791 struct io_context
*context
= (struct io_context
*)_context
;
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
--;
2807 fssh_mutex_unlock(&context
->io_mutex
);
2810 close_fd(descriptor
);
2817 /** Sets up a new io_control structure, and inherits the properties
2818 * of the parent io_control if it is given.
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
)
2832 fssh_memset(context
, 0, sizeof(struct io_context
));
2834 parentContext
= (struct io_context
*)_parentContext
;
2836 tableSize
= parentContext
->table_size
;
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
) {
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
) {
2859 fssh_mutex_lock(&parentContext
->io_mutex
);
2861 context
->cwd
= parentContext
->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
);
2878 context
->cwd
= sRoot
;
2881 inc_vnode_ref_count(context
->cwd
);
2884 context
->table_size
= tableSize
;
2891 vfs_free_io_context(void *_ioContext
)
2893 struct io_context
*context
= (struct io_context
*)_ioContext
;
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
);
2908 fssh_mutex_destroy(&context
->io_mutex
);
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");
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();
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
2956 create_vnode(struct vnode
*directory
, const char *name
, int openMode
, int perms
, bool kernel
)
2958 struct vnode
*vnode
;
2960 fssh_vnode_id newID
;
2963 if (!HAS_FS_CALL(directory
, create
))
2966 status
= FS_CALL(directory
, create
, name
, openMode
, perms
, &cookie
, &newID
);
2967 if (status
< FSSH_B_OK
)
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!");
2979 if ((status
= get_new_fd(FDTYPE_FILE
, NULL
, vnode
, cookie
, openMode
, kernel
)) >= 0)
2982 // something went wrong, clean up
2984 FS_CALL(vnode
, close
, cookie
);
2985 FS_CALL(vnode
, free_cookie
, cookie
);
2988 FS_CALL(directory
, unlink
, name
);
2994 /** Calls fs_open() on the given vnode and returns a new
2995 * file descriptor for it
2999 open_vnode(struct vnode
*vnode
, int openMode
, bool kernel
)
3004 status
= FS_CALL(vnode
, open
, openMode
, &cookie
);
3008 status
= get_new_fd(FDTYPE_FILE
, NULL
, vnode
, cookie
, openMode
, kernel
);
3010 FS_CALL(vnode
, close
, cookie
);
3011 FS_CALL(vnode
, free_cookie
, cookie
);
3017 /** Calls fs open_dir() on the given vnode and returns a new
3018 * file descriptor for it
3022 open_dir_vnode(struct vnode
*vnode
, bool kernel
)
3027 status
= FS_CALL(vnode
, open_dir
, &cookie
);
3028 if (status
< FSSH_B_OK
)
3031 // file is opened, create a fd
3032 status
= get_new_fd(FDTYPE_DIR
, NULL
, vnode
, cookie
, 0, kernel
);
3036 FS_CALL(vnode
, close_dir
, cookie
);
3037 FS_CALL(vnode
, free_dir_cookie
, cookie
);
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().
3049 open_attr_dir_vnode(struct vnode
*vnode
, bool kernel
)
3054 if (!HAS_FS_CALL(vnode
, open_attr_dir
))
3055 return FSSH_EOPNOTSUPP
;
3057 status
= FS_CALL(vnode
, open_attr_dir
, &cookie
);
3061 // file is opened, create a fd
3062 status
= get_new_fd(FDTYPE_ATTR_DIR
, NULL
, vnode
, cookie
, 0, kernel
);
3066 FS_CALL(vnode
, close_attr_dir
, cookie
);
3067 FS_CALL(vnode
, free_attr_dir_cookie
, cookie
);
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
;
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
)
3086 status
= create_vnode(directory
, name
, openMode
, perms
, kernel
);
3087 put_vnode(directory
);
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
;
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
);
3107 status
= create_vnode(directory
, name
, openMode
, perms
, kernel
);
3109 put_vnode(directory
);
3115 file_open_entry_ref(fssh_mount_id mountID
, fssh_vnode_id directoryID
, const char *name
, int openMode
, bool kernel
)
3117 struct vnode
*vnode
;
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
)
3131 status
= open_vnode(vnode
, openMode
, kernel
);
3132 if (status
< FSSH_B_OK
)
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
)
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
)
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
);
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
);
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
);
3213 file_seek(struct file_descriptor
*descriptor
, fssh_off_t pos
, int seekType
)
3217 FUNCTION(("file_seek(pos = %Ld, seekType = %d)\n", pos
, seekType
));
3218 // ToDo: seek should fail for pipes and FIFOs...
3225 offset
= descriptor
->pos
;
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
)
3240 offset
= stat
.fssh_st_size
;
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
;
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
)
3274 if (HAS_FS_CALL(vnode
, create_dir
))
3275 status
= FS_CALL(vnode
, create_dir
, name
, perms
);
3277 status
= FSSH_EROFS
;
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
);
3297 if (HAS_FS_CALL(vnode
, create_dir
))
3298 status
= FS_CALL(vnode
, create_dir
, filename
, perms
);
3300 status
= FSSH_EROFS
;
3308 dir_open_entry_ref(fssh_mount_id mountID
, fssh_vnode_id parentID
, const char *name
, bool kernel
)
3310 struct vnode
*vnode
;
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
3320 status
= entry_ref_to_vnode(mountID
, parentID
, name
, &vnode
);
3322 status
= get_vnode(mountID
, parentID
, &vnode
, false);
3323 if (status
< FSSH_B_OK
)
3326 status
= open_dir_vnode(vnode
, kernel
);
3327 if (status
< FSSH_B_OK
)
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
)
3349 status
= open_dir_vnode(vnode
, kernel
);
3350 if (status
< FSSH_B_OK
)
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
);
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
);
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
);
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,
3411 if (status
== FSSH_B_OK
) {
3412 entry
->d_dev
= vnode
->device
;
3413 entry
->d_ino
= vnode
->id
;
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
)
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
);
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
)
3445 // we need to adjust the read dirents
3447 // XXX: Currently reading only one dirent is supported. Make this a loop!
3448 fix_dirent(vnode
, buffer
);
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
;
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] == '/') {
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
)
3500 if (HAS_FS_CALL(directory
, remove_dir
)) {
3501 status
= FS_CALL(directory
, remove_dir
, name
);
3503 status
= FSSH_EROFS
;
3505 put_vnode(directory
);
3510 static fssh_status_t
3511 common_ioctl(struct file_descriptor
*descriptor
, uint32_t op
, void *buffer
,
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
;
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
);
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
);
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
))
3578 status
= FSSH_EOPNOTSUPP
;
3582 // Get file descriptor open mode
3583 status
= descriptor
->open_mode
;
3588 struct io_context
*context
= get_current_io_context(kernel
);
3590 status
= new_fd_etc(context
, descriptor
, (int)argument
);
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);
3604 status
= FSSH_B_BAD_VALUE
;
3607 // ToDo: add support for more ops?
3610 status
= FSSH_B_BAD_VALUE
;
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
);
3634 status
= FSSH_EOPNOTSUPP
;
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
3656 if (fssh_atomic_test_and_set64((int64_t *)&vnode
->mandatory_locked_by
,
3657 (fssh_addr_t
)descriptor
, 0) != 0)
3659 if (fssh_atomic_test_and_set((int32_t *)&vnode
->mandatory_locked_by
,
3660 (fssh_addr_t
)descriptor
, 0) != 0)
3662 status
= FSSH_B_BUSY
;
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
3684 if (fssh_atomic_test_and_set64((int64_t *)&vnode
->mandatory_locked_by
,
3685 0, (fssh_addr_t
)descriptor
) != (int64_t)descriptor
)
3687 if (fssh_atomic_test_and_set((int32_t *)&vnode
->mandatory_locked_by
,
3688 0, (fssh_addr_t
)descriptor
) != (int32_t)descriptor
)
3690 status
= FSSH_B_BAD_VALUE
;
3697 static fssh_status_t
3698 common_read_link(int fd
, char *path
, char *buffer
, fssh_size_t
*_bufferSize
,
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
)
3708 if (HAS_FS_CALL(vnode
, read_symlink
)) {
3709 status
= FS_CALL(vnode
, read_symlink
, buffer
, _bufferSize
);
3711 status
= FSSH_B_BAD_VALUE
;
3718 static fssh_status_t
3719 common_create_symlink(int fd
, char *path
, const char *toPath
, int mode
,
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
)
3733 if (HAS_FS_CALL(vnode
, create_symlink
))
3734 status
= FS_CALL(vnode
, create_symlink
, name
, toPath
, mode
);
3736 status
= FSSH_EROFS
;
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
)
3758 status
= path_to_vnode(toPath
, true, &vnode
, NULL
, kernel
);
3759 if (status
< FSSH_B_OK
)
3762 if (directory
->mount
!= vnode
->mount
) {
3763 status
= FSSH_B_CROSS_DEVICE_LINK
;
3767 if (HAS_FS_CALL(directory
, link
))
3768 status
= FS_CALL(directory
, link
, name
, vnode
);
3770 status
= FSSH_EROFS
;
3775 put_vnode(directory
);
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
);
3794 if (HAS_FS_CALL(vnode
, unlink
))
3795 status
= FS_CALL(vnode
, unlink
, filename
);
3797 status
= FSSH_EROFS
;
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
)
3815 if (HAS_FS_CALL(vnode
, access
))
3816 status
= FS_CALL(vnode
, access
, mode
);
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
);
3840 status
= fd_and_path_to_dir_vnode(newFD
, newPath
, &toVnode
, toName
, kernel
);
3844 if (fromVnode
->device
!= toVnode
->device
) {
3845 status
= FSSH_B_CROSS_DEVICE_LINK
;
3849 if (HAS_FS_CALL(fromVnode
, rename
))
3850 status
= FS_CALL(fromVnode
, rename
, fromName
, toVnode
, toName
);
3852 status
= FSSH_EROFS
;
3857 put_vnode(fromVnode
);
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
;
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
))
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
);
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
;
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
);
3940 if (HAS_FS_CALL(vnode
, write_stat
))
3941 status
= FS_CALL(vnode
, write_stat
, stat
, statMask
);
3943 status
= FSSH_EROFS
;
3952 attr_dir_open(int fd
, char *path
, bool kernel
)
3954 struct vnode
*vnode
;
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
)
3963 status
= open_attr_dir_vnode(vnode
, kernel
);
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
);
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
);
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
;
4027 attr_create(int fd
, const char *name
, uint32_t type
, int openMode
, bool kernel
)
4029 struct vnode
*vnode
;
4033 if (name
== NULL
|| *name
== '\0')
4034 return FSSH_B_BAD_VALUE
;
4036 vnode
= get_vnode_from_fd(fd
, kernel
);
4038 return FSSH_B_FILE_ERROR
;
4040 if (!HAS_FS_CALL(vnode
, create_attr
)) {
4041 status
= FSSH_EROFS
;
4045 status
= FS_CALL(vnode
, create_attr
, name
, type
, openMode
, &cookie
);
4046 if (status
< FSSH_B_OK
)
4049 if ((status
= get_new_fd(FDTYPE_ATTR
, NULL
, vnode
, cookie
, openMode
, kernel
)) >= 0)
4052 FS_CALL(vnode
, close_attr
, cookie
);
4053 FS_CALL(vnode
, free_attr_cookie
, cookie
);
4055 FS_CALL(vnode
, remove_attr
, name
);
4065 attr_open(int fd
, const char *name
, int openMode
, bool kernel
)
4067 struct vnode
*vnode
;
4071 if (name
== NULL
|| *name
== '\0')
4072 return FSSH_B_BAD_VALUE
;
4074 vnode
= get_vnode_from_fd(fd
, kernel
);
4076 return FSSH_B_FILE_ERROR
;
4078 if (!HAS_FS_CALL(vnode
, open_attr
)) {
4079 status
= FSSH_EOPNOTSUPP
;
4083 status
= FS_CALL(vnode
, open_attr
, name
, openMode
, &cookie
);
4084 if (status
< FSSH_B_OK
)
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)
4091 FS_CALL(vnode
, close_attr
, cookie
);
4092 FS_CALL(vnode
, free_attr_cookie
, cookie
);
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
);
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
);
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
);
4154 attr_seek(struct file_descriptor
*descriptor
, fssh_off_t pos
, int seekType
)
4163 offset
= descriptor
->pos
;
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
)
4178 offset
= stat
.fssh_st_size
;
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
;
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
))
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
);
4245 status
= FSSH_EROFS
;
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
;
4275 // are the files on the same volume?
4276 if (fromVnode
->device
!= toVnode
->device
) {
4277 status
= FSSH_B_CROSS_DEVICE_LINK
;
4281 if (HAS_FS_CALL(fromVnode
, rename_attr
))
4282 status
= FS_CALL(fromVnode
, rename_attr
, fromName
, toVnode
, toName
);
4284 status
= FSSH_EROFS
;
4287 put_fd(toDescriptor
);
4289 put_fd(fromDescriptor
);
4295 static fssh_status_t
4296 index_dir_open(fssh_mount_id mountID
, bool kernel
)
4298 struct fs_mount
*mount
;
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
)
4307 if (!HAS_FS_MOUNT_CALL(mount
, open_index_dir
)) {
4308 status
= FSSH_EOPNOTSUPP
;
4312 status
= FS_MOUNT_CALL(mount
, open_index_dir
, &cookie
);
4313 if (status
< FSSH_B_OK
)
4316 // get fd for the index directory
4317 status
= get_new_fd(FDTYPE_INDEX_DIR
, mount
, NULL
, cookie
, 0, kernel
);
4321 // something went wrong
4322 FS_MOUNT_CALL(mount
, close_index_dir
, cookie
);
4323 FS_MOUNT_CALL(mount
, free_index_dir_cookie
, cookie
);
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
);
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?
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
)
4393 if (!HAS_FS_MOUNT_CALL(mount
, create_index
)) {
4394 status
= FSSH_EROFS
;
4398 status
= FS_MOUNT_CALL(mount
, create_index
, name
, type
, flags
);
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
)
4417 if (!HAS_FS_MOUNT_CALL(mount
, read_index_stat
)) {
4418 status
= FSSH_EOPNOTSUPP
;
4422 status
= FS_MOUNT_CALL(mount
, read_index_stat
, name
, stat
);
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
)
4440 if (!HAS_FS_MOUNT_CALL(mount
, remove_index
)) {
4441 status
= FSSH_EROFS
;
4445 status
= FS_MOUNT_CALL(mount
, remove_index
, name
);
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
4456 For example, query parsing should be moved into the kernel.
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
;
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
)
4471 if (!HAS_FS_MOUNT_CALL(mount
, open_query
)) {
4472 status
= FSSH_EOPNOTSUPP
;
4476 status
= FS_MOUNT_CALL(mount
, open_query
, query
, flags
, port
, token
, &cookie
);
4477 if (status
< FSSH_B_OK
)
4480 // get fd for the index directory
4481 status
= get_new_fd(FDTYPE_QUERY
, mount
, NULL
, cookie
, 0, kernel
);
4485 // something went wrong
4486 FS_MOUNT_CALL(mount
, close_query
, cookie
);
4487 FS_MOUNT_CALL(mount
, free_query_cookie
, cookie
);
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
);
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?
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
;
4548 // General File System functions
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
)
4585 device
= normalizedDevice
.Path();
4586 // correct path to file device
4589 mount
= (struct fs_mount
*)malloc(sizeof(struct fs_mount
));
4591 return FSSH_B_NO_MEMORY
;
4593 mount
->volume
= (fssh_fs_volume
*)malloc(sizeof(fssh_fs_volume
));
4594 if (mount
->volume
== NULL
) {
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
;
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
;
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
;
4641 // we haven't mounted anything yet
4642 if (fssh_strcmp(path
, "/") != 0) {
4643 status
= FSSH_B_ERROR
;
4647 status
= mount
->fs
->mount(mount
->volume
, device
, flags
, args
, &rootID
);
4649 // ToDo: why should we hide the error code from the file system here?
4650 //status = ERR_VFS_GENERAL;
4654 struct vnode
*coveredVnode
;
4655 status
= path_to_vnode(path
, true, &coveredVnode
, NULL
, kernel
);
4656 if (status
< FSSH_B_OK
)
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
)
4665 if (!FSSH_S_ISDIR(coveredNodeStat
.fssh_st_mode
)) {
4666 status
= FSSH_B_NOT_A_DIRECTORY
;
4670 if (coveredVnode
->mount
->root_vnode
== coveredVnode
) {
4671 // this is already a mount point
4672 status
= FSSH_B_BUSY
;
4676 mount
->covers_vnode
= coveredVnode
;
4679 status
= mount
->fs
->mount(mount
->volume
, device
, flags
, args
, &rootID
);
4680 if (status
< FSSH_B_OK
)
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
;
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
);
4701 sRoot
= mount
->root_vnode
;
4706 FS_MOUNT_CALL_NO_PARAMS(mount
, unmount
);
4708 if (mount
->covers_vnode
)
4709 put_vnode(mount
->covers_vnode
);
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
);
4721 free(mount
->fs_name
);
4723 free(mount
->volume
);
4730 static fssh_status_t
4731 fs_unmount(char *path
, uint32_t flags
, bool kernel
)
4733 struct fs_mount
*mount
;
4734 struct vnode
*vnode
;
4737 FUNCTION(("vfs_unmount: entry. path = '%s', kernel %d\n", path
, kernel
));
4739 err
= path_to_vnode(path
, true, &vnode
, NULL
, kernel
);
4741 return FSSH_B_ENTRY_NOT_FOUND
;
4743 RecursiveLocker
mountOpLocker(sMountOpLock
);
4745 mount
= find_mount(vnode
->device
);
4747 fssh_panic("vfs_unmount: find_mount() failed on root vnode @%p of mount\n", vnode
);
4749 if (mount
->root_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;
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
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
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
4783 if ((flags
& FSSH_B_FORCE_UNMOUNT
) == 0) {
4784 fssh_mutex_unlock(&sVnodeMutex
);
4785 put_vnode(mount
->root_vnode
);
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
);
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
) {
4821 if (vnode
->ref_count
== 0) {
4822 // this vnode has been unused before
4823 list_remove_item(&sUnusedVnodeList
, vnode
);
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
);
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
)
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
;
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
,
4888 fssh_vnode_id id
= -1;
4892 fssh_recursive_lock_unlock(&mount
->rlock
);
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
;
4910 fssh_dprintf("syncing of mount %d stopped due to vnode %"
4911 FSSH_B_PRIdINO
".\n", (int)mount
->id
, id
);
4916 if (previousVnode
!= NULL
)
4917 put_vnode(previousVnode
);
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
)
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
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
)
4964 if (HAS_FS_MOUNT_CALL(mount
, write_fs_info
))
4965 status
= FS_MOUNT_CALL(mount
, write_fs_info
, info
, mask
);
4967 status
= FSSH_EROFS
;
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
)
4998 device
= FSSH_B_BAD_VALUE
;
5000 fssh_mutex_unlock(&sMountMutex
);
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
);
5018 status
= dir_vnode_to_path(context
->cwd
, buffer
, size
);
5020 status
= FSSH_B_ERROR
;
5022 fssh_mutex_unlock(&context
->io_mutex
);
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
);
5043 status
= FS_CALL(vnode
, read_stat
, &stat
);
5047 if (!FSSH_S_ISDIR(stat
.fssh_st_mode
)) {
5048 // nope, can't cwd to here
5049 status
= FSSH_B_NOT_A_DIRECTORY
;
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
);
5064 put_vnode(oldDirectory
);
5066 return FSSH_B_NO_ERROR
;
5075 // Calls from within the kernel
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);
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);
5102 _kern_read_fs_info(fssh_dev_t device
, struct fssh_fs_info
*info
)
5105 return FSSH_B_BAD_VALUE
;
5107 return fs_read_info(device
, info
);
5112 _kern_write_fs_info(fssh_dev_t device
, const struct fssh_fs_info
*info
, int mask
)
5115 return FSSH_B_BAD_VALUE
;
5117 return fs_write_info(device
, info
, mask
);
5124 // Note: _kern_sync() is also called from _user_sync()
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
));
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
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);
5235 _kern_fcntl(int fd
, int op
, uint32_t argument
)
5237 return common_fcntl(fd
, op
, argument
, true);
5244 return common_sync(fd
, true);
5249 _kern_lock_node(int fd
)
5251 return common_lock_node(fd
, true);
5256 _kern_unlock_node(int fd
)
5258 return common_unlock_node(fd
, true);
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.
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);
5296 _kern_remove_dir(int fd
, const char *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
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
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
5330 _kern_read_link(int fd
, const char *path
, char *buffer
, fssh_size_t
*_bufferSize
)
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.
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
)
5374 return common_create_symlink(fd
, pathBuffer
.LockBuffer(),
5375 toBuffer
, mode
, true);
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.
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
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
5432 * \return \c FSSH_B_OK, if the entry has been moved successfully, another
5433 * error code otherwise.
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);
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
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.
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
;
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);
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
);
5515 status
= FSSH_EOPNOTSUPP
;
5520 if (status
== FSSH_B_OK
&& originalStat
!= NULL
)
5521 fssh_memcpy(originalStat
, stat
, statSize
);
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
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
5545 * \return \c FSSH_B_OK, if the the stat data have been written successfully,
5546 * another error code otherwise.
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
;
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);
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
);
5585 status
= FSSH_EOPNOTSUPP
;
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
;
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);
5623 _kern_remove_attr(int fd
, const char *name
)
5625 return attr_remove(fd
, name
, true);
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);
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);
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);
5658 _kern_remove_index(fssh_dev_t device
, const char *name
)
5660 return index_remove(device
, name
, true);
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);
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
;
5682 pathBuffer
.SetTo(path
);
5684 return set_cwd(fd
, path
!= NULL
? pathBuffer
.LockBuffer() : NULL
, true);
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.
5698 int fd
= fssh_open(partition
, FSSH_O_RDWR
);
5702 // get the file system module
5703 fssh_file_system_module_info
* fsModule
= get_file_system(fsName
);
5704 if (fsModule
== NULL
) {
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
5715 // TODO: Get the actual size!
5717 status
= FSSH_B_NOT_SUPPORTED
;
5719 // put the file system module, close partition
5720 put_file_system(fsModule
);
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"