public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC 1/7] cramfs: allow remount rw
       [not found] <20080531152013.031903990@arndb.de>
@ 2008-05-31 15:20 ` arnd
  2008-05-31 15:20 ` [RFC 2/7] cramfs: create unique inode numbers arnd
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: arnd @ 2008-05-31 15:20 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, hch

[-- Attachment #1: 0001-cramfs-allow-remount-rw.patch --]
[-- Type: text/plain, Size: 646 bytes --]

It is not possible to write to the current cramfs, so the
check for the rw mount flag is unnecessary. This patch is
required to make fake the fake write patches work on cramfs.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 fs/cramfs/inode.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 0c3b618..08f08f9 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -226,7 +226,6 @@ static void cramfs_put_super(struct super_block *sb)
 
 static int cramfs_remount(struct super_block *sb, int *flags, char *data)
 {
-	*flags |= MS_RDONLY;
 	return 0;
 }
 
-- 
1.5.4.3

-- 


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC 2/7] cramfs: create unique inode numbers
       [not found] <20080531152013.031903990@arndb.de>
  2008-05-31 15:20 ` [RFC 1/7] cramfs: allow remount rw arnd
@ 2008-05-31 15:20 ` arnd
  2008-06-01 16:50   ` Jörn Engel
  2008-05-31 15:20 ` [RFC 3/7] cramfs: allow unlinking of files arnd
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 12+ messages in thread
From: arnd @ 2008-05-31 15:20 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, hch

