summaryrefslogtreecommitdiff
path: root/src/libfs/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libfs/core.c')
-rw-r--r--src/libfs/core.c562
1 files changed, 562 insertions, 0 deletions
diff --git a/src/libfs/core.c b/src/libfs/core.c
new file mode 100644
index 0000000..9743a64
--- /dev/null
+++ b/src/libfs/core.c
@@ -0,0 +1,562 @@
+/*
+ * <fs.c>
+ *
+ * Open Hack'Ware BIOS file systems management
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "bios.h"
+#include "libfs.h"
+#undef FS_DPRINTF
+#define FS_DPRINTF(fmt, args...) do { } while (0)
+
+static int special_file_get_type (const unsigned char *name)
+{
+ int ret;
+
+ if (strcmp(name, "root") == 0)
+ ret = FILE_ROOT;
+ else if (strcmp(name, "boot") == 0)
+ ret = FILE_BOOT;
+ else if (strcmp(name, "bootdir") == 0)
+ ret = FILE_BOOTDIR;
+ else
+ ret = FILE_UNKNOWN;
+
+ return ret;
+}
+
+void fs_cache_add_inode (inode_t *parent, inode_t *inode)
+{
+ inode_t **cur;
+
+ if (parent == NULL || inode == NULL)
+ return;
+ FS_DPRINTF("Add inode '%s' to '%s' cache\n", inode->name, parent->name);
+ for (cur = &parent->child; *cur != NULL; cur = &((*cur)->next)) {
+ if (strcmp((*cur)->name, inode->name) == 0) {
+ return;
+ }
+ }
+ *cur = inode;
+}
+
+static inode_t *fs_cache_get_inode (inode_t *parent,
+ const unsigned char *name)
+{
+ inode_t *cur, *rec;
+ int dec;
+
+ FS_DPRINTF("Look for '%s' into '%s' cache\n", name, parent->name);
+ if (name == NULL || parent == NULL)
+ return NULL;
+ if (name[0] == '/' && name[1] == '\0')
+ return parent->fs->root;
+ if (is_special_file(name))
+ dec = strlen(FS_SPECIAL) + 2;
+ else
+ dec = 0;
+ for (cur = parent->child; cur != NULL; cur = cur->next) {
+ if (strcmp(cur->name + dec, name + dec) == 0) {
+ cur->refcount++;
+ for (rec = parent; rec != NULL; rec = rec->parent)
+ rec->refcount++;
+ break;
+ }
+ }
+ cur = NULL;
+
+ return cur;
+}
+
+static void fs_cache_put_inode (inode_t *inode)
+{
+ void (*put_inode)(inode_t *inode);
+ inode_t *cur, **upd;
+
+ if (inode != NULL && --inode->refcount == 0) {
+ if (inode->parent == NULL)
+ return;
+ fs_cache_put_inode(inode->parent);
+ upd = &inode->parent->child;
+ for (cur = *upd; cur != NULL; cur = cur->next) {
+ if (cur == inode) {
+ (*upd) = cur->next;
+ put_inode = inode->fs->fs_ops->put_inode;
+ (*put_inode)(cur);
+ FS_DPRINTF("Free inode '%s' from '%s' cache\n",
+ inode->name, inode->parent->name);
+ free(cur);
+ return;
+ }
+ upd = &cur;
+ }
+ FS_ERROR("didn't find inode in list !\n");
+ }
+}
+
+static inode_t *fs_get_inode (inode_t *parent, const unsigned char *name)
+{
+ inode_t *(*get_inode)(inode_t *parent, const unsigned char *name);
+ inode_t *cur;
+
+ if (parent == NULL) {
+ FS_ERROR("Invalide inode '%s' (NULL)\n", name);
+ return NULL;
+ } else {
+ if (fs_inode_get_type(parent) != INODE_TYPE_DIR) {
+ FS_ERROR("Try to recurse in a non-directory inode (%d)\n",
+ parent->flags);
+ return NULL;
+ }
+ }
+ if (is_special_file(name)) {
+ int type;
+ /* Special files */
+ FS_DPRINTF("look for special file '%s'\n",
+ name + strlen(FS_SPECIAL) + 2);
+ type = special_file_get_type(name + strlen(FS_SPECIAL) + 2);
+ if (type == FILE_UNKNOWN) {
+ FS_ERROR("Unknown special file '%s'\n",
+ name + strlen(FS_SPECIAL) + 2);
+ return NULL;
+ }
+ cur = (*parent->fs->fs_ops->get_special_inode)(parent->fs, type);
+ FS_DPRINTF("boot file: %p '%s' %p boot dir: %p '%s' %p\n",
+ parent->fs->bootfile, parent->fs->bootfile->name,
+ &parent->fs->bootfile,
+ parent->fs->bootdir, parent->fs->bootdir->name,
+ &parent->fs->bootdir);
+ switch (type) {
+ case FILE_ROOT:
+ parent->fs->root = cur;
+ cur->parent = NULL;
+ cur->fs = parent->fs;
+ cur->name = strdup("");
+ return cur;
+ case FILE_BOOT:
+ parent->fs->bootfile = cur;
+ break;
+ case FILE_BOOTDIR:
+ parent->fs->bootdir = cur;
+ break;
+ }
+#if 0
+ parent = cur->parent;
+#else
+ cur->fs = parent->fs;
+ return cur;
+#endif
+ } else {
+ FS_DPRINTF("look for file '%s' in %p '%s'\n", name, parent,
+ parent->name);
+ DPRINTF("look for file '%s' in %p '%s'\n", name, parent,
+ parent->name);
+ cur = fs_cache_get_inode(parent, name);
+ if (cur != NULL) {
+ FS_DPRINTF("found inode '%s' %p in cache\n", name, cur);
+ DPRINTF("found inode '%s' %p in cache\n", name, cur);
+ return cur;
+ }
+ get_inode = parent->fs->fs_ops->get_inode;
+ cur = (*get_inode)(parent, name);
+ cur->name = strdup(name);
+ }
+ if (cur != NULL) {
+ cur->parent = parent;
+ cur->fs = parent->fs;
+ fs_cache_add_inode(parent, cur);
+ FS_DPRINTF("Inode '%s' in '%s': %d blocs size %d %d\n",
+ name, parent->name, cur->nb_blocs, cur->size.bloc,
+ cur->size.offset);
+ DPRINTF("Inode '%s' in '%s': %d blocs size %d %d\n",
+ name, parent->name, cur->nb_blocs, cur->size.bloc,
+ cur->size.offset);
+ } else {
+ FS_ERROR("Inode '%s' not found in '%s'\n", name, parent->name);
+ }
+
+ return cur;
+}
+
+static inline void fs_put_inode (inode_t *inode)
+{
+ fs_cache_put_inode(inode);
+}
+
+static inode_t *_fs_walk (inode_t *parent, const unsigned char *name)
+{
+ unsigned char tmpname[MAXNAME_LEN], *sl;
+ inode_t *new, *subdir;
+
+ FS_DPRINTF("'%s' %p\n", name, parent);
+ DPRINTF("'%s' %p\n", name, parent);
+ for (; *name == '/'; name++)
+ continue;
+ DPRINTF("'%s' %p\n", name, parent);
+ strcpy(tmpname, name);
+ sl = strchr(tmpname, '/');
+ if (sl != NULL) {
+ *sl = '\0';
+ subdir = fs_get_inode(parent, tmpname);
+ if (subdir == NULL)
+ return NULL;
+ new = _fs_walk(subdir, sl + 1);
+ } else {
+ new = fs_get_inode(parent, tmpname);
+ }
+
+ return new;
+}
+
+static inode_t *fs_walk (inode_t *parent, const unsigned char *name)
+{
+ unsigned char tmpname[MAXNAME_LEN];
+ int len;
+
+ FS_DPRINTF("'%s' %p\n", name, parent);
+ DPRINTF("'%s' %p %p\n", name, parent, parent->fs->root);
+ len = strlen(name);
+ memcpy(tmpname, name, len + 1);
+ if (tmpname[len - 1] == '/')
+ tmpname[--len] = '\0';
+ if (parent == parent->fs->root && tmpname[0] == '\0')
+ return parent->fs->root;
+
+ return _fs_walk(parent, tmpname);
+}
+
+static unsigned char *fs_inode_get_path (inode_t *inode)
+{
+ unsigned char tmpname[MAXNAME_LEN], *pname;
+ int len;
+ inode_t *parent;
+
+ parent = inode->parent;
+ if (parent == NULL || (inode->name[0] == '/' && inode->name[1] == '\0')) {
+ FS_DPRINTF("Reached root node '/'\n");
+ return strdup("/");
+ }
+ FS_DPRINTF("Recurse to root '%s'...\n", inode->name);
+ pname = fs_inode_get_path(parent);
+ FS_DPRINTF("'%s' '%s'\n", pname, inode->name);
+ len = strlen(pname);
+ memcpy(tmpname, pname, len);
+ if (tmpname[len - 1] != '/')
+ tmpname[len++] = '/';
+ strcpy(tmpname + len, inode->name);
+ free(pname);
+ FS_DPRINTF(" => '%s'\n", tmpname);
+
+ return strdup(tmpname);
+}
+
+static inline uint32_t fs_map_bloc (inode_t *inode, uint32_t bloc)
+{
+ FS_DPRINTF("%s: inode %p bloc %d %p %p %p\n", __func__, inode, bloc,
+ inode->fs, inode->fs->fs_ops, inode->fs->fs_ops->map_bloc);
+ return (*inode->fs->fs_ops->map_bloc)(inode, bloc);
+}
+
+fs_t *fs_probe (part_t *part, int set_raw)
+{
+ fs_t *new;
+ inode_t fake_inode;
+ fs_ops_t *fs_ops = NULL;
+ unsigned char *name = NULL;
+ void *private = NULL;
+ uint32_t size = 0;
+ int type = FS_TYPE_UNKNOWN;
+
+ FS_DPRINTF("\n");
+ if (set_raw == 2) {
+ DPRINTF("Check raw only\n");
+ goto raw_only;
+ }
+ DPRINTF("Probe ext2\n");
+ type = fs_ext2_probe(part, &size, &fs_ops, &name, &private);
+ if (type == FS_TYPE_UNKNOWN) {
+ DPRINTF("Probe isofs\n");
+ type = fs_isofs_probe(part, &size, &fs_ops, &name, &private);
+ if (type == FS_TYPE_UNKNOWN) {
+ DPRINTF("Probe HFS\n");
+ type = fs_hfs_probe(part, &size, &fs_ops, &name, &private);
+ if (set_raw) {
+ DPRINTF("Probe raw\n");
+ raw_only:
+ type = fs_raw_probe(part, &size, &fs_ops, &name, &private);
+ }
+ if (type == FS_TYPE_UNKNOWN) {
+ FS_ERROR("FS not identified\n");
+ return NULL;
+ }
+ }
+ }
+ if (fs_ops == NULL || size == 0) {
+ FS_ERROR("Missing param: %p %d\n", fs_ops, size);
+ return NULL;
+ }
+ new = malloc(sizeof(fs_t));
+ if (new == NULL)
+ return NULL;
+ new->type = type;
+ new->part = part;
+ new->size = size;
+ new->fs_ops = fs_ops;
+ new->name = name;
+ new->private = private;
+ /* Get root inode */
+ memset(&fake_inode, 0, sizeof(inode_t));
+ fake_inode.name = "fake_root";
+ fake_inode.fs = new;
+ fake_inode.refcount = 1;
+ fs_get_inode(&fake_inode, "\0" FS_SPECIAL "\0root");
+ if (new->root == NULL) {
+ FS_ERROR("Didn't find root inode\n");
+ free(new);
+ return NULL;
+ }
+ FS_DPRINTF("fs: %p root: %p root fs: %p\n", new, new->root, new->root->fs);
+ FS_DPRINTF("OK\n");
+
+ return new;
+}
+
+dir_t *fs_opendir (fs_t *fs, const unsigned char *name)
+{
+ inode_t *inode;
+ dir_t *new;
+
+ FS_DPRINTF("'%s'\n", name);
+ inode = fs_walk(fs->root, name);
+ if (inode == NULL)
+ return NULL;
+ new = malloc(sizeof(dir_t));
+ new->inode = inode;
+
+ return new;
+}
+
+dirent_t *fs_readdir (dir_t *dir)
+{
+ void (*put_inode)(inode_t *inode);
+ inode_t *inode;
+
+ inode = fs_get_inode(dir->inode, NULL);
+ if (inode == NULL)
+ return NULL;
+ if (dir->cur == NULL) {
+ dir->cur = malloc(sizeof(dirent_t));
+ dir->cur->dir = dir;
+ } else {
+ put_inode = dir->inode->fs->fs_ops->put_inode;
+ (*put_inode)(dir->cur->inode);
+ }
+ dir->cur->inode = inode;
+ dir->cur->dname = inode->name;
+
+ return dir->cur;
+}
+
+unsigned char *fs_get_path (dirent_t *dirent)
+{
+ return fs_inode_get_path(dirent->inode);
+}
+
+void fs_closedir (dir_t *dir)
+{
+ void (*put_inode)(inode_t *inode);
+
+ if (dir->cur != NULL) {
+ put_inode = dir->inode->fs->fs_ops->put_inode;
+ (*put_inode)(dir->cur->inode);
+ free(dir->cur);
+ }
+ free(dir);
+}
+
+inode_t *fs_open (fs_t *fs, const unsigned char *name)
+{
+ inode_t *inode;
+
+ FS_DPRINTF("'%s'\n", name);
+ inode = fs_walk(fs->root, name);
+ if (inode != NULL)
+ fs_seek(inode, 0, 0);
+
+ return inode;
+}
+
+int fs_seek (inode_t *inode, uint32_t bloc, uint32_t pos)
+{
+ if (inode == NULL || inode->fs == NULL) {
+ ERROR("%s: no inode / fs ! %p %p\n", __func__, inode,
+ inode == NULL ? NULL : inode->fs);
+ return -1;
+ }
+ FS_DPRINTF("%08x %08x\n", bloc, pos);
+ if (part_seek(inode->fs->part, fs_map_bloc(inode, bloc), pos) == -1)
+ return -1;
+ inode->vbloc = bloc;
+ inode->vpos = pos;
+
+ return 0;
+}
+
+int fs_read (inode_t *inode, void *buffer, int len)
+{
+ uint32_t bsize, total;
+ int done, tmp;
+
+ bsize = part_blocsize(inode->fs->part);
+ total = 0;
+ if (fs_seek(inode, inode->vbloc, inode->vpos) < 0)
+ return -1;
+ for (; len != 0; len -= done) {
+ tmp = bsize - inode->vpos;
+ if (len < tmp)
+ tmp = len;
+ done = part_read(inode->fs->part, buffer, tmp);
+ if (done < 0)
+ return -1;
+ inode->vpos += done;
+ if (inode->vpos >= bsize) {
+ inode->vbloc++;
+ inode->vpos -= bsize;
+ }
+ buffer += done;
+ total += done;
+ }
+
+ return total;
+}
+
+int fs_write (inode_t *inode, const void *buffer, unused int len)
+{
+ uint32_t bsize, total;
+ int done, tmp;
+
+ bsize = part_blocsize(inode->fs->part);
+ total = 0;
+ for (; len != 0; len -= done) {
+ tmp = bsize - inode->vpos;
+ if (len < tmp)
+ tmp = len;
+ done = part_write(inode->fs->part, buffer, tmp);
+ if (done < 0)
+ return -1;
+ inode->vpos += done;
+ if (inode->vpos >= bsize) {
+ inode->vbloc++;
+ inode->vpos -= bsize;
+ if (fs_seek(inode, inode->vbloc, inode->vpos) < 0)
+ return -1;
+ }
+ buffer += done;
+ total += done;
+ }
+
+ return total;
+}
+
+void fs_close (inode_t *inode)
+{
+ fs_put_inode(inode);
+}
+
+uint32_t fs_inode_get_type (inode_t *inode)
+{
+ return inode->flags & INODE_TYPE_MASK;
+}
+
+uint32_t fs_inode_get_flags (inode_t *inode)
+{
+ return inode->flags & INODE_FLAG_MASK;
+}
+
+uint32_t fs_inode_get_size (inode_t *inode)
+{
+ DPRINTF("%s: (%d * %d) + %d\n", __func__, inode->size.bloc,
+ part_blocsize(inode->fs->part), inode->size.offset);
+ return (inode->size.bloc * part_blocsize(inode->fs->part)) +
+ inode->size.offset;
+}
+
+part_t *fs_part (fs_t *fs)
+{
+ return fs->part;
+}
+
+uint32_t fs_get_type (fs_t *fs)
+{
+ return fs->type;
+}
+
+part_t *fs_inode_get_part (inode_t *inode)
+{
+ return inode->fs->part;
+}
+
+inode_t *fs_get_bootdir (fs_t *fs)
+{
+ FS_DPRINTF("fs: %p root: %p root fs: %p\n", fs, fs->root, fs->root->fs);
+ if (fs->bootdir == NULL) {
+ fs->bootdir = fs_get_inode(fs->root, "\0" FS_SPECIAL "\0bootdir");
+ }
+ FS_DPRINTF("fs: %p root: %p root fs: %p\n", fs, fs->root, fs->root->fs);
+ FS_DPRINTF("boot file: %p '%s' %p boot dir: %p '%s' %p\n",
+ fs->bootfile, fs->bootfile->name, &fs->bootfile,
+ fs->bootdir, fs->bootdir->name, &fs->bootdir);
+
+ return fs->bootdir;
+}
+unsigned char *fs_get_boot_dirname (fs_t *fs)
+{
+ if (fs->bootdir == NULL) {
+ fs_get_bootdir(fs);
+ if (fs->bootdir == NULL)
+ return NULL;
+ }
+ FS_DPRINTF("boot file: %p '%s' boot dir: %p '%s'\n",
+ fs->bootfile, fs->bootfile->name,
+ fs->bootdir, fs->bootdir->name);
+
+ return fs_inode_get_path(fs->bootdir);
+}
+
+inode_t *fs_get_bootfile (fs_t *fs)
+{
+ FS_DPRINTF("fs: %p root: %p root fs: %p\n", fs, fs->root, fs->root->fs);
+ FS_DPRINTF("boot file: %p '%s' %p boot dir: %p '%s' %p\n",
+ fs->bootfile, fs->bootfile->name, &fs->bootfile,
+ fs->bootdir, fs->bootdir->name, &fs->bootdir);
+ if (fs->bootfile == NULL) {
+ if (fs->bootdir == NULL)
+ fs_get_bootdir(fs);
+ if (fs->bootdir == NULL)
+ return NULL;
+ fs->bootfile = fs_get_inode(fs->bootdir, "\0" FS_SPECIAL "\0boot");
+ }
+ FS_DPRINTF("fs: %p root: %p root fs: %p\n", fs, fs->root, fs->root->fs);
+ FS_DPRINTF("boot file: %p '%s' %p boot dir: %p '%s' %p\n",
+ fs->bootfile, fs->bootfile->name, &fs->bootfile,
+ fs->bootdir, fs->bootdir->name, &fs->bootdir);
+
+ return fs->bootfile;
+}