[PATCH 06/10] AXFS: axfs_super.c

Jared Hulbert jaredeh at gmail.com
Thu Aug 21 01:45:37 EDT 2008


The many different devices AXFS can mount to and the various
dual device mounting schemes are supported here.

Signed-off-by: Jared Hulbert <jaredeh at gmail.com>
---
diff --git a/fs/axfs/axfs_super.c b/fs/axfs/axfs_super.c
new file mode 100644
index 0000000..5efab38
--- /dev/null
+++ b/fs/axfs/axfs_super.c
@@ -0,0 +1,864 @@
+/*
+ * Advanced XIP File System for Linux - AXFS
+ *   Readonly, compressed, and XIP filesystem for Linux systems big and small
+ *
+ * Copyright(c) 2008 Numonyx
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * Authors:
+ *  Eric Anderson
+ *  Jared Hulbert <jaredeh at gmail.com>
+ *  Sujaya Srinivasan
+ *  Justin Treon
+ *
+ * More info and current contacts at http://axfs.sourceforge.net
+ *
+ * axfs_super.c -
+ *   Contains the core code used to mount the fs.
+ *
+ */
+
+#include <linux/axfs.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/parser.h>
+#include <linux/statfs.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/mtd/mtd.h>
+
+/******************** Function Declarations ****************************/
+static struct super_operations axfs_sops;
+static struct axfs_super *axfs_get_sbi(void);
+static void axfs_put_sbi(struct axfs_super *);
+/***************** functions in other axfs files ***************************/
+int axfs_get_sb_bdev(struct file_system_type *, int, const char *,
+		     struct axfs_super *, struct vfsmount *, int *);
+void axfs_kill_block_super(struct super_block *);
+int axfs_copy_block(struct super_block *, void *, u64, u64);
+int axfs_is_dev_bdev(char *);
+int axfs_map_mtd(struct super_block *);
+void axfs_unmap_mtd(struct super_block *);
+int axfs_copy_mtd(struct super_block *, void *, u64, u64);
+int axfs_get_sb_mtd(struct file_system_type *, int, const char *,
+		    struct axfs_super *, struct vfsmount *, int *);
+int axfs_is_dev_mtd(char *, int *);
+void axfs_kill_mtd_super(struct super_block *);
+struct inode *axfs_create_vfs_inode(struct super_block *, int);
+int axfs_get_uml_address(char *, unsigned long *, unsigned long *);
+int axfs_init_profiling(struct axfs_super *);
+int axfs_shutdown_profiling(struct axfs_super *);
+void axfs_profiling_add(struct axfs_super *, unsigned long, unsigned int);
+struct inode *axfs_create_vfs_inode(struct super_block *, int);
+/******************************************************************************/
+
+static void axfs_free_region(struct axfs_super *sbi,
+			     struct axfs_region_desc *region)
+{
+	if (!region)
+		return;
+
+	if (AXFS_IS_REGION_XIP(sbi, region))
+		return;
+
+	if (region->virt_addr)
+		vfree(region->virt_addr);
+}
+
+static void axfs_put_sbi(struct axfs_super *sbi)
+{
+	if (!sbi)
+		return;
+
+	axfs_shutdown_profiling(sbi);
+
+	axfs_free_region(sbi, &sbi->strings);
+	axfs_free_region(sbi, &sbi->xip);
+	axfs_free_region(sbi, &sbi->compressed);
+	axfs_free_region(sbi, &sbi->byte_aligned);
+	axfs_free_region(sbi, &sbi->node_type);
+	axfs_free_region(sbi, &sbi->node_index);
+	axfs_free_region(sbi, &sbi->cnode_offset);
+	axfs_free_region(sbi, &sbi->cnode_index);
+	axfs_free_region(sbi, &sbi->banode_offset);
+	axfs_free_region(sbi, &sbi->cblock_offset);
+	axfs_free_region(sbi, &sbi->inode_file_size);
+	axfs_free_region(sbi, &sbi->inode_name_offset);
+	axfs_free_region(sbi, &sbi->inode_num_entries);
+	axfs_free_region(sbi, &sbi->inode_mode_index);
+	axfs_free_region(sbi, &sbi->inode_array_index);
+	axfs_free_region(sbi, &sbi->modes);
+	axfs_free_region(sbi, &sbi->uids);
+	axfs_free_region(sbi, &sbi->gids);
+
+	if (sbi->second_dev)
+		kfree(sbi->second_dev);
+
+	if (sbi->cblock_buffer[0])
+		vfree(sbi->cblock_buffer[0]);
+	if (sbi->cblock_buffer[1])
+		vfree(sbi->cblock_buffer[1]);
+
+	kfree(sbi);
+}
+
+static void axfs_put_super(struct super_block *sb)
+{
+	axfs_unmap_mtd(sb);
+	axfs_put_sbi(AXFS_SB(sb));
+	sb->s_fs_info = NULL;
+}
+
+static int axfs_copy_mem(struct super_block *sb, void *buf, u64 fsoffset,
+			 u64 len)
+{
+	struct axfs_super *sbi = AXFS_SB(sb);
+	unsigned long addr;
+
+	addr = sbi->virt_start_addr + (unsigned long)fsoffset;
+	memcpy(buf, (void *)addr, (size_t) len);
+	return 0;
+}
+
+static int axfs_copy_metadata(struct super_block *sb, void *buf, u64 fsoffset,
+			      u64 len)
+{
+	struct axfs_super *sbi = AXFS_SB(sb);
+	u64 end = fsoffset + len;
+	u64 a = sbi->mmap_size - fsoffset;
+	u64 b = end - sbi->mmap_size;
+	void *bb = (void *)((unsigned long)buf + (unsigned long)a);
+	int err;
+
+	/* Catches case where sbi is not yet fully initialized. */
+	if ((sbi->magic == 0) && (sbi->virt_start_addr != 0))
+		return axfs_copy_mem(sb, buf, fsoffset, len);
+
+	if (fsoffset < sbi->mmap_size) {
+		if (end > sbi->mmap_size) {
+			err = axfs_copy_metadata(sb, buf, fsoffset, a);
+			if (err)
+				return err;
+			err = axfs_copy_metadata(sb, bb, sbi->mmap_size, b);
+		} else {
+			if (AXFS_IS_OFFSET_MMAPABLE(sbi, fsoffset)) {
+				err = axfs_copy_mem(sb, buf, fsoffset, len);
+			} else if (AXFS_HAS_MTD(sb)) {
+				err = axfs_copy_mtd(sb, buf, fsoffset, len);
+			} else if (AXFS_HAS_BDEV(sb)) {
+				err = axfs_copy_block(sb, buf, fsoffset, len);
+			} else {
+				err = -EINVAL;
+			}
+		}
+	} else {
+		if (AXFS_NODEV(sb)) {
+			err = axfs_copy_mem(sb, buf, fsoffset, len);
+		} else if (AXFS_HAS_BDEV(sb)) {
+			err = axfs_copy_block(sb, buf, fsoffset, len);
+		} else if (AXFS_HAS_MTD(sb)) {
+			err = axfs_copy_mtd(sb, buf, fsoffset, len);
+		} else {
+			err = -EINVAL;
+		}
+	}
+	return err;
+}
+
+static int axfs_fill_region_data(struct super_block *sb,
+				 struct axfs_region_desc *region, int force)
+{
+	struct axfs_super *sbi = AXFS_SB(sb);
+	unsigned long addr;
+	void *buff = NULL;
+	void *vaddr;
+	int err = -ENOMEM;
+	u64 size = region->size;
+	u64 fsoffset = region->fsoffset;
+	u64 end = fsoffset + size;
+	u64 c_size = region->compressed_size;
+
+	if (size == 0)
+		return 0;
+
+	if (AXFS_IS_REGION_INCORE(region))
+		goto incore;
+
+	if (AXFS_IS_REGION_COMPRESSED(region))
+		goto incore;
+
+	if (AXFS_IS_REGION_XIP(sbi, region)) {
+		if ((end > sbi->mmap_size) && (force))
+			goto incore;
+		addr = sbi->virt_start_addr;
+		addr += (unsigned long)fsoffset;
+		region->virt_addr = (void *)addr;
+		return 0;
+	}
+
+	if (force)
+		goto incore;
+
+	region->virt_addr = NULL;
+	return 0;
+
+incore:
+	region->virt_addr = vmalloc(size);
+	if (!region->virt_addr)
+		goto out;
+	vaddr = region->virt_addr;
+
+	if (AXFS_IS_REGION_COMPRESSED(region)) {
+		buff = vmalloc(c_size);
+		if (!buff)
+			goto out;
+		axfs_copy_metadata(sb, buff, fsoffset, c_size);
+		err = axfs_uncompress_block(vaddr, size, buff, c_size);
+		if (!err)
+			goto out;
+		vfree(buff);
+	} else {
+		axfs_copy_metadata(sb, vaddr, fsoffset, size);
+	}
+
+	return 0;
+
+out:
+	if (buff)
+		vfree(buff);
+	if (region->virt_addr)
+		vfree(region->virt_addr);
+	return err;
+}
+
+static int axfs_fill_region_data_ptrs(struct super_block *sb)
+{
+	int err;
+	struct axfs_super *sbi = AXFS_SB(sb);
+
+	err = axfs_fill_region_data(sb, &sbi->strings, TRUE);
+	if (err)
+		goto out;
+
+	err = axfs_fill_region_data(sb, &sbi->xip, TRUE);
+	if (err)
+		goto out;
+
+	err = axfs_fill_region_data(sb, &sbi->compressed, FALSE);
+	if (err)
+		goto out;
+
+	err = axfs_fill_region_data(sb, &sbi->byte_aligned, FALSE);
+	if (err)
+		goto out;
+
+	err = axfs_fill_region_data(sb, &sbi->node_type, TRUE);
+	if (err)
+		goto out;
+	err = axfs_fill_region_data(sb, &sbi->node_index, TRUE);
+	if (err)
+		goto out;
+	err = axfs_fill_region_data(sb, &sbi->cnode_offset, TRUE);
+	if (err)
+		goto out;
+	err = axfs_fill_region_data(sb, &sbi->cnode_index, TRUE);
+	if (err)
+		goto out;
+	err = axfs_fill_region_data(sb, &sbi->banode_offset, TRUE);
+	if (err)
+		goto out;
+	err = axfs_fill_region_data(sb, &sbi->cblock_offset, TRUE);
+	if (err)
+		goto out;
+	err = axfs_fill_region_data(sb, &sbi->inode_file_size, TRUE);
+	if (err)
+		goto out;
+	err = axfs_fill_region_data(sb, &sbi->inode_name_offset, TRUE);
+	if (err)
+		goto out;
+	err = axfs_fill_region_data(sb, &sbi->inode_num_entries, TRUE);
+	if (err)
+		goto out;
+	err = axfs_fill_region_data(sb, &sbi->inode_mode_index, TRUE);
+	if (err)
+		goto out;
+	err = axfs_fill_region_data(sb, &sbi->inode_array_index, TRUE);
+	if (err)
+		goto out;
+	err = axfs_fill_region_data(sb, &sbi->modes, TRUE);
+	if (err)
+		goto out;
+	err = axfs_fill_region_data(sb, &sbi->uids, TRUE);
+	if (err)
+		goto out;
+	err = axfs_fill_region_data(sb, &sbi->gids, TRUE);
+	if (err)
+		goto out;
+
+out:
+	return err;
+}
+
+static int axfs_init_cblock_buffers(struct axfs_super *sbi)
+{
+	sbi->current_cnode_index = -1;
+	sbi->cblock_buffer[0] = vmalloc(sbi->cblock_size);
+	sbi->cblock_buffer[1] = vmalloc(sbi->cblock_size);
+	if ((!sbi->cblock_buffer[0]) || (!sbi->cblock_buffer[1]))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int axfs_fixup_devices(struct super_block *sb)
+{
+	struct axfs_super *sbi = AXFS_SB(sb);
+	int err = 0;
+
+	if (AXFS_HAS_MTD(sb)) {
+		err = axfs_map_mtd(sb);
+	} else if (AXFS_IS_IOMEM(sbi)) {
+		sbi->phys_start_addr = 0;
+	}
+	return err;
+}
+
+static void axfs_fill_region_desc(struct super_block *sb,
+				  struct axfs_region_desc_onmedia *out,
+				  u64 offset_be, struct axfs_region_desc *in)
+{
+	u64 offset = be64_to_cpu(offset_be);
+
+	axfs_copy_metadata(sb, (void *)out, offset, sizeof(*out));
+
+	in->fsoffset = be64_to_cpu(out->fsoffset);
+	in->size = be64_to_cpu(out->size);
+	in->compressed_size = be64_to_cpu(out->compressed_size);
+	in->max_index = be64_to_cpu(out->max_index);
+	in->table_byte_depth = out->table_byte_depth;
+	in->incore = out->incore;
+}
+
+static int axfs_fill_region_descriptors(struct super_block *sb,
+					struct axfs_super_onmedia *sbo)
+{
+	struct axfs_super *sbi = AXFS_SB(sb);
+	struct axfs_region_desc_onmedia *out;
+
+	out = kmalloc(sizeof(*out), GFP_KERNEL);
+	if (!out)
+		return -ENOMEM;
+	memset(out, 0, sizeof(*out));
+
+	axfs_fill_region_desc(sb, out, sbo->strings, &sbi->strings);
+	axfs_fill_region_desc(sb, out, sbo->xip, &sbi->xip);
+	axfs_fill_region_desc(sb, out, sbo->compressed, &sbi->compressed);
+	axfs_fill_region_desc(sb, out, sbo->byte_aligned, &sbi->byte_aligned);
+	axfs_fill_region_desc(sb, out, sbo->node_type, &sbi->node_type);
+	axfs_fill_region_desc(sb, out, sbo->node_index, &sbi->node_index);
+	axfs_fill_region_desc(sb, out, sbo->cnode_offset, &sbi->cnode_offset);
+	axfs_fill_region_desc(sb, out, sbo->cnode_index, &sbi->cnode_index);
+	axfs_fill_region_desc(sb, out, sbo->banode_offset, &sbi->banode_offset);
+	axfs_fill_region_desc(sb, out, sbo->cblock_offset, &sbi->cblock_offset);
+	axfs_fill_region_desc(sb, out, sbo->inode_file_size,
+			      &sbi->inode_file_size);
+	axfs_fill_region_desc(sb, out, sbo->inode_name_offset,
+			      &sbi->inode_name_offset);
+	axfs_fill_region_desc(sb, out, sbo->inode_num_entries,
+			      &sbi->inode_num_entries);
+	axfs_fill_region_desc(sb, out, sbo->inode_mode_index,
+			      &sbi->inode_mode_index);
+	axfs_fill_region_desc(sb, out, sbo->inode_array_index,
+			      &sbi->inode_array_index);
+	axfs_fill_region_desc(sb, out, sbo->modes, &sbi->modes);
+	axfs_fill_region_desc(sb, out, sbo->uids, &sbi->uids);
+	axfs_fill_region_desc(sb, out, sbo->gids, &sbi->gids);
+
+	kfree(out);
+
+	return 0;
+}
+
+int axfs_set_compression_type(struct axfs_super *sbi)
+{
+	if (sbi->compression_type != ZLIB)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int axfs_get_onmedia_super(struct super_block *sb)
+{
+	int err;
+	struct axfs_super *sbi = AXFS_SB(sb);
+	struct axfs_super_onmedia *sbo;
+
+	sbo = kmalloc(sizeof(*sbo), GFP_KERNEL);
+	if (!sbo)
+		return -ENOMEM;
+
+	axfs_copy_metadata(sb, (void *)sbo, 0, sizeof(*sbo));
+
+	/* Do sanity checks on the superblock */
+	if (be32_to_cpu(sbo->magic) != AXFS_MAGIC) {
+		printk(KERN_ERR "axfs: wrong magic\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	/* verify the signiture is correct */
+	if (strncmp(sbo->signature, AXFS_SIGNATURE, sizeof(AXFS_SIGNATURE))) {
+		printk(KERN_ERR "axfs: wrong axfs signature,"
+		       " read %s, expected %s\n",
+		       sbo->signature, AXFS_SIGNATURE);
+		err = -EINVAL;
+		goto out;
+	}
+
+	sbi->magic = be32_to_cpu(sbo->magic);
+	sbi->version_major = sbo->version_major;
+	sbi->version_minor = sbo->version_minor;
+	sbi->version_sub = sbo->version_sub;
+	sbi->files = be64_to_cpu(sbo->files);
+	sbi->size = be64_to_cpu(sbo->size);
+	sbi->blocks = be64_to_cpu(sbo->blocks);
+	sbi->mmap_size = be64_to_cpu(sbo->mmap_size);
+	sbi->cblock_size = be32_to_cpu(sbo->cblock_size);
+	sbi->timestamp.tv_sec = be64_to_cpu(sbo->timestamp);
+	sbi->timestamp.tv_nsec = 0;
+	sbi->compression_type = sbo->compression_type;
+
+	err = axfs_set_compression_type(sbi);
+	if (err)
+		goto out;
+
+	/* If no block or MTD device, adjust mmapable to cover all image */
+	if (AXFS_NODEV(sb))
+		sbi->mmap_size = sbi->size;
+
+	err = axfs_fill_region_descriptors(sb, sbo);
+	if (err)
+		goto out;
+
+	err = 0;
+out:
+	kfree(sbo);
+	return err;
+}
+
+/* Verify that the size of the block segment of a split filesystem
+   is less than or equal to that of the device containing it.
+   Validation of the size of an mmap segment vs. the device containing
+   it is handled by the point() function in axfs_map_mtd.
+*/
+int axfs_verify_device_sizes(struct super_block *sb)
+{
+	struct axfs_super *sbi = AXFS_SB(sb);
+	struct mtd_info *mtd0 = AXFS_MTD(sb);
+	struct mtd_info *mtd1 = AXFS_MTD1(sb);
+	int sndsize = sbi->size - sbi->mmap_size;
+
+	/* Whole FS on one device */
+	if (mtd0 && !mtd1 && (mtd0->size < sbi->size)) {
+		printk(KERN_ERR "axfs: ERROR: Filesystem extends beyond end of"
+		       "MTD! Filesystem cannot be safely mounted!\n");
+		printk(KERN_ERR "mtd name: %s, mtd size: 0x%x,"
+		       " fs size: 0x%llx\n", mtd0->name, mtd0->size, sbi->size);
+		return -EINVAL;
+	}
+
+	/* Split filesystem using physaddr */
+	if (sndsize && !mtd0 && mtd1 && (mtd1->size < sndsize)) {
+		printk(KERN_ERR "axfs: ERROR: The specified second_dev device "
+		       "is smaller than the store and download segment!\n");
+		printk(KERN_ERR "mtd name: %s, mtd size: 0x%x, "
+		       "snd size: 0x%x\n", mtd1->name, mtd1->size, sndsize);
+		return -EINVAL;
+	}
+
+	/* Split filesystem using two devices */
+	if (sndsize && mtd0 && mtd1) {
+		if (mtd0->size < sbi->mmap_size) {
+			printk(KERN_ERR "axfs: ERROR: Mmap segment extends"
+			       " beyond end of MTD!");
+			printk(KERN_ERR "mtd name: %s, mtd size: 0x%x, mmap "
+			       "size: 0x%llx",
+			       mtd0->name, mtd0->size, sbi->mmap_size);
+			return -EINVAL;
+		}
+		if (mtd1->size < sndsize) {
+			printk(KERN_ERR "axfs: ERROR: The specified second_dev "
+			       "device is smaller than the store and download "
+			       "segment!\n");
+			printk(KERN_ERR "mtd name: %s, mtd size: 0x%x, "
+			       "snd size: 0x%x\n",
+			       mtd1->name, mtd1->size, sndsize);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/* Read the last four bytes of the volume and make sure the AXFS magic is
+   present. */
+int axfs_verify_eofs_magic(struct super_block *sb)
+{
+	struct axfs_super *sbi = AXFS_SB(sb);
+	u32 buf = 0;
+	int err;
+	u32 fsoffset = sbi->size - sizeof(u32);
+	int len = sizeof(u32);
+
+	err = axfs_copy_metadata(sb, &buf, fsoffset, len);
+
+	if (err)
+		return -EINVAL;
+
+	if (be32_to_cpu(buf) != AXFS_MAGIC) {
+		printk(KERN_ERR "READ: 0x%x\n", be32_to_cpu(buf));
+		printk(KERN_ERR "ERROR: Filesystem is incomplete and cannot be "
+		       "mounted!\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int axfs_do_fill_super(struct super_block *sb)
+{
+	struct axfs_super *sbi = AXFS_SB(sb);
+	int err;
+
+	err = axfs_get_onmedia_super(sb);
+	if (err)
+		goto out;
+
+	err = axfs_fixup_devices(sb);
+	if (err)
+		goto out;
+
+	err = axfs_verify_device_sizes(sb);
+	if (err)
+		goto out;
+
+	err = axfs_verify_eofs_magic(sb);
+	if (err)
+		goto out;
+
+	err = axfs_fill_region_data_ptrs(sb);
+	if (err)
+		goto out;
+
+	/* Check that the root inode is in a sane state */
+	if (!S_ISDIR(AXFS_GET_MODE(sbi, 0))) {
+		printk(KERN_ERR "axfs: root is not a directory\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (AXFS_GET_INODE_NUM_ENTRIES(sbi, 0) == 0) {
+		printk(KERN_INFO "axfs: empty filesystem");
+		err = -EINVAL;
+		goto out;
+	}
+
+	err = axfs_init_cblock_buffers(sbi);
+	if (err)
+		goto out;
+
+	init_rwsem(&sbi->lock);
+
+	return 0;
+
+out:
+	axfs_put_super(sb);
+	return err;
+}
+
+int axfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct axfs_super *sbi_in = (struct axfs_super *)data;
+	struct axfs_super *sbi;
+	struct inode *root;
+	int err;
+
+	sbi = axfs_get_sbi();
+	sb->s_fs_info = (void *)sbi;
+	memcpy(sbi, sbi_in, sizeof(*sbi));
+
+	/* fully populate the incore superblock structures */
+	err = axfs_do_fill_super(sb);
+	if (err)
+		goto out;
+
+	sb->s_flags |= MS_RDONLY;
+
+	/* Setup the VFS super block now */
+	sb->s_op = &axfs_sops;
+	root = axfs_create_vfs_inode(sb, 0);
+	if (!root) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	sb->s_root = d_alloc_root(root);
+	if (!sb->s_root) {
+		iput(root);
+		err = -EINVAL;
+		goto out;
+	}
+
+	err = axfs_init_profiling(sbi);
+	if (err)
+		goto out;
+
+	return 0;
+
+out:
+	axfs_put_super(sb);
+	return err;
+}
+
+static int axfs_get_sb_address(struct file_system_type *fs_type, int flags,
+			       struct axfs_super *sbi, struct vfsmount *mnt,
+			       int *err)
+{
+	int mtdnr;
+	char *sd = sbi->second_dev;
+
+	if (sbi->phys_start_addr == 0)
+		return FALSE;
+
+	if (sbi->phys_start_addr & (PAGE_SIZE - 1)) {
+		printk(KERN_ERR
+		       "axfs: address 0x%lx for axfs image isn't aligned"
+		       " to a page boundary\n", sbi->phys_start_addr);
+		*err = -EINVAL;
+		return TRUE;
+	}
+
+	if (axfs_is_dev_mtd(sd, &mtdnr)) {
+		return axfs_get_sb_mtd(fs_type, flags, sd, sbi, mnt, err);
+	} else if (axfs_is_dev_bdev(sd)) {
+		return axfs_get_sb_bdev(fs_type, flags, sd, sbi, mnt, err);
+	} else {
+		*err = get_sb_nodev(fs_type, flags, sbi, axfs_fill_super, mnt);
+	}
+
+	return TRUE;
+}
+
+/* helpers for parse_axfs_options */
+enum {
+	OPTION_ERR,
+	OPTION_SECOND_DEV,
+	OPTION_PHYSICAL_ADDRESS_LOWER_X,
+	OPTION_PHYSICAL_ADDRESS_UPPER_X,
+	OPTION_IOMEM
+};
+
+/* helpers for parse_axfs_options */
+static match_table_t tokens = {
+	{OPTION_SECOND_DEV, "second_dev=%s"},
+	{OPTION_PHYSICAL_ADDRESS_LOWER_X, "physaddr=0x%s"},
+	{OPTION_PHYSICAL_ADDRESS_UPPER_X, "physaddr=0X%s"},
+	{OPTION_IOMEM, "iomem=%s"},
+	{OPTION_ERR, NULL}
+};
+
+static int axfs_check_options(char *options, struct axfs_super *sbi)
+{
+	unsigned long address = 0;
+	char *iomem = NULL;
+	unsigned long length = 0;
+	char *p;
+	int err = -EINVAL;
+	substring_t args[MAX_OPT_ARGS];
+
+	if (!options)
+		return 0;
+
+	if (!*options)
+		return 0;
+
+	while ((p = strsep(&options, ",")) != NULL) {
+		int token;
+		if (!*p)
+			continue;
+
+		token = match_token(p, tokens, args);
+		switch (token) {
+		case OPTION_SECOND_DEV:
+			sbi->second_dev = match_strdup(&args[0]);
+			if (!(sbi->second_dev)) {
+				err = -ENOMEM;
+				goto out;
+			}
+			if (!*(sbi->second_dev))
+				goto bad_value;
+			break;
+		case OPTION_IOMEM:
+			iomem = match_strdup(&args[0]);
+			if (!(iomem)) {
+				err = -ENOMEM;
+				goto out;
+			}
+			if (!*iomem)
+				goto bad_value;
+			break;
+		case OPTION_PHYSICAL_ADDRESS_LOWER_X:
+		case OPTION_PHYSICAL_ADDRESS_UPPER_X:
+			if (match_hex(&args[0], (int *)&address))
+				goto out;
+			if (!address)
+				goto bad_value;
+			break;
+		default:
+			printk(KERN_ERR
+			       "axfs: unrecognized mount option \"%s\" "
+			       "or missing value\n", p);
+			goto out;
+		}
+	}
+
+	if (iomem) {
+		if (address)
+			goto out;
+		err = axfs_get_uml_address(iomem, &address, &length);
+		kfree(iomem);
+		sbi->iomem_size = length;
+		sbi->virt_start_addr = address;
+	}
+
+	sbi->phys_start_addr = address;
+	return 0;
+
+bad_value:
+	printk(KERN_ERR
+	       "axfs: unrecognized mount option \"%s\" "
+	       "or missing value\n", p);
+out:
+	if (iomem)
+		kfree(iomem);
+	return err;
+}
+
+int axfs_get_sb(struct file_system_type *fs_type, int flags,
+		const char *dev_name, void *data, struct vfsmount *mnt)
+{
+	struct axfs_super *sbi;
+	int err;
+
+	sbi = axfs_get_sbi();
+	if (IS_ERR(sbi))
+		return PTR_ERR(sbi);
+
+	err = axfs_check_options((char *)data, sbi);
+	if (err)
+		goto out;
+
+	/* First we check if we are mounting directly to memory */
+	if (axfs_get_sb_address(fs_type, flags, sbi, mnt, &err))
+		goto out;
+
+	/* Next we assume there's a MTD device */
+	if (axfs_get_sb_mtd(fs_type, flags, dev_name, sbi, mnt, &err))
+		goto out;
+
+	/* Now we assume it's a block device */
+	if (sbi->second_dev) {
+		printk(KERN_ERR
+		       "axfs: can't specify secondary block device %s because"
+		       " %s is assumed to be a block device\n", sbi->second_dev,
+		       dev_name);
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (axfs_get_sb_bdev(fs_type, flags, dev_name, sbi, mnt, &err))
+		goto out;
+
+	err = -EINVAL;
+
+out:
+	axfs_put_sbi(sbi);
+	return err;
+}
+
+static struct axfs_super *axfs_get_sbi(void)
+{
+	struct axfs_super *sbi;
+
+	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
+	if (sbi)
+		return sbi;
+
+	axfs_put_sbi(sbi);
+	return ERR_PTR(-ENOMEM);
+}
+
+static void axfs_kill_super(struct super_block *sb)
+{
+	if (AXFS_NODEV(sb))
+		return kill_anon_super(sb);
+
+	if (AXFS_HAS_MTD(sb))
+		axfs_kill_mtd_super(sb);
+
+	if (AXFS_HAS_BDEV(sb))
+		axfs_kill_block_super(sb);
+}
+
+static int axfs_remount(struct super_block *sb, int *flags, char *data)
+{
+	*flags |= MS_RDONLY;
+	return 0;
+}
+
+static int axfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+	struct axfs_super *sbi = AXFS_SB(dentry->d_sb);
+
+	buf->f_type = AXFS_MAGIC;
+	buf->f_bsize = AXFS_PAGE_SIZE;
+	buf->f_blocks = sbi->blocks;
+	buf->f_bfree = 0;
+	buf->f_bavail = 0;
+	buf->f_files = sbi->files;
+	buf->f_ffree = 0;
+	buf->f_namelen = AXFS_MAXPATHLEN;
+	return 0;
+}
+
+static struct super_operations axfs_sops = {
+	.put_super = axfs_put_super,
+	.remount_fs = axfs_remount,
+	.statfs = axfs_statfs,
+};
+
+static struct file_system_type axfs_fs_type = {
+	.owner = THIS_MODULE,
+	.name = "axfs",
+	.get_sb = axfs_get_sb,
+	.kill_sb = axfs_kill_super,
+};
+
+static int __init init_axfs_fs(void)
+{
+	axfs_uncompress_init();
+	return register_filesystem(&axfs_fs_type);
+}
+
+static void __exit exit_axfs_fs(void)
+{
+	axfs_uncompress_exit();
+	unregister_filesystem(&axfs_fs_type);
+}
+
+module_init(init_axfs_fs);
+module_exit(exit_axfs_fs);
+MODULE_LICENSE("GPL");





More information about the linux-mtd mailing list