[-- Attachment #1: 0002-cramfs-create-unique-inode-numbers.patch --]
[-- Type: text/plain, Size: 6942 bytes --]

This changes the inode number in cramfs to be based on
the location of the dentry instead of the file, in order
to make inodes unique.
This lets us change and unlink files in a later patch
without changing all other files that contain the same
data, and it fixes a user-visible bug with 'cp -a'
trying to hardlink empty directories when copying from
a cramfs source.

A slight disadvantage is that identical files no longer
share a common page cache, so the data has to be read
from disk for each file individually.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 fs/cramfs/inode.c |  100 +++++++++++++++++++++++------------------------------
 1 files changed, 43 insertions(+), 57 deletions(-)

diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 08f08f9..8aa04d7 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -33,80 +33,60 @@ static const struct address_space_operations cramfs_aops;
 
 static DEFINE_MUTEX(read_mutex);
 
+#define OFFSET(x)	((unsigned long)(x)->i_private)
 
-/* These two macros may change in future, to provide better st_ino
-   semantics. */
-#define CRAMINO(x)	(((x)->offset && (x)->size)?(x)->offset<<2:1)
-#define OFFSET(x)	((x)->i_ino)
-
-
-static int cramfs_iget5_test(struct inode *inode, void *opaque)
+static struct inode *get_cramfs_inode(struct super_block *sb,
+				mode_t mode, dev_t dev)
 {
-	struct cramfs_inode *cramfs_inode = opaque;
-
-	if (inode->i_ino != CRAMINO(cramfs_inode))
-		return 0; /* does not match */
-
-	if (inode->i_ino != 1)
-		return 1;
+	struct inode *inode;
 
-	/* all empty directories, char, block, pipe, and sock, share inode #1 */
-
-	if ((inode->i_mode != cramfs_inode->mode) ||
-	    (inode->i_gid != cramfs_inode->gid) ||
-	    (inode->i_uid != cramfs_inode->uid))
-		return 0; /* does not match */
-
-	if ((S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) &&
-	    (inode->i_rdev != old_decode_dev(cramfs_inode->size)))
-		return 0; /* does not match */
-
-	return 1; /* matches */
-}
+	inode = new_inode(sb);
+	if (!inode)
+		return NULL;
 
-static int cramfs_iget5_set(struct inode *inode, void *opaque)
-{
-	static struct timespec zerotime;
-	struct cramfs_inode *cramfs_inode = opaque;
-	inode->i_mode = cramfs_inode->mode;
-	inode->i_uid = cramfs_inode->uid;
-	inode->i_size = cramfs_inode->size;
-	inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1;
-	inode->i_gid = cramfs_inode->gid;
-	/* Struct copy intentional */
-	inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime;
-	inode->i_ino = CRAMINO(cramfs_inode);
+	inode->i_mode = mode;
 	/* inode->i_nlink is left 1 - arguably wrong for directories,
 	   but it's the best we can do without reading the directory
            contents.  1 yields the right result in GNU find, even
 	   without -noleaf option. */
-	if (S_ISREG(inode->i_mode)) {
+	if (S_ISREG(mode)) {
 		inode->i_fop = &generic_ro_fops;
 		inode->i_data.a_ops = &cramfs_aops;
-	} else if (S_ISDIR(inode->i_mode)) {
+	} else if (S_ISDIR(mode)) {
 		inode->i_op = &cramfs_dir_inode_operations;
 		inode->i_fop = &cramfs_directory_operations;
-	} else if (S_ISLNK(inode->i_mode)) {
+	} else if (S_ISLNK(mode)) {
 		inode->i_op = &page_symlink_inode_operations;
 		inode->i_data.a_ops = &cramfs_aops;
 	} else {
 		inode->i_size = 0;
 		inode->i_blocks = 0;
-		init_special_inode(inode, inode->i_mode,
-			old_decode_dev(cramfs_inode->size));
+		init_special_inode(inode, mode, dev);
 	}
-	return 0;
+	return inode;
 }
 
-static struct inode *get_cramfs_inode(struct super_block *sb,
-				struct cramfs_inode * cramfs_inode)
+static struct inode *get_cramfs_inode_ondisk(struct super_block *sb,
+				struct cramfs_inode * cramfs_inode,
+				ino_t ino, off_t offset)
 {
-	struct inode *inode = iget5_locked(sb, CRAMINO(cramfs_inode),
-					    cramfs_iget5_test, cramfs_iget5_set,
-					    cramfs_inode);
-	if (inode && (inode->i_state & I_NEW)) {
-		unlock_new_inode(inode);
-	}
+	static struct timespec zerotime;
+	struct inode *inode;
+
+	inode = get_cramfs_inode(sb, cramfs_inode->mode,
+				old_decode_dev(cramfs_inode->size));
+	if (!inode)
+		return NULL;
+
+	inode->i_ino = ino;
+	inode->i_uid = cramfs_inode->uid;
+	inode->i_size = cramfs_inode->size;
+	inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1;
+	inode->i_gid = cramfs_inode->gid;
+	/* Struct copy intentional */
+	inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime;
+	inode->i_private = (void *)offset;
+
 	return inode;
 }
 
@@ -234,6 +214,7 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
 	int i;
 	struct cramfs_super super;
 	unsigned long root_offset;
+	unsigned long root_ino;
 	struct cramfs_sb_info *sbi;
 	struct inode *root;
 
@@ -252,6 +233,7 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
 	/* Read the first block and get the superblock from it */
 	memcpy(&super, cramfs_read(sb, 0, sizeof(super)), sizeof(super));
 	mutex_unlock(&read_mutex);
+	root_ino = offsetof(struct cramfs_super, root);
 
 	/* Do sanity checks on the superblock */
 	if (super.magic != CRAMFS_MAGIC) {
@@ -266,6 +248,7 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
 		mutex_lock(&read_mutex);
 		memcpy(&super, cramfs_read(sb, 512, sizeof(super)), sizeof(super));
 		mutex_unlock(&read_mutex);
+		root_ino += 512;
 		if (super.magic != CRAMFS_MAGIC) {
 			if (super.magic == CRAMFS_MAGIC_WEND && !silent)
 				printk(KERN_ERR "cramfs: wrong endianess\n");
@@ -310,7 +293,7 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
 
 	/* Set it all up.. */
 	sb->s_op = &cramfs_ops;
-	root = get_cramfs_inode(sb, &super.root);
+	root = get_cramfs_inode_ondisk(sb, &super.root, root_ino, root_offset);
 	if (!root)
 		goto out;
 	sb->s_root = d_alloc_root(root);
@@ -383,7 +366,7 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 		 */
 		namelen = de->namelen << 2;
 		memcpy(buf, name, namelen);
-		ino = CRAMINO(de);
+		ino = OFFSET(inode) + offset;
 		mode = de->mode;
 		mutex_unlock(&read_mutex);
 		nextoffset = offset + sizeof(*de) + namelen;
@@ -422,8 +405,10 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, s
 		struct cramfs_inode *de;
 		char *name;
 		int namelen, retval;
+		ino_t ino;
 
-		de = cramfs_read(dir->i_sb, OFFSET(dir) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN);
+		ino = OFFSET(dir) + offset;
+		de = cramfs_read(dir->i_sb, ino, sizeof(*de)+CRAMFS_MAXPATHLEN);
 		name = (char *)(de+1);
 
 		/* Try to take advantage of sorted directories */
@@ -454,7 +439,8 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, s
 		if (!retval) {
 			struct cramfs_inode entry = *de;
 			mutex_unlock(&read_mutex);
-			d_add(dentry, get_cramfs_inode(dir->i_sb, &entry));
+			d_add(dentry, get_cramfs_inode_ondisk(dir->i_sb,
+					&entry, ino, entry.offset << 2));
 			return NULL;
 		}
 		/* else (retval < 0) */
-- 
1.5.4.3

-- 


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC 3/7] cramfs: allow unlinking of files
       [not found] <20080531152013.031903990@arndb.de>
  2008-05-31 15:20 ` [RFC 1/7] cramfs: allow remount rw arnd
  2008-05-31 15:20 ` [RFC 2/7] cramfs: create unique inode numbers arnd
@ 2008-05-31 15:20 ` arnd
  2008-06-01 16:54   ` Jörn Engel
  2008-05-31 15:20 ` [RFC 4/7] cramfs: allow rmdir arnd
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 12+ messages in thread
From: arnd @ 2008-05-31 15:20 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, hch

[-- Attachment #1: 0003-cramfs-allow-unlinking-of-files.patch --]
[-- Type: text/plain, Size: 2342 bytes --]

The new cramfs_unlink function replaces an existing dentry with
a pinned negative dentry in cramfs, so that lookup does not find
it any more.

The readdir function gets changed here to no longer show the file
if a negative dentry exists.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 fs/cramfs/inode.c |   27 ++++++++++++++++++++++++++-
 1 files changed, 26 insertions(+), 1 deletions(-)

diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 8aa04d7..6b9f21f 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -348,12 +348,14 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 
 	copied = 0;
 	while (offset < inode->i_size) {
+		struct qstr qstr = { .name = buf, };
 		struct cramfs_inode *de;
 		unsigned long nextoffset;
 		char *name;
 		ino_t ino;
 		mode_t mode;
 		int namelen, error;
+		struct dentry *dentry;
 
 		mutex_lock(&read_mutex);
 		de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN);
@@ -379,7 +381,15 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 				break;
 			namelen--;
 		}
-		error = filldir(dirent, buf, namelen, offset, ino, mode >> 12);
+		qstr.len = namelen;
+		dentry = d_hash_and_lookup(filp->f_path.dentry, &qstr);
+
+		error = 0;
+		if (!dentry || (dentry->d_inode))
+				error = filldir(dirent, buf, namelen, offset,
+					ino, mode >> 12);
+
+		dput(dentry);
 		if (error)
 			break;
 
@@ -391,6 +401,19 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 	return 0;
 }
 
+int cramfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+	struct dentry *new;
+
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	drop_nlink(inode);
+	new = d_alloc(dentry->d_parent, &dentry->d_name);
+	d_add(new, NULL);
+
+	dget(dentry);
+	return 0;
+}
 /*
  * Lookup and fill in the inode data..
  */
@@ -512,10 +535,12 @@ static const struct file_operations cramfs_directory_operations = {
 };
 
 static const struct inode_operations cramfs_dir_inode_operations = {
+	.unlink		= cramfs_unlink,
 	.lookup		= cramfs_lookup,
 };
 
 static const struct super_operations cramfs_ops = {
+	.drop_inode	= generic_delete_inode,
 	.put_super	= cramfs_put_super,
 	.remount_fs	= cramfs_remount,
 	.statfs		= cramfs_statfs,
-- 
1.5.4.3

-- 


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC 4/7] cramfs: allow rmdir
       [not found] <20080531152013.031903990@arndb.de>
                   ` (2 preceding siblings ...)
  2008-05-31 15:20 ` [RFC 3/7] cramfs: allow unlinking of files arnd
@ 2008-05-31 15:20 ` arnd
  2008-05-31 15:20 ` [RFC 5/7] cramfs: allow writing to existing files arnd
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: arnd @ 2008-05-31 15:20 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, hch

[-- Attachment #1: 0004-cramfs-allow-rmdir.patch --]
[-- Type: text/plain, Size: 3852 bytes --]

rmdir in cramfs works much like unlink, but we need to check
first that the directory is empty. Since the link count is
always 1, we have to look at the directory contents for that,
but fortunately, we can abuse the readdir logic for that,
to check if at least one entry exists.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 fs/cramfs/inode.c |   71 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 61 insertions(+), 10 deletions(-)

diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 6b9f21f..8c3e8bb 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -326,18 +326,20 @@ static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 /*
  * Read a cramfs directory entry.
  */
-static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+static int cramfs_readdir_ondisk(struct dentry *dentry, void *dirent,
+			filldir_t filldir, loff_t *pos)
 {
-	struct inode *inode = filp->f_path.dentry->d_inode;
+	struct inode *inode = dentry->d_inode;
 	struct super_block *sb = inode->i_sb;
 	char *buf;
 	unsigned int offset;
 	int copied;
 
 	/* Offset within the thing. */
-	offset = filp->f_pos;
+	offset = *pos;
 	if (offset >= inode->i_size)
 		return 0;
+
 	/* Directory entries are always 4-byte aligned */
 	if (offset & 3)
 		return -EINVAL;
@@ -355,7 +357,7 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 		ino_t ino;
 		mode_t mode;
 		int namelen, error;
-		struct dentry *dentry;
+		struct dentry *d;
 
 		mutex_lock(&read_mutex);
 		de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN);
@@ -382,25 +384,32 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 			namelen--;
 		}
 		qstr.len = namelen;
-		dentry = d_hash_and_lookup(filp->f_path.dentry, &qstr);
+		d = d_hash_and_lookup(dentry, &qstr);
 
 		error = 0;
-		if (!dentry || (dentry->d_inode))
-				error = filldir(dirent, buf, namelen, offset,
-					ino, mode >> 12);
+		if (!d || (d->d_inode))
+			error = filldir(dirent, buf, namelen,
+					offset, ino, mode >> 12);
 
-		dput(dentry);
+		dput(d);
 		if (error)
 			break;
 
 		offset = nextoffset;
-		filp->f_pos = offset;
+		*pos = offset;
 		copied++;
 	}
 	kfree(buf);
+
 	return 0;
 }
 
+static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	return cramfs_readdir_ondisk(filp->f_dentry, dirent,
+					filldir, &filp->f_pos);
+}
+
 int cramfs_unlink(struct inode *dir, struct dentry *dentry)
 {
 	struct inode *inode = dentry->d_inode;
@@ -414,6 +423,47 @@ int cramfs_unlink(struct inode *dir, struct dentry *dentry)
 	dget(dentry);
 	return 0;
 }
+
+/*
+ * We use the readdir infrastructure to test whether a directory is
+ * empty or not. cramfs_test_filldir will get called for any on-disk
+ * dentries that have not been unlinked.
+ */
+static int cramfs_test_filldir(void * __buf, const char * name, int namlen,
+				loff_t offset, u64 ino, unsigned int d_type)
+{
+	int *buf = __buf;
+	*buf = 0;
+	return -ENOTEMPTY;
+}
+
+static int cramfs_empty(struct dentry *dentry)
+{
+	loff_t pos = 0;
+	int empty = 1;
+	int ret;
+	if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode))
+		return 1;
+
+	if (!simple_empty(dentry))
+		return 0;
+
+	ret = cramfs_readdir_ondisk(dentry, &empty, cramfs_test_filldir, &pos);
+	if (ret)
+		return ret;
+
+	return empty;
+}
+
+int cramfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	if (!cramfs_empty(dentry))
+		return -ENOTEMPTY;
+
+	cramfs_unlink(dir, dentry);
+	return 0;
+}
+
 /*
  * Lookup and fill in the inode data..
  */
@@ -536,6 +586,7 @@ static const struct file_operations cramfs_directory_operations = {
 
 static const struct inode_operations cramfs_dir_inode_operations = {
 	.unlink		= cramfs_unlink,
+	.rmdir		= cramfs_rmdir,
 	.lookup		= cramfs_lookup,
 };
 
-- 
1.5.4.3

-- 


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC 5/7] cramfs: allow writing to existing files
       [not found] <20080531152013.031903990@arndb.de>
                   ` (3 preceding siblings ...)
  2008-05-31 15:20 ` [RFC 4/7] cramfs: allow rmdir arnd
@ 2008-05-31 15:20 ` arnd
  2008-05-31 15:20 ` [RFC 6/7] cramfs: read directory entries from dcache arnd
  2008-05-31 15:20 ` [RFC 7/7] cramfs: add missing inode operations arnd
  6 siblings, 0 replies; 12+ messages in thread
From: arnd @ 2008-05-31 15:20 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, hch

[-- Attachment #1: 0005-cramfs-allow-writing-to-existing-files.patch --]
[-- Type: text/plain, Size: 1764 bytes --]

Existing files in cramfs basically become ramfs files,
and we use the ramfs_file_operations for them.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 fs/cramfs/inode.c |   13 +++++++++++--
 1 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 8c3e8bb..0d3ac80 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -15,6 +15,7 @@
 #include <linux/fs.h>
 #include <linux/pagemap.h>
 #include <linux/init.h>
+#include <linux/ramfs.h>
 #include <linux/string.h>
 #include <linux/blkdev.h>
 #include <linux/cramfs_fs.h>
@@ -50,7 +51,7 @@ static struct inode *get_cramfs_inode(struct super_block *sb,
            contents.  1 yields the right result in GNU find, even
 	   without -noleaf option. */
 	if (S_ISREG(mode)) {
-		inode->i_fop = &generic_ro_fops;
+		inode->i_fop = &ramfs_file_operations;
 		inode->i_data.a_ops = &cramfs_aops;
 	} else if (S_ISDIR(mode)) {
 		inode->i_op = &cramfs_dir_inode_operations;
@@ -531,6 +532,11 @@ static int cramfs_readpage(struct file *file, struct page * page)
 	u32 maxblock, bytes_filled;
 	void *pgdata;
 
+	/* FIXME: not enough we also need to handle sparse files
+	 * that originally came from the disk */
+	if (!inode->i_private)
+		return simple_readpage(file, page);
+
 	maxblock = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
 	bytes_filled = 0;
 	if (page->index < maxblock) {
@@ -568,7 +574,10 @@ static int cramfs_readpage(struct file *file, struct page * page)
 }
 
 static const struct address_space_operations cramfs_aops = {
-	.readpage = cramfs_readpage
+	.readpage	= cramfs_readpage,
+	.write_begin	= simple_write_begin,
+	.write_end	= simple_write_end,
+	.set_page_dirty = __set_page_dirty_no_writeback,
 };
 
 /*
-- 
1.5.4.3

-- 


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC 6/7] cramfs: read directory entries from dcache
       [not found] <20080531152013.031903990@arndb.de>
                   ` (4 preceding siblings ...)
  2008-05-31 15:20 ` [RFC 5/7] cramfs: allow writing to existing files arnd
@ 2008-05-31 15:20 ` arnd
  2008-05-31 15:20 ` [RFC 7/7] cramfs: add missing inode operations arnd
  6 siblings, 0 replies; 12+ messages in thread
From: arnd @ 2008-05-31 15:20 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, hch

[-- Attachment #1: 0006-cramfs-read-directory-entries-from-dcache.patch --]
[-- Type: text/plain, Size: 3954 bytes --]

When we create new files in a mounted cramfs, they
only appear in the dcache, so we need to have a combined
dcache_readdir plus cramfs_readdir to get all of them.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 fs/cramfs/inode.c |  100 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 97 insertions(+), 3 deletions(-)

diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 0d3ac80..e7d2b47 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -324,6 +324,46 @@ static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 	return 0;
 }
 
+/* Relationship between i_mode and the DT_xxx types */
+static inline unsigned char dt_type(struct inode *inode)
+{
+	return (inode->i_mode >> 12) & 15;
+}
+
+static int cramfs_readdir_cache(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct dentry *dentry = filp->f_path.dentry;
+	struct dentry *cursor = filp->private_data;
+	struct list_head *p, *q = &cursor->d_u.d_child;
+
+	/* taken from dcache_readdir */
+	spin_lock(&dcache_lock);
+	if (filp->f_pos == 2)
+		list_move(q, &dentry->d_subdirs);
+
+	for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
+		struct dentry *next;
+		next = list_entry(p, struct dentry, d_u.d_child);
+		if (d_unhashed(next) || !next->d_inode ||
+		    next->d_inode->i_private) // FIXME: renames are broken
+			continue;
+
+		spin_unlock(&dcache_lock);
+		if (filldir(dirent, next->d_name.name,
+			    next->d_name.len, filp->f_pos,
+			    next->d_inode->i_ino,
+			    dt_type(next->d_inode)) < 0)
+			return 0;
+		spin_lock(&dcache_lock);
+		/* next is still alive */
+		list_move(q, p);
+		p = q;
+		filp->f_pos++;
+	}
+	spin_unlock(&dcache_lock);
+	return 0;
+}
+
 /*
  * Read a cramfs directory entry.
  */
@@ -407,8 +447,59 @@ static int cramfs_readdir_ondisk(struct dentry *dentry, void *dirent,
 
 static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
-	return cramfs_readdir_ondisk(filp->f_dentry, dirent,
+	loff_t offset = filp->f_pos;
+	struct dentry *dentry = filp->f_path.dentry;
+	int ret;
+
+	ret = 0;
+	if (offset < dentry->d_inode->i_size)
+		ret = cramfs_readdir_ondisk(dentry, dirent,
 					filldir, &filp->f_pos);
+	if (ret)
+		return ret;
+
+	return cramfs_readdir_cache(filp, dirent, filldir);
+}
+
+static loff_t cramfs_dir_lseek(struct file *file, loff_t offset, int origin)
+{
+	struct inode *inode = file->f_path.dentry->d_inode;
+	mutex_lock(&inode->i_mutex);
+	switch (origin) {
+		case 1:
+			offset += file->f_pos;
+		case 0:
+			if (offset >= 0)
+				break;
+		default:
+			mutex_unlock(&inode->i_mutex);
+			return -EINVAL;
+	}
+
+	if (offset != file->f_pos) {
+		file->f_pos = offset;
+		if (file->f_pos >= inode->i_size) {
+			struct list_head *p;
+			struct dentry *cursor = file->private_data;
+			loff_t n = file->f_pos - inode->i_size;
+
+			spin_lock(&dcache_lock);
+			list_del(&cursor->d_u.d_child);
+			p = file->f_path.dentry->d_subdirs.next;
+			while (n && p != &file->f_path.dentry->d_subdirs) {
+				struct dentry *next;
+				next = list_entry(p, struct dentry, d_u.d_child);
+				if (!d_unhashed(next) && next->d_inode &&
+				    !next->d_inode->i_private) // FIXME: renames are broken)
+					n--;
+				p = p->next;
+			}
+			list_add_tail(&cursor->d_u.d_child, p);
+			spin_unlock(&dcache_lock);
+		}
+	}
+	mutex_unlock(&inode->i_mutex);
+	return offset;
 }
 
 int cramfs_unlink(struct inode *dir, struct dentry *dentry)
@@ -588,9 +679,12 @@ static const struct address_space_operations cramfs_aops = {
  * A directory can only readdir
  */
 static const struct file_operations cramfs_directory_operations = {
-	.llseek		= generic_file_llseek,
-	.read		= generic_read_dir,
+	.open		= dcache_dir_open,
+	.release	= dcache_dir_close,
+	.llseek		= cramfs_dir_lseek,
+	.fsync		= simple_sync_file,
 	.readdir	= cramfs_readdir,
+	.read		= generic_read_dir,
 };
 
 static const struct inode_operations cramfs_dir_inode_operations = {
-- 
1.5.4.3

-- 


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC 7/7] cramfs: add missing inode operations
       [not found] <20080531152013.031903990@arndb.de>
                   ` (5 preceding siblings ...)
  2008-05-31 15:20 ` [RFC 6/7] cramfs: read directory entries from dcache arnd
@ 2008-05-31 15:20 ` arnd
  6 siblings, 0 replies; 12+ messages in thread
From: arnd @ 2008-05-31 15:20 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, hch

[-- Attachment #1: 0007-cramfs-add-missing-inode-operations.patch --]
[-- Type: text/plain, Size: 2943 bytes --]

This adds support for create, link, symlink, mkdir, mknod
and rename, all of which are relatively simple to do based
on the previous patches.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 fs/cramfs/inode.c |   78 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 78 insertions(+), 0 deletions(-)

diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index e7d2b47..bcdd592 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -557,6 +557,78 @@ int cramfs_rmdir(struct inode *dir, struct dentry *dentry)
 }
 
 /*
+ * File creation. Allocate an inode, and we're done..
+ */
+static int
+cramfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+	struct inode * inode = get_cramfs_inode(dir->i_sb, mode, dev);
+	int error = -ENOSPC;
+
+	if (inode) {
+		if (dir->i_mode & S_ISGID) {
+			inode->i_gid = dir->i_gid;
+			if (S_ISDIR(mode))
+				inode->i_mode |= S_ISGID;
+		}
+		d_instantiate(dentry, inode);
+		dget(dentry);	/* Extra count - pin the dentry in core */
+		error = 0;
+		dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+	}
+	return error;
+}
+
+static int cramfs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+{
+	return cramfs_mknod(dir, dentry, mode | S_IFDIR, 0);
+}
+
+static int cramfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
+{
+	return cramfs_mknod(dir, dentry, mode | S_IFREG, 0);
+}
+
+static int cramfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
+{
+	struct inode *inode;
+	int error = -ENOSPC;
+
+	inode = get_cramfs_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0);
+	if (inode) {
+		int l = strlen(symname)+1;
+		error = page_symlink(inode, symname, l);
+		if (!error) {
+			if (dir->i_mode & S_ISGID)
+				inode->i_gid = dir->i_gid;
+			d_instantiate(dentry, inode);
+			dget(dentry);
+			dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+		} else
+			iput(inode);
+	}
+	return error;
+}
+
+int cramfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+		struct inode *new_dir, struct dentry *new_dentry)
+{
+	struct inode *inode = old_dentry->d_inode;
+
+	if (!cramfs_empty(new_dentry))
+		return -ENOTEMPTY;
+
+	if (new_dentry->d_inode)
+		cramfs_unlink(new_dir, new_dentry);
+
+	old_dir->i_ctime = old_dir->i_mtime = new_dir->i_ctime =
+		new_dir->i_mtime = inode->i_ctime = CURRENT_TIME;
+
+	return 0;
+}
+
+
+/*
  * Lookup and fill in the inode data..
  */
 static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
@@ -688,8 +760,14 @@ static const struct file_operations cramfs_directory_operations = {
 };
 
 static const struct inode_operations cramfs_dir_inode_operations = {
+	.create		= cramfs_create,
+	.link		= simple_link,
 	.unlink		= cramfs_unlink,
+	.symlink	= cramfs_symlink,
+	.mkdir		= cramfs_mkdir,
 	.rmdir		= cramfs_rmdir,
+	.mknod		= cramfs_mknod,
+	.rename		= cramfs_rename,
 	.lookup		= cramfs_lookup,
 };
 
-- 
1.5.4.3

-- 


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [RFC 2/7] cramfs: create unique inode numbers
  2008-05-31 15:20 ` [RFC 2/7] cramfs: create unique inode numbers arnd
@ 2008-06-01 16:50   ` Jörn Engel
  2008-06-01 21:24     ` Arnd Bergmann
  0 siblings, 1 reply; 12+ messages in thread
From: Jörn Engel @ 2008-06-01 16:50 UTC (permalink / raw)
  To: arnd; +Cc: linux-fsdevel, linux-kernel, hch

On Sat, 31 May 2008 17:20:15 +0200, arnd@arndb.de wrote:
> 
> This changes the inode number in cramfs to be based on
> the location of the dentry instead of the file, in order
> to make inodes unique.

Couldn't this cause problems for NFS?  The same inode no longer has a
stable inode number across reboots.  Basing on dentry location can also
be an information leak and cause problems on 64bit machines with old
userspace.

We could keep the original approach and use a static counter otherwise.
Something roughly like this:

static ino_t cramfs_get_ino(struct super_block *sb,
			struct cramfs_inode *cramfs_inode)
{
	static ino_t counter;
	struct inode *inode;

	if (cramfs_inode->offset && cramfs_inode->size)
		return cramfs_inode->offset << 2;

	for (;;) {
		counter++;
		if (!counter & 3)
			counter++; /* skip numbers that could be offsets */
		inode = cramfs_iget(sb, counter);
		if (!inode)
			return counter; /* free to use */
		iput(inode);
	}
}

> This lets us change and unlink files in a later patch
> without changing all other files that contain the same
> data, and it fixes a user-visible bug with 'cp -a'
> trying to hardlink empty directories when copying from
> a cramfs source.
> 
> A slight disadvantage is that identical files no longer
> share a common page cache, so the data has to be read
> from disk for each file individually.

That seems to be solved with above approach as well.

Jörn

-- 
You can take my soul, but not my lack of enthusiasm.
-- Wally

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 3/7] cramfs: allow unlinking of files
  2008-05-31 15:20 ` [RFC 3/7] cramfs: allow unlinking of files arnd
@ 2008-06-01 16:54   ` Jörn Engel
  2008-06-01 21:28     ` Arnd Bergmann
  0 siblings, 1 reply; 12+ messages in thread
From: Jörn Engel @ 2008-06-01 16:54 UTC (permalink / raw)
  To: arnd; +Cc: linux-fsdevel, linux-kernel, hch

On Sat, 31 May 2008 17:20:16 +0200, arnd@arndb.de wrote:
>  
> +int cramfs_unlink(struct inode *dir, struct dentry *dentry)
> +{
> +	struct inode *inode = dentry->d_inode;
> +	struct dentry *new;
> +
> +	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
> +	drop_nlink(inode);
> +	new = d_alloc(dentry->d_parent, &dentry->d_name);
> +	d_add(new, NULL);
> +
> +	dget(dentry);

How is the negative dentry dropped on umount?  Looks like we want a
per-sb list to put them on.  Then we can walk it and dput each one at
umount time.

> +	return 0;
> +}

Jörn

-- 
"[One] doesn't need to know [...] how to cause a headache in order
to take an aspirin."
-- Scott Culp, Manager of the Microsoft Security Response Center, 2001

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 2/7] cramfs: create unique inode numbers
  2008-06-01 16:50   ` Jörn Engel
@ 2008-06-01 21:24     ` Arnd Bergmann
  2008-06-02  5:42       ` Jörn Engel
  0 siblings, 1 reply; 12+ messages in thread
From: Arnd Bergmann @ 2008-06-01 21:24 UTC (permalink / raw)
  To: Jörn Engel; +Cc: linux-fsdevel, linux-kernel, hch

On Sunday 01 June 2008, Jörn Engel wrote:
> On Sat, 31 May 2008 17:20:15 +0200, arnd@arndb.de wrote:
> > 
> > This changes the inode number in cramfs to be based on
> > the location of the dentry instead of the file, in order
> > to make inodes unique.
> 
> Couldn't this cause problems for NFS?  The same inode no longer has a
> stable inode number across reboots.  Basing on dentry location can also
> be an information leak and cause problems on 64bit machines with old
> userspace.

Sorry if I was not clear with this: I meant dentry location on disk,
not in memory. So the inode number is still stable across reboots and
does not leak data, it is just different from before.

> We could keep the original approach and use a static counter otherwise.
> Something roughly like this:

One thing I like about my 2/7 patch is that it actually reduces the amount
of code in the file system, while your solution would increase it, with
otherwise identical behaviour.

	Arnd <><

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 3/7] cramfs: allow unlinking of files
  2008-06-01 16:54   ` Jörn Engel
@ 2008-06-01 21:28     ` Arnd Bergmann
  0 siblings, 0 replies; 12+ messages in thread
From: Arnd Bergmann @ 2008-06-01 21:28 UTC (permalink / raw)
  To: Jörn Engel; +Cc: linux-fsdevel, linux-kernel, hch

On Sunday 01 June 2008, Jörn Engel wrote:
> On Sat, 31 May 2008 17:20:16 +0200, arnd@arndb.de wrote:
> >  
> > +int cramfs_unlink(struct inode *dir, struct dentry *dentry)
> > +{
> > +     struct inode *inode = dentry->d_inode;
> > +     struct dentry *new;
> > +
> > +     inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
> > +     drop_nlink(inode);
> > +     new = d_alloc(dentry->d_parent, &dentry->d_name);
> > +     d_add(new, NULL);
> > +
> > +     dget(dentry);
> 
> How is the negative dentry dropped on umount?  Looks like we want a
> per-sb list to put them on.  Then we can walk it and dput each one at
> umount time.

Good point. I think ramfs does exactly that, using kill_litter_super.
I haven't tested unmount so far, and should probably do something like
that in cramfs_put_super.

	Arnd <><

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 2/7] cramfs: create unique inode numbers
  2008-06-01 21:24     ` Arnd Bergmann
@ 2008-06-02  5:42       ` Jörn Engel
  0 siblings, 0 replies; 12+ messages in thread
From: Jörn Engel @ 2008-06-02  5:42 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-fsdevel, linux-kernel, hch

On Sun, 1 June 2008 23:24:51 +0200, Arnd Bergmann wrote:
> On Sunday 01 June 2008, Jörn Engel wrote:
> > 
> > Couldn't this cause problems for NFS?  The same inode no longer has a
> > stable inode number across reboots.  Basing on dentry location can also
> > be an information leak and cause problems on 64bit machines with old
> > userspace.
> 
> Sorry if I was not clear with this: I meant dentry location on disk,
> not in memory. So the inode number is still stable across reboots and
> does not leak data, it is just different from before.

In that case I'd better retract my whole comment. ;)

Jörn

-- 
All art is but imitation of nature.
-- Lucius Annaeus Seneca

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2008-06-02  5:43 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20080531152013.031903990@arndb.de>
2008-05-31 15:20 ` [RFC 1/7] cramfs: allow remount rw arnd
2008-05-31 15:20 ` [RFC 2/7] cramfs: create unique inode numbers arnd
2008-06-01 16:50   ` Jörn Engel
2008-06-01 21:24     ` Arnd Bergmann
2008-06-02  5:42       ` Jörn Engel
2008-05-31 15:20 ` [RFC 3/7] cramfs: allow unlinking of files arnd
2008-06-01 16:54   ` Jörn Engel
2008-06-01 21:28     ` Arnd Bergmann
2008-05-31 15:20 ` [RFC 4/7] cramfs: allow rmdir arnd
2008-05-31 15:20 ` [RFC 5/7] cramfs: allow writing to existing files arnd
2008-05-31 15:20 ` [RFC 6/7] cramfs: read directory entries from dcache arnd
2008-05-31 15:20 ` [RFC 7/7] cramfs: add missing inode operations arnd

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