blob: 34917bec364591a2f85c1e87ebe1779cf24ce3ae [file] [log] [blame]
/*
* (C) Copyright 2007 Michal Simek
*
* Michal SIMEK <monstr@monstr.eu>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <malloc.h>
#include <command.h>
#if (CONFIG_COMMANDS & CFG_CMD_JFFS2)
#include <asm/byteorder.h>
#include <linux/stat.h>
#include <jffs2/jffs2.h>
#include <jffs2/load_kernel.h>
#undef DEBUG_ROMFS
/* ROMFS superblock */
struct romfs_super {
u32 word0;
u32 word1;
u32 size;
u32 checksum;
char name[0];
};
struct romfs_inode {
u32 next;
u32 spec;
u32 size;
u32 checksum;
char name[0];
};
extern flash_info_t flash_info[];
#define PART_OFFSET(x) (x->offset + flash_info[x->dev->id->num].start[0])
#define ALIGN(x) (((x) & 0xfffffff0))
#define HEADERSIZE(name) (0x20 + ALIGN(strlen(name)))
static unsigned long romfs_resolve (unsigned long begin, unsigned long offset,
unsigned long size, int raw, char *filename)
{
unsigned long inodeoffset = 0, nextoffset;
struct romfs_inode *inode;
#ifdef DEBUG_ROMFS
printf ("ROMFS_resolve: begin 0x%x, offset 0x%x, size 0x%x, raw 0x%x, \
filename %s\n", begin, offset, size, raw, filename);
#endif
while (inodeoffset < size) {
inode = (struct romfs_inode *)(begin + offset + inodeoffset);
offset = 0;
nextoffset = ALIGN (inode->next);
#ifdef DEBUG_ROMFS
printf("inode 0x%x, name %s - len 0x%x, next inode 0x%x, \
compare names 0x%x\n",
inode, inode->name, strlen (inode->name), nextoffset,
strncmp (filename, inode->name, strlen (filename)));
#endif
if (!strncmp (filename, inode->name, strlen (inode->name))) {
char *p = strtok (NULL, "/");
if (raw && (p == NULL || *p == '\0')) {
return offset + inodeoffset;
}
return romfs_resolve (begin,
inodeoffset + HEADERSIZE (inode->name),
size, raw, p);
}
inodeoffset = nextoffset;
}
printf ("can't find corresponding entry\n");
return 0;
}
int romfs_load (char *loadoffset, struct part_info *info, char *filename)
{
struct romfs_inode *inode;
struct romfs_super *sb;
char *data;
int pocet;
sb = (struct romfs_super *) PART_OFFSET (info);
unsigned long offset;
offset = romfs_resolve (PART_OFFSET (info), HEADERSIZE (sb->name),
sb->size, 1, strtok (filename, "/"));
if (offset <= 0)
return offset;
inode = (struct romfs_inode *)(PART_OFFSET (info) + offset);
data = (char *)((int)inode + HEADERSIZE (inode->name));
pocet = inode->size;
while (pocet--) {
*loadoffset++ = *data++;
}
return inode->size;
}
static int romfs_list_inode (struct part_info *info, unsigned long offset)
{
struct romfs_inode *inode =
(struct romfs_inode *)(PART_OFFSET (info) + offset);
struct romfs_inode *hardlink = NULL;
char str[3], *data;
/*
* mapping spec.info means
* 0 hard link link destination [file header]
* 1 directory first file's header
* 2 regular file unused, must be zero [MBZ]
* 3 symbolic link unused, MBZ (file data is the link content)
* 4 block device 16/16 bits major/minor number
* 5 char device - " -
* 6 socket unused, MBZ
* 7 fifo unused, MBZ
*/
char attributes[] = "hdflbcsp";
str[0] = attributes[inode->next & 0x7];
str[1] = (inode->next & 0x8) ? 'x' : '-';
str[2] = '\0';
if ((str[0] == 'b') || (str[0] == 'c')) {
#ifdef DEBUG_ROMFS
printf (" %s %3d,%3d %12s 0x%08x 0x%08x", str,
(inode->spec & 0xffff0000) >> 16,
inode->spec & 0x0000ffff, inode->name, inode,
inode->spec);
#else
printf (" %s %3d,%3d %12s", str,
(inode->spec & 0xffff0000) >> 16,
inode->spec & 0x0000ffff);
#endif
} else {
#ifdef DEBUG_ROMFS
printf (" %s %7d %12s 0x%08x 0x%08x", str, inode->size,
inode->name, inode, inode->spec);
#else
printf (" %s %7d %12s", str, inode->size, inode->name);
#endif
if (str[0] == 'l') {
data = (char *)((int)inode + HEADERSIZE (inode->name));
puts (" -> ");
puts (data);
}
if (str[0] == 'h') {
hardlink = (struct romfs_inode *)(PART_OFFSET (info) +
inode->spec);
puts (" -> ");
puts (hardlink->name);
}
}
puts ("\n");
return ALIGN (inode->next);
}
int romfs_ls (struct part_info *info, char *filename)
{
struct romfs_inode *inode;
unsigned long inodeoffset = 0, nextoffset;
unsigned long offset, size;
struct romfs_super *sb;
sb = (struct romfs_super *)PART_OFFSET (info);
if (strlen (filename) == 0 || !strcmp (filename, "/")) {
offset = HEADERSIZE (sb->name);
size = sb->size;
} else {
offset = romfs_resolve (PART_OFFSET (info),
HEADERSIZE (sb->name), sb->size, 1,
strtok (filename, "/"));
if (offset == 0) {
return offset;
}
inode = (struct romfs_inode *)(PART_OFFSET (info) + offset);
if ((inode->next & 0x7) != 1) {
return (romfs_list_inode (info, offset) > 0);
}
size = sb->size;
offset = offset + HEADERSIZE (inode->name);
}
inodeoffset = offset + inodeoffset;
while (inodeoffset < size) {
nextoffset = romfs_list_inode (info, inodeoffset);
if (nextoffset == 0)
break;
inodeoffset = nextoffset;
}
return 1;
}
int romfs_info (struct part_info *info)
{
struct romfs_super *sb;
sb = (struct romfs_super *)PART_OFFSET (info);
printf ("name: \t\t%s, len %d B\n", sb->name, strlen (sb->name));
printf ("size of SB:\t%d B\n", HEADERSIZE (sb->name));
printf ("full size:\t%d B\n", sb->size);
printf ("checksum:\t0x%x\n", sb->checksum);
return 0;
}
int romfs_check (struct part_info *info)
{
struct romfs_super *sb;
if (info->dev->id->type != MTD_DEV_TYPE_NOR)
return 0;
sb = (struct romfs_super *)PART_OFFSET (info);
if ((sb->word0 != 0x2D726F6D) || (sb->word1 != 0x3166732D)) {
return 0;
}
return 1;
}
#endif