linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Andi Drebes <lists-receive@programmierforen.de>
To: linux-fsdevel@vger.kernel.org
Cc: Christoph Hellwig <hch@infradead.org>,
	Andrew Morton <akpm@linux-foundation.org>,
	Linus Torvalds <torvalds@linux-foundation.org>
Subject: [PATCH 0/2] cramfs: Add mount option "swapendian"
Date: Thu, 15 Nov 2007 21:35:16 +0100	[thread overview]
Message-ID: <200711152135.16932.lists-receive@programmierforen.de> (raw)
In-Reply-To: <200711152129.25686.lists-receive@programmierforen.de>

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@programmierforen.de>
---
 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@programmierforen.de>
+ *	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@programmierforen.de>
+ *
+ * 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)

  reply	other threads:[~2007-11-15 20:35 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-11-15 20:29 [PATCH 0/2] cramfs: support for other endianness Andi Drebes
2007-11-15 20:35 ` Andi Drebes [this message]
2007-11-15 20:45   ` [PATCH 0/2] cramfs: Add mount option "swapendian" Linus Torvalds
2007-11-15 20:46     ` Linus Torvalds
2007-11-15 20:51       ` Christoph Hellwig
2007-11-15 20:49     ` Christoph Hellwig
2007-11-15 20:57       ` Linus Torvalds
2007-11-15 21:15     ` Andi Drebes
2007-11-15 21:37       ` Linus Torvalds
2007-11-15 22:48         ` Phillip Lougher
2007-11-16 10:28           ` Andi Drebes
2007-11-16 15:44             ` Linus Torvalds
2007-11-15 21:03   ` Jörn Engel
2007-11-15 20:37 ` [PATCH 0/2] cramfs: update README file Andi Drebes
2007-11-15 20:43 ` [PATCH 0/2] cramfs: support for other endianness Andi Drebes

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=200711152135.16932.lists-receive@programmierforen.de \
    --to=lists-receive@programmierforen.de \
    --cc=akpm@linux-foundation.org \
    --cc=hch@infradead.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=torvalds@linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).