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