Embedded Linux development
 help / color / mirror / Atom feed
* Re: [PATCH 09/16 v4] pramfs: dir operations
From: Marco Stornelli @ 2010-11-24  8:30 UTC (permalink / raw)
  To: Paul Mundt
  Cc: Linux Kernel, Linux Embedded, Linux FS Devel, Andrew Morton,
	Tim Bird, Greg Kroah-Hartman
In-Reply-To: <20101124081755.GD2212@linux-sh.org>

2010/11/24 Paul Mundt <lethal@linux-sh.org>:
> On Sat, Nov 20, 2010 at 11:00:45AM +0100, Marco Stornelli wrote:
>> +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 = dentry->d_name.len > PRAM_NAME_LEN ?
>> +             PRAM_NAME_LEN : dentry->d_name.len;
>> +
>
>        namelen = min_t(unsigned int, dentry->d_name.len, PRAM_NAME_LEN);
>
> ?

Ok, I can use it, I have to check the type.

>
>> +#define S_SHIFT 12
>> +static unsigned int dtype_by_mode[S_IFMT >> S_SHIFT] = {
>> +     [S_IFREG >> S_SHIFT]    DT_REG,
>> +     [S_IFDIR >> S_SHIFT]    DT_DIR,
>> +     [S_IFCHR >> S_SHIFT]    DT_CHR,
>> +     [S_IFBLK >> S_SHIFT]    DT_BLK,
>> +     [S_IFIFO >> S_SHIFT]    DT_FIFO,
>> +     [S_IFSOCK >> S_SHIFT]   DT_SOCK,
>> +     [S_IFLNK >> S_SHIFT]    DT_LNK,
>> +};
>> +
>> +static int pram_readdir(struct file *filp, void *dirent, filldir_t filldir)
>> +{
> ...
>> +             ret = filldir(dirent, name, namelen,
>> +                           filp->f_pos, ino,
>> +                           dtype_by_mode[(be16_to_cpu(pi->i_mode) & S_IFMT)>>S_SHIFT]);
>> +             filp->f_pos = pi->i_d.d_next ? be64_to_cpu(pi->i_d.d_next) : 3;
>
> You might try to provide some generic helpers for this, at least GFS2
> also does the same thing:
>
> include/linux/gfs2_ondisk.h:#define DT2IF(dt) (((dt) << 12) & S_IFMT)
> include/linux/gfs2_ondisk.h:#define IF2DT(sif) (((sif) & S_IFMT) >> 12)
>

I'll look at it.

Marco
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 08/16 v4] pramfs: headers
From: Marco Stornelli @ 2010-11-24  8:23 UTC (permalink / raw)
  To: Paul Mundt
  Cc: Linux Kernel, Linux Embedded, Linux FS Devel, Andrew Morton,
	Greg Kroah-Hartman, Tim Bird
In-Reply-To: <20101124080658.GC2212@linux-sh.org>

2010/11/24 Paul Mundt <lethal@linux-sh.org>:
> On Sat, Nov 20, 2010 at 11:00:15AM +0100, Marco Stornelli wrote:
>> +/*
>> + * Debug code
>> + */
>> +#define pram_dbg(s, args...) pr_debug("PRAMFS: "s, ## args)
>> +#define pram_err(s, args...) pr_err("PRAMFS: "s, ## args)
>> +#define pram_warn(s, args...)        pr_warning("PRAMFS: "s, ## args)
>> +#define pram_info(s, args...)        pr_info("PRAMFS: "s, ## args)
>> +
> Please kill off all of this and just use KBUILD_MODNAME centrally.

I didn't know this way to do, I'll look at it.

>
>> +#ifdef CONFIG_PRAMFS_WRITE_PROTECT
>> +extern void pram_writeable(void *vaddr, unsigned long size, int rw);
>> +
>> +#define wrprotect(addr, size) pram_writeable(addr, size, 0)
>> +
>> +#else
>> +
>> +#define wrprotect(addr, size) do {} while (0)
>> +
>> +#endif /* CONFIG PRAMFS_WRITE_PROTECT */
>> +
> Perhaps this should be pram_wrprotect()? Does this really benefit from
> being a config option instead of a mount option? Will this handle
> multiple mounts with some write protected and others not?

See my previous response.

>
>> +#ifdef CONFIG_PRAMFS_WRITE_PROTECT
>> +static inline void pram_memunlock_range(void *p, unsigned long len)
>> +{
>> +#ifndef CONFIG_X86
>> +     local_irq_disable();
>> +#endif
>> +     preempt_disable();
>> +     pram_writeable(p, len, 1);
>> +}
>> +
> This needs some explaining, or killing. While the latter is preferable,
> we can also work with the former.
>

Maybe I didn't understand, you mean preemt_disable() without disabling
the interrupt?
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 09/16 v4] pramfs: dir operations
From: Paul Mundt @ 2010-11-24  8:17 UTC (permalink / raw)
  To: Marco Stornelli
  Cc: Linux Kernel, Linux Embedded, Linux FS Devel, Andrew Morton,
	Tim Bird, Greg Kroah-Hartman
In-Reply-To: <4CE79C4D.6000104@gmail.com>

On Sat, Nov 20, 2010 at 11:00:45AM +0100, Marco Stornelli wrote:
> +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 = dentry->d_name.len > PRAM_NAME_LEN ?
> +		PRAM_NAME_LEN : dentry->d_name.len;
> +

	namelen = min_t(unsigned int, dentry->d_name.len, PRAM_NAME_LEN);

?

> +#define S_SHIFT 12
> +static unsigned int dtype_by_mode[S_IFMT >> S_SHIFT] = {
> +	[S_IFREG >> S_SHIFT]    DT_REG,
> +	[S_IFDIR >> S_SHIFT]    DT_DIR,
> +	[S_IFCHR >> S_SHIFT]    DT_CHR,
> +	[S_IFBLK >> S_SHIFT]    DT_BLK,
> +	[S_IFIFO >> S_SHIFT]    DT_FIFO,
> +	[S_IFSOCK >> S_SHIFT]   DT_SOCK,
> +	[S_IFLNK >> S_SHIFT]    DT_LNK,
> +};
> +
> +static int pram_readdir(struct file *filp, void *dirent, filldir_t filldir)
> +{
...
> +		ret = filldir(dirent, name, namelen,
> +			      filp->f_pos, ino,
> +			      dtype_by_mode[(be16_to_cpu(pi->i_mode) & S_IFMT)>>S_SHIFT]);
> +		filp->f_pos = pi->i_d.d_next ? be64_to_cpu(pi->i_d.d_next) : 3;

You might try to provide some generic helpers for this, at least GFS2
also does the same thing:

include/linux/gfs2_ondisk.h:#define DT2IF(dt) (((dt) << 12) & S_IFMT)
include/linux/gfs2_ondisk.h:#define IF2DT(sif) (((sif) & S_IFMT) >> 12)

^ permalink raw reply

* Re: [PATCH 04/16 v4] pramfs: file operations
From: Marco Stornelli @ 2010-11-24  8:11 UTC (permalink / raw)
  To: Paul Mundt
  Cc: Linux Kernel, Linux Embedded, Linux FS Devel, Andrew Morton,
	Tim Bird, Greg Kroah-Hartman
In-Reply-To: <20101124075818.GB2212@linux-sh.org>

2010/11/24 Paul Mundt <lethal@linux-sh.org>:
> On Sat, Nov 20, 2010 at 10:58:40AM +0100, Marco Stornelli wrote:
>> diff -Nurp linux-2.6.36-orig/fs/pramfs/file.c linux-2.6.36/fs/pramfs/file.c
>> --- linux-2.6.36-orig/fs/pramfs/file.c        1970-01-01 01:00:00.000000000 +0100
>> +++ linux-2.6.36/fs/pramfs/file.c     2010-09-24 18:34:03.000000000 +0200
>> @@ -0,0 +1,166 @@
>> +/*
>> + * FILE NAME fs/pramfs/file.c
>> + *
>> + * BRIEF DESCRIPTION
>> + *
>> + * File operations for files.
>> + *
> This FILE NAME / BRIEF DESCRIPTION thing should probably die, it's also
> not used consistently throughout the series.

Maybe yes, it's more a problem then a advantage :)

>
>> +static int pram_open_file(struct inode *inode, struct file *filp)
>> +{
>> +#ifndef CONFIG_PRAMFS_XIP
>> +     /* Without XIP we force to use Direct IO */
>> +     filp->f_flags |= O_DIRECT;
>> +#endif
>> +     return generic_file_open(inode, filp);
>> +}
>> +
> Relying on a config option for this is in violently poor taste. Rely on
> the config option to enable/disable XIP support as you like, but whether
> it's actually mounted XIP or not should really depend be determined by a
> mount option. Presently you have no way of dealing with the file system
> being mounted multiple times with mixed XIP and non-XIP, which doesn't
> seem like a particularly exotic configuration. As you seem to have copied

Maybe on embedded, it could be.

> most of this from ext2, I'm curious why you opted to hardcode this
> instead of maintaining the flexibility that ext2 XIP has over this.
>

First of all because it was simpler :) In addition there was some
design problem to use it in combination with the memory protection.
The difference with ext2 is that we aren't talking about a general
purpose fs used (mainly) on normal desktop/server systems, but a
specific fs for embedded world, so I think some little constraints are
ok.

Marco

^ permalink raw reply

* Re: [PATCH 08/16 v4] pramfs: headers
From: Paul Mundt @ 2010-11-24  8:06 UTC (permalink / raw)
  To: Marco Stornelli
  Cc: Linux Kernel, Linux Embedded, Linux FS Devel, Andrew Morton,
	Greg Kroah-Hartman, Tim Bird
In-Reply-To: <4CE79C2F.5090001@gmail.com>

On Sat, Nov 20, 2010 at 11:00:15AM +0100, Marco Stornelli wrote:
> +/*
> + * Debug code
> + */
> +#define pram_dbg(s, args...)	pr_debug("PRAMFS: "s, ## args)
> +#define pram_err(s, args...)	pr_err("PRAMFS: "s, ## args)
> +#define pram_warn(s, args...)	pr_warning("PRAMFS: "s, ## args)
> +#define pram_info(s, args...)	pr_info("PRAMFS: "s, ## args)
> +
Please kill off all of this and just use KBUILD_MODNAME centrally.

> +#ifdef CONFIG_PRAMFS_WRITE_PROTECT
> +extern void pram_writeable(void *vaddr, unsigned long size, int rw);
> +
> +#define wrprotect(addr, size) pram_writeable(addr, size, 0)
> +
> +#else
> +
> +#define wrprotect(addr, size) do {} while (0)
> +
> +#endif /* CONFIG PRAMFS_WRITE_PROTECT */
> +
Perhaps this should be pram_wrprotect()? Does this really benefit from
being a config option instead of a mount option? Will this handle
multiple mounts with some write protected and others not?

> +#ifdef CONFIG_PRAMFS_WRITE_PROTECT
> +static inline void pram_memunlock_range(void *p, unsigned long len)
> +{
> +#ifndef CONFIG_X86
> +	local_irq_disable();
> +#endif
> +	preempt_disable();
> +	pram_writeable(p, len, 1);
> +}
> +
This needs some explaining, or killing. While the latter is preferable,
we can also work with the former.

