diff --git a/src/kernel/core/fs/builtin_fs.h b/src/kernel/core/fs/builtin_fs.h new file mode 100644 index 0000000000..d7907d3fa8 --- /dev/null +++ b/src/kernel/core/fs/builtin_fs.h @@ -0,0 +1,24 @@ +/* +** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ + +#ifndef BUILTIN_FS_H +#define BUILTIN_FS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern status_t bootstrap_rootfs(void); +extern status_t bootstrap_pipefs(void); +extern status_t bootstrap_devfs(void); +extern status_t bootstrap_bootfs(void); + +#ifdef __cplusplus +} +#endif + +#endif /* BUILTIN_FS_H */ diff --git a/src/kernel/core/fs/pipefs.cpp b/src/kernel/core/fs/pipefs.cpp new file mode 100644 index 0000000000..1f9de396ec --- /dev/null +++ b/src/kernel/core/fs/pipefs.cpp @@ -0,0 +1,804 @@ +/* +** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ + + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "builtin_fs.h" + + +// ToDo: handles file names suboptimally - it has all file names +// in a single linked list, no hash lookups or whatever. + +#define PIPEFS_TRACE 0 + +#if PIPEFS_TRACE +# define TRACE(x) dprintf x +#else +# define TRACE(x) +#endif + +struct pipefs_vnode { + pipefs_vnode *next; + pipefs_vnode *hash_next; + vnode_id id; + int32 type; + char *name; +}; + +struct dir_cookie { + dir_cookie *next; + dir_cookie *prev; + pipefs_vnode *current; + int open_mode; +}; + +struct pipefs { + mount_id id; + mutex lock; + vnode_id next_vnode_id; + hash_table *vnode_list_hash; + pipefs_vnode *root_vnode; + + // root directory contents - we don't support other directories + pipefs_vnode *first_entry; + dir_cookie *first_dir_cookie; +}; + + +#define PIPEFS_HASH_SIZE 16 + + +static uint32 +pipefs_vnode_hash_func(void *_vnode, const void *_key, uint32 range) +{ + pipefs_vnode *vnode = (pipefs_vnode *)_vnode; + const vnode_id *key = (const vnode_id *)_key; + + if (vnode != NULL) + return vnode->id % range; + + return (*key) % range; +} + + +static int +pipefs_vnode_compare_func(void *_vnode, const void *_key) +{ + pipefs_vnode *vnode = (pipefs_vnode *)_vnode; + const vnode_id *key = (const vnode_id *)_key; + + if (vnode->id == *key) + return 0; + + return -1; +} + + +static pipefs_vnode * +pipefs_create_vnode(pipefs *fs, const char *name, int32 type) +{ + pipefs_vnode *vnode = (pipefs_vnode *)malloc(sizeof(pipefs_vnode)); + if (vnode == NULL) + return NULL; + + vnode->name = strdup(name); + if (vnode->name == NULL) { + free(vnode); + return NULL; + } + + vnode->next = NULL; + vnode->id = fs->next_vnode_id++; + vnode->type = type; + + return vnode; +} + + +static status_t +pipefs_delete_vnode(pipefs *fs, pipefs_vnode *vnode, bool forceDelete) +{ + // remove it from the global hash table + hash_remove(fs->vnode_list_hash, vnode); + + free(vnode->name); + free(vnode); + + return 0; +} + + +static void +insert_cookie_in_jar(pipefs *fs, dir_cookie *cookie) +{ + cookie->next = fs->first_dir_cookie; + fs->first_dir_cookie = cookie; + cookie->prev = NULL; +} + + +static void +remove_cookie_from_jar(pipefs *fs, dir_cookie *cookie) +{ + if (cookie->next) + cookie->next->prev = cookie->prev; + + if (cookie->prev) + cookie->prev->next = cookie->next; + + if (fs->first_dir_cookie == cookie) + fs->first_dir_cookie = cookie->next; + + cookie->prev = cookie->next = NULL; +} + + +/* makes sure none of the dircookies point to the vnode passed in */ + +static void +update_dircookies(pipefs *fs, pipefs_vnode *vnode) +{ + dir_cookie *cookie; + + for (cookie = fs->first_dir_cookie; cookie; cookie = cookie->next) { + if (cookie->current == vnode) + cookie->current = vnode->next; + } +} + + +static pipefs_vnode * +pipefs_find(pipefs *fs, const char *name) +{ + if (!strcmp(name, ".") + || !strcmp(name, "..")) + return fs->root_vnode; + + for (pipefs_vnode *vnode = fs->first_entry; vnode; vnode = vnode->next) { + if (strcmp(vnode->name, name) == 0) + return vnode; + } + return NULL; +} + + +static status_t +pipefs_insert(pipefs *fs, pipefs_vnode *vnode) +{ + vnode->next = fs->first_entry; + fs->first_entry = vnode; + + return B_OK; +} + + +static status_t +pipefs_remove_from_fs(pipefs *fs, pipefs_vnode *findNode) +{ + pipefs_vnode *vnode, *last; + + for (vnode = fs->first_entry, last = NULL; vnode; last = vnode, vnode = vnode->next) { + if (vnode == findNode) { + /* make sure all dircookies dont point to this vnode */ + update_dircookies(fs, vnode); + + if (last) + last->next = vnode->next; + else + fs->first_entry = vnode->next; + + vnode->next = NULL; + return B_OK; + } + } + return B_ENTRY_NOT_FOUND; +} + + +static status_t +pipefs_remove(pipefs *fs, pipefs_vnode *directory, const char *name) +{ + struct pipefs_vnode *vnode; + status_t status = B_OK; + + mutex_lock(&fs->lock); + + vnode = pipefs_find(fs, name); + if (!vnode) + status = B_ENTRY_NOT_FOUND; + else if (vnode->type == S_IFDIR) + status = B_NOT_ALLOWED; + + if (status < B_OK) + goto err; + + pipefs_remove_from_fs(fs, vnode); + notify_listener(B_ENTRY_REMOVED, fs->id, directory->id, 0, vnode->id, name); + + // schedule this vnode to be removed when it's ref goes to zero + vfs_remove_vnode(fs->id, vnode->id); + +err: + mutex_unlock(&fs->lock); + + return status; +} + + +// #pragma mark - + + +static status_t +pipefs_mount(mount_id id, const char *device, void *args, fs_volume *_fs, vnode_id *_rootVnodeID) +{ + pipefs *fs; + pipefs_vnode *vnode; + status_t err; + + TRACE(("pipefs_mount: entry\n")); + + fs = (pipefs *)malloc(sizeof(pipefs)); + if (fs == NULL) + return ENOMEM; + + fs->id = id; + fs->next_vnode_id = 0; + + err = mutex_init(&fs->lock, "pipefs_mutex"); + if (err < 0) + goto err1; + + fs->vnode_list_hash = hash_init(PIPEFS_HASH_SIZE, (addr)&vnode->hash_next - (addr)vnode, + &pipefs_vnode_compare_func, &pipefs_vnode_hash_func); + if (fs->vnode_list_hash == NULL) { + err = B_NO_MEMORY; + goto err2; + } + + // create the root vnode + vnode = pipefs_create_vnode(fs, "", S_IFDIR); + if (vnode == NULL) { + err = B_NO_MEMORY; + goto err3; + } + + fs->root_vnode = vnode; + fs->first_entry = NULL; + fs->first_dir_cookie = NULL; + + hash_insert(fs->vnode_list_hash, vnode); + + *_rootVnodeID = vnode->id; + *_fs = fs; + + return B_OK; + +err3: + hash_uninit(fs->vnode_list_hash); +err2: + mutex_destroy(&fs->lock); +err1: + free(fs); + + return err; +} + + +static status_t +pipefs_unmount(fs_volume _fs) +{ + struct pipefs *fs = (struct pipefs *)_fs; + struct pipefs_vnode *vnode; + struct hash_iterator i; + + TRACE(("pipefs_unmount: entry fs = %p\n", fs)); + + // put_vnode on the root to release the ref to it + vfs_put_vnode(fs->id, fs->root_vnode->id); + + // delete all of the vnodes + hash_open(fs->vnode_list_hash, &i); + while ((vnode = (struct pipefs_vnode *)hash_next(fs->vnode_list_hash, &i)) != NULL) { + pipefs_delete_vnode(fs, vnode, true); + } + hash_close(fs->vnode_list_hash, &i, false); + + hash_uninit(fs->vnode_list_hash); + mutex_destroy(&fs->lock); + free(fs); + + return 0; +} + + +static status_t +pipefs_sync(fs_volume fs) +{ + TRACE(("pipefs_sync: entry\n")); + + return 0; +} + + +static status_t +pipefs_lookup(fs_volume _fs, fs_vnode _dir, const char *name, vnode_id *_id, int *_type) +{ + pipefs *fs = (pipefs *)_fs; + status_t status; + + TRACE(("pipefs_lookup: entry dir %p, name '%s'\n", _dir, name)); + + pipefs_vnode *directory = (pipefs_vnode *)_dir; + if (directory->type != S_IFDIR) + return B_NOT_A_DIRECTORY; + + mutex_lock(&fs->lock); + + // look it up + pipefs_vnode *vnode = pipefs_find(fs, name); + if (!vnode) { + status = B_ENTRY_NOT_FOUND; + goto err; + } + + pipefs_vnode *vnodeDummy; + status = vfs_get_vnode(fs->id, vnode->id, (fs_vnode *)&vnodeDummy); + if (status < B_OK) + goto err; + + *_id = vnode->id; + *_type = vnode->type; + +err: + mutex_unlock(&fs->lock); + + return status; +} + + +static status_t +pipefs_get_vnode_name(fs_volume _fs, fs_vnode _vnode, char *buffer, size_t bufferSize) +{ + pipefs_vnode *vnode = (pipefs_vnode *)_vnode; + + TRACE(("devfs_get_vnode_name: vnode = %p\n", vnode)); + + strlcpy(buffer, vnode->name, bufferSize); + return B_OK; +} + + +static status_t +pipefs_get_vnode(fs_volume _fs, vnode_id id, fs_vnode *_vnode, bool reenter) +{ + pipefs *fs = (pipefs *)_fs; + + TRACE(("pipefs_getvnode: asking for vnode 0x%Lx, r %d\n", id, reenter)); + + if (!reenter) + mutex_lock(&fs->lock); + + *_vnode = hash_lookup(fs->vnode_list_hash, &id); + + if (!reenter) + mutex_unlock(&fs->lock); + + TRACE(("pipefs_getnvnode: looked it up at %p\n", *_vnode)); + + if (*_vnode) + return B_OK; + + return B_ENTRY_NOT_FOUND; +} + + +static status_t +pipefs_put_vnode(fs_volume _fs, fs_vnode _vnode, bool reenter) +{ +#if PIPEFS_TRACE + pipefs_vnode *vnode = (pipefs_vnode *)_vnode; + + TRACE(("pipefs_putvnode: entry on vnode 0x%Lx, r %d\n", vnode->id, reenter)); +#endif + return B_OK; +} + + +static status_t +pipefs_remove_vnode(fs_volume _fs, fs_vnode _vnode, bool reenter) +{ + pipefs *fs = (pipefs *)_fs; + + TRACE(("pipefs_removevnode: remove %p (0x%Lx), r %d\n", vnode, vnode->id, reenter)); + + if (!reenter) + mutex_lock(&fs->lock); + + pipefs_vnode *vnode = (pipefs_vnode *)_vnode; + if (vnode->next) { + // can't remove node if it's linked to the dir + panic("pipefs_remove_vnode(): vnode %p asked to be removed is present in dir\n", vnode); + } + + pipefs_delete_vnode(fs, vnode, false); + + if (!reenter) + mutex_unlock(&fs->lock); + + return 0; +} + + +static status_t +pipefs_create(fs_volume _fs, fs_vnode _dir, const char *name, int omode, int perms, fs_cookie *_cookie, vnode_id *new_vnid) +{ + pipefs *fs = (pipefs *)_fs; + + TRACE(("pipefs_create_dir: dir %p, name = '%s', perms = %d, id = 0x%Lx pointer id = %p\n", dir, name, perms,*new_vnid, new_vnid)); + + mutex_lock(&fs->lock); + + pipefs_vnode *directory = (pipefs_vnode *)_dir; + status_t status = B_OK; + + pipefs_vnode *vnode = pipefs_find(fs, name); + if (vnode != NULL) { + status = B_FILE_EXISTS; + goto err; + } + + vnode = pipefs_create_vnode(fs, name, S_IFIFO); + if (vnode == NULL) { + status = B_NO_MEMORY; + goto err; + } + + pipefs_insert(fs, vnode); + hash_insert(fs->vnode_list_hash, vnode); + + mutex_unlock(&fs->lock); + + notify_listener(B_ENTRY_CREATED, fs->id, directory->id, 0, vnode->id, name); + return B_OK; + +err1: + pipefs_delete_vnode(fs, vnode, false); +err: + mutex_unlock(&fs->lock); + + return status; +} + + +static status_t +pipefs_open(fs_volume _fs, fs_vnode _v, int openMode, fs_cookie *_cookie) +{ + // allow to open the file, but it can't be done anything with it + + *_cookie = NULL; + // initialize the cookie, because pipefs_free_cookie() relies on it + + return B_OK; +} + + +static status_t +pipefs_close(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie) +{ + TRACE(("pipefs_close: entry vnode %p, cookie %p\n", _vnode, _cookie)); + + return 0; +} + + +static status_t +pipefs_free_cookie(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie) +{ +// pipefs_cookie *cookie = _cookie; + + TRACE(("pipefs_freecookie: entry vnode %p, cookie %p\n", _vnode, _cookie)); + +// if (cookie) +// free(cookie); + + return 0; +} + + +static status_t +pipefs_fsync(fs_volume _fs, fs_vnode _v) +{ + return B_OK; +} + + +static ssize_t +pipefs_read(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, off_t pos, + void *buffer, size_t *_length) +{ + TRACE(("pipefs_read: vnode %p, cookie %p, pos 0x%Lx , len 0x%lx\n", vnode, cookie, pos, *len)); + + return EINVAL; +} + + +static ssize_t +pipefs_write(fs_volume fs, fs_vnode vnode, fs_cookie cookie, off_t pos, + const void *buffer, size_t *_length) +{ + TRACE(("pipefs_write: vnode %p, cookie %p, pos 0x%Lx , len 0x%lx\n", vnode, cookie, pos, *len)); + + return EINVAL; +} + + +static off_t +pipefs_seek(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, off_t pos, int seekType) +{ + return ESPIPE; +} + + +static status_t +pipefs_create_dir(fs_volume _fs, fs_vnode _dir, const char *name, int perms, vnode_id *_newID) +{ + return ENOSYS; +} + + +static status_t +pipefs_remove_dir(fs_volume _fs, fs_vnode _dir, const char *name) +{ + return EPERM; +} + + +static status_t +pipefs_open_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie *_cookie) +{ + pipefs *fs = (pipefs *)_fs; + + TRACE(("pipefs_open_dir(): vnode = %p\n", _vnode)); + + pipefs_vnode *vnode = (pipefs_vnode *)_vnode; + if (vnode->type != S_IFDIR) + return B_BAD_VALUE; + + if (vnode != fs->root_vnode) + panic("pipefs: found directory that's not the root!"); + + dir_cookie *cookie = (dir_cookie *)malloc(sizeof(dir_cookie)); + if (cookie == NULL) + return B_NO_MEMORY; + + mutex_lock(&fs->lock); + + cookie->current = fs->first_entry; + + insert_cookie_in_jar(fs, cookie); + *_cookie = cookie; + + mutex_unlock(&fs->lock); + + return B_OK; +} + + +static status_t +pipefs_read_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, + struct dirent *dirent, size_t bufferSize, uint32 *_num) +{ + pipefs *fs = (pipefs *)_fs; + status_t status = 0; + + TRACE(("pipefs_read_dir: vnode %p, cookie %p, buffer = %p, bufferSize = %ld, num = %p\n", _vnode, cookie, dirent, bufferSize,_num)); + + mutex_lock(&fs->lock); + + dir_cookie *cookie = (dir_cookie *)_cookie; + if (cookie->current == NULL) { + // we're at the end of the directory + *_num = 0; + status = B_OK; + goto err; + } + + dirent->d_dev = fs->id; + dirent->d_ino = cookie->current->id; + dirent->d_reclen = strlen(cookie->current->name) + sizeof(struct dirent); + + if (dirent->d_reclen > bufferSize) { + status = ENOBUFS; + goto err; + } + + status = user_strlcpy(dirent->d_name, cookie->current->name, bufferSize); + if (status < 0) + goto err; + + cookie->current = cookie->current->next; + +err: + mutex_unlock(&fs->lock); + + return status; +} + + +static status_t +pipefs_rewind_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie) +{ + pipefs *fs = (pipefs *)_fs; + + mutex_lock(&fs->lock); + + dir_cookie *cookie = (dir_cookie *)_cookie; + cookie->current = fs->first_entry; + + mutex_unlock(&fs->lock); + return B_OK; +} + + +static status_t +pipefs_close_dir(fs_volume _fs, fs_vnode _v, fs_cookie _cookie) +{ + TRACE(("pipefs_close: entry vnode %p, cookie %p\n", _vnode, _cookie)); + + return 0; +} + + +static status_t +pipefs_free_dir_cookie(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie) +{ + dir_cookie *cookie = (dir_cookie *)_cookie; + + TRACE(("pipefs_freecookie: entry vnode %p, cookie %p\n", _vnode, cookie)); + + free(cookie); + return 0; +} + + +static status_t +pipefs_ioctl(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, ulong op, + void *buffer, size_t length) +{ + TRACE(("pipefs_ioctl: vnode %p, cookie %p, op %ld, buf %p, len %ld\n", + _vnode, _cookie, op, buffer, length)); + + return EINVAL; +} + + +static status_t +pipefs_can_page(fs_volume _fs, fs_vnode _v) +{ + return -1; +} + + +static ssize_t +pipefs_read_pages(fs_volume _fs, fs_vnode _v, iovecs *vecs, off_t pos) +{ + return EPERM; +} + + +static ssize_t +pipefs_write_pages(fs_volume _fs, fs_vnode _v, iovecs *vecs, off_t pos) +{ + return EPERM; +} + + +static status_t +pipefs_unlink(fs_volume _fs, fs_vnode _dir, const char *name) +{ + pipefs *fs = (pipefs *)_fs; + pipefs_vnode *directory = (pipefs_vnode *)_dir; + + TRACE(("pipefs_unlink: dir %p (0x%Lx), name '%s'\n", _dir, directory->id, name)); + + return pipefs_remove(fs, directory, name); +} + + +static status_t +pipefs_read_stat(fs_volume _fs, fs_vnode _vnode, struct stat *stat) +{ + pipefs *fs = (pipefs *)_fs; + pipefs_vnode *vnode = (pipefs_vnode *)_vnode; + + TRACE(("pipefs_rstat: vnode %p (0x%Lx), stat %p\n", vnode, vnode->id, stat)); + + stat->st_dev = fs->id; + stat->st_ino = vnode->id; + stat->st_size = 0; // ToDo: oughta get me! + stat->st_mode = vnode->type | DEFFILEMODE; + + return 0; +} + + +// #pragma mark - + + +static struct fs_ops pipefs_ops = { + &pipefs_mount, + &pipefs_unmount, + NULL, + NULL, + &pipefs_sync, + + &pipefs_lookup, + &pipefs_get_vnode_name, + + &pipefs_get_vnode, + &pipefs_put_vnode, + &pipefs_remove_vnode, + + &pipefs_can_page, + &pipefs_read_pages, + &pipefs_write_pages, + + /* common */ + &pipefs_ioctl, + &pipefs_fsync, + + NULL, // fs_read_link() + NULL, // fs_write_link() + NULL, // fs_symlink() + NULL, // fs_link() + &pipefs_unlink, + NULL, // fs_rename() + + NULL, // fs_access() + &pipefs_read_stat, + NULL, // fs_write_stat() + + /* file */ + &pipefs_create, + &pipefs_open, + &pipefs_close, + &pipefs_free_cookie, + &pipefs_read, + &pipefs_write, + + /* directory */ + &pipefs_create_dir, + &pipefs_remove_dir, + &pipefs_open_dir, + &pipefs_close_dir, + &pipefs_free_dir_cookie, + &pipefs_read_dir, + &pipefs_rewind_dir, + + // the other operations are not supported (attributes, indices, queries) + NULL, +}; + + +extern "C" status_t +bootstrap_pipefs(void) +{ + dprintf("bootstrap_pipefs: entry\n"); + + return vfs_register_filesystem("pipefs", &pipefs_ops); +}