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 (namelen == strlen(filename))) { 131 char *p = strtok (NULL, "/"); 132 133 if (raw && (p == NULL || *p == '\0')) 134 return offset + inodeoffset; 135 136 if (S_ISDIR (CRAMFS_16 (inode->mode))) { 137 return cramfs_resolve (begin, 138 CRAMFS_GET_OFFSET 139 (inode) << 2, 140 CRAMFS_24 (inode-> 141 size), raw, 142 p); 143 } else if (S_ISREG (CRAMFS_16 (inode->mode))) { 144 return offset + inodeoffset; 145 } else { 146 printf ("%*.*s: unsupported file type (%x)\n", 147 namelen, namelen, name, 148 CRAMFS_16 (inode->mode)); 149 return 0; 150 } 151 } 152 153 inodeoffset = nextoffset; 154 } 155 156 printf ("can't find corresponding entry\n"); 157 return 0; 158 } 159 160 static int cramfs_uncompress (unsigned long begin, unsigned long offset, 161 unsigned long loadoffset) 162 { 163 struct cramfs_inode *inode = (struct cramfs_inode *) (begin + offset); 164 unsigned long *block_ptrs = (unsigned long *) 165 (begin + (CRAMFS_GET_OFFSET (inode) << 2)); 166 unsigned long curr_block = (CRAMFS_GET_OFFSET (inode) + 167 (((CRAMFS_24 (inode->size)) + 168 4095) >> 12)) << 2; 169 int size, total_size = 0; 170 int i; 171 172 cramfs_uncompress_init (); 173 174 for (i = 0; i < ((CRAMFS_24 (inode->size) + 4095) >> 12); i++) { 175 size = cramfs_uncompress_block ((void *) loadoffset, 176 (void *) (begin + curr_block), 177 (CRAMFS_32 (block_ptrs[i]) - 178 curr_block)); 179 if (size < 0) 180 return size; 181 loadoffset += size; 182 total_size += size; 183 curr_block = CRAMFS_32 (block_ptrs[i]); 184 } 185 186 cramfs_uncompress_exit (); 187 return total_size; 188 } 189 190 int cramfs_load (char *loadoffset, struct part_info *info, char *filename) 191 { 192 unsigned long offset; 193 194 if (cramfs_read_super (info)) 195 return -1; 196 197 offset = cramfs_resolve (PART_OFFSET(info), 198 CRAMFS_GET_OFFSET (&(super.root)) << 2, 199 CRAMFS_24 (super.root.size), 0, 200 strtok (filename, "/")); 201 202 if (offset <= 0) 203 return offset; 204 205 return cramfs_uncompress (PART_OFFSET(info), offset, 206 (unsigned long) loadoffset); 207 } 208 209 static int cramfs_list_inode (struct part_info *info, unsigned long offset) 210 { 211 struct cramfs_inode *inode = (struct cramfs_inode *) 212 (PART_OFFSET(info) + offset); 213 char *name, str[20]; 214 int namelen, nextoff; 215 216 /* 217 * Namelengths on disk are shifted by two 218 * and the name padded out to 4-byte boundaries 219 * with zeroes. 220 */ 221 namelen = CRAMFS_GET_NAMELEN (inode) << 2; 222 name = (char *) inode + sizeof (struct cramfs_inode); 223 nextoff = namelen; 224 225 for (;;) { 226 if (!namelen) 227 return namelen; 228 if (name[namelen - 1]) 229 break; 230 namelen--; 231 } 232 233 printf (" %s %8d %*.*s", mkmodestr (CRAMFS_16 (inode->mode), str), 234 CRAMFS_24 (inode->size), namelen, namelen, name); 235 236 if ((CRAMFS_16 (inode->mode) & S_IFMT) == S_IFLNK) { 237 /* symbolic link. 238 * Unpack the link target, trusting in the inode's size field. 239 */ 240 unsigned long size = CRAMFS_24 (inode->size); 241 char *link = malloc (size); 242 243 if (link != NULL && cramfs_uncompress (PART_OFFSET(info), offset, 244 (unsigned long) link) 245 == size) 246 printf (" -> %*.*s\n", (int) size, (int) size, link); 247 else 248 printf (" [Error reading link]\n"); 249 if (link) 250 free (link); 251 } else 252 printf ("\n"); 253 254 return nextoff; 255 } 256 257 int cramfs_ls (struct part_info *info, char *filename) 258 { 259 struct cramfs_inode *inode; 260 unsigned long inodeoffset = 0, nextoffset; 261 unsigned long offset, size; 262 263 if (cramfs_read_super (info)) 264 return -1; 265 266 if (strlen (filename) == 0 || !strcmp (filename, "/")) { 267 /* Root directory. Use root inode in super block */ 268 offset = CRAMFS_GET_OFFSET (&(super.root)) << 2; 269 size = CRAMFS_24 (super.root.size); 270 } else { 271 /* Resolve the path */ 272 offset = cramfs_resolve (PART_OFFSET(info), 273 CRAMFS_GET_OFFSET (&(super.root)) << 274 2, CRAMFS_24 (super.root.size), 1, 275 strtok (filename, "/")); 276 277 if (offset <= 0) 278 return offset; 279 280 /* Resolving was successful. Examine the inode */ 281 inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset); 282 if (!S_ISDIR (CRAMFS_16 (inode->mode))) { 283 /* It's not a directory - list it, and that's that */ 284 return (cramfs_list_inode (info, offset) > 0); 285 } 286 287 /* It's a directory. List files within */ 288 offset = CRAMFS_GET_OFFSET (inode) << 2; 289 size = CRAMFS_24 (inode->size); 290 } 291 292 /* List the given directory */ 293 while (inodeoffset < size) { 294 inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset + 295 inodeoffset); 296 297 nextoffset = cramfs_list_inode (info, offset + inodeoffset); 298 if (nextoffset == 0) 299 break; 300 inodeoffset += sizeof (struct cramfs_inode) + nextoffset; 301 } 302 303 return 1; 304 } 305 306 int cramfs_info (struct part_info *info) 307 { 308 if (cramfs_read_super (info)) 309 return 0; 310 311 printf ("size: 0x%x (%u)\n", super.size, super.size); 312 313 if (super.flags != 0) { 314 printf ("flags:\n"); 315 if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) 316 printf ("\tFSID version 2\n"); 317 if (super.flags & CRAMFS_FLAG_SORTED_DIRS) 318 printf ("\tsorted dirs\n"); 319 if (super.flags & CRAMFS_FLAG_HOLES) 320 printf ("\tholes\n"); 321 if (super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) 322 printf ("\tshifted root offset\n"); 323 } 324 325 printf ("fsid:\n\tcrc: 0x%x\n\tedition: 0x%x\n", 326 super.fsid.crc, super.fsid.edition); 327 printf ("name: %16s\n", super.name); 328 329 return 1; 330 } 331 332 int cramfs_check (struct part_info *info) 333 { 334 struct cramfs_super *sb; 335 336 if (info->dev->id->type != MTD_DEV_TYPE_NOR) 337 return 0; 338 339 sb = (struct cramfs_super *) PART_OFFSET(info); 340 if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC)) { 341 /* check at 512 byte offset */ 342 sb = (struct cramfs_super *) (PART_OFFSET(info) + 512); 343 if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC)) 344 return 0; 345 } 346 return 1; 347 } 348