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