^ permalink raw reply

* Re: [PATCH 04/16 v4] pramfs: file operations
From: Paul Mundt @ 2010-11-24  7:58 UTC (permalink / raw)
  To: Marco Stornelli
  Cc: Linux Kernel, Linux Embedded, Linux FS Devel, Andrew Morton,
	Tim Bird, Greg Kroah-Hartman
In-Reply-To: <4CE79BD0.3030200@gmail.com>

On Sat, Nov 20, 2010 at 10:58:40AM +0100, Marco Stornelli wrote:
> diff -Nurp linux-2.6.36-orig/fs/pramfs/file.c linux-2.6.36/fs/pramfs/file.c
> --- linux-2.6.36-orig/fs/pramfs/file.c	1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.36/fs/pramfs/file.c	2010-09-24 18:34:03.000000000 +0200
> @@ -0,0 +1,166 @@
> +/*
> + * FILE NAME fs/pramfs/file.c
> + *
> + * BRIEF DESCRIPTION
> + *
> + * File operations for files.
> + *
This FILE NAME / BRIEF DESCRIPTION thing should probably die, it's also
not used consistently throughout the series.

> +static int pram_open_file(struct inode *inode, struct file *filp)
> +{
> +#ifndef CONFIG_PRAMFS_XIP
> +	/* Without XIP we force to use Direct IO */
> +	filp->f_flags |= O_DIRECT;
> +#endif
> +	return generic_file_open(inode, filp);
> +}
> +
Relying on a config option for this is in violently poor taste. Rely on
the config option to enable/disable XIP support as you like, but whether
it's actually mounted XIP or not should really depend be determined by a
mount option. Presently you have no way of dealing with the file system
being mounted multiple times with mixed XIP and non-XIP, which doesn't
seem like a particularly exotic configuration. As you seem to have copied
most of this from ext2, I'm curious why you opted to hardcode this
instead of maintaining the flexibility that ext2 XIP has over this.

^ permalink raw reply

* Re: [RFC] persistent store
From: Tony Luck @ 2010-11-22 18:33 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Huang Ying, linux-kernel@vger.kernel.org,
	linux-arch@vger.kernel.org, tglx@linutronix.de, mingo@elte.hu,
	greg@kroah.com, akpm@linux-foundation.org, Linux Embedded
In-Reply-To: <AANLkTimqn+qS5RxETz1DznsMDS4gZBGxkLkdCgTQ7qG-@mail.gmail.com>

On Mon, Nov 22, 2010 at 10:24 AM, Geert Uytterhoeven
<geert@linux-m68k.org> wrote:
> Any tangents with "pramfs: persistent and protected RAM filesystem"
> on linux-embedded?

That looks nice for building on top on a sane h/w design (a
block of non-volatile RAM).  My pstore code works with
uglier things. The ACPI/ERST interface provides ways to
read/write/clear "records" from what might be some NVRAM,
but might also be a channel to a service processor ... and
ERST is what I'd like to see made usable (without subjecting
users to the implementation details).

-Tony

^ permalink raw reply

* Re: [RFC] persistent store
From: Geert Uytterhoeven @ 2010-11-22 18:24 UTC (permalink / raw)
  To: Tony Luck
  Cc: Huang Ying, linux-kernel@vger.kernel.org,
	linux-arch@vger.kernel.org, tglx@linutronix.de, mingo@elte.hu,
	greg@kroah.com, akpm@linux-foundation.org, Linux Embedded
In-Reply-To: <AANLkTi=fNH4-VjLh1zzk-UdEQevR16_K8igdLeodw6yL@mail.gmail.com>

On Mon, Nov 22, 2010 at 17:55, Tony Luck <tony.luck@intel.com> wrote:
> On Sun, Nov 21, 2010 at 5:59 PM, Huang Ying <ying.huang@intel.com> wrote:
>> This patch provides a general "read" interface for kmsg_dumper and some
>> other persistent storage users. Another possible choice is to just
>> extend the original interface to add persistent store support. For
>> example, we can add a "read" function in kmsg_dumper, and output the
>> content of persistent store via extend /dev/kmsg via prefix every line
>> comes from persistent store or adding some "ioctl" to do that. (But it
>> seems that nobody likes "ioctl").
>
> In Linux (and Unix before it) "everything is a file" ... but this
> doesn't work very well if the file has internal structure (e.g.
> is made of records that can be individually changed or
> deleted).  A filesystem seems a much better model.

Any tangents with "pramfs: persistent and protected RAM filesystem"
on linux-embedded?

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* [PATCH 16/16 v4] pramfs: makefile and Kconfig
From: Marco Stornelli @ 2010-11-20 10:03 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Tim Bird, Andrew Morton,
	Greg Kroah-Hartman

From: Marco Stornelli <marco.stornelli@gmail.com>

Makefile and Kconfig.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/Makefile linux-2.6.36/fs/Makefile
--- linux-2.6.36-orig/fs/Makefile	2010-09-13 01:07:37.000000000 +0200
+++ linux-2.6.36/fs/Makefile	2010-09-14 18:49:52.000000000 +0200
@@ -126,3 +126,4 @@ obj-$(CONFIG_BTRFS_FS)		+= btrfs/
 obj-$(CONFIG_GFS2_FS)           += gfs2/
 obj-$(CONFIG_EXOFS_FS)          += exofs/
 obj-$(CONFIG_CEPH_FS)		+= ceph/
+obj-$(CONFIG_PRAMFS)		+= pramfs/
diff -Nurp linux-2.6.36-orig/fs/Kconfig linux-2.6.36/fs/Kconfig
--- linux-2.6.36-orig/fs/Kconfig	2010-09-13 01:07:37.000000000 +0200
+++ linux-2.6.36/fs/Kconfig	2010-09-14 18:49:52.000000000 +0200
@@ -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,13 +25,14 @@ 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"
  config FS_POSIX_ACL
-# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs/nfs4)
+# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs/nfs4/pramfs)
 #
 # NOTE: you can implement Posix ACLs without these helpers (XFS does).
 # 	Never use this symbol for ifdefs.
@@ -189,6 +190,7 @@ source "fs/romfs/Kconfig"
 source "fs/sysv/Kconfig"
 source "fs/ufs/Kconfig"
 source "fs/exofs/Kconfig"
+source "fs/pramfs/Kconfig"
  endif # MISC_FILESYSTEMS
 diff -Nurp linux-2.6.36-orig/fs/pramfs/Kconfig linux-2.6.36/fs/pramfs/Kconfig
--- linux-2.6.36-orig/fs/pramfs/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/Kconfig	2010-10-30 10:30:19.000000000 +0200
@@ -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 "Enable Execute-in-place in PRAMFS"
+	depends on PRAMFS && !PRAMFS_WRITE_PROTECT
+	help
+	   Say Y here to enable XIP feature of PRAMFS.
+
+config PRAMFS_WRITE_PROTECT
+	bool "Enable 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
+	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 && 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.
diff -Nurp linux-2.6.36-orig/fs/pramfs/Makefile linux-2.6.36/fs/pramfs/Makefile
--- linux-2.6.36-orig/fs/pramfs/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/Makefile	2010-09-14 18:49:52.000000000 +0200
@@ -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
+
+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
 

^ permalink raw reply

* [PATCH 15/16 v4] pramfs: test module
From: Marco Stornelli @ 2010-11-20 10:03 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Tim Bird, Andrew Morton,
	Greg Kroah-Hartman

From: Marco Stornelli <marco.stornelli@gmail.com>

