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 #include <asm/byteorder.h> 30dd875c76Swdenk #include <linux/stat.h> 31180d3f74Swdenk #include <jffs2/jffs2.h> 32dd875c76Swdenk #include <jffs2/load_kernel.h> 33180d3f74Swdenk #include <cramfs/cramfs_fs.h> 34dd875c76Swdenk 35dd875c76Swdenk /* These two macros may change in future, to provide better st_ino 36dd875c76Swdenk semantics. */ 37dd875c76Swdenk #define CRAMINO(x) (CRAMFS_GET_OFFSET(x) ? CRAMFS_GET_OFFSET(x)<<2 : 1) 38dd875c76Swdenk #define OFFSET(x) ((x)->i_ino) 39dd875c76Swdenk 40dd875c76Swdenk struct cramfs_super super; 41dd875c76Swdenk 42700a0c64SWolfgang Denk /* CPU address space offset calculation macro, struct part_info offset is 43700a0c64SWolfgang Denk * device address space offset, so we need to shift it by a device start address. */ 44*e856bdcfSMasahiro Yamada #if defined(CONFIG_MTD_NOR_FLASH) 45e6f2e902SMarian Balakowicz extern flash_info_t flash_info[]; 4606503f16SScott Wood #define PART_OFFSET(x) ((ulong)x->offset + \ 4706503f16SScott Wood flash_info[x->dev->id->num].start[0]) 4862a813bcSHeiko Schocher #else 4906503f16SScott Wood #define PART_OFFSET(x) ((ulong)x->offset) 5062a813bcSHeiko Schocher #endif 51700a0c64SWolfgang Denk 52dd875c76Swdenk static int cramfs_read_super (struct part_info *info) 53dd875c76Swdenk { 54dd875c76Swdenk unsigned long root_offset; 55dd875c76Swdenk 56dd875c76Swdenk /* Read the first block and get the superblock from it */ 57700a0c64SWolfgang Denk memcpy (&super, (void *) PART_OFFSET(info), sizeof (super)); 58dd875c76Swdenk 59dd875c76Swdenk /* Do sanity checks on the superblock */ 60dd875c76Swdenk if (super.magic != CRAMFS_32 (CRAMFS_MAGIC)) { 61dd875c76Swdenk /* check at 512 byte offset */ 62700a0c64SWolfgang Denk memcpy (&super, (void *) PART_OFFSET(info) + 512, sizeof (super)); 63dd875c76Swdenk if (super.magic != CRAMFS_32 (CRAMFS_MAGIC)) { 64dd875c76Swdenk printf ("cramfs: wrong magic\n"); 65dd875c76Swdenk return -1; 66dd875c76Swdenk } 67dd875c76Swdenk } 68dd875c76Swdenk 69dd875c76Swdenk /* flags is reused several times, so swab it once */ 70dd875c76Swdenk super.flags = CRAMFS_32 (super.flags); 71dd875c76Swdenk super.size = CRAMFS_32 (super.size); 72dd875c76Swdenk 73dd875c76Swdenk /* get feature flags first */ 74dd875c76Swdenk if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) { 75dd875c76Swdenk printf ("cramfs: unsupported filesystem features\n"); 76dd875c76Swdenk return -1; 77dd875c76Swdenk } 78dd875c76Swdenk 79dd875c76Swdenk /* Check that the root inode is in a sane state */ 80dd875c76Swdenk if (!S_ISDIR (CRAMFS_16 (super.root.mode))) { 81dd875c76Swdenk printf ("cramfs: root is not a directory\n"); 82dd875c76Swdenk return -1; 83dd875c76Swdenk } 84dd875c76Swdenk root_offset = CRAMFS_GET_OFFSET (&(super.root)) << 2; 85dd875c76Swdenk if (root_offset == 0) { 86dd875c76Swdenk printf ("cramfs: empty filesystem"); 87dd875c76Swdenk } else if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) && 88dd875c76Swdenk ((root_offset != sizeof (struct cramfs_super)) && 89dd875c76Swdenk (root_offset != 512 + sizeof (struct cramfs_super)))) { 90dd875c76Swdenk printf ("cramfs: bad root offset %lu\n", root_offset); 91dd875c76Swdenk return -1; 92dd875c76Swdenk } 93dd875c76Swdenk 94dd875c76Swdenk return 0; 95dd875c76Swdenk } 96dd875c76Swdenk 97700a0c64SWolfgang Denk static unsigned long cramfs_resolve (unsigned long begin, unsigned long offset, 98dd875c76Swdenk unsigned long size, int raw, 99dd875c76Swdenk char *filename) 100dd875c76Swdenk { 101dd875c76Swdenk unsigned long inodeoffset = 0, nextoffset; 102dd875c76Swdenk 103dd875c76Swdenk while (inodeoffset < size) { 104dd875c76Swdenk struct cramfs_inode *inode; 105dd875c76Swdenk char *name; 106dd875c76Swdenk int namelen; 107dd875c76Swdenk 108dd875c76Swdenk inode = (struct cramfs_inode *) (begin + offset + 109dd875c76Swdenk inodeoffset); 110dd875c76Swdenk 111dd875c76Swdenk /* 112dd875c76Swdenk * Namelengths on disk are shifted by two 113dd875c76Swdenk * and the name padded out to 4-byte boundaries 114dd875c76Swdenk * with zeroes. 115dd875c76Swdenk */ 116dd875c76Swdenk namelen = CRAMFS_GET_NAMELEN (inode) << 2; 117dd875c76Swdenk name = (char *) inode + sizeof (struct cramfs_inode); 118dd875c76Swdenk 119dd875c76Swdenk nextoffset = 120dd875c76Swdenk inodeoffset + sizeof (struct cramfs_inode) + namelen; 121dd875c76Swdenk 122dd875c76Swdenk for (;;) { 123dd875c76Swdenk if (!namelen) 124dd875c76Swdenk return -1; 125dd875c76Swdenk if (name[namelen - 1]) 126dd875c76Swdenk break; 127dd875c76Swdenk namelen--; 128dd875c76Swdenk } 129dd875c76Swdenk 130457dd025SHolger Brunck if (!strncmp(filename, name, namelen) && 131457dd025SHolger Brunck (namelen == strlen(filename))) { 132dd875c76Swdenk char *p = strtok (NULL, "/"); 133dd875c76Swdenk 134dd875c76Swdenk if (raw && (p == NULL || *p == '\0')) 135dd875c76Swdenk return offset + inodeoffset; 136dd875c76Swdenk 137dd875c76Swdenk if (S_ISDIR (CRAMFS_16 (inode->mode))) { 138dd875c76Swdenk return cramfs_resolve (begin, 139dd875c76Swdenk CRAMFS_GET_OFFSET 140dd875c76Swdenk (inode) << 2, 141dd875c76Swdenk CRAMFS_24 (inode-> 142dd875c76Swdenk size), raw, 143dd875c76Swdenk p); 144dd875c76Swdenk } else if (S_ISREG (CRAMFS_16 (inode->mode))) { 145dd875c76Swdenk return offset + inodeoffset; 146dd875c76Swdenk } else { 147dd875c76Swdenk printf ("%*.*s: unsupported file type (%x)\n", 148dd875c76Swdenk namelen, namelen, name, 149dd875c76Swdenk CRAMFS_16 (inode->mode)); 150dd875c76Swdenk return 0; 151dd875c76Swdenk } 152dd875c76Swdenk } 153dd875c76Swdenk 154dd875c76Swdenk inodeoffset = nextoffset; 155dd875c76Swdenk } 156dd875c76Swdenk 157dd875c76Swdenk printf ("can't find corresponding entry\n"); 158dd875c76Swdenk return 0; 159dd875c76Swdenk } 160dd875c76Swdenk 161700a0c64SWolfgang Denk static int cramfs_uncompress (unsigned long begin, unsigned long offset, 162dd875c76Swdenk unsigned long loadoffset) 163dd875c76Swdenk { 164dd875c76Swdenk struct cramfs_inode *inode = (struct cramfs_inode *) (begin + offset); 165dd875c76Swdenk unsigned long *block_ptrs = (unsigned long *) 166dd875c76Swdenk (begin + (CRAMFS_GET_OFFSET (inode) << 2)); 167dd875c76Swdenk unsigned long curr_block = (CRAMFS_GET_OFFSET (inode) + 168dd875c76Swdenk (((CRAMFS_24 (inode->size)) + 169dd875c76Swdenk 4095) >> 12)) << 2; 170dd875c76Swdenk int size, total_size = 0; 171dd875c76Swdenk int i; 172dd875c76Swdenk 173dd875c76Swdenk cramfs_uncompress_init (); 174dd875c76Swdenk 175dd875c76Swdenk for (i = 0; i < ((CRAMFS_24 (inode->size) + 4095) >> 12); i++) { 176dd875c76Swdenk size = cramfs_uncompress_block ((void *) loadoffset, 177dd875c76Swdenk (void *) (begin + curr_block), 178dd875c76Swdenk (CRAMFS_32 (block_ptrs[i]) - 179dd875c76Swdenk curr_block)); 180dd875c76Swdenk if (size < 0) 181dd875c76Swdenk return size; 182dd875c76Swdenk loadoffset += size; 183dd875c76Swdenk total_size += size; 184dd875c76Swdenk curr_block = CRAMFS_32 (block_ptrs[i]); 185dd875c76Swdenk } 186dd875c76Swdenk 187dd875c76Swdenk cramfs_uncompress_exit (); 188dd875c76Swdenk return total_size; 189dd875c76Swdenk } 190dd875c76Swdenk 191dd875c76Swdenk int cramfs_load (char *loadoffset, struct part_info *info, char *filename) 192dd875c76Swdenk { 193dd875c76Swdenk unsigned long offset; 194dd875c76Swdenk 195dd875c76Swdenk if (cramfs_read_super (info)) 196dd875c76Swdenk return -1; 197dd875c76Swdenk 198700a0c64SWolfgang Denk offset = cramfs_resolve (PART_OFFSET(info), 199dd875c76Swdenk CRAMFS_GET_OFFSET (&(super.root)) << 2, 200dd875c76Swdenk CRAMFS_24 (super.root.size), 0, 201dd875c76Swdenk strtok (filename, "/")); 202dd875c76Swdenk 203dd875c76Swdenk if (offset <= 0) 204dd875c76Swdenk return offset; 205dd875c76Swdenk 206700a0c64SWolfgang Denk return cramfs_uncompress (PART_OFFSET(info), offset, 207dd875c76Swdenk (unsigned long) loadoffset); 208dd875c76Swdenk } 209dd875c76Swdenk 210dd875c76Swdenk static int cramfs_list_inode (struct part_info *info, unsigned long offset) 211dd875c76Swdenk { 212dd875c76Swdenk struct cramfs_inode *inode = (struct cramfs_inode *) 213700a0c64SWolfgang Denk (PART_OFFSET(info) + offset); 214dd875c76Swdenk char *name, str[20]; 215dd875c76Swdenk int namelen, nextoff; 216dd875c76Swdenk 217dd875c76Swdenk /* 218dd875c76Swdenk * Namelengths on disk are shifted by two 219dd875c76Swdenk * and the name padded out to 4-byte boundaries 220dd875c76Swdenk * with zeroes. 221dd875c76Swdenk */ 222dd875c76Swdenk namelen = CRAMFS_GET_NAMELEN (inode) << 2; 223dd875c76Swdenk name = (char *) inode + sizeof (struct cramfs_inode); 224dd875c76Swdenk nextoff = namelen; 225dd875c76Swdenk 226dd875c76Swdenk for (;;) { 227dd875c76Swdenk if (!namelen) 228dd875c76Swdenk return namelen; 229dd875c76Swdenk if (name[namelen - 1]) 230dd875c76Swdenk break; 231dd875c76Swdenk namelen--; 232dd875c76Swdenk } 233dd875c76Swdenk 234dd875c76Swdenk printf (" %s %8d %*.*s", mkmodestr (CRAMFS_16 (inode->mode), str), 235dd875c76Swdenk CRAMFS_24 (inode->size), namelen, namelen, name); 236dd875c76Swdenk 237dd875c76Swdenk if ((CRAMFS_16 (inode->mode) & S_IFMT) == S_IFLNK) { 238dd875c76Swdenk /* symbolic link. 239dd875c76Swdenk * Unpack the link target, trusting in the inode's size field. 240dd875c76Swdenk */ 241dd875c76Swdenk unsigned long size = CRAMFS_24 (inode->size); 242dd875c76Swdenk char *link = malloc (size); 243dd875c76Swdenk 244700a0c64SWolfgang Denk if (link != NULL && cramfs_uncompress (PART_OFFSET(info), offset, 245dd875c76Swdenk (unsigned long) link) 246dd875c76Swdenk == size) 247dd875c76Swdenk printf (" -> %*.*s\n", (int) size, (int) size, link); 248dd875c76Swdenk else 249dd875c76Swdenk printf (" [Error reading link]\n"); 250dd875c76Swdenk if (link) 251dd875c76Swdenk free (link); 252dd875c76Swdenk } else 253dd875c76Swdenk printf ("\n"); 254dd875c76Swdenk 255dd875c76Swdenk return nextoff; 256dd875c76Swdenk } 257dd875c76Swdenk 258dd875c76Swdenk int cramfs_ls (struct part_info *info, char *filename) 259dd875c76Swdenk { 260dd875c76Swdenk struct cramfs_inode *inode; 261dd875c76Swdenk unsigned long inodeoffset = 0, nextoffset; 262dd875c76Swdenk unsigned long offset, size; 263dd875c76Swdenk 264dd875c76Swdenk if (cramfs_read_super (info)) 265dd875c76Swdenk return -1; 266dd875c76Swdenk 267dd875c76Swdenk if (strlen (filename) == 0 || !strcmp (filename, "/")) { 268dd875c76Swdenk /* Root directory. Use root inode in super block */ 269dd875c76Swdenk offset = CRAMFS_GET_OFFSET (&(super.root)) << 2; 270dd875c76Swdenk size = CRAMFS_24 (super.root.size); 271dd875c76Swdenk } else { 272dd875c76Swdenk /* Resolve the path */ 273700a0c64SWolfgang Denk offset = cramfs_resolve (PART_OFFSET(info), 274dd875c76Swdenk CRAMFS_GET_OFFSET (&(super.root)) << 275dd875c76Swdenk 2, CRAMFS_24 (super.root.size), 1, 276dd875c76Swdenk strtok (filename, "/")); 277dd875c76Swdenk 278dd875c76Swdenk if (offset <= 0) 279dd875c76Swdenk return offset; 280dd875c76Swdenk 281dd875c76Swdenk /* Resolving was successful. Examine the inode */ 282700a0c64SWolfgang Denk inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset); 283dd875c76Swdenk if (!S_ISDIR (CRAMFS_16 (inode->mode))) { 284dd875c76Swdenk /* It's not a directory - list it, and that's that */ 285dd875c76Swdenk return (cramfs_list_inode (info, offset) > 0); 286dd875c76Swdenk } 287dd875c76Swdenk 288dd875c76Swdenk /* It's a directory. List files within */ 289dd875c76Swdenk offset = CRAMFS_GET_OFFSET (inode) << 2; 290dd875c76Swdenk size = CRAMFS_24 (inode->size); 291dd875c76Swdenk } 292dd875c76Swdenk 293dd875c76Swdenk /* List the given directory */ 294dd875c76Swdenk while (inodeoffset < size) { 295700a0c64SWolfgang Denk inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset + 296dd875c76Swdenk inodeoffset); 297dd875c76Swdenk 298dd875c76Swdenk nextoffset = cramfs_list_inode (info, offset + inodeoffset); 299dd875c76Swdenk if (nextoffset == 0) 300dd875c76Swdenk break; 301dd875c76Swdenk inodeoffset += sizeof (struct cramfs_inode) + nextoffset; 302dd875c76Swdenk } 303dd875c76Swdenk 304dd875c76Swdenk return 1; 305dd875c76Swdenk } 306dd875c76Swdenk 307dd875c76Swdenk int cramfs_info (struct part_info *info) 308dd875c76Swdenk { 309dd875c76Swdenk if (cramfs_read_super (info)) 310dd875c76Swdenk return 0; 311dd875c76Swdenk 312dd875c76Swdenk printf ("size: 0x%x (%u)\n", super.size, super.size); 313dd875c76Swdenk 314dd875c76Swdenk if (super.flags != 0) { 315dd875c76Swdenk printf ("flags:\n"); 316dd875c76Swdenk if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) 317dd875c76Swdenk printf ("\tFSID version 2\n"); 318dd875c76Swdenk if (super.flags & CRAMFS_FLAG_SORTED_DIRS) 319dd875c76Swdenk printf ("\tsorted dirs\n"); 320dd875c76Swdenk if (super.flags & CRAMFS_FLAG_HOLES) 321dd875c76Swdenk printf ("\tholes\n"); 322dd875c76Swdenk if (super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) 323dd875c76Swdenk printf ("\tshifted root offset\n"); 324dd875c76Swdenk } 325dd875c76Swdenk 326dd875c76Swdenk printf ("fsid:\n\tcrc: 0x%x\n\tedition: 0x%x\n", 327dd875c76Swdenk super.fsid.crc, super.fsid.edition); 328dd875c76Swdenk printf ("name: %16s\n", super.name); 329dd875c76Swdenk 330dd875c76Swdenk return 1; 331dd875c76Swdenk } 332dd875c76Swdenk 333dd875c76Swdenk int cramfs_check (struct part_info *info) 334dd875c76Swdenk { 335700a0c64SWolfgang Denk struct cramfs_super *sb; 336dd875c76Swdenk 337700a0c64SWolfgang Denk if (info->dev->id->type != MTD_DEV_TYPE_NOR) 338700a0c64SWolfgang Denk return 0; 339700a0c64SWolfgang Denk 340700a0c64SWolfgang Denk sb = (struct cramfs_super *) PART_OFFSET(info); 341dd875c76Swdenk if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC)) { 342dd875c76Swdenk /* check at 512 byte offset */ 343700a0c64SWolfgang Denk sb = (struct cramfs_super *) (PART_OFFSET(info) + 512); 344700a0c64SWolfgang Denk if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC)) 345dd875c76Swdenk return 0; 346dd875c76Swdenk } 347dd875c76Swdenk return 1; 348dd875c76Swdenk } 349