summaryrefslogtreecommitdiffstats
path: root/fs/squashfs/squashfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/squashfs/squashfs.c')
-rw-r--r--fs/squashfs/squashfs.c368
1 files changed, 368 insertions, 0 deletions
diff --git a/fs/squashfs/squashfs.c b/fs/squashfs/squashfs.c
new file mode 100644
index 0000000000..d00dee6caa
--- /dev/null
+++ b/fs/squashfs/squashfs.c
@@ -0,0 +1,368 @@
+#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 "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+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;
+}
+
+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;
+ }
+
+ 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,
+ .drv = {
+ .probe = squashfs_probe,
+ .remove = squashfs_remove,
+ .name = "squashfs",
+ }
+};
+
+static int squashfs_init(void)
+{
+ return register_fs_driver(&squashfs_driver);
+}
+
+device_initcall(squashfs_init);