Test module.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/pramfs_test.c linux-2.6.36/fs/pramfs/pramfs_test.c
--- linux-2.6.36-orig/fs/pramfs/pramfs_test.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/pramfs_test.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,49 @@
+/*
+ * FILE NAME fs/pramfs/pramfs_test.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Pramfs test module.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/module.h>
+#include <linux/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);
 

^ permalink raw reply

* [PATCH 14/16 v4] pramfs: write protection
From: Marco Stornelli @ 2010-11-20 10:02 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Andrew Morton, Greg Kroah-Hartman,
	Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

Memory write protection.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/wprotect.c linux-2.6.36/fs/pramfs/wprotect.c
--- linux-2.6.36-orig/fs/pramfs/wprotect.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/wprotect.c	2010-10-30 11:59:54.000000000 +0200
@@ -0,0 +1,41 @@
+/*
+ * FILE NAME fs/pramfs/wprotect.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Write protection for the filesystem pages.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include "pram.h"
+
+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);
+}

^ permalink raw reply

* [PATCH 13/16 v4] pramfs: xattr block descriptors tree
From: Marco Stornelli @ 2010-11-20 10:02 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Tim Bird, Andrew Morton,
	Greg Kroah-Hartman

From: Marco Stornelli <marco.stornelli@gmail.com>

Extended attributes block descriptors tree.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/desctree.c linux-2.6.36/fs/pramfs/desctree.c
--- linux-2.6.36-orig/fs/pramfs/desctree.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/desctree.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,183 @@
+/*
+ * FILE NAME fs/pramfs/desctree.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Extended attributes block descriptors tree.
+ *
+ * Copyright 2010 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);
+}
diff -Nurp linux-2.6.36-orig/fs/pramfs/desctree.h linux-2.6.36/fs/pramfs/desctree.h
--- linux-2.6.36-orig/fs/pramfs/desctree.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/desctree.h	2010-09-18 08:41:02.000000000 +0200
@@ -0,0 +1,46 @@
+/*
+ * FILE NAME fs/pramfs/desctree.h
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Extended attributes block descriptors tree.
+ *
+ * Copyright 2010 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 <asm/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);
+};
+

^ permalink raw reply

* [PATCH 12/16 v4] pramfs: extended attributes
From: Marco Stornelli @ 2010-11-20 10:01 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Tim Bird, Andrew Morton,
	Greg Kroah-Hartman

From: Marco Stornelli <marco.stornelli@gmail.com>

Extended attributes operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/xattr.c linux-2.6.36/fs/pramfs/xattr.c
--- linux-2.6.36-orig/fs/pramfs/xattr.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xattr.c	2010-09-14 19:45:40.000000000 +0200
@@ -0,0 +1,1108 @@
+/*
+ * FILE NAME fs/pramfs/xattr.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Extended attributes operations.
+ *
+ * Copyright 2010 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 alligned 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 = (struct pram_sb_info *)sb->s_fs_info;
+	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 = (struct pram_sb_info *)sb->s_fs_info;
+
+	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;
+	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("inode %ld: bad block %llu", inode->i_ino,
+		be64_to_cpu(pi->i_xattr));
+		error = -EIO;
+		goto cleanup;
+	}
+	/* find named attribute */
+	name_len = strlen(name);
+
+	error = -ERANGE;
+	if (name_len > 255)
+		goto cleanup;
+	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;
+	}
+	/* Check the remaining name entries */
+	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");
+	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 = (struct pram_sb_info *)sb->s_fs_info;
+
+	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("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. Buffer
+ * 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 = (struct pram_sb_info *)sb->s_fs_info;
+	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("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 = (struct pram_sb_info *)sb->s_fs_info;
+	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(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(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 = (struct pram_sb_info *)sb->s_fs_info;
+	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("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("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 = LOOKUP_DESC(sbi, blocknr);
+	if (!desc) {
+		pram_err("descriptor not found for blocknr %lu", blocknr);
+		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 = (struct pram_sb_info *)sb->s_fs_info;
+	/*
+	 * 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 = (struct pram_sb_info *)sb->s_fs_info;
+	__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 = (struct pram_sb_info *)sb->s_fs_info;
+
+	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("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);
+}
diff -Nurp linux-2.6.36-orig/fs/pramfs/xattr.h linux-2.6.36/fs/pramfs/xattr.h
--- linux-2.6.36-orig/fs/pramfs/xattr.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xattr.h	2010-09-18 08:40:35.000000000 +0200
@@ -0,0 +1,133 @@
+/*
+ * FILE NAME fs/pramfs/xattr.h
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Extended attributes for the pram filesystem.
+ *
+ * Copyright 2010 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);
+#else
+static inline int pram_init_security(struct inode *inode, struct inode *dir)
+{
+	return 0;
+}
+#endif
diff -Nurp linux-2.6.36-orig/fs/pramfs/xattr_security.c linux-2.6.36/fs/pramfs/xattr_security.c
--- linux-2.6.36-orig/fs/pramfs/xattr_security.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xattr_security.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,80 @@
+/*
+ * FILE NAME fs/pramfs/xattr_security.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Handler for storing security labels as extended attributes.
+ *
+ * Copyright 2010 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/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/pram_fs.h>
+#include <linux/security.h>
+#include "xattr.h"
+
+static size_t pram_xattr_security_list(struct dentry *dentry, char *list,
+				       size_t list_size, const char *name,
+				       size_t name_len, int type)
+{
+	const int prefix_len = XATTR_SECURITY_PREFIX_LEN;
+	const size_t total_len = prefix_len + name_len + 1;
+
+	if (list && total_len <= list_size) {
+		memcpy(list, XATTR_SECURITY_PREFIX, prefix_len);
+		memcpy(list+prefix_len, name, name_len);
+		list[prefix_len + name_len] = '\0';
+	}
+	return total_len;
+}
+
+static int pram_xattr_security_get(struct dentry *dentry, const char *name,
+		       void *buffer, size_t size, int type)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+	return pram_xattr_get(dentry->d_inode, PRAM_XATTR_INDEX_SECURITY, name,
+			      buffer, size);
+}
+
+static int pram_xattr_security_set(struct dentry *dentry, const char *name,
+		const void *value, size_t size, int flags, int type)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+	return pram_xattr_set(dentry->d_inode, PRAM_XATTR_INDEX_SECURITY, name,
+			      value, size, flags);
+}
+
+int pram_init_security(struct inode *inode, struct inode *dir)
+{
+	int err;
+	size_t len;
+	void *value;
+	char *name;
+
+	err = security_inode_init_security(inode, dir, &name, &value, &len);
+	if (err) {
+		if (err == -EOPNOTSUPP)
+			return 0;
+		return err;
+	}
+	err = pram_xattr_set(inode, PRAM_XATTR_INDEX_SECURITY,
+			     name, value, len, 0);
+	kfree(name);
+	kfree(value);
+	return err;
+}
+
+const struct xattr_handler pram_xattr_security_handler = {
+	.prefix	= XATTR_SECURITY_PREFIX,
+	.list	= pram_xattr_security_list,
+	.get	= pram_xattr_security_get,
+	.set	= pram_xattr_security_set,
+};
diff -Nurp linux-2.6.36-orig/fs/pramfs/xattr_trusted.c linux-2.6.36/fs/pramfs/xattr_trusted.c
--- linux-2.6.36-orig/fs/pramfs/xattr_trusted.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xattr_trusted.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,67 @@
+/*
+ * FILE NAME fs/pramfs/xattr_trusted.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Handler for trusted extended attributes.
+ *
+ * Copyright 2010 Marco Stornelli <marco.stornelli@gmail.com>
+ *
+ * based on fs/ext2/xattr_trusted.c with the following copyright:
+ *
+ * Copyright (C) 2003 by 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/module.h>
+#include <linux/string.h>
+#include <linux/capability.h>
+#include <linux/fs.h>
+#include <linux/pram_fs.h>
+#include "xattr.h"
+
+static size_t pram_xattr_trusted_list(struct dentry *dentry, char *list,
+				size_t list_size, const char *name,
+				size_t name_len, int type)
+{
+	const int prefix_len = XATTR_TRUSTED_PREFIX_LEN;
+	const size_t total_len = prefix_len + name_len + 1;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return 0;
+
+	if (list && total_len <= list_size) {
+		memcpy(list, XATTR_TRUSTED_PREFIX, prefix_len);
+		memcpy(list+prefix_len, name, name_len);
+		list[prefix_len + name_len] = '\0';
+	}
+	return total_len;
+}
+
+static int pram_xattr_trusted_get(struct dentry *dentry, const char *name,
+				void *buffer, size_t size, int type)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+	return pram_xattr_get(dentry->d_inode, PRAM_XATTR_INDEX_TRUSTED, name,
+			      buffer, size);
+}
+
+static int pram_xattr_trusted_set(struct dentry *dentry, const char *name,
+		const void *value, size_t size, int flags, int type)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+	return pram_xattr_set(dentry->d_inode, PRAM_XATTR_INDEX_TRUSTED, name,
+			      value, size, flags);
+}
+
+const struct xattr_handler pram_xattr_trusted_handler = {
+	.prefix	= XATTR_TRUSTED_PREFIX,
+	.list	= pram_xattr_trusted_list,
+	.get	= pram_xattr_trusted_get,
+	.set	= pram_xattr_trusted_set,
+};
diff -Nurp linux-2.6.36-orig/fs/pramfs/xattr_user.c linux-2.6.36/fs/pramfs/xattr_user.c
--- linux-2.6.36-orig/fs/pramfs/xattr_user.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xattr_user.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,63 @@
+/*
+ * FILE NAME fs/pramfs/xattr_user.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Handler for extended user attributes.
+ *
+ * Copyright 2010 Marco Stornelli <marco.stornelli@gmail.com>
+ *
+ * based on fs/ext2/xattr_user.c with the following copyright:
+ *
+ * Copyright (C) 2001 by 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/module.h>
+#include <linux/string.h>
+#include "pram.h"
+#include "xattr.h"
+
+static size_t pram_xattr_user_list(struct dentry *dentry, char *list, size_t list_size,
+		const char *name, size_t name_len, int type)
+{
+	const size_t prefix_len = XATTR_USER_PREFIX_LEN;
+	const size_t total_len = prefix_len + name_len + 1;
+
+	if (list && total_len <= list_size) {
+		memcpy(list, XATTR_USER_PREFIX, prefix_len);
+		memcpy(list+prefix_len, name, name_len);
+		list[prefix_len + name_len] = '\0';
+	}
+	return total_len;
+}
+
+static int pram_xattr_user_get(struct dentry *dentry, const char *name,
+		void *buffer, size_t size, int type)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+	return pram_xattr_get(dentry->d_inode, PRAM_XATTR_INDEX_USER,
+			      name, buffer, size);
+}
+
+static int pram_xattr_user_set(struct dentry *dentry, const char *name,
+		const void *value, size_t size, int flags, int type)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+
+	return pram_xattr_set(dentry->d_inode, PRAM_XATTR_INDEX_USER,
+			      name, value, size, flags);
+}
+
+const struct xattr_handler pram_xattr_user_handler = {
+	.prefix	= XATTR_USER_PREFIX,
+	.list	= pram_xattr_user_list,
+	.get	= pram_xattr_user_get,
+	.set	= pram_xattr_user_set,
+};
 

^ permalink raw reply

* [PATCH 11/16 v4] pramfs: ACL management
From: Marco Stornelli @ 2010-11-20 10:01 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Andrew Morton, Greg Kroah-Hartman,
	Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

ACL operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/acl.c linux-2.6.36/fs/pramfs/acl.c
--- linux-2.6.36-orig/fs/pramfs/acl.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/acl.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,418 @@
+/*
+ * FILE NAME fs/pramfs/acl.c
+ *
+ * BRIEF MODULE DESCRIPTION
+ *
+ * POSIX ACL operations
+ *
+ * Copyright 2010 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;
+
+	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;
+
+	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)
+{
+	struct posix_acl *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)) {
+		acl = pram_get_acl(dir, ACL_TYPE_DEFAULT);
+		if (IS_ERR(acl))
+			return PTR_ERR(acl);
+		if (!acl)
+			inode->i_mode &= ~current_umask();
+	}
+
+	if (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 (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 (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 (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;
+
+	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 (!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,
+};
diff -Nurp linux-2.6.36-orig/fs/pramfs/acl.h linux-2.6.36/fs/pramfs/acl.h
--- linux-2.6.36-orig/fs/pramfs/acl.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/acl.h	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,88 @@
+/*
+ * FILE NAME fs/pramfs/acl.h
+ *
+ * BRIEF MODULE DESCRIPTION
+ *
+ * POSIX ACL operations
+ *
+ * Copyright 2010 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);
+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
 

^ permalink raw reply

* [PATCH 10/16 v4] pramfs: xip operations
From: Marco Stornelli @ 2010-11-20 10:01 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Andrew Morton, Tim Bird,
	Greg Kroah-Hartman

From: Marco Stornelli <marco.stornelli@gmail.com>

XIP operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/xip.c linux-2.6.36/fs/pramfs/xip.c
--- linux-2.6.36-orig/fs/pramfs/xip.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xip.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,90 @@
+/*
+ * FILE NAME fs/pramfs/xip.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * XIP operations.
+ *
+ * Copyright 2009-2010 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/genhd.h>
+#include <linux/buffer_head.h>
+#include "pram.h"
+#include "xip.h"
+
+static int pram_find_and_alloc_blocks(struct inode *inode, sector_t iblock,
+				     sector_t *data_block, int create)
+{
+	int err = -EIO;
+	u64 block;
+
+	mutex_lock(&PRAM_I(inode)->truncate_mutex);
+
+	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:
+	mutex_unlock(&PRAM_I(inode)->truncate_mutex);
+	return err;
+}
+
+
+static int __pram_get_block(struct inode *inode, pgoff_t pgoff, int create,
+		   sector_t *result)
+{
+	int rc = 0;
+	sector_t iblock;
+
+	/* find starting block number to access */
+	iblock = (sector_t)pgoff << (PAGE_CACHE_SHIFT - inode->i_blkbits);
+
+	rc = pram_find_and_alloc_blocks(inode, iblock, 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;
+
+	/* 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 = page_to_pfn(virt_to_page((unsigned long)*kmem));
+
+exit:
+	return rc;
+}
diff -Nurp linux-2.6.36-orig/fs/pramfs/xip.h linux-2.6.36/fs/pramfs/xip.h
--- linux-2.6.36-orig/fs/pramfs/xip.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xip.h	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,24 @@
+/*
+ * FILE NAME fs/pramfs/xip.h
+ *
+ * BRIEF DESCRIPTION
+ *
+ * XIP operations.
+ *
+ * Copyright 2009-2010 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 *);
+
+#else
+
+#define pram_get_xip_mem NULL
+
+#endif
+
 

^ permalink raw reply

* [PATCH 09/16 v4] pramfs: dir operations
From: Marco Stornelli @ 2010-11-20 10:00 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Andrew Morton, Tim Bird,
	Greg Kroah-Hartman

From: Marco Stornelli <marco.stornelli@gmail.com>

File operations for directories.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/dir.c linux-2.6.36/fs/pramfs/dir.c
--- linux-2.6.36-orig/fs/pramfs/dir.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/dir.c	2010-09-17 19:08:54.000000000 +0200
@@ -0,0 +1,215 @@
+/*
+ * FILE NAME fs/pramfs/dir.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * File operations for directories.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/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 = dentry->d_name.len > PRAM_NAME_LEN ?
+		PRAM_NAME_LEN : dentry->d_name.len;
+
+	pidir = pram_get_inode(dir->i_sb, dir->i_ino);
+	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(pitail);
+		pitail->i_d.d_next = cpu_to_be64(inode->i_ino);
+		pram_memlock_inode(pitail);
+
+		prev_ino = tail_ino;
+
+		pram_memunlock_inode(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(pidir);
+	} else {
+		/* the directory is empty */
+		prev_ino = 0;
+
+		pram_memunlock_inode(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(pidir);
+	}
+
+
+	pram_memunlock_inode(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(pi);
+	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;
+
+	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;
+
+	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(next);
+			next->i_d.d_prev = 0;
+			pram_memlock_inode(next);
+
+			pram_memunlock_inode(pidir);
+			pidir->i_type.dir.head = pi->i_d.d_next;
+		} else {
+			pram_memunlock_inode(pidir);
+			pidir->i_type.dir.head = 0;
+			pidir->i_type.dir.tail = 0;
+		}
+		pram_memlock_inode(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(prev);
+		prev->i_d.d_next = 0;
+		pram_memlock_inode(prev);
+
+		pram_memunlock_inode(pidir);
+		pidir->i_type.dir.tail = pi->i_d.d_prev;
+		pram_memlock_inode(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(prev);
+			prev->i_d.d_next = pi->i_d.d_next;
+			pram_memlock_inode(prev);
+
+			pram_memunlock_inode(next);
+			next->i_d.d_prev = pi->i_d.d_prev;
+			pram_memlock_inode(next);
+		}
+	}
+
+	pram_memunlock_inode(pi);
+	pi->i_d.d_next = 0;
+	pi->i_d.d_prev = 0;
+	pi->i_d.d_parent = 0;
+	pram_memlock_inode(pi);
+
+	return 0;
+}
+
+#define S_SHIFT 12
+static unsigned int dtype_by_mode[S_IFMT >> S_SHIFT] = {
+	[S_IFREG >> S_SHIFT]    DT_REG,
+	[S_IFDIR >> S_SHIFT]    DT_DIR,
+	[S_IFCHR >> S_SHIFT]    DT_CHR,
+	[S_IFBLK >> S_SHIFT]    DT_BLK,
+	[S_IFIFO >> S_SHIFT]    DT_FIFO,
+	[S_IFSOCK >> S_SHIFT]   DT_SOCK,
+	[S_IFLNK >> S_SHIFT]    DT_LNK,
+};
+
+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,
+			      dtype_by_mode[(be16_to_cpu(pi->i_mode) & S_IFMT)>>S_SHIFT]);
+		filp->f_pos = pi->i_d.d_next ? be64_to_cpu(pi->i_d.d_next) : 3;
+	} else
+		filp->f_pos = 3;
+
+	return ret;
+}
+
+struct file_operations pram_dir_operations = {
+	.read		= generic_read_dir,
+	.readdir	= pram_readdir,
+	.fsync		= noop_fsync,
+};
 

^ permalink raw reply

* [PATCH 08/16 v4] pramfs: headers
From: Marco Stornelli @ 2010-11-20 10:00 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Andrew Morton, Greg Kroah-Hartman,
	Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

Definitions for the PRAMFS filesystem.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/pram.h linux-2.6.36/fs/pramfs/pram.h
--- linux-2.6.36-orig/fs/pramfs/pram.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/pram.h	2010-10-30 12:02:45.000000000 +0200
@@ -0,0 +1,317 @@
+/*
+ * FILE NAME pram.h
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Definitions for the PRAMFS filesystem.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#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/types.h>
+
+/*
+ * Debug code
+ */
+#define pram_dbg(s, args...)	pr_debug("PRAMFS: "s, ## args)
+#define pram_err(s, args...)	pr_err("PRAMFS: "s, ## args)
+#define pram_warn(s, args...)	pr_warning("PRAMFS: "s, ## args)
+#define pram_info(s, args...)	pr_info("PRAMFS: "s, ## args)
+
+/* Function Prototypes */
+
+#ifdef CONFIG_PRAMFS_XIP
+
+#define pram_read	xip_file_read
+#define pram_write	xip_file_write
+#define pram_mmap	xip_file_mmap
+#define pram_aio_read	NULL
+#define pram_aio_write	NULL
+#define pram_readpage	NULL
+#define pram_direct_IO	NULL
+
+#else
+
+#define pram_read	do_sync_read
+#define pram_write	do_sync_write
+#define pram_mmap	__pram_mmap
+#define pram_aio_read	generic_file_aio_read
+#define pram_aio_write	generic_file_aio_write
+#define pram_direct_IO	__pram_direct_IO
+#define pram_readpage	__pram_readpage
+
+extern int pram_get_and_update_block(struct inode *inode, sector_t iblock,
+				     struct buffer_head *bh, int create);
+
+static inline int __pram_readpage(struct file *file, struct page *page)
+{
+	return block_read_full_page(page, pram_get_and_update_block);
+}
+
+/* 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);
+
+#endif /* CONFIG_PRAMFS_XIP */
+
+#define pram_set_bit			ext2_set_bit
+#define pram_clear_bit			ext2_clear_bit
+#define pram_find_next_zero_bit		ext2_find_next_zero_bit
+
+/* 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, int num);
+extern u64 pram_find_data_block(struct inode *inode,
+					 int 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);
+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);
+extern int pram_notify_change(struct dentry *dentry, struct iattr *attr);
+
+
+/* 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);
+
+
+#ifdef CONFIG_PRAMFS_WRITE_PROTECT
+extern void pram_writeable(void *vaddr, unsigned long size, int rw);
+
+#define wrprotect(addr, size) pram_writeable(addr, size, 0)
+
+#else
+
+#define wrprotect(addr, size) do {} while (0)
+
+#endif /* CONFIG PRAMFS_WRITE_PROTECT */
+
+/* Inline functions start here */
+
+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;
+}
+
+/* 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 = (struct pram_sb_info *)sb->s_fs_info;
+	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 = (struct pram_sb_info *)sb->s_fs_info;
+	return (struct pram_super_block *)(sbi->virt_addr + PRAM_SB_SIZE);
+}
+
+/* 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
+static inline void pram_memunlock_range(void *p, unsigned long len)
+{
+#ifndef CONFIG_X86
+	local_irq_disable();
+#endif
+	preempt_disable();
+	pram_writeable(p, len, 1);
+}
+
+static inline void pram_memlock_range(void *p, unsigned long len)
+{
+	pram_writeable(p, len, 0);
+	preempt_enable();
+#ifndef CONFIG_X86
+	local_irq_enable();
+#endif
+}
+#else
+static inline void pram_memunlock_range(void *p, unsigned long len) {}
+static inline void pram_memlock_range(void *p, unsigned long len) {}
+#endif
+
+/* write protection for super block */
+#define pram_memunlock_super(ps) \
+	pram_memunlock_range((ps), PRAM_SB_SIZE)
+#define pram_memlock_super(ps) {\
+	pram_sync_super(ps);\
+	pram_memlock_range((ps), PRAM_SB_SIZE);\
+}
+
+/* write protection for inode metadata */
+#define pram_memunlock_inode(pi) \
+	pram_memunlock_range((pi), PRAM_INODE_SIZE)
+#define pram_memlock_inode(pi) {\
+	pram_sync_inode(pi);\
+	pram_memlock_range((pi), PRAM_INODE_SIZE);\
+}
+
+/* write protection for a data block */
+#define pram_memunlock_block(sb, bp) \
+	pram_memunlock_range((bp), (sb)->s_blocksize)
+#define pram_memlock_block(sb, bp) \
+	pram_memlock_range((bp), (sb)->s_blocksize)
+
+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;
+}
+
+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
+	/*
+	 * truncate_mutex is for serialising the truncate path against
+	 * get/update block.
+	 */
+	struct mutex truncate_mutex;
+	struct inode vfs_inode;
+};
+
+static inline struct pram_inode_vfs *PRAM_I(struct inode *inode)
+{
+	return container_of(inode, struct pram_inode_vfs, vfs_inode);
+}
+
+/*
+ * Inodes and files operations
+ */
+
+/* dir.c */
+extern struct file_operations pram_dir_operations;
+
+/* file.c */
+extern struct inode_operations pram_file_inode_operations;
+extern struct file_operations pram_file_operations;
+
+/* inode.c */
+extern struct address_space_operations pram_aops;
+
+/* namei.c */
+extern struct inode_operations pram_dir_inode_operations;
+
+/* symlink.c */
+extern struct inode_operations pram_symlink_inode_operations;
+
+extern struct backing_dev_info pram_backing_dev_info;
+
+#endif	/* __PRAM_H */
diff -Nurp linux-2.6.36-orig/include/linux/magic.h linux-2.6.36/include/linux/magic.h
--- linux-2.6.36-orig/include/linux/magic.h	2010-09-13 01:07:37.000000000 +0200
+++ linux-2.6.36/include/linux/magic.h	2010-09-14 18:49:52.000000000 +0200
@@ -39,6 +39,7 @@
 #define OPENPROM_SUPER_MAGIC	0x9fa1
 #define PROC_SUPER_MAGIC	0x9fa0
 #define QNX4_SUPER_MAGIC	0x002f		/* qnx4 fs detection */
