mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-03 21:48:15 +00:00 
			
		
		
		
	As part of bringing the master branch back in to next, we need to allow for all of these changes to exist here. Reported-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Tom Rini <trini@konsulko.com>
		
			
				
	
	
		
			385 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			385 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * cramfs.c
 | 
						|
 *
 | 
						|
 * Copyright (C) 1999 Linus Torvalds
 | 
						|
 *
 | 
						|
 * Copyright (C) 2000-2002 Transmeta Corporation
 | 
						|
 *
 | 
						|
 * Copyright (C) 2003 Kai-Uwe Bloem,
 | 
						|
 * Auerswald GmbH & Co KG, <linux-development@auerswald.de>
 | 
						|
 * - adapted from the www.tuxbox.org u-boot tree, added "ls" command
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU General Public License (Version 2) as
 | 
						|
 * published by the Free Software Foundation.
 | 
						|
 *
 | 
						|
 * Compressed ROM filesystem for Linux.
 | 
						|
 *
 | 
						|
 * TODO:
 | 
						|
 * add support for resolving symbolic links
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * These are the VFS interfaces to the compressed ROM filesystem.
 | 
						|
 * The actual compression is based on zlib, see the other files.
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <malloc.h>
 | 
						|
#include <asm/byteorder.h>
 | 
						|
#include <linux/stat.h>
 | 
						|
#include <jffs2/jffs2.h>
 | 
						|
#include <jffs2/load_kernel.h>
 | 
						|
#include <cramfs/cramfs_fs.h>
 | 
						|
 | 
						|
/* These two macros may change in future, to provide better st_ino
 | 
						|
   semantics. */
 | 
						|
#define CRAMINO(x)	(CRAMFS_GET_OFFSET(x) ? CRAMFS_GET_OFFSET(x)<<2 : 1)
 | 
						|
#define OFFSET(x)	((x)->i_ino)
 | 
						|
 | 
						|
struct cramfs_super super;
 | 
						|
 | 
						|
/* CPU address space offset calculation macro, struct part_info offset is
 | 
						|
 * device address space offset, so we need to shift it by a device start address. */
 | 
						|
#if defined(CONFIG_MTD_NOR_FLASH)
 | 
						|
#include <flash.h>
 | 
						|
#define PART_OFFSET(x)	((ulong)x->offset + \
 | 
						|
			 flash_info[x->dev->id->num].start[0])
 | 
						|
#else
 | 
						|
#define PART_OFFSET(x)	((ulong)x->offset)
 | 
						|
#endif
 | 
						|
 | 
						|
static int cramfs_uncompress (unsigned long begin, unsigned long offset,
 | 
						|
			      unsigned long loadoffset);
 | 
						|
 | 
						|
