diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-15 15:27:34 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-15 15:27:34 -0700 |
commit | 837c0efd6fcc2b9203a543fe8ac256063024c037 (patch) | |
tree | b38fab3b1ac914e1e308adad10fbee9c1ba91617 | |
parent | e71d7aebb2c8d3da4fa1ef0721c874d0fc1f269b (diff) | |
download | ltsi-kernel-837c0efd6fcc2b9203a543fe8ac256063024c037.tar.gz |
pramfs patches added
18 files changed, 6791 insertions, 0 deletions
diff --git a/patches.pramfs/01-pramfs-documentation.patch b/patches.pramfs/01-pramfs-documentation.patch new file mode 100644 index 00000000000000..4d9da4c25bd419 --- /dev/null +++ b/patches.pramfs/01-pramfs-documentation.patch @@ -0,0 +1,211 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:15:53 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:09:16 +0200 +Subject: pramfs: documentation +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE993C.4050601@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +Documentation for PRAMFS. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> +--- + Documentation/filesystems/pramfs.txt | 179 +++++++++++++++++++++++++++++++++++ + Documentation/filesystems/xip.txt | 2 + 2 files changed, 181 insertions(+) + +--- /dev/null ++++ b/Documentation/filesystems/pramfs.txt +@@ -0,0 +1,179 @@ ++ ++PRAMFS Overview ++=============== ++ ++Many embedded systems have a block of non-volatile RAM separate from ++normal system memory, i.e. of which the kernel maintains no memory page ++descriptors. For such systems it would be beneficial to mount a ++fast read/write filesystem over this "I/O memory", for storing frequently ++accessed data that must survive system reboots and power cycles or volatile ++data avoiding to write on a disk or flash. An example usage might be system ++logs under /var/log or debug information of a flight-recorder. ++ ++Linux traditionally had no support for a persistent, non-volatile RAM-based ++filesystem, persistent meaning the filesystem survives a system reboot ++or power cycle intact. The RAM-based filesystems such as tmpfs and ramfs ++have no actual backing store but exist entirely in the page and buffer ++caches, hence the filesystem disappears after a system reboot or ++power cycle. ++ ++A relatively straightforward solution is to write a simple block driver ++for the non-volatile RAM, and mount over it any disk-based filesystem such ++as ext2, ext3, ext4, etc. ++ ++But the disk-based fs over non-volatile RAM block driver approach has ++some drawbacks: ++ ++1. Complexity of disk-based fs: disk-based filesystems such as ext2/ext3/ext4 ++ were designed for optimum performance on spinning disk media, so they ++ implement features such as block groups, which attempts to group inode data ++ into a contiguous set of data blocks to minimize disk seeking when accessing ++ files. For RAM there is no such concern; a file's data blocks can be ++ scattered throughout the media with no access speed penalty at all. So block ++ groups in a filesystem mounted over RAM just adds unnecessary ++ complexity. A better approach is to use a filesystem specifically ++ tailored to RAM media which does away with these disk-based features. ++ This increases the efficient use of space on the media, i.e. more ++ space is dedicated to actual file data storage and less to meta-data ++ needed to maintain that file data. ++ ++2. Different problems between disks and RAM: Because PRAMFS attempts to avoid ++ filesystem corruption caused by kernel bugs, dirty pages in the page cache ++ are not allowed to be written back to the backing-store RAM. This way, an ++ errant write into the page cache will not get written back to the filesystem. ++ However, if the backing-store RAM is comparable in access speed to system ++ memory, the penalty of not using caching is minimal. With this consideration ++ it's better to move file data directly between the user buffers and the backing ++ store RAM, i.e. use direct I/O. This prevents the unnecessary populating of ++ the page cache with dirty pages. However direct I/O has to be enabled at ++ every file open. To enable direct I/O at all times for all regular files ++ requires either that applications be modified to include the O_DIRECT flag on ++ all file opens, or that the filesystem used performs direct I/O by default. ++ ++The Persistent/Protected RAM Special Filesystem (PRAMFS) is a read/write ++filesystem that has been designed to address these issues. PRAMFS is targeted ++to fast I/O memory, and if the memory is non-volatile, the filesystem will be ++persistent. ++ ++In PRAMFS, direct I/O is enabled across all files in the filesystem, in other ++words the O_DIRECT flag is forced on every open of a PRAMFS file. Also, file ++I/O in the PRAMFS is always synchronous. There is no need to block the current ++process while the transfer to/from the PRAMFS is in progress, since one of ++the requirements of the PRAMFS is that the filesystem exists in fast RAM. So ++file I/O in PRAMFS is always direct, synchronous, and never blocks. ++ ++The data organization in PRAMFS can be thought of as an extremely simplified ++version of ext2, such that the ratio of data to meta-data is very high. ++ ++PRAMFS supports the execute-in-place. With XIP, instead of keeping data in the ++page cache, the need to have a page cache copy is eliminated completely. ++Read&write type operations are performed directly from/to the memory. For file ++mappings, the RAM itself is mapped directly into userspace. XIP, in addition, ++speed up the applications start-up time because it removes the needs of any ++copies. ++ ++PRAMFS is write protected. The page table entries that map the backing-store ++RAM are normally marked read-only. Write operations into the filesystem ++temporarily mark the affected pages as writeable, the write operation is ++carried out with locks held, and then the page table entries is ++marked read-only again. ++This feature provides protection against filesystem corruption caused by errant ++writes into the RAM due to kernel bugs for instance. In case there are systems ++where the write protection is not possible (for instance the RAM cannot be ++mapped with page tables), this feature can be disabled via the ++CONFIG_PRAMFS_WRITE_PROTECT config option. ++ ++PRAMFS supports extended attributes, ACLs and security labels. ++ ++In summary, PRAMFS is a light-weight, space-efficient special filesystem that ++is ideal for systems with a block of fast non-volatile RAM that need to access ++data on it using a standard filesytem interface. ++ ++Supported mount options ++======================= ++ ++The PRAMFS currently requires one mount option, and there are several ++optional mount options: ++ ++physaddr= Required. It tells PRAMFS the physical address of the ++ start of the RAM that makes up the filesystem. The ++ physical address must be located on a page boundary. ++ ++init= Optional. It is used to initialize the memory to an ++ empty filesystem. Any data in an existing filesystem ++ will be lost if this option is given. The parameter to ++ "init=" is the RAM in kilo/mega/giga bytes. ++ ++bs= Optional. It is used to specify a block size. It is ++ ignored if the "init=" option is not specified, since ++ otherwise the block size is read from the PRAMFS ++ super-block. The default blocksize is 2048 bytes, ++ and the allowed block sizes are 512, 1024, 2048, and ++ 4096. ++ ++bpi= Optional. It is used to specify the bytes per inode ++ ratio, i.e. for every N bytes in the filesystem, an ++ inode will be created. This behaves the same as the "-i" ++ option to mke2fs. It is ignored if the "init=" option is ++ not specified. ++ ++N= Optional. It is used to specify the number of inodes to ++ allocate in the inode table. If the option is not ++ specified, the bytes-per-inode ratio is used to ++ calculate the number of inodes. If neither the "N=" or ++ "bpi=" options are specified, the default behavior is to ++ reserve 5% of the total space in the filesystem for the ++ inode table. This option behaves the same as the "-N" ++ option to mke2fs. It is ignored if the "init=" option is ++ not specified. ++ ++errors= Optional. It can be "cont", "remount-ro" and "panic". With the ++ first value no action is done in case of error. With the second ++ one the fs is mounted read-only. with the third one a kernel ++ panic happens. Default action is to continue on error. ++ ++acl,noacl Optional. Enable/disable the support for access control lists ++ (disabled by default). ++ ++user_xattr, Optional. Enable/disable the support for the user extended ++user_noxattr attributes (disabled by default). ++ ++noprotect Optional. Disable the memory protection (enabled by default). ++ ++xip Optional. Enable the execute-in-place (disabled by default). ++ ++Examples: ++ ++mount -t pramfs -o physaddr=0x20000000,init=1M,bs=1k none /mnt/pram ++ ++This example locates the filesystem at physical address 0x20000000, and ++also requests an empty filesystem be initialized, of total size of one ++megabyte and blocksize of one kilobyte. The mount point is /mnt/pram. ++ ++mount -t pramfs -o physaddr=0x20000000 none /mnt/pram ++ ++This example locates the filesystem at physical address 0x20000000 as in ++the first example, but uses the intact filesystem that already exists. ++ ++Current Limitations ++=================== ++ ++- The RAM used for PRAMFS must be directly addressable. ++ ++- PRAMFS does not support hard links. ++ ++- PRAMFS supports only private memory mappings. This allows most ++ executables to run, but programs that attempt shared memory ++ mappings, such as X apps that use X shared memory, will fail. ++ ++- PRAMFS does not support quota settings. ++ ++Further Documentation ++===================== ++ ++If you are interested in the internal design of PRAMFS, there is ++documentation available at the Sourceforge PRAMFS home page at ++http://pramfs.sourceforge.net/. ++ ++Please send bug reports/comments/feedback to the pramfs development ++list at sourceforge: pramfs-devel@lists.sourceforge.net. +--- a/Documentation/filesystems/xip.txt ++++ b/Documentation/filesystems/xip.txt +@@ -48,6 +48,8 @@ blocks if needed. + This address space operation is mutually exclusive with readpage&writepage that + do page cache read/write operations. + The following filesystems support it as of today: ++- pramfs: persistent and protected RAM filesystem, see ++ Documentation/filesystems/pramfs.txt + - ext2: the second extended filesystem, see Documentation/filesystems/ext2.txt + + A set of file operations that do utilize get_xip_page can be found in diff --git a/patches.pramfs/02-pramfs-super-operations.patch b/patches.pramfs/02-pramfs-super-operations.patch new file mode 100644 index 00000000000000..0e384880c4c5b9 --- /dev/null +++ b/patches.pramfs/02-pramfs-super-operations.patch @@ -0,0 +1,997 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:16:06 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:09:30 +0200 +Subject: pramfs: super operations +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE994A.40008@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +Super block operations. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> +--- + fs/pramfs/super.c | 977 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 977 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/super.c +@@ -0,0 +1,977 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Super block operations. ++ * ++ * Copyright 2009-2011 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/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 <linux/cred.h> ++#include <linux/backing-dev.h> ++#include <linux/ioport.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 err: 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 = (__force void *)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\n", 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; ++ unsigned long long ulltmp; ++ char *end; ++ char org_end; ++ int err; ++ ++ if (!options || strncmp(options, "physaddr=", 9) != 0) ++ return (phys_addr_t)ULLONG_MAX; ++ options += 9; ++ end = strchr(options, ',') ?: options + strlen(options); ++ org_end = *end; ++ *end = '\0'; ++ err = kstrtoull(options, 0, &ulltmp); ++ *end = org_end; ++ options = end; ++ phys_addr = (phys_addr_t)ulltmp; ++ if (err) { ++ 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; ++ case Opt_noprotect: ++#ifdef CONFIG_PRAMFS_WRITE_PROTECT ++ if (remount) ++ goto bad_opt; ++ clear_opt(sbi->s_mount_opt, PROTECT); ++#endif ++ break; ++#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\n"); ++ 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\n"); ++ 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\n"); ++ 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 = PRAM_SB(sb); ++ ++ pram_info("creating an empty pramfs of size %lu\n", size); ++ sbi->virt_addr = pram_ioremap(sbi->phys_addr, size, ++ pram_is_protected(sb)); ++ ++ 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_be16(sbi->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 void pram_root_check(struct super_block *sb, struct pram_inode *root_pi) ++{ ++ pram_memunlock_inode(sb, root_pi); ++ ++ if (root_pi->i_d.d_next) { ++ pram_warn("root->next not NULL, trying to fix\n"); ++ goto fail1; ++ } ++ ++ if (!S_ISDIR(be16_to_cpu(root_pi->i_mode))) { ++ pram_warn("root is not a directory, trying to fix\n"); ++ goto fail2; ++ } ++ ++ if (pram_calc_checksum((u8 *)root_pi, PRAM_INODE_SIZE)) { ++ pram_warn("checksum error in root inode, trying to fix\n"); ++ goto fail3; ++ } ++ fail1: ++ root_pi->i_d.d_next = 0; ++ fail2: ++ root_pi->i_mode = cpu_to_be16(S_IRWXUGO|S_ISVTX|S_IFDIR); ++ fail3: ++ root_pi->i_d.d_parent = cpu_to_be64(PRAM_ROOT_INO); ++ pram_memlock_inode(sb, root_pi); ++} ++ ++static int pram_fill_super(struct super_block *sb, void *data, int silent) ++{ ++ struct pram_super_block *super, *super_redund; ++ struct pram_inode *root_pi; ++ struct pram_sb_info *sbi = NULL; ++ struct inode *root_i = NULL; ++ 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_pi = pram_init(sb, initsize); ++ ++ if (IS_ERR(root_pi)) ++ 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; ++ sbi->virt_addr = pram_ioremap(sbi->phys_addr, initsize, ++ pram_is_protected(sb)); ++ 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 */ ++ pram_memunlock_super(sb, super); ++ memcpy(super, super_redund, PRAM_SB_SIZE); ++ pram_memlock_super(sb, super); ++ } ++ } ++ ++ /* 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 */ ++ pram_memunlock_super(sb, super); ++ memcpy(super, super_redund, PRAM_SB_SIZE); ++ pram_memlock_super(sb, super); ++ } ++ } ++ ++ 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_pi = pram_get_inode(sb, PRAM_ROOT_INO); ++ ++ /* Check that the root inode is in a sane state */ ++ pram_root_check(sb, root_pi); ++ ++ /* Remap the whole filesystem now */ ++ if (pram_is_protected(sb)) ++ pram_writeable(sbi->virt_addr, PAGE_SIZE, 1); ++ iounmap((void __iomem *)sbi->virt_addr); ++ release_mem_region(sbi->phys_addr, PAGE_SIZE); ++ sbi->virt_addr = pram_ioremap(sbi->phys_addr, initsize, ++ pram_is_protected(sb)); ++ if (!sbi->virt_addr) { ++ printk(KERN_ERR "ioremap of the pramfs image failed\n"); ++ goto out; ++ } ++ super = pram_get_super(sb); ++ ++#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_flags |= MS_NOSEC; ++ root_i = pram_iget(sb, PRAM_ROOT_INO); ++ if (IS_ERR(root_i)) { ++ retval = PTR_ERR(root_i); ++ goto out; ++ } ++ ++ sb->s_root = d_alloc_root(root_i); ++ if (!sb->s_root) { ++ iput(root_i); ++ printk(KERN_ERR "get pramfs root inode failed\n"); ++ retval = -ENOMEM; ++ goto out; ++ } ++ ++ retval = 0; ++ return retval; ++ out: ++ if (sbi->virt_addr) { ++ if (pram_is_protected(sb)) ++ pram_writeable(sbi->virt_addr, initsize, 1); ++ iounmap((void __iomem *)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 = PRAM_SB(vfs->mnt_sb); ++ ++ 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 = PRAM_SB(sb); ++ int ret = -EINVAL; ++ ++ /* Store the old options */ ++ lock_super(sb); ++ 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); ++ /* update mount time */ ++ ps->s_mtime = cpu_to_be32(get_seconds()); ++ pram_memlock_super(sb, ps); ++ } ++ ++ unlock_super(sb); ++ ret = 0; ++ return ret; ++ ++ restore_opt: ++ sb->s_flags = old_sb_flags; ++ sbi->s_mount_opt = old_mount_opt; ++ unlock_super(sb); ++ return ret; ++} ++ ++static void pram_put_super(struct super_block *sb) ++{ ++ struct pram_sb_info *sbi = PRAM_SB(sb); ++ 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_NOFS); ++ if (!vi) ++ return NULL; ++ vi->vfs_inode.i_version = 1; ++ return &vi->vfs_inode; ++} ++ ++static void pram_i_callback(struct rcu_head *head) ++{ ++ struct inode *inode = container_of(head, struct inode, i_rcu); ++ INIT_LIST_HEAD(&inode->i_dentry); ++ kmem_cache_free(pram_inode_cachep, PRAM_I(inode)); ++} ++ ++static void pram_destroy_inode(struct inode *inode) ++{ ++ call_rcu(&inode->i_rcu, pram_i_callback); ++} ++ ++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->i_meta_mutex); ++ mutex_init(&vi->i_link_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 struct dentry *pram_mount(struct file_system_type *fs_type, ++ int flags, const char *dev_name, void *data) ++{ ++ return mount_nodev(fs_type, flags, data, pram_fill_super); ++} ++ ++static struct file_system_type pram_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "pramfs", ++ .mount = pram_mount, ++ .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 (IS_ERR(inode)) ++ return ERR_CAST(inode); ++ 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) diff --git a/patches.pramfs/03-pramfs-inode-operations.patch b/patches.pramfs/03-pramfs-inode-operations.patch new file mode 100644 index 00000000000000..0922dff18da87f --- /dev/null +++ b/patches.pramfs/03-pramfs-inode-operations.patch @@ -0,0 +1,823 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:16:22 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:09:45 +0200 +Subject: pramfs: inode operations +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE9959.8090902@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +Inode methods (allocate/free/read/write). + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> +--- + fs/pramfs/inode.c | 803 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 803 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/inode.c +@@ -0,0 +1,803 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Inode methods (allocate/free/read/write). ++ * ++ * Copyright 2009-2011 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/fs.h> ++#include <linux/sched.h> ++#include <linux/highuid.h> ++#include <linux/module.h> ++#include <linux/mpage.h> ++#include <linux/backing-dev.h> ++#include"pram.h" ++#include"xattr.h" ++#include"xip.h" ++#include"acl.h" ++ ++struct backing_dev_info pram_backing_dev_info __read_mostly = { ++ .ra_pages = 0,/* No readahead */ ++ .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK, ++}; ++ ++/* ++ * allocate a data blockfor inode andreturn it's absolute blocknr. ++ * Zeroes out the blockif zero set. Increments inode->i_blocks. ++ */ ++static int pram_new_data_block(struct inode *inode,unsigned long *blocknr, ++ int zero) ++{ ++ int errval = pram_new_block(inode->i_sb, blocknr, zero); ++ ++ if (!errval) { ++ struct pram_inode *pi = pram_get_inode(inode->i_sb, ++ inode->i_ino); ++ inode->i_blocks++; ++ pram_memunlock_inode(inode->i_sb, pi); ++ pi->i_blocks = cpu_to_be32(inode->i_blocks); ++ pram_memlock_inode(inode->i_sb, pi); ++ } ++ ++ return errval; ++} ++ ++/* ++ * find the offset to the block represented by the given inode's file ++ * relative block number. ++ */ ++u64 pram_find_data_block(struct inode *inode,unsigned long file_blocknr) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct pram_inode *pi; ++ u64 *row;/* ptr to row block */ ++ u64 *col;/* ptr to column blocks */ ++ u64 bp = 0; ++ unsigned int i_row, i_col; ++ unsigned int N = sb->s_blocksize >> 3;/* num block ptrs per block */ ++ unsigned int Nbits = sb->s_blocksize_bits - 3; ++ ++ pi = pram_get_inode(sb, inode->i_ino); ++ ++ i_row = file_blocknr >> Nbits; ++ i_col = file_blocknr & (N-1); ++ ++ row = pram_get_block(sb, be64_to_cpu(pi->i_type.reg.row_block)); ++ if (row) { ++ col = pram_get_block(sb, be64_to_cpu(row[i_row])); ++ if (col) ++ bp = be64_to_cpu(col[i_col]); ++ } ++ ++ return bp; ++} ++ ++/* ++ * Free data blocks from inode in the range start <=> end ++ */ ++static void __pram_truncate_blocks(struct inode *inode, loff_t start, ++ loff_t end) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct pram_inode *pi = pram_get_inode(sb, inode->i_ino); ++ int N = sb->s_blocksize >> 3;/* num block ptrs per block */ ++ int Nbits = sb->s_blocksize_bits - 3; ++ int first_row_index, last_row_index, i, j; ++ unsigned long blocknr, first_blocknr, last_blocknr; ++ unsigned int freed = 0; ++ u64 *row;/* ptr to row block */ ++ u64 *col;/* ptr to column blocks */ ++ ++ if (!pi->i_type.reg.row_block) ++ return; ++ ++ first_blocknr = (start + sb->s_blocksize - 1) >> sb->s_blocksize_bits; ++ ++ if (pi->i_flags & cpu_to_be32(PRAM_EOFBLOCKS_FL)) ++ last_blocknr = (1UL << (2*sb->s_blocksize_bits - 6)) - 1; ++ else ++ last_blocknr = end >> sb->s_blocksize_bits; ++ ++ if (first_blocknr > last_blocknr) ++ return; ++ ++ first_row_index = first_blocknr >> Nbits; ++ last_row_index = last_blocknr >> Nbits; ++ ++ row = pram_get_block(sb, be64_to_cpu(pi->i_type.reg.row_block)); ++ ++ for (i = first_row_index; i <= last_row_index; i++) { ++ int first_col_index = (i == first_row_index) ? ++ first_blocknr & (N-1) : 0; ++ int last_col_index = (i == last_row_index) ? ++ last_blocknr & (N-1) : N-1; ++ ++ if (unlikely(!row[i])) ++ continue; ++ ++ col = pram_get_block(sb, be64_to_cpu(row[i])); ++ ++ for (j = first_col_index; j <= last_col_index; j++) { ++ ++ if (unlikely(!col[j])) ++ continue; ++ ++ blocknr = pram_get_blocknr(sb, be64_to_cpu(col[j])); ++ pram_free_block(sb, blocknr); ++ freed++; ++ pram_memunlock_block(sb, col); ++ col[j] = 0; ++ pram_memlock_block(sb, col); ++ } ++ ++ if (first_col_index == 0) { ++ blocknr = pram_get_blocknr(sb, be64_to_cpu(row[i])); ++ pram_free_block(sb, blocknr); ++ pram_memunlock_block(sb, row); ++ row[i] = 0; ++ pram_memlock_block(sb, row); ++ } ++ } ++ ++ inode->i_blocks -= freed; ++ ++ if (start == 0) { ++ blocknr = pram_get_blocknr(sb, ++ be64_to_cpu(pi->i_type.reg.row_block)); ++ pram_free_block(sb, blocknr); ++ pram_memunlock_inode(sb, pi); ++ pi->i_type.reg.row_block = 0; ++ goto update_blocks; ++ } ++ pram_memunlock_inode(sb, pi); ++ ++ update_blocks: ++ pi->i_blocks = cpu_to_be32(inode->i_blocks); ++ pram_memlock_inode(sb, pi); ++} ++ ++static void pram_truncate_blocks(struct inode *inode, loff_t start, loff_t end) ++{ ++ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || ++ S_ISLNK(inode->i_mode))) ++ return; ++ if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) ++ return; ++ ++ __pram_truncate_blocks(inode, start, end); ++ inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; ++ pram_update_inode(inode); ++} ++ ++/* ++ * Allocate num data blocksfor inode, starting at given file-relative ++ * block number. ++ */ ++int pram_alloc_blocks(struct inode *inode,int file_blocknr,unsigned int num) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct pram_inode *pi = pram_get_inode(sb, inode->i_ino); ++ int N = sb->s_blocksize >> 3;/* num block ptrs per block */ ++ int Nbits = sb->s_blocksize_bits - 3; ++ int first_file_blocknr; ++ int last_file_blocknr; ++ int first_row_index, last_row_index; ++ int i, j, errval; ++ unsigned long blocknr; ++ u64 *row; ++ u64 *col; ++ ++ if (!pi->i_type.reg.row_block) { ++ /* alloc the 2nd order array block */ ++ errval = pram_new_block(sb, &blocknr, 1); ++ if (errval) { ++ pram_dbg("failed to alloc 2nd order array block\n"); ++ goto fail; ++ } ++ pram_memunlock_inode(sb, pi); ++ pi->i_type.reg.row_block = cpu_to_be64(pram_get_block_off(sb, ++ blocknr)); ++ pram_memlock_inode(sb, pi); ++ } ++ ++ row = pram_get_block(sb, be64_to_cpu(pi->i_type.reg.row_block)); ++ ++ first_file_blocknr = file_blocknr; ++ last_file_blocknr = file_blocknr + num - 1; ++ ++ first_row_index = first_file_blocknr >> Nbits; ++ last_row_index = last_file_blocknr >> Nbits; ++ ++ for (i = first_row_index; i <= last_row_index; i++) { ++ int first_col_index, last_col_index; ++ ++ /* ++ * we are starting anew row, so make sure ++ * there is a block allocatedfor the row. ++ */ ++ if (!row[i]) { ++ /* allocate the row block */ ++ errval = pram_new_block(sb, &blocknr, 1); ++ if (errval) { ++ pram_dbg("failed to alloc row block\n"); ++ goto fail; ++ } ++ pram_memunlock_block(sb, row); ++ row[i] = cpu_to_be64(pram_get_block_off(sb, blocknr)); ++ pram_memlock_block(sb, row); ++ } ++ col = pram_get_block(sb, be64_to_cpu(row[i])); ++ ++ first_col_index = (i == first_row_index) ? ++ first_file_blocknr & (N-1) : 0; ++ ++ last_col_index = (i == last_row_index) ? ++ last_file_blocknr & (N-1) : N-1; ++ ++ for (j = first_col_index; j <= last_col_index; j++) { ++ if (!col[j]) { ++ errval = pram_new_data_block(inode, &blocknr, ++ 1); ++ if (errval) { ++ pram_dbg("fail to alloc data block\n"); ++ /* For later recovery in truncate... */ ++ pram_memunlock_inode(inode->i_sb, pi); ++ pi->i_flags |= cpu_to_be32(PRAM_EOFBLOCKS_FL); ++ pram_memlock_inode(inode->i_sb, pi); ++ goto fail; ++ } ++ pram_memunlock_block(sb, col); ++ col[j] = cpu_to_be64(pram_get_block_off(sb, ++ blocknr)); ++ pram_memlock_block(sb, col); ++ } ++ } ++ } ++ ++ errval = 0; ++ fail: ++ return errval; ++} ++ ++static int pram_read_inode(struct inode *inode,struct pram_inode *pi) ++{ ++ int ret = -EIO; ++ ++ mutex_lock(&PRAM_I(inode)->i_meta_mutex); ++ ++ if (pram_calc_checksum((u8 *)pi, PRAM_INODE_SIZE)) { ++ pram_err(inode->i_sb,"checksum error in inode %08x\n", ++ (u32)inode->i_ino); ++ goto bad_inode; ++ } ++ ++ inode->i_mode = be16_to_cpu(pi->i_mode); ++ inode->i_uid = be32_to_cpu(pi->i_uid); ++ inode->i_gid = be32_to_cpu(pi->i_gid); ++ inode->i_nlink = be16_to_cpu(pi->i_links_count); ++ inode->i_size = be32_to_cpu(pi->i_size); ++ inode->i_atime.tv_sec = be32_to_cpu(pi->i_atime); ++ inode->i_ctime.tv_sec = be32_to_cpu(pi->i_ctime); ++ inode->i_mtime.tv_sec = be32_to_cpu(pi->i_mtime); ++ inode->i_atime.tv_nsec = inode->i_mtime.tv_nsec = ++ inode->i_ctime.tv_nsec = 0; ++ inode->i_generation = be32_to_cpu(pi->i_generation); ++ pram_set_inode_flags(inode, pi); ++ ++ /* checkif the inode is active. */ ++ if (inode->i_nlink == 0 && (inode->i_mode == 0 || ++ be32_to_cpu(pi->i_dtime))) { ++ /*this inode is deleted */ ++ pram_dbg("read inode: inode %lu not active", inode->i_ino); ++ ret = -ESTALE; ++ goto bad_inode; ++ } ++ ++ inode->i_blocks = be32_to_cpu(pi->i_blocks); ++ inode->i_ino = pram_get_inodenr(inode->i_sb, pi); ++ inode->i_mapping->a_ops = &pram_aops; ++ inode->i_mapping->backing_dev_info = &pram_backing_dev_info; ++ ++ switch (inode->i_mode & S_IFMT) { ++ case S_IFREG: ++ if (pram_use_xip(inode->i_sb)) { ++ inode->i_mapping->a_ops = &pram_aops_xip; ++ inode->i_fop = &pram_xip_file_operations; ++ }else { ++ inode->i_op = &pram_file_inode_operations; ++ inode->i_fop = &pram_file_operations; ++ } ++ break; ++ case S_IFDIR: ++ inode->i_op = &pram_dir_inode_operations; ++ inode->i_fop = &pram_dir_operations; ++ break; ++ case S_IFLNK: ++ inode->i_op = &pram_symlink_inode_operations; ++ break; ++ default: ++ inode->i_size = 0; ++ inode->i_op = &pram_special_inode_operations; ++ init_special_inode(inode, inode->i_mode, ++ be32_to_cpu(pi->i_type.dev.rdev)); ++ break; ++ } ++ ++ mutex_unlock(&PRAM_I(inode)->i_meta_mutex); ++ return 0; ++ ++ bad_inode: ++ make_bad_inode(inode); ++ mutex_unlock(&PRAM_I(inode)->i_meta_mutex); ++ return ret; ++} ++ ++int pram_update_inode(struct inode *inode) ++{ ++ struct pram_inode *pi; ++ int retval = 0; ++ ++ pi = pram_get_inode(inode->i_sb, inode->i_ino); ++ if (!pi) ++ return -EACCES; ++ ++ mutex_lock(&PRAM_I(inode)->i_meta_mutex); ++ ++ pram_memunlock_inode(inode->i_sb, pi); ++ pi->i_mode = cpu_to_be16(inode->i_mode); ++ pi->i_uid = cpu_to_be32(inode->i_uid); ++ pi->i_gid = cpu_to_be32(inode->i_gid); ++ pi->i_links_count = cpu_to_be16(inode->i_nlink); ++ pi->i_size = cpu_to_be32(inode->i_size); ++ pi->i_blocks = cpu_to_be32(inode->i_blocks); ++ pi->i_atime = cpu_to_be32(inode->i_atime.tv_sec); ++ pi->i_ctime = cpu_to_be32(inode->i_ctime.tv_sec); ++ pi->i_mtime = cpu_to_be32(inode->i_mtime.tv_sec); ++ pi->i_generation = cpu_to_be32(inode->i_generation); ++ pram_get_inode_flags(inode, pi); ++ ++ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) ++ pi->i_type.dev.rdev = cpu_to_be32(inode->i_rdev); ++ ++ pram_memlock_inode(inode->i_sb, pi); ++ ++ mutex_unlock(&PRAM_I(inode)->i_meta_mutex); ++ return retval; ++} ++ ++/* ++ * NOTE! When we get the inode, we're the only people ++ * that have access to it, and as such there are no ++ * race conditions we have to worry about. The inode ++ * is not on the hash-lists, and it cannot be reached ++ * through the filesystem because the directory entry ++ * has been deleted earlier. ++ */ ++static void pram_free_inode(struct inode *inode) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct pram_super_block *ps; ++ struct pram_inode *pi; ++ unsigned long inode_nr; ++ ++ pram_xattr_delete_inode(inode); ++ ++ lock_super(sb); ++ ++ inode_nr = (inode->i_ino - PRAM_ROOT_INO) >> PRAM_INODE_BITS; ++ ++ pi = pram_get_inode(sb, inode->i_ino); ++ pram_memunlock_inode(sb, pi); ++ pi->i_dtime = cpu_to_be32(get_seconds()); ++ pi->i_type.reg.row_block = 0; ++ pi->i_xattr = 0; ++ pram_memlock_inode(sb, pi); ++ ++ /* increment s_free_inodes_count */ ++ ps = pram_get_super(sb); ++ pram_memunlock_super(sb, ps); ++ if (inode_nr < be32_to_cpu(ps->s_free_inode_hint)) ++ ps->s_free_inode_hint = cpu_to_be32(inode_nr); ++ be32_add_cpu(&ps->s_free_inodes_count, 1); ++ if (be32_to_cpu(ps->s_free_inodes_count) == ++ be32_to_cpu(ps->s_inodes_count) - 1) { ++ /* filesystem is empty */ ++ pram_dbg("fs is empty!\n"); ++ ps->s_free_inode_hint = cpu_to_be32(1); ++ } ++ pram_memlock_super(sb, ps); ++ ++ unlock_super(sb); ++} ++ ++struct inode *pram_iget(struct super_block *sb,unsigned long ino) ++{ ++ struct inode *inode; ++ struct pram_inode *pi; ++ int err; ++ ++ inode = iget_locked(sb, ino); ++ if (unlikely(!inode)) ++ return ERR_PTR(-ENOMEM); ++ if (!(inode->i_state & I_NEW)) ++ return inode; ++ ++ pi = pram_get_inode(sb, ino); ++ if (!pi) { ++ err = -EACCES; ++ goto fail; ++ } ++ err = pram_read_inode(inode, pi); ++ if (unlikely(err)) ++ goto fail; ++ ++ unlock_new_inode(inode); ++ return inode; ++fail: ++ iget_failed(inode); ++ return ERR_PTR(err); ++} ++ ++void pram_evict_inode(struct inode *inode) ++{ ++ int want_delete = 0; ++ ++ if (!inode->i_nlink && !is_bad_inode(inode)) ++ want_delete = 1; ++ ++ truncate_inode_pages(&inode->i_data, 0); ++ ++ if (want_delete) { ++ /* unlink from chain in the inode's directory */ ++ pram_remove_link(inode); ++ pram_truncate_blocks(inode, 0, inode->i_size); ++ inode->i_size = 0; ++ } ++ ++ end_writeback(inode); ++ ++ if (want_delete) ++ pram_free_inode(inode); ++} ++ ++ ++ ++struct inode *pram_new_inode(struct inode *dir,int mode, ++ const struct qstr *qstr) ++{ ++ struct super_block *sb; ++ struct pram_sb_info *sbi; ++ struct pram_super_block *ps; ++ struct inode *inode; ++ struct pram_inode *pi =NULL; ++ struct pram_inode *diri =NULL; ++ int i, errval; ++ ino_t ino = 0; ++ ++ sb = dir->i_sb; ++ sbi = (struct pram_sb_info *)sb->s_fs_info; ++ inode = new_inode(sb); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ ++ lock_super(sb); ++ ps = pram_get_super(sb); ++ ++ if (ps->s_free_inodes_count) { ++ /* find the oldest unused pram inode */ ++ for (i = be32_to_cpu(ps->s_free_inode_hint); ++ i < be32_to_cpu(ps->s_inodes_count); i++) { ++ ino = PRAM_ROOT_INO + (i << PRAM_INODE_BITS); ++ pi = pram_get_inode(sb, ino); ++ /* checkif the inode is active. */ ++ if (be16_to_cpu(pi->i_links_count) == 0 && ++ (be16_to_cpu(pi->i_mode) == 0 || ++ be32_to_cpu(pi->i_dtime))) { ++ /*this inode is deleted */ ++ break; ++ } ++ } ++ ++ if (unlikely(i >= be32_to_cpu(ps->s_inodes_count))) { ++ pram_err(sb,"free inodes count!=0 but none free!?\n"); ++ errval = -ENOSPC; ++ goto fail1; ++ } ++ ++ pram_dbg("allocating inode %lu\n", ino); ++ }else { ++ pram_dbg("no space left to createnew inode!\n"); ++ errval = -ENOSPC; ++ goto fail1; ++ } ++ ++ diri = pram_get_inode(sb, dir->i_ino); ++ if (!diri) { ++ errval = -EACCES; ++ goto fail1; ++ } ++ ++ /* chosen inode is in ino */ ++ inode->i_ino = ino; ++ inode_init_owner(inode, dir, mode); ++ inode->i_blocks = inode->i_size = 0; ++ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; ++ ++ inode->i_generation = atomic_add_return(1, &sbi->next_generation); ++ ++ pram_memunlock_inode(sb, pi); ++ pi->i_d.d_next = 0; ++ pi->i_d.d_prev = 0; ++ pi->i_dtime = 0; ++ pi->i_flags = pram_mask_flags(mode, diri->i_flags); ++ pram_memlock_inode(sb, pi); ++ ++ pram_set_inode_flags(inode, pi); ++ ++ if (insert_inode_locked(inode) < 0) { ++ errval = -EINVAL; ++ goto fail2; ++ } ++ errval = pram_write_inode(inode,NULL); ++ if (errval) ++ goto fail2; ++ ++ errval = pram_init_acl(inode, dir); ++ if (errval) ++ goto fail2; ++ ++ errval = pram_init_security(inode, dir, qstr); ++ if (errval) ++ goto fail2; ++ ++ pram_memunlock_super(sb, ps); ++ be32_add_cpu(&ps->s_free_inodes_count, -1); ++ if (i < be32_to_cpu(ps->s_inodes_count)-1) ++ ps->s_free_inode_hint = cpu_to_be32(i+1); ++ else ++ ps->s_free_inode_hint = 0; ++ pram_memlock_super(sb, ps); ++ ++ unlock_super(sb); ++ ++ return inode; ++fail2: ++ unlock_super(sb); ++ inode->i_nlink = 0; ++ unlock_new_inode(inode); ++ iput(inode); ++ return ERR_PTR(errval); ++fail1: ++ unlock_super(sb); ++ make_bad_inode(inode); ++ iput(inode); ++ return ERR_PTR(errval); ++} ++ ++int pram_write_inode(struct inode *inode,struct writeback_control *wbc) ++{ ++ return pram_update_inode(inode); ++} ++ ++/* ++ * dirty_inode() is called from __mark_inode_dirty() ++ */ ++void pram_dirty_inode(struct inode *inode,int flags) ++{ ++ pram_update_inode(inode); ++} ++ ++static int pram_readpage(struct file *file,struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ struct super_block *sb = inode->i_sb; ++ loff_t offset, size; ++ unsigned long fillsize, blocknr, bytes_filled; ++ u64 block; ++ void *buf, *bp; ++ int ret; ++ ++ buf = kmap(page); ++ if (!buf) ++ return -ENOMEM; ++ ++ offset = page_offset(page); ++ size = i_size_read(inode); ++ blocknr = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits); ++ fillsize = 0; ++ bytes_filled = 0; ++ ret = 0; ++ if (offset < size) { ++ size -= offset; ++ fillsize = size > PAGE_SIZE ? PAGE_SIZE : size; ++ while (fillsize) { ++ int count = fillsize > sb->s_blocksize ? ++ sb->s_blocksize : fillsize; ++ block = pram_find_data_block(inode, blocknr); ++ if (likely(block)) { ++ bp = pram_get_block(sb, block); ++ if (!bp) { ++ SetPageError(page); ++ bytes_filled = 0; ++ ret = -EIO; ++ goto out; ++ } ++ memcpy(buf + bytes_filled, bp, count); ++ }else { ++ memset(buf + bytes_filled, 0, count); ++ } ++ bytes_filled += count; ++ fillsize -= count; ++ blocknr++; ++ } ++ } ++ out: ++ if (bytes_filled < PAGE_SIZE) ++ memset(buf + bytes_filled, 0, PAGE_SIZE - bytes_filled); ++ if (ret == 0) ++ SetPageUptodate(page); ++ ++ flush_dcache_page(page); ++ kunmap(page); ++ unlock_page(page); ++ return ret; ++} ++ ++/* ++ * Called to zeros out a single block. It's used in the"resize" ++ * to avoid to keep data incase the file grow up again. ++ */ ++static int pram_block_truncate_page(struct inode *inode, loff_t newsize) ++{ ++ struct super_block *sb = inode->i_sb; ++ unsigned long offset = newsize & (sb->s_blocksize - 1); ++ unsigned long blocknr, length; ++ u64 blockoff; ++ char *bp; ++ int ret = 0; ++ ++ /* Block boundary or extending ? */ ++ if (!offset || newsize > inode->i_size) ++ goto out; ++ ++ length = sb->s_blocksize - offset; ++ blocknr = newsize >> sb->s_blocksize_bits; ++ ++ blockoff = pram_find_data_block(inode, blocknr); ++ ++ /* Hole ? */ ++ if (!blockoff) ++ goto out; ++ ++ bp = pram_get_block(inode->i_sb, blockoff); ++ if (!bp) { ++ ret = -EACCES; ++ goto out; ++ } ++ pram_memunlock_block(sb, bp); ++ memset(bp + offset, 0, length); ++ pram_memlock_block(sb, bp); ++out: ++ return ret; ++} ++ ++static int pram_setsize(struct inode *inode, loff_t newsize) ++{ ++ int ret = 0; ++ loff_t oldsize = inode->i_size; ++ ++ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || ++ S_ISLNK(inode->i_mode))) ++ return -EINVAL; ++ if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) ++ return -EPERM; ++ ++ if (newsize != oldsize) { ++ if (mapping_is_xip(inode->i_mapping)) ++ ret = xip_truncate_page(inode->i_mapping, newsize); ++ else ++ ret = pram_block_truncate_page(inode, newsize); ++ ++ if (ret) ++ return ret; ++ i_size_write(inode, newsize); ++ } ++ /* ++ * Waitfor any concurrent readers to finish before to truncate the ++ * blocks. Anynew reader will see thenew i_size so no problem. ++ * In addition we have to wait, in xipcase, the call of xip_file_fault. ++ */ ++ synchronize_rcu(); ++ truncate_pagecache(inode, oldsize, newsize); ++ __pram_truncate_blocks(inode, newsize, oldsize); ++ /* Checkfor the flag EOFBLOCKS is still valid after the set size */ ++ check_eof_blocks(inode, newsize); ++ inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; ++ pram_update_inode(inode); ++ ++ return ret; ++} ++ ++int pram_notify_change(struct dentry *dentry,struct iattr *attr) ++{ ++ struct inode *inode = dentry->d_inode; ++ struct pram_inode *pi = pram_get_inode(inode->i_sb, inode->i_ino); ++ int error; ++ ++ if (!pi) ++ return -EACCES; ++ ++ error = inode_change_ok(inode, attr); ++ if (error) ++ return error; ++ ++ if (attr->ia_valid & ATTR_SIZE && ++ (attr->ia_size != inode->i_size || ++ pi->i_flags & cpu_to_be32(PRAM_EOFBLOCKS_FL))) { ++ error = pram_setsize(inode, attr->ia_size); ++ if (error) ++ return error; ++ } ++ setattr_copy(inode, attr); ++ if (attr->ia_valid & ATTR_MODE) ++ error = pram_acl_chmod(inode); ++ error = pram_update_inode(inode); ++ ++ return error; ++} ++ ++void pram_set_inode_flags(struct inode *inode,struct pram_inode *pi) ++{ ++ unsigned int flags = be32_to_cpu(pi->i_flags); ++ ++ inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC); ++ if (flags & FS_SYNC_FL) ++ inode->i_flags |= S_SYNC; ++ if (flags & FS_APPEND_FL) ++ inode->i_flags |= S_APPEND; ++ if (flags & FS_IMMUTABLE_FL) ++ inode->i_flags |= S_IMMUTABLE; ++ if (flags & FS_NOATIME_FL) ++ inode->i_flags |= S_NOATIME; ++ if (flags & FS_DIRSYNC_FL) ++ inode->i_flags |= S_DIRSYNC; ++ if (!pi->i_xattr) ++ inode_has_no_xattr(inode); ++} ++ ++void pram_get_inode_flags(struct inode *inode,struct pram_inode *pi) ++{ ++ unsigned int flags = inode->i_flags; ++ unsigned int pram_flags = be32_to_cpu(pi->i_flags); ++ ++ pram_flags &= ~(FS_SYNC_FL|FS_APPEND_FL|FS_IMMUTABLE_FL| ++ FS_NOATIME_FL|FS_DIRSYNC_FL); ++ if (flags & S_SYNC) ++ pram_flags |= FS_SYNC_FL; ++ if (flags & S_APPEND) ++ pram_flags |= FS_APPEND_FL; ++ if (flags & S_IMMUTABLE) ++ pram_flags |= FS_IMMUTABLE_FL; ++ if (flags & S_NOATIME) ++ pram_flags |= FS_NOATIME_FL; ++ if (flags & S_DIRSYNC) ++ pram_flags |= FS_DIRSYNC_FL; ++ ++ pi->i_flags = cpu_to_be32(pram_flags); ++} ++ ++const struct address_space_operations pram_aops = { ++ .readpage = pram_readpage, ++ .direct_IO = pram_direct_IO, ++}; ++ ++const struct address_space_operations pram_aops_xip = { ++ .get_xip_mem = pram_get_xip_mem, ++}; diff --git a/patches.pramfs/04-pramfs-file-operations.patch b/patches.pramfs/04-pramfs-file-operations.patch new file mode 100644 index 00000000000000..ad34e7ce71b8de --- /dev/null +++ b/patches.pramfs/04-pramfs-file-operations.patch @@ -0,0 +1,418 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:16:34 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:09:59 +0200 +Subject: pramfs: file operations +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE9967.40809@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +File operations. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> +--- + fs/pramfs/file.c | 398 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 398 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/file.c +@@ -0,0 +1,398 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * File operations for files. ++ * ++ * Copyright 2009-2011 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/fs.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/uio.h> ++#include <linux/mm.h> ++#include <linux/uaccess.h> ++#include <linux/falloc.h> ++#include "pram.h" ++#include "acl.h" ++#include "xip.h" ++#include "xattr.h" ++ ++/* ++ * The following functions are helper routines to copy to/from ++ * user space and iter over io vectors (mainly for readv/writev). ++ * They are used in the direct IO path. ++ */ ++static size_t __pram_iov_copy_from(char *vaddr, ++ const struct iovec *iov, size_t base, size_t bytes) ++{ ++ size_t copied = 0, left = 0; ++ ++ while (bytes) { ++ char __user *buf = iov->iov_base + base; ++ int copy = min(bytes, iov->iov_len - base); ++ ++ base = 0; ++ left = __copy_from_user(vaddr, buf, copy); ++ copied += copy; ++ bytes -= copy; ++ vaddr += copy; ++ iov++; ++ ++ if (unlikely(left)) ++ break; ++ } ++ return copied - left; ++} ++ ++static size_t __pram_iov_copy_to(char *vaddr, ++ const struct iovec *iov, size_t base, size_t bytes) ++{ ++ size_t copied = 0, left = 0; ++ ++ while (bytes) { ++ char __user *buf = iov->iov_base + base; ++ int copy = min(bytes, iov->iov_len - base); ++ ++ base = 0; ++ left = __copy_to_user(buf, vaddr, copy); ++ copied += copy; ++ bytes -= copy; ++ vaddr += copy; ++ iov++; ++ ++ if (unlikely(left)) ++ break; ++ } ++ return copied - left; ++} ++ ++static size_t pram_iov_copy_from(void *to, struct iov_iter *i, size_t bytes) ++{ ++ size_t copied; ++ ++ if (likely(i->nr_segs == 1)) { ++ int left; ++ char __user *buf = i->iov->iov_base + i->iov_offset; ++ left = __copy_from_user(to, buf, bytes); ++ copied = bytes - left; ++ } else { ++ copied = __pram_iov_copy_from(to, i->iov, i->iov_offset, bytes); ++ } ++ ++ return copied; ++} ++ ++static size_t pram_iov_copy_to(void *from, struct iov_iter *i, size_t bytes) ++{ ++ size_t copied; ++ ++ if (likely(i->nr_segs == 1)) { ++ int left; ++ char __user *buf = i->iov->iov_base + i->iov_offset; ++ left = __copy_to_user(buf, from, bytes); ++ copied = bytes - left; ++ } else { ++ copied = __pram_iov_copy_to(from, i->iov, i->iov_offset, bytes); ++ } ++ ++ return copied; ++} ++ ++static size_t __pram_clear_user(const struct iovec *iov, size_t base, ++ size_t bytes) ++{ ++ size_t claened = 0, left = 0; ++ ++ while (bytes) { ++ char __user *buf = iov->iov_base + base; ++ int clear = min(bytes, iov->iov_len - base); ++ ++ base = 0; ++ left = __clear_user(buf, clear); ++ claened += clear; ++ bytes -= clear; ++ iov++; ++ ++ if (unlikely(left)) ++ break; ++ } ++ return claened - left; ++} ++ ++static size_t pram_clear_user(struct iov_iter *i, size_t bytes) ++{ ++ size_t clear; ++ ++ if (likely(i->nr_segs == 1)) { ++ int left; ++ char __user *buf = i->iov->iov_base + i->iov_offset; ++ left = __clear_user(buf, bytes); ++ clear = bytes - left; ++ } else { ++ clear = __pram_clear_user(i->iov, i->iov_offset, bytes); ++ } ++ ++ return clear; ++} ++ ++static int pram_open_file(struct inode *inode, struct file *filp) ++{ ++ filp->f_flags |= O_DIRECT; ++ return generic_file_open(inode, filp); ++} ++ ++ssize_t pram_direct_IO(int rw, struct kiocb *iocb, ++ const struct iovec *iov, ++ loff_t offset, unsigned long nr_segs) ++{ ++ struct file *file = iocb->ki_filp; ++ struct inode *inode = file->f_mapping->host; ++ struct super_block *sb = inode->i_sb; ++ int progress = 0, hole = 0, alloc_once = 1; ++ ssize_t retval = 0; ++ void *tmp = NULL; ++ unsigned long blocknr, blockoff, blocknr_start; ++ struct iov_iter iter; ++ unsigned int num_blocks; ++ size_t length = iov_length(iov, nr_segs); ++ loff_t size; ++ ++ /* ++ * If we are in the write path we are under i_mutex but no lock held ++ * in the read, so we need to be sync with truncate to avoid race ++ * conditions. ++ */ ++ if (rw == READ) ++ rcu_read_lock(); ++ ++ size = i_size_read(inode); ++ ++ if (length < 0) { ++ retval = -EINVAL; ++ goto out; ++ } ++ if ((rw == READ) && (offset + length > size)) ++ length = size - offset; ++ if (!length) ++ goto out; ++ ++ /* find starting block number to access */ ++ blocknr = offset >> sb->s_blocksize_bits; ++ /* find starting offset within starting block */ ++ blockoff = offset & (sb->s_blocksize - 1); ++ /* find number of blocks to access */ ++ num_blocks = (blockoff + length + sb->s_blocksize - 1) >> ++ sb->s_blocksize_bits; ++ blocknr_start = blocknr; ++ ++ if (rw == WRITE) { ++ /* prepare a temporary buffer to hold a user data block ++ for writing. */ ++ tmp = kmalloc(sb->s_blocksize, GFP_KERNEL); ++ if (!tmp) { ++ retval = -ENOMEM; ++ goto out; ++ } ++ } ++ ++ iov_iter_init(&iter, iov, nr_segs, length, 0); ++ ++ while (length) { ++ int count; ++ u8 *bp = NULL; ++ u64 block = pram_find_data_block(inode, blocknr); ++ if (!block) { ++ if (alloc_once && rw == WRITE) { ++ /* ++ * Allocate the data blocks starting from ++ * blocknr to the end. ++ */ ++ retval = pram_alloc_blocks(inode, blocknr, ++ num_blocks - (blocknr - ++ blocknr_start)); ++ if (retval) ++ goto fail; ++ /* retry....*/ ++ block = pram_find_data_block(inode, blocknr); ++ BUG_ON(!block); ++ alloc_once = 0; ++ } else if (unlikely(rw == READ)) { ++ /* We are falling in a hole */ ++ hole = 1; ++ goto hole; ++ } ++ } ++ bp = (u8 *)pram_get_block(sb, block); ++ if (!bp) { ++ retval = -EACCES; ++ goto fail; ++ } ++ hole: ++ ++blocknr; ++ ++ count = blockoff + length > sb->s_blocksize ? ++ sb->s_blocksize - blockoff : length; ++ ++ if (rw == READ) { ++ if (unlikely(hole)) { ++ retval = pram_clear_user(&iter, count); ++ if (retval != count) { ++ retval = -EFAULT; ++ goto fail; ++ } ++ } else { ++ retval = pram_iov_copy_to(&bp[blockoff], &iter, ++ count); ++ if (retval != count) { ++ retval = -EFAULT; ++ goto fail; ++ } ++ } ++ } else { ++ retval = pram_iov_copy_from(tmp, &iter, count); ++ if (retval != count) { ++ retval = -EFAULT; ++ goto fail; ++ } ++ ++ pram_memunlock_block(inode->i_sb, bp); ++ memcpy(&bp[blockoff], tmp, count); ++ pram_memlock_block(inode->i_sb, bp); ++ } ++ ++ progress += count; ++ iov_iter_advance(&iter, count); ++ length -= count; ++ blockoff = 0; ++ hole = 0; ++ } ++ ++ retval = progress; ++ /* ++ * Check for the flag EOFBLOCKS is still valid after the extending ++ * write. ++ */ ++ if (rw == WRITE && (offset + length >= size)) ++ check_eof_blocks(inode, size + retval); ++ fail: ++ kfree(tmp); ++ out: ++ if (rw == READ) ++ rcu_read_unlock(); ++ return retval; ++} ++ ++static int pram_check_flags(int flags) ++{ ++ if (!(flags & O_DIRECT)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static long pram_fallocate(struct file *file, int mode, loff_t offset, ++ loff_t len) ++{ ++ struct inode *inode = file->f_path.dentry->d_inode; ++ long ret = 0; ++ unsigned long blocknr, blockoff; ++ int num_blocks, blocksize_mask; ++ struct pram_inode *pi; ++ loff_t new_size; ++ ++ /* We only support the FALLOC_FL_KEEP_SIZE mode */ ++ if (mode & ~FALLOC_FL_KEEP_SIZE) ++ return -EOPNOTSUPP; ++ ++ if (S_ISDIR(inode->i_mode)) ++ return -ENODEV; ++ ++ mutex_lock(&inode->i_mutex); ++ ++ new_size = len + offset; ++ if (!(mode & FALLOC_FL_KEEP_SIZE) && new_size > inode->i_size) { ++ ret = inode_newsize_ok(inode, new_size); ++ if (ret) ++ goto out; ++ } ++ ++ blocksize_mask = (1 << inode->i_sb->s_blocksize_bits) - 1; ++ blocknr = offset >> inode->i_sb->s_blocksize_bits; ++ blockoff = offset & blocksize_mask; ++ num_blocks = (blockoff + len + blocksize_mask) >> ++ inode->i_sb->s_blocksize_bits; ++ ret = pram_alloc_blocks(inode, blocknr, num_blocks); ++ if (ret) ++ goto out; ++ ++ if (mode & FALLOC_FL_KEEP_SIZE) { ++ pi = pram_get_inode(inode->i_sb, inode->i_ino); ++ if (!pi) { ++ ret = -EACCES; ++ goto out; ++ } ++ pram_memunlock_inode(inode->i_sb, pi); ++ pi->i_flags |= cpu_to_be32(PRAM_EOFBLOCKS_FL); ++ pram_memlock_inode(inode->i_sb, pi); ++ } ++ ++ inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; ++ if (!(mode & FALLOC_FL_KEEP_SIZE) && new_size > inode->i_size) ++ inode->i_size = new_size; ++ ret = pram_update_inode(inode); ++ out: ++ mutex_unlock(&inode->i_mutex); ++ return ret; ++} ++ ++const struct file_operations pram_file_operations = { ++ .llseek = generic_file_llseek, ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = generic_file_aio_write, ++ .mmap = generic_file_readonly_mmap, ++ .open = pram_open_file, ++ .fsync = noop_fsync, ++ .check_flags = pram_check_flags, ++ .unlocked_ioctl = pram_ioctl, ++ .splice_read = generic_file_splice_read, ++ .fallocate = pram_fallocate, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = pram_compat_ioctl, ++#endif ++}; ++ ++#ifdef CONFIG_PRAMFS_XIP ++const struct file_operations pram_xip_file_operations = { ++ .llseek = generic_file_llseek, ++ .read = pram_xip_file_read, ++ .write = xip_file_write, ++ .mmap = pram_xip_file_mmap, ++ .open = generic_file_open, ++ .fsync = noop_fsync, ++ .unlocked_ioctl = pram_ioctl, ++ .fallocate = pram_fallocate, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = pram_compat_ioctl, ++#endif ++}; ++#endif ++ ++const struct inode_operations pram_file_inode_operations = { ++#ifdef CONFIG_PRAMFS_XATTR ++ .setxattr = generic_setxattr, ++ .getxattr = generic_getxattr, ++ .listxattr = pram_listxattr, ++ .removexattr = generic_removexattr, ++#endif ++ .setattr = pram_notify_change, ++ .check_acl = pram_check_acl, ++}; diff --git a/patches.pramfs/05-pramfs-block-allocation.patch b/patches.pramfs/05-pramfs-block-allocation.patch new file mode 100644 index 00000000000000..bd7deb1c1f20c7 --- /dev/null +++ b/patches.pramfs/05-pramfs-block-allocation.patch @@ -0,0 +1,180 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:16:46 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:10:11 +0200 +Subject: pramfs: block allocation +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE9973.3020402@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +Block allocation operations. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> +--- + fs/pramfs/balloc.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 160 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/balloc.c +@@ -0,0 +1,160 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * The blocks allocation and deallocation routines. ++ * ++ * Copyright 2009-2011 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/fs.h> ++#include <linux/bitops.h> ++#include "pram.h" ++ ++void pram_bitmap_fill(unsigned long *dst, int nbits) ++{ ++ size_t nlongs = BITS_TO_LONGS(nbits); ++ if (!small_const_nbits(nbits)) { ++ int len = (nlongs - 1) * sizeof(unsigned long); ++ memset(dst, 0xff, len); ++ } ++ if (BITS_PER_LONG == 64) ++ dst[nlongs - 1] = cpu_to_le64(BITMAP_LAST_WORD_MASK(nbits)); ++ else ++ dst[nlongs - 1] = cpu_to_le32(BITMAP_LAST_WORD_MASK(nbits)); ++} ++ ++/* ++ * This just marks in-use the blocks that make up the bitmap. ++ * The bitmap must be writeable before calling. ++ */ ++void pram_init_bitmap(struct super_block *sb) ++{ ++ struct pram_super_block *ps = pram_get_super(sb); ++ unsigned long *bitmap = pram_get_bitmap(sb); ++ int blocks = be32_to_cpu(ps->s_bitmap_blocks); ++ ++ memset(bitmap, 0, blocks << sb->s_blocksize_bits); ++ ++ pram_bitmap_fill(bitmap, blocks); ++} ++ ++ ++/* Free absolute blocknr */ ++void pram_free_block(struct super_block *sb, unsigned long blocknr) ++{ ++ struct pram_super_block *ps; ++ u64 bitmap_block; ++ unsigned long bitmap_bnr; ++ void *bitmap; ++ void *bp; ++ ++ lock_super(sb); ++ ++ bitmap = pram_get_bitmap(sb); ++ /* ++ * find the block within the bitmap that contains the inuse bit ++ * for the block we need to free. We need to unlock this bitmap ++ * block to clear the inuse bit. ++ */ ++ bitmap_bnr = blocknr >> (3 + sb->s_blocksize_bits); ++ bitmap_block = pram_get_block_off(sb, bitmap_bnr); ++ bp = pram_get_block(sb, bitmap_block); ++ ++ pram_memunlock_block(sb, bp); ++ pram_clear_bit(blocknr, bitmap); /* mark the block free */ ++ pram_memlock_block(sb, bp); ++ ++ ps = pram_get_super(sb); ++ pram_memunlock_super(sb, ps); ++ ++ if (blocknr < be32_to_cpu(ps->s_free_blocknr_hint)) ++ ps->s_free_blocknr_hint = cpu_to_be32(blocknr); ++ be32_add_cpu(&ps->s_free_blocks_count, 1); ++ pram_memlock_super(sb, ps); ++ ++ unlock_super(sb); ++} ++ ++ ++/* ++ * allocate a block and return it's absolute blocknr. Zeroes out the ++ * block if zero set. ++ */ ++int pram_new_block(struct super_block *sb, unsigned long *blocknr, int zero) ++{ ++ struct pram_super_block *ps; ++ u64 bitmap_block; ++ unsigned long bnr, bitmap_bnr; ++ int errval; ++ void *bitmap; ++ void *bp; ++ ++ lock_super(sb); ++ ps = pram_get_super(sb); ++ bitmap = pram_get_bitmap(sb); ++ ++ if (ps->s_free_blocks_count) { ++ /* find the oldest unused block */ ++ bnr = pram_find_next_zero_bit(bitmap, ++ be32_to_cpu(ps->s_blocks_count), ++ be32_to_cpu(ps->s_free_blocknr_hint)); ++ ++ if (bnr < be32_to_cpu(ps->s_bitmap_blocks) || ++ bnr >= be32_to_cpu(ps->s_blocks_count)) { ++ pram_dbg("no free blocks found!\n"); ++ errval = -ENOSPC; ++ goto fail; ++ } ++ ++ pram_memunlock_super(sb, ps); ++ be32_add_cpu(&ps->s_free_blocks_count, -1); ++ if (bnr < (be32_to_cpu(ps->s_blocks_count)-1)) ++ ps->s_free_blocknr_hint = cpu_to_be32(bnr+1); ++ else ++ ps->s_free_blocknr_hint = 0; ++ pram_memlock_super(sb, ps); ++ } else { ++ pram_dbg("all blocks allocated\n"); ++ errval = -ENOSPC; ++ goto fail; ++ } ++ ++ /* ++ * find the block within the bitmap that contains the inuse bit ++ * for the unused block we just found. We need to unlock it to ++ * set the inuse bit. ++ */ ++ bitmap_bnr = bnr >> (3 + sb->s_blocksize_bits); ++ bitmap_block = pram_get_block_off(sb, bitmap_bnr); ++ bp = pram_get_block(sb, bitmap_block); ++ ++ pram_memunlock_block(sb, bp); ++ pram_set_bit(bnr, bitmap); /* mark the new block in use */ ++ pram_memlock_block(sb, bp); ++ ++ if (zero) { ++ bp = pram_get_block(sb, pram_get_block_off(sb, bnr)); ++ pram_memunlock_block(sb, bp); ++ memset(bp, 0, sb->s_blocksize); ++ pram_memlock_block(sb, bp); ++ } ++ ++ *blocknr = bnr; ++ pram_dbg("allocated blocknr %lu", bnr); ++ errval = 0; ++ fail: ++ unlock_super(sb); ++ return errval; ++} ++ ++unsigned long pram_count_free_blocks(struct super_block *sb) ++{ ++ struct pram_super_block *ps = pram_get_super(sb); ++ return be32_to_cpu(ps->s_free_blocks_count); ++} diff --git a/patches.pramfs/06-pramfs-inode-operations-for-dirs.patch b/patches.pramfs/06-pramfs-inode-operations-for-dirs.patch new file mode 100644 index 00000000000000..84662393702a45 --- /dev/null +++ b/patches.pramfs/06-pramfs-inode-operations-for-dirs.patch @@ -0,0 +1,396 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:16:57 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:10:23 +0200 +Subject: pramfs: inode operations for dirs +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE997F.6020507@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +Inode operations for directories. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> +--- + fs/pramfs/namei.c | 376 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 376 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/namei.c +@@ -0,0 +1,376 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Inode operations for directories. ++ * ++ * Copyright 2009-2011 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/fs.h> ++#include <linux/pagemap.h> ++#include "pram.h" ++#include "acl.h" ++#include "xattr.h" ++#include "xip.h" ++ ++/* ++ * Couple of helper functions - make the code slightly cleaner. ++ */ ++ ++static inline void pram_inc_count(struct inode *inode) ++{ ++ inode->i_nlink++; ++ pram_write_inode(inode, NULL); ++} ++ ++static inline void pram_dec_count(struct inode *inode) ++{ ++ if (inode->i_nlink) { ++ inode->i_nlink--; ++ pram_write_inode(inode, NULL); ++ } ++} ++ ++static inline int pram_add_nondir(struct inode *dir, ++ struct dentry *dentry, ++ struct inode *inode) ++{ ++ int err = pram_add_link(dentry, inode); ++ if (!err) { ++ d_instantiate(dentry, inode); ++ unlock_new_inode(inode); ++ return 0; ++ } ++ pram_dec_count(inode); ++ unlock_new_inode(inode); ++ iput(inode); ++ return err; ++} ++ ++/* ++ * Methods themselves. ++ */ ++ ++static ino_t pram_inode_by_name(struct inode *dir, struct dentry *dentry) ++{ ++ struct pram_inode *pi; ++ ino_t ino; ++ int namelen; ++ ++ pi = pram_get_inode(dir->i_sb, dir->i_ino); ++ ino = be64_to_cpu(pi->i_type.dir.head); ++ ++ while (ino) { ++ pi = pram_get_inode(dir->i_sb, ino); ++ ++ if (pi->i_links_count) { ++ namelen = strlen(pi->i_d.d_name); ++ ++ if (namelen == dentry->d_name.len && ++ !memcmp(dentry->d_name.name, ++ pi->i_d.d_name, namelen)) ++ break; ++ } ++ ++ ino = be64_to_cpu(pi->i_d.d_next); ++ } ++ ++ return ino; ++} ++ ++static struct dentry *pram_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ struct inode *inode = NULL; ++ ino_t ino; ++ ++ if (dentry->d_name.len > PRAM_NAME_LEN) ++ return ERR_PTR(-ENAMETOOLONG); ++ ++ ino = pram_inode_by_name(dir, dentry); ++ if (ino) { ++ inode = pram_iget(dir->i_sb, ino); ++ if (inode == ERR_PTR(-ESTALE)) { ++ pram_err(dir->i_sb, __func__, ++ "deleted inode referenced: %lu", ++ (unsigned long) ino); ++ return ERR_PTR(-EIO); ++ } ++ } ++ ++ return d_splice_alias(inode, dentry); ++} ++ ++ ++/* ++ * By the time this is called, we already have created ++ * the directory cache entry for the new file, but it ++ * is so far negative - it has no inode. ++ * ++ * If the create succeeds, we fill in the inode information ++ * with d_instantiate(). ++ */ ++static int pram_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *nd) ++{ ++ struct inode *inode = pram_new_inode(dir, mode, &dentry->d_name); ++ int err = PTR_ERR(inode); ++ if (!IS_ERR(inode)) { ++ inode->i_op = &pram_file_inode_operations; ++ if (pram_use_xip(inode->i_sb)) { ++ inode->i_mapping->a_ops = &pram_aops_xip; ++ inode->i_fop = &pram_xip_file_operations; ++ } else { ++ inode->i_fop = &pram_file_operations; ++ inode->i_mapping->a_ops = &pram_aops; ++ } ++ err = pram_add_nondir(dir, dentry, inode); ++ } ++ return err; ++} ++ ++static int pram_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ dev_t rdev) ++{ ++ struct inode *inode = pram_new_inode(dir, mode, &dentry->d_name); ++ int err = PTR_ERR(inode); ++ if (!IS_ERR(inode)) { ++ init_special_inode(inode, mode, rdev); ++ inode->i_op = &pram_special_inode_operations; ++ pram_write_inode(inode, NULL); /* update rdev */ ++ err = pram_add_nondir(dir, dentry, inode); ++ } ++ return err; ++} ++ ++static int pram_symlink(struct inode *dir, struct dentry *dentry, ++ const char *symname) ++{ ++ struct super_block *sb = dir->i_sb; ++ int err = -ENAMETOOLONG; ++ unsigned len = strlen(symname); ++ struct inode *inode; ++ ++ if (len+1 > sb->s_blocksize) ++ goto out; ++ ++ inode = pram_new_inode(dir, S_IFLNK | S_IRWXUGO, &dentry->d_name); ++ err = PTR_ERR(inode); ++ if (IS_ERR(inode)) ++ goto out; ++ ++ inode->i_op = &pram_symlink_inode_operations; ++ inode->i_mapping->a_ops = &pram_aops; ++ ++ err = pram_block_symlink(inode, symname, len); ++ if (err) ++ goto out_fail; ++ ++ inode->i_size = len; ++ pram_write_inode(inode, NULL); ++ ++ err = pram_add_nondir(dir, dentry, inode); ++out: ++ return err; ++ ++out_fail: ++ pram_dec_count(inode); ++ unlock_new_inode(inode); ++ iput(inode); ++ goto out; ++} ++ ++static int pram_link(struct dentry *dest_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ pram_dbg("hard links not supported\n"); ++ return -EOPNOTSUPP; ++} ++ ++static int pram_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ struct inode *inode = dentry->d_inode; ++ inode->i_ctime = dir->i_ctime; ++ pram_dec_count(inode); ++ return 0; ++} ++ ++static int pram_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++{ ++ struct inode *inode; ++ struct pram_inode *pi; ++ int err = -EMLINK; ++ ++ if (dir->i_nlink >= PRAM_LINK_MAX) ++ goto out; ++ ++ pram_inc_count(dir); ++ ++ inode = pram_new_inode(dir, S_IFDIR | mode, &dentry->d_name); ++ err = PTR_ERR(inode); ++ if (IS_ERR(inode)) ++ goto out_dir; ++ ++ inode->i_op = &pram_dir_inode_operations; ++ inode->i_fop = &pram_dir_operations; ++ inode->i_mapping->a_ops = &pram_aops; ++ ++ pram_inc_count(inode); ++ ++ /* make the new directory empty */ ++ pi = pram_get_inode(dir->i_sb, inode->i_ino); ++ pram_memunlock_inode(dir->i_sb, pi); ++ pi->i_type.dir.head = pi->i_type.dir.tail = 0; ++ pram_memlock_inode(dir->i_sb, pi); ++ ++ err = pram_add_link(dentry, inode); ++ if (err) ++ goto out_fail; ++ ++ d_instantiate(dentry, inode); ++ unlock_new_inode(inode); ++out: ++ return err; ++ ++out_fail: ++ pram_dec_count(inode); ++ pram_dec_count(inode); ++ unlock_new_inode(inode); ++ iput(inode); ++out_dir: ++ pram_dec_count(dir); ++ goto out; ++} ++ ++static int pram_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ struct inode *inode = dentry->d_inode; ++ struct pram_inode *pi; ++ int err = -ENOTEMPTY; ++ ++ if (!inode) ++ return -ENOENT; ++ ++ pi = pram_get_inode(dir->i_sb, inode->i_ino); ++ ++ /* directory to delete is empty? */ ++ if (pi->i_type.dir.tail == 0) { ++ inode->i_ctime = dir->i_ctime; ++ inode->i_size = 0; ++ inode->i_nlink = 0; ++ pram_write_inode(inode, NULL); ++ pram_dec_count(dir); ++ err = 0; ++ } else { ++ pram_dbg("dir not empty\n"); ++ } ++ ++ return err; ++} ++ ++static int pram_rename(struct inode *old_dir, ++ struct dentry *old_dentry, ++ struct inode *new_dir, ++ struct dentry *new_dentry) ++{ ++ struct inode *old_inode = old_dentry->d_inode; ++ struct inode *new_inode = new_dentry->d_inode; ++ struct pram_inode *pi_new; ++ int err = -ENOENT; ++ ++ if (new_inode) { ++ err = -ENOTEMPTY; ++ pi_new = pram_get_inode(new_dir->i_sb, new_inode->i_ino); ++ if (S_ISDIR(old_inode->i_mode)) { ++ if (pi_new->i_type.dir.tail != 0) ++ goto out; ++ if (new_inode->i_nlink) ++ drop_nlink(new_inode); ++ } ++ ++ new_inode->i_ctime = CURRENT_TIME; ++ pram_dec_count(new_inode); ++ } else { ++ if (S_ISDIR(old_inode->i_mode)) { ++ err = -EMLINK; ++ if (new_dir->i_nlink >= PRAM_LINK_MAX) ++ goto out; ++ pram_dec_count(old_dir); ++ pram_inc_count(new_dir); ++ } ++ } ++ ++ /* unlink the inode from the old directory ... */ ++ err = pram_remove_link(old_inode); ++ if (err) ++ goto out; ++ ++ /* and link it into the new directory. */ ++ err = pram_add_link(new_dentry, old_inode); ++ if (err) ++ goto out; ++ ++ err = 0; ++ out: ++ return err; ++} ++ ++struct dentry *pram_get_parent(struct dentry *child) ++{ ++ struct inode *inode; ++ struct pram_inode *pi, *piparent; ++ ino_t ino; ++ ++ pi = pram_get_inode(child->d_inode->i_sb, child->d_inode->i_ino); ++ if (!pi) ++ return ERR_PTR(-EACCES); ++ ++ piparent = pram_get_inode(child->d_inode->i_sb, ++ be64_to_cpu(pi->i_d.d_parent)); ++ if (!pi) ++ return ERR_PTR(-ENOENT); ++ ++ ino = pram_get_inodenr(child->d_inode->i_sb, piparent); ++ if (ino) ++ inode = pram_iget(child->d_inode->i_sb, ino); ++ else ++ return ERR_PTR(-ENOENT); ++ ++ return d_obtain_alias(inode); ++} ++ ++const struct inode_operations pram_dir_inode_operations = { ++ .create = pram_create, ++ .lookup = pram_lookup, ++ .link = pram_link, ++ .unlink = pram_unlink, ++ .symlink = pram_symlink, ++ .mkdir = pram_mkdir, ++ .rmdir = pram_rmdir, ++ .mknod = pram_mknod, ++ .rename = pram_rename, ++#ifdef CONFIG_PRAMFS_XATTR ++ .setxattr = generic_setxattr, ++ .getxattr = generic_getxattr, ++ .listxattr = pram_listxattr, ++ .removexattr = generic_removexattr, ++#endif ++ .setattr = pram_notify_change, ++ .check_acl = pram_check_acl, ++}; ++ ++const struct inode_operations pram_special_inode_operations = { ++#ifdef CONFIG_PRAMFS_XATTR ++ .setxattr = generic_setxattr, ++ .getxattr = generic_getxattr, ++ .listxattr = pram_listxattr, ++ .removexattr = generic_removexattr, ++#endif ++ .setattr = pram_notify_change, ++ .check_acl = pram_check_acl, ++}; diff --git a/patches.pramfs/07-pramfs-symlinks.patch b/patches.pramfs/07-pramfs-symlinks.patch new file mode 100644 index 00000000000000..9725f9a55c8375 --- /dev/null +++ b/patches.pramfs/07-pramfs-symlinks.patch @@ -0,0 +1,96 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:17:09 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:10:36 +0200 +Subject: pramfs: symlinks +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE998C.1040504@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +Symlink operations. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> +--- + fs/pramfs/symlink.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 76 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/symlink.c +@@ -0,0 +1,76 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Symlink operations ++ * ++ * Copyright 2009-2011 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/fs.h> ++#include "pram.h" ++#include "xattr.h" ++ ++int pram_block_symlink(struct inode *inode, const char *symname, int len) ++{ ++ struct super_block *sb = inode->i_sb; ++ u64 block; ++ char *blockp; ++ int err; ++ ++ err = pram_alloc_blocks(inode, 0, 1); ++ if (err) ++ return err; ++ ++ block = pram_find_data_block(inode, 0); ++ blockp = pram_get_block(sb, block); ++ ++ pram_memunlock_block(sb, blockp); ++ memcpy(blockp, symname, len); ++ blockp[len] = '\0'; ++ pram_memlock_block(sb, blockp); ++ return 0; ++} ++ ++static int pram_readlink(struct dentry *dentry, char __user *buffer, int buflen) ++{ ++ struct inode *inode = dentry->d_inode; ++ struct super_block *sb = inode->i_sb; ++ u64 block; ++ char *blockp; ++ ++ block = pram_find_data_block(inode, 0); ++ blockp = pram_get_block(sb, block); ++ return vfs_readlink(dentry, buffer, buflen, blockp); ++} ++ ++static void *pram_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ struct inode *inode = dentry->d_inode; ++ struct super_block *sb = inode->i_sb; ++ off_t block; ++ int status; ++ char *blockp; ++ ++ block = pram_find_data_block(inode, 0); ++ blockp = pram_get_block(sb, block); ++ status = vfs_follow_link(nd, blockp); ++ return ERR_PTR(status); ++} ++ ++const struct inode_operations pram_symlink_inode_operations = { ++ .readlink = pram_readlink, ++ .follow_link = pram_follow_link, ++ .setattr = pram_notify_change, ++#ifdef CONFIG_PRAMFS_XATTR ++ .setxattr = generic_setxattr, ++ .getxattr = generic_getxattr, ++ .listxattr = pram_listxattr, ++ .removexattr = generic_removexattr, ++#endif ++}; diff --git a/patches.pramfs/08-pramfs-headers.patch b/patches.pramfs/08-pramfs-headers.patch new file mode 100644 index 00000000000000..d4d4f8098b7fb2 --- /dev/null +++ b/patches.pramfs/08-pramfs-headers.patch @@ -0,0 +1,516 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:17:23 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:10:49 +0200 +Subject: pramfs: headers +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE9999.8000104@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +Definitions for the PRAMFS filesystem. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> +--- + fs/pramfs/pram.h | 283 +++++++++++++++++++++++++++++++++++++++++++++ + include/linux/fs.h | 2 + include/linux/magic.h | 1 + include/linux/pram_fs.h | 130 ++++++++++++++++++++ + include/linux/pram_fs_sb.h | 45 +++++++ + 5 files changed, 461 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/pram.h +@@ -0,0 +1,283 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Definitions for the PRAMFS filesystem. ++ * ++ * Copyright 2009-2011 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. ++ */ ++#ifndef __PRAM_H ++#define __PRAM_H ++ ++#include <linux/buffer_head.h> ++#include <linux/pram_fs.h> ++#include <linux/pram_fs_sb.h> ++#include <linux/crc16.h> ++#include <linux/mutex.h> ++#include <linux/rcupdate.h> ++#include <linux/types.h> ++#include "wprotect.h" ++ ++/* ++ * Debug code ++ */ ++#ifdef pr_fmt ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++#endif ++ ++#define pram_dbg(s, args...) pr_debug(s, ## args) ++#define pram_err(sb, s, args...) pram_error_mng(sb, s, ## args) ++#define pram_warn(s, args...) pr_warning(s, ## args) ++#define pram_info(s, args...) pr_info(s, ## args) ++ ++#define pram_set_bit __test_and_set_bit_le ++#define pram_clear_bit __test_and_clear_bit_le ++#define pram_find_next_zero_bit find_next_zero_bit_le ++ ++#define clear_opt(o, opt) (o &= ~PRAM_MOUNT_##opt) ++#define set_opt(o, opt) (o |= PRAM_MOUNT_##opt) ++#define test_opt(sb, opt) (PRAM_SB(sb)->s_mount_opt & PRAM_MOUNT_##opt) ++ ++/* ++ * Pram inode flags ++ * ++ * PRAM_EOFBLOCKS_FL There are blocks allocated beyond eof ++ */ ++#define PRAM_EOFBLOCKS_FL 0x20000000 ++/* Flags that should be inherited by new inodes from their parent. */ ++#define PRAM_FL_INHERITED (FS_SECRM_FL | FS_UNRM_FL | FS_COMPR_FL |\ ++ FS_SYNC_FL | FS_IMMUTABLE_FL | FS_APPEND_FL |\ ++ FS_NODUMP_FL | FS_NOATIME_FL | FS_COMPRBLK_FL |\ ++ FS_NOCOMP_FL | FS_JOURNAL_DATA_FL |\ ++ FS_NOTAIL_FL | FS_DIRSYNC_FL) ++/* Flags that are appropriate for regular files (all but dir-specific ones). */ ++#define PRAM_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL)) ++/* Flags that are appropriate for non-directories/regular files. */ ++#define PRAM_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL) ++#define PRAM_FL_USER_VISIBLE (FS_FL_USER_VISIBLE | PRAM_EOFBLOCKS_FL) ++ ++/* Function Prototypes */ ++extern void pram_error_mng(struct super_block *sb, const char *fmt, ...); ++ ++/* file.c */ ++extern ssize_t pram_direct_IO(int rw, struct kiocb *iocb, ++ const struct iovec *iov, ++ loff_t offset, unsigned long nr_segs); ++extern int pram_mmap(struct file *file, struct vm_area_struct *vma); ++ ++/* balloc.c */ ++extern void pram_init_bitmap(struct super_block *sb); ++extern void pram_free_block(struct super_block *sb, unsigned long blocknr); ++extern int pram_new_block(struct super_block *sb, unsigned long *blocknr, ++ int zero); ++extern unsigned long pram_count_free_blocks(struct super_block *sb); ++ ++/* dir.c */ ++extern int pram_add_link(struct dentry *dentry, struct inode *inode); ++extern int pram_remove_link(struct inode *inode); ++ ++/* namei.c */ ++extern struct dentry *pram_get_parent(struct dentry *child); ++ ++/* inode.c */ ++extern int pram_alloc_blocks(struct inode *inode, int file_blocknr, ++ unsigned int num); ++extern u64 pram_find_data_block(struct inode *inode, ++ unsigned long file_blocknr); ++ ++extern struct inode *pram_iget(struct super_block *sb, unsigned long ino); ++extern void pram_put_inode(struct inode *inode); ++extern void pram_evict_inode(struct inode *inode); ++extern struct inode *pram_new_inode(struct inode *dir, int mode, ++ const struct qstr *qstr); ++extern int pram_update_inode(struct inode *inode); ++extern int pram_write_inode(struct inode *inode, struct writeback_control *wbc); ++extern void pram_dirty_inode(struct inode *inode, int flags); ++extern int pram_notify_change(struct dentry *dentry, struct iattr *attr); ++extern void pram_set_inode_flags(struct inode *inode, struct pram_inode *pi); ++extern void pram_get_inode_flags(struct inode *inode, struct pram_inode *pi); ++ ++/* ioctl.c */ ++extern long pram_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); ++#ifdef CONFIG_COMPAT ++extern long pram_compat_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg); ++#endif ++ ++/* super.c */ ++#ifdef CONFIG_PRAMFS_TEST ++extern struct pram_super_block *get_pram_super(void); ++#endif ++extern struct super_block *pram_read_super(struct super_block *sb, ++ void *data, ++ int silent); ++extern int pram_statfs(struct dentry *d, struct kstatfs *buf); ++extern int pram_remount(struct super_block *sb, int *flags, char *data); ++ ++/* symlink.c */ ++extern int pram_block_symlink(struct inode *inode, ++ const char *symname, int len); ++ ++/* Inline functions start here */ ++ ++/* Mask out flags that are inappropriate for the given type of inode. */ ++static inline __be32 pram_mask_flags(umode_t mode, __be32 flags) ++{ ++ flags &= cpu_to_be32(PRAM_FL_INHERITED); ++ if (S_ISDIR(mode)) ++ return flags; ++ else if (S_ISREG(mode)) ++ return flags & cpu_to_be32(PRAM_REG_FLMASK); ++ else ++ return flags & cpu_to_be32(PRAM_OTHER_FLMASK); ++} ++ ++static inline int pram_calc_checksum(u8 *data, int n) ++{ ++ u16 crc = 0; ++ crc = crc16(~0, (__u8 *)data + sizeof(__be16), n - sizeof(__be16)); ++ if (*((__be16 *)data) == cpu_to_be16(crc)) ++ return 0; ++ else ++ return 1; ++} ++ ++struct pram_inode_vfs { ++#ifdef CONFIG_PRAMFS_XATTR ++ /* ++ * Extended attributes can be read independently of the main file ++ * data. Taking i_mutex even when reading would cause contention ++ * between readers of EAs and writers of regular file data, so ++ * instead we synchronize on xattr_sem when reading or changing ++ * EAs. ++ */ ++ struct rw_semaphore xattr_sem; ++#endif ++ struct mutex i_meta_mutex; ++ struct mutex i_link_mutex; ++ struct inode vfs_inode; ++}; ++ ++static inline struct pram_sb_info *PRAM_SB(struct super_block *sb) ++{ ++ return sb->s_fs_info; ++} ++ ++static inline struct pram_inode_vfs *PRAM_I(struct inode *inode) ++{ ++ return container_of(inode, struct pram_inode_vfs, vfs_inode); ++} ++ ++/* If this is part of a read-modify-write of the super block, ++ pram_memunlock_super() before calling! */ ++static inline struct pram_super_block * ++pram_get_super(struct super_block *sb) ++{ ++ struct pram_sb_info *sbi = PRAM_SB(sb); ++ return (struct pram_super_block *)sbi->virt_addr; ++} ++ ++static inline struct pram_super_block * ++pram_get_redund_super(struct super_block *sb) ++{ ++ struct pram_sb_info *sbi = PRAM_SB(sb); ++ return (struct pram_super_block *)(sbi->virt_addr + PRAM_SB_SIZE); ++} ++ ++static inline void * ++pram_get_bitmap(struct super_block *sb) ++{ ++ struct pram_super_block *ps = pram_get_super(sb); ++ return (void *)ps + be64_to_cpu(ps->s_bitmap_start); ++} ++ ++/* If this is part of a read-modify-write of the inode metadata, ++ pram_memunlock_inode() before calling! */ ++static inline struct pram_inode * ++pram_get_inode(struct super_block *sb, u64 ino) ++{ ++ struct pram_super_block *ps = pram_get_super(sb); ++ return ino ? (struct pram_inode *)((void *)ps + ino) : NULL; ++} ++ ++static inline ino_t ++pram_get_inodenr(struct super_block *sb, struct pram_inode *pi) ++{ ++ struct pram_super_block *ps = pram_get_super(sb); ++ return (ino_t)((unsigned long)pi - (unsigned long)ps); ++} ++ ++static inline u64 ++pram_get_block_off(struct super_block *sb, unsigned long blocknr) ++{ ++ struct pram_super_block *ps = pram_get_super(sb); ++ return (u64)(be64_to_cpu(ps->s_bitmap_start) + ++ (blocknr << sb->s_blocksize_bits)); ++} ++ ++static inline unsigned long ++pram_get_blocknr(struct super_block *sb, u64 block) ++{ ++ struct pram_super_block *ps = pram_get_super(sb); ++ return (block - be64_to_cpu(ps->s_bitmap_start)) >> ++ sb->s_blocksize_bits; ++} ++ ++/* If this is part of a read-modify-write of the block, ++ pram_memunlock_block() before calling! */ ++static inline void * ++pram_get_block(struct super_block *sb, u64 block) ++{ ++ struct pram_super_block *ps = pram_get_super(sb); ++ return block ? ((void *)ps + block) : NULL; ++} ++ ++static inline unsigned long ++pram_get_pfn(struct super_block *sb, u64 block) ++{ ++ return (PRAM_SB(sb)->phys_addr + block) >> PAGE_SHIFT; ++} ++ ++static inline void check_eof_blocks(struct inode *inode, loff_t size) ++{ ++ struct pram_inode *pi = pram_get_inode(inode->i_sb, inode->i_ino); ++ if (unlikely(!pi)) ++ return; ++ if ((pi->i_flags & cpu_to_be32(PRAM_EOFBLOCKS_FL)) && ++ size + inode->i_sb->s_blocksize >= ++ (inode->i_blocks << inode->i_sb->s_blocksize_bits)) ++ pi->i_flags &= cpu_to_be32(~PRAM_EOFBLOCKS_FL); ++} ++ ++/* ++ * Inodes and files operations ++ */ ++ ++/* dir.c */ ++extern const struct file_operations pram_dir_operations; ++ ++/* file.c */ ++extern const struct inode_operations pram_file_inode_operations; ++extern const struct file_operations pram_file_operations; ++extern const struct file_operations pram_xip_file_operations; ++ ++/* inode.c */ ++extern const struct address_space_operations pram_aops; ++extern const struct address_space_operations pram_aops_xip; ++ ++/* namei.c */ ++extern const struct inode_operations pram_dir_inode_operations; ++extern const struct inode_operations pram_special_inode_operations; ++ ++/* symlink.c */ ++extern const struct inode_operations pram_symlink_inode_operations; ++ ++extern struct backing_dev_info pram_backing_dev_info; ++ ++#endif /* __PRAM_H */ +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -405,6 +405,7 @@ struct kobject; + struct pipe_inode_info; + struct poll_table_struct; + struct kstatfs; ++struct vm_fault; + struct vm_area_struct; + struct vfsmount; + struct cred; +@@ -2346,6 +2347,7 @@ extern int nonseekable_open(struct inode + #ifdef CONFIG_FS_XIP + extern ssize_t xip_file_read(struct file *filp, char __user *buf, size_t len, + loff_t *ppos); ++extern int xip_file_fault(struct vm_area_struct *vma, struct vm_fault *vmf); + extern int xip_file_mmap(struct file * file, struct vm_area_struct * vma); + extern ssize_t xip_file_write(struct file *filp, const char __user *buf, + size_t len, loff_t *ppos); +--- a/include/linux/magic.h ++++ b/include/linux/magic.h +@@ -41,6 +41,7 @@ + #define NFS_SUPER_MAGIC 0x6969 + #define OPENPROM_SUPER_MAGIC 0x9fa1 + #define PROC_SUPER_MAGIC 0x9fa0 ++#define PRAM_SUPER_MAGIC 0xEFFA + #define QNX4_SUPER_MAGIC 0x002f /* qnx4 fs detection */ + + #define REISERFS_SUPER_MAGIC 0x52654973 /* used by gcc */ +--- /dev/null ++++ b/include/linux/pram_fs.h +@@ -0,0 +1,130 @@ ++/* ++ * FILE NAME include/linux/pram_fs.h ++ * ++ * BRIEF DESCRIPTION ++ * ++ * Definitions for the PRAMFS filesystem. ++ * ++ * Copyright 2009-2011 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. ++ */ ++#ifndef _LINUX_PRAM_FS_H ++#define _LINUX_PRAM_FS_H ++ ++#include <linux/types.h> ++#include <linux/magic.h> ++ ++/* ++ * The PRAM filesystem constants/structures ++ */ ++ ++/* ++ * Mount flags ++ */ ++#define PRAM_MOUNT_PROTECT 0x000001 /* Use memory protection */ ++#define PRAM_MOUNT_XATTR_USER 0x000002 /* Extended user attributes */ ++#define PRAM_MOUNT_POSIX_ACL 0x000004 /* POSIX ACL */ ++#define PRAM_MOUNT_XIP 0x000008 /* Execute in place */ ++#define PRAM_MOUNT_ERRORS_CONT 0x000010 /* Continue on errors */ ++#define PRAM_MOUNT_ERRORS_RO 0x000020 /* Remount fs ro on errors */ ++#define PRAM_MOUNT_ERRORS_PANIC 0x000040 /* Panic on errors */ ++ ++/* ++ * Maximal count of links to a file ++ */ ++#define PRAM_LINK_MAX 32000 ++ ++#define PRAM_MIN_BLOCK_SIZE 512 ++#define PRAM_MAX_BLOCK_SIZE 4096 ++#define PRAM_DEF_BLOCK_SIZE 2048 ++ ++#define PRAM_INODE_SIZE 128 /* must be power of two */ ++#define PRAM_INODE_BITS 7 ++ ++/* ++ * Structure of a directory entry in PRAMFS. ++ * Offsets are to the inode that holds the referenced dentry. ++ */ ++struct pram_dentry { ++ __be64 d_next; /* next dentry in this directory */ ++ __be64 d_prev; /* previous dentry in this directory */ ++ __be64 d_parent; /* parent directory */ ++ char d_name[0]; ++}; ++ ++ ++/* ++ * Structure of an inode in PRAMFS ++ */ ++struct pram_inode { ++ __be16 i_sum; /* checksum of this inode */ ++ __be32 i_uid; /* Owner Uid */ ++ __be32 i_gid; /* Group Id */ ++ __be16 i_mode; /* File mode */ ++ __be16 i_links_count; /* Links count */ ++ __be32 i_blocks; /* Blocks count */ ++ __be32 i_size; /* Size of data in bytes */ ++ __be32 i_atime; /* Access time */ ++ __be32 i_ctime; /* Creation time */ ++ __be32 i_mtime; /* Modification time */ ++ __be32 i_dtime; /* Deletion Time */ ++ __be64 i_xattr; /* Extended attribute block */ ++ __be32 i_generation; /* File version (for NFS) */ ++ __be32 i_flags; /* Inode flags */ ++ ++ union { ++ struct { ++ /* ++ * ptr to row block of 2D block pointer array, ++ * file block #'s 0 to (blocksize/8)^2 - 1. ++ */ ++ __be64 row_block; ++ } reg; /* regular file or symlink inode */ ++ struct { ++ __be64 head; /* first entry in this directory */ ++ __be64 tail; /* last entry in this directory */ ++ } dir; ++ struct { ++ __be32 rdev; /* major/minor # */ ++ } dev; /* device inode */ ++ } i_type; ++ ++ struct pram_dentry i_d; ++}; ++ ++#define PRAM_NAME_LEN \ ++ (PRAM_INODE_SIZE - offsetof(struct pram_inode, i_d.d_name) - 1) ++ ++ ++#define PRAM_SB_SIZE 128 /* must be power of two */ ++ ++/* ++ * Structure of the super block in PRAMFS ++ */ ++struct pram_super_block { ++ __be16 s_sum; /* checksum of this sb, including padding */ ++ __be64 s_size; /* total size of fs in bytes */ ++ __be32 s_blocksize; /* blocksize in bytes */ ++ __be32 s_inodes_count; /* total inodes count (used or free) */ ++ __be32 s_free_inodes_count;/* free inodes count */ ++ __be32 s_free_inode_hint; /* start hint for locating free inodes */ ++ __be32 s_blocks_count; /* total data blocks count (used or free) */ ++ __be32 s_free_blocks_count;/* free data blocks count */ ++ __be32 s_free_blocknr_hint;/* free data blocks count */ ++ __be64 s_bitmap_start; /* data block in-use bitmap location */ ++ __be32 s_bitmap_blocks;/* size of bitmap in number of blocks */ ++ __be32 s_mtime; /* Mount time */ ++ __be32 s_wtime; /* Write time */ ++ __be16 s_magic; /* Magic signature */ ++ char s_volume_name[16]; /* volume name */ ++}; ++ ++/* The root inode follows immediately after the redundant super block */ ++#define PRAM_ROOT_INO (PRAM_SB_SIZE*2) ++ ++#endif /* _LINUX_PRAM_FS_H */ +--- /dev/null ++++ b/include/linux/pram_fs_sb.h +@@ -0,0 +1,45 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Definitions for the PRAM filesystem. ++ * ++ * Copyright 2009-2011 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. ++ */ ++ ++#ifndef _LINUX_PRAM_FS_SB ++#define _LINUX_PRAM_FS_SB ++ ++/* ++ * PRAM filesystem super-block data in memory ++ */ ++struct pram_sb_info { ++ /* ++ * base physical and virtual address of PRAMFS (which is also ++ * the pointer to the super block) ++ */ ++ phys_addr_t phys_addr; ++ void *virt_addr; ++ ++ /* Mount options */ ++ unsigned long bpi; ++ unsigned long num_inodes; ++ unsigned long blocksize; ++ unsigned long initsize; ++ unsigned long s_mount_opt; ++ uid_t uid; /* Mount uid for root directory */ ++ gid_t gid; /* Mount gid for root directory */ ++ mode_t mode; /* Mount mode for root directory */ ++ atomic_t next_generation; ++#ifdef CONFIG_PRAMFS_XATTR ++ struct rb_root desc_tree; ++ spinlock_t desc_tree_lock; ++#endif ++}; ++ ++#endif /* _LINUX_PRAM_FS_SB */ diff --git a/patches.pramfs/09-pramfs-dir-operations.patch b/patches.pramfs/09-pramfs-dir-operations.patch new file mode 100644 index 00000000000000..e3441a1fbcd67e --- /dev/null +++ b/patches.pramfs/09-pramfs-dir-operations.patch @@ -0,0 +1,241 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:17:35 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:11:01 +0200 +Subject: pramfs: dir operations +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE99A5.30806@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +File operations for directories. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> +--- + fs/pramfs/dir.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 221 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/dir.c +@@ -0,0 +1,221 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * File operations for directories. ++ * ++ * Copyright 2009-2011 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/fs.h> ++#include <linux/pagemap.h> ++#include "pram.h" ++ ++/* ++ * Parent is locked. ++ */ ++int pram_add_link(struct dentry *dentry, struct inode *inode) ++{ ++ struct inode *dir = dentry->d_parent->d_inode; ++ struct pram_inode *pidir, *pi, *pitail = NULL; ++ u64 tail_ino, prev_ino; ++ ++ const char *name = dentry->d_name.name; ++ ++ int namelen = min_t(unsigned int, dentry->d_name.len, PRAM_NAME_LEN); ++ ++ pidir = pram_get_inode(dir->i_sb, dir->i_ino); ++ ++ mutex_lock(&PRAM_I(dir)->i_link_mutex); ++ ++ pi = pram_get_inode(dir->i_sb, inode->i_ino); ++ ++ dir->i_mtime = dir->i_ctime = CURRENT_TIME; ++ ++ tail_ino = be64_to_cpu(pidir->i_type.dir.tail); ++ if (tail_ino != 0) { ++ pitail = pram_get_inode(dir->i_sb, tail_ino); ++ pram_memunlock_inode(dir->i_sb, pitail); ++ pitail->i_d.d_next = cpu_to_be64(inode->i_ino); ++ pram_memlock_inode(dir->i_sb, pitail); ++ ++ prev_ino = tail_ino; ++ ++ pram_memunlock_inode(dir->i_sb, pidir); ++ pidir->i_type.dir.tail = cpu_to_be64(inode->i_ino); ++ pidir->i_mtime = cpu_to_be32(dir->i_mtime.tv_sec); ++ pidir->i_ctime = cpu_to_be32(dir->i_ctime.tv_sec); ++ pram_memlock_inode(dir->i_sb, pidir); ++ } else { ++ /* the directory is empty */ ++ prev_ino = 0; ++ ++ pram_memunlock_inode(dir->i_sb, pidir); ++ pidir->i_type.dir.tail = cpu_to_be64(inode->i_ino); ++ pidir->i_type.dir.head = cpu_to_be64(inode->i_ino); ++ pidir->i_mtime = cpu_to_be32(dir->i_mtime.tv_sec); ++ pidir->i_ctime = cpu_to_be32(dir->i_ctime.tv_sec); ++ pram_memlock_inode(dir->i_sb, pidir); ++ } ++ ++ ++ pram_memunlock_inode(dir->i_sb, pi); ++ pi->i_d.d_prev = cpu_to_be64(prev_ino); ++ pi->i_d.d_parent = cpu_to_be64(dir->i_ino); ++ memcpy(pi->i_d.d_name, name, namelen); ++ pi->i_d.d_name[namelen] = '\0'; ++ pram_memlock_inode(dir->i_sb, pi); ++ mutex_unlock(&PRAM_I(dir)->i_link_mutex); ++ return 0; ++} ++ ++int pram_remove_link(struct inode *inode) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct pram_inode *prev = NULL; ++ struct pram_inode *next = NULL; ++ struct pram_inode *pidir, *pi; ++ struct inode *dir = NULL; ++ ++ pi = pram_get_inode(sb, inode->i_ino); ++ pidir = pram_get_inode(sb, be64_to_cpu(pi->i_d.d_parent)); ++ if (!pidir) ++ return -EACCES; ++ ++ dir = pram_iget(inode->i_sb, be64_to_cpu(pi->i_d.d_parent)); ++ if (IS_ERR(dir)) ++ return -EACCES; ++ mutex_lock(&PRAM_I(dir)->i_link_mutex); ++ ++ if (inode->i_ino == be64_to_cpu(pidir->i_type.dir.head)) { ++ /* first inode in directory */ ++ next = pram_get_inode(sb, be64_to_cpu(pi->i_d.d_next)); ++ ++ if (next) { ++ pram_memunlock_inode(sb, next); ++ next->i_d.d_prev = 0; ++ pram_memlock_inode(sb, next); ++ ++ pram_memunlock_inode(sb, pidir); ++ pidir->i_type.dir.head = pi->i_d.d_next; ++ } else { ++ pram_memunlock_inode(sb, pidir); ++ pidir->i_type.dir.head = 0; ++ pidir->i_type.dir.tail = 0; ++ } ++ pram_memlock_inode(sb, pidir); ++ } else if (inode->i_ino == be64_to_cpu(pidir->i_type.dir.tail)) { ++ /* last inode in directory */ ++ prev = pram_get_inode(sb, be64_to_cpu(pi->i_d.d_prev)); ++ ++ pram_memunlock_inode(sb, prev); ++ prev->i_d.d_next = 0; ++ pram_memlock_inode(sb, prev); ++ ++ pram_memunlock_inode(sb, pidir); ++ pidir->i_type.dir.tail = pi->i_d.d_prev; ++ pram_memlock_inode(sb, pidir); ++ } else { ++ /* somewhere in the middle */ ++ prev = pram_get_inode(sb, be64_to_cpu(pi->i_d.d_prev)); ++ next = pram_get_inode(sb, be64_to_cpu(pi->i_d.d_next)); ++ ++ if (prev && next) { ++ pram_memunlock_inode(sb, prev); ++ prev->i_d.d_next = pi->i_d.d_next; ++ pram_memlock_inode(sb, prev); ++ ++ pram_memunlock_inode(sb, next); ++ next->i_d.d_prev = pi->i_d.d_prev; ++ pram_memlock_inode(sb, next); ++ } ++ } ++ ++ pram_memunlock_inode(sb, pi); ++ pi->i_d.d_next = 0; ++ pi->i_d.d_prev = 0; ++ pi->i_d.d_parent = 0; ++ pram_memlock_inode(sb, pi); ++ mutex_unlock(&PRAM_I(dir)->i_link_mutex); ++ iput(dir); ++ ++ return 0; ++} ++ ++#define DT2IF(dt) (((dt) << 12) & S_IFMT) ++#define IF2DT(sif) (((sif) & S_IFMT) >> 12) ++ ++static int pram_readdir(struct file *filp, void *dirent, filldir_t filldir) ++{ ++ struct inode *inode = filp->f_dentry->d_inode; ++ struct super_block *sb = inode->i_sb; ++ struct pram_inode *pi; ++ int namelen, ret = 0; ++ char *name; ++ ino_t ino; ++ ++ pi = pram_get_inode(sb, inode->i_ino); ++ ++ switch ((unsigned long)filp->f_pos) { ++ case 0: ++ ret = filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR); ++ filp->f_pos++; ++ return ret; ++ case 1: ++ ret = filldir(dirent, "..", 2, 1, be64_to_cpu(pi->i_d.d_parent), ++ DT_DIR); ++ ino = be64_to_cpu(pi->i_type.dir.head); ++ filp->f_pos = ino ? ino : 2; ++ return ret; ++ case 2: ++ ino = be64_to_cpu(pi->i_type.dir.head); ++ if (ino) { ++ filp->f_pos = ino; ++ pi = pram_get_inode(sb, ino); ++ break; ++ } else { ++ /* the directory is empty */ ++ filp->f_pos = 2; ++ return 0; ++ } ++ case 3: ++ return 0; ++ default: ++ ino = filp->f_pos; ++ pi = pram_get_inode(sb, ino); ++ break; ++ } ++ ++ while (pi && !be16_to_cpu(pi->i_links_count)) { ++ ino = filp->f_pos = be64_to_cpu(pi->i_d.d_next); ++ pi = pram_get_inode(sb, ino); ++ } ++ ++ if (pi) { ++ name = pi->i_d.d_name; ++ namelen = strlen(name); ++ ++ ret = filldir(dirent, name, namelen, ++ filp->f_pos, ino, ++ IF2DT(be16_to_cpu(pi->i_mode))); ++ filp->f_pos = pi->i_d.d_next ? be64_to_cpu(pi->i_d.d_next) : 3; ++ } else ++ filp->f_pos = 3; ++ ++ return ret; ++} ++ ++const struct file_operations pram_dir_operations = { ++ .read = generic_read_dir, ++ .readdir = pram_readdir, ++ .fsync = noop_fsync, ++ .unlocked_ioctl = pram_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = pram_compat_ioctl, ++#endif ++}; diff --git a/patches.pramfs/10-pramfs-xip-support.patch b/patches.pramfs/10-pramfs-xip-support.patch new file mode 100644 index 00000000000000..f9626701f5182f --- /dev/null +++ b/patches.pramfs/10-pramfs-xip-support.patch @@ -0,0 +1,195 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:17:49 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:11:14 +0200 +Subject: pramfs: xip support +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE99B2.1050508@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +XIP operations. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> + +--- + fs/pramfs/xip.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + fs/pramfs/xip.h | 33 +++++++++++++++ + mm/filemap_xip.c | 3 - + 3 files changed, 152 insertions(+), 1 deletion(-) + +--- /dev/null ++++ b/fs/pramfs/xip.c +@@ -0,0 +1,117 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * XIP operations. ++ * ++ * Copyright 2009-2011 Marco Stornelli <marco.stornelli@gmail.com> ++ * 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/mm.h> ++#include <linux/fs.h> ++#include <linux/buffer_head.h> ++#include "pram.h" ++#include "xip.h" ++ ++/* ++ * Wrappers. We need to use the rcu read lock to avoid ++ * concurrent truncate operation. No problem for write because we held ++ * i_mutex. ++ */ ++ssize_t pram_xip_file_read(struct file *filp, char __user *buf, ++ size_t len, loff_t *ppos) ++{ ++ ssize_t res; ++ rcu_read_lock(); ++ res = xip_file_read(filp, buf, len, ppos); ++ rcu_read_unlock(); ++ return res; ++} ++ ++static int pram_xip_file_fault(struct vm_area_struct *vma, struct vm_fault *vmf) ++{ ++ int ret = 0; ++ rcu_read_lock(); ++ ret = xip_file_fault(vma, vmf); ++ rcu_read_unlock(); ++ return ret; ++} ++ ++static const struct vm_operations_struct pram_xip_vm_ops = { ++ .fault = pram_xip_file_fault, ++}; ++ ++int pram_xip_file_mmap(struct file * file, struct vm_area_struct * vma) ++{ ++ BUG_ON(!file->f_mapping->a_ops->get_xip_mem); ++ ++ file_accessed(file); ++ vma->vm_ops = &pram_xip_vm_ops; ++ vma->vm_flags |= VM_CAN_NONLINEAR | VM_MIXEDMAP; ++ return 0; ++} ++ ++static int pram_find_and_alloc_blocks(struct inode *inode, sector_t iblock, ++ sector_t *data_block, int create) ++{ ++ int err = -EIO; ++ u64 block; ++ ++ block = pram_find_data_block(inode, iblock); ++ ++ if (!block) { ++ if (!create) { ++ err = -ENODATA; ++ goto err; ++ } ++ ++ err = pram_alloc_blocks(inode, iblock, 1); ++ if (err) ++ goto err; ++ ++ block = pram_find_data_block(inode, iblock); ++ if (!block) { ++ err = -ENODATA; ++ goto err; ++ } ++ } ++ ++ *data_block = block; ++ err = 0; ++ ++ err: ++ return err; ++} ++ ++static inline int __pram_get_block(struct inode *inode, pgoff_t pgoff, ++ int create, sector_t *result) ++{ ++ int rc = 0; ++ ++ rc = pram_find_and_alloc_blocks(inode, (sector_t)pgoff, result, create); ++ ++ if (rc == -ENODATA) ++ BUG_ON(create); ++ ++ return rc; ++} ++ ++int pram_get_xip_mem(struct address_space *mapping, pgoff_t pgoff, int create, ++ void **kmem, unsigned long *pfn) ++{ ++ int rc; ++ sector_t block = 0; ++ ++ /* first, retrieve the block */ ++ rc = __pram_get_block(mapping->host, pgoff, create, &block); ++ if (rc) ++ goto exit; ++ ++ *kmem = pram_get_block(mapping->host->i_sb, block); ++ *pfn = pram_get_pfn(mapping->host->i_sb, block); ++ ++exit: ++ return rc; ++} +--- /dev/null ++++ b/fs/pramfs/xip.h +@@ -0,0 +1,33 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * XIP operations. ++ * ++ * Copyright 2009-2011 Marco Stornelli <marco.stornelli@gmail.com> ++ * 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. ++ */ ++ ++#ifdef CONFIG_PRAMFS_XIP ++int pram_get_xip_mem(struct address_space *, pgoff_t, int, void **, ++ unsigned long *); ++ssize_t pram_xip_file_read(struct file *filp, char __user *buf, ++ size_t len, loff_t *ppos); ++int pram_xip_file_mmap(struct file * file, struct vm_area_struct * vma); ++static inline int pram_use_xip(struct super_block *sb) ++{ ++ struct pram_sb_info *sbi = PRAM_SB(sb); ++ return sbi->s_mount_opt & PRAM_MOUNT_XIP; ++} ++#define mapping_is_xip(map) (map->a_ops->get_xip_mem) ++ ++#else ++ ++#define mapping_is_xip(map) 0 ++#define pram_use_xip(sb) 0 ++#define pram_get_xip_mem NULL ++#define pram_xip_file_read NULL ++#define pram_xip_file_mmap NULL ++ ++#endif +--- a/mm/filemap_xip.c ++++ b/mm/filemap_xip.c +@@ -218,7 +218,7 @@ retry: + * + * This function is derived from filemap_fault, but used for execute in place + */ +-static int xip_file_fault(struct vm_area_struct *vma, struct vm_fault *vmf) ++int xip_file_fault(struct vm_area_struct *vma, struct vm_fault *vmf) + { + struct file *file = vma->vm_file; + struct address_space *mapping = file->f_mapping; +@@ -302,6 +302,7 @@ out: + } + } + ++EXPORT_SYMBOL_GPL(xip_file_fault); + static const struct vm_operations_struct xip_file_vm_ops = { + .fault = xip_file_fault, + }; diff --git a/patches.pramfs/11-pramfs-acl-support.patch b/patches.pramfs/11-pramfs-acl-support.patch new file mode 100644 index 00000000000000..4959831c5f87ae --- /dev/null +++ b/patches.pramfs/11-pramfs-acl-support.patch @@ -0,0 +1,552 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:18:05 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:11:30 +0200 +Subject: pramfs: ACL support +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE99C2.5020909@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +ACL operations. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> + +--- + fs/pramfs/acl.c | 441 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + fs/pramfs/acl.h | 86 ++++++++++ + 2 files changed, 527 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/acl.c +@@ -0,0 +1,441 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * POSIX ACL operations ++ * ++ * Copyright 2010-2011 Marco Stornelli <marco.stornelli@gmail.com> ++ * ++ * based on fs/ext2/acl.c with the following copyright: ++ * ++ * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de> ++ * ++ * 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/capability.h> ++#include <linux/init.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/fs.h> ++#include "pram.h" ++#include "xattr.h" ++#include "acl.h" ++ ++/* ++ * Load ACL information from filesystem. ++ */ ++static struct posix_acl *pram_acl_load(const void *value, size_t size) ++{ ++ const char *end = (char *)value + size; ++ int n, count; ++ struct posix_acl *acl; ++ ++ if (!value) ++ return NULL; ++ if (size < sizeof(struct pram_acl_header)) ++ return ERR_PTR(-EINVAL); ++ if (((struct pram_acl_header *)value)->a_version != ++ cpu_to_be32(PRAM_ACL_VERSION)) ++ return ERR_PTR(-EINVAL); ++ value = (char *)value + sizeof(struct pram_acl_header); ++ count = pram_acl_count(size); ++ if (count < 0) ++ return ERR_PTR(-EINVAL); ++ if (count == 0) ++ return NULL; ++ acl = posix_acl_alloc(count, GFP_KERNEL); ++ if (!acl) ++ return ERR_PTR(-ENOMEM); ++ for (n = 0; n < count; n++) { ++ struct pram_acl_entry *entry = (struct pram_acl_entry *)value; ++ if ((char *)value + sizeof(struct pram_acl_entry_short) > end) ++ goto fail; ++ acl->a_entries[n].e_tag = be16_to_cpu(entry->e_tag); ++ acl->a_entries[n].e_perm = be16_to_cpu(entry->e_perm); ++ switch (acl->a_entries[n].e_tag) { ++ case ACL_USER_OBJ: ++ case ACL_GROUP_OBJ: ++ case ACL_MASK: ++ case ACL_OTHER: ++ value = (char *)value + ++ sizeof(struct pram_acl_entry_short); ++ acl->a_entries[n].e_id = ACL_UNDEFINED_ID; ++ break; ++ case ACL_USER: ++ case ACL_GROUP: ++ value = (char *)value + sizeof(struct pram_acl_entry); ++ if ((char *)value > end) ++ goto fail; ++ acl->a_entries[n].e_id = ++ be32_to_cpu(entry->e_id); ++ break; ++ default: ++ goto fail; ++ } ++ } ++ if (value != end) ++ goto fail; ++ return acl; ++ ++fail: ++ posix_acl_release(acl); ++ return ERR_PTR(-EINVAL); ++} ++ ++/* ++ * Save ACL information into the filesystem. ++ */ ++static void *pram_acl_save(const struct posix_acl *acl, size_t *size) ++{ ++ struct pram_acl_header *ext_acl; ++ char *e; ++ size_t n; ++ ++ *size = pram_acl_size(acl->a_count); ++ ext_acl = kmalloc(sizeof(struct pram_acl_header) + acl->a_count * ++ sizeof(struct pram_acl_entry), GFP_KERNEL); ++ if (!ext_acl) ++ return ERR_PTR(-ENOMEM); ++ ext_acl->a_version = cpu_to_be32(PRAM_ACL_VERSION); ++ e = (char *)ext_acl + sizeof(struct pram_acl_header); ++ for (n = 0; n < acl->a_count; n++) { ++ struct pram_acl_entry *entry = (struct pram_acl_entry *)e; ++ entry->e_tag = cpu_to_be16(acl->a_entries[n].e_tag); ++ entry->e_perm = cpu_to_be16(acl->a_entries[n].e_perm); ++ switch (acl->a_entries[n].e_tag) { ++ case ACL_USER: ++ case ACL_GROUP: ++ entry->e_id = ++ cpu_to_be32(acl->a_entries[n].e_id); ++ e += sizeof(struct pram_acl_entry); ++ break; ++ case ACL_USER_OBJ: ++ case ACL_GROUP_OBJ: ++ case ACL_MASK: ++ case ACL_OTHER: ++ e += sizeof(struct pram_acl_entry_short); ++ break; ++ default: ++ goto fail; ++ } ++ } ++ return (char *)ext_acl; ++ ++fail: ++ kfree(ext_acl); ++ return ERR_PTR(-EINVAL); ++} ++ ++/* ++ * inode->i_mutex: don't care ++ */ ++static struct posix_acl *pram_get_acl(struct inode *inode, int type) ++{ ++ int name_index; ++ char *value = NULL; ++ struct posix_acl *acl; ++ int retval; ++ ++ if (!test_opt(inode->i_sb, POSIX_ACL)) ++ return NULL; ++ ++ acl = get_cached_acl(inode, type); ++ if (acl != ACL_NOT_CACHED) ++ return acl; ++ ++ switch (type) { ++ case ACL_TYPE_ACCESS: ++ name_index = PRAM_XATTR_INDEX_POSIX_ACL_ACCESS; ++ break; ++ case ACL_TYPE_DEFAULT: ++ name_index = PRAM_XATTR_INDEX_POSIX_ACL_DEFAULT; ++ break; ++ default: ++ BUG(); ++ } ++ retval = pram_xattr_get(inode, name_index, "", NULL, 0); ++ if (retval > 0) { ++ value = kmalloc(retval, GFP_KERNEL); ++ if (!value) ++ return ERR_PTR(-ENOMEM); ++ retval = pram_xattr_get(inode, name_index, "", value, retval); ++ } ++ if (retval > 0) ++ acl = pram_acl_load(value, retval); ++ else if (retval == -ENODATA || retval == -ENOSYS) ++ acl = NULL; ++ else ++ acl = ERR_PTR(retval); ++ kfree(value); ++ ++ if (!IS_ERR(acl)) ++ set_cached_acl(inode, type, acl); ++ ++ return acl; ++} ++ ++/* ++ * inode->i_mutex: down ++ */ ++static int pram_set_acl(struct inode *inode, int type, struct posix_acl *acl) ++{ ++ int name_index; ++ void *value = NULL; ++ size_t size = 0; ++ int error; ++ ++ if (S_ISLNK(inode->i_mode)) ++ return -EOPNOTSUPP; ++ if (!test_opt(inode->i_sb, POSIX_ACL)) ++ return 0; ++ ++ switch (type) { ++ case ACL_TYPE_ACCESS: ++ name_index = PRAM_XATTR_INDEX_POSIX_ACL_ACCESS; ++ if (acl) { ++ mode_t mode = inode->i_mode; ++ error = posix_acl_equiv_mode(acl, &mode); ++ if (error < 0) ++ return error; ++ else { ++ inode->i_mode = mode; ++ inode->i_ctime = CURRENT_TIME_SEC; ++ mark_inode_dirty(inode); ++ if (error == 0) ++ acl = NULL; ++ } ++ } ++ break; ++ case ACL_TYPE_DEFAULT: ++ name_index = PRAM_XATTR_INDEX_POSIX_ACL_DEFAULT; ++ if (!S_ISDIR(inode->i_mode)) ++ return acl ? -EACCES : 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (acl) { ++ value = pram_acl_save(acl, &size); ++ if (IS_ERR(value)) ++ return (int)PTR_ERR(value); ++ } ++ ++ error = pram_xattr_set(inode, name_index, "", value, size, 0); ++ ++ kfree(value); ++ if (!error) ++ set_cached_acl(inode, type, acl); ++ return error; ++} ++ ++int pram_check_acl(struct inode *inode, int mask, unsigned int flags) ++{ ++ struct posix_acl *acl; ++ ++ if (flags & IPERM_FLAG_RCU) { ++ if (!negative_cached_acl(inode, ACL_TYPE_ACCESS)) ++ return -ECHILD; ++ return -EAGAIN; ++ } ++ ++ acl = pram_get_acl(inode, ACL_TYPE_ACCESS); ++ if (IS_ERR(acl)) ++ return PTR_ERR(acl); ++ if (acl) { ++ int error = posix_acl_permission(inode, acl, mask); ++ posix_acl_release(acl); ++ return error; ++ } ++ ++ return -EAGAIN; ++} ++ ++/* ++ * Initialize the ACLs of a new inode. Called from pram_new_inode. ++ * ++ * dir->i_mutex: down ++ * inode->i_mutex: up (access to inode is still exclusive) ++ */ ++int pram_init_acl(struct inode *inode, struct inode *dir) ++{ ++ struct posix_acl *acl = NULL; ++ int error = 0; ++ ++ if (!S_ISLNK(inode->i_mode)) { ++ if (test_opt(dir->i_sb, POSIX_ACL)) { ++ acl = pram_get_acl(dir, ACL_TYPE_DEFAULT); ++ if (IS_ERR(acl)) ++ return PTR_ERR(acl); ++ } ++ if (!acl) ++ inode->i_mode &= ~current_umask(); ++ } ++ ++ if (test_opt(inode->i_sb, POSIX_ACL) && acl) { ++ struct posix_acl *clone; ++ mode_t mode; ++ ++ if (S_ISDIR(inode->i_mode)) { ++ error = pram_set_acl(inode, ACL_TYPE_DEFAULT, acl); ++ if (error) ++ goto cleanup; ++ } ++ clone = posix_acl_clone(acl, GFP_KERNEL); ++ error = -ENOMEM; ++ if (!clone) ++ goto cleanup; ++ mode = inode->i_mode; ++ error = posix_acl_create_masq(clone, &mode); ++ if (error >= 0) { ++ inode->i_mode = mode; ++ if (error > 0) { ++ /* This is an extended ACL */ ++ error = pram_set_acl(inode, ++ ACL_TYPE_ACCESS, clone); ++ } ++ } ++ posix_acl_release(clone); ++ } ++cleanup: ++ posix_acl_release(acl); ++ return error; ++} ++ ++/* ++ * Does chmod for an inode that may have an Access Control List. The ++ * inode->i_mode field must be updated to the desired value by the caller ++ * before calling this function. ++ * Returns 0 on success, or a negative error number. ++ * ++ * We change the ACL rather than storing some ACL entries in the file ++ * mode permission bits (which would be more efficient), because that ++ * would break once additional permissions (like ACL_APPEND, ACL_DELETE ++ * for directories) are added. There are no more bits available in the ++ * file mode. ++ * ++ * inode->i_mutex: down ++ */ ++int pram_acl_chmod(struct inode *inode) ++{ ++ struct posix_acl *acl, *clone; ++ int error; ++ ++ if (!test_opt(inode->i_sb, POSIX_ACL)) ++ return 0; ++ if (S_ISLNK(inode->i_mode)) ++ return -EOPNOTSUPP; ++ acl = pram_get_acl(inode, ACL_TYPE_ACCESS); ++ if (IS_ERR(acl) || !acl) ++ return PTR_ERR(acl); ++ clone = posix_acl_clone(acl, GFP_KERNEL); ++ posix_acl_release(acl); ++ if (!clone) ++ return -ENOMEM; ++ error = posix_acl_chmod_masq(clone, inode->i_mode); ++ if (!error) ++ error = pram_set_acl(inode, ACL_TYPE_ACCESS, clone); ++ posix_acl_release(clone); ++ return error; ++} ++ ++/* ++ * Extended attribut handlers ++ */ ++static size_t pram_xattr_list_acl_access(struct dentry *dentry, char *list, ++ size_t list_size, const char *name, ++ size_t name_len, int type) ++{ ++ const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS); ++ ++ if (!test_opt(dentry->d_sb, POSIX_ACL)) ++ return 0; ++ if (list && size <= list_size) ++ memcpy(list, POSIX_ACL_XATTR_ACCESS, size); ++ return size; ++} ++ ++static size_t pram_xattr_list_acl_default(struct dentry *dentry, char *list, ++ size_t list_size, const char *name, ++ size_t name_len, int type) ++{ ++ const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT); ++ ++ if (!test_opt(dentry->d_sb, POSIX_ACL)) ++ return 0; ++ if (list && size <= list_size) ++ memcpy(list, POSIX_ACL_XATTR_DEFAULT, size); ++ return size; ++} ++ ++static int pram_xattr_get_acl(struct dentry *dentry, const char *name, ++ void *buffer, size_t size, int type) ++{ ++ struct posix_acl *acl; ++ int error; ++ ++ if (strcmp(name, "") != 0) ++ return -EINVAL; ++ if (!test_opt(dentry->d_sb, POSIX_ACL)) ++ return -EOPNOTSUPP; ++ ++ acl = pram_get_acl(dentry->d_inode, type); ++ if (IS_ERR(acl)) ++ return PTR_ERR(acl); ++ if (acl == NULL) ++ return -ENODATA; ++ error = posix_acl_to_xattr(acl, buffer, size); ++ posix_acl_release(acl); ++ ++ return error; ++} ++ ++static int pram_xattr_set_acl(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags, ++ int type) ++{ ++ struct posix_acl *acl; ++ int error; ++ ++ if (strcmp(name, "") != 0) ++ return -EINVAL; ++ if (!test_opt(dentry->d_sb, POSIX_ACL)) ++ return -EOPNOTSUPP; ++ if (!is_owner_or_cap(dentry->d_inode)) ++ return -EPERM; ++ ++ if (value) { ++ acl = posix_acl_from_xattr(value, size); ++ if (IS_ERR(acl)) ++ return PTR_ERR(acl); ++ else if (acl) { ++ error = posix_acl_valid(acl); ++ if (error) ++ goto release_and_out; ++ } ++ } else ++ acl = NULL; ++ ++ error = pram_set_acl(dentry->d_inode, type, acl); ++ ++release_and_out: ++ posix_acl_release(acl); ++ return error; ++} ++ ++const struct xattr_handler pram_xattr_acl_access_handler = { ++ .prefix = POSIX_ACL_XATTR_ACCESS, ++ .flags = ACL_TYPE_ACCESS, ++ .list = pram_xattr_list_acl_access, ++ .get = pram_xattr_get_acl, ++ .set = pram_xattr_set_acl, ++}; ++ ++const struct xattr_handler pram_xattr_acl_default_handler = { ++ .prefix = POSIX_ACL_XATTR_DEFAULT, ++ .flags = ACL_TYPE_DEFAULT, ++ .list = pram_xattr_list_acl_default, ++ .get = pram_xattr_get_acl, ++ .set = pram_xattr_set_acl, ++}; +--- /dev/null ++++ b/fs/pramfs/acl.h +@@ -0,0 +1,86 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * POSIX ACL operations ++ * ++ * Copyright 2010-2011 Marco Stornelli <marco.stornelli@gmail.com> ++ * 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. ++ * ++ * Based on fs/ext2/acl.h with the following copyright: ++ * ++ * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de> ++ * ++ */ ++ ++#include <linux/posix_acl_xattr.h> ++ ++#define PRAM_ACL_VERSION 0x0001 ++ ++struct pram_acl_entry { ++ __be16 e_tag; ++ __be16 e_perm; ++ __be32 e_id; ++}; ++ ++struct pram_acl_entry_short { ++ __be16 e_tag; ++ __be16 e_perm; ++}; ++ ++struct pram_acl_header { ++ __be32 a_version; ++}; ++ ++static inline size_t pram_acl_size(int count) ++{ ++ if (count <= 4) { ++ return sizeof(struct pram_acl_header) + ++ count * sizeof(struct pram_acl_entry_short); ++ } else { ++ return sizeof(struct pram_acl_header) + ++ 4 * sizeof(struct pram_acl_entry_short) + ++ (count - 4) * sizeof(struct pram_acl_entry); ++ } ++} ++ ++static inline int pram_acl_count(size_t size) ++{ ++ ssize_t s; ++ size -= sizeof(struct pram_acl_header); ++ s = size - 4 * sizeof(struct pram_acl_entry_short); ++ if (s < 0) { ++ if (size % sizeof(struct pram_acl_entry_short)) ++ return -1; ++ return size / sizeof(struct pram_acl_entry_short); ++ } else { ++ if (s % sizeof(struct pram_acl_entry)) ++ return -1; ++ return s / sizeof(struct pram_acl_entry) + 4; ++ } ++} ++ ++#ifdef CONFIG_PRAMFS_POSIX_ACL ++ ++/* acl.c */ ++extern int pram_check_acl(struct inode *, int, unsigned int); ++extern int pram_acl_chmod(struct inode *); ++extern int pram_init_acl(struct inode *, struct inode *); ++ ++#else ++#include <linux/sched.h> ++#define pram_check_acl NULL ++#define pram_get_acl NULL ++#define pram_set_acl NULL ++ ++static inline int pram_acl_chmod(struct inode *inode) ++{ ++ return 0; ++} ++ ++static inline int pram_init_acl(struct inode *inode, struct inode *dir) ++{ ++ return 0; ++} ++#endif diff --git a/patches.pramfs/12-pramfs-extended-attributes-support.patch b/patches.pramfs/12-pramfs-extended-attributes-support.patch new file mode 100644 index 00000000000000..77ac49108b2a33 --- /dev/null +++ b/patches.pramfs/12-pramfs-extended-attributes-support.patch @@ -0,0 +1,1277 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:18:23 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:11:46 +0200 +Subject: pramfs: extended attributes support +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE99D2.9050103@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +Extended attributes operations. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> + +--- + fs/pramfs/xattr.c | 1118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + fs/pramfs/xattr.h | 134 ++++++ + 2 files changed, 1252 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/xattr.c +@@ -0,0 +1,1118 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Extended attributes operations. ++ * ++ * Copyright 2010-2011 Marco Stornelli <marco.stornelli@gmail.com> ++ * ++ * based on fs/ext2/xattr.c with the following copyright: ++ * ++ * Fix by Harrison Xing <harrison@mountainviewdata.com>. ++ * Extended attributes for symlinks and special files added per ++ * suggestion of Luka Renko <luka.renko@hermes.si>. ++ * xattr consolidation Copyright (c) 2004 James Morris <jmorris@redhat.com>, ++ * Red Hat Inc. ++ * ++ * 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. ++ */ ++ ++/* ++ * Extended attributes are stored in blocks allocated outside of ++ * any inode. The i_xattr field is then made to point to this allocated ++ * block. If all extended attributes of an inode are identical, these ++ * inodes may share the same extended attribute block. Such situations ++ * are automatically detected by keeping a cache of recent attribute block ++ * numbers and hashes over the block's contents in memory. ++ * ++ * ++ * Extended attribute block layout: ++ * ++ * +------------------+ ++ * | header | ++ * | entry 1 | | ++ * | entry 2 | | growing downwards ++ * | entry 3 | v ++ * | four null bytes | ++ * | . . . | ++ * | value 1 | ^ ++ * | value 3 | | growing upwards ++ * | value 2 | | ++ * +------------------+ ++ * ++ * The block header is followed by multiple entry descriptors. These entry ++ * descriptors are variable in size, and aligned to PRAM_XATTR_PAD ++ * byte boundaries. The entry descriptors are sorted by attribute name, ++ * so that two extended attribute blocks can be compared efficiently. ++ * ++ * Attribute values are aligned to the end of the block, stored in ++ * no specific order. They are also padded to PRAM_XATTR_PAD byte ++ * boundaries. No additional gaps are left between them. ++ * ++ * Locking strategy ++ * ---------------- ++ * pi->i_xattr is protected by PRAM_I(inode)->xattr_sem. ++ * EA blocks are only changed if they are exclusive to an inode, so ++ * holding xattr_sem also means that nothing but the EA block's reference ++ * count will change. Multiple writers to an EA block are synchronized ++ * by the mutex in each block descriptor. Block descriptors are kept in a ++ * red black tree and the key is the absolute block number. ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/mbcache.h> ++#include <linux/rwsem.h> ++#include <linux/security.h> ++#include "pram.h" ++#include "xattr.h" ++#include "acl.h" ++#include "desctree.h" ++ ++#define HDR(bp) ((struct pram_xattr_header *)(bp)) ++#define ENTRY(ptr) ((struct pram_xattr_entry *)(ptr)) ++#define FIRST_ENTRY(bh) ENTRY(HDR(bh)+1) ++#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0) ++#define GET_DESC(sbi, blocknr) \ ++ lookup_xblock_desc(sbi, blocknr, pram_xblock_desc_cache, 1) ++#define LOOKUP_DESC(sbi, blocknr) lookup_xblock_desc(sbi, blocknr, NULL, 0) ++ ++#ifdef PRAM_XATTR_DEBUG ++# define ea_idebug(inode, f...) do { \ ++ printk(KERN_DEBUG "inode %ld: ", inode->i_ino); \ ++ printk(f); \ ++ printk("\n"); \ ++ } while (0) ++# define ea_bdebug(blocknr, f...) do { \ ++ printk(KERN_DEBUG "block %lu: ", blocknr); \ ++ printk(f); \ ++ printk("\n"); \ ++ } while (0) ++#else ++# define ea_idebug(f...) ++# define ea_bdebug(f...) ++#endif ++ ++static int pram_xattr_set2(struct inode *, char *, struct pram_xblock_desc *, ++ struct pram_xattr_header *); ++ ++static int pram_xattr_cache_insert(struct super_block *sb, ++ unsigned long blocknr, u32 xhash); ++static struct pram_xblock_desc *pram_xattr_cache_find(struct inode *, ++ struct pram_xattr_header *); ++static void pram_xattr_rehash(struct pram_xattr_header *, ++ struct pram_xattr_entry *); ++ ++static struct mb_cache *pram_xattr_cache; ++static struct kmem_cache *pram_xblock_desc_cache; ++ ++static const struct xattr_handler *pram_xattr_handler_map[] = { ++ [PRAM_XATTR_INDEX_USER] = &pram_xattr_user_handler, ++#ifdef CONFIG_PRAMFS_POSIX_ACL ++ [PRAM_XATTR_INDEX_POSIX_ACL_ACCESS] = &pram_xattr_acl_access_handler, ++ [PRAM_XATTR_INDEX_POSIX_ACL_DEFAULT] = &pram_xattr_acl_default_handler, ++#endif ++ [PRAM_XATTR_INDEX_TRUSTED] = &pram_xattr_trusted_handler, ++#ifdef CONFIG_PRAMFS_SECURITY ++ [PRAM_XATTR_INDEX_SECURITY] = &pram_xattr_security_handler, ++#endif ++}; ++ ++const struct xattr_handler *pram_xattr_handlers[] = { ++ &pram_xattr_user_handler, ++ &pram_xattr_trusted_handler, ++#ifdef CONFIG_PRAMFS_POSIX_ACL ++ &pram_xattr_acl_access_handler, ++ &pram_xattr_acl_default_handler, ++#endif ++#ifdef CONFIG_PRAMFS_SECURITY ++ &pram_xattr_security_handler, ++#endif ++ NULL ++}; ++ ++static void desc_put(struct super_block *sb, struct pram_xblock_desc *desc) ++{ ++ struct pram_sb_info *sbi = PRAM_SB(sb); ++ if (!put_xblock_desc(sbi, desc)) { ++ /* Ok we can free the block and its descriptor */ ++ pram_dbg("freeing block %lu and its descriptor", desc->blocknr); ++ pram_free_block(sb, desc->blocknr); ++ kmem_cache_free(pram_xblock_desc_cache, desc); ++ } ++} ++ ++static inline const struct xattr_handler *pram_xattr_handler(int name_index) ++{ ++ const struct xattr_handler *handler = NULL; ++ ++ if (name_index > 0 && name_index < ARRAY_SIZE(pram_xattr_handler_map)) ++ handler = pram_xattr_handler_map[name_index]; ++ return handler; ++} ++ ++/* ++ * pram_xattr_get() ++ * ++ * Copy an extended attribute into the buffer ++ * provided, or compute the buffer size required. ++ * Buffer is NULL to compute the size of the buffer required. ++ * ++ * Returns a negative error number on failure, or the number of bytes ++ * used / required on success. ++ */ ++int pram_xattr_get(struct inode *inode, int name_index, const char *name, ++ void *buffer, size_t buffer_size) ++{ ++ char *bp = NULL; ++ struct pram_xattr_entry *entry; ++ struct pram_xblock_desc *desc; ++ struct pram_inode *pi; ++ size_t name_len, size; ++ char *end; ++ int error = 0; ++ unsigned long blocknr; ++ struct super_block *sb = inode->i_sb; ++ struct pram_sb_info *sbi = PRAM_SB(sb); ++ ++ ea_idebug(inode, "name=%d<%s>, buffer=%p, buffer_size=%ld", ++ name_index, name, buffer, (long)buffer_size); ++ ++ pi = pram_get_inode(sb, inode->i_ino); ++ if (!pi) ++ return -EINVAL; ++ if (name == NULL) ++ return -EINVAL; ++ name_len = strlen(name); ++ if (name_len > 255) ++ return -ERANGE; ++ down_read(&PRAM_I(inode)->xattr_sem); ++ error = -ENODATA; ++ if (!pi->i_xattr) ++ goto cleanup; ++ ea_idebug(inode, "reading block %llu", be64_to_cpu(pi->i_xattr)); ++ bp = pram_get_block(sb, be64_to_cpu(pi->i_xattr)); ++ error = -EIO; ++ if (!bp) ++ goto cleanup; ++ end = bp + sb->s_blocksize; ++ blocknr = pram_get_blocknr(sb, be64_to_cpu(pi->i_xattr)); ++ ea_bdebug(blocknr, "refcount=%d", be32_to_cpu(HDR(bp)->h_refcount)); ++ if (HDR(bp)->h_magic != cpu_to_be32(PRAM_XATTR_MAGIC)) { ++bad_block: pram_err(sb, "inode %ld: bad block %llu", inode->i_ino, ++ be64_to_cpu(pi->i_xattr)); ++ error = -EIO; ++ goto cleanup; ++ } ++ /* find named attribute */ ++ entry = FIRST_ENTRY(bp); ++ while (!IS_LAST_ENTRY(entry)) { ++ struct pram_xattr_entry *next = ++ PRAM_XATTR_NEXT(entry); ++ if ((char *)next >= end) ++ goto bad_block; ++ if (name_index == entry->e_name_index && ++ name_len == entry->e_name_len && ++ memcmp(name, entry->e_name, name_len) == 0) ++ goto found; ++ entry = next; ++ } ++ ++ desc = GET_DESC(sbi, blocknr); ++ if (IS_ERR(desc)) { ++ error = -ENOMEM; ++ goto cleanup; ++ } ++ desc_put(sb, desc); ++ if (pram_xattr_cache_insert(sb, blocknr, ++ be32_to_cpu(HDR(bp)->h_hash))) ++ ea_idebug(inode, "cache insert failed"); ++ error = -ENODATA; ++ goto cleanup; ++found: ++ /* check the buffer size */ ++ if (entry->e_value_block != 0) ++ goto bad_block; ++ size = be32_to_cpu(entry->e_value_size); ++ if (size > inode->i_sb->s_blocksize || ++ be16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize) ++ goto bad_block; ++ ++ desc = GET_DESC(sbi, blocknr); ++ if (IS_ERR(desc)) { ++ error = -ENOMEM; ++ goto cleanup; ++ } ++ desc_put(sb, desc); ++ if (pram_xattr_cache_insert(sb, blocknr, ++ be32_to_cpu(HDR(bp)->h_hash))) ++ ea_idebug(inode, "cache insert failed"); ++ if (buffer) { ++ error = -ERANGE; ++ if (size > buffer_size) ++ goto cleanup; ++ /* return value of attribute */ ++ memcpy(buffer, bp + be16_to_cpu(entry->e_value_offs), ++ size); ++ } ++ error = size; ++ ++cleanup: ++ up_read(&PRAM_I(inode)->xattr_sem); ++ ++ return error; ++} ++ ++/* ++ * pram_xattr_list() ++ * ++ * Copy a list of attribute names into the buffer ++ * provided, or compute the buffer size required. ++ * Buffer is NULL to compute the size of the buffer required. ++ * ++ * Returns a negative error number on failure, or the number of bytes ++ * used / required on success. ++ */ ++static int pram_xattr_list(struct dentry *dentry, char *buffer, ++ size_t buffer_size) ++{ ++ struct inode *inode = dentry->d_inode; ++ char *bp = NULL; ++ struct pram_xattr_entry *entry; ++ struct pram_xblock_desc *desc; ++ struct pram_inode *pi; ++ char *end; ++ size_t rest = buffer_size; ++ int error = 0; ++ unsigned long blocknr; ++ struct super_block *sb = inode->i_sb; ++ struct pram_sb_info *sbi = PRAM_SB(sb); ++ ++ ea_idebug(inode, "buffer=%p, buffer_size=%ld", ++ buffer, (long)buffer_size); ++ ++ pi = pram_get_inode(sb, inode->i_ino); ++ if (!pi) ++ return error; ++ down_read(&PRAM_I(inode)->xattr_sem); ++ error = 0; ++ if (!pi->i_xattr) ++ goto cleanup; ++ ea_idebug(inode, "reading block %llu", be64_to_cpu(pi->i_xattr)); ++ bp = pram_get_block(sb, be64_to_cpu(pi->i_xattr)); ++ blocknr = pram_get_blocknr(sb, be64_to_cpu(pi->i_xattr)); ++ error = -EIO; ++ if (!bp) ++ goto cleanup; ++ ea_bdebug(blocknr, "refcount=%d", be32_to_cpu(HDR(bp)->h_refcount)); ++ end = bp + sb->s_blocksize; ++ if (HDR(bp)->h_magic != cpu_to_be32(PRAM_XATTR_MAGIC)) { ++bad_block: pram_err(sb, "inode %ld: bad block %llu", inode->i_ino, ++ be64_to_cpu(pi->i_xattr)); ++ error = -EIO; ++ goto cleanup; ++ } ++ ++ /* check the on-disk data structure */ ++ entry = FIRST_ENTRY(bp); ++ while (!IS_LAST_ENTRY(entry)) { ++ struct pram_xattr_entry *next = PRAM_XATTR_NEXT(entry); ++ ++ if ((char *)next >= end) ++ goto bad_block; ++ entry = next; ++ } ++ ++ desc = GET_DESC(sbi, blocknr); ++ if (IS_ERR(desc)) { ++ error = -ENOMEM; ++ goto cleanup; ++ } ++ desc_put(sb, desc); ++ if (pram_xattr_cache_insert(sb, blocknr, ++ be32_to_cpu(HDR(bp)->h_hash))) ++ ea_idebug(inode, "cache insert failed"); ++ ++ /* list the attribute names */ ++ for (entry = FIRST_ENTRY(bp); !IS_LAST_ENTRY(entry); ++ entry = PRAM_XATTR_NEXT(entry)) { ++ const struct xattr_handler *handler = ++ pram_xattr_handler(entry->e_name_index); ++ ++ if (handler) { ++ size_t size = handler->list(dentry, buffer, rest, ++ entry->e_name, ++ entry->e_name_len, ++ handler->flags); ++ if (buffer) { ++ if (size > rest) { ++ error = -ERANGE; ++ goto cleanup; ++ } ++ buffer += size; ++ } ++ rest -= size; ++ } ++ } ++ error = buffer_size - rest; /* total size */ ++ ++cleanup: ++ up_read(&PRAM_I(inode)->xattr_sem); ++ ++ return error; ++} ++ ++/* ++ * Inode operation listxattr() ++ * ++ * dentry->d_inode->i_mutex: don't care ++ */ ++ssize_t pram_listxattr(struct dentry *dentry, char *buffer, size_t size) ++{ ++ return pram_xattr_list(dentry, buffer, size); ++} ++ ++/* ++ * pram_xattr_set() ++ * ++ * Create, replace or remove an extended attribute for this inode. Value ++ * is NULL to remove an existing extended attribute, and non-NULL to ++ * either replace an existing extended attribute, or create a new extended ++ * attribute. The flags XATTR_REPLACE and XATTR_CREATE ++ * specify that an extended attribute must exist and must not exist ++ * previous to the call, respectively. ++ * ++ * Returns 0, or a negative error number on failure. ++ */ ++int pram_xattr_set(struct inode *inode, int name_index, const char *name, ++ const void *value, size_t value_len, int flags) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct pram_sb_info *sbi = PRAM_SB(sb); ++ struct pram_xattr_header *header = NULL; ++ struct pram_xattr_entry *here, *last; ++ struct pram_inode *pi; ++ struct pram_xblock_desc *desc = NULL; ++ size_t name_len, free, min_offs = sb->s_blocksize; ++ int not_found = 1, error; ++ char *end; ++ char *bp = NULL; ++ unsigned long blocknr = 0; ++ ++ /* ++ * header -- Points either into bp, or to a temporarily ++ * allocated buffer. ++ * here -- The named entry found, or the place for inserting, within ++ * the block pointed to by header. ++ * last -- Points right after the last named entry within the block ++ * pointed to by header. ++ * min_offs -- The offset of the first value (values are aligned ++ * towards the end of the block). ++ * end -- Points right after the block pointed to by header. ++ */ ++ ++ ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld", ++ name_index, name, value, (long)value_len); ++ ++ if (value == NULL) ++ value_len = 0; ++ if (name == NULL) ++ return -EINVAL; ++ name_len = strlen(name); ++ if (name_len > 255 || value_len > sb->s_blocksize) ++ return -ERANGE; ++ pi = pram_get_inode(sb, inode->i_ino); ++ if (!pi) ++ return -EINVAL; ++ down_write(&PRAM_I(inode)->xattr_sem); ++ if (pi->i_xattr) { ++ /* The inode already has an extended attribute block. */ ++ bp = pram_get_block(sb, be64_to_cpu(pi->i_xattr)); ++ error = -EIO; ++ if (!bp) ++ goto cleanup; ++ blocknr = pram_get_blocknr(sb, be64_to_cpu(pi->i_xattr)); ++ ea_bdebug(blocknr, "refcount=%d", ++ be32_to_cpu(HDR(bp)->h_refcount)); ++ header = HDR(bp); ++ end = bp + sb->s_blocksize; ++ if (header->h_magic != cpu_to_be32(PRAM_XATTR_MAGIC)) { ++bad_block: ++ pram_err(sb, "inode %ld: bad block %llu", inode->i_ino, ++ be64_to_cpu(pi->i_xattr)); ++ error = -EIO; ++ goto cleanup; ++ } ++ /* Find the named attribute. */ ++ here = FIRST_ENTRY(bp); ++ while (!IS_LAST_ENTRY(here)) { ++ struct pram_xattr_entry *next = PRAM_XATTR_NEXT(here); ++ if ((char *)next >= end) ++ goto bad_block; ++ if (!here->e_value_block && here->e_value_size) { ++ size_t offs = be16_to_cpu(here->e_value_offs); ++ if (offs < min_offs) ++ min_offs = offs; ++ } ++ not_found = name_index - here->e_name_index; ++ if (!not_found) ++ not_found = name_len - here->e_name_len; ++ if (!not_found) ++ not_found = memcmp(name, here->e_name, ++ name_len); ++ if (not_found <= 0) ++ break; ++ here = next; ++ } ++ last = here; ++ /* We still need to compute min_offs and last. */ ++ while (!IS_LAST_ENTRY(last)) { ++ struct pram_xattr_entry *next = PRAM_XATTR_NEXT(last); ++ if ((char *)next >= end) ++ goto bad_block; ++ if (!last->e_value_block && last->e_value_size) { ++ size_t offs = be16_to_cpu(last->e_value_offs); ++ if (offs < min_offs) ++ min_offs = offs; ++ } ++ last = next; ++ } ++ ++ /* Check whether we have enough space left. */ ++ free = min_offs - ((char *)last - (char *)header) - ++ sizeof(__u32); ++ } else { ++ /* We will use a new extended attribute block. */ ++ free = sb->s_blocksize - ++ sizeof(struct pram_xattr_header) - sizeof(__u32); ++ here = last = NULL; /* avoid gcc uninitialized warning. */ ++ } ++ ++ if (not_found) { ++ /* Request to remove a nonexistent attribute? */ ++ error = -ENODATA; ++ if (flags & XATTR_REPLACE) ++ goto cleanup; ++ error = 0; ++ if (value == NULL) ++ goto cleanup; ++ } else { ++ /* Request to create an existing attribute? */ ++ error = -EEXIST; ++ if (flags & XATTR_CREATE) ++ goto cleanup; ++ if (!here->e_value_block && here->e_value_size) { ++ size_t size = be32_to_cpu(here->e_value_size); ++ ++ if (be16_to_cpu(here->e_value_offs) + size > ++ sb->s_blocksize || size > sb->s_blocksize) ++ goto bad_block; ++ free += PRAM_XATTR_SIZE(size); ++ } ++ free += PRAM_XATTR_LEN(name_len); ++ } ++ error = -ENOSPC; ++ if (free < PRAM_XATTR_LEN(name_len) + PRAM_XATTR_SIZE(value_len)) ++ goto cleanup; ++ ++ /* Here we know that we can set the new attribute. */ ++ ++ if (header) { ++ struct mb_cache_entry *ce; ++ ++ desc = GET_DESC(sbi, blocknr); ++ if (IS_ERR(desc)) { ++ error = -ENOMEM; ++ goto cleanup; ++ } ++ ++ /* assert(header == HDR(bp)); */ ++ ce = mb_cache_entry_get(pram_xattr_cache, ++ (struct block_device *)sbi, ++ blocknr); ++ mutex_lock(&desc->lock); ++ pram_memunlock_block(sb, bp); ++ if (header->h_refcount == cpu_to_be32(1)) { ++ ea_bdebug(blocknr, "modifying in-place"); ++ if (ce) ++ mb_cache_entry_free(ce); ++ /* keep it locked while modifying it. */ ++ } else { ++ int offset; ++ ++ if (ce) ++ mb_cache_entry_release(ce); ++ pram_memlock_block(sb, bp); ++ mutex_unlock(&desc->lock); ++ ea_bdebug(desc->blocknr, "cloning"); ++ header = kmalloc(inode->i_sb->s_blocksize, GFP_KERNEL); ++ error = -ENOMEM; ++ if (header == NULL) ++ goto cleanup; ++ memcpy(header, HDR(bp), inode->i_sb->s_blocksize); ++ header->h_refcount = cpu_to_be32(1); ++ ++ offset = (char *)here - bp; ++ here = ENTRY((char *)header + offset); ++ offset = (char *)last - bp; ++ last = ENTRY((char *)header + offset); ++ } ++ } else { ++ /* Allocate a buffer where we construct the new block. */ ++ header = kzalloc(sb->s_blocksize, GFP_KERNEL); ++ error = -ENOMEM; ++ if (header == NULL) ++ goto cleanup; ++ end = (char *)header + sb->s_blocksize; ++ header->h_magic = cpu_to_be32(PRAM_XATTR_MAGIC); ++ header->h_refcount = cpu_to_be32(1); ++ last = here = ENTRY(header+1); ++ } ++ ++ /* Iff we are modifying the block in-place, the block is locked here. */ ++ ++ if (not_found) { ++ /* Insert the new name. */ ++ size_t size = PRAM_XATTR_LEN(name_len); ++ size_t rest = (char *)last - (char *)here; ++ memmove((char *)here + size, here, rest); ++ memset(here, 0, size); ++ here->e_name_index = name_index; ++ here->e_name_len = name_len; ++ memcpy(here->e_name, name, name_len); ++ } else { ++ if (!here->e_value_block && here->e_value_size) { ++ char *first_val = (char *)header + min_offs; ++ size_t offs = be16_to_cpu(here->e_value_offs); ++ char *val = (char *)header + offs; ++ size_t size = PRAM_XATTR_SIZE( ++ be32_to_cpu(here->e_value_size)); ++ ++ if (size == PRAM_XATTR_SIZE(value_len)) { ++ /* The old and the new value have the same ++ size. Just replace. */ ++ here->e_value_size = cpu_to_be32(value_len); ++ memset(val + size - PRAM_XATTR_PAD, 0, ++ PRAM_XATTR_PAD); /* Clear pad bytes. */ ++ memcpy(val, value, value_len); ++ goto skip_replace; ++ } ++ ++ /* Remove the old value. */ ++ memmove(first_val + size, first_val, val - first_val); ++ memset(first_val, 0, size); ++ here->e_value_offs = 0; ++ min_offs += size; ++ ++ /* Adjust all value offsets. */ ++ last = ENTRY(header+1); ++ while (!IS_LAST_ENTRY(last)) { ++ size_t o = be16_to_cpu(last->e_value_offs); ++ if (!last->e_value_block && o < offs) ++ last->e_value_offs = ++ cpu_to_be16(o + size); ++ last = PRAM_XATTR_NEXT(last); ++ } ++ } ++ if (value == NULL) { ++ /* Remove the old name. */ ++ size_t size = PRAM_XATTR_LEN(name_len); ++ last = ENTRY((char *)last - size); ++ memmove(here, (char *)here + size, ++ (char *)last - (char *)here); ++ memset(last, 0, size); ++ } ++ } ++ ++ if (value != NULL) { ++ /* Insert the new value. */ ++ here->e_value_size = cpu_to_be32(value_len); ++ if (value_len) { ++ size_t size = PRAM_XATTR_SIZE(value_len); ++ char *val = (char *)header + min_offs - size; ++ here->e_value_offs = ++ cpu_to_be16((char *)val - (char *)header); ++ memset(val + size - PRAM_XATTR_PAD, 0, ++ PRAM_XATTR_PAD); /* Clear the pad bytes. */ ++ memcpy(val, value, value_len); ++ } ++ } ++ ++skip_replace: ++ if (IS_LAST_ENTRY(ENTRY(header+1))) { ++ /* This block is now empty. */ ++ if (bp && header == HDR(bp)) { ++ /* we were modifying in-place. */ ++ pram_memlock_block(sb, bp); ++ mutex_unlock(&desc->lock); ++ } ++ error = pram_xattr_set2(inode, bp, desc, NULL); ++ } else { ++ pram_xattr_rehash(header, here); ++ if (bp && header == HDR(bp)) { ++ /* we were modifying in-place. */ ++ pram_memlock_block(sb, bp); ++ mutex_unlock(&desc->lock); ++ } ++ error = pram_xattr_set2(inode, bp, desc, header); ++ } ++ ++cleanup: ++ desc_put(sb, desc); ++ if (!(bp && header == HDR(bp))) ++ kfree(header); ++ up_write(&PRAM_I(inode)->xattr_sem); ++ ++ return error; ++} ++ ++/* ++ * Second half of pram_xattr_set(): Update the file system. ++ */ ++static int pram_xattr_set2(struct inode *inode, char *old_bp, ++ struct pram_xblock_desc *old_desc, ++ struct pram_xattr_header *header) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct pram_sb_info *sbi = PRAM_SB(sb); ++ struct pram_xblock_desc *new_desc = NULL; ++ unsigned long blocknr; ++ struct pram_inode *pi; ++ int error; ++ char *new_bp = NULL; ++ ++ if (header) { ++ new_desc = pram_xattr_cache_find(inode, header); ++ if (new_desc) { ++ new_bp = pram_get_block(sb, ++ pram_get_block_off(sb, new_desc->blocknr)); ++ /* We found an identical block in the cache. */ ++ if (new_bp == old_bp) { ++ ea_bdebug(new_desc->blocknr, ++ "keeping this block"); ++ } else { ++ /* The old block is released after updating ++ the inode. */ ++ ea_bdebug(new_desc->blocknr, "reusing block"); ++ pram_memunlock_block(sb, new_bp); ++ be32_add_cpu(&HDR(new_bp)->h_refcount, 1); ++ pram_memlock_block(sb, new_bp); ++ ea_bdebug(new_desc->blocknr, "refcount now=%d", ++ be32_to_cpu(HDR(new_bp)->h_refcount)); ++ } ++ blocknr = new_desc->blocknr; ++ mutex_unlock(&new_desc->lock); ++ desc_put(sb, new_desc); ++ } else if (old_bp && header == HDR(old_bp)) { ++ /* Keep this block. No need to lock the block as we ++ don't need to change the reference count. */ ++ new_bp = old_bp; ++ pram_xattr_cache_insert(sb, old_desc->blocknr, ++ HDR(new_bp)->h_hash); ++ blocknr = old_desc->blocknr; ++ } else { ++ /* We need to allocate a new block */ ++ struct pram_xblock_desc *new_desc; ++ ++ error = pram_new_block(sb, &blocknr, 1); ++ if (error) ++ goto out; ++ ea_idebug(inode, "creating block %lu", blocknr); ++ new_desc = kmem_cache_alloc(pram_xblock_desc_cache, ++ GFP_KERNEL); ++ if (!new_desc) { ++ pram_free_block(sb, blocknr); ++ error = -EIO; ++ goto out; ++ } ++ xblock_desc_init_always(new_desc); ++ new_desc->blocknr = blocknr; ++ new_bp = pram_get_block(sb, ++ pram_get_block_off(sb, blocknr)); ++ if (!new_bp) { ++ pram_free_block(sb, blocknr); ++ kmem_cache_free(pram_xblock_desc_cache, ++ new_desc); ++ error = -EIO; ++ goto out; ++ } ++ pram_memunlock_block(sb, new_bp); ++ memcpy(new_bp, header, sb->s_blocksize); ++ pram_memlock_block(sb, new_bp); ++ insert_xblock_desc(sbi, new_desc); ++ pram_xattr_cache_insert(sb, new_desc->blocknr, ++ HDR(new_bp)->h_hash); ++ } ++ } ++ ++ /* Update the inode. */ ++ pi = pram_get_inode(sb, inode->i_ino); ++ pram_memunlock_inode(sb, pi); ++ pi->i_xattr = new_bp ? be64_to_cpu(pram_get_block_off(sb, blocknr)) : 0; ++ inode->i_ctime = CURRENT_TIME_SEC; ++ pi->i_ctime = cpu_to_be32(inode->i_ctime.tv_sec); ++ pram_memlock_inode(sb, pi); ++ ++ error = 0; ++ if (old_bp && old_bp != new_bp) { ++ struct mb_cache_entry *ce; ++ ++ /* Here old_desc MUST be valid or we have a bug */ ++ BUG_ON(!old_desc); ++ ++ /* ++ * If there was an old block and we are no longer using it, ++ * release the old block. ++ */ ++ ce = mb_cache_entry_get(pram_xattr_cache, ++ (struct block_device *)sbi, ++ old_desc->blocknr); ++ mutex_lock(&old_desc->lock); ++ if (HDR(old_bp)->h_refcount == cpu_to_be32(1)) { ++ /* Free the old block. */ ++ if (ce) ++ mb_cache_entry_free(ce); ++ ea_bdebug(old_desc->blocknr, "freeing"); ++ mutex_unlock(&old_desc->lock); ++ /* Caller will call desc_put later */ ++ mark_free_desc(old_desc); ++ } else { ++ /* Decrement the refcount only. */ ++ pram_memunlock_block(sb, old_bp); ++ be32_add_cpu(&HDR(old_bp)->h_refcount, -1); ++ pram_memlock_block(sb, old_bp); ++ if (ce) ++ mb_cache_entry_release(ce); ++ ea_bdebug(old_desc->blocknr, "refcount now=%d", ++ be32_to_cpu(HDR(old_bp)->h_refcount)); ++ mutex_unlock(&old_desc->lock); ++ } ++ } ++ ++out: ++ return error; ++} ++ ++/* ++ * pram_xattr_delete_inode() ++ * ++ * Free extended attribute resources associated with this inode. This ++ * is called immediately before an inode is freed. ++ */ ++void pram_xattr_delete_inode(struct inode *inode) ++{ ++ char *bp = NULL; ++ struct mb_cache_entry *ce; ++ struct pram_inode *pi; ++ struct pram_xblock_desc *desc; ++ struct super_block *sb = inode->i_sb; ++ struct pram_sb_info *sbi = PRAM_SB(sb); ++ unsigned long blocknr; ++ ++ pi = pram_get_inode(sb, inode->i_ino); ++ if (!pi) ++ goto cleanup; ++ down_write(&PRAM_I(inode)->xattr_sem); ++ if (!pi->i_xattr) ++ goto cleanup; ++ bp = pram_get_block(sb, be64_to_cpu(pi->i_xattr)); ++ if (!bp) { ++ pram_err(sb, "inode %ld: block %llu read error", inode->i_ino, ++ be64_to_cpu(pi->i_xattr)); ++ goto cleanup; ++ } ++ blocknr = pram_get_blocknr(sb, be64_to_cpu(pi->i_xattr)); ++ if (HDR(bp)->h_magic != cpu_to_be32(PRAM_XATTR_MAGIC)) { ++ pram_err(sb, "inode %ld: bad block %llu", inode->i_ino, ++ be64_to_cpu(pi->i_xattr)); ++ goto cleanup; ++ } ++ ce = mb_cache_entry_get(pram_xattr_cache, ++ (struct block_device *)sbi, blocknr); ++ desc = GET_DESC(sbi, blocknr); ++ if (IS_ERR(desc)) ++ goto cleanup; ++ mutex_lock(&desc->lock); ++ if (HDR(bp)->h_refcount == cpu_to_be32(1)) { ++ if (ce) ++ mb_cache_entry_free(ce); ++ mark_free_desc(desc); ++ } else { ++ be32_add_cpu(&HDR(bp)->h_refcount, -1); ++ if (ce) ++ mb_cache_entry_release(ce); ++ ea_bdebug(blocknr, "refcount now=%d", ++ be32_to_cpu(HDR(bp)->h_refcount)); ++ mutex_unlock(&desc->lock); ++ } ++ desc_put(sb, desc); ++ ++cleanup: ++ up_write(&PRAM_I(inode)->xattr_sem); ++} ++ ++/* ++ * pram_xattr_put_super() ++ * ++ * This is called when a file system is unmounted. ++ */ ++void pram_xattr_put_super(struct super_block *sb) ++{ ++ struct pram_sb_info *sbi = PRAM_SB(sb); ++ /* ++ * NOTE: we haven't got any block device to use with mb. Mb code ++ * doesn't use the pointer but it uses only the address as unique ++ * key so it's safe to use a "general purpose" address. We use ++ * super block info data as unique key. Maybe it'd be better to ++ * change mb code in order to use a generic void pointer to a ++ * generic id. ++ */ ++ mb_cache_shrink((struct block_device *)sbi); ++ erase_tree(sbi, pram_xblock_desc_cache); ++ kmem_cache_shrink(pram_xblock_desc_cache); ++} ++ ++ ++/* ++ * pram_xattr_cache_insert() ++ * ++ * Create a new entry in the extended attribute cache, and insert ++ * it unless such an entry is already in the cache. ++ * ++ * Returns 0, or a negative error number on failure. ++ */ ++static int pram_xattr_cache_insert(struct super_block *sb, ++ unsigned long blocknr, u32 xhash) ++{ ++ struct pram_sb_info *sbi = PRAM_SB(sb); ++ __u32 hash = be32_to_cpu(xhash); ++ struct mb_cache_entry *ce; ++ int error; ++ ++ ce = mb_cache_entry_alloc(pram_xattr_cache, GFP_NOFS); ++ if (!ce) ++ return -ENOMEM; ++ error = mb_cache_entry_insert(ce, (struct block_device *)sbi, blocknr, ++ hash); ++ if (error) { ++ mb_cache_entry_free(ce); ++ if (error == -EBUSY) { ++ ea_bdebug(blocknr, "already in cache"); ++ error = 0; ++ } ++ } else { ++ ea_bdebug(blocknr, "inserting [%x]", (int)hash); ++ mb_cache_entry_release(ce); ++ } ++ return error; ++} ++ ++/* ++ * pram_xattr_cmp() ++ * ++ * Compare two extended attribute blocks for equality. ++ * ++ * Returns 0 if the blocks are equal, 1 if they differ, and ++ * a negative error number on errors. ++ */ ++static int pram_xattr_cmp(struct pram_xattr_header *header1, ++ struct pram_xattr_header *header2) ++{ ++ struct pram_xattr_entry *entry1, *entry2; ++ ++ entry1 = ENTRY(header1+1); ++ entry2 = ENTRY(header2+1); ++ while (!IS_LAST_ENTRY(entry1)) { ++ if (IS_LAST_ENTRY(entry2)) ++ return 1; ++ if (entry1->e_hash != entry2->e_hash || ++ entry1->e_name_index != entry2->e_name_index || ++ entry1->e_name_len != entry2->e_name_len || ++ entry1->e_value_size != entry2->e_value_size || ++ memcmp(entry1->e_name, entry2->e_name, entry1->e_name_len)) ++ return 1; ++ if (entry1->e_value_block != 0 || entry2->e_value_block != 0) ++ return -EIO; ++ if (memcmp((char *)header1 + be16_to_cpu(entry1->e_value_offs), ++ (char *)header2 + be16_to_cpu(entry2->e_value_offs), ++ be32_to_cpu(entry1->e_value_size))) ++ return 1; ++ ++ entry1 = PRAM_XATTR_NEXT(entry1); ++ entry2 = PRAM_XATTR_NEXT(entry2); ++ } ++ if (!IS_LAST_ENTRY(entry2)) ++ return 1; ++ return 0; ++} ++ ++/* ++ * pram_xattr_cache_find() ++ * ++ * Find an identical extended attribute block. ++ * ++ * Returns a locked extended block descriptor for the block found, or ++ * NULL if such a block was not found or an error occurred. ++ * The block, however, is not memory unlocked. ++ */ ++static struct pram_xblock_desc *pram_xattr_cache_find(struct inode *inode, ++ struct pram_xattr_header *header) ++{ ++ __u32 hash = be32_to_cpu(header->h_hash); ++ struct mb_cache_entry *ce; ++ struct pram_xblock_desc *desc; ++ struct super_block *sb = inode->i_sb; ++ struct pram_sb_info *sbi = PRAM_SB(sb); ++ ++ if (!header->h_hash) ++ return NULL; /* never share */ ++ ea_idebug(inode, "looking for cached blocks [%x]", (int)hash); ++again: ++ ce = mb_cache_entry_find_first(pram_xattr_cache, ++ (struct block_device *)sbi, hash); ++ while (ce) { ++ char *bp; ++ ++ if (IS_ERR(ce)) { ++ if (PTR_ERR(ce) == -EAGAIN) ++ goto again; ++ break; ++ } ++ ++ bp = pram_get_block(sb, pram_get_block_off(sb, (unsigned long)ce->e_block)); ++ if (!bp) { ++ pram_err(sb, "inode %ld: block %ld read error", ++ inode->i_ino, (unsigned long) ce->e_block); ++ } else { ++ desc = LOOKUP_DESC(sbi, ce->e_block); ++ if (!desc) { ++ mb_cache_entry_release(ce); ++ return NULL; ++ } ++ mutex_lock(&desc->lock); ++ if (be32_to_cpu(HDR(bp)->h_refcount) > ++ PRAM_XATTR_REFCOUNT_MAX) { ++ ea_idebug(inode, "block %ld refcount %d>%d", ++ (unsigned long) ce->e_block, ++ be32_to_cpu(HDR(bp)->h_refcount), ++ PRAM_XATTR_REFCOUNT_MAX); ++ } else if (!pram_xattr_cmp(header, HDR(bp))) { ++ mb_cache_entry_release(ce); ++ return desc; ++ } ++ mutex_unlock(&desc->lock); ++ } ++ ce = mb_cache_entry_find_next(ce, ++ (struct block_device *)sbi, ++ hash); ++ } ++ return NULL; ++} ++ ++#define NAME_HASH_SHIFT 5 ++#define VALUE_HASH_SHIFT 16 ++ ++/* ++ * pram_xattr_hash_entry() ++ * ++ * Compute the hash of an extended attribute. ++ */ ++static inline void pram_xattr_hash_entry(struct pram_xattr_header *header, ++ struct pram_xattr_entry *entry) ++{ ++ __u32 hash = 0; ++ char *name = entry->e_name; ++ int n; ++ ++ for (n = 0; n < entry->e_name_len; n++) { ++ hash = (hash << NAME_HASH_SHIFT) ^ ++ (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^ ++ *name++; ++ } ++ ++ if (entry->e_value_block == 0 && entry->e_value_size != 0) { ++ __be32 *value = (__be32 *)((char *)header + ++ be16_to_cpu(entry->e_value_offs)); ++ for (n = (be32_to_cpu(entry->e_value_size) + ++ PRAM_XATTR_ROUND) >> PRAM_XATTR_PAD_BITS; n; n--) { ++ hash = (hash << VALUE_HASH_SHIFT) ^ ++ (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^ ++ be32_to_cpu(*value++); ++ } ++ } ++ entry->e_hash = cpu_to_be32(hash); ++} ++ ++#undef NAME_HASH_SHIFT ++#undef VALUE_HASH_SHIFT ++ ++#define BLOCK_HASH_SHIFT 16 ++ ++/* ++ * pram_xattr_rehash() ++ * ++ * Re-compute the extended attribute hash value after an entry has changed. ++ */ ++static void pram_xattr_rehash(struct pram_xattr_header *header, ++ struct pram_xattr_entry *entry) ++{ ++ struct pram_xattr_entry *here; ++ __u32 hash = 0; ++ ++ pram_xattr_hash_entry(header, entry); ++ here = ENTRY(header+1); ++ while (!IS_LAST_ENTRY(here)) { ++ if (!here->e_hash) { ++ /* Block is not shared if an entry's hash value == 0 */ ++ hash = 0; ++ break; ++ } ++ hash = (hash << BLOCK_HASH_SHIFT) ^ ++ (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^ ++ be32_to_cpu(here->e_hash); ++ here = PRAM_XATTR_NEXT(here); ++ } ++ header->h_hash = cpu_to_be32(hash); ++} ++ ++#undef BLOCK_HASH_SHIFT ++ ++static void init_xblock_desc_once(void *foo) ++{ ++ struct pram_xblock_desc *desc = (struct pram_xblock_desc *) foo; ++ ++ xblock_desc_init_once(desc); ++} ++ ++int __init init_pram_xattr(void) ++{ ++ int ret = 0; ++ pram_xattr_cache = mb_cache_create("pram_xattr", 6); ++ if (!pram_xattr_cache) { ++ ret = -ENOMEM; ++ goto fail1; ++ } ++ ++ pram_xblock_desc_cache = kmem_cache_create("pram_xblock_desc", ++ sizeof(struct pram_xblock_desc), ++ 0, (SLAB_RECLAIM_ACCOUNT| ++ SLAB_MEM_SPREAD), ++ init_xblock_desc_once); ++ if (!pram_xblock_desc_cache) { ++ ret = -ENOMEM; ++ goto fail2; ++ } ++ ++ return 0; ++fail2: ++ mb_cache_destroy(pram_xattr_cache); ++fail1: ++ return ret; ++} ++ ++void exit_pram_xattr(void) ++{ ++ mb_cache_destroy(pram_xattr_cache); ++ kmem_cache_destroy(pram_xblock_desc_cache); ++} +--- /dev/null ++++ b/fs/pramfs/xattr.h +@@ -0,0 +1,134 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Extended attributes for the pram filesystem. ++ * ++ * Copyright 2010-2011 Marco Stornelli <marco.stornelli@gmail.com> ++ * ++ * based on fs/ext2/xattr.h with the following copyright: ++ * ++ *(C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org> ++ * ++ * 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/init.h> ++#include <linux/xattr.h> ++ ++/* Magic value in attribute blocks */ ++#define PRAM_XATTR_MAGIC 0x6d617270 ++ ++/* Maximum number of references to one attribute block */ ++#define PRAM_XATTR_REFCOUNT_MAX 1024 ++ ++/* Name indexes */ ++#define PRAM_XATTR_INDEX_USER 1 ++#define PRAM_XATTR_INDEX_POSIX_ACL_ACCESS 2 ++#define PRAM_XATTR_INDEX_POSIX_ACL_DEFAULT 3 ++#define PRAM_XATTR_INDEX_TRUSTED 4 ++#define PRAM_XATTR_INDEX_SECURITY 5 ++ ++struct pram_xattr_header { ++ __be32 h_magic; /* magic number for identification */ ++ __be32 h_refcount; /* reference count */ ++ __be32 h_hash; /* hash value of all attributes */ ++ __u32 h_reserved[4]; /* zero right now */ ++}; ++ ++struct pram_xattr_entry { ++ __u8 e_name_len; /* length of name */ ++ __u8 e_name_index; /* attribute name index */ ++ __be16 e_value_offs; /* offset in disk block of value */ ++ __be32 e_value_block; /* disk block attribute is stored on (n/i) */ ++ __be32 e_value_size; /* size of attribute value */ ++ __be32 e_hash; /* hash value of name and value */ ++ char e_name[0]; /* attribute name */ ++}; ++ ++#define PRAM_XATTR_PAD_BITS 2 ++#define PRAM_XATTR_PAD (1<<PRAM_XATTR_PAD_BITS) ++#define PRAM_XATTR_ROUND (PRAM_XATTR_PAD-1) ++#define PRAM_XATTR_LEN(name_len) \ ++ (((name_len) + PRAM_XATTR_ROUND + \ ++ sizeof(struct pram_xattr_entry)) & ~PRAM_XATTR_ROUND) ++#define PRAM_XATTR_NEXT(entry) \ ++ ((struct pram_xattr_entry *)( \ ++ (char *)(entry) + PRAM_XATTR_LEN((entry)->e_name_len))) ++#define PRAM_XATTR_SIZE(size) \ ++ (((size) + PRAM_XATTR_ROUND) & ~PRAM_XATTR_ROUND) ++ ++#ifdef CONFIG_PRAMFS_XATTR ++ ++extern const struct xattr_handler pram_xattr_user_handler; ++extern const struct xattr_handler pram_xattr_trusted_handler; ++extern const struct xattr_handler pram_xattr_acl_access_handler; ++extern const struct xattr_handler pram_xattr_acl_default_handler; ++extern const struct xattr_handler pram_xattr_security_handler; ++ ++extern ssize_t pram_listxattr(struct dentry *, char *, size_t); ++ ++extern int pram_xattr_get(struct inode *, int, const char *, void *, size_t); ++extern int pram_xattr_set(struct inode *, int, const char *, const void *, ++ size_t, int); ++ ++extern void pram_xattr_delete_inode(struct inode *); ++extern void pram_xattr_put_super(struct super_block *); ++ ++extern int init_pram_xattr(void) __init; ++extern void exit_pram_xattr(void); ++ ++extern const struct xattr_handler *pram_xattr_handlers[]; ++ ++# else /* CONFIG_PRAMFS_XATTR */ ++ ++static inline int ++pram_xattr_get(struct inode *inode, int name_index, ++ const char *name, void *buffer, size_t size) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline int ++pram_xattr_set(struct inode *inode, int name_index, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline void ++pram_xattr_delete_inode(struct inode *inode) ++{ ++} ++ ++static inline void ++pram_xattr_put_super(struct super_block *sb) ++{ ++} ++ ++static inline int ++init_pram_xattr(void) ++{ ++ return 0; ++} ++ ++static inline void ++exit_pram_xattr(void) ++{ ++} ++ ++#define pram_xattr_handlers NULL ++ ++# endif /* CONFIG_PRAMFS_XATTR */ ++ ++#ifdef CONFIG_PRAMFS_SECURITY ++extern int pram_init_security(struct inode *inode, struct inode *dir, ++ const struct qstr *qstr); ++#else ++static inline int pram_init_security(struct inode *inode, struct inode *dir, ++ const struct qstr *qstr) ++{ ++ return 0; ++} ++#endif diff --git a/patches.pramfs/13-pramfs-extended-attributes-descriptors-tree.patch b/patches.pramfs/13-pramfs-extended-attributes-descriptors-tree.patch new file mode 100644 index 00000000000000..5733054cdf310b --- /dev/null +++ b/patches.pramfs/13-pramfs-extended-attributes-descriptors-tree.patch @@ -0,0 +1,250 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:18:39 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:12:02 +0200 +Subject: pramfs: extended attributes descriptors tree +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE99E2.9000206@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +Extended attributes block descriptors tree. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> + +--- + fs/pramfs/desctree.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++ + fs/pramfs/desctree.h | 44 ++++++++++++ + 2 files changed, 225 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/desctree.c +@@ -0,0 +1,181 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Extended attributes block descriptors tree. ++ * ++ * Copyright 2010-2011 Marco Stornelli <marco.stornelli@gmail.com> ++ * ++ * 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/spinlock.h> ++#include "desctree.h" ++#include "pram.h" ++ ++/* xblock_desc_init_always() ++ * ++ * These are initializations that need to be done on every ++ * descriptor allocation as the fields are not initialised ++ * by slab allocation. ++ */ ++void xblock_desc_init_always(struct pram_xblock_desc *desc) ++{ ++ atomic_set(&desc->refcount, 0); ++ desc->blocknr = 0; ++ desc->flags = 0; ++} ++ ++/* xblock_desc_init_once() ++ * ++ * These are initializations that only need to be done ++ * once, because the fields are idempotent across use ++ * of the descriptor, so let the slab aware of that. ++ */ ++void xblock_desc_init_once(struct pram_xblock_desc *desc) ++{ ++ mutex_init(&desc->lock); ++} ++ ++/* __insert_xblock_desc() ++ * ++ * Insert a new descriptor in the tree. ++ */ ++static void __insert_xblock_desc(struct pram_sb_info *sbi, ++ unsigned long blocknr, struct rb_node *node) ++{ ++ struct rb_node **p = &(sbi->desc_tree.rb_node); ++ struct rb_node *parent = NULL; ++ struct pram_xblock_desc *desc; ++ ++ while (*p) { ++ parent = *p; ++ desc = rb_entry(parent, struct pram_xblock_desc, node); ++ ++ if (blocknr < desc->blocknr) ++ p = &(*p)->rb_left; ++ else if (blocknr > desc->blocknr) ++ p = &(*p)->rb_right; ++ else ++ /* Oops...an other descriptor for the same block ? */ ++ BUG(); ++ } ++ ++ rb_link_node(node, parent, p); ++ rb_insert_color(node, &sbi->desc_tree); ++} ++ ++void insert_xblock_desc(struct pram_sb_info *sbi, struct pram_xblock_desc *desc) ++{ ++ spin_lock(&sbi->desc_tree_lock); ++ __insert_xblock_desc(sbi, desc->blocknr, &desc->node); ++ spin_unlock(&sbi->desc_tree_lock); ++}; ++ ++/* __lookup_xblock_desc() ++ * ++ * Search an extended attribute descriptor in the tree via the ++ * block number. It returns the descriptor if it's found or ++ * NULL. If not found it creates a new descriptor if create is not 0. ++ */ ++static struct pram_xblock_desc *__lookup_xblock_desc(struct pram_sb_info *sbi, ++ unsigned long blocknr, ++ struct kmem_cache *cache, ++ int create) ++{ ++ struct rb_node *n = sbi->desc_tree.rb_node; ++ struct pram_xblock_desc *desc = NULL; ++ ++ while (n) { ++ desc = rb_entry(n, struct pram_xblock_desc, node); ++ ++ if (blocknr < desc->blocknr) ++ n = n->rb_left; ++ else if (blocknr > desc->blocknr) ++ n = n->rb_right; ++ else { ++ atomic_inc(&desc->refcount); ++ goto out; ++ } ++ } ++ ++ /* not found */ ++ if (create) { ++ desc = kmem_cache_alloc(cache, GFP_NOFS); ++ if (!desc) ++ return ERR_PTR(-ENOMEM); ++ xblock_desc_init_always(desc); ++ atomic_set(&desc->refcount, 1); ++ desc->blocknr = blocknr; ++ __insert_xblock_desc(sbi, desc->blocknr, &desc->node); ++ } ++out: ++ return desc; ++} ++ ++struct pram_xblock_desc *lookup_xblock_desc(struct pram_sb_info *sbi, ++ unsigned long blocknr, ++ struct kmem_cache *cache, ++ int create) ++{ ++ struct pram_xblock_desc *desc = NULL; ++ ++ spin_lock(&sbi->desc_tree_lock); ++ desc = __lookup_xblock_desc(sbi, blocknr, cache, create); ++ spin_unlock(&sbi->desc_tree_lock); ++ return desc; ++} ++ ++/* put_xblock_desc() ++ * ++ * Decrement the reference count and if it reaches zero and the ++ * desciptor has been marked to be free, then we free it. ++ * It returns 0 if the descriptor has been deleted and 1 otherwise. ++ */ ++int put_xblock_desc(struct pram_sb_info *sbi, struct pram_xblock_desc *desc) ++{ ++ int ret = 1; ++ if (!desc) ++ return ret; ++ ++ if (atomic_dec_and_lock(&desc->refcount, &sbi->desc_tree_lock)) { ++ if (test_bit(FREEING, &desc->flags)) { ++ rb_erase(&desc->node, &sbi->desc_tree); ++ pram_dbg("erasing desc for block %lu\n", desc->blocknr); ++ ret = 0; ++ } ++ spin_unlock(&sbi->desc_tree_lock); ++ } ++ return ret; ++}; ++ ++/* mark_free_desc() ++ * ++ * Mark free a descriptor. The descriptor will be deleted later in the ++ * put_xblock_desc(). ++ */ ++void mark_free_desc(struct pram_xblock_desc *desc) ++{ ++ set_bit(FREEING, &desc->flags); ++} ++ ++/* erase_tree() ++ * ++ * Free all objects in the tree. ++ */ ++void erase_tree(struct pram_sb_info *sbi, struct kmem_cache *cachep) ++{ ++ struct rb_node *n; ++ struct pram_xblock_desc *desc; ++ ++ spin_lock(&sbi->desc_tree_lock); ++ n = rb_first(&sbi->desc_tree); ++ while (n) { ++ desc = rb_entry(n, struct pram_xblock_desc, node); ++ rb_erase(n, &sbi->desc_tree); ++ kmem_cache_free(cachep, desc); ++ n = rb_next(n); ++ } ++ spin_unlock(&sbi->desc_tree_lock); ++} +--- /dev/null ++++ b/fs/pramfs/desctree.h +@@ -0,0 +1,44 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Extended attributes block descriptors tree. ++ * ++ * Copyright 2010-2011 Marco Stornelli <marco.stornelli@gmail.com> ++ * ++ * 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/atomic.h> ++#include <linux/slab.h> ++#include "pram.h" ++ ++struct pram_xblock_desc { ++#define FREEING (1UL << 1) ++ unsigned long flags; /* descriptor flags */ ++ atomic_t refcount; /* users count of this descriptor */ ++ unsigned long blocknr; /* absolute block number */ ++ struct mutex lock; /* block lock */ ++ struct rb_node node; /* node in the rb tree */ ++}; ++ ++extern struct pram_xblock_desc *lookup_xblock_desc(struct pram_sb_info *sbi, ++ unsigned long blocknr, ++ struct kmem_cache *, int); ++extern void insert_xblock_desc(struct pram_sb_info *sbi, ++ struct pram_xblock_desc *desc); ++extern void mark_free_desc(struct pram_xblock_desc *desc); ++extern int put_xblock_desc(struct pram_sb_info *sbi, ++ struct pram_xblock_desc *desc); ++extern void xblock_desc_init_always(struct pram_xblock_desc *desc); ++extern void xblock_desc_init_once(struct pram_xblock_desc *desc); ++extern void erase_tree(struct pram_sb_info *sbi, ++ struct kmem_cache *); ++ ++static inline void xblock_desc_init(struct pram_xblock_desc *desc) ++{ ++ xblock_desc_init_always(desc); ++ xblock_desc_init_once(desc); ++}; ++ diff --git a/patches.pramfs/14-pramfs-memory-write-protection.patch b/patches.pramfs/14-pramfs-memory-write-protection.patch new file mode 100644 index 00000000000000..e7d6b51f2cd02b --- /dev/null +++ b/patches.pramfs/14-pramfs-memory-write-protection.patch @@ -0,0 +1,219 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:18:51 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:12:16 +0200 +Subject: pramfs: memory write protection +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE99F0.6020105@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +Memory write protection. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> + +--- + fs/pramfs/wprotect.c | 41 +++++++++++++ + fs/pramfs/wprotect.h | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 194 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/wprotect.c +@@ -0,0 +1,41 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Write protection for the filesystem pages. ++ * ++ * Copyright 2009-2011 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/fs.h> ++#include <linux/mm.h> ++#include <linux/io.h> ++#include "pram.h" ++ ++DEFINE_SPINLOCK(writeable_lock); ++ ++void pram_writeable(void *vaddr, unsigned long size, int rw) ++{ ++ int ret = 0; ++ unsigned long nrpages = size >> PAGE_SHIFT; ++ unsigned long addr = (unsigned long)vaddr; ++ ++ /* Page aligned */ ++ addr &= PAGE_MASK; ++ ++ if (size & (PAGE_SIZE - 1)) ++ nrpages++; ++ ++ if (rw) ++ ret = set_memory_rw(addr, nrpages); ++ else ++ ret = set_memory_ro(addr, nrpages); ++ ++ BUG_ON(ret); ++} +--- /dev/null ++++ b/fs/pramfs/wprotect.h +@@ -0,0 +1,153 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Memory protection definitions for the PRAMFS filesystem. ++ * ++ * Copyright 2010-2011 Marco Stornelli <marco.stornelli@gmail.com> ++ * 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. ++ */ ++ ++#ifndef __WPROTECT_H ++#define __WPROTECT_H ++ ++#include <linux/pram_fs.h> ++ ++/* pram_memunlock_super() before calling! */ ++static inline void pram_sync_super(struct pram_super_block *ps) ++{ ++ u16 crc = 0; ++ ps->s_wtime = cpu_to_be32(get_seconds()); ++ ps->s_sum = 0; ++ crc = crc16(~0, (__u8 *)ps + sizeof(__be16), PRAM_SB_SIZE - ++ sizeof(__be16)); ++ ps->s_sum = cpu_to_be16(crc); ++ /* Keep sync redundant super block */ ++ memcpy((void *)ps + PRAM_SB_SIZE, (void *)ps, PRAM_SB_SIZE); ++} ++ ++/* pram_memunlock_inode() before calling! */ ++static inline void pram_sync_inode(struct pram_inode *pi) ++{ ++ u16 crc = 0; ++ pi->i_sum = 0; ++ crc = crc16(~0, (__u8 *)pi + sizeof(__be16), PRAM_INODE_SIZE - ++ sizeof(__be16)); ++ pi->i_sum = cpu_to_be16(crc); ++} ++ ++#ifdef CONFIG_PRAMFS_WRITE_PROTECT ++extern void pram_writeable(void *vaddr, unsigned long size, int rw); ++extern spinlock_t writeable_lock; ++static inline int pram_is_protected(struct super_block *sb) ++{ ++ struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info; ++ return sbi->s_mount_opt & PRAM_MOUNT_PROTECT; ++} ++ ++static inline void __pram_memunlock_range(void *p, unsigned long len) ++{ ++ /* ++ * NOTE: Ideally we should lock all the kernel to be memory safe ++ * and avoid to write in the protected memory, ++ * obviously it's not possible, so we only serialize ++ * the operations at fs level. We can't disable the interrupts ++ * because we could have a deadlock in this path. ++ */ ++ spin_lock(&writeable_lock); ++ pram_writeable(p, len, 1); ++} ++ ++static inline void __pram_memlock_range(void *p, unsigned long len) ++{ ++ pram_writeable(p, len, 0); ++ spin_unlock(&writeable_lock); ++} ++ ++static inline void pram_memunlock_range(struct super_block *sb, void *p, ++ unsigned long len) ++{ ++ if (pram_is_protected(sb)) ++ __pram_memunlock_range(p, len); ++} ++ ++static inline void pram_memlock_range(struct super_block *sb, void *p, ++ unsigned long len) ++{ ++ if (pram_is_protected(sb)) ++ __pram_memlock_range(p, len); ++} ++ ++static inline void pram_memunlock_super(struct super_block *sb, ++ struct pram_super_block *ps) ++{ ++ if (pram_is_protected(sb)) ++ __pram_memunlock_range(ps, PRAM_SB_SIZE); ++} ++ ++static inline void pram_memlock_super(struct super_block *sb, ++ struct pram_super_block *ps) ++{ ++ pram_sync_super(ps); ++ if (pram_is_protected(sb)) ++ __pram_memlock_range(ps, PRAM_SB_SIZE); ++} ++ ++static inline void pram_memunlock_inode(struct super_block *sb, ++ struct pram_inode *pi) ++{ ++ if (pram_is_protected(sb)) ++ __pram_memunlock_range(pi, PRAM_SB_SIZE); ++} ++ ++static inline void pram_memlock_inode(struct super_block *sb, ++ struct pram_inode *pi) ++{ ++ pram_sync_inode(pi); ++ if (pram_is_protected(sb)) ++ __pram_memlock_range(pi, PRAM_SB_SIZE); ++} ++ ++static inline void pram_memunlock_block(struct super_block *sb, ++ void *bp) ++{ ++ if (pram_is_protected(sb)) ++ __pram_memunlock_range(bp, sb->s_blocksize); ++} ++ ++static inline void pram_memlock_block(struct super_block *sb, ++ void *bp) ++{ ++ if (pram_is_protected(sb)) ++ __pram_memlock_range(bp, sb->s_blocksize); ++} ++ ++#else ++#define pram_is_protected(sb) 0 ++#define pram_writeable(vaddr, size, rw) do {} while (0) ++static inline void pram_memunlock_range(struct super_block *sb, void *p, ++ unsigned long len) {} ++static inline void pram_memlock_range(struct super_block *sb, void *p, ++ unsigned long len) {} ++static inline void pram_memunlock_super(struct super_block *sb, ++ struct pram_super_block *ps) {} ++static inline void pram_memlock_super(struct super_block *sb, ++ struct pram_super_block *ps) ++{ ++ pram_sync_super(ps); ++} ++static inline void pram_memunlock_inode(struct super_block *sb, ++ struct pram_inode *pi) {} ++static inline void pram_memlock_inode(struct super_block *sb, ++ struct pram_inode *pi) ++{ ++ pram_sync_inode(pi); ++} ++static inline void pram_memunlock_block(struct super_block *sb, ++ void *bp) {} ++static inline void pram_memlock_block(struct super_block *sb, ++ void *bp) {} ++ ++#endif /* CONFIG PRAMFS_WRITE_PROTECT */ ++#endif diff --git a/patches.pramfs/15-pramfs-test-module.patch b/patches.pramfs/15-pramfs-test-module.patch new file mode 100644 index 00000000000000..43c666c140707d --- /dev/null +++ b/patches.pramfs/15-pramfs-test-module.patch @@ -0,0 +1,68 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:19:02 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:12:29 +0200 +Subject: pramfs: test module +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE99FD.4040508@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +Test module. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> + +--- + fs/pramfs/pramfs_test.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/pramfs_test.c +@@ -0,0 +1,47 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Pramfs test module. ++ * ++ * Copyright 2009-2011 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/version.h> ++#include <linux/init.h> ++#include <linux/fs.h> ++#include "pram.h" ++ ++int __init test_pramfs_write(void) ++{ ++ struct pram_super_block *psb; ++ ++ psb = get_pram_super(); ++ if (!psb) { ++ printk(KERN_ERR ++ "%s: PRAMFS super block not found (not mounted?)\n", ++ __func__); ++ return 1; ++ } ++ ++ /* ++ * Attempt an unprotected clear of checksum information in the ++ * superblock, this should cause a kernel page protection fault. ++ */ ++ printk("%s: writing to kernel VA %p\n", __func__, psb); ++ psb->s_sum = 0; ++ ++ return 0; ++} ++ ++void test_pramfs_write_cleanup(void) {} ++ ++/* Module information */ ++MODULE_LICENSE("GPL"); ++module_init(test_pramfs_write); ++module_exit(test_pramfs_write_cleanup); diff --git a/patches.pramfs/16-pramfs-ioctl-operations.patch b/patches.pramfs/16-pramfs-ioctl-operations.patch new file mode 100644 index 00000000000000..1287cd36ea169e --- /dev/null +++ b/patches.pramfs/16-pramfs-ioctl-operations.patch @@ -0,0 +1,142 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:19:17 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:12:42 +0200 +Subject: pramfs: ioctl operations +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE9A0A.2080201@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +Ioctl operations. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> + +--- + fs/pramfs/ioctl.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 121 insertions(+) + +--- /dev/null ++++ b/fs/pramfs/ioctl.c +@@ -0,0 +1,121 @@ ++/* ++ * BRIEF DESCRIPTION ++ * ++ * Ioctl operations. ++ * ++ * Copyright 2010-2011 Marco Stornelli <marco.stornelli@gmail.com> ++ * ++ * 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/capability.h> ++#include <linux/time.h> ++#include <linux/sched.h> ++#include <linux/compat.h> ++#include <linux/mount.h> ++#include "pram.h" ++ ++long pram_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ struct inode *inode = filp->f_dentry->d_inode; ++ struct pram_inode *pi; ++ unsigned int flags; ++ int ret; ++ ++ pi = pram_get_inode(inode->i_sb, inode->i_ino); ++ if (!pi) ++ return -EACCES; ++ ++ switch (cmd) { ++ case FS_IOC_GETFLAGS: ++ flags = be32_to_cpu(pi->i_flags) & PRAM_FL_USER_VISIBLE; ++ return put_user(flags, (int __user *) arg); ++ case FS_IOC_SETFLAGS: { ++ unsigned int oldflags; ++ ++ ret = mnt_want_write(filp->f_path.mnt); ++ if (ret) ++ return ret; ++ ++ if (!inode_owner_or_capable(inode)) { ++ ret = -EPERM; ++ goto flags_out; ++ } ++ ++ if (get_user(flags, (int __user *) arg)) { ++ ret = -EFAULT; ++ goto flags_out; ++ } ++ ++ mutex_lock(&inode->i_mutex); ++ oldflags = be32_to_cpu(pi->i_flags); ++ ++ if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { ++ if (!capable(CAP_LINUX_IMMUTABLE)) { ++ mutex_unlock(&inode->i_mutex); ++ ret = -EPERM; ++ goto flags_out; ++ } ++ } ++ ++ if (!S_ISDIR(inode->i_mode)) ++ flags &= ~FS_DIRSYNC_FL; ++ ++ flags = flags & FS_FL_USER_MODIFIABLE; ++ flags |= oldflags & ~FS_FL_USER_MODIFIABLE; ++ pram_memunlock_inode(inode->i_sb, pi); ++ pi->i_flags = cpu_to_be32(flags); ++ inode->i_ctime = CURRENT_TIME_SEC; ++ pi->i_ctime = cpu_to_be32(inode->i_ctime.tv_sec); ++ pram_set_inode_flags(inode, pi); ++ pram_memlock_inode(inode->i_sb, pi); ++ mutex_unlock(&inode->i_mutex); ++flags_out: ++ mnt_drop_write(filp->f_path.mnt); ++ return ret; ++ } ++ case FS_IOC_GETVERSION: ++ return put_user(inode->i_generation, (int __user *) arg); ++ case FS_IOC_SETVERSION: ++ if (!inode_owner_or_capable(inode)) ++ return -EPERM; ++ ret = mnt_want_write(filp->f_path.mnt); ++ if (ret) ++ return ret; ++ if (get_user(inode->i_generation, (int __user *) arg)) { ++ ret = -EFAULT; ++ } else { ++ inode->i_ctime = CURRENT_TIME_SEC; ++ pram_update_inode(inode); ++ } ++ mnt_drop_write(filp->f_path.mnt); ++ return ret; ++ default: ++ return -ENOTTY; ++ } ++} ++ ++#ifdef CONFIG_COMPAT ++long pram_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case FS_IOC32_GETFLAGS: ++ cmd = FS_IOC_GETFLAGS; ++ break; ++ case FS_IOC32_SETFLAGS: ++ cmd = FS_IOC_SETFLAGS; ++ break; ++ case FS_IOC32_GETVERSION: ++ cmd = FS_IOC_GETVERSION; ++ break; ++ case FS_IOC32_SETVERSION: ++ cmd = FS_IOC_SETVERSION; ++ break; ++ default: ++ return -ENOIOCTLCMD; ++ } ++ return pram_ioctl(file, cmd, (unsigned long) compat_ptr(arg)); ++} ++#endif diff --git a/patches.pramfs/17-pramfs-makefile-and-kconfig.patch b/patches.pramfs/17-pramfs-makefile-and-kconfig.patch new file mode 100644 index 00000000000000..9988f85966be3f --- /dev/null +++ b/patches.pramfs/17-pramfs-makefile-and-kconfig.patch @@ -0,0 +1,192 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Sat May 12 10:19:31 2012 +From: Marco Stornelli <marco.stornelli@gmail.com> +Date: Sat, 12 May 2012 19:12:54 +0200 +Subject: pramfs: Makefile and Kconfig +To: LTSI <ltsi-dev@lists.linuxfoundation.org> +Message-ID: <4FAE9A16.4000501@gmail.com> + + +From: Marco Stornelli <marco.stornelli@gmail.com> + +Makefile and Kconfig. + +Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> + +--- + MAINTAINERS | 9 ++++++ + arch/Kconfig | 3 ++ + arch/x86/Kconfig | 1 + fs/Kconfig | 6 ++-- + fs/Makefile | 1 + fs/pramfs/Kconfig | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + fs/pramfs/Makefile | 14 ++++++++++ + 7 files changed, 104 insertions(+), 2 deletions(-) + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -5049,6 +5049,15 @@ S: Maintained + F: drivers/net/pptp.c + W: http://sourceforge.net/projects/accel-pptp + ++PRAM FILE SYSTEM ++M: Marco Stornelli <marco.stornelli@gmail.com> ++W: http://pramfs.sourceforge.net ++L: pramfs-devel@lists.sourceforge.net (subscribers-only) ++S: Maintained ++F: Documentation/filesystems/pramfs.txt ++F: fs/pramfs/ ++F: include/linux/pram* ++ + PREEMPTIBLE KERNEL + M: Robert Love <rml@tech9.net> + L: kpreempt-tech@lists.sourceforge.net +--- a/arch/Kconfig ++++ b/arch/Kconfig +@@ -97,6 +97,9 @@ config USER_RETURN_NOTIFIER + config HAVE_IOREMAP_PROT + bool + ++config HAVE_SET_MEMORY_RO ++ bool ++ + config HAVE_KPROBES + bool + +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -22,6 +22,7 @@ config X86 + select HAVE_OPROFILE + select HAVE_PERF_EVENTS + select HAVE_IRQ_WORK ++ select HAVE_SET_MEMORY_RO + select HAVE_IOREMAP_PROT + select HAVE_KPROBES + select HAVE_MEMBLOCK +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -13,7 +13,7 @@ source "fs/ext4/Kconfig" + config FS_XIP + # execute in place + bool +- depends on EXT2_FS_XIP ++ depends on EXT2_FS_XIP || PRAMFS_XIP + default y + + source "fs/jbd/Kconfig" +@@ -25,7 +25,8 @@ config FS_MBCACHE + default y if EXT2_FS=y && EXT2_FS_XATTR + default y if EXT3_FS=y && EXT3_FS_XATTR + default y if EXT4_FS=y && EXT4_FS_XATTR +- default m if EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4_FS_XATTR ++ default y if PRAMFS=y && PRAMFS_XATTR ++ default m if EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4_FS_XATTR || PRAMFS_XATTR + + source "fs/reiserfs/Kconfig" + source "fs/jfs/Kconfig" +@@ -207,6 +208,7 @@ source "fs/romfs/Kconfig" + source "fs/pstore/Kconfig" + source "fs/sysv/Kconfig" + source "fs/ufs/Kconfig" ++source "fs/pramfs/Kconfig" + source "fs/exofs/Kconfig" + + endif # MISC_FILESYSTEMS +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -124,3 +124,4 @@ obj-$(CONFIG_GFS2_FS) += gfs2/ + obj-$(CONFIG_EXOFS_FS) += exofs/ + obj-$(CONFIG_CEPH_FS) += ceph/ + obj-$(CONFIG_PSTORE) += pstore/ ++obj-$(CONFIG_PRAMFS) += pramfs/ +--- /dev/null ++++ b/fs/pramfs/Kconfig +@@ -0,0 +1,72 @@ ++config PRAMFS ++ tristate "Persistent and Protected RAM file system support" ++ depends on HAS_IOMEM && EXPERIMENTAL ++ select CRC16 ++ help ++ If your system has a block of fast (comparable in access speed to ++ system memory) and non-volatile RAM and you wish to mount a ++ light-weight, full-featured, and space-efficient filesystem over it, ++ say Y here, and read <file:Documentation/filesystems/pramfs.txt>. ++ ++ To compile this as a module, choose M here: the module will be ++ called pramfs. ++ ++config PRAMFS_XIP ++ bool "Execute-in-place in PRAMFS" ++ depends on PRAMFS && BLOCK ++ help ++ Say Y here to enable XIP feature of PRAMFS. ++ ++config PRAMFS_WRITE_PROTECT ++ bool "PRAMFS write protection" ++ depends on PRAMFS && MMU && HAVE_SET_MEMORY_RO ++ default y ++ help ++ Say Y here to enable the write protect feature of PRAMFS. ++ ++config PRAMFS_XATTR ++ bool "PRAMFS extended attributes" ++ depends on PRAMFS && BLOCK ++ help ++ Extended attributes are name:value pairs associated with inodes by ++ the kernel or by users (see the attr(5) manual page, or visit ++ <http://acl.bestbits.at/> for details). ++ ++ If unsure, say N. ++ ++config PRAMFS_POSIX_ACL ++ bool "PRAMFS POSIX Access Control Lists" ++ depends on PRAMFS_XATTR ++ select FS_POSIX_ACL ++ help ++ Posix Access Control Lists (ACLs) support permissions for users and ++ groups beyond the owner/group/world scheme. ++ ++ To learn more about Access Control Lists, visit the Posix ACLs for ++ Linux website <http://acl.bestbits.at/>. ++ ++ If you don't know what Access Control Lists are, say N. ++ ++config PRAMFS_SECURITY ++ bool "PRAMFS Security Labels" ++ depends on PRAMFS_XATTR ++ help ++ Security labels support alternative access control models ++ implemented by security modules like SELinux. This option ++ enables an extended attribute handler for file security ++ labels in the pram filesystem. ++ ++ If you are not using a security module that requires using ++ extended attributes for file security labels, say N. ++ ++config PRAMFS_TEST ++ boolean ++ depends on PRAMFS ++ ++config PRAMFS_TEST_MODULE ++ tristate "PRAMFS Test" ++ depends on PRAMFS && PRAMFS_WRITE_PROTECT && m ++ select PRAMFS_TEST ++ help ++ Say Y here to build a simple module to test the protection of ++ PRAMFS. The module will be called pramfs_test. +--- /dev/null ++++ b/fs/pramfs/Makefile +@@ -0,0 +1,14 @@ ++# ++# Makefile for the linux pram-filesystem routines. ++# ++ ++obj-$(CONFIG_PRAMFS) += pramfs.o ++obj-$(CONFIG_PRAMFS_TEST_MODULE) += pramfs_test.o ++ ++pramfs-y := balloc.o dir.o file.o inode.o namei.o super.o symlink.o ioctl.o ++ ++pramfs-$(CONFIG_PRAMFS_WRITE_PROTECT) += wprotect.o ++pramfs-$(CONFIG_PRAMFS_XIP) += xip.o ++pramfs-$(CONFIG_PRAMFS_XATTR) += xattr.o xattr_user.o xattr_trusted.o desctree.o ++pramfs-$(CONFIG_PRAMFS_POSIX_ACL) += acl.o ++pramfs-$(CONFIG_PRAMFS_SECURITY) += xattr_security.o @@ -566,3 +566,21 @@ patches.runtime_pm/0207-PM-Runtime-don-t-forget-to-wake-up-waitqueue-on-fail.pat patches.runtime_pm/0208-PM-Hibernate-Disable-usermode-helpers-right-before-f.patch patches.runtime_pm/0209-PM-Sleep-Move-disabling-of-usermode-helpers-to-the-f.patch patches.runtime_pm/0210-PM-QoS-add-pm_qos_update_request_timeout-API.patch + +patches.pramfs/01-pramfs-documentation.patch +patches.pramfs/02-pramfs-super-operations.patch +patches.pramfs/03-pramfs-inode-operations.patch +patches.pramfs/04-pramfs-file-operations.patch +patches.pramfs/05-pramfs-block-allocation.patch +patches.pramfs/06-pramfs-inode-operations-for-dirs.patch +patches.pramfs/07-pramfs-symlinks.patch +patches.pramfs/08-pramfs-headers.patch +patches.pramfs/09-pramfs-dir-operations.patch +patches.pramfs/10-pramfs-xip-support.patch +patches.pramfs/11-pramfs-acl-support.patch +patches.pramfs/12-pramfs-extended-attributes-support.patch +patches.pramfs/13-pramfs-extended-attributes-descriptors-tree.patch +patches.pramfs/14-pramfs-memory-write-protection.patch +patches.pramfs/15-pramfs-test-module.patch +patches.pramfs/16-pramfs-ioctl-operations.patch +patches.pramfs/17-pramfs-makefile-and-kconfig.patch |