+#define PRAM_SUPER_MAGIC	0xEFFA
  #define REISERFS_SUPER_MAGIC	0x52654973	/* used by gcc */
 					/* used by file system utilities that
diff -Nurp linux-2.6.36-orig/include/linux/pram_fs.h linux-2.6.36/include/linux/pram_fs.h
--- linux-2.6.36-orig/include/linux/pram_fs.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/include/linux/pram_fs.h	2010-09-25 14:10:14.000000000 +0200
@@ -0,0 +1,118 @@
+/*
+ * FILE NAME include/linux/pram_fs.h
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Definitions for the PRAMFS filesystem.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef _LINUX_PRAM_FS_H
+#define _LINUX_PRAM_FS_H
+
+#include <linux/types.h>
+#include <linux/magic.h>
+
+/*
+ * The PRAM filesystem constants/structures
+ */
+
+/*
+ * 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) */
+
+	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 */
diff -Nurp linux-2.6.36-orig/include/linux/pram_fs_sb.h linux-2.6.36/include/linux/pram_fs_sb.h
--- linux-2.6.36-orig/include/linux/pram_fs_sb.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/include/linux/pram_fs_sb.h	2010-09-18 08:39:39.000000000 +0200
@@ -0,0 +1,44 @@
+/*
+ * FILE NAME include/linux/pram_fs_sb.h
+ *
+ * Definitions for the PRAM filesystem.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#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;
+	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 */
 

