[PATCH] Add endianness support to cramfs tools

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

 



cramfs is an endianness dependent file system.
So far, the cramfs utilities did not support cramfs
images of different endianness than the host machine.

A separate utility, cramfsswap, was required in order
to change the endianness of the image before and after
using cramfs utilities. The extra utility introduced
extra maintenance and an additional step in the process.

This patch adds endianness support to mkfs.cramfs and
fsck.cramfs. fsck.cramfs now automatically detects the image
endianness, and can work on images of either endianness.
mkfs.cramfs now accepts a new optional parameter (-N) that 
allows creating the cramfs image in either endianness.

Signed-off-by: Roy Peled <the.roy.peled@xxxxxxxxx>

---
diff -urNp util-linux-ng-2.14.1.org/configure.ac util-linux-ng-2.14.1/configure.ac
--- util-linux-ng-2.14.1.org/configure.ac	2008-09-10 13:13:27.000000000 +0300
+++ util-linux-ng-2.14.1/configure.ac	2008-11-12 06:19:13.000000000 +0200
@@ -27,6 +27,7 @@ esac
 AC_PROG_CC_STDC
 AC_GNU_SOURCE
 AC_CANONICAL_HOST
+AC_C_BIGENDIAN
 
 linux_os=no
 case ${host_os} in
diff -urNp util-linux-ng-2.14.1.org/disk-utils/Makefile.am util-linux-ng-2.14.1/disk-utils/Makefile.am
--- util-linux-ng-2.14.1.org/disk-utils/Makefile.am	2008-09-10 12:02:42.000000000 +0300
+++ util-linux-ng-2.14.1/disk-utils/Makefile.am	2008-11-12 06:19:13.000000000 +0200
@@ -35,8 +35,8 @@ endif
 
 if BUILD_CRAMFS
 sbin_PROGRAMS += fsck.cramfs mkfs.cramfs
-fsck_cramfs_SOURCES = fsck.cramfs.c cramfs.h
-mkfs_cramfs_SOURCES = mkfs.cramfs.c cramfs.h ../lib/md5.c
+fsck_cramfs_SOURCES = fsck.cramfs.c cramfs.h cramfs_common.c cramfs_common.h
+mkfs_cramfs_SOURCES = mkfs.cramfs.c cramfs.h cramfs_common.c cramfs_common.h ../lib/md5.c
 fsck_cramfs_LDADD = -lz
 mkfs_cramfs_LDADD = -lz
 endif
