[PATCH 0/2] cramfs: Add mount option "swapendian"

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch introduces the mount option "swapendian" for cramfs.
When this option is set, cramfs is able to mount an image that
was created on a machine whose endianness differs from the mounting
machine's one.
If somebody tries to mount an image with another endianness but
forgets to set this option, cramfs will give a hint for it.

Signed-off-by: Andi Drebes <andi@xxxxxxxxxxxxxxxxxxx>
---
 fs/cramfs/inode.c             |  112 +++++++++++++++++++++++++++++++----------
 include/linux/cramfs_endian.h |   58 +++++++++++++++++++++
 include/linux/cramfs_fs_sb.h  |    1 +
 3 files changed, 144 insertions(+), 27 deletions(-)

diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 350680f..8da03b0 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -4,6 +4,10 @@
  * Copyright (C) 1999 Linus Torvalds.
  *
  * This file is released under the GPL.
+ *
+ * Changelog:
+ *	11/07 - Andi Drebes <andi@xxxxxxxxxxxxxxxxxxx>
+ *	Added mount option "swapendian"
  */
 
 /*
@@ -18,6 +22,7 @@
 #include <linux/string.h>
 #include <linux/blkdev.h>
 #include <linux/cramfs_fs.h>
+#include <linux/cramfs_endian.h>
 #include <linux/slab.h>
 #include <linux/cramfs_fs_sb.h>
 #include <linux/buffer_head.h>
@@ -157,19 +162,24 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned i
 	blocknr = offset >> PAGE_CACHE_SHIFT;
 	offset &= PAGE_CACHE_SIZE - 1;
 
-	/* Check if an existing buffer already has the data.. */
-	for (i = 0; i < READ_BUFFERS; i++) {
-		unsigned int blk_offset;
-
-		if (buffer_dev[i] != sb)
-			continue;
-		if (blocknr < buffer_blocknr[i])
-			continue;
-		blk_offset = (blocknr - buffer_blocknr[i]) << PAGE_CACHE_SHIFT;
-		blk_offset += offset;
-		if (blk_offset + len > BUFFER_SIZE)
-			continue;
-		return read_buffers[i] + blk_offset;
+	/* Caching is disabled if the filesystem's
+	   and the machine's endianness differ. */
+	if(likely(CRAMFS_SB(sb)->endian))
+	{
+		/* Check if an existing buffer already has the data.. */
+		for (i = 0; i < READ_BUFFERS; i++) {
+			unsigned int blk_offset;
+
+			if (buffer_dev[i] != sb)
+				continue;
+			if (blocknr < buffer_blocknr[i])
+				continue;
+			blk_offset = (blocknr - buffer_blocknr[i]) << PAGE_CACHE_SHIFT;
+			blk_offset += offset;
+			if (blk_offset + len > BUFFER_SIZE)
+				continue;
+			return read_buffers[i] + blk_offset;
+		}
 	}
 
 	devsize = mapping->host->i_size >> PAGE_CACHE_SHIFT;
@@ -246,6 +256,14 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
 		return -ENOMEM;
 	sb->s_fs_info = sbi;
 
+	/* assume the right endianness */
+	sbi->endian = 1;
+
+	/* Check mount options:
+	   Does the user want to mount an image with a different endianness? */
+	if(strcmp("swapendian", data) == 0)
+		sbi->endian = 0;
+
 	/* Invalidate the read buffers on mount: think disk change.. */
 	mutex_lock(&read_mutex);
 	for (i = 0; i < READ_BUFFERS; i++)
@@ -256,26 +274,49 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
 	mutex_unlock(&read_mutex);
 
 	/* Do sanity checks on the superblock */
-	if (super.magic != CRAMFS_MAGIC) {
-		/* check for wrong endianess */
-		if (super.magic == CRAMFS_MAGIC_WEND) {
-			if (!silent)
-				printk(KERN_ERR "cramfs: wrong endianess\n");
-			goto out;
-		}
-
+	if (super.magic != CRAMFS_MAGIC && super.magic != CRAMFS_MAGIC_WEND) {
 		/* check at 512 byte offset */
 		mutex_lock(&read_mutex);
 		memcpy(&super, cramfs_read(sb, 512, sizeof(super)), sizeof(super));
 		mutex_unlock(&read_mutex);
-		if (super.magic != CRAMFS_MAGIC) {
-			if (super.magic == CRAMFS_MAGIC_WEND && !silent)
-				printk(KERN_ERR "cramfs: wrong endianess\n");
-			else if (!silent)
+
+		if (super.magic == CRAMFS_MAGIC_WEND) {
+			goto other_endian;
+		}
+		else if (super.magic != CRAMFS_MAGIC) {
+			if (!silent)
 				printk(KERN_ERR "cramfs: wrong magic\n");
+
 			goto out;
 		}
 	}
+	/* check for wrong endianess */
+	else if (super.magic == CRAMFS_MAGIC_WEND)
+	{
+other_endian:
+		if (sbi->endian) {
+			if (!silent) {
+				printk(KERN_ERR "cramfs: it seems as if you were trying to mount a filesystem "
+					"whose endianness does not match the host's one. You might want to try "
+					"the option \"swapendian\" when mounting the filesystem.\n");
+				printk(KERN_ERR "cramfs: the filesystem will not be mounted.\n");
+			}
+			goto out;
+		}
+		else {
+			if (!sbi->endian) {
+				if (!silent)
+					printk(KERN_INFO "cramfs: mounting cramfs with another endianness\n");
+				CRAMFS_CONVERT_SUPER(super);
+			}
+		}
+	}
+	else if (super.magic == CRAMFS_MAGIC && !sbi->endian)
+	{
+		printk(KERN_ERR "cramfs: you are trying to mount a filesystem whose endianness matches the "
+			"host's one. Do not use the option \"swapendian\".\n");
+		goto out;
+	}
 
 	/* get feature flags first */
 	if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) {
@@ -352,6 +393,7 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 	char *buf;
 	unsigned int offset;
 	int copied;
+	unsigned long endian = CRAMFS_SB(filp->f_path.dentry->d_inode->i_sb)->endian;
 
 	/* Offset within the thing. */
 	offset = filp->f_pos;
@@ -377,6 +419,8 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 		mutex_lock(&read_mutex);
 		de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN);
 		name = (char *)(de+1);
+		if(unlikely(!endian))
+			CRAMFS_CONVERT_INODE(de);
 
 		/*
 		 * Namelengths on disk are shifted by two
@@ -417,8 +461,10 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, s
 {
 	unsigned int offset = 0;
 	int sorted;
+	unsigned long endian;
 
 	mutex_lock(&read_mutex);
+	endian = CRAMFS_SB(dir->i_sb)->endian;
 	sorted = CRAMFS_SB(dir->i_sb)->flags & CRAMFS_FLAG_SORTED_DIRS;
 	while (offset < dir->i_size) {
 		struct cramfs_inode *de;
@@ -427,6 +473,8 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, s
 
 		de = cramfs_read(dir->i_sb, OFFSET(dir) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN);
 		name = (char *)(de+1);
+		if(unlikely(!endian))
+			CRAMFS_CONVERT_INODE(de);
 
 		/* Try to take advantage of sorted directories */
 		if (sorted && (dentry->d_name.name[0] < name[0]))
@@ -473,6 +521,7 @@ static int cramfs_readpage(struct file *file, struct page * page)
 	struct inode *inode = page->mapping->host;
 	u32 maxblock, bytes_filled;
 	void *pgdata;
+	unsigned long endian = CRAMFS_SB(inode->i_sb)->endian;
 
 	maxblock = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
 	bytes_filled = 0;
@@ -483,9 +532,18 @@ static int cramfs_readpage(struct file *file, struct page * page)
 
 		start_offset = OFFSET(inode) + maxblock*4;
 		mutex_lock(&read_mutex);
-		if (page->index)
+		if (page->index) {
 			start_offset = *(u32 *) cramfs_read(sb, blkptr_offset-4, 4);
-		compr_len = (*(u32 *) cramfs_read(sb, blkptr_offset, 4) - start_offset);
+
+			if(unlikely(!endian))
+				start_offset = CPU_ENDIAN_32(start_offset);
+		}
+
+		if(unlikely(!endian))
+			compr_len = CPU_ENDIAN_32(*(u32 *) cramfs_read(sb, blkptr_offset, 4)) - start_offset;
+		else
+			compr_len = (*(u32 *) cramfs_read(sb, blkptr_offset, 4) - start_offset);
+
 		mutex_unlock(&read_mutex);
 		pgdata = kmap(page);
 		if (compr_len == 0)
diff --git a/include/linux/cramfs_endian.h b/include/linux/cramfs_endian.h
new file mode 100644
index 0000000..9b90b26
--- /dev/null
+++ b/include/linux/cramfs_endian.h
@@ -0,0 +1,58 @@
+/*
+ * cramfs_endian.h provides definitions used when mounting
+ * a cram filesystem whose endianness doesn't match the host's
+ * one.
+ *
+ * Copyright 2007 (C) Andi Drebes <andi@xxxxxxxxxxxxxxxxxxx>
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef __CRAMFS_ENDIAN_H
+#define __CRAMFS_ENDIAN_H
+
+#ifdef __LITTLE_ENDIAN
+	#define CPU_ENDIAN_32(x) (be32_to_cpu(x))
+	#define CPU_ENDIAN_16(x) (be16_to_cpu(x))
+#elif defined __BIG_ENDIAN
+	#define CPU_ENDIAN_32(x) (le32_to_cpu(x))
+	#define CPU_ENDIAN_16(x) (le16_to_cpu(x))
+#else
+	#error "Neither __LITTLE_ENDIAN nor __BIG_ENDIAN is defined!"
+#endif
+
+/* Converts a cramfs_info from the wrong endianess
+   to host endianess. */
+#define CRAMFS_CONVERT_INFO(info) \
+	do { \
+		(info).crc = CPU_ENDIAN_32((info).crc); \
+		(info).edition = CPU_ENDIAN_32((info).edition); \
+		(info).blocks = CPU_ENDIAN_32((info).blocks); \
+		(info).files = CPU_ENDIAN_32((info).files); \
+	} while(0)
+
+/* Converts a cramfs_info from the wrong endianess
+   to host endianess. */
+#define CRAMFS_CONVERT_INODE(inode) \
+	do { \
+		__u8* ptr = (__u8*)(inode);\
+		(inode)->mode = CPU_ENDIAN_16((inode)->mode); \
+		(inode)->uid = CPU_ENDIAN_16((inode)->uid); \
+		(inode)->size = (ptr[4] << 16) | (ptr[5] << 8) | (ptr[6]) ; \
+		(inode)->offset = ((ptr[8] & 0x03) << 24) | (ptr[9] << 16) | (ptr[10] << 8) | ptr[11]; \
+		(inode)->namelen = (ptr[8] & 0x3f) >> 2; \
+	} while(0)
+
+/* Converts a cramfs superblock from the wrong endianess
+   to host endianess. */
+#define CRAMFS_CONVERT_SUPER(super) \
+	do { \
+		(super).magic = CPU_ENDIAN_32((super).magic); \
+		(super).size = CPU_ENDIAN_32((super).size); \
+		(super).flags = CPU_ENDIAN_32((super).flags); \
+		(super).future = CPU_ENDIAN_32((super).future); \
+		CRAMFS_CONVERT_INFO((super).fsid); \
+		CRAMFS_CONVERT_INODE(&(super).root); \
+	} while(0)
+
+#endif //__CRAMFS_ENDIAN_H
diff --git a/include/linux/cramfs_fs_sb.h b/include/linux/cramfs_fs_sb.h
index 8390693..dda8e09 100644
--- a/include/linux/cramfs_fs_sb.h
+++ b/include/linux/cramfs_fs_sb.h
@@ -10,6 +10,7 @@ struct cramfs_sb_info {
 			unsigned long blocks;
 			unsigned long files;
 			unsigned long flags;
+			unsigned long endian; /* 1: host endian */
 };
 
 static inline struct cramfs_sb_info *CRAMFS_SB(struct super_block *sb)
-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux