1dd875c76Swdenk /* 2dd875c76Swdenk * cramfs.c 3dd875c76Swdenk * 4dd875c76Swdenk * Copyright (C) 1999 Linus Torvalds 5dd875c76Swdenk * 6dd875c76Swdenk * Copyright (C) 2000-2002 Transmeta Corporation 7dd875c76Swdenk * 8dd875c76Swdenk * Copyright (C) 2003 Kai-Uwe Bloem, 9dd875c76Swdenk * Auerswald GmbH & Co KG, <linux-development@auerswald.de> 10dd875c76Swdenk * - adapted from the www.tuxbox.org u-boot tree, added "ls" command 11dd875c76Swdenk * 12dd875c76Swdenk * This program is free software; you can redistribute it and/or modify 13dd875c76Swdenk * it under the terms of the GNU General Public License (Version 2) as 14dd875c76Swdenk * published by the Free Software Foundation. 15dd875c76Swdenk * 16dd875c76Swdenk * Compressed ROM filesystem for Linux. 17dd875c76Swdenk * 18dd875c76Swdenk * TODO: 19dd875c76Swdenk * add support for resolving symbolic links 20dd875c76Swdenk */ 21dd875c76Swdenk 22dd875c76Swdenk /* 23dd875c76Swdenk * These are the VFS interfaces to the compressed ROM filesystem. 24dd875c76Swdenk * The actual compression is based on zlib, see the other files. 25dd875c76Swdenk */ 26dd875c76Swdenk 27dd875c76Swdenk #include <common.h> 28dd875c76Swdenk #include <malloc.h> 29dd875c76Swdenk 30dd875c76Swdenk #if (CONFIG_COMMANDS & CFG_CMD_JFFS2) 31dd875c76Swdenk 32dd875c76Swdenk #include <asm/byteorder.h> 33dd875c76Swdenk #include <linux/stat.h> 34180d3f74Swdenk #include <jffs2/jffs2.h> 35dd875c76Swdenk #include <jffs2/load_kernel.h> 36180d3f74Swdenk #include <cramfs/cramfs_fs.h> 37dd875c76Swdenk 38dd875c76Swdenk /* These two macros may change in future, to provide better st_ino 39dd875c76Swdenk semantics. */ 40dd875c76Swdenk #define CRAMINO(x) (CRAMFS_GET_OFFSET(x) ? CRAMFS_GET_OFFSET(x)<<2 : 1) 41dd875c76Swdenk #define OFFSET(x) ((x)->i_ino) 42dd875c76Swdenk 43dd875c76Swdenk struct cramfs_super super; 44dd875c76Swdenk 45*700a0c64SWolfgang Denk /* CPU address space offset calculation macro, struct part_info offset is 46*700a0c64SWolfgang Denk * device address space offset, so we need to shift it by a device start address. */ 47*700a0c64SWolfgang Denk extern flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; 48*700a0c64SWolfgang Denk #define PART_OFFSET(x) (x->offset + flash_info[x->dev->id->num].start[0]) 49*700a0c64SWolfgang Denk 50dd875c76Swdenk static int cramfs_read_super (struct part_info *info) 51dd875c76Swdenk { 52dd875c76Swdenk unsigned long root_offset; 53dd875c76Swdenk 54dd875c76Swdenk /* Read the first block and get the superblock from it */ 55*700a0c64SWolfgang Denk memcpy (&super, (void *) PART_OFFSET(info), sizeof (super)); 56dd875c76Swdenk 57dd875c76Swdenk /* Do sanity checks on the superblock */ 58dd875c76Swdenk if (super.magic != CRAMFS_32 (CRAMFS_MAGIC)) { 59dd875c76Swdenk /* check at 512 byte offset */ 60*700a0c64SWolfgang Denk memcpy (&super, (void *) PART_OFFSET(info) + 512, sizeof (super)); 61dd875c76Swdenk if (super.magic != CRAMFS_32 (CRAMFS_MAGIC)) { 62dd875c76Swdenk printf ("cramfs: wrong magic\n"); 63dd875c76Swdenk return -1; 64dd875c76Swdenk } 65dd875c76Swdenk } 66dd875c76Swdenk 67dd875c76Swdenk /* flags is reused several times, so swab it once */ 68dd875c76Swdenk super.flags = CRAMFS_32 (super.flags); 69dd875c76Swdenk super.size = CRAMFS_32 (super.size); 70dd875c76Swdenk 71dd875c76Swdenk /* get feature flags first */ 72dd875c76Swdenk if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) { 73dd875c76Swdenk printf ("cramfs: unsupported filesystem features\n"); 74dd875c76Swdenk return -1; 75dd875c76Swdenk } 76dd875c76Swdenk 77dd875c76Swdenk /* Check that the root inode is in a sane state */ 78dd875c76Swdenk if (!S_ISDIR (CRAMFS_16 (super.root.mode))) { 79dd875c76Swdenk printf ("cramfs: root is not a directory\n"); 80dd875c76Swdenk return -1; 81dd875c76Swdenk } 82dd875c76Swdenk root_offset = CRAMFS_GET_OFFSET (&(super.root)) << 2; 83dd875c76Swdenk if (root_offset == 0) { 84dd875c76Swdenk printf ("cramfs: empty filesystem"); 85dd875c76Swdenk } else if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) && 86dd875c76Swdenk ((root_offset != sizeof (struct cramfs_super)) && 87dd875c76Swdenk (root_offset != 512 + sizeof (struct cramfs_super)))) { 88dd875c76Swdenk printf ("cramfs: bad root offset %lu\n", root_offset); 89dd875c76Swdenk return -1; 90dd875c76Swdenk } 91dd875c76Swdenk 92dd875c76Swdenk return 0; 93dd875c76Swdenk } 94dd875c76Swdenk 95*700a0c64SWolfgang Denk static unsigned long cramfs_resolve (unsigned long begin, unsigned long offset, 96dd875c76Swdenk unsigned long size, int raw, 97dd875c76Swdenk char *filename) 98dd875c76Swdenk { 99dd875c76Swdenk unsigned long inodeoffset = 0, nextoffset; 100dd875c76Swdenk 101dd875c76Swdenk while (inodeoffset < size) { 102dd875c76Swdenk struct cramfs_inode *inode; 103dd875c76Swdenk char *name; 104dd875c76Swdenk int namelen; 105dd875c76Swdenk 106dd875c76Swdenk inode = (struct cramfs_inode *) (begin + offset + 107dd875c76Swdenk inodeoffset); 108dd875c76Swdenk 109dd875c76Swdenk /* 110dd875c76Swdenk * Namelengths on disk are shifted by two 111dd875c76Swdenk * and the name padded out to 4-byte boundaries 112dd875c76Swdenk * with zeroes. 113dd875c76Swdenk */ 114dd875c76Swdenk namelen = CRAMFS_GET_NAMELEN (inode) << 2; 115dd875c76Swdenk name = (char *) inode + sizeof (struct cramfs_inode); 116dd875c76Swdenk 117dd875c76Swdenk nextoffset = 118dd875c76Swdenk inodeoffset + sizeof (struct cramfs_inode) + namelen; 119dd875c76Swdenk 120dd875c76Swdenk for (;;) { 121dd875c76Swdenk if (!namelen) 122dd875c76Swdenk return -1; 123dd875c76Swdenk if (name[namelen - 1]) 124dd875c76Swdenk break; 125dd875c76Swdenk namelen--; 126dd875c76Swdenk } 127dd875c76Swdenk 128dd875c76Swdenk if (!strncmp (filename, name, namelen)) { 129dd875c76Swdenk char *p = strtok (NULL, "/"); 130dd875c76Swdenk 131dd875c76Swdenk if (raw && (p == NULL || *p == '\0')) 132dd875c76Swdenk return offset + inodeoffset; 133dd875c76Swdenk 134dd875c76Swdenk if (S_ISDIR (CRAMFS_16 (inode->mode))) { 135dd875c76Swdenk return cramfs_resolve (begin, 136dd875c76Swdenk CRAMFS_GET_OFFSET 137dd875c76Swdenk (inode) << 2, 138dd875c76Swdenk CRAMFS_24 (inode-> 139dd875c76Swdenk size), raw, 140dd875c76Swdenk p); 141dd875c76Swdenk } else if (S_ISREG (CRAMFS_16 (inode->mode))) { 142dd875c76Swdenk return offset + inodeoffset; 143dd875c76Swdenk } else { 144dd875c76Swdenk printf ("%*.*s: unsupported file type (%x)\n", 145dd875c76Swdenk namelen, namelen, name, 146dd875c76Swdenk CRAMFS_16 (inode->mode)); 147dd875c76Swdenk return 0; 148dd875c76Swdenk } 149dd875c76Swdenk } 150dd875c76Swdenk 151dd875c76Swdenk inodeoffset = nextoffset; 152dd875c76Swdenk } 153dd875c76Swdenk 154dd875c76Swdenk printf ("can't find corresponding entry\n"); 155dd875c76Swdenk return 0; 156dd875c76Swdenk } 157dd875c76Swdenk 158*700a0c64SWolfgang Denk static int cramfs_uncompress (unsigned long begin, unsigned long offset, 159dd875c76Swdenk unsigned long loadoffset) 160dd875c76Swdenk { 161dd875c76Swdenk struct cramfs_inode *inode = (struct cramfs_inode *) (begin + offset); 162dd875c76Swdenk unsigned long *block_ptrs = (unsigned long *) 163dd875c76Swdenk (begin + (CRAMFS_GET_OFFSET (inode) << 2)); 164dd875c76Swdenk unsigned long curr_block = (CRAMFS_GET_OFFSET (inode) + 165dd875c76Swdenk (((CRAMFS_24 (inode->size)) + 166dd875c76Swdenk 4095) >> 12)) << 2; 167dd875c76Swdenk int size, total_size = 0; 168dd875c76Swdenk int i; 169dd875c76Swdenk 170dd875c76Swdenk cramfs_uncompress_init (); 171dd875c76Swdenk 172dd875c76Swdenk for (i = 0; i < ((CRAMFS_24 (inode->size) + 4095) >> 12); i++) { 173dd875c76Swdenk size = cramfs_uncompress_block ((void *) loadoffset, 174dd875c76Swdenk (void *) (begin + curr_block), 175dd875c76Swdenk (CRAMFS_32 (block_ptrs[i]) - 176dd875c76Swdenk curr_block)); 177dd875c76Swdenk if (size < 0) 178dd875c76Swdenk return size; 179dd875c76Swdenk loadoffset += size; 180dd875c76Swdenk total_size += size; 181dd875c76Swdenk curr_block = CRAMFS_32 (block_ptrs[i]); 182dd875c76Swdenk } 183dd875c76Swdenk 184dd875c76Swdenk cramfs_uncompress_exit (); 185dd875c76Swdenk return total_size; 186dd875c76Swdenk } 187dd875c76Swdenk 188dd875c76Swdenk int cramfs_load (char *loadoffset, struct part_info *info, char *filename) 189dd875c76Swdenk { 190dd875c76Swdenk unsigned long offset; 191dd875c76Swdenk 192dd875c76Swdenk if (cramfs_read_super (info)) 193dd875c76Swdenk return -1; 194dd875c76Swdenk 195*700a0c64SWolfgang Denk offset = cramfs_resolve (PART_OFFSET(info), 196dd875c76Swdenk CRAMFS_GET_OFFSET (&(super.root)) << 2, 197dd875c76Swdenk CRAMFS_24 (super.root.size), 0, 198dd875c76Swdenk strtok (filename, "/")); 199dd875c76Swdenk 200dd875c76Swdenk if (offset <= 0) 201dd875c76Swdenk return offset; 202dd875c76Swdenk 203*700a0c64SWolfgang Denk return cramfs_uncompress (PART_OFFSET(info), offset, 204dd875c76Swdenk (unsigned long) loadoffset); 205dd875c76Swdenk } 206dd875c76Swdenk 207dd875c76Swdenk static int cramfs_list_inode (struct part_info *info, unsigned long offset) 208dd875c76Swdenk { 209dd875c76Swdenk struct cramfs_inode *inode = (struct cramfs_inode *) 210*700a0c64SWolfgang Denk (PART_OFFSET(info) + offset); 211dd875c76Swdenk char *name, str[20]; 212dd875c76Swdenk int namelen, nextoff; 213dd875c76Swdenk 214dd875c76Swdenk /* 215dd875c76Swdenk * Namelengths on disk are shifted by two 216dd875c76Swdenk * and the name padded out to 4-byte boundaries 217dd875c76Swdenk * with zeroes. 218dd875c76Swdenk */ 219dd875c76Swdenk namelen = CRAMFS_GET_NAMELEN (inode) << 2; 220dd875c76Swdenk name = (char *) inode + sizeof (struct cramfs_inode); 221dd875c76Swdenk nextoff = namelen; 222dd875c76Swdenk 223dd875c76Swdenk for (;;) { 224dd875c76Swdenk if (!namelen) 225dd875c76Swdenk return namelen; 226dd875c76Swdenk if (name[namelen - 1]) 227dd875c76Swdenk break; 228dd875c76Swdenk namelen--; 229dd875c76Swdenk } 230dd875c76Swdenk 231dd875c76Swdenk printf (" %s %8d %*.*s", mkmodestr (CRAMFS_16 (inode->mode), str), 232dd875c76Swdenk CRAMFS_24 (inode->size), namelen, namelen, name); 233dd875c76Swdenk 234dd875c76Swdenk if ((CRAMFS_16 (inode->mode) & S_IFMT) == S_IFLNK) { 235dd875c76Swdenk /* symbolic link. 236dd875c76Swdenk * Unpack the link target, trusting in the inode's size field. 237dd875c76Swdenk */ 238dd875c76Swdenk unsigned long size = CRAMFS_24 (inode->size); 239dd875c76Swdenk char *link = malloc (size); 240dd875c76Swdenk 241*700a0c64SWolfgang Denk if (link != NULL && cramfs_uncompress (PART_OFFSET(info), offset, 242dd875c76Swdenk (unsigned long) link) 243dd875c76Swdenk == size) 244dd875c76Swdenk printf (" -> %*.*s\n", (int) size, (int) size, link); 245dd875c76Swdenk else 246dd875c76Swdenk printf (" [Error reading link]\n"); 247dd875c76Swdenk if (link) 248dd875c76Swdenk free (link); 249dd875c76Swdenk } else 250dd875c76Swdenk printf ("\n"); 251dd875c76Swdenk 252dd875c76Swdenk return nextoff; 253dd875c76Swdenk } 254dd875c76Swdenk 255dd875c76Swdenk int cramfs_ls (struct part_info *info, char *filename) 256dd875c76Swdenk { 257dd875c76Swdenk struct cramfs_inode *inode; 258dd875c76Swdenk unsigned long inodeoffset = 0, nextoffset; 259dd875c76Swdenk unsigned long offset, size; 260dd875c76Swdenk 261dd875c76Swdenk if (cramfs_read_super (info)) 262dd875c76Swdenk return -1; 263dd875c76Swdenk 264dd875c76Swdenk if (strlen (filename) == 0 || !strcmp (filename, "/")) { 265dd875c76Swdenk /* Root directory. Use root inode in super block */ 266dd875c76Swdenk offset = CRAMFS_GET_OFFSET (&(super.root)) << 2; 267dd875c76Swdenk size = CRAMFS_24 (super.root.size); 268dd875c76Swdenk } else { 269dd875c76Swdenk /* Resolve the path */ 270*700a0c64SWolfgang Denk offset = cramfs_resolve (PART_OFFSET(info), 271dd875c76Swdenk CRAMFS_GET_OFFSET (&(super.root)) << 272dd875c76Swdenk 2, CRAMFS_24 (super.root.size), 1, 273dd875c76Swdenk strtok (filename, "/")); 274dd875c76Swdenk 275dd875c76Swdenk if (offset <= 0) 276dd875c76Swdenk return offset; 277dd875c76Swdenk 278dd875c76Swdenk /* Resolving was successful. Examine the inode */ 279*700a0c64SWolfgang Denk inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset); 280dd875c76Swdenk if (!S_ISDIR (CRAMFS_16 (inode->mode))) { 281dd875c76Swdenk /* It's not a directory - list it, and that's that */ 282dd875c76Swdenk return (cramfs_list_inode (info, offset) > 0); 283dd875c76Swdenk } 284dd875c76Swdenk 285dd875c76Swdenk /* It's a directory. List files within */ 286dd875c76Swdenk offset = CRAMFS_GET_OFFSET (inode) << 2; 287dd875c76Swdenk size = CRAMFS_24 (inode->size); 288dd875c76Swdenk } 289dd875c76Swdenk 290dd875c76Swdenk /* List the given directory */ 291dd875c76Swdenk while (inodeoffset < size) { 292*700a0c64SWolfgang Denk inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset + 293dd875c76Swdenk inodeoffset); 294dd875c76Swdenk 295dd875c76Swdenk nextoffset = cramfs_list_inode (info, offset + inodeoffset); 296dd875c76Swdenk if (nextoffset == 0) 297dd875c76Swdenk break; 298dd875c76Swdenk inodeoffset += sizeof (struct cramfs_inode) + nextoffset; 299dd875c76Swdenk } 300dd875c76Swdenk 301dd875c76Swdenk return 1; 302dd875c76Swdenk } 303dd875c76Swdenk 304dd875c76Swdenk int cramfs_info (struct part_info *info) 305dd875c76Swdenk { 306dd875c76Swdenk if (cramfs_read_super (info)) 307dd875c76Swdenk return 0; 308dd875c76Swdenk 309dd875c76Swdenk printf ("size: 0x%x (%u)\n", super.size, super.size); 310dd875c76Swdenk 311dd875c76Swdenk if (super.flags != 0) { 312dd875c76Swdenk printf ("flags:\n"); 313dd875c76Swdenk if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) 314dd875c76Swdenk printf ("\tFSID version 2\n"); 315dd875c76Swdenk if (super.flags & CRAMFS_FLAG_SORTED_DIRS) 316dd875c76Swdenk printf ("\tsorted dirs\n"); 317dd875c76Swdenk if (super.flags & CRAMFS_FLAG_HOLES) 318dd875c76Swdenk printf ("\tholes\n"); 319dd875c76Swdenk if (super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) 320dd875c76Swdenk printf ("\tshifted root offset\n"); 321dd875c76Swdenk } 322dd875c76Swdenk 323dd875c76Swdenk printf ("fsid:\n\tcrc: 0x%x\n\tedition: 0x%x\n", 324dd875c76Swdenk super.fsid.crc, super.fsid.edition); 325dd875c76Swdenk printf ("name: %16s\n", super.name); 326dd875c76Swdenk 327dd875c76Swdenk return 1; 328dd875c76Swdenk } 329dd875c76Swdenk 330dd875c76Swdenk int cramfs_check (struct part_info *info) 331dd875c76Swdenk { 332*700a0c64SWolfgang Denk struct cramfs_super *sb; 333dd875c76Swdenk 334*700a0c64SWolfgang Denk if (info->dev->id->type != MTD_DEV_TYPE_NOR) 335*700a0c64SWolfgang Denk return 0; 336*700a0c64SWolfgang Denk 337*700a0c64SWolfgang Denk sb = (struct cramfs_super *) PART_OFFSET(info); 338dd875c76Swdenk if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC)) { 339dd875c76Swdenk /* check at 512 byte offset */ 340*700a0c64SWolfgang Denk sb = (struct cramfs_super *) (PART_OFFSET(info) + 512); 341*700a0c64SWolfgang Denk if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC)) 342dd875c76Swdenk return 0; 343dd875c76Swdenk } 344dd875c76Swdenk return 1; 345dd875c76Swdenk } 346dd875c76Swdenk 347dd875c76Swdenk #endif /* CFG_FS_CRAMFS */ 348