Skip to contentSkip to conversation

cramfs: allow remount rw

RFC In review [email protected] 2 participants 12 messages May 31, 2008 – Jun 2, 2008 1 message not in the archive

Subsystems (2)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]

#1 lore raw

RFC 1/7 cramfs: allow remount rw

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.

---
 fs/cramfs/inode.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)
fs/cramfs/inode.c +0 -1
index 0c3b618..08f08f9 100644
@@ -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;
 }
 
Signature · 3 lines
1.5.4.3

-- 
#2 lore raw

RFC 2/7 cramfs: create unique inode numbers

In reply to a message not in the archive

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.

---
 fs/cramfs/inode.c |  100 +++++++++++++++++++++++------------------------------
 1 files changed, 43 insertions(+), 57 deletions(-)
fs/cramfs/inode.c +43 -57
index 08f08f9..8aa04d7 100644
@@ -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) */
Signature · 3 lines
1.5.4.3

-- 
#3 lore raw

Re: [RFC 2/7] cramfs: create unique inode numbers

Quoted 4 lines · On Sat, 31 May 2008 17:20:15 +0200, [email protected] 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);
	}
}
Quoted 9 lines

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

Signature · 6 lines
You can take my soul, but not my lack of enthusiasm.
-- Wally
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#4 lore raw

Re: [RFC 2/7] cramfs: create unique inode numbers

Quoted 10 lines · Jörn Engel
Quoted 4 lines · On Sat, 31 May 2008 17:20:15 +0200, [email protected] 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.

Quoted 2 lines

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 <><

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

#5 lore raw

Re: [RFC 2/7] cramfs: create unique inode numbers

Quoted 10 lines · Arnd Bergmann
Quoted 5 lines · Jörn Engel

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

Signature · 6 lines
All art is but imitation of nature.
-- Lucius Annaeus Seneca
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#6 lore raw

RFC 3/7 cramfs: allow unlinking of files

In reply to a message not in the archive

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.

---
 fs/cramfs/inode.c |   27 ++++++++++++++++++++++++++-
 1 files changed, 26 insertions(+), 1 deletions(-)
fs/cramfs/inode.c +26 -1
index 8aa04d7..6b9f21f 100644
@@ -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,
Signature · 3 lines
1.5.4.3

-- 
#7 lore raw

Re: [RFC 3/7] cramfs: allow unlinking of files

fs/cramfs/inode.c static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir) · line 414

Quoted 12 lines · On Sat, 31 May 2008 17:20:16 +0200, [email protected] wrote:
fs/cramfs/inode.c +11 -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);

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.

fs/cramfs/inode.c static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir) · line 416

Quoted 2 lines
fs/cramfs/inode.c +2 -0
+	return 0;
+}

Jörn

Signature · 7 lines
"[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
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#8 lore raw

Re: [RFC 3/7] cramfs: allow unlinking of files

Quoted 17 lines · Jörn Engel

fs/cramfs/inode.c static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir) · line 414

Quoted 12 lines · On Sat, 31 May 2008 17:20:16 +0200, [email protected] wrote:

fs/cramfs/inode.c +11 -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);

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 <><

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

#9 lore raw

RFC 4/7 cramfs: allow rmdir

In reply to a message not in the archive

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.

---
 fs/cramfs/inode.c |   71 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 61 insertions(+), 10 deletions(-)
fs/cramfs/inode.c +61 -10
index 6b9f21f..8c3e8bb 100644
@@ -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,
 };
 
Signature · 3 lines
1.5.4.3

-- 
#10 lore raw

RFC 5/7 cramfs: allow writing to existing files

In reply to a message not in the archive

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

---
 fs/cramfs/inode.c |   13 +++++++++++--
 1 files changed, 11 insertions(+), 2 deletions(-)
fs/cramfs/inode.c +11 -2
index 8c3e8bb..0d3ac80 100644
@@ -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,
 };
 
 /*
Signature · 3 lines
1.5.4.3

-- 
#11 lore raw

RFC 6/7 cramfs: read directory entries from dcache

In reply to a message not in the archive

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.

---
 fs/cramfs/inode.c |  100 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 97 insertions(+), 3 deletions(-)
fs/cramfs/inode.c +97 -3
index 0d3ac80..e7d2b47 100644
@@ -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 = {
Signature · 3 lines
1.5.4.3

-- 
#12 lore raw

RFC 7/7 cramfs: add missing inode operations

In reply to a message not in the archive

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

---
 fs/cramfs/inode.c |   78 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 78 insertions(+), 0 deletions(-)
fs/cramfs/inode.c +78 -0
index e7d2b47..bcdd592 100644
@@ -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,
 };
 
Signature · 3 lines
1.5.4.3

--