diff options
Diffstat (limited to 'fs/ext4/ext_barebox.c')
-rw-r--r-- | fs/ext4/ext_barebox.c | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/fs/ext4/ext_barebox.c b/fs/ext4/ext_barebox.c new file mode 100644 index 0000000000..adc8f758e0 --- /dev/null +++ b/fs/ext4/ext_barebox.c @@ -0,0 +1,293 @@ +/* + * barebox ext4 support + * + * Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * 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. + * + */ + +#include <common.h> +#include <driver.h> +#include <init.h> +#include <malloc.h> +#include <fs.h> +#include <command.h> +#include <errno.h> +#include <linux/stat.h> +#include <linux/ctype.h> +#include <xfuncs.h> +#include <fcntl.h> +#include "ext4_common.h" + +int ext4fs_devread(struct ext_filesystem *fs, int __sector, int byte_offset, + int byte_len, char *buf) +{ + ssize_t size; + uint64_t sector = __sector; + + size = cdev_read(fs->cdev, buf, byte_len, sector * SECTOR_SIZE + byte_offset, 0); + if (size < 0) { + dev_err(fs->dev, "read error at sector %d: %s\n", __sector, + strerror(-size)); + return size; + } + + return 0; +} + +static int ext_open(struct device_d *dev, FILE *file, const char *filename) +{ + struct ext_filesystem *fs = dev->priv; + struct ext2fs_node *inode; + int ret; + + ret = ext4fs_open(fs->data, filename, &inode); + if (ret) + return ret; + + file->size = __le32_to_cpu(inode->inode.size); + file->inode = inode; + + return 0; +} + +static int ext_close(struct device_d *dev, FILE *f) +{ + struct ext_filesystem *fs = dev->priv; + + ext4fs_free_node(f->inode, &fs->data->diropen); + + return 0; +} + +static int ext_read(struct device_d *_dev, FILE *f, void *buf, size_t insize) +{ + return ext4fs_read_file(f->inode, f->pos, insize, buf); +} + +static loff_t ext_lseek(struct device_d *dev, FILE *f, loff_t pos) +{ + f->pos = pos; + + return f->pos; +} + +struct ext4fs_dir { + struct ext2fs_node *dirnode; + int fpos; + DIR dir; +}; + +static DIR *ext_opendir(struct device_d *dev, const char *pathname) +{ + struct ext_filesystem *fs = dev->priv; + struct ext4fs_dir *ext4_dir; + int type, ret; + + ext4_dir = xzalloc(sizeof(*ext4_dir)); + + ret = ext4fs_find_file(pathname, &fs->data->diropen, &ext4_dir->dirnode, + &type); + if (ret) { + free(ext4_dir); + return NULL; + } + + if (type != FILETYPE_DIRECTORY) + return NULL; + + ext4_dir->dir.priv = ext4_dir; + + ret = ext4fs_read_inode(ext4_dir->dirnode->data, ext4_dir->dirnode->ino, + &ext4_dir->dirnode->inode); + if (ret) { + ext4fs_free_node(ext4_dir->dirnode, &fs->data->diropen); + free(ext4_dir); + + return NULL; + } + + return &ext4_dir->dir; +} + +static struct dirent *ext_readdir(struct device_d *dev, DIR *dir) +{ + struct ext4fs_dir *ext4_dir = dir->priv; + struct ext2_dirent dirent; + struct ext2fs_node *diro = ext4_dir->dirnode; + int ret; + char *filename; + + if (ext4_dir->fpos >= __le32_to_cpu(diro->inode.size)) + return NULL; + + ret = ext4fs_read_file(diro, ext4_dir->fpos, sizeof(struct ext2_dirent), + (char *) &dirent); + if (ret < 0) + return NULL; + + if (dirent.namelen == 0) + return NULL; + + filename = xzalloc(dirent.namelen + 1); + + ret = ext4fs_read_file(diro, ext4_dir->fpos + sizeof(struct ext2_dirent), + dirent.namelen, filename); + if (ret < 0) { + free(filename); + return NULL; + } + + filename[dirent.namelen] = '\0'; + + ext4_dir->fpos += __le16_to_cpu(dirent.direntlen); + + strcpy(dir->d.d_name, filename); + + free(filename); + + return &dir->d; +} + +static int ext_closedir(struct device_d *dev, DIR *dir) +{ + struct ext_filesystem *fs = dev->priv; + struct ext4fs_dir *ext4_dir = dir->priv; + + ext4fs_free_node(ext4_dir->dirnode, &fs->data->diropen); + + free(ext4_dir); + + return 0; +} + +static int ext_stat(struct device_d *dev, const char *filename, struct stat *s) +{ + struct ext_filesystem *fs = dev->priv; + struct ext2fs_node *node; + int status, ret; + + status = ext4fs_find_file(filename, &fs->data->diropen, &node, NULL); + if (status) + return -ENOENT; + + ret = ext4fs_read_inode(node->data, node->ino, &node->inode); + if (ret) + return ret; + + s->st_size = __le32_to_cpu(node->inode.size); + s->st_mode = __le16_to_cpu(node->inode.mode); + + ext4fs_free_node(node, &fs->data->diropen); + + return 0; +} + +static int ext_readlink(struct device_d *dev, const char *pathname, + char *buf, size_t bufsiz) +{ + struct ext_filesystem *fs = dev->priv; + struct ext2fs_node *node; + char *symlink; + int ret, len, type; + + ret = ext4fs_find_file(pathname, &fs->data->diropen, &node, &type); + if (ret) + return ret; + + if (type != FILETYPE_SYMLINK) + return -EINVAL; + + symlink = ext4fs_read_symlink(node); + if (!symlink) + return -ENOENT; + + len = min(bufsiz, strlen(symlink)); + + memcpy(buf, symlink, len); + + free(symlink); + + return 0; +} + +static int ext_probe(struct device_d *dev) +{ + struct fs_device_d *fsdev = dev_to_fs_device(dev); + char *backingstore = fsdev->backingstore; + int ret; + struct ext_filesystem *fs; + + fs = xzalloc(sizeof(*fs)); + + dev->priv = fs; + fs->dev = dev; + + if (!strncmp(backingstore , "/dev/", 5)) + backingstore += 5; + + fs->cdev = cdev_open(backingstore, O_RDWR); + if (!fs->cdev) { + ret = -ENOENT; + goto err_open; + } + + ret = ext4fs_mount(fs); + if (ret) + goto err_mount; + + return 0; + +err_mount: + cdev_close(fs->cdev); +err_open: + free(fs); + + return ret; +} + +static void ext_remove(struct device_d *dev) +{ + struct ext_filesystem *fs = dev->priv; + + ext4fs_umount(fs); + cdev_close(fs->cdev); + free(fs); +} + +static struct fs_driver_d ext_driver = { + .open = ext_open, + .close = ext_close, + .read = ext_read, + .lseek = ext_lseek, + .opendir = ext_opendir, + .readdir = ext_readdir, + .closedir = ext_closedir, + .stat = ext_stat, + .readlink = ext_readlink, + .type = filetype_ext, + .flags = 0, + .drv = { + .probe = ext_probe, + .remove = ext_remove, + .name = "ext4", + } +}; + +static int ext_init(void) +{ + return register_fs_driver(&ext_driver); +} + +coredevice_initcall(ext_init); |