2 * Copyright 2002-2017, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6 * Distributed under the terms of the NewOS License.
11 # include "fssh_api_wrapper.h"
13 # include "KOpenHashTable.h"
19 # include <sys/stat.h>
21 # include <fs_cache.h>
22 # include <KernelExport.h>
23 # include <NodeMonitor.h>
27 # include <OpenHashTable.h>
28 # include <util/AutoLock.h>
36 using namespace FSShell
;
37 # define user_strlcpy(to, from, len) (strlcpy(to, from, len), FSSH_B_OK)
41 //#define TRACE_ROOTFS
43 # define TRACE(x) dprintf x
51 struct rootfs_stream
{
54 struct rootfs_vnode
* dir_head
;
58 struct stream_symlink
{
65 struct rootfs_vnode
* all_next
;
68 timespec modification_time
;
69 timespec creation_time
;
72 struct rootfs_vnode
* parent
;
73 struct rootfs_vnode
* dir_next
;
74 struct rootfs_stream stream
;
78 typedef ino_t KeyType
;
79 typedef rootfs_vnode ValueType
;
81 size_t HashKey(KeyType key
) const
86 size_t Hash(ValueType
* vnode
) const
91 bool Compare(KeyType key
, ValueType
* vnode
) const
93 return vnode
->id
== key
;
96 ValueType
*& GetLink(ValueType
* value
) const
98 return value
->all_next
;
102 typedef BOpenHashTable
<VnodeHash
> VnodeTable
;
109 VnodeTable
* vnode_list_hash
;
110 struct rootfs_vnode
* root_vnode
;
113 // dircookie, dirs are only types of streams supported by rootfs
114 struct rootfs_dir_cookie
{
115 struct list_link link
;
117 struct rootfs_vnode
* current
;
118 int32 iteration_state
;
121 // directory iteration states
123 ITERATION_STATE_DOT
= 0,
124 ITERATION_STATE_DOT_DOT
= 1,
125 ITERATION_STATE_OTHERS
= 2,
126 ITERATION_STATE_BEGIN
= ITERATION_STATE_DOT
,
130 // extern only to make forward declaration possible
131 extern fs_volume_ops sVolumeOps
;
132 extern fs_vnode_ops sVnodeOps
;
137 #define ROOTFS_HASH_SIZE 16
143 bigtime_t time
= real_time_clock_usecs();
146 tv
.tv_sec
= time
/ 1000000;
147 tv
.tv_nsec
= (time
% 1000000) * 1000;
153 get_parent_id(struct rootfs_vnode
* vnode
)
155 if (vnode
->parent
!= NULL
)
156 return vnode
->parent
->id
;
161 static struct rootfs_vnode
*
162 rootfs_create_vnode(struct rootfs
* fs
, struct rootfs_vnode
* parent
,
163 const char* name
, int type
)
165 struct rootfs_vnode
* vnode
;
167 vnode
= (rootfs_vnode
*)malloc(sizeof(struct rootfs_vnode
));
171 memset(vnode
, 0, sizeof(struct rootfs_vnode
));
174 vnode
->name
= strdup(name
);
175 if (vnode
->name
== NULL
) {
181 vnode
->id
= fs
->next_vnode_id
++;
182 vnode
->stream
.type
= type
;
183 vnode
->creation_time
= vnode
->modification_time
= current_timespec();
184 vnode
->uid
= geteuid();
185 vnode
->gid
= parent
? parent
->gid
: getegid();
186 // inherit group from parent if possible
189 list_init(&vnode
->stream
.dir
.cookies
);
190 mutex_init(&vnode
->stream
.dir
.cookie_lock
, "rootfs dir cookies");
198 rootfs_delete_vnode(struct rootfs
* fs
, struct rootfs_vnode
* v
, bool force_delete
)
200 // cant delete it if it's in a directory or is a directory
202 if (!force_delete
&& (v
->stream
.dir
.dir_head
!= NULL
|| v
->dir_next
!= NULL
))
205 // remove it from the global hash table
206 fs
->vnode_list_hash
->Remove(v
);
208 if (S_ISDIR(v
->stream
.type
))
209 mutex_destroy(&v
->stream
.dir
.cookie_lock
);
218 /*! Makes sure none of the dircookies point to the vnode passed in. */
220 update_dir_cookies(struct rootfs_vnode
* dir
, struct rootfs_vnode
* vnode
)
222 struct rootfs_dir_cookie
* cookie
= NULL
;
224 while ((cookie
= (rootfs_dir_cookie
*)list_get_next_item(
225 &dir
->stream
.dir
.cookies
, cookie
)) != NULL
) {
226 MutexLocker
cookieLocker(cookie
->lock
);
227 if (cookie
->current
== vnode
)
228 cookie
->current
= vnode
->dir_next
;
233 static struct rootfs_vnode
*
234 rootfs_find_in_dir(struct rootfs_vnode
* dir
, const char* path
)
236 struct rootfs_vnode
* vnode
;
238 if (!strcmp(path
, "."))
240 if (!strcmp(path
, ".."))
243 for (vnode
= dir
->stream
.dir
.dir_head
; vnode
; vnode
= vnode
->dir_next
) {
244 if (!strcmp(vnode
->name
, path
))
252 rootfs_insert_in_dir(struct rootfs
* fs
, struct rootfs_vnode
* dir
,
253 struct rootfs_vnode
* vnode
)
255 // make sure the directory stays sorted alphabetically
257 struct rootfs_vnode
* node
= dir
->stream
.dir
.dir_head
;
258 struct rootfs_vnode
* last
= NULL
;
259 while (node
!= NULL
&& strcmp(node
->name
, vnode
->name
) < 0) {
261 node
= node
->dir_next
;
264 // the new vnode is the first entry in the list
265 vnode
->dir_next
= dir
->stream
.dir
.dir_head
;
266 dir
->stream
.dir
.dir_head
= vnode
;
268 // insert after that node
269 vnode
->dir_next
= last
->dir_next
;
270 last
->dir_next
= vnode
;
274 dir
->modification_time
= current_timespec();
276 notify_stat_changed(fs
->id
, get_parent_id(dir
), dir
->id
,
277 B_STAT_MODIFICATION_TIME
);
283 rootfs_remove_from_dir(struct rootfs
* fs
, struct rootfs_vnode
* dir
,
284 struct rootfs_vnode
* removeVnode
)
286 struct rootfs_vnode
* vnode
;
287 struct rootfs_vnode
* lastVnode
;
289 for (vnode
= dir
->stream
.dir
.dir_head
, lastVnode
= NULL
; vnode
!= NULL
;
290 lastVnode
= vnode
, vnode
= vnode
->dir_next
) {
291 if (vnode
== removeVnode
) {
292 // make sure all dircookies dont point to this vnode
293 update_dir_cookies(dir
, vnode
);
296 lastVnode
->dir_next
= vnode
->dir_next
;
298 dir
->stream
.dir
.dir_head
= vnode
->dir_next
;
299 vnode
->dir_next
= NULL
;
301 dir
->modification_time
= current_timespec();
302 notify_stat_changed(fs
->id
, get_parent_id(dir
), dir
->id
,
303 B_STAT_MODIFICATION_TIME
);
307 return B_ENTRY_NOT_FOUND
;
312 rootfs_is_dir_empty(struct rootfs_vnode
* dir
)
314 return !dir
->stream
.dir
.dir_head
;
318 /*! You must hold the FS write lock when calling this function */
320 remove_node(struct rootfs
* fs
, struct rootfs_vnode
* directory
,
321 struct rootfs_vnode
* vnode
)
323 // schedule this vnode to be removed when it's ref goes to zero
325 bool gotNode
= (get_vnode(fs
->volume
, vnode
->id
, NULL
) == B_OK
);
327 status_t status
= B_OK
;
329 status
= remove_vnode(fs
->volume
, vnode
->id
);
331 if (status
== B_OK
) {
332 rootfs_remove_from_dir(fs
, directory
, vnode
);
333 notify_entry_removed(fs
->id
, directory
->id
, vnode
->name
, vnode
->id
);
337 put_vnode(fs
->volume
, vnode
->id
);
344 rootfs_remove(struct rootfs
* fs
, struct rootfs_vnode
* dir
, const char* name
,
347 struct rootfs_vnode
* vnode
;
348 status_t status
= B_OK
;
350 WriteLocker
locker(fs
->lock
);
352 vnode
= rootfs_find_in_dir(dir
, name
);
354 status
= B_ENTRY_NOT_FOUND
;
355 else if (isDirectory
&& !S_ISDIR(vnode
->stream
.type
))
356 status
= B_NOT_A_DIRECTORY
;
357 else if (!isDirectory
&& S_ISDIR(vnode
->stream
.type
))
358 status
= B_IS_A_DIRECTORY
;
359 else if (isDirectory
&& !rootfs_is_dir_empty(vnode
))
360 status
= B_DIRECTORY_NOT_EMPTY
;
365 entry_cache_remove(fs
->volume
->id
, dir
->id
, name
);
367 return remove_node(fs
, dir
, vnode
);
375 rootfs_mount(fs_volume
* volume
, const char* device
, uint32 flags
,
376 const char* args
, ino_t
* _rootID
)
379 struct rootfs_vnode
* vnode
;
382 TRACE(("rootfs_mount: entry\n"));
384 fs
= (rootfs
*)malloc(sizeof(struct rootfs
));
388 volume
->private_volume
= fs
;
389 volume
->ops
= &sVolumeOps
;
392 fs
->next_vnode_id
= 1;
394 rw_lock_init(&fs
->lock
, "rootfs");
396 fs
->vnode_list_hash
= new(std::nothrow
) VnodeTable();
397 if (fs
->vnode_list_hash
== NULL
398 || fs
->vnode_list_hash
->Init(ROOTFS_HASH_SIZE
) != B_OK
) {
403 // create the root vnode
404 vnode
= rootfs_create_vnode(fs
, NULL
, ".", S_IFDIR
| 0777);
409 vnode
->parent
= vnode
;
411 fs
->root_vnode
= vnode
;
412 fs
->vnode_list_hash
->Insert(vnode
);
413 publish_vnode(volume
, vnode
->id
, vnode
, &sVnodeOps
, vnode
->stream
.type
, 0);
415 *_rootID
= vnode
->id
;
420 delete fs
->vnode_list_hash
;
422 rw_lock_destroy(&fs
->lock
);
430 rootfs_unmount(fs_volume
* _volume
)
432 struct rootfs
* fs
= (struct rootfs
*)_volume
->private_volume
;
434 TRACE(("rootfs_unmount: entry fs = %p\n", fs
));
436 // release the reference to the root
437 put_vnode(fs
->volume
, fs
->root_vnode
->id
);
439 // delete all of the vnodes
440 VnodeTable::Iterator
i(fs
->vnode_list_hash
);
442 while (i
.HasNext()) {
443 struct rootfs_vnode
* vnode
= i
.Next();
444 rootfs_delete_vnode(fs
, vnode
, true);
447 delete fs
->vnode_list_hash
;
448 rw_lock_destroy(&fs
->lock
);
456 rootfs_sync(fs_volume
* _volume
)
458 TRACE(("rootfs_sync: entry\n"));
465 rootfs_lookup(fs_volume
* _volume
, fs_vnode
* _dir
, const char* name
, ino_t
* _id
)
467 struct rootfs
* fs
= (struct rootfs
*)_volume
->private_volume
;
468 struct rootfs_vnode
* dir
= (struct rootfs_vnode
*)_dir
->private_node
;
469 struct rootfs_vnode
* vnode
;
471 TRACE(("rootfs_lookup: entry dir %p, name '%s'\n", dir
, name
));
472 if (!S_ISDIR(dir
->stream
.type
))
473 return B_NOT_A_DIRECTORY
;
475 ReadLocker
locker(fs
->lock
);
478 vnode
= rootfs_find_in_dir(dir
, name
);
480 return B_ENTRY_NOT_FOUND
;
482 status_t status
= get_vnode(fs
->volume
, vnode
->id
, NULL
);
486 entry_cache_add(fs
->volume
->id
, dir
->id
, name
, vnode
->id
);
494 rootfs_get_vnode_name(fs_volume
* _volume
, fs_vnode
* _vnode
, char* buffer
,
497 struct rootfs_vnode
* vnode
= (struct rootfs_vnode
*)_vnode
->private_node
;
499 TRACE(("rootfs_get_vnode_name: vnode = %p (name = %s)\n", vnode
,
502 strlcpy(buffer
, vnode
->name
, bufferSize
);
508 rootfs_get_vnode(fs_volume
* _volume
, ino_t id
, fs_vnode
* _vnode
, int* _type
,
509 uint32
* _flags
, bool reenter
)
511 struct rootfs
* fs
= (struct rootfs
*)_volume
->private_volume
;
512 struct rootfs_vnode
* vnode
;
514 TRACE(("rootfs_getvnode: asking for vnode %Ld, r %d\n", id
, reenter
));
517 rw_lock_read_lock(&fs
->lock
);
519 vnode
= fs
->vnode_list_hash
->Lookup(id
);
522 rw_lock_read_unlock(&fs
->lock
);
524 TRACE(("rootfs_getnvnode: looked it up at %p\n", vnode
));
527 return B_ENTRY_NOT_FOUND
;
529 _vnode
->private_node
= vnode
;
530 _vnode
->ops
= &sVnodeOps
;
531 *_type
= vnode
->stream
.type
;
539 rootfs_put_vnode(fs_volume
* _volume
, fs_vnode
* _vnode
, bool reenter
)
542 struct rootfs_vnode
* vnode
= (struct rootfs_vnode
*)_vnode
->private_node
;
544 TRACE(("rootfs_putvnode: entry on vnode 0x%Lx, r %d\n", vnode
->id
, reenter
));
546 return B_OK
; // whatever
551 rootfs_remove_vnode(fs_volume
* _volume
, fs_vnode
* _vnode
, bool reenter
)
553 struct rootfs
* fs
= (struct rootfs
*)_volume
->private_volume
;
554 struct rootfs_vnode
* vnode
= (struct rootfs_vnode
*)_vnode
->private_node
;
556 TRACE(("rootfs_remove_vnode: remove %p (0x%Lx), r %d\n", vnode
, vnode
->id
,
560 rw_lock_write_lock(&fs
->lock
);
562 if (vnode
->dir_next
) {
563 // can't remove node if it's linked to the dir
564 panic("rootfs_remove_vnode: vnode %p asked to be removed is present in "
568 rootfs_delete_vnode(fs
, vnode
, false);
571 rw_lock_write_unlock(&fs
->lock
);
578 rootfs_create(fs_volume
* _volume
, fs_vnode
* _dir
, const char* name
, int omode
,
579 int perms
, void** _cookie
, ino_t
* _newID
)
586 rootfs_open(fs_volume
* _volume
, fs_vnode
* _v
, int openMode
, void** _cookie
)
588 struct rootfs_vnode
* vnode
= (rootfs_vnode
*)_v
->private_node
;
590 if (S_ISDIR(vnode
->stream
.type
) && (openMode
& O_RWMASK
) != O_RDONLY
)
591 return B_IS_A_DIRECTORY
;
592 if ((openMode
& O_DIRECTORY
) != 0 && !S_ISDIR(vnode
->stream
.type
))
593 return B_NOT_A_DIRECTORY
;
595 // allow to open the file, but it can't be done anything with it
603 rootfs_close(fs_volume
* _volume
, fs_vnode
* _vnode
, void* _cookie
)
605 TRACE(("rootfs_close: entry vnode %p, cookie %p\n", _vnode
->private_node
,
612 rootfs_free_cookie(fs_volume
* _volume
, fs_vnode
* _v
, void* _cookie
)
619 rootfs_fsync(fs_volume
* _volume
, fs_vnode
* _v
)
626 rootfs_read(fs_volume
* _volume
, fs_vnode
* _vnode
, void* _cookie
,
627 off_t pos
, void* buffer
, size_t* _length
)
634 rootfs_write(fs_volume
* _volume
, fs_vnode
* vnode
, void* cookie
,
635 off_t pos
, const void* buffer
, size_t* _length
)
637 TRACE(("rootfs_write: vnode %p, cookie %p, pos 0x%Lx , len %#x\n",
638 vnode
, cookie
, pos
, (int)*_length
));
645 rootfs_create_dir(fs_volume
* _volume
, fs_vnode
* _dir
, const char* name
,
648 struct rootfs
* fs
= (rootfs
*)_volume
->private_volume
;
649 struct rootfs_vnode
* dir
= (rootfs_vnode
*)_dir
->private_node
;
650 struct rootfs_vnode
* vnode
;
652 TRACE(("rootfs_create_dir: dir %p, name = '%s', perms = %d\n", dir
, name
,
655 WriteLocker
locker(fs
->lock
);
657 vnode
= rootfs_find_in_dir(dir
, name
);
659 return B_FILE_EXISTS
;
661 TRACE(("rootfs_create: creating new vnode\n"));
662 vnode
= rootfs_create_vnode(fs
, dir
, name
, S_IFDIR
| (mode
& S_IUMSK
));
666 rootfs_insert_in_dir(fs
, dir
, vnode
);
667 fs
->vnode_list_hash
->Insert(vnode
);
669 entry_cache_add(fs
->volume
->id
, dir
->id
, name
, vnode
->id
);
670 notify_entry_created(fs
->id
, dir
->id
, name
, vnode
->id
);
677 rootfs_remove_dir(fs_volume
* _volume
, fs_vnode
* _dir
, const char* name
)
679 struct rootfs
* fs
= (rootfs
*)_volume
->private_volume
;
680 struct rootfs_vnode
* dir
= (rootfs_vnode
*)_dir
->private_node
;
682 TRACE(("rootfs_remove_dir: dir %p (0x%Lx), name '%s'\n", dir
, dir
->id
,
685 return rootfs_remove(fs
, dir
, name
, true);
690 rootfs_open_dir(fs_volume
* _volume
, fs_vnode
* _v
, void** _cookie
)
692 struct rootfs
* fs
= (struct rootfs
*)_volume
->private_volume
;
693 struct rootfs_vnode
* vnode
= (struct rootfs_vnode
*)_v
->private_node
;
694 struct rootfs_dir_cookie
* cookie
;
696 TRACE(("rootfs_open: vnode %p\n", vnode
));
698 if (!S_ISDIR(vnode
->stream
.type
))
701 cookie
= (rootfs_dir_cookie
*)malloc(sizeof(struct rootfs_dir_cookie
));
705 mutex_init(&cookie
->lock
, "rootfs dir cookie");
707 ReadLocker
locker(fs
->lock
);
709 cookie
->current
= vnode
->stream
.dir
.dir_head
;
710 cookie
->iteration_state
= ITERATION_STATE_BEGIN
;
712 mutex_lock(&vnode
->stream
.dir
.cookie_lock
);
713 list_add_item(&vnode
->stream
.dir
.cookies
, cookie
);
714 mutex_unlock(&vnode
->stream
.dir
.cookie_lock
);
723 rootfs_free_dir_cookie(fs_volume
* _volume
, fs_vnode
* _vnode
, void* _cookie
)
725 struct rootfs_dir_cookie
* cookie
= (rootfs_dir_cookie
*)_cookie
;
726 struct rootfs_vnode
* vnode
= (rootfs_vnode
*)_vnode
->private_node
;
727 struct rootfs
* fs
= (rootfs
*)_volume
->private_volume
;
729 ReadLocker
locker(fs
->lock
);
731 mutex_lock(&vnode
->stream
.dir
.cookie_lock
);
732 list_remove_item(&vnode
->stream
.dir
.cookies
, cookie
);
733 mutex_unlock(&vnode
->stream
.dir
.cookie_lock
);
737 mutex_destroy(&cookie
->lock
);
745 rootfs_read_dir(fs_volume
* _volume
, fs_vnode
* _vnode
, void* _cookie
,
746 struct dirent
* dirent
, size_t bufferSize
, uint32
* _num
)
748 struct rootfs_vnode
* vnode
= (struct rootfs_vnode
*)_vnode
->private_node
;
749 struct rootfs_dir_cookie
* cookie
= (rootfs_dir_cookie
*)_cookie
;
750 struct rootfs
* fs
= (rootfs
*)_volume
->private_volume
;
751 struct rootfs_vnode
* childNode
= NULL
;
752 const char* name
= NULL
;
753 struct rootfs_vnode
* nextChildNode
= NULL
;
755 TRACE(("rootfs_read_dir: vnode %p, cookie %p, buffer = %p, bufferSize = %d, "
756 "num = %p\n", _vnode
, cookie
, dirent
, (int)bufferSize
, _num
));
758 ReadLocker
locker(fs
->lock
);
760 MutexLocker
cookieLocker(cookie
->lock
);
761 int nextState
= cookie
->iteration_state
;
763 switch (cookie
->iteration_state
) {
764 case ITERATION_STATE_DOT
:
767 nextChildNode
= vnode
->stream
.dir
.dir_head
;
768 nextState
= cookie
->iteration_state
+ 1;
770 case ITERATION_STATE_DOT_DOT
:
771 childNode
= vnode
->parent
;
773 nextChildNode
= vnode
->stream
.dir
.dir_head
;
774 nextState
= cookie
->iteration_state
+ 1;
777 childNode
= cookie
->current
;
779 name
= childNode
->name
;
780 nextChildNode
= childNode
->dir_next
;
786 // we're at the end of the directory
791 dirent
->d_dev
= fs
->id
;
792 dirent
->d_ino
= childNode
->id
;
793 dirent
->d_reclen
= strlen(name
) + sizeof(struct dirent
);
795 if (dirent
->d_reclen
> bufferSize
)
798 int nameLength
= user_strlcpy(dirent
->d_name
, name
,
799 bufferSize
- sizeof(struct dirent
));
800 if (nameLength
< B_OK
)
803 cookie
->current
= nextChildNode
;
804 cookie
->iteration_state
= nextState
;
811 rootfs_rewind_dir(fs_volume
* _volume
, fs_vnode
* _vnode
, void* _cookie
)
813 struct rootfs_dir_cookie
* cookie
= (rootfs_dir_cookie
*)_cookie
;
814 struct rootfs_vnode
* vnode
= (rootfs_vnode
*)_vnode
->private_node
;
815 struct rootfs
* fs
= (rootfs
*)_volume
->private_volume
;
817 ReadLocker
locker(fs
->lock
);
818 MutexLocker
cookieLocker(cookie
->lock
);
820 cookie
->current
= vnode
->stream
.dir
.dir_head
;
821 cookie
->iteration_state
= ITERATION_STATE_BEGIN
;
828 rootfs_ioctl(fs_volume
* _volume
, fs_vnode
* _v
, void* _cookie
, uint32 op
,
829 void* buffer
, size_t length
)
831 TRACE(("rootfs_ioctl: vnode %p, cookie %p, op %d, buf %p, length %d\n",
832 _volume
, _cookie
, (int)op
, buffer
, (int)length
));
839 rootfs_can_page(fs_volume
* _volume
, fs_vnode
* _v
, void* cookie
)
846 rootfs_read_pages(fs_volume
* _volume
, fs_vnode
* _v
, void* cookie
, off_t pos
,
847 const iovec
* vecs
, size_t count
, size_t* _numBytes
)
849 return B_NOT_ALLOWED
;
854 rootfs_write_pages(fs_volume
* _volume
, fs_vnode
* _v
, void* cookie
, off_t pos
,
855 const iovec
* vecs
, size_t count
, size_t* _numBytes
)
857 return B_NOT_ALLOWED
;
862 rootfs_read_link(fs_volume
* _volume
, fs_vnode
* _link
, char* buffer
,
865 struct rootfs_vnode
* link
= (rootfs_vnode
*)_link
->private_node
;
867 if (!S_ISLNK(link
->stream
.type
))
870 if (link
->stream
.symlink
.length
< *_bufferSize
)
871 *_bufferSize
= link
->stream
.symlink
.length
;
873 memcpy(buffer
, link
->stream
.symlink
.path
, *_bufferSize
);
879 rootfs_symlink(fs_volume
* _volume
, fs_vnode
* _dir
, const char* name
,
880 const char* path
, int mode
)
882 struct rootfs
* fs
= (rootfs
*)_volume
->private_volume
;
883 struct rootfs_vnode
* dir
= (rootfs_vnode
*)_dir
->private_node
;
884 struct rootfs_vnode
* vnode
;
886 TRACE(("rootfs_symlink: dir %p, name = '%s', path = %s\n", dir
, name
, path
));
888 WriteLocker
locker(fs
->lock
);
890 vnode
= rootfs_find_in_dir(dir
, name
);
892 return B_FILE_EXISTS
;
894 TRACE(("rootfs_create: creating new symlink\n"));
895 vnode
= rootfs_create_vnode(fs
, dir
, name
, S_IFLNK
| (mode
& S_IUMSK
));
899 rootfs_insert_in_dir(fs
, dir
, vnode
);
900 fs
->vnode_list_hash
->Insert(vnode
);
902 vnode
->stream
.symlink
.path
= strdup(path
);
903 if (vnode
->stream
.symlink
.path
== NULL
) {
904 rootfs_delete_vnode(fs
, vnode
, false);
907 vnode
->stream
.symlink
.length
= strlen(path
);
909 entry_cache_add(fs
->volume
->id
, dir
->id
, name
, vnode
->id
);
911 notify_entry_created(fs
->id
, dir
->id
, name
, vnode
->id
);
918 rootfs_unlink(fs_volume
* _volume
, fs_vnode
* _dir
, const char* name
)
920 struct rootfs
* fs
= (rootfs
*)_volume
->private_volume
;
921 struct rootfs_vnode
* dir
= (rootfs_vnode
*)_dir
->private_node
;
923 TRACE(("rootfs_unlink: dir %p (0x%Lx), name '%s'\n", dir
, dir
->id
, name
));
925 return rootfs_remove(fs
, dir
, name
, false);
930 rootfs_rename(fs_volume
* _volume
, fs_vnode
* _fromDir
, const char* fromName
,
931 fs_vnode
* _toDir
, const char* toName
)
933 struct rootfs
* fs
= (rootfs
*)_volume
->private_volume
;
934 struct rootfs_vnode
* fromDirectory
= (rootfs_vnode
*)_fromDir
->private_node
;
935 struct rootfs_vnode
* toDirectory
= (rootfs_vnode
*)_toDir
->private_node
;
937 TRACE(("rootfs_rename: from %p (0x%Lx, %s), fromName '%s', to %p "
938 "(0x%Lx, %s), toName '%s'\n", fromDirectory
, fromDirectory
->id
,
939 fromDirectory
->name
!= NULL
? fromDirectory
->name
: "NULL",
940 fromName
, toDirectory
, toDirectory
->id
,
941 toDirectory
->name
!= NULL
? toDirectory
->name
: "NULL",
944 // Prevent renaming /boot, since that will stop everything from working.
945 // TODO: This should be solved differently. Either root should still be
946 // able to do this or a mechanism should be introduced that does this
947 // at the VFS level, for example by checking nodes for a specific
949 if (fromDirectory
->id
== 1 && strcmp(fromName
, "boot") == 0)
952 WriteLocker
locker(fs
->lock
);
954 struct rootfs_vnode
* vnode
= rootfs_find_in_dir(fromDirectory
, fromName
);
956 return B_ENTRY_NOT_FOUND
;
958 // make sure the target is not a subdirectory of us
959 struct rootfs_vnode
* parent
= toDirectory
->parent
;
960 while (parent
!= NULL
&& parent
!= parent
->parent
) {
964 parent
= parent
->parent
;
967 struct rootfs_vnode
* targetVnode
= rootfs_find_in_dir(toDirectory
, toName
);
968 if (targetVnode
!= NULL
) {
969 // target node exists, let's see if it is an empty directory
970 if (S_ISDIR(targetVnode
->stream
.type
)
971 && !rootfs_is_dir_empty(targetVnode
))
972 return B_NAME_IN_USE
;
974 // so we can cleanly remove it
975 entry_cache_remove(fs
->volume
->id
, toDirectory
->id
, toName
);
976 remove_node(fs
, toDirectory
, targetVnode
);
979 // we try to reuse the existing name buffer if possible
980 if (strlen(fromName
) < strlen(toName
)) {
981 char* nameBuffer
= strdup(toName
);
982 if (nameBuffer
== NULL
)
986 vnode
->name
= nameBuffer
;
988 // we can just copy it
989 strcpy(vnode
->name
, toName
);
992 // remove it from the dir
993 entry_cache_remove(fs
->volume
->id
, fromDirectory
->id
, fromName
);
994 rootfs_remove_from_dir(fs
, fromDirectory
, vnode
);
996 // Add it back to the dir with the new name.
997 // We need to do this even in the same directory,
998 // so that it keeps sorted correctly.
999 rootfs_insert_in_dir(fs
, toDirectory
, vnode
);
1001 entry_cache_add(fs
->volume
->id
, toDirectory
->id
, toName
, vnode
->id
);
1003 notify_entry_moved(fs
->id
, fromDirectory
->id
, fromName
, toDirectory
->id
,
1011 rootfs_read_stat(fs_volume
* _volume
, fs_vnode
* _v
, struct stat
* stat
)
1013 struct rootfs
* fs
= (rootfs
*)_volume
->private_volume
;
1014 struct rootfs_vnode
* vnode
= (rootfs_vnode
*)_v
->private_node
;
1016 TRACE(("rootfs_read_stat: vnode %p (0x%Lx), stat %p\n", vnode
, vnode
->id
,
1019 // stream exists, but we know to return size 0, since we can only hold
1021 stat
->st_dev
= fs
->id
;
1022 stat
->st_ino
= vnode
->id
;
1023 if (S_ISLNK(vnode
->stream
.type
))
1024 stat
->st_size
= vnode
->stream
.symlink
.length
;
1027 stat
->st_mode
= vnode
->stream
.type
;
1030 stat
->st_blksize
= 65536;
1031 stat
->st_blocks
= 0;
1033 stat
->st_uid
= vnode
->uid
;
1034 stat
->st_gid
= vnode
->gid
;
1036 stat
->st_atim
.tv_sec
= real_time_clock();
1037 stat
->st_atim
.tv_nsec
= 0;
1038 stat
->st_mtim
= stat
->st_ctim
= vnode
->modification_time
;
1039 stat
->st_crtim
= vnode
->creation_time
;
1046 rootfs_write_stat(fs_volume
* _volume
, fs_vnode
* _vnode
, const struct stat
* stat
,
1049 struct rootfs
* fs
= (rootfs
*)_volume
->private_volume
;
1050 struct rootfs_vnode
* vnode
= (rootfs_vnode
*)_vnode
->private_node
;
1052 TRACE(("rootfs_write_stat: vnode %p (0x%Lx), stat %p\n", vnode
, vnode
->id
,
1055 // we cannot change the size of anything
1056 if (statMask
& B_STAT_SIZE
)
1059 WriteLocker
locker(fs
->lock
);
1061 if ((statMask
& B_STAT_MODE
) != 0) {
1062 vnode
->stream
.type
= (vnode
->stream
.type
& ~S_IUMSK
)
1063 | (stat
->st_mode
& S_IUMSK
);
1066 if ((statMask
& B_STAT_UID
) != 0)
1067 vnode
->uid
= stat
->st_uid
;
1068 if ((statMask
& B_STAT_GID
) != 0)
1069 vnode
->gid
= stat
->st_gid
;
1071 if ((statMask
& B_STAT_MODIFICATION_TIME
) != 0)
1072 vnode
->modification_time
= stat
->st_mtim
;
1073 if ((statMask
& B_STAT_CREATION_TIME
) != 0)
1074 vnode
->creation_time
= stat
->st_crtim
;
1078 notify_stat_changed(fs
->id
, get_parent_id(vnode
), vnode
->id
, statMask
);
1084 rootfs_create_special_node(fs_volume
* _volume
, fs_vnode
* _dir
, const char* name
,
1085 fs_vnode
* subVnode
, mode_t mode
, uint32 flags
, fs_vnode
* _superVnode
,
1088 struct rootfs
* fs
= (rootfs
*)_volume
->private_volume
;
1089 struct rootfs_vnode
* dir
= (rootfs_vnode
*)_dir
->private_node
;
1090 struct rootfs_vnode
* vnode
;
1092 WriteLocker
locker(fs
->lock
);
1095 vnode
= rootfs_find_in_dir(dir
, name
);
1097 return B_FILE_EXISTS
;
1100 vnode
= rootfs_create_vnode(fs
, dir
, name
, mode
);
1105 rootfs_insert_in_dir(fs
, dir
, vnode
);
1107 flags
|= B_VNODE_PUBLISH_REMOVED
;
1109 fs
->vnode_list_hash
->Insert(vnode
);
1111 _superVnode
->private_node
= vnode
;
1112 _superVnode
->ops
= &sVnodeOps
;
1113 *_nodeID
= vnode
->id
;
1115 if (subVnode
== NULL
)
1116 subVnode
= _superVnode
;
1118 status_t status
= publish_vnode(fs
->volume
, vnode
->id
,
1119 subVnode
->private_node
, subVnode
->ops
, mode
, flags
);
1120 if (status
!= B_OK
) {
1122 rootfs_remove_from_dir(fs
, dir
, vnode
);
1123 rootfs_delete_vnode(fs
, vnode
, false);
1128 entry_cache_add(fs
->volume
->id
, dir
->id
, name
, vnode
->id
);
1129 notify_entry_created(fs
->id
, dir
->id
, name
, vnode
->id
);
1137 rootfs_std_ops(int32 op
, ...)
1143 case B_MODULE_UNINIT
:
1154 fs_volume_ops sVolumeOps
= {
1161 // the other operations are not supported (indices, queries)
1165 fs_vnode_ops sVnodeOps
= {
1167 &rootfs_get_vnode_name
,
1170 &rootfs_remove_vnode
,
1174 &rootfs_write_pages
,
1177 NULL
, // cancel_io()
1179 NULL
, // get_file_map()
1183 NULL
, // fs_set_flags()
1194 NULL
, // fs_access()
1203 &rootfs_free_cookie
,
1211 &rootfs_close
, // same as for files - it does nothing, anyway
1212 &rootfs_free_dir_cookie
,
1216 /* attribute directory operations */
1217 NULL
, // open_attr_dir
1218 NULL
, // close_attr_dir
1219 NULL
, // free_attr_dir_cookie
1220 NULL
, // read_attr_dir
1221 NULL
, // rewind_attr_dir
1223 /* attribute operations */
1224 NULL
, // create_attr
1227 NULL
, // free_attr_cookie
1231 NULL
, // read_attr_stat
1232 NULL
, // write_attr_stat
1233 NULL
, // rename_attr
1234 NULL
, // remove_attr
1236 /* support for node and FS layers */
1237 &rootfs_create_special_node
,
1238 NULL
, // get_super_vnode,
1243 file_system_module_info gRootFileSystem
= {
1245 "file_systems/rootfs" B_CURRENT_FS_API_VERSION
,
1250 "rootfs", // short_name
1251 "Root File System", // pretty_name
1254 NULL
, // identify_partition()
1255 NULL
, // scan_partition()
1256 NULL
, // free_identify_partition_cookie()
1257 NULL
, // free_partition_content_cookie()