/* ** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. ** Distributed under the terms of the NewOS License. */ #include #include #include #include #include #include #include #include #include #include #include #include "builtin_fs.h" #define BOOTFS_TRACE 0 #if BOOTFS_TRACE #define TRACE(x) dprintf x #else #define TRACE(x) #endif static char *bootdir = NULL; static off_t bootdir_len = 0; static region_id bootdir_region = -1; typedef enum { STREAM_TYPE_FILE = S_IFREG, STREAM_TYPE_DIR = S_IFDIR, } stream_type; struct bootfs_stream { stream_type type; union { struct stream_dir { struct bootfs_vnode *dir_head; struct bootfs_cookie *jar_head; } dir; struct stream_file { void *start; off_t len; } file; } u; }; struct bootfs_vnode { struct bootfs_vnode *all_next; vnode_id id; char *name; struct bootfs_vnode *parent; struct bootfs_vnode *dir_next; struct bootfs_stream stream; }; struct bootfs { mount_id id; mutex lock; int32 next_vnode_id; void *vnode_list_hash; struct bootfs_vnode *root_vnode; }; struct bootfs_cookie { struct bootfs_stream *s; int oflags; union { struct cookie_dir { struct bootfs_cookie *next; struct bootfs_cookie *prev; struct bootfs_vnode *ptr; } dir; struct cookie_file { off_t pos; } file; } u; }; #define BOOTFS_HASH_SIZE 16 static uint32 bootfs_vnode_hash_func(void *_v, const void *_key, uint32 range) { struct bootfs_vnode *v = _v; const vnode_id *key = _key; if (v != NULL) return v->id % range; return (*key) % range; } static int bootfs_vnode_compare_func(void *_v, const void *_key) { struct bootfs_vnode *v = _v; const vnode_id *key = _key; if (v->id == *key) return 0; return -1; } static struct bootfs_vnode * bootfs_create_vnode(struct bootfs *fs, const char *name) { struct bootfs_vnode *v; v = malloc(sizeof(struct bootfs_vnode)); if (v == NULL) return NULL; memset(v, 0, sizeof(struct bootfs_vnode)); v->id = atomic_add(&fs->next_vnode_id, 1); v->name = strdup(name); if (v->name == NULL) { free(v); return NULL; } return v; } static status_t bootfs_delete_vnode(struct bootfs *fs, struct bootfs_vnode *v, bool force_delete) { // cant delete it if it's in a directory or is a directory // and has children if (!force_delete && ((v->stream.type == STREAM_TYPE_DIR && v->stream.u.dir.dir_head != NULL) || v->dir_next != NULL)) return EPERM; // remove it from the global hash table hash_remove(fs->vnode_list_hash, v); if (v->name != NULL) free(v->name); free(v); return 0; } static void insert_cookie_in_jar(struct bootfs_vnode *dir, struct bootfs_cookie *cookie) { cookie->u.dir.next = dir->stream.u.dir.jar_head; dir->stream.u.dir.jar_head = cookie; cookie->u.dir.prev = NULL; } static void remove_cookie_from_jar(struct bootfs_vnode *dir, struct bootfs_cookie *cookie) { if(cookie->u.dir.next) cookie->u.dir.next->u.dir.prev = cookie->u.dir.prev; if(cookie->u.dir.prev) cookie->u.dir.prev->u.dir.next = cookie->u.dir.next; if(dir->stream.u.dir.jar_head == cookie) dir->stream.u.dir.jar_head = cookie->u.dir.next; cookie->u.dir.prev = cookie->u.dir.next = NULL; } /** Makes sure none of the dircookies point to the vnode passed in */ static void update_dircookies(struct bootfs_vnode *dir, struct bootfs_vnode *v) { struct bootfs_cookie *cookie; for(cookie = dir->stream.u.dir.jar_head; cookie; cookie = cookie->u.dir.next) { if(cookie->u.dir.ptr == v) { cookie->u.dir.ptr = v->dir_next; } } } static struct bootfs_vnode * bootfs_find_in_dir(struct bootfs_vnode *dir, const char *path) { struct bootfs_vnode *v; if(dir->stream.type != STREAM_TYPE_DIR) return NULL; if(!strcmp(path, ".")) return dir; if(!strcmp(path, "..")) return dir->parent; for(v = dir->stream.u.dir.dir_head; v; v = v->dir_next) { // dprintf("bootfs_find_in_dir: looking at entry '%s'\n", v->name); if(strcmp(v->name, path) == 0) { // dprintf("bootfs_find_in_dir: found it at 0x%x\n", v); return v; } } return NULL; } static status_t bootfs_insert_in_dir(struct bootfs_vnode *dir, struct bootfs_vnode *v) { if(dir->stream.type != STREAM_TYPE_DIR) return EINVAL; v->dir_next = dir->stream.u.dir.dir_head; dir->stream.u.dir.dir_head = v; v->parent = dir; return 0; } static status_t bootfs_remove_from_dir(struct bootfs_vnode *dir, struct bootfs_vnode *findit) { struct bootfs_vnode *v; struct bootfs_vnode *last_v; for(v = dir->stream.u.dir.dir_head, last_v = NULL; v; last_v = v, v = v->dir_next) { if(v == findit) { /* make sure all dircookies dont point to this vnode */ update_dircookies(dir, v); if(last_v) last_v->dir_next = v->dir_next; else dir->stream.u.dir.dir_head = v->dir_next; v->dir_next = NULL; return 0; } } return -1; } /* XXX seems to be unused static bool bootfs_is_dir_empty(struct bootfs_vnode *dir) { if(dir->stream.type != STREAM_TYPE_DIR) return false; return !dir->stream.u.dir.dir_head; } */ /** Creates a path of vnodes up to the last part of the passed in path. * returns the vnode the last segment should be a part of and * a pointer to the leaf of the path. * clobbers the path string passed in */ static struct bootfs_vnode * bootfs_create_path(struct bootfs *fs, char *path, struct bootfs_vnode *base, char **path_leaf) { struct bootfs_vnode *v; char *temp; bool done; // strip off any leading '/' or spaces for(; *path == '/' || *path == ' '; path++) ; // first, find the leaf *path_leaf = strrchr(path, '/'); if(*path_leaf == NULL) { // didn't find it, so this path only is a leaf *path_leaf = path; return base; } // this is a multipart path, seperate the leaf off **path_leaf = '\0'; (*path_leaf)++; if(**path_leaf == '\0') { // the path ended with '/'. That's invalid return NULL; } // now, lets walk down the path, building vnodes as we need em done = false; for(; !done; path = temp+1) { // find the next seperator and knock it out temp = strchr(path, '/'); if(temp) { *temp = '\0'; } else { done = true; } if(*path == '\0') { // zero length path segment, continue continue; } v = bootfs_find_in_dir(base, path); if(!v) { v = bootfs_create_vnode(fs, path); if(!v) return NULL; v->stream.type = STREAM_TYPE_DIR; v->stream.u.dir.dir_head = NULL; v->stream.u.dir.jar_head = NULL; bootfs_insert_in_dir(base, v); hash_insert(fs->vnode_list_hash, v); } else { // we found one if(v->stream.type != STREAM_TYPE_DIR) return NULL; } base = v; } return base; } static status_t bootfs_create_vnode_tree(struct bootfs *fs, struct bootfs_vnode *root) { int i; boot_entry *entry; int err; struct bootfs_vnode *new_vnode; struct bootfs_vnode *dir; char path[SYS_MAX_PATH_LEN]; char *leaf; entry = (boot_entry *)bootdir; for (i = 0; i < BOOTDIR_MAX_ENTRIES; i++) { if(entry[i].be_type != BE_TYPE_NONE && entry[i].be_type != BE_TYPE_DIRECTORY) { strncpy(path, entry[i].be_name, SYS_MAX_PATH_LEN-1); path[SYS_MAX_PATH_LEN-1] = '\0'; dir = bootfs_create_path(fs, path, root, &leaf); if(!dir) continue; new_vnode = bootfs_create_vnode(fs, leaf); if(new_vnode == NULL) return ENOMEM; // set up the new node new_vnode->stream.type = STREAM_TYPE_FILE; new_vnode->stream.u.file.start = bootdir + entry[i].be_offset * PAGE_SIZE; new_vnode->stream.u.file.len = entry[i].be_vsize; dprintf("bootfs_create_vnode_tree: added entry '%s', start %p, len 0x%Lx\n", new_vnode->name, new_vnode->stream.u.file.start, new_vnode->stream.u.file.len); // insert it into the parent dir bootfs_insert_in_dir(dir, new_vnode); hash_insert(fs->vnode_list_hash, new_vnode); } } return 0; } // #pragma mark - static status_t bootfs_mount(mount_id id, const char *device, void *args, fs_volume *_fs, vnode_id *root_vnid) { struct bootfs *fs; struct bootfs_vnode *v; status_t err; TRACE(("bootfs_mount: entry\n")); fs = malloc(sizeof(struct bootfs)); if(fs == NULL) { err = ENOMEM; goto err; } fs->id = id; fs->next_vnode_id = 0; err = mutex_init(&fs->lock, "bootfs_mutex"); if(err < 0) { goto err1; } fs->vnode_list_hash = hash_init(BOOTFS_HASH_SIZE, (addr)&v->all_next - (addr)v, &bootfs_vnode_compare_func, &bootfs_vnode_hash_func); if(fs->vnode_list_hash == NULL) { err = ENOMEM; goto err2; } // create a vnode v = bootfs_create_vnode(fs, ""); if(v == NULL) { err = ENOMEM; goto err3; } // set it up v->parent = v; // create a dir stream for it to hold v->stream.type = STREAM_TYPE_DIR; v->stream.u.dir.dir_head = NULL; fs->root_vnode = v; hash_insert(fs->vnode_list_hash, v); err = bootfs_create_vnode_tree(fs, fs->root_vnode); if(err < 0) { goto err4; } *root_vnid = v->id; *_fs = fs; return 0; err4: bootfs_delete_vnode(fs, v, true); err3: hash_uninit(fs->vnode_list_hash); err2: mutex_destroy(&fs->lock); err1: free(fs); err: return err; } static status_t bootfs_unmount(fs_volume _fs) { struct bootfs *fs = _fs; struct bootfs_vnode *v; struct hash_iterator i; TRACE(("bootfs_unmount: entry fs = %p\n", fs)); // delete all of the vnodes hash_open(fs->vnode_list_hash, &i); while((v = (struct bootfs_vnode *)hash_next(fs->vnode_list_hash, &i)) != NULL) { bootfs_delete_vnode(fs, v, 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 bootfs_sync(fs_volume fs) { TRACE(("bootfs_sync: entry\n")); return 0; } static status_t bootfs_lookup(fs_volume _fs, fs_vnode _dir, const char *name, vnode_id *_id, int *_type) { struct bootfs *fs = (struct bootfs *)_fs; struct bootfs_vnode *dir = (struct bootfs_vnode *)_dir; struct bootfs_vnode *vnode, *vdummy; status_t status; TRACE(("bootfs_lookup: entry dir %p, name '%s'\n", dir, name)); if (dir->stream.type != STREAM_TYPE_DIR) return B_NOT_A_DIRECTORY; mutex_lock(&fs->lock); // look it up vnode = bootfs_find_in_dir(dir, name); if (!vnode) { status = B_ENTRY_NOT_FOUND; goto err; } status = vfs_get_vnode(fs->id, vnode->id, (fs_vnode *)&vdummy); if (status < 0) goto err; *_id = vnode->id; *_type = dir->stream.type; err: mutex_unlock(&fs->lock); return status; } static status_t bootfs_get_vnode_name(fs_volume _fs, fs_vnode _vnode, char *buffer, size_t bufferSize) { struct bootfs_vnode *vnode = (struct bootfs_vnode *)_vnode; TRACE(("devfs_get_vnode_name: vnode = %p\n",vnode)); strlcpy(buffer,vnode->name,bufferSize); return B_OK; } static status_t bootfs_get_vnode(fs_volume _fs, vnode_id id, fs_vnode *v, bool r) { struct bootfs *fs = (struct bootfs *)_fs; TRACE(("bootfs_get_vnode: asking for vnode 0x%Lx, r %d\n", id, r)); if (!r) mutex_lock(&fs->lock); *v = hash_lookup(fs->vnode_list_hash, &id); if (!r) mutex_unlock(&fs->lock); TRACE(("bootfs_get_vnode: looked it up at %p\n", *v)); if (*v) return B_OK; return B_ENTRY_NOT_FOUND; } static status_t bootfs_put_vnode(fs_volume _fs, fs_vnode _v, bool r) { struct bootfs_vnode *v = (struct bootfs_vnode *)_v; TRACE(("bootfs_put_vnode: entry on vnode 0x%Lx r %d\n", v->id, r)); return 0; // whatever } static status_t bootfs_remove_vnode(fs_volume _fs, fs_vnode _v, bool reenter) { struct bootfs *fs = (struct bootfs *)_fs; struct bootfs_vnode *v = (struct bootfs_vnode *)_v; struct bootfs_vnode dummy; status_t err; TRACE(("bootfs_remove_vnode: remove %p (0x%Lx) r %d\n", v, v->id, reenter)); if (!reenter) mutex_lock(&fs->lock); if (v->dir_next) { // can't remove node if it's linked to the dir panic("bootfs_remove_vnode: vnode %p asked to be removed is present in dir\n", v); } bootfs_delete_vnode(fs, v, false); err = 0; err: if (!reenter) mutex_unlock(&fs->lock); return err; } static status_t bootfs_create(fs_volume _fs, fs_vnode _dir, const char *name, int omode, int perms, fs_cookie *_cookie, vnode_id *new_vnid) { return EROFS; } static status_t bootfs_open(fs_volume _fs, fs_vnode _v, int oflags, fs_cookie *_cookie) { struct bootfs *fs = _fs; struct bootfs_vnode *vnode = _v; struct bootfs_cookie *cookie; TRACE(("bootfs_open: vnode %p, oflags 0x%x\n", vnode, oflags)); cookie = malloc(sizeof(struct bootfs_cookie)); if (cookie == NULL) return ENOMEM; mutex_lock(&fs->lock); cookie->s = &vnode->stream; cookie->u.file.pos = 0; *_cookie = cookie; mutex_unlock(&fs->lock); return B_OK; } static status_t bootfs_close(fs_volume _fs, fs_vnode _v, fs_cookie _cookie) { struct bootfs *fs = _fs; struct bootfs_vnode *v = _v; struct bootfs_cookie *cookie = _cookie; TRACE(("bootfs_close: entry vnode %p, cookie %p\n", v, cookie)); return 0; } static status_t bootfs_free_cookie(fs_volume _fs, fs_vnode _v, fs_cookie _cookie) { struct bootfs *fs = _fs; struct bootfs_vnode *v = _v; struct bootfs_cookie *cookie = _cookie; TRACE(("bootfs_freecookie: entry vnode %p, cookie %p\n", v, cookie)); if (cookie) free(cookie); return 0; } static status_t bootfs_fsync(fs_volume _fs, fs_vnode _v) { return 0; } static ssize_t bootfs_read(fs_volume _fs, fs_vnode _v, fs_cookie _cookie, off_t pos, void *buf, size_t *len) { struct bootfs *fs = _fs; struct bootfs_vnode *v = _v; struct bootfs_cookie *cookie = _cookie; ssize_t err = 0; TRACE(("bootfs_read: vnode %p, cookie %p, pos 0x%Lx, len 0x%lx\n", v, cookie, pos, *len)); mutex_lock(&fs->lock); switch(cookie->s->type) { case STREAM_TYPE_FILE: if(*len <= 0) { err = 0; break; } if(pos < 0) { // we'll read where the cookie is at pos = cookie->u.file.pos; } if(pos >= cookie->s->u.file.len) { *len = 0; err = ESPIPE; break; } if(pos + *len > cookie->s->u.file.len) { // trim the read *len = cookie->s->u.file.len - pos; } err = user_memcpy(buf, cookie->s->u.file.start + pos, *len); if(err < 0) { goto err; } cookie->u.file.pos = pos + *len; err = 0; break; default: *len = 0; err = EINVAL; } err: mutex_unlock(&fs->lock); return err; } static ssize_t bootfs_write(fs_volume fs, fs_vnode v, fs_cookie cookie, off_t pos, const void *buf, size_t *len) { TRACE(("bootfs_write: vnode %p, cookie %p, pos 0x%Lx , len 0x%lx\n", v, cookie, pos, *len)); return EROFS; } static off_t bootfs_seek(fs_volume _fs, fs_vnode _v, fs_cookie _cookie, off_t pos, int seekType) { struct bootfs *fs = _fs; struct bootfs_vnode *v = _v; struct bootfs_cookie *cookie = _cookie; status_t err = B_OK; TRACE(("bootfs_seek: vnode %p, cookie %p, pos 0x%Lx , seek_type %d\n", v, cookie, pos, seekType)); if (cookie->s->type != STREAM_TYPE_FILE) return EINVAL; mutex_lock(&fs->lock); switch (seekType) { case SEEK_SET: if (pos < 0) pos = 0; if (pos > cookie->s->u.file.len) pos = cookie->s->u.file.len; cookie->u.file.pos = pos; break; case SEEK_CUR: if (pos + cookie->u.file.pos > cookie->s->u.file.len) pos = cookie->s->u.file.len; else if (pos + cookie->u.file.pos < 0) pos = 0; else pos += cookie->u.file.pos; break; case SEEK_END: if (pos > 0) pos = cookie->s->u.file.len; else if (pos + cookie->s->u.file.len < 0) pos = 0; else pos += cookie->s->u.file.len; break; default: err = EINVAL; } if (err == B_OK) cookie->u.file.pos = pos; mutex_unlock(&fs->lock); if (err < B_OK) return err; return pos; } static status_t bootfs_create_dir(fs_volume _fs, fs_vnode _dir, const char *name, int perms, vnode_id *new_vnid) { return EROFS; } static status_t bootfs_open_dir(fs_volume _fs, fs_vnode _v, fs_cookie *_cookie) { struct bootfs *fs = _fs; struct bootfs_vnode *vnode = _v; struct bootfs_cookie *cookie; status_t status = 0; TRACE(("bootfs_open_dir: vnode %p\n", vnode)); if (vnode->stream.type != STREAM_TYPE_DIR) return EINVAL; cookie = malloc(sizeof(struct bootfs_cookie)); if (cookie == NULL) return ENOMEM; mutex_lock(&fs->lock); cookie->s = &vnode->stream; cookie->u.dir.ptr = vnode->stream.u.dir.dir_head; *_cookie = cookie; mutex_unlock(&fs->lock); return status; } static status_t bootfs_read_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, struct dirent *dirent, size_t bufferSize, uint32 *_num) { struct bootfs_cookie *cookie = _cookie; struct bootfs *fs = _fs; status_t status; TRACE(("bootfs_read_dir(fs_volume = %p vnode = %p, fs_cookie = %p, buffer = %p, bufferSize = %ld, num = %ld)\n",_fs, _vnode, cookie, dirent, bufferSize, *_num)); mutex_lock(&fs->lock); if (cookie->u.dir.ptr == NULL) { *_num = 0; status = B_OK; goto err; } dirent->d_dev = fs->id; dirent->d_ino = cookie->u.dir.ptr->id; dirent->d_reclen = strlen(cookie->u.dir.ptr->name) + sizeof(struct dirent); if (dirent->d_reclen > bufferSize) { status = ENOBUFS; goto err; } status = user_strcpy(dirent->d_name, cookie->u.dir.ptr->name); if (status < B_OK) goto err; cookie->u.dir.ptr = cookie->u.dir.ptr->dir_next; err: mutex_unlock(&fs->lock); return status; } static status_t bootfs_rewind_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie) { struct bootfs *fs = _fs; struct bootfs_vnode *vnode = _vnode; struct bootfs_cookie *cookie = _cookie; mutex_lock(&fs->lock); cookie->u.dir.ptr = vnode->stream.u.dir.dir_head; mutex_unlock(&fs->lock); return B_OK; } static status_t bootfs_ioctl(fs_volume _fs, fs_vnode _v, fs_cookie _cookie, ulong op, void *buf, size_t len) { TRACE(("bootfs_ioctl: fs_volume %p vnode %p, fs_cookie %p, op %lu, buf %p, len %ld\n", _fs, _v, _cookie, op, buf, len)); return EINVAL; } static status_t bootfs_can_page(fs_volume _fs, fs_vnode _v) { struct bootfs_vnode *v = _v; TRACE(("bootfs_canpage: vnode %p\n", v)); if (v->stream.type == STREAM_TYPE_FILE) return 1; else return 0; } static ssize_t bootfs_read_pages(fs_volume _fs, fs_vnode _v, iovecs *vecs, off_t pos) { struct bootfs *fs = _fs; struct bootfs_vnode *v = _v; unsigned int i; TRACE(("bootfs_readpage: fs_cookie %p vnode %p, vecs %p, pos 0x%Ld\n",fs, v, vecs, pos)); for (i = 0; i < vecs->num; i++) { if (pos >= v->stream.u.file.len) { memset(vecs->vec[i].iov_base, 0, vecs->vec[i].iov_len); pos += vecs->vec[i].iov_len; } else { unsigned int copy_len; copy_len = min(vecs->vec[i].iov_len, v->stream.u.file.len - pos); memcpy(vecs->vec[i].iov_base, v->stream.u.file.start + pos, copy_len); if (copy_len < vecs->vec[i].iov_len) memset((char *)vecs->vec[i].iov_base + copy_len, 0, vecs->vec[i].iov_len - copy_len); pos += vecs->vec[i].iov_len; } } return EPERM; } static ssize_t bootfs_write_pages(fs_volume _fs, fs_vnode _v, iovecs *vecs, off_t pos) { struct bootfs *fs = _fs; struct bootfs_vnode *v = _v; TRACE(("bootfs_writepage: fs_cookie %p vnode %p, vecs %p, pos %Ld \n", fs, v, vecs, pos)); return EPERM; } static status_t bootfs_unlink(fs_volume _fs, fs_vnode _dir, const char *name) { return EROFS; } static status_t bootfs_rename(fs_volume _fs, fs_vnode _olddir, const char *oldname, fs_vnode _newdir, const char *newname) { return EROFS; } static status_t bootfs_read_stat(fs_volume _fs, fs_vnode _v, struct stat *stat) { struct bootfs *fs = _fs; struct bootfs_vnode *v = _v; status_t err = 0; TRACE(("bootfs_rstat: fs_cookie %p vnode %p v->id 0x%Lx , stat %p\n", fs, v, v->id, stat)); mutex_lock(&fs->lock); /* Read Only File System */ /** XXX - no pretense at access control, just tell people * they can read it :) */ stat->st_mode = (S_IRUSR | S_IRGRP | S_IROTH); stat->st_size = 0; stat->st_ino = v->id; switch(v->stream.type) { case STREAM_TYPE_DIR: stat->st_mode |= S_IFDIR; break; case STREAM_TYPE_FILE: stat->st_size = v->stream.u.file.len; stat->st_mode |= S_IFREG; break; default: err = EINVAL; break; } err: mutex_unlock(&fs->lock); return err; } static status_t bootfs_write_stat(fs_volume _fs, fs_vnode _v, const struct stat *stat, int stat_mask) { struct bootfs *fs = _fs; struct bootfs_vnode *v = _v; TRACE(("bootfs_wstat: fs_cookie %p, vnode %p, v->id 0x%Lx, stat 0x%p, stat_mask 0x%x\n", fs, v, v->id, stat, stat_mask)); // cannot change anything return EROFS; } // #pragma mark - static struct fs_ops bootfs_ops = { &bootfs_mount, &bootfs_unmount, NULL, NULL, &bootfs_sync, &bootfs_lookup, &bootfs_get_vnode_name, &bootfs_get_vnode, &bootfs_put_vnode, &bootfs_remove_vnode, &bootfs_can_page, &bootfs_read_pages, &bootfs_write_pages, /* common */ &bootfs_ioctl, &bootfs_fsync, NULL, // read_link NULL, // write_link NULL, // symlink NULL, // link &bootfs_unlink, &bootfs_rename, NULL, // access &bootfs_read_stat, &bootfs_write_stat, /* file */ &bootfs_create, &bootfs_open, &bootfs_close, &bootfs_free_cookie, &bootfs_read, &bootfs_write, /* dir */ &bootfs_create_dir, NULL, // remove_dir &bootfs_open_dir, &bootfs_close, // we are using the same operations for directories &bootfs_free_cookie, // and files here - that's intended, not by accident &bootfs_read_dir, &bootfs_rewind_dir, // the other operations are not supported (attributes, indices, queries) NULL, }; status_t bootstrap_bootfs(void) { area_id area; area_info areaInfo; TRACE(("bootstrap_bootfs: entry\n")); // find the bootdir and set it up area = find_area("bootdir"); if (area < 0) panic("bootstrap_bootfs: no bootdir area found!\n"); get_area_info(area, &areaInfo); bootdir = (char *)areaInfo.address; bootdir_len = areaInfo.size; bootdir_region = areaInfo.area; TRACE(("bootstrap_bootfs: found bootdir at %p\n", bootdir)); return vfs_register_filesystem("bootfs", &bootfs_ops); }