9
0
Fork 0
barebox/fs/squashfs/squashfs.c

404 lines
7.7 KiB
C

#include <common.h>
#include <malloc.h>
#include <driver.h>
#include <init.h>
#include <errno.h>
#include <fs.h>
#include <xfuncs.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/pagemap.h>
#include <linux/mtd/ubi.h>
#include <linux/mtd/mtd.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
struct ubi_volume_desc;
char *squashfs_devread(struct squashfs_sb_info *fs, int byte_offset,
int byte_len)
{
ssize_t size;
char *buf;
buf = malloc(byte_len);
if (buf == NULL)
return NULL;
size = cdev_read(fs->cdev, buf, byte_len, byte_offset, 0);
if (size < 0) {
dev_err(fs->dev, "read error: %s\n",
strerror(-size));
return NULL;
}
return buf;
}
static struct inode *duplicate_inode(struct inode *inode)
{
struct squashfs_inode_info *ei;
ei = malloc(sizeof(struct squashfs_inode_info));
if (ei == NULL) {
ERROR("Error allocating memory for inode\n");
return NULL;
}
memcpy(ei, squashfs_i(inode),
sizeof(struct squashfs_inode_info));
return &ei->vfs_inode;
}
static struct inode *squashfs_findfile(struct super_block *sb,
const char *filename, char *buf)
{
char *next;
char fpath[128];
char *name = fpath;
struct inode *inode;
struct inode *t_inode = NULL;
strcpy(fpath, filename);
/* Remove all leading slashes */
while (*name == '/')
name++;
inode = duplicate_inode(sb->s_root->d_inode);
/*
* Handle root-directory ('/')
*/
if (!name || *name == '\0')
return inode;
for (;;) {
/* Extract the actual part from the pathname. */
next = strchr(name, '/');
if (next) {
/* Remove all leading slashes. */
while (*next == '/')
*(next++) = '\0';
}
t_inode = squashfs_lookup(inode, name, 0);
if (t_inode == NULL)
break;
/*
* Check if directory with this name exists
*/
/* Found the node! */
if (!next || *next == '\0') {
if (buf != NULL)
sprintf(buf, "%s", name);
free(squashfs_i(inode));
return t_inode;
}
name = next;
free(squashfs_i(inode));
inode = t_inode;
}
free(squashfs_i(inode));
return NULL;
}
void squashfs_set_rootarg(struct squashfs_priv *priv, struct fs_device_d *fsdev)
{
struct ubi_volume_desc *ubi_vol;
struct ubi_volume_info vi = {};
struct ubi_device_info di = {};
struct mtd_info *mtd;
char *str;
if (!IS_ENABLED(CONFIG_MTD_UBI))
return;
ubi_vol = ubi_open_volume_cdev(fsdev->cdev, UBI_READONLY);
if (IS_ERR(ubi_vol))
return;
ubi_get_volume_info(ubi_vol, &vi);
ubi_get_device_info(vi.ubi_num, &di);
mtd = di.mtd;
str = basprintf("root=/dev/ubiblock%d_%d ubi.mtd=%s ubi.block=%d,%d rootfstype=squashfs",
vi.ubi_num, vi.vol_id, mtd->cdev.partname, vi.ubi_num, vi.vol_id);
fsdev_set_linux_rootarg(fsdev, str);
free(str);
}
static int squashfs_probe(struct device_d *dev)
{
struct fs_device_d *fsdev;
struct squashfs_priv *priv;
int ret;
fsdev = dev_to_fs_device(dev);
priv = xzalloc(sizeof(struct squashfs_priv));
dev->priv = priv;
ret = fsdev_open_cdev(fsdev);
if (ret)
goto err_out;
ret = squashfs_mount(fsdev, 0);
if (ret) {
dev_err(dev, "no valid squashfs found\n");
goto err_out;
}
squashfs_set_rootarg(priv, fsdev);
return 0;
err_out:
free(priv);
return ret;
}
static void squashfs_remove(struct device_d *dev)
{
struct squashfs_priv *priv = dev->priv;
squashfs_put_super(&priv->sb);
free(priv);
}
static int squashfs_open(struct device_d *dev, FILE *file, const char *filename)
{
struct squashfs_priv *priv = dev->priv;
struct inode *inode;
struct squashfs_page *page;
int i;
inode = squashfs_findfile(&priv->sb, filename, NULL);
if (!inode)
return -ENOENT;
page = malloc(sizeof(struct squashfs_page));
page->buf = calloc(32, sizeof(*page->buf));
for (i = 0; i < 32; i++) {
page->buf[i] = malloc(PAGE_CACHE_SIZE);
if (page->buf[i] == NULL) {
dev_err(dev, "error allocation read buffer\n");
goto error;
}
}
page->data_block = 0;
page->idx = 0;
page->real_page.inode = inode;
file->size = inode->i_size;
file->priv = page;
return 0;
error:
for (; i > 0; --i)
free(page->buf[i]);
free(page->buf);
free(page);
return -ENOMEM;
}
static int squashfs_close(struct device_d *dev, FILE *f)
{
struct squashfs_page *page = f->priv;
int i;
for (i = 0; i < 32; i++)
free(page->buf[i]);
free(page->buf);
free(squashfs_i(page->real_page.inode));
free(page);
return 0;
}
static int squashfs_read_buf(struct squashfs_page *page, int pos, void **buf)
{
unsigned int data_block = pos / (32 * PAGE_CACHE_SIZE);
unsigned int data_block_pos = pos % (32 * PAGE_CACHE_SIZE);
unsigned int idx = data_block_pos / PAGE_CACHE_SIZE;
if (data_block != page->data_block || page->idx == 0) {
page->idx = 0;
page->real_page.index = data_block * 32;
squashfs_readpage(NULL, &page->real_page);
page->data_block = data_block;
}
*buf = page->buf[idx];
return 0;
}
static int squashfs_read(struct device_d *_dev, FILE *f, void *buf,
size_t insize)
{
unsigned int size = insize;
unsigned int pos = f->pos;
unsigned int ofs;
unsigned int now;
void *pagebuf;
struct squashfs_page *page = f->priv;
/* Read till end of current buffer page */
ofs = pos % PAGE_CACHE_SIZE;
if (ofs) {
squashfs_read_buf(page, pos, &pagebuf);
now = min(size, PAGE_CACHE_SIZE - ofs);
memcpy(buf, pagebuf + ofs, now);
size -= now;
pos += now;
buf += now;
}
/* Do full buffer pages */
while (size >= PAGE_CACHE_SIZE) {
squashfs_read_buf(page, pos, &pagebuf);
memcpy(buf, pagebuf, PAGE_CACHE_SIZE);
size -= PAGE_CACHE_SIZE;
pos += PAGE_CACHE_SIZE;
buf += PAGE_CACHE_SIZE;
}
/* And the rest */
if (size) {
squashfs_read_buf(page, pos, &pagebuf);
memcpy(buf, pagebuf, size);
size = 0;
}
return insize;
}
static loff_t squashfs_lseek(struct device_d *dev, FILE *f, loff_t pos)
{
f->pos = pos;
return pos;
}
struct squashfs_dir {
struct file file;
struct dentry dentry;
struct dentry root_dentry;
struct inode inode;
struct qstr nm;
DIR dir;
char d_name[256];
char root_d_name[256];
};
static DIR *squashfs_opendir(struct device_d *dev, const char *pathname)
{
struct squashfs_priv *priv = dev->priv;
struct inode *inode;
struct squashfs_dir *dir;
char buf[256];
inode = squashfs_findfile(&priv->sb, pathname, buf);
if (!inode)
return NULL;
dir = xzalloc(sizeof(struct squashfs_dir));
dir->dir.priv = dir;
dir->root_dentry.d_inode = inode;
sprintf(dir->d_name, "%s", buf);
sprintf(dir->root_d_name, "%s", buf);
return &dir->dir;
}
static struct dirent *squashfs_readdir(struct device_d *dev, DIR *_dir)
{
struct squashfs_dir *dir = _dir->priv;
struct dentry *root_dentry = &dir->root_dentry;
if (squashfs_lookup_next(root_dentry->d_inode,
dir->root_d_name,
dir->d_name))
return NULL;
strcpy(_dir->d.d_name, dir->d_name);
return &_dir->d;
}
static int squashfs_closedir(struct device_d *dev, DIR *_dir)
{
struct squashfs_dir *dir = _dir->priv;
free(squashfs_i(dir->root_dentry.d_inode));
free(dir);
return 0;
}
static int squashfs_stat(struct device_d *dev, const char *filename,
struct stat *s)
{
struct squashfs_priv *priv = dev->priv;
struct inode *inode;
inode = squashfs_findfile(&priv->sb, filename, NULL);
if (!inode)
return -ENOENT;
s->st_size = inode->i_size;
s->st_mode = inode->i_mode;
free(squashfs_i(inode));
return 0;
}
static struct fs_driver_d squashfs_driver = {
.open = squashfs_open,
.close = squashfs_close,
.read = squashfs_read,
.lseek = squashfs_lseek,
.opendir = squashfs_opendir,
.readdir = squashfs_readdir,
.closedir = squashfs_closedir,
.stat = squashfs_stat,
.type = filetype_squashfs,
.drv = {
.probe = squashfs_probe,
.remove = squashfs_remove,
.name = "squashfs",
}
};
static int squashfs_init(void)
{
return register_fs_driver(&squashfs_driver);
}
device_initcall(squashfs_init);