From: Marco Stornelli <marco.stornelli@gmail.com>
To: Linux Kernel <linux-kernel@vger.kernel.org>
Cc: Linux FS Devel <linux-fsdevel@vger.kernel.org>,
Andrew Morton <akpm@linux-foundation.org>,
Tim Bird <tim.bird@am.sony.com>,
Linux Embedded <linux-embedded@vger.kernel.org>
Subject: [PATCH 02/16 v6] pramfs: super operations
Date: Thu, 16 Dec 2010 18:59:35 +0100 [thread overview]
Message-ID: <4D0A5387.6050409@gmail.com> (raw)
From: Marco Stornelli <marco.stornelli@gmail.com>
Super block operations.
Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/super.c linux-2.6.36/fs/pramfs/super.c
--- linux-2.6.36-orig/fs/pramfs/super.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/super.c 2010-12-12 18:30:50.000000000 +0100
@@ -0,0 +1,932 @@
+/*
+ * BRIEF DESCRIPTION
+ *
+ * Super block operations.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/parser.h>
+#include <linux/vfs.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/seq_file.h>
+#include <linux/mount.h>
+#include <linux/mm.h>
+#include <linux/ctype.h>
+#include <linux/bitops.h>
+#include <linux/magic.h>
+#include <linux/exportfs.h>
+#include <linux/random.h>
+#include "xattr.h"
+#include "pram.h"
+
+static struct super_operations pram_sops;
+static const struct export_operations pram_export_ops;
+static struct kmem_cache *pram_inode_cachep;
+
+#ifdef CONFIG_PRAMFS_TEST
+static void *first_pram_super;
+
+struct pram_super_block *get_pram_super(void)
+{
+ return (struct pram_super_block *)first_pram_super;
+}
+EXPORT_SYMBOL(get_pram_super);
+#endif
+
+void pram_error_mng(struct super_block * sb, const char * fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ printk(KERN_ERR "pramfs error: ");
+ vprintk(fmt, args);
+ printk("\n");
+ va_end(args);
+
+ if (test_opt(sb, ERRORS_PANIC))
+ panic("pramfs: panic from previous error\n");
+ if (test_opt(sb, ERRORS_RO)) {
+ printk(KERN_CRIT "pramfs error: remounting filesystem read-only");
+ sb->s_flags |= MS_RDONLY;
+ }
+}
+
+static void pram_set_blocksize(struct super_block *sb, unsigned long size)
+{
+ int bits;
+
+ /*
+ * We've already validated the user input and the value here must be
+ * between PRAM_MAX_BLOCK_SIZE and PRAM_MIN_BLOCK_SIZE
+ * and it must be a power of 2.
+ */
+ bits = fls(size) - 1;
+ sb->s_blocksize_bits = bits;
+ sb->s_blocksize = (1<<bits);
+}
+
+static inline void *pram_ioremap(phys_addr_t phys_addr, ssize_t size, bool protect)
+{
+ void *retval;
+
+ /*
+ * NOTE: Userland may not map this resource, we will mark the region so
+ * /dev/mem and the sysfs MMIO access will not be allowed. This
+ * restriction depends on STRICT_DEVMEM option. If this option is
+ * disabled or not available we mark the region only as busy.
+ */
+ retval = request_mem_region_exclusive(phys_addr, size, "pramfs");
+ if (!retval)
+ goto fail;
+
+ retval = ioremap_nocache(phys_addr, size);
+
+ if (retval && protect)
+ pram_writeable(retval, size, 0);
+fail:
+ return retval;
+}
+
+static loff_t pram_max_size(int bits)
+{
+ loff_t res;
+ res = (1ULL << (3*bits - 6)) - 1;
+
+ if (res > MAX_LFS_FILESIZE)
+ res = MAX_LFS_FILESIZE;
+
+ pram_info("max file size %llu bytes", res);
+ return res;
+}
+
+enum {
+ Opt_addr, Opt_bpi, Opt_size,
+ Opt_num_inodes, Opt_mode, Opt_uid,
+ Opt_gid, Opt_blocksize, Opt_user_xattr,
+ Opt_nouser_xattr, Opt_noprotect,
+ Opt_acl, Opt_noacl, Opt_xip,
+ Opt_err_cont, Opt_err_panic, Opt_err_ro,
+ Opt_err
+};
+
+static const match_table_t tokens = {
+ {Opt_bpi, "physaddr=%x"},
+ {Opt_bpi, "bpi=%u"},
+ {Opt_size, "init=%s"},
+ {Opt_num_inodes, "N=%u"},
+ {Opt_mode, "mode=%o"},
+ {Opt_uid, "uid=%u"},
+ {Opt_gid, "gid=%u"},
+ {Opt_blocksize, "bs=%s"},
+ {Opt_user_xattr, "user_xattr"},
+ {Opt_user_xattr, "nouser_xattr"},
+ {Opt_noprotect, "noprotect"},
+ {Opt_acl, "acl"},
+ {Opt_acl, "noacl"},
+ {Opt_xip, "xip"},
+ {Opt_err_cont, "errors=continue"},
+ {Opt_err_panic, "errors=panic"},
+ {Opt_err_ro, "errors=remount-ro"},
+ {Opt_err, NULL},
+};
+
+static phys_addr_t get_phys_addr(void **data)
+{
+ phys_addr_t phys_addr;
+ char *options = (char *) *data;
+
+ if (!options || strncmp(options, "physaddr=", 9) != 0)
+ return (phys_addr_t)ULLONG_MAX;
+ options += 9;
+ phys_addr = (phys_addr_t)simple_strtoull(options, &options, 0);
+ if (*options && *options != ',') {
+ printk(KERN_ERR "Invalid phys addr specification: %s\n",
+ (char *) *data);
+ return (phys_addr_t)ULLONG_MAX;
+ }
+ if (phys_addr & (PAGE_SIZE - 1)) {
+ printk(KERN_ERR "physical address 0x%16llx for pramfs isn't "
+ "aligned to a page boundary\n",
+ (u64)phys_addr);
+ return (phys_addr_t)ULLONG_MAX;
+ }
+ if (*options == ',')
+ options++;
+ *data = (void *) options;
+ return phys_addr;
+}
+
+static int pram_parse_options(char *options, struct pram_sb_info *sbi, bool remount)
+{
+ char *p, *rest;
+ substring_t args[MAX_OPT_ARGS];
+ int option;
+
+ if (!options)
+ return 0;
+
+ while ((p = strsep(&options, ",")) != NULL) {
+ int token;
+ if (!*p)
+ continue;
+
+ token = match_token(p, tokens, args);
+ switch (token) {
+ case Opt_addr:
+ if (remount)
+ goto bad_opt;
+ /* physaddr managed in get_phys_addr() */
+ break;
+ case Opt_bpi:
+ if (remount)
+ goto bad_opt;
+ if (match_int(&args[0], &option))
+ goto bad_val;
+ sbi->bpi = option;
+ break;
+ case Opt_uid:
+ if (remount)
+ goto bad_opt;
+ if (match_int(&args[0], &option))
+ goto bad_val;
+ sbi->uid = option;
+ break;
+ case Opt_gid:
+ if (match_int(&args[0], &option))
+ goto bad_val;
+ sbi->gid = option;
+ break;
+ case Opt_mode:
+ if (match_octal(&args[0], &option))
+ goto bad_val;
+ sbi->mode = option & 01777U;
+ break;
+ case Opt_size:
+ if (remount)
+ goto bad_opt;
+ /* memparse() will accept a K/M/G without a digit */
+ if (!isdigit(*args[0].from))
+ goto bad_val;
+ sbi->initsize = memparse(args[0].from, &rest);
+ break;
+ case Opt_num_inodes:
+ if (remount)
+ goto bad_opt;
+ if (match_int(&args[0], &option))
+ goto bad_val;
+ sbi->num_inodes = option;
+ break;
+ case Opt_blocksize:
+ if (remount)
+ goto bad_opt;
+ /* memparse() will accept a K/M/G without a digit */
+ if (!isdigit(*args[0].from))
+ goto bad_val;
+ sbi->blocksize = memparse(args[0].from, &rest);
+ if (sbi->blocksize < PRAM_MIN_BLOCK_SIZE ||
+ sbi->blocksize > PRAM_MAX_BLOCK_SIZE ||
+ !is_power_of_2(sbi->blocksize))
+ goto bad_val;
+ break;
+ case Opt_err_panic:
+ clear_opt(sbi->s_mount_opt, ERRORS_CONT);
+ clear_opt(sbi->s_mount_opt, ERRORS_RO);
+ set_opt(sbi->s_mount_opt, ERRORS_PANIC);
+ break;
+ case Opt_err_ro:
+ clear_opt(sbi->s_mount_opt, ERRORS_CONT);
+ clear_opt(sbi->s_mount_opt, ERRORS_PANIC);
+ set_opt(sbi->s_mount_opt, ERRORS_RO);
+ break;
+ case Opt_err_cont:
+ clear_opt(sbi->s_mount_opt, ERRORS_RO);
+ clear_opt(sbi->s_mount_opt, ERRORS_PANIC);
+ set_opt(sbi->s_mount_opt, ERRORS_CONT);
+ break;
+#ifdef CONFIG_PRAMFS_WRITE_PROTECT
+ case Opt_noprotect:
+ if (remount)
+ goto bad_opt;
+ clear_opt(sbi->s_mount_opt, PROTECT);
+ break;
+#else
+ case Opt_noprotect:
+ pram_info("noprotect options not supported");
+ break;
+#endif
+#ifdef CONFIG_PRAMFS_XATTR
+ case Opt_user_xattr:
+ set_opt(sbi->s_mount_opt, XATTR_USER);
+ break;
+ case Opt_nouser_xattr:
+ clear_opt(sbi->s_mount_opt, XATTR_USER);
+ break;
+#else
+ case Opt_user_xattr:
+ case Opt_nouser_xattr:
+ pram_info("(no)user_xattr options not supported");
+ break;
+#endif
+#ifdef CONFIG_PRAMFS_POSIX_ACL
+ case Opt_acl:
+ set_opt(sbi->s_mount_opt, POSIX_ACL);
+ break;
+ case Opt_noacl:
+ clear_opt(sbi->s_mount_opt, POSIX_ACL);
+ break;
+#else
+ case Opt_acl:
+ case Opt_noacl:
+ pram_info("(no)acl options not supported");
+ break;
+#endif
+ case Opt_xip:
+#ifdef CONFIG_PRAMFS_XIP
+ if (remount)
+ goto bad_opt;
+ set_opt(sbi->s_mount_opt, XIP);
+ break;
+#else
+ pram_info("xip option not supported");
+ break;
+#endif
+ default: {
+ goto bad_opt;
+ }
+ }
+ }
+
+ return 0;
+
+bad_val:
+ printk(KERN_ERR "Bad value '%s' for mount option '%s'\n", args[0].from, p);
+ return -EINVAL;
+bad_opt:
+ printk(KERN_ERR "Bad mount option: \"%s\"\n", p);
+ return -EINVAL;
+}
+
+static struct pram_inode *pram_init(struct super_block *sb, unsigned long size)
+{
+ unsigned long bpi, num_inodes, bitmap_size, blocksize, num_blocks;
+ u64 bitmap_start;
+ struct pram_inode *root_i;
+ struct pram_super_block *super;
+ struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+
+ pram_info("creating an empty pramfs of size %lu\n", size);
+ if (pram_is_protected(sb))
+ sbi->virt_addr = pram_ioremap(sbi->phys_addr, size, 1);
+ else
+ sbi->virt_addr = pram_ioremap(sbi->phys_addr, size, 0);
+
+ if (!sbi->virt_addr) {
+ printk(KERN_ERR "ioremap of the pramfs image failed\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+#ifdef CONFIG_PRAMFS_TEST
+ if (!first_pram_super)
+ first_pram_super = sbi->virt_addr;
+#endif
+
+ if (!sbi->blocksize)
+ blocksize = PRAM_DEF_BLOCK_SIZE;
+ else
+ blocksize = sbi->blocksize;
+
+ pram_set_blocksize(sb, blocksize);
+ blocksize = sb->s_blocksize;
+
+ if (sbi->blocksize && sbi->blocksize != blocksize)
+ sbi->blocksize = blocksize;
+
+ if (size < blocksize) {
+ printk(KERN_ERR "size smaller then block size\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!sbi->bpi)
+ /*
+ * default is that 5% of the filesystem is
+ * devoted to the inode table
+ */
+ bpi = 20 * PRAM_INODE_SIZE;
+ else
+ bpi = sbi->bpi;
+
+ if (!sbi->num_inodes)
+ num_inodes = size / bpi;
+ else
+ num_inodes = sbi->num_inodes;
+
+ /*
+ * up num_inodes such that the end of the inode table
+ * (and start of bitmap) is on a block boundary
+ */
+ bitmap_start = (PRAM_SB_SIZE*2) + (num_inodes<<PRAM_INODE_BITS);
+ if (bitmap_start & (blocksize - 1))
+ bitmap_start = (bitmap_start + blocksize) &
+ ~(blocksize-1);
+ num_inodes = (bitmap_start - (PRAM_SB_SIZE*2)) >> PRAM_INODE_BITS;
+
+ if (sbi->num_inodes && num_inodes != sbi->num_inodes)
+ sbi->num_inodes = num_inodes;
+
+ num_blocks = (size - bitmap_start) >> sb->s_blocksize_bits;
+
+ if (!num_blocks) {
+ printk(KERN_ERR "num blocks equals to zero\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* calc the data blocks in-use bitmap size in bytes */
+ if (num_blocks & 7)
+ bitmap_size = ((num_blocks + 8) & ~7) >> 3;
+ else
+ bitmap_size = num_blocks >> 3;
+ /* round it up to the nearest blocksize boundary */
+ if (bitmap_size & (blocksize - 1))
+ bitmap_size = (bitmap_size + blocksize) & ~(blocksize-1);
+
+ pram_info("blocksize %lu, num inodes %lu, num blocks %lu\n",
+ blocksize, num_inodes, num_blocks);
+ pram_dbg("bitmap start 0x%08x, bitmap size %lu\n",
+ (unsigned int)bitmap_start, bitmap_size);
+ pram_dbg("max name length %d\n", (unsigned int)PRAM_NAME_LEN);
+
+ super = pram_get_super(sb);
+ pram_memunlock_range(sb, super, bitmap_start + bitmap_size);
+
+ /* clear out super-block and inode table */
+ memset(super, 0, bitmap_start);
+ super->s_size = cpu_to_be64(size);
+ super->s_blocksize = cpu_to_be32(blocksize);
+ super->s_inodes_count = cpu_to_be32(num_inodes);
+ super->s_blocks_count = cpu_to_be32(num_blocks);
+ super->s_free_inodes_count = cpu_to_be32(num_inodes - 1);
+ super->s_bitmap_blocks = cpu_to_be32(bitmap_size >> sb->s_blocksize_bits);
+ super->s_free_blocks_count = cpu_to_be32(num_blocks - be32_to_cpu(super->s_bitmap_blocks));
+ super->s_free_inode_hint = cpu_to_be32(1);
+ super->s_bitmap_start = cpu_to_be64(bitmap_start);
+ super->s_magic = cpu_to_be16(PRAM_SUPER_MAGIC);
+ pram_sync_super(super);
+
+ root_i = pram_get_inode(sb, PRAM_ROOT_INO);
+
+ root_i->i_mode = cpu_to_be32(sbi->mode);
+ root_i->i_mode = cpu_to_be16(root_i->i_mode | S_IFDIR);
+ root_i->i_uid = cpu_to_be32(sbi->uid);
+ root_i->i_gid = cpu_to_be32(sbi->gid);
+ root_i->i_links_count = cpu_to_be16(2);
+ root_i->i_d.d_parent = cpu_to_be64(PRAM_ROOT_INO);
+ pram_sync_inode(root_i);
+
+ pram_init_bitmap(sb);
+
+ pram_memlock_range(sb, super, bitmap_start + bitmap_size);
+
+ return root_i;
+}
+
+static inline void set_default_opts(struct pram_sb_info *sbi)
+{
+ set_opt(sbi->s_mount_opt, PROTECT);
+ set_opt(sbi->s_mount_opt, ERRORS_CONT);
+}
+
+static int pram_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct pram_super_block *super, *super_redund;
+ struct pram_inode *root_i;
+ struct pram_sb_info *sbi = NULL;
+ u64 root_offset;
+ unsigned long blocksize, initsize = 0;
+ u32 random = 0;
+ int retval = -EINVAL;
+
+ BUILD_BUG_ON(sizeof(struct pram_super_block) > PRAM_SB_SIZE);
+ BUILD_BUG_ON(sizeof(struct pram_inode) > PRAM_INODE_SIZE);
+
+ sbi = kzalloc(sizeof(struct pram_sb_info), GFP_KERNEL);
+ if (!sbi)
+ return -ENOMEM;
+ sb->s_fs_info = sbi;
+
+ set_default_opts(sbi);
+
+#ifdef CONFIG_PRAMFS_XATTR
+ spin_lock_init(&sbi->desc_tree_lock);
+ sbi->desc_tree.rb_node = NULL;
+#endif
+
+ sbi->phys_addr = get_phys_addr(&data);
+ if (sbi->phys_addr == (phys_addr_t)ULLONG_MAX)
+ goto out;
+
+ get_random_bytes(&random, sizeof(u32));
+ atomic_set(&sbi->next_generation, random);
+
+ /* Init with default values */
+ sbi->mode = (S_IRWXUGO | S_ISVTX);
+ sbi->uid = current_fsuid();
+ sbi->gid = current_fsgid();
+
+ if (pram_parse_options(data, sbi, 0))
+ goto out;
+
+ if (test_opt(sb, XIP) && test_opt(sb, PROTECT)) {
+ printk(KERN_ERR "xip and protect options both enabled\n");
+ goto out;
+ }
+
+ if (test_opt(sb, XIP) && sbi->blocksize != PAGE_SIZE) {
+ printk(KERN_ERR "blocksize not equal to page size and xip enabled\n");
+ goto out;
+ }
+
+ initsize = sbi->initsize;
+
+ /* Init a new pramfs instance */
+ if (initsize) {
+ root_i = pram_init(sb, initsize);
+
+ if (IS_ERR(root_i))
+ goto out;
+
+ super = pram_get_super(sb);
+
+ goto setup_sb;
+ }
+
+ pram_dbg("checking physical address 0x%016llx for pramfs image\n",
+ (u64)sbi->phys_addr);
+
+ /* Map only one page for now. Will remap it when fs size is known. */
+ initsize = PAGE_SIZE;
+ if (pram_is_protected(sb))
+ sbi->virt_addr = pram_ioremap(sbi->phys_addr, initsize, 1);
+ else
+ sbi->virt_addr = pram_ioremap(sbi->phys_addr, initsize, 0);
+ if (!sbi->virt_addr) {
+ printk(KERN_ERR "ioremap of the pramfs image failed\n");
+ goto out;
+ }
+
+ super = pram_get_super(sb);
+ super_redund = pram_get_redund_super(sb);
+
+ /* Do sanity checks on the superblock */
+ if (be16_to_cpu(super->s_magic) != PRAM_SUPER_MAGIC) {
+ if (be16_to_cpu(super_redund->s_magic) != PRAM_SUPER_MAGIC) {
+ if (!silent)
+ printk(KERN_ERR "Can't find a valid pramfs "
+ "partition\n");
+ goto out;
+ } else {
+ pram_warn("Error in super block: try to repair it with "
+ "the redundant copy");
+ /* Try to auto-recover the super block */
+ memcpy(super, super_redund, PRAM_SB_SIZE);
+ }
+ }
+
+ /* Read the superblock */
+ if (pram_calc_checksum((u8 *)super, PRAM_SB_SIZE)) {
+ if (pram_calc_checksum((u8 *)super_redund, PRAM_SB_SIZE)) {
+ printk(KERN_ERR "checksum error in super block\n");
+ goto out;
+ } else {
+ pram_warn("Error in super block: try to repair it with "
+ "the redundant copy");
+ /* Try to auto-recover the super block */
+ memcpy(super, super_redund, PRAM_SB_SIZE);
+ }
+ }
+
+ blocksize = be32_to_cpu(super->s_blocksize);
+ pram_set_blocksize(sb, blocksize);
+
+ initsize = be64_to_cpu(super->s_size);
+ pram_info("pramfs image appears to be %lu KB in size\n", initsize>>10);
+ pram_info("blocksize %lu\n", blocksize);
+
+ /* Read the root inode */
+ root_i = pram_get_inode(sb, PRAM_ROOT_INO);
+
+ /* Check that the root inode is in a sane state */
+ if (pram_calc_checksum((u8 *)root_i, PRAM_INODE_SIZE)) {
+ printk(KERN_ERR "checksum error in root inode!\n");
+ goto out;
+ }
+
+ if (be64_to_cpu(root_i->i_d.d_next)) {
+ printk(KERN_ERR "root->next not NULL??!!\n");
+ goto out;
+ }
+
+ if (!S_ISDIR(be16_to_cpu(root_i->i_mode))) {
+ printk(KERN_ERR "root is not a directory!\n");
+ goto out;
+ }
+
+ root_offset = be64_to_cpu(root_i->i_type.dir.head);
+ if (root_offset == 0)
+ pram_dbg("empty filesystem\n");
+
+ /* Remap the whole filesystem now */
+ if (pram_is_protected(sb))
+ pram_writeable(sbi->virt_addr, PAGE_SIZE, 1);
+ iounmap(sbi->virt_addr);
+ release_mem_region(sbi->phys_addr, PAGE_SIZE);
+ if (pram_is_protected(sb))
+ sbi->virt_addr = pram_ioremap(sbi->phys_addr, initsize, 1);
+ else
+ sbi->virt_addr = pram_ioremap(sbi->phys_addr, initsize, 0);
+ if (!sbi->virt_addr) {
+ printk(KERN_ERR "ioremap of the pramfs image failed\n");
+ goto out;
+ }
+ super = pram_get_super(sb);
+ root_i = pram_get_inode(sb, PRAM_ROOT_INO);
+
+#ifdef CONFIG_PRAMFS_TEST
+ if (!first_pram_super)
+ first_pram_super = sbi->virt_addr;
+#endif
+
+ /* Set it all up.. */
+ setup_sb:
+ sb->s_magic = be16_to_cpu(super->s_magic);
+ sb->s_op = &pram_sops;
+ sb->s_maxbytes = pram_max_size(sb->s_blocksize_bits);
+ sb->s_time_gran = 1;
+ sb->s_export_op = &pram_export_ops;
+ sb->s_xattr = pram_xattr_handlers;
+#ifdef CONFIG_PRAMFS_POSIX_ACL
+ sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
+ (sbi->s_mount_opt & PRAM_MOUNT_POSIX_ACL) ?
+ MS_POSIXACL : 0;
+#endif
+ sb->s_root = d_alloc_root(pram_iget(sb, PRAM_ROOT_INO));
+
+ retval = 0;
+ return retval;
+ out:
+ if (sbi->virt_addr) {
+ if (pram_is_protected(sb))
+ pram_writeable(sbi->virt_addr, initsize, 1);
+ iounmap(sbi->virt_addr);
+ release_mem_region(sbi->phys_addr, initsize);
+ }
+
+ kfree(sbi);
+ return retval;
+}
+
+int pram_statfs(struct dentry *d, struct kstatfs *buf)
+{
+ struct super_block *sb = d->d_sb;
+ struct pram_super_block *ps = pram_get_super(sb);
+
+ buf->f_type = PRAM_SUPER_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = be32_to_cpu(ps->s_blocks_count);
+ buf->f_bfree = buf->f_bavail = pram_count_free_blocks(sb);
+ buf->f_files = be32_to_cpu(ps->s_inodes_count);
+ buf->f_ffree = be32_to_cpu(ps->s_free_inodes_count);
+ buf->f_namelen = PRAM_NAME_LEN;
+ return 0;
+}
+
+static int pram_show_options(struct seq_file *seq, struct vfsmount *vfs)
+{
+ struct pram_sb_info *sbi = (struct pram_sb_info *)vfs->mnt_sb->s_fs_info;
+
+ seq_printf(seq, ",physaddr=0x%016llx", (u64)sbi->phys_addr);
+ if (sbi->initsize)
+ seq_printf(seq, ",init=%luk", sbi->initsize >> 10);
+ if (sbi->blocksize)
+ seq_printf(seq, ",bs=%lu", sbi->blocksize);
+ if (sbi->bpi)
+ seq_printf(seq, ",bpi=%lu", sbi->bpi);
+ if (sbi->num_inodes)
+ seq_printf(seq, ",N=%lu", sbi->num_inodes);
+ if (sbi->mode != (S_IRWXUGO | S_ISVTX))
+ seq_printf(seq, ",mode=%03o", sbi->mode);
+ if (sbi->uid != 0)
+ seq_printf(seq, ",uid=%u", sbi->uid);
+ if (sbi->gid != 0)
+ seq_printf(seq, ",gid=%u", sbi->gid);
+ if (test_opt(vfs->mnt_sb, ERRORS_RO))
+ seq_puts(seq, ",errors=remount-ro");
+ if (test_opt(vfs->mnt_sb, ERRORS_PANIC))
+ seq_puts(seq, ",errors=panic");
+#ifdef CONFIG_PRAMFS_WRITE_PROTECT
+ /* memory protection enabled by default */
+ if (!test_opt(vfs->mnt_sb, PROTECT))
+ seq_puts(seq, ",noprotect");
+#else
+ /*
+ * If it's not compiled say to the user that there
+ * isn't the protection.
+ */
+ seq_puts(seq, ",noprotect");
+#endif
+
+#ifdef CONFIG_PRAMFS_XATTR
+ /* user xattr not enabled by default */
+ if (test_opt(vfs->mnt_sb, XATTR_USER))
+ seq_puts(seq, ",user_xattr");
+#endif
+
+#ifdef CONFIG_PRAMFS_POSIX_ACL
+ /* acl not enabled by default */
+ if (test_opt(vfs->mnt_sb, POSIX_ACL))
+ seq_puts(seq, ",acl");
+#endif
+
+#ifdef CONFIG_PRAMFS_XIP
+ /* xip not enabled by default */
+ if (test_opt(vfs->mnt_sb, XIP))
+ seq_puts(seq, ",xip");
+#endif
+
+ return 0;
+}
+
+int pram_remount(struct super_block *sb, int *mntflags, char *data)
+{
+ unsigned long old_sb_flags;
+ unsigned long old_mount_opt;
+ struct pram_super_block *ps;
+ struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+ int ret = -EINVAL;
+
+ /* Store the old options */
+ old_sb_flags = sb->s_flags;
+ old_mount_opt = sbi->s_mount_opt;
+
+ if (pram_parse_options(data, sbi, 1))
+ goto restore_opt;
+
+ sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
+ ((sbi->s_mount_opt & PRAM_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
+
+ if ((*mntflags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) {
+ ps = pram_get_super(sb);
+ pram_memunlock_super(sb, ps);
+ ps->s_mtime = cpu_to_be32(get_seconds()); /* update mount time */
+ pram_memlock_super(sb, ps);
+ }
+
+ ret = 0;
+ return ret;
+
+ restore_opt:
+ sb->s_flags = old_sb_flags;
+ sbi->s_mount_opt = old_mount_opt;
+ return ret;
+}
+
+void pram_put_super(struct super_block *sb)
+{
+ struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+ struct pram_super_block *ps = pram_get_super(sb);
+ u64 size = be64_to_cpu(ps->s_size);
+
+#ifdef CONFIG_PRAMFS_TEST
+ if (first_pram_super == sbi->virt_addr)
+ first_pram_super = NULL;
+#endif
+
+ pram_xattr_put_super(sb);
+ /* It's unmount time, so unmap the pramfs memory */
+ if (sbi->virt_addr) {
+ if (pram_is_protected(sb))
+ pram_writeable(sbi->virt_addr, size, 1);
+ iounmap(sbi->virt_addr);
+ sbi->virt_addr = NULL;
+ release_mem_region(sbi->phys_addr, size);
+ }
+
+ sb->s_fs_info = NULL;
+ kfree(sbi);
+}
+
+static struct inode *pram_alloc_inode(struct super_block *sb)
+{
+ struct pram_inode_vfs *vi = (struct pram_inode_vfs *)
+ kmem_cache_alloc(pram_inode_cachep, GFP_KERNEL);
+ if (!vi)
+ return NULL;
+ vi->vfs_inode.i_version = 1;
+ return &vi->vfs_inode;
+}
+
+static void pram_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(pram_inode_cachep, PRAM_I(inode));
+}
+
+static void init_once(void *foo)
+{
+ struct pram_inode_vfs *vi = (struct pram_inode_vfs *) foo;
+
+#ifdef CONFIG_PRAMFS_XATTR
+ init_rwsem(&vi->xattr_sem);
+#endif
+ mutex_init(&vi->truncate_mutex);
+ mutex_init(&vi->i_meta_mutex);
+ inode_init_once(&vi->vfs_inode);
+}
+
+static int __init init_inodecache(void)
+{
+ pram_inode_cachep = kmem_cache_create("pram_inode_cache",
+ sizeof(struct pram_inode_vfs),
+ 0, (SLAB_RECLAIM_ACCOUNT|
+ SLAB_MEM_SPREAD),
+ init_once);
+ if (pram_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void destroy_inodecache(void)
+{
+ kmem_cache_destroy(pram_inode_cachep);
+}
+
+/*
+ * the super block writes are all done "on the fly", so the
+ * super block is never in a "dirty" state, so there's no need
+ * for write_super.
+ */
+static struct super_operations pram_sops = {
+ .alloc_inode = pram_alloc_inode,
+ .destroy_inode = pram_destroy_inode,
+ .write_inode = pram_write_inode,
+ .dirty_inode = pram_dirty_inode,
+ .evict_inode = pram_evict_inode,
+ .put_super = pram_put_super,
+ .statfs = pram_statfs,
+ .remount_fs = pram_remount,
+ .show_options = pram_show_options,
+};
+
+static int pram_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data, struct vfsmount *mnt)
+{
+ return get_sb_nodev(fs_type, flags, data, pram_fill_super, mnt);
+}
+
+static struct file_system_type pram_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "pramfs",
+ .get_sb = pram_get_sb,
+ .kill_sb = kill_anon_super,
+};
+
+static struct inode *pram_nfs_get_inode(struct super_block *sb,
+ u64 ino, u32 generation)
+{
+ struct pram_super_block *ps = pram_get_super(sb);
+ struct inode *inode;
+
+ if (ino < PRAM_ROOT_INO)
+ return ERR_PTR(-ESTALE);
+ if (((ino - PRAM_ROOT_INO) >> PRAM_INODE_BITS) > be32_to_cpu(ps->s_inodes_count))
+ return ERR_PTR(-ESTALE);
+
+ inode = pram_iget(sb, ino);
+ if (!inode)
+ return ERR_PTR(-ESTALE);
+ if (generation && inode->i_generation != generation) {
+ /* we didn't find the right inode.. */
+ iput(inode);
+ return ERR_PTR(-ESTALE);
+ }
+ return inode;
+}
+
+static struct dentry *
+pram_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len,
+ int fh_type)
+{
+ return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
+ pram_nfs_get_inode);
+}
+
+static struct dentry *
+pram_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len,
+ int fh_type)
+{
+ return generic_fh_to_parent(sb, fid, fh_len, fh_type,
+ pram_nfs_get_inode);
+}
+
+static const struct export_operations pram_export_ops = {
+ .fh_to_dentry = pram_fh_to_dentry,
+ .fh_to_parent = pram_fh_to_parent,
+ .get_parent = pram_get_parent,
+};
+
+static int __init init_pram_fs(void)
+{
+ int rc = 0;
+
+ rc = init_pram_xattr();
+ if (rc)
+ return rc;
+
+ rc = init_inodecache();
+ if (rc)
+ goto out1;
+
+ rc = bdi_init(&pram_backing_dev_info);
+ if (rc)
+ goto out2;
+
+ rc = register_filesystem(&pram_fs_type);
+ if (rc)
+ goto out3;
+
+ return 0;
+
+out3:
+ bdi_destroy(&pram_backing_dev_info);
+out2:
+ destroy_inodecache();
+out1:
+ exit_pram_xattr();
+ return rc;
+}
+
+static void __exit exit_pram_fs(void)
+{
+ unregister_filesystem(&pram_fs_type);
+ bdi_destroy(&pram_backing_dev_info);
+ destroy_inodecache();
+ exit_pram_xattr();
+}
+
+MODULE_AUTHOR("Marco Stornelli <marco.stornelli@gmail.com>");
+MODULE_DESCRIPTION("Protected/Persistent RAM Filesystem");
+MODULE_LICENSE("GPL");
+
+module_init(init_pram_fs)
+module_exit(exit_pram_fs)
reply other threads:[~2010-12-16 17:59 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4D0A5387.6050409@gmail.com \
--to=marco.stornelli@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=linux-embedded@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=tim.bird@am.sony.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.