diff -urNp util-linux-ng-2.14.1.org/disk-utils/cramfs_common.c util-linux-ng-2.14.1/disk-utils/cramfs_common.c
--- util-linux-ng-2.14.1.org/disk-utils/cramfs_common.c	1970-01-01 02:00:00.000000000 +0200
+++ util-linux-ng-2.14.1/disk-utils/cramfs_common.c	2008-11-12 07:34:32.000000000 +0200
@@ -0,0 +1,120 @@
+/*
+ * cramfs_common - cramfs common code
+ *
+ * Copyright (c) 2008 Roy Peled, the.roy.peled  -at-  gmail.com
+ * Copyright (c) 2004-2006 by Michael Holzt, kju -at- fqdn.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <string.h>
+#include "cramfs_common.h"
+
+/*
+	u32 Endianness swap.
+	This algorithm works on both Big endian machines and Little endian machines.
+*/
+static u32 _swap32(u32 what)
+{
+	int out = 0;
+	int i;
+	for (i=0;i<sizeof(u32);i++) {
+		out <<= 8;
+		out |= what&0xff;
+		what >>= 8;
+	}
+	return out;
+}
+
+u32 u32_toggle_endianness(int big_endian, u32 what)
+{
+	return big_endian == HOST_IS_BIG_ENDIAN ? what : _swap32(what);
+}
+
+void super_toggle_endianness(int big_endian, struct cramfs_super *super)
+{
+	if (big_endian != HOST_IS_BIG_ENDIAN) {
+		super->magic = _swap32(super->magic);
+		super->size = _swap32(super->size);
+		super->flags = _swap32(super->flags);
+		super->future = _swap32(super->future);
+		super->fsid.crc = _swap32(super->fsid.crc);
+		super->fsid.edition = _swap32(super->fsid.edition);
+		super->fsid.blocks = _swap32(super->fsid.blocks);
+		super->fsid.files = _swap32(super->fsid.files);	
+	}
+}
+
+void inode_toggle_endianness(int input_big_endian, int output_big_endian, struct cramfs_inode *inode_in, struct cramfs_inode *inode_out)
+{
+	if (input_big_endian == output_big_endian) {
+		memmove(inode_out, inode_in, sizeof(*inode_out));
+	}
+	else {
+		unsigned char inode_out_buf[sizeof(*inode_in)];
+		unsigned char *inode_in_buf = (unsigned char*)inode_in;
+
+		inode_out_buf[0] = inode_in_buf[1]; /* 16 bit: mode */
+		inode_out_buf[1] = inode_in_buf[0];
+
+		inode_out_buf[2] = inode_in_buf[3]; /* 16 bit: uid */
+		inode_out_buf[3] = inode_in_buf[2]; 
+
+		inode_out_buf[4] = inode_in_buf[6]; /* 24 bit: size */
+		inode_out_buf[5] = inode_in_buf[5];
+		inode_out_buf[6] = inode_in_buf[4];
+
+		inode_out_buf[7] = inode_in_buf[7]; /* 8 bit: gid width */
+
+		/* Stop the madness! Outlaw C bitfields! They are unportable and nasty!
+		See for yourself what a mess this is: */
+
+		if (output_big_endian) {
+			inode_out_buf[ 8] = ( (inode_in_buf[ 8]&0x3F) << 2 ) | 
+				( (inode_in_buf[11]&0xC0) >> 6 );
+
+			inode_out_buf[ 9] = ( (inode_in_buf[11]&0x3F) << 2 ) |
+				( (inode_in_buf[10]&0xC0) >> 6 );
+
+			inode_out_buf[10] = ( (inode_in_buf[10]&0x3F) << 2 ) |
+				( (inode_in_buf[ 9]&0xC0) >> 6 );
+
+			inode_out_buf[11] = ( (inode_in_buf[ 9]&0x3F) << 2 ) |
+				( (inode_in_buf[ 8]&0xC0) >> 6 );
+		}
+		else {
+			inode_out_buf[ 8] = ( (inode_in_buf[ 8]&0xFD) >> 2 ) | 
+				( (inode_in_buf[11]&0x03) << 6 );
+
+			inode_out_buf[ 9] = ( (inode_in_buf[11]&0xFD) >> 2 ) | 
+				( (inode_in_buf[10]&0x03) << 6 );
+
+			inode_out_buf[10] = ( (inode_in_buf[10]&0xFD) >> 2 ) | 
+				( (inode_in_buf[ 9]&0x03) << 6 );
+
+			inode_out_buf[11] = ( (inode_in_buf[ 9]&0xFD) >> 2 ) | 
+				( (inode_in_buf[ 8]&0x03) << 6 );
+		}
+
+		memmove(inode_out, inode_out_buf, sizeof(*inode_out));
+	}
+}
+
+void inode_endianness_to_host(int from_big_endian, struct cramfs_inode *inode_in, struct cramfs_inode *inode_out)
+{
+	inode_toggle_endianness(from_big_endian, HOST_IS_BIG_ENDIAN, inode_in, inode_out);
+}
+
+void inode_endianness_from_host(int to_big_endian, struct cramfs_inode *inode_in, struct cramfs_inode *inode_out)
+{
+	inode_toggle_endianness(HOST_IS_BIG_ENDIAN, to_big_endian, inode_in, inode_out);
+}
diff -urNp util-linux-ng-2.14.1.org/disk-utils/cramfs_common.h util-linux-ng-2.14.1/disk-utils/cramfs_common.h
--- util-linux-ng-2.14.1.org/disk-utils/cramfs_common.h	1970-01-01 02:00:00.000000000 +0200
+++ util-linux-ng-2.14.1/disk-utils/cramfs_common.h	2008-11-12 07:34:31.000000000 +0200
@@ -0,0 +1,37 @@
+/*
+ * cramfs_common - cramfs common code
+ *
+ * Copyright (c) 2008 Roy Peled, the.roy.peled  -at-  gmail
+ * Copyright (c) 2004-2006 by Michael Holzt, kju -at- fqdn.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __CRAMFS_COMMON_H
+#define __CRAMFS_COMMON_H
+
+#include "cramfs.h"
+
+#ifndef HOST_IS_BIG_ENDIAN
+#ifdef WORDS_BIGENDIAN
+#define HOST_IS_BIG_ENDIAN 1
+#else
+#define HOST_IS_BIG_ENDIAN 0
+#endif
+#endif
+
+u32 u32_toggle_endianness(int big_endian, u32 what);
+void super_toggle_endianness(int from_big_endian, struct cramfs_super *super);
+void inode_endianness_to_host(int from_big_endian, struct cramfs_inode *inode_in, struct cramfs_inode *inode_out);
+void inode_endianness_from_host(int to_big_endian, struct cramfs_inode *inode_in, struct cramfs_inode *inode_out);
+
+#endif
diff -urNp util-linux-ng-2.14.1.org/disk-utils/fsck.cramfs.c util-linux-ng-2.14.1/disk-utils/fsck.cramfs.c
--- util-linux-ng-2.14.1.org/disk-utils/fsck.cramfs.c	2008-09-10 12:02:42.000000000 +0300
+++ util-linux-ng-2.14.1/disk-utils/fsck.cramfs.c	2008-11-12 06:19:13.000000000 +0200
@@ -55,6 +55,7 @@
 #include <sys/sysmacros.h>	/* for major, minor */
 
 #include "cramfs.h"
