vfs: check userland buffers before reading them.
[haiku.git] / src / system / kernel / fs / rootfs.cpp
blob61678a6b2f805575d5212a6ed98e4a73586b79e9
1 /*
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.
7 */
10 #if FS_SHELL
11 # include "fssh_api_wrapper.h"
13 # include "KOpenHashTable.h"
14 # include "list.h"
15 #else
16 # include <stdio.h>
17 # include <stdlib.h>
18 # include <string.h>
19 # include <sys/stat.h>
21 # include <fs_cache.h>
22 # include <KernelExport.h>
23 # include <NodeMonitor.h>
25 # include <debug.h>
26 # include <lock.h>
27 # include <OpenHashTable.h>
28 # include <util/AutoLock.h>
29 # include <vfs.h>
30 # include <vm/vm.h>
31 #endif
35 #if FS_SHELL
36 using namespace FSShell;
37 # define user_strlcpy(to, from, len) (strlcpy(to, from, len), FSSH_B_OK)
38 #endif
41 //#define TRACE_ROOTFS
42 #ifdef TRACE_ROOTFS
43 # define TRACE(x) dprintf x
44 #else
45 # define TRACE(x)
46 #endif
49 namespace {
51 struct rootfs_stream {
52 mode_t type;
53 struct stream_dir {
54 struct rootfs_vnode* dir_head;
55 struct list cookies;
56 mutex cookie_lock;
57 } dir;
58 struct stream_symlink {
59 char* path;
60 size_t length;
61 } symlink;
64 struct rootfs_vnode {
65 struct rootfs_vnode* all_next;
66 ino_t id;
67 char* name;
68 timespec modification_time;
69 timespec creation_time;
70 uid_t uid;
71 gid_t gid;
72 struct rootfs_vnode* parent;
73 struct rootfs_vnode* dir_next;
74 struct rootfs_stream stream;
77 struct VnodeHash {
78 typedef ino_t KeyType;
79 typedef rootfs_vnode ValueType;
81 size_t HashKey(KeyType key) const
83 return key;
86 size_t Hash(ValueType* vnode) const
88 return vnode->id;
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;
104 struct rootfs {
105 fs_volume* volume;
106 dev_t id;
107 rw_lock lock;
108 ino_t next_vnode_id;
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;
116 mutex lock;
117 struct rootfs_vnode* current;
118 int32 iteration_state;
121 // directory iteration states
122 enum {
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;
134 } // namespace
137 #define ROOTFS_HASH_SIZE 16
140 static timespec
141 current_timespec()
143 bigtime_t time = real_time_clock_usecs();
145 timespec tv;
146 tv.tv_sec = time / 1000000;
147 tv.tv_nsec = (time % 1000000) * 1000;
148 return tv;
152 static ino_t
153 get_parent_id(struct rootfs_vnode* vnode)
155 if (vnode->parent != NULL)
156 return vnode->parent->id;
157 return -1;
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));
168 if (vnode == NULL)
169 return NULL;
171 memset(vnode, 0, sizeof(struct rootfs_vnode));
173 if (name != NULL) {
174 vnode->name = strdup(name);
175 if (vnode->name == NULL) {
176 free(vnode);
177 return 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
188 if (S_ISDIR(type)) {
189 list_init(&vnode->stream.dir.cookies);
190 mutex_init(&vnode->stream.dir.cookie_lock, "rootfs dir cookies");
193 return vnode;
197 static status_t
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
201 // and has children
202 if (!force_delete && (v->stream.dir.dir_head != NULL || v->dir_next != NULL))
203 return EPERM;
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);
211 free(v->name);
212 free(v);
214 return 0;
218 /*! Makes sure none of the dircookies point to the vnode passed in. */
219 static void
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, "."))
239 return dir;
240 if (!strcmp(path, ".."))
241 return dir->parent;
243 for (vnode = dir->stream.dir.dir_head; vnode; vnode = vnode->dir_next) {
244 if (!strcmp(vnode->name, path))
245 return vnode;
247 return NULL;
251 static status_t
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) {
260 last = node;
261 node = node->dir_next;
263 if (last == NULL) {
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;
267 } else {
268 // insert after that node
269 vnode->dir_next = last->dir_next;
270 last->dir_next = vnode;
273 vnode->parent = dir;
274 dir->modification_time = current_timespec();
276 notify_stat_changed(fs->id, get_parent_id(dir), dir->id,
277 B_STAT_MODIFICATION_TIME);
278 return B_OK;
282 static status_t
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);
295 if (lastVnode)
296 lastVnode->dir_next = vnode->dir_next;
297 else
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);
304 return B_OK;
307 return B_ENTRY_NOT_FOUND;
311 static bool
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 */
319 static status_t
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;
328 if (gotNode)
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);
336 if (gotNode)
337 put_vnode(fs->volume, vnode->id);
339 return status;
343 static status_t
344 rootfs_remove(struct rootfs* fs, struct rootfs_vnode* dir, const char* name,
345 bool isDirectory)
347 struct rootfs_vnode* vnode;
348 status_t status = B_OK;
350 WriteLocker locker(fs->lock);
352 vnode = rootfs_find_in_dir(dir, name);
353 if (!vnode)
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;
362 if (status != B_OK)
363 return status;
365 entry_cache_remove(fs->volume->id, dir->id, name);
367 return remove_node(fs, dir, vnode);
371 // #pragma mark -
374 static status_t
375 rootfs_mount(fs_volume* volume, const char* device, uint32 flags,
376 const char* args, ino_t* _rootID)
378 struct rootfs* fs;
379 struct rootfs_vnode* vnode;
380 status_t err;
382 TRACE(("rootfs_mount: entry\n"));
384 fs = (rootfs*)malloc(sizeof(struct rootfs));
385 if (fs == NULL)
386 return B_NO_MEMORY;
388 volume->private_volume = fs;
389 volume->ops = &sVolumeOps;
390 fs->volume = volume;
391 fs->id = volume->id;
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) {
399 err = B_NO_MEMORY;
400 goto err2;
403 // create the root vnode
404 vnode = rootfs_create_vnode(fs, NULL, ".", S_IFDIR | 0777);
405 if (vnode == NULL) {
406 err = B_NO_MEMORY;
407 goto err3;
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;
417 return B_OK;
419 err3:
420 delete fs->vnode_list_hash;
421 err2:
422 rw_lock_destroy(&fs->lock);
423 free(fs);
425 return err;
429 static status_t
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);
449 free(fs);
451 return B_OK;
455 static status_t
456 rootfs_sync(fs_volume* _volume)
458 TRACE(("rootfs_sync: entry\n"));
460 return B_OK;
464 static status_t
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);
477 // look it up
478 vnode = rootfs_find_in_dir(dir, name);
479 if (!vnode)
480 return B_ENTRY_NOT_FOUND;
482 status_t status = get_vnode(fs->volume, vnode->id, NULL);
483 if (status != B_OK)
484 return status;
486 entry_cache_add(fs->volume->id, dir->id, name, vnode->id);
488 *_id = vnode->id;
489 return B_OK;
493 static status_t
494 rootfs_get_vnode_name(fs_volume* _volume, fs_vnode* _vnode, char* buffer,
495 size_t bufferSize)
497 struct rootfs_vnode* vnode = (struct rootfs_vnode*)_vnode->private_node;
499 TRACE(("rootfs_get_vnode_name: vnode = %p (name = %s)\n", vnode,
500 vnode->name));
502 strlcpy(buffer, vnode->name, bufferSize);
503 return B_OK;
507 static status_t
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));
516 if (!reenter)
517 rw_lock_read_lock(&fs->lock);
519 vnode = fs->vnode_list_hash->Lookup(id);
521 if (!reenter)
522 rw_lock_read_unlock(&fs->lock);
524 TRACE(("rootfs_getnvnode: looked it up at %p\n", vnode));
526 if (vnode == NULL)
527 return B_ENTRY_NOT_FOUND;
529 _vnode->private_node = vnode;
530 _vnode->ops = &sVnodeOps;
531 *_type = vnode->stream.type;
532 *_flags = 0;
534 return B_OK;
538 static status_t
539 rootfs_put_vnode(fs_volume* _volume, fs_vnode* _vnode, bool reenter)
541 #ifdef TRACE_ROOTFS
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));
545 #endif
546 return B_OK; // whatever
550 static status_t
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,
557 reenter));
559 if (!reenter)
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 "
565 "dir\n", vnode);
568 rootfs_delete_vnode(fs, vnode, false);
570 if (!reenter)
571 rw_lock_write_unlock(&fs->lock);
573 return B_OK;
577 static status_t
578 rootfs_create(fs_volume* _volume, fs_vnode* _dir, const char* name, int omode,
579 int perms, void** _cookie, ino_t* _newID)
581 return B_BAD_VALUE;
585 static status_t
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
597 *_cookie = NULL;
598 return B_OK;
602 static status_t
603 rootfs_close(fs_volume* _volume, fs_vnode* _vnode, void* _cookie)
605 TRACE(("rootfs_close: entry vnode %p, cookie %p\n", _vnode->private_node,
606 _cookie));
607 return B_OK;
611 static status_t
612 rootfs_free_cookie(fs_volume* _volume, fs_vnode* _v, void* _cookie)
614 return B_OK;
618 static status_t
619 rootfs_fsync(fs_volume* _volume, fs_vnode* _v)
621 return B_OK;
625 static status_t
626 rootfs_read(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
627 off_t pos, void* buffer, size_t* _length)
629 return EINVAL;
633 static status_t
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));
640 return EPERM;
644 static status_t
645 rootfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char* name,
646 int mode)
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,
653 mode));
655 WriteLocker locker(fs->lock);
657 vnode = rootfs_find_in_dir(dir, name);
658 if (vnode != NULL)
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));
663 if (vnode == NULL)
664 return B_NO_MEMORY;
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);
672 return B_OK;
676 static status_t
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,
683 name));
685 return rootfs_remove(fs, dir, name, true);
689 static status_t
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))
699 return B_BAD_VALUE;
701 cookie = (rootfs_dir_cookie*)malloc(sizeof(struct rootfs_dir_cookie));
702 if (cookie == NULL)
703 return B_NO_MEMORY;
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);
716 *_cookie = cookie;
718 return B_OK;
722 static status_t
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);
735 locker.Unlock();
737 mutex_destroy(&cookie->lock);
739 free(cookie);
740 return B_OK;
744 static status_t
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:
765 childNode = vnode;
766 name = ".";
767 nextChildNode = vnode->stream.dir.dir_head;
768 nextState = cookie->iteration_state + 1;
769 break;
770 case ITERATION_STATE_DOT_DOT:
771 childNode = vnode->parent;
772 name = "..";
773 nextChildNode = vnode->stream.dir.dir_head;
774 nextState = cookie->iteration_state + 1;
775 break;
776 default:
777 childNode = cookie->current;
778 if (childNode) {
779 name = childNode->name;
780 nextChildNode = childNode->dir_next;
782 break;
785 if (!childNode) {
786 // we're at the end of the directory
787 *_num = 0;
788 return B_OK;
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)
796 return ENOBUFS;
798 int nameLength = user_strlcpy(dirent->d_name, name,
799 bufferSize - sizeof(struct dirent));
800 if (nameLength < B_OK)
801 return nameLength;
803 cookie->current = nextChildNode;
804 cookie->iteration_state = nextState;
805 *_num = 1;
806 return B_OK;
810 static status_t
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;
823 return B_OK;
827 static status_t
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));
834 return B_BAD_VALUE;
838 static bool
839 rootfs_can_page(fs_volume* _volume, fs_vnode* _v, void* cookie)
841 return false;
845 static status_t
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;
853 static status_t
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;
861 static status_t
862 rootfs_read_link(fs_volume* _volume, fs_vnode* _link, char* buffer,
863 size_t* _bufferSize)
865 struct rootfs_vnode* link = (rootfs_vnode*)_link->private_node;
867 if (!S_ISLNK(link->stream.type))
868 return B_BAD_VALUE;
870 if (link->stream.symlink.length < *_bufferSize)
871 *_bufferSize = link->stream.symlink.length;
873 memcpy(buffer, link->stream.symlink.path, *_bufferSize);
874 return B_OK;
878 static status_t
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);
891 if (vnode != NULL)
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));
896 if (vnode == NULL)
897 return B_NO_MEMORY;
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);
905 return B_NO_MEMORY;
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);
913 return B_OK;
917 static status_t
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);
929 static status_t
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",
942 toName));
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
948 // attribute.
949 if (fromDirectory->id == 1 && strcmp(fromName, "boot") == 0)
950 return EPERM;
952 WriteLocker locker(fs->lock);
954 struct rootfs_vnode* vnode = rootfs_find_in_dir(fromDirectory, fromName);
955 if (vnode == NULL)
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) {
961 if (parent == vnode)
962 return B_BAD_VALUE;
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)
983 return B_NO_MEMORY;
985 free(vnode->name);
986 vnode->name = nameBuffer;
987 } else {
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,
1004 toName, vnode->id);
1006 return B_OK;
1010 static status_t
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,
1017 stat));
1019 // stream exists, but we know to return size 0, since we can only hold
1020 // directories
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;
1025 else
1026 stat->st_size = 0;
1027 stat->st_mode = vnode->stream.type;
1029 stat->st_nlink = 1;
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;
1041 return B_OK;
1045 static status_t
1046 rootfs_write_stat(fs_volume* _volume, fs_vnode* _vnode, const struct stat* stat,
1047 uint32 statMask)
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,
1053 stat));
1055 // we cannot change the size of anything
1056 if (statMask & B_STAT_SIZE)
1057 return B_BAD_VALUE;
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;
1076 locker.Unlock();
1078 notify_stat_changed(fs->id, get_parent_id(vnode), vnode->id, statMask);
1079 return B_OK;
1083 static status_t
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,
1086 ino_t* _nodeID)
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);
1094 if (name != NULL) {
1095 vnode = rootfs_find_in_dir(dir, name);
1096 if (vnode != NULL)
1097 return B_FILE_EXISTS;
1100 vnode = rootfs_create_vnode(fs, dir, name, mode);
1101 if (vnode == NULL)
1102 return B_NO_MEMORY;
1104 if (name != NULL)
1105 rootfs_insert_in_dir(fs, dir, vnode);
1106 else
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) {
1121 if (name != NULL)
1122 rootfs_remove_from_dir(fs, dir, vnode);
1123 rootfs_delete_vnode(fs, vnode, false);
1124 return status;
1127 if (name != NULL) {
1128 entry_cache_add(fs->volume->id, dir->id, name, vnode->id);
1129 notify_entry_created(fs->id, dir->id, name, vnode->id);
1132 return B_OK;
1136 static status_t
1137 rootfs_std_ops(int32 op, ...)
1139 switch (op) {
1140 case B_MODULE_INIT:
1141 return B_OK;
1143 case B_MODULE_UNINIT:
1144 return B_OK;
1146 default:
1147 return B_ERROR;
1152 namespace {
1154 fs_volume_ops sVolumeOps = {
1155 &rootfs_unmount,
1156 NULL,
1157 NULL,
1158 &rootfs_sync,
1159 &rootfs_get_vnode,
1161 // the other operations are not supported (indices, queries)
1162 NULL,
1165 fs_vnode_ops sVnodeOps = {
1166 &rootfs_lookup,
1167 &rootfs_get_vnode_name,
1169 &rootfs_put_vnode,
1170 &rootfs_remove_vnode,
1172 &rootfs_can_page,
1173 &rootfs_read_pages,
1174 &rootfs_write_pages,
1176 NULL, // io()
1177 NULL, // cancel_io()
1179 NULL, // get_file_map()
1181 /* common */
1182 &rootfs_ioctl,
1183 NULL, // fs_set_flags()
1184 NULL, // select
1185 NULL, // deselect
1186 &rootfs_fsync,
1188 &rootfs_read_link,
1189 &rootfs_symlink,
1190 NULL, // fs_link()
1191 &rootfs_unlink,
1192 &rootfs_rename,
1194 NULL, // fs_access()
1195 &rootfs_read_stat,
1196 &rootfs_write_stat,
1197 NULL,
1199 /* file */
1200 &rootfs_create,
1201 &rootfs_open,
1202 &rootfs_close,
1203 &rootfs_free_cookie,
1204 &rootfs_read,
1205 &rootfs_write,
1207 /* directory */
1208 &rootfs_create_dir,
1209 &rootfs_remove_dir,
1210 &rootfs_open_dir,
1211 &rootfs_close, // same as for files - it does nothing, anyway
1212 &rootfs_free_dir_cookie,
1213 &rootfs_read_dir,
1214 &rootfs_rewind_dir,
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
1225 NULL, // open_attr
1226 NULL, // close_attr
1227 NULL, // free_attr_cookie
1228 NULL, // read_attr
1229 NULL, // write_attr
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,
1241 } // namespace
1243 file_system_module_info gRootFileSystem = {
1245 "file_systems/rootfs" B_CURRENT_FS_API_VERSION,
1247 rootfs_std_ops,
1250 "rootfs", // short_name
1251 "Root File System", // pretty_name
1252 0, // DDM flags
1254 NULL, // identify_partition()
1255 NULL, // scan_partition()
1256 NULL, // free_identify_partition_cookie()
1257 NULL, // free_partition_content_cookie()
1259 &rootfs_mount,