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