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