+#include "cramfs_common.h"
 #include "nls.h"
 
 #define BLKGETSIZE _IO(0x12,96) /* return device size */
@@ -64,6 +65,7 @@ static const char *progname = "cramfsck"
 static int fd;			/* ROM image file descriptor */
 static char *filename;		/* ROM image filename */
 struct cramfs_super super;	/* just find the cramfs superblock once */
+static int cramfs_is_big_endian = 0;	/* source is big endian */
 static int opt_verbose = 0;	/* 1 = verbose (-v), 2+ = very verbose (-vv) */
 
 char *extract_dir = NULL;	/* extraction directory (-x) */
@@ -140,6 +142,21 @@ static void die(int status, int syserr, 
 	exit(status);
 }
 
+int get_superblock_endianness(u32 magic)
+{
+	if (magic == CRAMFS_MAGIC) {
+		cramfs_is_big_endian = HOST_IS_BIG_ENDIAN;
+		return 0;
+  	}
+	else if (magic == u32_toggle_endianness(!HOST_IS_BIG_ENDIAN, CRAMFS_MAGIC)) {
+		cramfs_is_big_endian = !HOST_IS_BIG_ENDIAN;
+		return 0;
+  	}
+	else {
+		return -1;
+	}
+}
+
 static void test_super(int *start, size_t *length) {
 	struct stat st;
 
@@ -172,7 +189,7 @@ static void test_super(int *start, size_
 	if (read(fd, &super, sizeof(super)) != sizeof(super)) {
 		die(FSCK_ERROR, 1, "read failed: %s", filename);
 	}
-	if (super.magic == CRAMFS_MAGIC) {
+	if (get_superblock_endianness(super.magic) != -1) {
 		*start = 0;
 	}
 	else if (*length >= (PAD_SIZE + sizeof(super))) {
@@ -180,15 +197,22 @@ static void test_super(int *start, size_
 		if (read(fd, &super, sizeof(super)) != sizeof(super)) {
 			die(FSCK_ERROR, 1, "read failed: %s", filename);
 		}
-		if (super.magic == CRAMFS_MAGIC) {
+		if (get_superblock_endianness(super.magic) != -1) {
 			*start = PAD_SIZE;
 		}
+		else {
+			die(FSCK_UNCORRECTED, 0, "superblock magic not found");
+		}
 	}
-
-	/* superblock tests */
-	if (super.magic != CRAMFS_MAGIC) {
+	else {
 		die(FSCK_UNCORRECTED, 0, "superblock magic not found");
 	}
+	
+	if (opt_verbose) {
+		printf("cramfs endianness is %s\n", cramfs_is_big_endian ? "big" : "little");
+	}
+
+	super_toggle_endianness(cramfs_is_big_endian, &super);
 	if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) {
 		die(FSCK_ERROR, 0, "unsupported filesystem features");
 	}
@@ -314,7 +338,7 @@ static struct cramfs_inode *cramfs_iget(
 	if (!inode) {
 		die(FSCK_ERROR, 1, "malloc failed");
 	}
-	*inode = *i;
+	inode_endianness_to_host(cramfs_is_big_endian, i, inode);
 	return inode;
 }
 
@@ -333,9 +357,10 @@ static void iput(struct cramfs_inode *in
  */
 static struct cramfs_inode *read_super(void)
 {
-	unsigned long offset = super.root.offset << 2;
+	struct cramfs_inode * root = cramfs_iget(&super.root);
+	unsigned long offset = root->offset << 2;
 
-	if (!S_ISDIR(super.root.mode))
+	if (!S_ISDIR(root->mode))
 		die(FSCK_UNCORRECTED, 0, "root inode is not directory");
 	if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
 	    ((offset != sizeof(struct cramfs_super)) &&
@@ -343,7 +368,7 @@ static struct cramfs_inode *read_super(v
 	{
 		die(FSCK_UNCORRECTED, 0, "bad root offset (%lu)", offset);
 	}
-	return cramfs_iget(&super.root);
+	return root;
 }
 
 static int uncompress_block(void *src, int len)
@@ -378,7 +403,7 @@ static void do_uncompress(char *path, in
 
 	do {
 		unsigned long out = page_size;
-		unsigned long next = *(u32 *) romfs_read(offset);
+		unsigned long next = u32_toggle_endianness(cramfs_is_big_endian, *(u32 *) romfs_read(offset));
 
 		if (next > end_data) {
 			end_data = next;
@@ -539,7 +564,7 @@ static void do_symlink(char *path, struc
 {
 	unsigned long offset = i->offset << 2;
 	unsigned long curr = offset + 4;
-	unsigned long next = *(u32 *) romfs_read(offset);
+	unsigned long next = u32_toggle_endianness(cramfs_is_big_endian, *(u32 *) romfs_read(offset));
 	unsigned long size;
 
 	if (offset == 0) {
diff -urNp util-linux-ng-2.14.1.org/disk-utils/mkfs.cramfs.c util-linux-ng-2.14.1/disk-utils/mkfs.cramfs.c
--- util-linux-ng-2.14.1.org/disk-utils/mkfs.cramfs.c	2008-09-10 12:02:42.000000000 +0300
+++ util-linux-ng-2.14.1/disk-utils/mkfs.cramfs.c	2008-11-12 06:19:13.000000000 +0200
@@ -38,6 +38,7 @@
 #include <zlib.h>
 
 #include "cramfs.h"
+#include "cramfs_common.h"
 #include "md5.h"
 #include "nls.h"
 
@@ -55,6 +56,7 @@ static int verbose = 0;
 static unsigned int blksize; /* settable via -b option */
 static long total_blocks = 0, total_nodes = 1; /* pre-count the root node */
 static int image_length = 0;
+static int cramfs_is_big_endian = 0; /* target is big endian */
 
 /*
  * If opt_holes is set, then mkcramfs can create explicit holes in the
@@ -121,7 +123,7 @@ usage(int status) {
 	FILE *stream = status ? stderr : stdout;
 
 	fprintf(stream,
-		_("usage: %s [-h] [-v] [-b blksize] [-e edition] [-i file] "
+		_("usage: %s [-h] [-v] [-b blksize] [-e edition] [-N endian] [-i file] "
 		  "[-n name] dirname outfile\n"
 		  " -h         print this help\n"
 		  " -v         be verbose\n"
@@ -129,6 +131,7 @@ usage(int status) {
 		    "(non-zero exit status)\n"
 		  " -b blksize use this blocksize, must equal page size\n"
 		  " -e edition set edition number (part of fsid)\n"
+		  " -N endian  set cramfs endianness (big|little|host), default host\n"
 		  " -i file    insert a file image into the filesystem "
 		    "(requires >= 2.4.0)\n"
 		  " -n name    set name of cramfs filesystem\n"
@@ -454,17 +457,22 @@ static unsigned int write_superblock(str
 	super->root.size = root->size;
 	super->root.offset = offset >> 2;
 
+	super_toggle_endianness(cramfs_is_big_endian, super);
+	inode_endianness_from_host(cramfs_is_big_endian, &super->root, &super->root);
+
 	return offset;
 }
 
 static void set_data_offset(struct entry *entry, char *base, unsigned long offset)
 {
 	struct cramfs_inode *inode = (struct cramfs_inode *) (base + entry->dir_offset);
+	inode_endianness_to_host(cramfs_is_big_endian, inode, inode);
 	if (offset >= (1 << (2 + CRAMFS_OFFSET_WIDTH))) {
 		fprintf(stderr, _("filesystem too big.  Exiting.\n"));
 		exit(8);
 	}
 	inode->offset = (offset >> 2);
+	inode_endianness_from_host(cramfs_is_big_endian, inode, inode);
 }
 
 
@@ -523,6 +531,7 @@ static unsigned int write_directory_stru
 				entry_stack[stack_entries] = entry;
 				stack_entries++;
 			}
+			inode_endianness_from_host(cramfs_is_big_endian, inode, inode);
 			entry = entry->next;
 		}
 
@@ -630,7 +639,7 @@ do_compress(char *base, unsigned int off
 			exit(8);
 		}
 
-		*(u32 *) (base + offset) = curr;
+		*(u32 *) (base + offset) = u32_toggle_endianness(cramfs_is_big_endian, curr);
 		offset += 4;
 	} while (size);
 
@@ -734,6 +743,7 @@ int main(int argc, char **argv)
 	char const *dirname, *outfile;
 	u32 crc = crc32(0L, Z_NULL, 0);
 	int c;
+	cramfs_is_big_endian = HOST_IS_BIG_ENDIAN; /* default is to use host order */
 
 	blksize = sysconf(_SC_PAGESIZE);
 	total_blocks = 0;
@@ -750,7 +760,7 @@ int main(int argc, char **argv)
 	textdomain(PACKAGE);
 
 	/* command line options */
-	while ((c = getopt(argc, argv, "hb:Ee:i:n:psVvz")) != EOF) {
+	while ((c = getopt(argc, argv, "hb:Ee:i:n:N:psVvz")) != EOF) {
 		switch (c) {
 		case 'h':
 			usage(0);
@@ -765,6 +775,20 @@ int main(int argc, char **argv)
 		case 'e':
 			opt_edition = atoi(optarg);
 			break;
+		case 'N':
+			if (strcmp(optarg, "big") == 0)  {
+				cramfs_is_big_endian = 1;
+			}
+			else if (strcmp(optarg, "little") == 0) {
+				cramfs_is_big_endian = 0;
+			}
+			else if (strcmp(optarg, "host") == 0);	/* default */
+			else 	{
+				perror("invalid endianness given. Must be 'big', 'little', or 'host'");
+				exit(16);
+			}
+
+			break;
 		case 'i':
 			opt_image = optarg;
 			if (lstat(opt_image, &st) < 0) {
@@ -891,7 +915,7 @@ int main(int argc, char **argv)
 
 	/* Put the checksum in. */
 	crc = crc32(crc, (unsigned char *) (rom_image+opt_pad), (offset-opt_pad));
-	((struct cramfs_super *) (rom_image+opt_pad))->fsid.crc = crc;
+	((struct cramfs_super *) (rom_image+opt_pad))->fsid.crc = u32_toggle_endianness(cramfs_is_big_endian, crc);
 	if (verbose)
 		printf(_("CRC: %x\n"), crc);
 
--
To unsubscribe from this list: send the line "unsubscribe util-linux-ng" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux