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