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