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 40 struct 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 extern flash_info_t flash_info[]; 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 52 static int cramfs_uncompress (unsigned long begin, unsigned long offset, 53 unsigned long loadoffset); 54 55 static 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. */ 101 static 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 116 static 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 \ 170 non-terminal path\n"); 171 return 0; 172 } 173 link = cramfs_uncompress_link (begin, 174 offset + inodeoffset); 175 if (!link) { 176 printf ("%*.*s: Error reading link\n", 177 namelen, namelen, name); 178 return 0; 179 } else if (link[0] == '/') { 180 printf ("unsupported symlink to \ 181 absolute path\n"); 182 free (link); 183 return 0; 184 } 185 ret = cramfs_resolve (begin, 186 offset, 187 size, 188 raw, 189 strtok(link, "/")); 190 free (link); 191 return ret; 192 } else { 193 printf ("%*.*s: unsupported file type (%x)\n", 194 namelen, namelen, name, 195 CRAMFS_16 (inode->mode)); 196 return 0; 197 } 198 } 199 200 inodeoffset = nextoffset; 201 } 202 203 printf ("can't find corresponding entry\n"); 204 return 0; 205 } 206 207 static int cramfs_uncompress (unsigned long begin, unsigned long offset, 208 unsigned long loadoffset) 209 { 210 struct cramfs_inode *inode = (struct cramfs_inode *) (begin + offset); 211 u32 *block_ptrs = (u32 *) 212 (begin + (CRAMFS_GET_OFFSET (inode) << 2)); 213 unsigned long curr_block = (CRAMFS_GET_OFFSET (inode) + 214 (((CRAMFS_24 (inode->size)) + 215 4095) >> 12)) << 2; 216 int size, total_size = 0; 217 int i; 218 219 cramfs_uncompress_init (); 220 221 for (i = 0; i < ((CRAMFS_24 (inode->size) + 4095) >> 12); i++) { 222 size = cramfs_uncompress_block ((void *) loadoffset, 223 (void *) (begin + curr_block), 224 (CRAMFS_32 (block_ptrs[i]) - 225 curr_block)); 226 if (size < 0) 227 return size; 228 loadoffset += size; 229 total_size += size; 230 curr_block = CRAMFS_32 (block_ptrs[i]); 231 } 232 233 cramfs_uncompress_exit (); 234 return total_size; 235 } 236 237 int cramfs_load (char *loadoffset, struct part_info *info, char *filename) 238 { 239 unsigned long offset; 240 241 if (cramfs_read_super (info)) 242 return -1; 243 244 offset = cramfs_resolve (PART_OFFSET(info), 245 CRAMFS_GET_OFFSET (&(super.root)) << 2, 246 CRAMFS_24 (super.root.size), 0, 247 strtok (filename, "/")); 248 249 if (offset <= 0) 250 return offset; 251 252 return cramfs_uncompress (PART_OFFSET(info), offset, 253 (unsigned long) loadoffset); 254 } 255 256 static int cramfs_list_inode (struct part_info *info, unsigned long offset) 257 { 258 struct cramfs_inode *inode = (struct cramfs_inode *) 259 (PART_OFFSET(info) + offset); 260 char *name, str[20]; 261 int namelen, nextoff; 262 263 /* 264 * Namelengths on disk are shifted by two 265 * and the name padded out to 4-byte boundaries 266 * with zeroes. 267 */ 268 namelen = CRAMFS_GET_NAMELEN (inode) << 2; 269 name = (char *) inode + sizeof (struct cramfs_inode); 270 nextoff = namelen; 271 272 for (;;) { 273 if (!namelen) 274 return namelen; 275 if (name[namelen - 1]) 276 break; 277 namelen--; 278 } 279 280 printf (" %s %8d %*.*s", mkmodestr (CRAMFS_16 (inode->mode), str), 281 CRAMFS_24 (inode->size), namelen, namelen, name); 282 283 if ((CRAMFS_16 (inode->mode) & S_IFMT) == S_IFLNK) { 284 char *link = cramfs_uncompress_link (PART_OFFSET(info), offset); 285 if (link) 286 printf (" -> %s\n", link); 287 else 288 printf (" [Error reading link]\n"); 289 free (link); 290 } else 291 printf ("\n"); 292 293 return nextoff; 294 } 295 296 int cramfs_ls (struct part_info *info, char *filename) 297 { 298 struct cramfs_inode *inode; 299 unsigned long inodeoffset = 0, nextoffset; 300 unsigned long offset, size; 301 302 if (cramfs_read_super (info)) 303 return -1; 304 305 if (strlen (filename) == 0 || !strcmp (filename, "/")) { 306 /* Root directory. Use root inode in super block */ 307 offset = CRAMFS_GET_OFFSET (&(super.root)) << 2; 308 size = CRAMFS_24 (super.root.size); 309 } else { 310 /* Resolve the path */ 311 offset = cramfs_resolve (PART_OFFSET(info), 312 CRAMFS_GET_OFFSET (&(super.root)) << 313 2, CRAMFS_24 (super.root.size), 1, 314 strtok (filename, "/")); 315 316 if (offset <= 0) 317 return offset; 318 319 /* Resolving was successful. Examine the inode */ 320 inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset); 321 if (!S_ISDIR (CRAMFS_16 (inode->mode))) { 322 /* It's not a directory - list it, and that's that */ 323 return (cramfs_list_inode (info, offset) > 0); 324 } 325 326 /* It's a directory. List files within */ 327 offset = CRAMFS_GET_OFFSET (inode) << 2; 328 size = CRAMFS_24 (inode->size); 329 } 330 331 /* List the given directory */ 332 while (inodeoffset < size) { 333 inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset + 334 inodeoffset); 335 336 nextoffset = cramfs_list_inode (info, offset + inodeoffset); 337 if (nextoffset == 0) 338 break; 339 inodeoffset += sizeof (struct cramfs_inode) + nextoffset; 340 } 341 342 return 1; 343 } 344 345 int cramfs_info (struct part_info *info) 346 { 347 if (cramfs_read_super (info)) 348 return 0; 349 350 printf ("size: 0x%x (%u)\n", super.size, super.size); 351 352 if (super.flags != 0) { 353 printf ("flags:\n"); 354 if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) 355 printf ("\tFSID version 2\n"); 356 if (super.flags & CRAMFS_FLAG_SORTED_DIRS) 357 printf ("\tsorted dirs\n"); 358 if (super.flags & CRAMFS_FLAG_HOLES) 359 printf ("\tholes\n"); 360 if (super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) 361 printf ("\tshifted root offset\n"); 362 } 363 364 printf ("fsid:\n\tcrc: 0x%x\n\tedition: 0x%x\n", 365 super.fsid.crc, super.fsid.edition); 366 printf ("name: %16s\n", super.name); 367 368 return 1; 369 } 370 371 int cramfs_check (struct part_info *info) 372 { 373 struct cramfs_super *sb; 374 375 if (info->dev->id->type != MTD_DEV_TYPE_NOR) 376 return 0; 377 378 sb = (struct cramfs_super *) PART_OFFSET(info); 379 if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC)) { 380 /* check at 512 byte offset */ 381 sb = (struct cramfs_super *) (PART_OFFSET(info) + 512); 382 if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC)) 383 return 0; 384 } 385 return 1; 386 } 387