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