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