static int cramfs_read_super (struct part_info *info)
 | 
						|
{
 | 
						|
	unsigned long root_offset;
 | 
						|
 | 
						|
	/* Read the first block and get the superblock from it */
 | 
						|
	memcpy (&super, (void *) PART_OFFSET(info), sizeof (super));
 | 
						|
 | 
						|
	/* Do sanity checks on the superblock */
 | 
						|
	if (super.magic != CRAMFS_32 (CRAMFS_MAGIC)) {
 | 
						|
		/* check at 512 byte offset */
 | 
						|
		memcpy (&super, (void *) PART_OFFSET(info) + 512, sizeof (super));
 | 
						|
		if (super.magic != CRAMFS_32 (CRAMFS_MAGIC)) {
 | 
						|
			printf ("cramfs: wrong magic\n");
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* flags is reused several times, so swab it once */
 | 
						|
	super.flags = CRAMFS_32 (super.flags);
 | 
						|
	super.size = CRAMFS_32 (super.size);
 | 
						|
 | 
						|
	/* get feature flags first */
 | 
						|
	if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) {
 | 
						|
		printf ("cramfs: unsupported filesystem features\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Check that the root inode is in a sane state */
 | 
						|
	if (!S_ISDIR (CRAMFS_16 (super.root.mode))) {
 | 
						|
		printf ("cramfs: root is not a directory\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	root_offset = CRAMFS_GET_OFFSET (&(super.root)) << 2;
 | 
						|
	if (root_offset == 0) {
 | 
						|
		printf ("cramfs: empty filesystem");
 | 
						|
	} else if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
 | 
						|
		   ((root_offset != sizeof (struct cramfs_super)) &&
 | 
						|
		    (root_offset != 512 + sizeof (struct cramfs_super)))) {
 | 
						|
		printf ("cramfs: bad root offset %lu\n", root_offset);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Unpack to an allocated buffer, trusting in the inode's size field. */
 | 
						|
static char *cramfs_uncompress_link (unsigned long begin, unsigned long offset)
 | 
						|
{
 | 
						|
	struct cramfs_inode *inode = (struct cramfs_inode *)(begin + offset);
 | 
						|
	unsigned long size = CRAMFS_24 (inode->size);
 | 
						|
	char *link = malloc (size + 1);
 | 
						|
 | 
						|
	if (!link || cramfs_uncompress (begin, offset, (unsigned long)link) != size) {
 | 
						|
		free (link);
 | 
						|
		link = NULL;
 | 
						|
	} else {
 | 
						|
		link[size] = '\0';
 | 
						|
	}
 | 
						|
	return link;
 | 
						|
}
 | 
						|
 | 
						|
static unsigned long cramfs_resolve (unsigned long begin, unsigned long offset,
 | 
						|
				     unsigned long size, int raw,
 | 
						|
				     char *filename)
 | 
						|
{
 | 
						|
	unsigned long inodeoffset = 0, nextoffset;
 | 
						|
 | 
						|
	while (inodeoffset < size) {
 | 
						|
		struct cramfs_inode *inode;
 | 
						|
		char *name;
 | 
						|
		int namelen;
 | 
						|
 | 
						|
		inode = (struct cramfs_inode *) (begin + offset +
 | 
						|
						 inodeoffset);
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Namelengths on disk are shifted by two
 | 
						|
		 * and the name padded out to 4-byte boundaries
 | 
						|
		 * with zeroes.
 | 
						|
		 */
 | 
						|
		namelen = CRAMFS_GET_NAMELEN (inode) << 2;
 | 
						|
		name = (char *) inode + sizeof (struct cramfs_inode);
 | 
						|
 | 
						|
		nextoffset =
 | 
						|
			inodeoffset + sizeof (struct cramfs_inode) + namelen;
 | 
						|
 | 
						|
		for (;;) {
 | 
						|
			if (!namelen)
 | 
						|
				return -1;
 | 
						|
			if (name[namelen - 1])
 | 
						|
				break;
 | 
						|
			namelen--;
 | 
						|
		}
 | 
						|
 | 
						|
		if (!strncmp(filename, name, namelen) &&
 | 
						|
		    (namelen == strlen(filename))) {
 | 
						|
			char *p = strtok (NULL, "/");
 | 
						|
 | 
						|
			if (raw && (p == NULL || *p == '\0'))
 | 
						|
				return offset + inodeoffset;
 | 
						|
 | 
						|
			if (S_ISDIR (CRAMFS_16 (inode->mode))) {
 | 
						|
				return cramfs_resolve (begin,
 | 
						|
						       CRAMFS_GET_OFFSET
 | 
						|
						       (inode) << 2,
 | 
						|
						       CRAMFS_24 (inode->
 | 
						|
								  size), raw,
 | 
						|
						       p);
 | 
						|
			} else if (S_ISREG (CRAMFS_16 (inode->mode))) {
 | 
						|
				return offset + inodeoffset;
 | 
						|
			} else if (S_ISLNK (CRAMFS_16 (inode->mode))) {
 | 
						|
				unsigned long ret;
 | 
						|
				char *link;
 | 
						|
				if (p && strlen(p)) {
 | 
						|
					printf ("unsupported symlink to non-terminal path\n");
 | 
						|
					return 0;
 | 
						|
				}
 | 
						|
				link = cramfs_uncompress_link (begin,
 | 
						|
						offset + inodeoffset);
 | 
						|
				if (!link) {
 | 
						|
					printf ("%*.*s: Error reading link\n",
 | 
						|
						namelen, namelen, name);
 | 
						|
					return 0;
 | 
						|
				} else if (link[0] == '/') {
 | 
						|
					printf ("unsupported symlink to absolute path\n");
 | 
						|
					free (link);
 | 
						|
					return 0;
 | 
						|
				}
 | 
						|
				ret = cramfs_resolve (begin,
 | 
						|
						      offset,
 | 
						|
						      size,
 | 
						|
						      raw,
 | 
						|
						      strtok(link, "/"));
 | 
						|
				free (link);
 | 
						|
				return ret;
 | 
						|
			} else {
 | 
						|
				printf ("%*.*s: unsupported file type (%x)\n",
 | 
						|
					namelen, namelen, name,
 | 
						|
					CRAMFS_16 (inode->mode));
 | 
						|
				return 0;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		inodeoffset = nextoffset;
 | 
						|
	}
 | 
						|
 | 
						|
	printf ("can't find corresponding entry\n");
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int cramfs_uncompress (unsigned long begin, unsigned long offset,
 | 
						|
			      unsigned long loadoffset)
 | 
						|
{
 | 
						|
	struct cramfs_inode *inode = (struct cramfs_inode *) (begin + offset);
 | 
						|
	u32 *block_ptrs = (u32 *)
 | 
						|
		(begin + (CRAMFS_GET_OFFSET (inode) << 2));
 | 
						|
	unsigned long curr_block = (CRAMFS_GET_OFFSET (inode) +
 | 
						|
				    (((CRAMFS_24 (inode->size)) +
 | 
						|
				      4095) >> 12)) << 2;
 | 
						|
	int size, total_size = 0;
 | 
						|
	int i;
 | 
						|
 | 
						|
	cramfs_uncompress_init ();
 | 
						|
 | 
						|
	for (i = 0; i < ((CRAMFS_24 (inode->size) + 4095) >> 12); i++) {
 | 
						|
		size = cramfs_uncompress_block ((void *) loadoffset,
 | 
						|
						(void *) (begin + curr_block),
 | 
						|
						(CRAMFS_32 (block_ptrs[i]) -
 | 
						|
						 curr_block));
 | 
						|
		if (size < 0)
 | 
						|
			return size;
 | 
						|
		loadoffset += size;
 | 
						|
		total_size += size;
 | 
						|
		curr_block = CRAMFS_32 (block_ptrs[i]);
 | 
						|
	}
 | 
						|
 | 
						|
	cramfs_uncompress_exit ();
 | 
						|
	return total_size;
 | 
						|
}
 | 
						|
 | 
						|
int cramfs_load (char *loadoffset, struct part_info *info, char *filename)
 | 
						|
{
 | 
						|
	unsigned long offset;
 | 
						|
 | 
						|
	if (cramfs_read_super (info))
 | 
						|
		return -1;
 | 
						|
 | 
						|
	offset = cramfs_resolve (PART_OFFSET(info),
 | 
						|
				 CRAMFS_GET_OFFSET (&(super.root)) << 2,
 | 
						|
				 CRAMFS_24 (super.root.size), 0,
 | 
						|
				 strtok (filename, "/"));
 | 
						|
 | 
						|
	if (offset <= 0)
 | 
						|
		return offset;
 | 
						|
 | 
						|
	return cramfs_uncompress (PART_OFFSET(info), offset,
 | 
						|
				  (unsigned long) loadoffset);
 | 
						|
}
 | 
						|
 | 
						|
static int cramfs_list_inode (struct part_info *info, unsigned long offset)
 | 
						|
{
 | 
						|
	struct cramfs_inode *inode = (struct cramfs_inode *)
 | 
						|
		(PART_OFFSET(info) + offset);
 | 
						|
	char *name, str[20];
 | 
						|
	int namelen, nextoff;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Namelengths on disk are shifted by two
 | 
						|
	 * and the name padded out to 4-byte boundaries
 | 
						|
	 * with zeroes.
 | 
						|
	 */
 | 
						|
	namelen = CRAMFS_GET_NAMELEN (inode) << 2;
 | 
						|
	name = (char *) inode + sizeof (struct cramfs_inode);
 | 
						|
	nextoff = namelen;
 | 
						|
 | 
						|
	for (;;) {
 | 
						|
		if (!namelen)
 | 
						|
			return namelen;
 | 
						|
		if (name[namelen - 1])
 | 
						|
			break;
 | 
						|
		namelen--;
 | 
						|
	}
 | 
						|
 | 
						|
	printf (" %s %8d %*.*s", mkmodestr (CRAMFS_16 (inode->mode), str),
 | 
						|
		CRAMFS_24 (inode->size), namelen, namelen, name);
 | 
						|
 | 
						|
	if ((CRAMFS_16 (inode->mode) & S_IFMT) == S_IFLNK) {
 | 
						|
		char *link = cramfs_uncompress_link (PART_OFFSET(info), offset);
 | 
						|
		if (link)
 | 
						|
			printf (" -> %s\n", link);
 | 
						|
		else
 | 
						|
			printf (" [Error reading link]\n");
 | 
						|
		free (link);
 | 
						|
	} else
 | 
						|
		printf ("\n");
 | 
						|
 | 
						|
	return nextoff;
 | 
						|
}
 | 
						|
 | 
						|
int cramfs_ls (struct part_info *info, char *filename)
 | 
						|
{
 | 
						|
	struct cramfs_inode *inode;
 | 
						|
	unsigned long inodeoffset = 0, nextoffset;
 | 
						|
	unsigned long offset, size;
 | 
						|
 | 
						|
	if (cramfs_read_super (info))
 | 
						|
		return -1;
 | 
						|
 | 
						|
	if (strlen (filename) == 0 || !strcmp (filename, "/")) {
 | 
						|
		/* Root directory. Use root inode in super block */
 | 
						|
		offset = CRAMFS_GET_OFFSET (&(super.root)) << 2;
 | 
						|
		size = CRAMFS_24 (super.root.size);
 | 
						|
	} else {
 | 
						|
		/* Resolve the path */
 | 
						|
		offset = cramfs_resolve (PART_OFFSET(info),
 | 
						|
					 CRAMFS_GET_OFFSET (&(super.root)) <<
 | 
						|
					 2, CRAMFS_24 (super.root.size), 1,
 | 
						|
					 strtok (filename, "/"));
 | 
						|
 | 
						|
		if (offset <= 0)
 | 
						|
			return offset;
 | 
						|
 | 
						|
		/* Resolving was successful. Examine the inode */
 | 
						|
		inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset);
 | 
						|
		if (!S_ISDIR (CRAMFS_16 (inode->mode))) {
 | 
						|
			/* It's not a directory - list it, and that's that */
 | 
						|
			return (cramfs_list_inode (info, offset) > 0);
 | 
						|
		}
 | 
						|
 | 
						|
		/* It's a directory. List files within */
 | 
						|
		offset = CRAMFS_GET_OFFSET (inode) << 2;
 | 
						|
		size = CRAMFS_24 (inode->size);
 | 
						|
	}
 | 
						|
 | 
						|
	/* List the given directory */
 | 
						|
	while (inodeoffset < size) {
 | 
						|
		inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset +
 | 
						|
						 inodeoffset);
 | 
						|
 | 
						|
		nextoffset = cramfs_list_inode (info, offset + inodeoffset);
 | 
						|
		if (nextoffset == 0)
 | 
						|
			break;
 | 
						|
		inodeoffset += sizeof (struct cramfs_inode) + nextoffset;
 | 
						|
	}
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
int cramfs_info (struct part_info *info)
 | 
						|
{
 | 
						|
	if (cramfs_read_super (info))
 | 
						|
		return 0;
 | 
						|
 | 
						|
	printf ("size: 0x%x (%u)\n", super.size, super.size);
 | 
						|
 | 
						|
	if (super.flags != 0) {
 | 
						|
		printf ("flags:\n");
 | 
						|
		if (super.flags & CRAMFS_FLAG_FSID_VERSION_2)
 | 
						|
			printf ("\tFSID version 2\n");
 | 
						|
		if (super.flags & CRAMFS_FLAG_SORTED_DIRS)
 | 
						|
			printf ("\tsorted dirs\n");
 | 
						|
		if (super.flags & CRAMFS_FLAG_HOLES)
 | 
						|
			printf ("\tholes\n");
 | 
						|
		if (super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET)
 | 
						|
			printf ("\tshifted root offset\n");
 | 
						|
	}
 | 
						|
 | 
						|
	printf ("fsid:\n\tcrc: 0x%x\n\tedition: 0x%x\n",
 | 
						|
		super.fsid.crc, super.fsid.edition);
 | 
						|
	printf ("name: %16s\n", super.name);
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
int cramfs_check (struct part_info *info)
 | 
						|
{
 | 
						|
	struct cramfs_super *sb;
 | 
						|
 | 
						|
	if (info->dev->id->type != MTD_DEV_TYPE_NOR)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	sb = (struct cramfs_super *) PART_OFFSET(info);
 | 
						|
	if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC)) {
 | 
						|
		/* check at 512 byte offset */
 | 
						|
		sb = (struct cramfs_super *) (PART_OFFSET(info) + 512);
 | 
						|
		if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC))
 | 
						|
			return 0;
 | 
						|
	}
 | 
						|
	return 1;
 | 
						|
}
 |