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 <[EMAIL PROTECTED]>
---
 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 <[EMAIL PROTECTED]>
+ *     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 <[EMAIL PROTECTED]>
+ *
+ * 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 [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to