^ permalink raw reply

* [PATCH 07/16 v4] pramfs: symbolic links
From: Marco Stornelli @ 2010-11-20  9:59 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Tim Bird, Andrew Morton,
	Greg Kroah-Hartman

From: Marco Stornelli <marco.stornelli@gmail.com>

Symlink operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/symlink.c linux-2.6.36/fs/pramfs/symlink.c
--- linux-2.6.36-orig/fs/pramfs/symlink.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/symlink.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,78 @@
+/*
+ * FILE NAME fs/pramfs/symlink.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Symlink operations
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/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 *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);
+}
+
+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
+};
 

^ permalink raw reply

* [PATCH 06/16 v4] pramfs: Inode operations for directories
From: Marco Stornelli @ 2010-11-20  9:59 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Andrew Morton, Tim Bird,
	Greg Kroah-Hartman

From: Marco Stornelli <marco.stornelli@gmail.com>

Inode operations for directories.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/namei.c linux-2.6.36/fs/pramfs/namei.c
--- linux-2.6.36-orig/fs/pramfs/namei.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/namei.c	2010-09-18 12:00:35.000000000 +0200
@@ -0,0 +1,363 @@
+/*
+ * FILE NAME fs/pramfs/namei.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Inode operations for directories.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include "pram.h"
+#include "acl.h"
+#include "xattr.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, 0);
+}
+
+static inline void pram_dec_count(struct inode *inode)
+{
+	if (inode->i_nlink) {
+		inode->i_nlink--;
+		pram_write_inode(inode, 0);
+	}
+}
+
+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)
+			return ERR_PTR(-EACCES);
+	}
+
+	d_splice_alias(inode, dentry);
+	return NULL;
+}
+
+
+/*
+ * 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);
+	int err = PTR_ERR(inode);
+	if (!IS_ERR(inode)) {
+
+		inode->i_op = &pram_file_inode_operations;
+		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);
+	int err = PTR_ERR(inode);
+	if (!IS_ERR(inode)) {
+		init_special_inode(inode, mode, rdev);
+		pram_write_inode(inode, 0); /* 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);
+	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, 0);
+
+	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);
+	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(pi);
+	pi->i_type.dir.head = pi->i_type.dir.tail = 0;
+	pram_memlock_inode(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, 0);
+		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)
+				new_inode->i_nlink--;
+		}
+
+		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);
+		if (!inode)
+			return ERR_PTR(-EACCES);
+	} else
+		return ERR_PTR(-ENOENT);
+
+	return d_obtain_alias(inode);
+}
+
+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,
+};
 

^ permalink raw reply

* [PATCH 05/16 v4] pramfs: block allocation
From: Marco Stornelli @ 2010-11-20  9:59 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Tim Bird, Andrew Morton,
	Greg Kroah-Hartman

From: Marco Stornelli <marco.stornelli@gmail.com>

Block allocation operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/balloc.c linux-2.6.36/fs/pramfs/balloc.c
--- linux-2.6.36-orig/fs/pramfs/balloc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/balloc.c	2010-09-26 18:05:06.000000000 +0200
@@ -0,0 +1,149 @@
+/*
+ * FILE NAME fs/pramfs/balloc.c
+ *
+ * BRIEF MODULE DESCRIPTION
+ *
+ * The blocks allocation and deallocation routines.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/fs.h>
+#include <linux/bitops.h>
+#include "pram.h"
+
+/*
+ * 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);
+
+	bitmap_set(bitmap, 0, 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(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(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_err("no free blocks found!\n");
+			errval = -ENOSPC;
+			goto fail;
+		}
+
+		pram_dbg("allocating blocknr %lu\n", bnr);
+		pram_memunlock_super(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(ps);
+	} else {
+		pram_err("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);
+}

^ permalink raw reply

* [PATCH 04/16 v4] pramfs: file operations
From: Marco Stornelli @ 2010-11-20  9:58 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Andrew Morton, Tim Bird,
	Greg Kroah-Hartman

From: Marco Stornelli <marco.stornelli@gmail.com>

File operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/file.c linux-2.6.36/fs/pramfs/file.c
--- linux-2.6.36-orig/fs/pramfs/file.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/file.c	2010-09-24 18:34:03.000000000 +0200
@@ -0,0 +1,166 @@
+/*
+ * FILE NAME fs/pramfs/file.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * File operations for files.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uio.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+#include "pram.h"
+#include "acl.h"
+#include "xip.h"
+#include "xattr.h"
+
+static int pram_open_file(struct inode *inode, struct file *filp)
+{
+#ifndef CONFIG_PRAMFS_XIP
+	/* Without XIP we force to use Direct IO */
+	filp->f_flags |= O_DIRECT;
+#endif
+	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;
+	ssize_t retval = 0;
+	void *tmp = NULL;
+	unsigned long blocknr, blockoff;
+	int num_blocks, blocksize_mask, blocksize, blocksize_bits;
+	char __user *buf = iov->iov_base;
+	size_t length = iov_length(iov, nr_segs);
+
+	if (length < 0)
+		return -EINVAL;
+	if ((rw == READ) && (offset + length > inode->i_size))
+		length = inode->i_size - offset;
+	if (!length)
+		goto out;
+
+	blocksize_bits = inode->i_sb->s_blocksize_bits;
+	blocksize = 1 << blocksize_bits;
+	blocksize_mask = blocksize - 1;
+
+	/* find starting block number to access */
+	blocknr = offset >> blocksize_bits;
+	/* find starting offset within starting block */
+	blockoff = offset & blocksize_mask;
+	/* find number of blocks to access */
+	num_blocks = (blockoff + length + blocksize_mask) >> blocksize_bits;
+
+	if (rw == WRITE) {
+		/* prepare a temporary buffer to hold a user data block
+		   for writing. */
+		tmp = kmalloc(blocksize, GFP_KERNEL);
+		if (!tmp)
+			return -ENOMEM;
+		/* now allocate the data blocks we'll need */
+		retval = pram_alloc_blocks(inode, blocknr, num_blocks);
+		if (retval)
+			goto fail1;
+	}
+
+	while (length) {
+		int count;
+		u8 *bp = NULL;
+		u64 block = pram_find_data_block(inode, blocknr++);
+		if (unlikely(!block && rw == READ)) {
+			/* We are falling in a hole */
+			hole = 1;
+		} else {
+			bp = (u8 *)pram_get_block(sb, block);
+			if (!bp)
+				goto fail2;
+		}
+
+		count = blockoff + length > blocksize ?
+			blocksize - blockoff : length;
+
+		if (rw == READ) {
+			if (unlikely(hole)) {
+				retval = clear_user(buf, count);
+				if (retval) {
+					retval = -EFAULT;
+					goto fail1;
+				}
+			} else {
+				retval = copy_to_user(buf, &bp[blockoff], count);
+				if (retval) {
+					retval = -EFAULT;
+					goto fail1;
+				}
+			}
+		} else {
+			retval = copy_from_user(tmp, buf, count);
+			if (retval) {
+				retval = -EFAULT;
+				goto fail1;
+			}
+
+			pram_memunlock_block(inode->i_sb, bp);
+			memcpy(&bp[blockoff], tmp, count);
+			pram_memlock_block(inode->i_sb, bp);
+		}
+
+		progress += count;
+		buf += count;
+		length -= count;
+		blockoff = 0;
+		hole = 0;
+	}
+
+fail2:
+	retval = progress;
+fail1:
+	kfree(tmp);
+out:
+	return retval;
+}
+
+int __pram_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	/* Only private mappings */
+	if (vma->vm_flags & VM_SHARED)
+		return -EINVAL;
+	return generic_file_mmap(file, vma);
+}
+
+struct file_operations pram_file_operations = {
+	.llseek		= generic_file_llseek,
+	.read		= pram_read,
+	.write		= pram_write,
+	.aio_read	= pram_aio_read,
+	.aio_write	= pram_aio_write,
+	.mmap		= pram_mmap,
+	.open		= pram_open_file,
+	.fsync		= noop_fsync,
+};
+
+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,
+};

^ permalink raw reply

* [PATCH 03/26 v4] pramfs: inode operations
From: Marco Stornelli @ 2010-11-20  9:58 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Andrew Morton, Tim Bird,
	Greg Kroah-Hartman

From: Marco Stornelli <marco.stornelli@gmail.com>

