All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.