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)
next prev parent 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).