Inode methods (allocate/free/read/write).

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/inode.c linux-2.6.36/fs/pramfs/inode.c
--- linux-2.6.36-orig/fs/pramfs/inode.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/inode.c	2010-09-26 18:04:38.000000000 +0200
@@ -0,0 +1,710 @@
+/*
+ * FILE NAME fs/pramfs/inode.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Inode methods (allocate/free/read/write).
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include <linux/highuid.h>
+#include <linux/quotaops.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 block for inode and return it's absolute blocknr.
+ * Zeroes out the block if 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(pi);
+		pi->i_blocks = cpu_to_be32(inode->i_blocks);
+		pram_memlock_inode(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, int 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;
+	int i_row, i_col;
+	int N = sb->s_blocksize >> 3; /* num block ptrs per block */
+	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 (start > end || !inode->i_blocks || !pi->i_type.reg.row_block)
+		return;
+
+	mutex_lock(&PRAM_I(inode)->truncate_mutex);
+
+	first_blocknr = (start + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
+	last_blocknr = (end + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
+	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(pi);
+		pi->i_type.reg.row_block = 0;
+		pram_memlock_inode(pi);
+	}
+	pram_memunlock_inode(pi);
+	pi->i_blocks = cpu_to_be32(inode->i_blocks);
+	pram_memlock_inode(pi);
+
+	mutex_unlock(&PRAM_I(inode)->truncate_mutex);
+}
+
+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 blocks for inode, starting at given file-relative
+ * block number. All blocks except the last are zeroed out.
+ */
+int pram_alloc_blocks(struct inode *inode, int file_blocknr, 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_err("failed to alloc 2nd order array block\n");
+			goto fail;
+		}
+		pram_memunlock_inode(pi);
+		pi->i_type.reg.row_block = cpu_to_be64(pram_get_block_off(sb, blocknr));
+		pram_memlock_inode(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 a new row, so make sure
+		 * there is a block allocated for the row.
+		 */
+		if (!row[i]) {
+			/* allocate the row block */
+			errval = pram_new_block(sb, &blocknr, 1);
+			if (errval) {
+				pram_err("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++) {
+			int last_block =
+				(i == last_row_index) && (j == last_col_index);
+			if (!col[j]) {
+				errval = pram_new_data_block(inode,
+							      &blocknr,
+							      !last_block);
+				if (errval) {
+					pram_err("failed to alloc "
+						  "data block\n");
+					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;
+
+	if (pram_calc_checksum((u8 *)pi, PRAM_INODE_SIZE)) {
+		pram_err("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);
+
+	/* check if 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 = -EINVAL;
+		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;
+
+	insert_inode_hash(inode);
+	switch (inode->i_mode & S_IFMT) {
+	case S_IFREG:
+		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;
+		init_special_inode(inode, inode->i_mode,
+				   be32_to_cpu(pi->i_type.dev.rdev));
+		break;
+	}
+
+	return 0;
+
+ bad_inode:
+	make_bad_inode(inode);
+	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);
+
+	pram_memunlock_inode(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);
+
+	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(pi);
+	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(pi);
+	pi->i_dtime = cpu_to_be32(get_seconds());
+	pi->i_type.reg.row_block = 0;
+	pi->i_xattr = 0;
+	pram_memlock_inode(pi);
+
+	/* increment s_free_inodes_count */
+	ps = pram_get_super(sb);
+	pram_memunlock_super(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(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);
+		if (inode->i_blocks)
+			pram_truncate_blocks(inode, 0, inode->i_size);
+		inode->i_size = 0;
+	}
+
+	invalidate_inode_buffers(inode);
+	end_writeback(inode);
+
+	if (want_delete)
+		pram_free_inode(inode);
+}
+
+
+struct inode *pram_new_inode(struct inode *dir, int mode)
+{
+	struct super_block *sb;
+	struct pram_sb_info *sbi;
+	struct pram_super_block *ps;
+	struct inode *inode;
+	struct pram_inode *pi = 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);
+			/* check if 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 (i >= be32_to_cpu(ps->s_inodes_count)) {
+			pram_err("s_free_inodes_count!=0 but none free!?\n");
+			errval = -ENOSPC;
+			goto fail1;
+		}
+
+		pram_dbg("allocating inode %lu\n", ino);
+		pram_memunlock_super(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(ps);
+	} else {
+		pram_err("no space left to create new inode!\n");
+		errval = -ENOSPC;
+		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(pi);
+	pi->i_d.d_next = 0;
+	pi->i_d.d_prev = 0;
+	pram_memlock_inode(pi);
+
+	if (insert_inode_locked(inode) < 0) {
+		errval = -EINVAL;
+		goto fail2;
+	}
+	pram_write_inode(inode, 0);
+
+	errval = pram_init_acl(inode, dir);
+	if (errval)
+		goto fail2;
+
+	errval = pram_init_security(inode, dir);
+	if (errval)
+		goto fail2;
+
+	unlock_super(sb);
+
+	return inode;
+fail2:
+	unlock_super(sb);
+	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)
+{
+	pram_write_inode(inode, NULL);
+}
+
+/* pram_get_and_update_block()
+ *
+ * Look for a block. If not found it can create a new one if create is
+ * different from zero.
+ *
+ * It returns zero if plain lookup failed or blocks mapped or allocated
+ * (plain lookup failed is not an error, e.g. for holes). Minor than zero
+ * otherwise.
+ */
+int pram_get_and_update_block(struct inode *inode, sector_t iblock,
+				     struct buffer_head *bh, int create)
+{
+	struct super_block *sb = inode->i_sb;
+	unsigned int blocksize = 1 << inode->i_blkbits;
+	int err = 0;
+	u64 block;
+	void *bp;
+
+	mutex_lock(&PRAM_I(inode)->truncate_mutex);
+
+	block = pram_find_data_block(inode, iblock);
+
+	if (!block) {
+		if (!create)
+			goto out;
+
+		err = pram_alloc_blocks(inode, iblock, 1);
+		if (err)
+			goto out;
+		block = pram_find_data_block(inode, iblock);
+		if (!block) {
+			err = -EIO;
+			goto out;
+		}
+		set_buffer_new(bh);
+	}
+
+	bh->b_blocknr = block;
+	set_buffer_mapped(bh);
+
+	/* now update the buffer synchronously */
+	bp = pram_get_block(sb, block);
+	if (buffer_new(bh)) {
+		pram_memunlock_block(sb, bp);
+		memset(bp, 0, blocksize);
+		pram_memlock_block(sb, bp);
+		memset(bh->b_data, 0, blocksize);
+	} else {
+		memcpy(bh->b_data, bp, blocksize);
+	}
+
+	set_buffer_uptodate(bh);
+
+ out:
+	mutex_unlock(&PRAM_I(inode)->truncate_mutex);
+	return err;
+}
+
+/*
+ * Called to zeros out a single block. It's used in the "resize"
+ * to avoid to keep data in case the file grow up again.
+ */
+static int pram_clear_block(struct inode *inode, loff_t newsize)
+{
+	pgoff_t index = newsize >> PAGE_CACHE_SHIFT;
+	unsigned long offset = newsize & (PAGE_CACHE_SIZE - 1);
+	unsigned long blocksize, length;
+	sector_t iblock;
+	u64 blockoff;
+	char *bp;
+	int ret = 0;
+
+	blocksize = 1 << inode->i_blkbits;
+	length = offset & (blocksize - 1);
+
+	/* Block boundary ? */
+	if (!length)
+		goto out;
+
+	length = blocksize - length;
+	iblock = (sector_t)index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
+
+	mutex_lock(&PRAM_I(inode)->truncate_mutex);
+	blockoff = pram_find_data_block(inode, iblock);
+
+	/* Hole ? */
+	if (!blockoff)
+		goto out_unlock;
+
+	bp = pram_get_block(inode->i_sb, blockoff);
+	if (!bp) {
+		ret = -EACCES;
+		goto out_unlock;
+	}
+	pram_memunlock_block(inode->i_sb, bp);
+	memset(bp + offset, 0, length);
+	pram_memlock_block(inode->i_sb, bp);
+
+out_unlock:
+	mutex_unlock(&PRAM_I(inode)->truncate_mutex);
+out:
+	return ret;
+}
+
+static int pram_setsize(struct inode *inode, loff_t newsize)
+{
+	int ret = 0;
+	loff_t oldsize;
+
+	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;
+
+	ret = pram_clear_block(inode, newsize);
+	if (ret)
+		return ret;
+
+	oldsize = inode->i_size;
+	i_size_write(inode, newsize);
+	__pram_truncate_blocks(inode, newsize, oldsize);
+	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;
+	int error;
+
+	error = inode_change_ok(inode, attr);
+	if (error)
+		return error;
+
+	if (attr->ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) {
+		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;
+}
+
+struct address_space_operations pram_aops = {
+	.readpage	= pram_readpage,
+	.direct_IO	= pram_direct_IO,
+	.get_xip_mem	= pram_get_xip_mem,
+};
 

^ permalink raw reply

* [PATCH 02/16 v4] pramfs: super operations
From: Marco Stornelli @ 2010-11-20  9:57 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Tim Bird, Andrew Morton,
	Greg Kroah-Hartman

From: Marco Stornelli <marco.stornelli@gmail.com>

Super block operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/super.c linux-2.6.36/fs/pramfs/super.c
--- linux-2.6.36-orig/fs/pramfs/super.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/super.c	2010-09-25 14:09:47.000000000 +0200
@@ -0,0 +1,746 @@
+/*
+ * FILE NAME fs/pramfs/super.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Super block operations.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/parser.h>
+#include <linux/vfs.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/seq_file.h>
+#include <linux/mount.h>
+#include <linux/mm.h>
+#include <linux/ctype.h>
+#include <linux/bitops.h>
+#include <linux/magic.h>
+#include <linux/exportfs.h>
+#include <linux/random.h>
+#include "xattr.h"
+#include "pram.h"
+
+static struct super_operations pram_sops;
+static const struct export_operations pram_export_ops;
+static struct kmem_cache *pram_inode_cachep;
+
+#ifdef CONFIG_PRAMFS_TEST
+static void *first_pram_super;
+
+struct pram_super_block *get_pram_super(void)
+{
+	return (struct pram_super_block *)first_pram_super;
+}
+EXPORT_SYMBOL(get_pram_super);
+#endif
+
+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)
+{
+	void *retval;
+
+	/*
+	 * NOTE: Userland may not map this resource, we will mark the region so
+	 * /dev/mem and the sysfs MMIO access will not be allowed. This
+	 * restriction depends on STRICT_DEVMEM option. If this option is
+	 * disabled or not available we mark the region only as busy.
+	 */
+	retval = request_mem_region_exclusive(phys_addr, size, "pramfs");
+	if (!retval)
+		goto fail;
+
+	retval = ioremap_nocache(phys_addr, size);
+
+	if (retval)
+		wrprotect(retval, size);
+fail:
+	return retval;
+}
+
+static loff_t pram_max_size(int bits)
+{
+	loff_t res;
+	res = (1ULL << (3*bits - 6)) - 1;
+
+	if (res > MAX_LFS_FILESIZE)
+		res = MAX_LFS_FILESIZE;
+
+	pram_info("Max file size %llu bytes", res);
+	return res;
+}
+
+enum {
+	Opt_addr, Opt_bpi, Opt_size,
+	Opt_num_inodes, Opt_mode, Opt_uid,
+	Opt_gid, Opt_blocksize, Opt_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_err,	NULL},
+};
+
+static phys_addr_t get_phys_addr(void **data)
+{
+	phys_addr_t phys_addr;
+	char *options = (char *) *data;
+
+	if (!options || strncmp(options, "physaddr=", 9) != 0)
+		return (phys_addr_t)ULLONG_MAX;
+	options += 9;
+	phys_addr = (phys_addr_t)simple_strtoull(options, &options, 0);
+	if (*options && *options != ',') {
+		pram_err("Invalid phys addr specification: %s\n",
+		       (char *) *data);
+		return (phys_addr_t)ULLONG_MAX;
+	}
+	if (phys_addr & (PAGE_SIZE - 1)) {
+		pram_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)
+{
+	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: {
+				/* physaddr managed in get_phys_addr() */
+				break;
+			}
+			case Opt_bpi: {
+				if (match_int(&args[0], &option))
+					goto bad_val;
+				sbi->bpi = option;
+				break;
+			}
+			case Opt_uid: {
+				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: {
+				/* 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 (match_int(&args[0], &option))
+					goto bad_val;
+				sbi->num_inodes = option;
+				break;
+			}
+			case Opt_blocksize: {
+				/* 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;
+			}
+			default: {
+				pram_err("Bad mount option: \"%s\"\n", p);
+				return -EINVAL;
+				break;
+			}
+		}
+	}
+
+	return 0;
+
+bad_val:
+	pram_err("Bad value '%s' for mount option '%s'\n", args[0].from, p);
+	return -EINVAL;
+}
+
+static struct pram_inode *pram_init(struct super_block *sb, unsigned long size)
+{
+	unsigned long bpi, num_inodes, bitmap_size, blocksize, num_blocks;
+	u64 bitmap_start;
+	struct pram_inode *root_i;
+	struct pram_super_block *super;
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+
+	pram_info("creating an empty pramfs of size %lu\n", size);
+#ifdef CONFIG_PRAMFS_XIP
+	pram_info("pramfs with xip enabled\n");
+#endif
+	sbi->virt_addr = pram_ioremap(sbi->phys_addr, size);
+	if (!sbi->virt_addr) {
+		pram_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) {
+		pram_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) {
+		pram_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(super, bitmap_start + bitmap_size);
+
+	/* clear out super-block and inode table */
+	memset(super, 0, bitmap_start);
+	super->s_size = cpu_to_be64(size);
+	super->s_blocksize = cpu_to_be32(blocksize);
+	super->s_inodes_count = cpu_to_be32(num_inodes);
+	super->s_blocks_count = cpu_to_be32(num_blocks);
+	super->s_free_inodes_count = cpu_to_be32(num_inodes - 1);
+	super->s_bitmap_blocks = cpu_to_be32(bitmap_size >> sb->s_blocksize_bits);
+	super->s_free_blocks_count = cpu_to_be32(num_blocks - be32_to_cpu(super->s_bitmap_blocks));
+	super->s_free_inode_hint = cpu_to_be32(1);
+	super->s_bitmap_start = cpu_to_be64(bitmap_start);
+	super->s_magic = cpu_to_be16(PRAM_SUPER_MAGIC);
+	pram_sync_super(super);
+
+	root_i = pram_get_inode(sb, PRAM_ROOT_INO);
+
+	root_i->i_mode = cpu_to_be32(sbi->mode);
+	root_i->i_mode = cpu_to_be16(root_i->i_mode | S_IFDIR);
+	root_i->i_uid = cpu_to_be32(sbi->uid);
+	root_i->i_gid = cpu_to_be32(sbi->gid);
+	root_i->i_links_count = cpu_to_be16(2);
+	root_i->i_d.d_parent = cpu_to_be64(PRAM_ROOT_INO);
+	pram_sync_inode(root_i);
+
+	pram_init_bitmap(sb);
+
+	pram_memlock_range(super, bitmap_start + bitmap_size);
+
+	return root_i;
+}
+
+static int pram_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct pram_super_block *super, *super_redund;
+	struct pram_inode *root_i;
+	struct pram_sb_info *sbi = NULL;
+	u64 root_offset;
+	unsigned long blocksize, initsize = 0;
+	u32 random = 0;
+	int retval = -EINVAL;
+
+	sbi = kzalloc(sizeof(struct pram_sb_info), GFP_KERNEL);
+	if (!sbi)
+		return -ENOMEM;
+	sb->s_fs_info = 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))
+		goto out;
+
+	initsize = sbi->initsize;
+
+	/* Init a new pramfs instance */
+	if (initsize) {
+		root_i = pram_init(sb, initsize);
+
+		if (IS_ERR(root_i))
+			goto out;
+
+		super = pram_get_super(sb);
+
+		goto setup_sb;
+	}
+
+	pram_dbg("checking physical address 0x%016llx for pramfs image\n",
+		   (u64)sbi->phys_addr);
+
+	/* Map only one page for now. Will remap it when fs size is known. */
+	initsize = PAGE_SIZE;
+	sbi->virt_addr = pram_ioremap(sbi->phys_addr, initsize);
+	if (!sbi->virt_addr) {
+		pram_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)
+				pram_err("Can't find a valid pramfs "
+								"partition\n");
+			goto out;
+		} else {
+			pram_warn("Error in super block: try to repair it with "
+							  "the redundant copy");
+			/* Try to auto-recover the super block */
+			memcpy(super, super_redund, PRAM_SB_SIZE);
+		}
+	}
+
+	/* Read the superblock */
+	if (pram_calc_checksum((u8 *)super, PRAM_SB_SIZE)) {
+		if (pram_calc_checksum((u8 *)super_redund, PRAM_SB_SIZE)) {
+			pram_err("checksum error in super block\n");
+			goto out;
+		} else {
+			pram_warn("Error in super block: try to repair it with "
+							  "the redundant copy");
+			/* Try to auto-recover the super block */
+			memcpy(super, super_redund, PRAM_SB_SIZE);
+		}
+	}
+
+	blocksize = be32_to_cpu(super->s_blocksize);
+	pram_set_blocksize(sb, blocksize);
+
+	initsize = be64_to_cpu(super->s_size);
+	pram_info("pramfs image appears to be %lu KB in size\n", initsize>>10);
+	pram_info("blocksize %lu\n", blocksize);
+
+	/* Read the root inode */
+	root_i = pram_get_inode(sb, PRAM_ROOT_INO);
+
+	/* Check that the root inode is in a sane state */
+	if (pram_calc_checksum((u8 *)root_i, PRAM_INODE_SIZE)) {
+		pram_err("checksum error in root inode!\n");
+		goto out;
+	}
+
+	if (be64_to_cpu(root_i->i_d.d_next)) {
+		pram_err("root->next not NULL??!!\n");
+		goto out;
+	}
+
+	if (!S_ISDIR(be16_to_cpu(root_i->i_mode))) {
+		pram_err("root is not a directory!\n");
+		goto out;
+	}
+
+	root_offset = be64_to_cpu(root_i->i_type.dir.head);
+	if (root_offset == 0)
+		pram_dbg("empty filesystem\n");
+
+	/* Remap the whole filesystem now */
+	iounmap(sbi->virt_addr);
+	release_mem_region(sbi->phys_addr, PAGE_SIZE);
+	sbi->virt_addr = pram_ioremap(sbi->phys_addr, initsize);
+	if (!sbi->virt_addr) {
+		pram_err("ioremap of the pramfs image failed\n");
+		goto out;
+	}
+	super = pram_get_super(sb);
+	root_i = pram_get_inode(sb, PRAM_ROOT_INO);
+
+#ifdef CONFIG_PRAMFS_TEST
+	if (!first_pram_super)
+		first_pram_super = sbi->virt_addr;
+#endif
+
+	/* Set it all up.. */
+ setup_sb:
+	sb->s_magic = be16_to_cpu(super->s_magic);
+	sb->s_op = &pram_sops;
+	sb->s_maxbytes = pram_max_size(sb->s_blocksize_bits);
+	sb->s_time_gran = 1;
+	sb->s_export_op = &pram_export_ops;
+	sb->s_xattr = pram_xattr_handlers;
+#ifdef	CONFIG_PRAMFS_POSIX_ACL
+	sb->s_flags |= MS_POSIXACL;
+#endif
+	sb->s_root = d_alloc_root(pram_iget(sb, PRAM_ROOT_INO));
+
+	retval = 0;
+	return retval;
+ out:
+	if (sbi->virt_addr) {
+		iounmap(sbi->virt_addr);
+		release_mem_region(sbi->phys_addr, initsize);
+	}
+
+	kfree(sbi);
+	return retval;
+}
+
+int pram_statfs(struct dentry *d, struct kstatfs *buf)
+{
+	struct super_block *sb = d->d_sb;
+	struct pram_super_block *ps = pram_get_super(sb);
+
+	buf->f_type = PRAM_SUPER_MAGIC;
+	buf->f_bsize = sb->s_blocksize;
+	buf->f_blocks = be32_to_cpu(ps->s_blocks_count);
+	buf->f_bfree = buf->f_bavail = pram_count_free_blocks(sb);
+	buf->f_files = be32_to_cpu(ps->s_inodes_count);
+	buf->f_ffree = be32_to_cpu(ps->s_free_inodes_count);
+	buf->f_namelen = PRAM_NAME_LEN;
+	return 0;
+}
+
+static int pram_show_options(struct seq_file *seq, struct vfsmount *vfs)
+{
+	struct pram_sb_info *sbi = (struct pram_sb_info *)vfs->mnt_sb->s_fs_info;
+
+	seq_printf(seq, ",physaddr=0x%016llx", (u64)sbi->phys_addr);
+	if (sbi->initsize)
+		seq_printf(seq, ",init=%luk", sbi->initsize >> 10);
+	if (sbi->blocksize)
+		seq_printf(seq, ",bs=%lu", sbi->blocksize);
+	if (sbi->bpi)
+		seq_printf(seq, ",bpi=%lu", sbi->bpi);
+	if (sbi->num_inodes)
+		seq_printf(seq, ",N=%lu", sbi->num_inodes);
+	if (sbi->mode != (S_IRWXUGO | S_ISVTX))
+		seq_printf(seq, ",mode=%03o", sbi->mode);
+	if (sbi->uid != 0)
+		seq_printf(seq, ",uid=%u", sbi->uid);
+	if (sbi->gid != 0)
+		seq_printf(seq, ",gid=%u", sbi->gid);
+
+	return 0;
+}
+
+int pram_remount(struct super_block *sb, int *mntflags, char *data)
+{
+	struct pram_super_block *ps;
+
+	if ((*mntflags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) {
+		ps = pram_get_super(sb);
+		pram_memunlock_super(ps);
+		ps->s_mtime = cpu_to_be32(get_seconds()); /* update mount time */
+		pram_memlock_super(ps);
+	}
+
+	return 0;
+}
+
+void pram_put_super(struct super_block *sb)
+{
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+	struct pram_super_block *ps = pram_get_super(sb);
+	u64 size = be64_to_cpu(ps->s_size);
+
+#ifdef CONFIG_PRAMFS_TEST
+	if (first_pram_super == sbi->virt_addr)
+		first_pram_super = NULL;
+#endif
+
+	pram_xattr_put_super(sb);
+	/* It's unmount time, so unmap the pramfs memory */
+	if (sbi->virt_addr) {
+		iounmap(sbi->virt_addr);
+		sbi->virt_addr = NULL;
+		release_mem_region(sbi->phys_addr, size);
+	}
+
+	sb->s_fs_info = NULL;
+	kfree(sbi);
+}
+
+static struct inode *pram_alloc_inode(struct super_block *sb)
+{
+	struct pram_inode_vfs *vi = (struct pram_inode_vfs *)
+				kmem_cache_alloc(pram_inode_cachep, GFP_KERNEL);
+	if (!vi)
+		return NULL;
+	vi->vfs_inode.i_version = 1;
+	return &vi->vfs_inode;
+}
+
+static void pram_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(pram_inode_cachep, PRAM_I(inode));
+}
+
+static void init_once(void *foo)
+{
+	struct pram_inode_vfs *vi = (struct pram_inode_vfs *) foo;
+
+#ifdef CONFIG_PRAMFS_XATTR
+	init_rwsem(&vi->xattr_sem);
+#endif
+	mutex_init(&vi->truncate_mutex);
+	inode_init_once(&vi->vfs_inode);
+}
+
+static int __init init_inodecache(void)
+{
+	pram_inode_cachep = kmem_cache_create("pram_inode_cache",
+					     sizeof(struct pram_inode_vfs),
+					     0, (SLAB_RECLAIM_ACCOUNT|
+						SLAB_MEM_SPREAD),
+					     init_once);
+	if (pram_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	kmem_cache_destroy(pram_inode_cachep);
+}
+
+/*
+ * the super block writes are all done "on the fly", so the
+ * super block is never in a "dirty" state, so there's no need
+ * for write_super.
+ */
+static struct super_operations pram_sops = {
+	.alloc_inode	= pram_alloc_inode,
+	.destroy_inode	= pram_destroy_inode,
+	.write_inode	= pram_write_inode,
+	.dirty_inode	= pram_dirty_inode,
+	.evict_inode	= pram_evict_inode,
+	.put_super	= pram_put_super,
+	.statfs		= pram_statfs,
+	.remount_fs	= pram_remount,
+	.show_options	= pram_show_options,
+};
+
+static int pram_get_sb(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data, struct vfsmount *mnt)
+{
+	return get_sb_nodev(fs_type, flags, data, pram_fill_super, mnt);
+}
+
+static struct file_system_type pram_fs_type = {
+	.owner          = THIS_MODULE,
+	.name           = "pramfs",
+	.get_sb         = pram_get_sb,
+	.kill_sb        = kill_anon_super,
+};
+
+static struct inode *pram_nfs_get_inode(struct super_block *sb,
+		u64 ino, u32 generation)
+{
+	struct pram_super_block *ps = pram_get_super(sb);
+	struct inode *inode;
+
+	if (ino < PRAM_ROOT_INO)
+		return ERR_PTR(-ESTALE);
+	if (((ino - PRAM_ROOT_INO) >> PRAM_INODE_BITS) > be32_to_cpu(ps->s_inodes_count))
+		return ERR_PTR(-ESTALE);
+
+	inode = pram_iget(sb, ino);
+	if (!inode)
+		return ERR_PTR(-ESTALE);
+	if (generation && inode->i_generation != generation) {
+		/* we didn't find the right inode.. */
+		iput(inode);
+		return ERR_PTR(-ESTALE);
+	}
+	return inode;
+}
+
+static struct dentry *
+pram_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len,
+		   int fh_type)
+{
+	return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
+				    pram_nfs_get_inode);
+}
+
+static struct dentry *
+pram_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len,
+		   int fh_type)
+{
+	return generic_fh_to_parent(sb, fid, fh_len, fh_type,
+				    pram_nfs_get_inode);
+}
+
+static const struct export_operations pram_export_ops = {
+	.fh_to_dentry = pram_fh_to_dentry,
+	.fh_to_parent = pram_fh_to_parent,
+	.get_parent = pram_get_parent,
+};
+
+static int __init init_pram_fs(void)
+{
+	int rc = 0;
+
+	rc = init_pram_xattr();
+	if (rc)
+		return rc;
+
+	rc = init_inodecache();
+	if (rc)
+		goto out1;
+
+	rc = bdi_init(&pram_backing_dev_info);
+	if (rc)
+		goto out2;
+
+	rc = register_filesystem(&pram_fs_type);
+	if (rc)
+		goto out3;
+
+	return 0;
+
+out3:
+	bdi_destroy(&pram_backing_dev_info);
+out2:
+	destroy_inodecache();
+out1:
+	exit_pram_xattr();
+	return rc;
+}
+
+static void __exit exit_pram_fs(void)
+{
+	unregister_filesystem(&pram_fs_type);
+	bdi_destroy(&pram_backing_dev_info);
+	destroy_inodecache();
+	exit_pram_xattr();
+}
+
+MODULE_AUTHOR("Marco Stornelli <marco.stornelli@gmail.com>");
+MODULE_DESCRIPTION("Protected/Persistent RAM Filesystem");
+MODULE_LICENSE("GPL");
+
+module_init(init_pram_fs)
+module_exit(exit_pram_fs)

^ permalink raw reply

* [PATCH 01/16 v4] pramfs: documentation
From: Marco Stornelli @ 2010-11-20  9:57 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Andrew Morton, Tim Bird,
	Greg Kroah-Hartman

From: Marco Stornelli <marco.stornelli@gmail.com>

Documentation for PRAMFS.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/Documentation/filesystems/pramfs.txt linux-2.6.36/Documentation/filesystems/pramfs.txt
--- linux-2.6.36-orig/Documentation/filesystems/pramfs.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/Documentation/filesystems/pramfs.txt	2010-10-23 09:04:32.000000000 +0200
@@ -0,0 +1,296 @@
+
+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. An
+example usage might be system logs under /var/log, or a user address
+book in a cell phone or PDA.
+
+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.
+
+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.
+
+
+ChangeLog
+=========
+
+1.2.1
+	- kernel 2.6.36
+	- replaced d_add with d_splice_alias in pram_lookup to support NFS
+	- reworked pram_get_parent
+	- added disable/enable irq in the pram_{memlock|memunlock}_range
+	  in order to avoid "open windows" with writeable memory
+	- when xattr support was enabled, the semaphore xattr_sem wasn't
+	  initialized
+	- implemented new truncate convention
+	- removed pram_truncate_page
+	- fixed a possible oops in pram_put_super, a super block field was
+	  used after iounmap
+	- fixed file holes management
+	- super block s_size field now is 64bit
+
+1.2.0
+	- kernel 2.6.35
+	- added support for setattr callback, now truncate command works
+	- added support for extended attributes, ACLs, security labels
+	- fixed scalability problem with truncate mutex
+	- fixed bitmap endianess and problems with 64bit archs (bug 3013785)
+	- replaced functions pram_fill_new_inode and pram_fill_inode with
+	  pram_read_inode and pram_iget
+	- added support for export operations, now it's possible to use
+	  NFS to export pram mount points
+	- added generation field in the inode struct for file versioning
+	  (for NFS)
+	- fixed the settings of fields s_maxbytes and s_time_gran in the super
+	  block struct
+	- removed any reference to quota operations, they are not supported
+
+1.1.6
+	- kernel 2.6.33
+	- added preempt_enable/disable in pram_{memlock|memunlock}_range in
+	  order to avoid race conditions
+	- replaced pram_unlock_* and pram_lock_* with pram_memlock_* and
+	  pram_memunlock_*
+
+1.1.5
+	- kernel 2.6.31
+	- reworked pram_writeable in order to use the funcions
+	  write_on_kernel_pte_range and write_off_kernel_pte_range
+	- added writeable_kernel_pte_range in order to compile on ARM arch
+	- added truncate_inode_pages() in pram_delete_inode()
+	- fixed an endianess conversion error in super.c, df command didn't work
+	- fixed an error in pram_find_data_block(), some code was relative to
+	  32-bit design yet
+	- fixed an error in pram_free_block(), endianess conversion not needed
+	- fixed lock usage, possible deadlock recognized by lockdep
+
+1.1.4
+	- fs layout now endianess indipendent
+	- debug functions replaced with pr_*
+	- changed CONFIG_PRAMFS_NOWP to CONFIG_PRAMFS_WRITE_PROTECT and
+	  revert the meaning
+	- removed the rootfs mounting
+	- redundant superblock
+	- crc performed with crc16
+	- moved out pte manipulations
+	- used only flush_kernel_tlb_range instead of non standard
+	  flush_kernel_tlb_page
+	- replaced BKL with a mutex
+
+1.1.3:
+	- kernel 2.6.30
+	- replaced DQUOT macros with vfs_dq_* functions in inode.c
+
+1.1.2:
+	- kernel 2.6.29
+	- replaced current->fsuid and current->fsgid in inode.c with
+	  current_fsuid() and current_fsgid() macro
+	- replaced pram_fsync with simple_file_sync (deleted fsync.c)
+	- removed ioctl.c
+	- fix the minus inside pram_sync_super and pram_sync_inode
+	  inline functions
+	- now flags must be explicit in pram_lock/unlock_inode,
+	  pram_lock/unlock_super and in pram_lock/unlock_block
+	- fix compiler warning about unused flags variable
+	- now multiple instances of pramfs are allowed
+	- added management of mode, uid and gid mount options
+	- renamed find_pramfs_super() in pram_get_super() and changed its
+	  behaviour to remove the dependecy from a kernel symbol not exported
+	- added superblock operation pram_show_options
+	- added pram_parse_options function
+	- added checks for some mount parameters to avoid kernel crashes
+	- now a physical address of zero is allowed
+	- added the use of request_mem_region_exclusive
+	- now phys_addr in the struct pram_sb_info is phys_addr_t type
+
+1.1.1:
+	- kernel 2.6.28.1
+	- now the code is compliant to kernel coding style
+	- replaced typedef pram_off_t with the standard off_t
+
+1.1.0
+	- kernel 2.6.27
+	- added xip feature.
+
+1.0.4:
+	- kernel 2.6.10
+	- include <asm/tlbflush.h> in wprotect.c for
+	  flush_tlb_kernel_range().
+	- fixed a bug in pram_mknod(). The pramfs inode needs
+	  updating after calling init_special_inode() to update
+	  the rdev.
+1.0.3:
+	- kernel 2.6.9.
+	- __ioremap() definition not consistent across archs, use
+	  ioremap() instead.
+	- flush_tlb_kernel_page() is only available on some archs.
+	- fixed bug in pram_fill_super(): root inode pointer needs
+	  to be recalculated after remapping whole fs.
+1.0.2:
+	- kernel 2.6.4.
+	- use pram_truncate() in pram_delete_inode().
+	- dangling pram_lock_inode() removed in pram_truncate_blocks().
+	- edits to this README
+
+1.0.1:
+	- port to kernel 2.6.3.
+	- implement direct_IO() method instead of custom file read/write
+	  methods.
+	- do away with __ioremap_readonly() requirement.
+	- implement inode truncate() method.
+
+1.0.0:
+	- Started ChangeLog (kernel 2.4.22).
diff -Nurp linux-2.6.36-orig/Documentation/filesystems/xip.txt linux-2.6.36/Documentation/filesystems/xip.txt
--- linux-2.6.36-orig/Documentation/filesystems/xip.txt	2010-09-13 01:07:37.000000000 +0200
+++ linux-2.6.36/Documentation/filesystems/xip.txt	2010-09-14 18:49:52.000000000 +0200
@@ -49,6 +49,8 @@ This address space operation is mutually
 do page cache read/write operations.
 The following filesystems support it as of today:
 - ext2: the second extended filesystem, see Documentation/filesystems/ext2.txt
+- pramfs: persistent and protected RAM filesystem, see
+  Documentation/filesystems/pramfs.txt
 
 A set of file operations that do utilize get_xip_page can be found in
 mm/filemap_xip.c . The following file operation implementations are provided:

^ permalink raw reply

* [PATCH 00/16 v4] pramfs: persistent and protected RAM filesystem
From: Marco Stornelli @ 2010-11-20  9:56 UTC (permalink / raw)
  To: Linux Kernel
  Cc: Linux Embedded, Linux FS Devel, Tim Bird, Andrew Morton,
	Greg Kroah-Hartman

Hi all,

fourth round for the patch series. I summarize here the changes to
improve the review:

v4:
- in bitmap init used already present bitmap_set function
- fix a possible memory leak in an error path reported by yidong zhang

v3:
- fix a possible memory leak in an error path reported by yidong zhang
- fix a warning when using XIP about not used __pram_mmap symbol
- fix test module header description and replaced TEST_MODULE with
PRAMFS_TEST_MODULE in the Kconfig and Makefile according to the comments
done by Randy Dunlap
- fix a compilation warning in super.c reported by James Hogan
- fix a compilation error when XIP was enabled
- removed not used symbol PRAM_XATTR_INDEX_LUSTRE
- fix some comment style issue

v2:
- fix documentation errors reported by Randy Dunlap and Kieran Bingham
- reworked memory write protection functions with the suggestions of
Andi Kleen

v1:
- first draft

Andrew/Greg, can you insert the code in the mm/staging tree?

Marco

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox