From 9db8ed3312a2137b12cb0cb15c4267ead96dbf8b Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 5 Jul 2007 18:01:37 +0200 Subject: [PATCH] svn_rev_261 WIP Filesystem support --- fs/cramfs/cramfs.c | 124 +++++++++++--------- fs/fs.c | 250 ++++++++++++++++++++++++++++++---------- fs/ramfs.c | 282 +++++++++++++++++++++++++++++++++++++++++++++ include/driver.h | 12 +- include/fs.h | 31 ++++- lib_generic/misc.c | 2 +- 6 files changed, 582 insertions(+), 119 deletions(-) create mode 100644 fs/ramfs.c diff --git a/fs/cramfs/cramfs.c b/fs/cramfs/cramfs.c index a5f5a5842..ee6341b9d 100644 --- a/fs/cramfs/cramfs.c +++ b/fs/cramfs/cramfs.c @@ -209,11 +209,11 @@ int cramfs_load (char *loadoffset, struct device_d *dev, const char *filename) (unsigned long) loadoffset); } -static int cramfs_list_inode (struct device_d *dev, unsigned long offset) +static int cramfs_fill_dirent (struct device_d *dev, unsigned long offset, struct dirent *d) { struct cramfs_inode *inode = (struct cramfs_inode *) (dev->map_base + offset); - char *name, str[20]; + char *name; int namelen, nextoff; /* @@ -233,80 +233,89 @@ static int cramfs_list_inode (struct device_d *dev, unsigned long offset) namelen--; } - printf (" %s %8d %*.*s", mkmodestr (CRAMFS_16 (inode->mode), str), - CRAMFS_24 (inode->size), namelen, namelen, name); - - if ((CRAMFS_16 (inode->mode) & S_IFMT) == S_IFLNK) { - /* symbolic link. - * Unpack the link target, trusting in the inode's size field. - */ - unsigned long size = CRAMFS_24 (inode->size); - char *link = malloc (size); - - if (link != NULL && cramfs_uncompress (dev->map_base, offset, - (unsigned long) link) - == size) - printf (" -> %*.*s\n", (int) size, (int) size, link); - else - printf (" [Error reading link]\n"); - if (link) - free (link); - } else - printf ("\n"); + d->mode = CRAMFS_16 (inode->mode); + d->size = CRAMFS_24 (inode->size); + memset(d->name, 0, 256); + strncpy(d->name, name, namelen); return nextoff; } -int cramfs_ls (struct device_d *dev, const char *filename) -{ +struct cramfs_dir { struct cramfs_inode *inode; - unsigned long inodeoffset = 0, nextoffset; unsigned long offset, size; + unsigned long inodeoffset; + struct dir dir; +}; + +struct dir* cramfs_opendir(struct device_d *_dev, const char *filename) +{ char *f; + struct fs_device_d *fsdev = _dev->type_data; + struct device_d *dev = fsdev->parent; - printf("cramfs_ls: %s\n", filename); - - if (cramfs_read_super (dev)) - return -1; + struct cramfs_dir *dir = malloc(sizeof(struct cramfs_dir)); + memset(dir, 0, sizeof(struct cramfs_dir)); + dir->dir.priv = dir; if (strlen (filename) == 0 || !strcmp (filename, "/")) { /* Root directory. Use root inode in super block */ - offset = CRAMFS_GET_OFFSET (&(super.root)) << 2; - size = CRAMFS_24 (super.root.size); + dir->offset = CRAMFS_GET_OFFSET (&(super.root)) << 2; + dir->size = CRAMFS_24 (super.root.size); } else { f = strdup(filename); /* Resolve the path */ - offset = cramfs_resolve (dev->map_base, + dir->offset = cramfs_resolve (dev->map_base, CRAMFS_GET_OFFSET (&(super.root)) << 2, CRAMFS_24 (super.root.size), 1, strtok (f, "/")); free(f); - if (offset <= 0) - return offset; + if (dir->offset <= 0) + goto err_free; /* Resolving was successful. Examine the inode */ - inode = (struct cramfs_inode *) (dev->map_base + offset); - if (!S_ISDIR (CRAMFS_16 (inode->mode))) { - /* It's not a directory - list it, and that's that */ - return (cramfs_list_inode (dev, offset) > 0); + dir->inode = (struct cramfs_inode *) (dev->map_base + dir->offset); + if (!S_ISDIR (CRAMFS_16 (dir->inode->mode))) { + /* It's not a directory */ + goto err_free; } /* It's a directory. List files within */ - offset = CRAMFS_GET_OFFSET (inode) << 2; - size = CRAMFS_24 (inode->size); + dir->offset = CRAMFS_GET_OFFSET (dir->inode) << 2; + dir->size = CRAMFS_24 (dir->inode->size); } + return &dir->dir; + +err_free: + free(dir); + return NULL; +} + +struct dirent* cramfs_readdir(struct device_d *_dev, struct dir *_dir) +{ + struct fs_device_d *fsdev = _dev->type_data; + struct device_d *dev = fsdev->parent; + struct cramfs_dir *dir = _dir->priv; + unsigned long nextoffset; + /* List the given directory */ - while (inodeoffset < size) { - inode = (struct cramfs_inode *) (dev->map_base + offset + - inodeoffset); + if (dir->inodeoffset < dir->size) { + dir->inode = (struct cramfs_inode *) (dev->map_base + dir->offset + + dir->inodeoffset); - nextoffset = cramfs_list_inode (dev, offset + inodeoffset); - if (nextoffset == 0) - break; - inodeoffset += sizeof (struct cramfs_inode) + nextoffset; + nextoffset = cramfs_fill_dirent (dev, dir->offset + dir->inodeoffset, &_dir->d); + + dir->inodeoffset += sizeof (struct cramfs_inode) + nextoffset; + return &_dir->d; } + return NULL; +} +int cramfs_closedir(struct device_d *dev, struct dir *_dir) +{ + struct cramfs_dir *dir = _dir->priv; + free(dir); return 0; } @@ -338,7 +347,14 @@ int cramfs_info (struct device_d *dev) int cramfs_probe(struct device_d *dev) { - if (cramfs_read_super (dev)) { + struct fs_device_d *fsdev; + + printf("%s: dev: %p\n",__FUNCTION__, dev); + + fsdev = dev->type_data; + printf("%s: fsdev: %p\n",__FUNCTION__, fsdev); + + if (cramfs_read_super (fsdev->parent)) { printf("no valid cramfs found on %s\n",dev->id); return -EINVAL; } @@ -347,19 +363,21 @@ int cramfs_probe(struct device_d *dev) } static struct fs_driver_d cramfs_driver = { - .type = FS_TYPE_CRAMFS, - .probe = cramfs_probe, - .ls = cramfs_ls, + .type = FS_TYPE_CRAMFS, + .opendir = cramfs_opendir, + .readdir = cramfs_readdir, + .closedir = cramfs_closedir, .drv = { .type = DEVICE_TYPE_FS, + .probe = cramfs_probe, .name = "cramfs", - .driver_data = &cramfs_driver, + .type_data = &cramfs_driver, } }; int cramfs_init(void) { - return register_fs_driver(&cramfs_driver); + return register_driver(&cramfs_driver.drv); } device_initcall(cramfs_init); diff --git a/fs/fs.c b/fs/fs.c index 824645bca..ec2373c97 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -37,26 +37,48 @@ char *mkmodestr(unsigned long mode, char *str) return str; } -static int fs_probe(struct device_d *dev) -{ - struct fs_device_d *fs_dev = dev->platform_data; +struct mtab_entry { + char path[PATH_MAX]; + struct mtab_entry *next; + struct device_d *dev; +}; - return fs_dev->driver->probe(fs_dev->parent); +static struct mtab_entry *mtab; + +struct mtab_entry *get_mtab_entry_by_path(const char *_path) +{ + struct mtab_entry *match = NULL, *e = mtab; + char *path, *tok; + + if (*_path != '/') + return NULL; + + path = strdup(_path); + + tok = strchr(path + 1, '/'); + if (tok) + *tok = 0; + + while (e) { + if (!strcmp(path, e->path)) { + match = e; + break; + } + e = e->next; + } + + free(path); + + return match ? match : mtab; } -int register_fs_driver(struct fs_driver_d *new_fs_drv) +int mount (struct device_d *dev, char *fsname, char *path) { - new_fs_drv->drv.probe = fs_probe; - new_fs_drv->drv.driver_data = new_fs_drv; - - return register_driver(&new_fs_drv->drv); -} - -int register_filesystem(struct device_d *dev, char *fsname) -{ - struct fs_device_d *new_fs_dev; struct driver_d *drv; struct fs_driver_d *fs_drv; + struct mtab_entry *entry; + struct fs_device_d *fsdev; + int ret; drv = get_driver_by_name(fsname); if (!drv) { @@ -69,72 +91,146 @@ int register_filesystem(struct device_d *dev, char *fsname) return -EINVAL; } - new_fs_dev = malloc(sizeof(struct fs_device_d)); + /* check if path exists */ + /* TODO */ - fs_drv = drv->driver_data; + fs_drv = drv->type_data; + printf("mount: fs_drv: %p\n", fs_drv); - new_fs_dev->driver = fs_drv; - new_fs_dev->parent = dev; - new_fs_dev->dev.platform_data = new_fs_dev; - new_fs_dev->dev.type = DEVICE_TYPE_FS; - sprintf(new_fs_dev->dev.name, "%s", fsname); - sprintf(new_fs_dev->dev.id, "%s", "fs0"); - - register_device(&new_fs_dev->dev); - - if (!new_fs_dev->dev.driver) { - unregister_device(&new_fs_dev->dev); - return -ENODEV; + if (fs_drv->flags & FS_DRIVER_NO_DEV) { + dev = malloc(sizeof(struct device_d)); + memset(dev, 0, sizeof(struct device_d)); + sprintf(dev->name, "%s", fsname); + dev->type = DEVICE_TYPE_FS; + if ((ret = register_device(dev))) { + free(dev); + return ret; + } + if (!dev->driver) { + /* driver didn't accept the device. Bail out */ + free(dev); + return -EINVAL; + } + } else { + fsdev = malloc(sizeof(struct fs_device_d)); + memset(fsdev, 0, sizeof(struct fs_device_d)); + fsdev->parent = dev; + sprintf(fsdev->dev.name, "%s", fsname); + fsdev->dev.type = DEVICE_TYPE_FS; + fsdev->dev.type_data = fsdev; + if ((ret = register_device(&fsdev->dev))) { + free(fsdev); + return ret; + } + if (!fsdev->dev.driver) { + /* driver didn't accept the device. Bail out */ + free(fsdev); + return -EINVAL; + } + dev = &fsdev->dev; } + /* add mtab entry */ + entry = malloc(sizeof(struct mtab_entry)); + sprintf(entry->path, "%s", path); + entry->dev = dev; + entry->next = NULL; + + if (!mtab) + mtab = entry; + else { + struct mtab_entry *e = mtab; + while (e->next) + e = e->next; + e->next = entry; + } + + printf("mount: mtab->dev: %p\n", mtab->dev); + return 0; } -int ls(struct device_d *dev, const char *filename) -{ - struct fs_device_d *fs_dev; - - if (!dev || dev->type != DEVICE_TYPE_FS || !dev->driver) - return -ENODEV; - - fs_dev = dev->platform_data; - - return fs_dev->driver->ls(fs_dev->parent, filename); -} - -/* addfs */ -int do_addfs ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +int do_mount (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { struct device_d *dev; int ret = 0; - if (argc != 3) { + if (argc != 4) { printf ("Usage:\n%s\n", cmdtp->usage); return 1; } dev = get_device_by_id(argv[1]); - if (!dev) { - printf("no such device: %s\n", argv[1]); - return -ENODEV; - } - if ((ret = register_filesystem(dev, argv[2]))) { - perror("register_device", ret); + if ((ret = mount(dev, argv[2], argv[3]))) { + perror("mount", ret); return 1; } return 0; } -int do_ls ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +struct dir *opendir(const char *pathname) { struct device_d *dev; - char *endp; + struct fs_driver_d *fsdrv; + struct dir *dir; + struct mtab_entry *e; + + e = get_mtab_entry_by_path(pathname); + if (e != mtab) + pathname += strlen(e->path); + + dev = e->dev; +// printf("opendir: dev: %p\n",dev); + + fsdrv = (struct fs_driver_d *)dev->driver->type_data; +// printf("opendir: fsdrv: %p\n",fsdrv); + + dir = fsdrv->opendir(dev, pathname); + if (dir) { + dir->dev = dev; + dir->fsdrv = fsdrv; + } + + return dir; +} + +struct dirent *readdir(struct dir *dir) +{ + return dir->fsdrv->readdir(dir->dev, dir); +} + +int closedir(struct dir *dir) +{ + return dir->fsdrv->closedir(dir->dev, dir); +} + +static int ls(const char *path) +{ + struct dir *dir; + struct dirent *d; + char modestr[11]; + + dir = opendir(path); + if (!dir) + return -ENOENT; + + while ((d = readdir(dir))) { + unsigned long namelen = strlen(d->name); + mkmodestr(d->mode, modestr); + printf("%s %8d %*.*s\n",modestr, d->size, namelen, namelen, d->name); + } + + closedir(dir); + + return 0; +} + +int do_ls (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ int ret; - dev = device_from_spec_str(argv[1], &endp); - - ret = ls(dev, endp); + ret = ls(argv[1]); if (ret) { perror("ls", ret); return 1; @@ -146,13 +242,51 @@ int do_ls ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) U_BOOT_CMD( ls, 2, 0, do_ls, "ls - list a file or directory\n", - " list files on device" + " list files on path" +); + +int mkdir (const char *pathname) +{ + struct fs_driver_d *fsdrv; + struct device_d *dev; + struct mtab_entry *e; + + e = get_mtab_entry_by_path(pathname); + if (e != mtab) + pathname += strlen(e->path); + + dev = e->dev; + + fsdrv = (struct fs_driver_d *)dev->driver->type_data; + + if (fsdrv->mkdir) + return fsdrv->mkdir(dev, pathname); + return -EROFS; +} + +int do_mkdir (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int ret; + + ret = mkdir(argv[1]); + if (ret) { + perror("ls", ret); + return 1; + } + + return 0; +} + +U_BOOT_CMD( + mkdir, 2, 0, do_mkdir, + "mkdir - create a new directory\n", + "" ); U_BOOT_CMD( - addfs, 3, 0, do_addfs, - "addfs - add a filesystem to a device\n", - " add a filesystem of type 'type' on the given device" + mount, 4, 0, do_mount, + "mount - mount a filesystem to a device\n", + " add a filesystem of type 'type' on the given device" ); #if 0 U_BOOT_CMD( diff --git a/fs/ramfs.c b/fs/ramfs.c new file mode 100644 index 000000000..7d3d2b673 --- /dev/null +++ b/fs/ramfs.c @@ -0,0 +1,282 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct handle_d { + int (*read)(struct handle_d *, ...); + int (*write)(struct handle_d *, ...); + + struct device_d *dev; +}; + +struct data_d { + char *data; + ulong size; + struct data_d *next; +}; + +struct node_d { + char *name; + struct node_d *next; + struct node_d *child; + ulong mode; + + struct handle_d *handle; + + struct data_d *data; +}; + +struct filesystem_d { + int (*create)(struct device_d *dev, const char *pathname, mode_t mode); + struct handle_d *(*open)(struct device_d *dev, const char *pathname, mode_t mode); + int (*remove)(struct device_d *dev, const char *pathname); + int (*mknod)(struct device_d *dev, const char *pathname, struct handle_d *handle); + int (*ls)(struct device_d *dev, const char *pathname); +}; + +int create(const char *pathname, ulong mode); +struct handle_d *open(const char *pathname, ulong type); +int remove(const char *pathname); +int mknod(const char *pathname, struct handle_d *handle); + +struct ramfs_priv { + struct node_d root; +}; + +/* ---------------------------------------------------------------*/ +struct node_d * __lookup(struct node_d *node, const char *name) +{ +// printf("__lookup: %s in %p\n",name, node); + node = node->child; + if (!node || node->mode != S_IFDIR) + return NULL; + + while (node) { + if (!strcmp(node->name, name)) + return node; + node = node->next; + } + return NULL; +} + +struct node_d* rlookup(struct node_d *node, const char *path) +{ + static char *buf; + char *part; +//printf("rlookup %s in %p\n",path, node); + buf = strdup(path); + + part = strtok(buf, "/"); + if (!part) + goto out; + + do { + node = __lookup(node, part); + if (!node) + goto out; + part = strtok(NULL, "/"); + } while(part); + +out: + free(buf); + return node; +} + +/* + * - Remove all multiple slashes + * - Remove trailing slashes (except path consists of only + * a single slash) + * - TODO: illegal characters? + */ +void normalise_path(char *path) +{ + char *out = path, *in = path; + + while(*in) { + if(*in == '/') { + *out++ = *in++; + while(*in == '/') + in++; + } else { + *out++ = *in++; + } + } + + /* + * Remove trailing slash, but only if + * we were given more than a single slash + */ + if (out > path + 1 && *(out - 1) == '/') + *(out - 1) = 0; + + *out = 0; +} + +int node_add_child(struct node_d *node, const char *filename, ulong mode) +{ + struct node_d *new_node = malloc(sizeof(struct node_d)); + memset(new_node, 0, sizeof(struct node_d)); + new_node->name = strdup(filename); + new_node->mode = mode; + +// printf("node_add_child: %p -> %p\n", node, new_node); + if (!node->child) { + node->child = new_node; + return 0; + } + + node = node->child; + + while (node->next) + node = node->next; + + node->next = new_node; + return 0; +} + +/* ---------------------------------------------------------------*/ + +int ramfs_create(struct device_d *dev, const char *pathname, ulong mode) +{ + struct ramfs_priv *priv = dev->priv; + char *path = strdup(pathname); + char *file; + struct node_d *node; + + normalise_path(path); + if (*path == '/') + path++; + +// printf("after normalise: %s\n",path); + + if ((file = strrchr(path, '/'))) { + *file = 0; + file++; + node = rlookup(&priv->root, path); + if (!node) + return -ENOENT; + } else { + file = path; + node = &priv->root; + } + + if(__lookup(node, file)) + return -EEXIST; + + return node_add_child(node, file, mode); +} + +int ramfs_mkdir(struct device_d *dev, const char *pathname) +{ + return ramfs_create(dev, pathname, S_IFDIR); +} + +int ramfs_probe(struct device_d *dev) +{ + struct ramfs_priv *priv = malloc(sizeof(struct ramfs_priv)); + + printf("ramfs_probe\n"); + memset(priv, 0, sizeof(struct ramfs_priv)); + + dev->priv = priv; + + priv->root.name = "/"; + priv->root.mode = S_IFDIR; + + return 0; +} + +static struct handle_d *ramfs_open(struct device_d *dev, const char *pathname) +{ + return NULL; +} + +struct dir* ramfs_opendir(struct device_d *dev, const char *pathname) +{ + struct dir *dir; + struct ramfs_priv *priv = dev->priv; + struct node_d *node = rlookup(&priv->root, pathname); + + if (!node) + return NULL; + + if (node->mode != S_IFDIR) + return NULL; + + dir = malloc(sizeof(struct dir)); + if (!dir) + return NULL; + + dir->node = node->child; + + return dir; +} + +struct dirent* ramfs_readdir(struct device_d *dev, struct dir *dir) +{ + if (dir->node) { + strcpy(dir->d.name, dir->node->name); + dir->d.mode = dir->node->mode; + dir->node = dir->node->next; + return &dir->d; + } + return NULL; +} + +int ramfs_closedir(struct device_d *dev, struct dir *dir) +{ + free(dir); + return 0; +} + +static struct fs_driver_d ramfs_driver = { + .type = FS_TYPE_RAMFS, + .create = ramfs_create, + .open = ramfs_open, + + .mkdir = ramfs_mkdir, + + .opendir = ramfs_opendir, + .readdir = ramfs_readdir, + .closedir = ramfs_closedir, + + .flags = FS_DRIVER_NO_DEV, + .drv = { + .type = DEVICE_TYPE_FS, + .probe = ramfs_probe, + .name = "ramfs", + .type_data = &ramfs_driver, + } +}; + +int ramfs_init(void) +{ + return register_driver(&ramfs_driver.drv); +} + +device_initcall(ramfs_init); + +/* --------- Testing --------- */ + +static int do_create ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ +// int ret; + + printf("create %s\n",argv[1]); +// ret = ramfs_create(&ramfs_device, argv[1], S_IFDIR); +// perror("create", ret); + return 0; +} + +U_BOOT_CMD( + create, 2, 0, do_create, + "ls - list a file or directory\n", + " list files on device" +); + diff --git a/include/driver.h b/include/driver.h index 0936462c9..c7ede1acc 100644 --- a/include/driver.h +++ b/include/driver.h @@ -26,8 +26,11 @@ struct device_d { */ unsigned long map_base; - void *platform_data; - void *priv; + void *platform_data; /* board specific information about this device */ + void *priv; /* data private to the driver */ + void *type_data; /* In case this device is a specific device, this pointer + * points to the type specific device, i.e. eth_device + */ /* The driver for this device */ struct driver_d *driver; @@ -55,9 +58,10 @@ struct driver_d { int (*get) (struct device_d*, struct param_d *); int (*set) (struct device_d*, struct param_d *, value_t val); - void *driver_data; unsigned long type; - void *type_data; + void *type_data; /* In case this driver is of a specific type, i.e. a filesystem + * driver, this pointer points to the corresponding data struct + */ }; #define RW_SIZE(x) (x) diff --git a/include/fs.h b/include/fs.h index 4fd69acaf..c600a9dd9 100644 --- a/include/fs.h +++ b/include/fs.h @@ -6,23 +6,46 @@ #define FS_TYPE_CRAMFS 1 #define FS_TYPE_RAMFS 2 +#define PATH_MAX 1024 /* include/linux/limits.h */ + struct partition; +struct node_d; + +struct dirent { + unsigned long mode; + unsigned long size; + char name[256]; +}; + +struct dir { + struct device_d *dev; + struct fs_driver_d *fsdrv; + struct node_d *node; + struct dirent d; + void *priv; /* private data for the fs driver */ +}; + +#define FS_DRIVER_NO_DEV 1 struct fs_driver_d { ulong type; char *name; - int (*ls) (struct device_d *dev, const char *filename); int (*load) (char *dst, struct device_d *dev, const char *filename); int (*probe) (struct device_d *dev); int (*create)(struct device_d *dev, const char *pathname, ulong type); + int (*mkdir)(struct device_d *dev, const char *pathname); struct handle_d *(*open)(struct device_d *dev, const char *pathname); + struct dir* (*opendir)(struct device_d *dev, const char *pathname); + struct dirent* (*readdir)(struct device_d *dev, struct dir *dir); + int (*closedir)(struct device_d *dev, struct dir *dir); + struct driver_d drv; + + unsigned long flags; }; struct fs_device_d { - ulong type; - char *name; struct device_d *parent; /* the device we are associated with */ struct device_d dev; /* our own device */ @@ -34,4 +57,6 @@ int register_filesystem(struct device_d *dev, char *fsname); int register_fs_driver(struct fs_driver_d *new_fs_drv); +char *mkmodestr(unsigned long mode, char *str); + #endif /* __FS_H */ diff --git a/lib_generic/misc.c b/lib_generic/misc.c index eab6ebbc7..6df4f5c15 100644 --- a/lib_generic/misc.c +++ b/lib_generic/misc.c @@ -68,7 +68,7 @@ int register_device(struct device_d *new_device) dev = first_device; - if(get_device_by_id(new_device->id)) { + if(*new_device->id && get_device_by_id(new_device->id)) { printf("device %s already exists\n", new_device->id); return -EINVAL; }