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