4 * Copyright (C) 1999 Linus Torvalds
6 * Copyright (C) 2000-2002 Transmeta Corporation
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
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.
16 * Compressed ROM filesystem for Linux.
19 * add support for resolving symbolic links
23 * These are the VFS interfaces to the compressed ROM filesystem.
24 * The actual compression is based on zlib, see the other files.
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>
35 /* These two macros may change in future, to provide better st_ino
37 #define CRAMINO(x) (CRAMFS_GET_OFFSET(x) ? CRAMFS_GET_OFFSET(x)<<2 : 1)
38 #define OFFSET(x) ((x)->i_ino)
40 struct cramfs_super super
;
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])
49 #define PART_OFFSET(x) ((ulong)x->offset)
52 static int cramfs_read_super (struct part_info
*info
)
54 unsigned long root_offset
;
56 /* Read the first block and get the superblock from it */
57 memcpy (&super
, (void *) PART_OFFSET(info
), sizeof (super
));
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");
69 /* flags is reused several times, so swab it once */
70 super
.flags
= CRAMFS_32 (super
.flags
);
71 super
.size
= CRAMFS_32 (super
.size
);
73 /* get feature flags first */
74 if (super
.flags
& ~CRAMFS_SUPPORTED_FLAGS
) {
75 printf ("cramfs: unsupported filesystem features\n");
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");
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
);
97 static unsigned long cramfs_resolve (unsigned long begin
, unsigned long offset
,
98 unsigned long size
, int raw
,
101 unsigned long inodeoffset
= 0, nextoffset
;
103 while (inodeoffset
< size
) {
104 struct cramfs_inode
*inode
;
108 inode
= (struct cramfs_inode
*) (begin
+ offset
+
112 * Namelengths on disk are shifted by two
113 * and the name padded out to 4-byte boundaries
116 namelen
= CRAMFS_GET_NAMELEN (inode
) << 2;
117 name
= (char *) inode
+ sizeof (struct cramfs_inode
);
120 inodeoffset
+ sizeof (struct cramfs_inode
) + namelen
;
125 if (name
[namelen
- 1])
130 if (!strncmp(filename
, name
, namelen
) &&
131 (namelen
== strlen(filename
))) {
132 char *p
= strtok (NULL
, "/");
134 if (raw
&& (p
== NULL
|| *p
== '\0'))
135 return offset
+ inodeoffset
;
137 if (S_ISDIR (CRAMFS_16 (inode
->mode
))) {
138 return cramfs_resolve (begin
,
144 } else if (S_ISREG (CRAMFS_16 (inode
->mode
))) {
145 return offset
+ inodeoffset
;
147 printf ("%*.*s: unsupported file type (%x)\n",
148 namelen
, namelen
, name
,
149 CRAMFS_16 (inode
->mode
));
154 inodeoffset
= nextoffset
;
157 printf ("can't find corresponding entry\n");
161 static int cramfs_uncompress (unsigned long begin
, unsigned long offset
,
162 unsigned long loadoffset
)
164 struct cramfs_inode
*inode
= (struct cramfs_inode
*) (begin
+ offset
);
165 u32
*block_ptrs
= (u32
*)
166 (begin
+ (CRAMFS_GET_OFFSET (inode
) << 2));
167 unsigned long curr_block
= (CRAMFS_GET_OFFSET (inode
) +
168 (((CRAMFS_24 (inode
->size
)) +
170 int size
, total_size
= 0;
173 cramfs_uncompress_init ();
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
]) -
184 curr_block
= CRAMFS_32 (block_ptrs
[i
]);
187 cramfs_uncompress_exit ();
191 int cramfs_load (char *loadoffset
, struct part_info
*info
, char *filename
)
193 unsigned long offset
;
195 if (cramfs_read_super (info
))
198 offset
= cramfs_resolve (PART_OFFSET(info
),
199 CRAMFS_GET_OFFSET (&(super
.root
)) << 2,
200 CRAMFS_24 (super
.root
.size
), 0,
201 strtok (filename
, "/"));
206 return cramfs_uncompress (PART_OFFSET(info
), offset
,
207 (unsigned long) loadoffset
);
210 static int cramfs_list_inode (struct part_info
*info
, unsigned long offset
)
212 struct cramfs_inode
*inode
= (struct cramfs_inode
*)
213 (PART_OFFSET(info
) + offset
);
215 int namelen
, nextoff
;
218 * Namelengths on disk are shifted by two
219 * and the name padded out to 4-byte boundaries
222 namelen
= CRAMFS_GET_NAMELEN (inode
) << 2;
223 name
= (char *) inode
+ sizeof (struct cramfs_inode
);
229 if (name
[namelen
- 1])
234 printf (" %s %8d %*.*s", mkmodestr (CRAMFS_16 (inode
->mode
), str
),
235 CRAMFS_24 (inode
->size
), namelen
, namelen
, name
);
237 if ((CRAMFS_16 (inode
->mode
) & S_IFMT
) == S_IFLNK
) {
239 * Unpack the link target, trusting in the inode's size field.
241 unsigned long size
= CRAMFS_24 (inode
->size
);
242 char *link
= malloc (size
);
244 if (link
!= NULL
&& cramfs_uncompress (PART_OFFSET(info
), offset
,
245 (unsigned long) link
)
247 printf (" -> %*.*s\n", (int) size
, (int) size
, link
);
249 printf (" [Error reading link]\n");
258 int cramfs_ls (struct part_info
*info
, char *filename
)
260 struct cramfs_inode
*inode
;
261 unsigned long inodeoffset
= 0, nextoffset
;
262 unsigned long offset
, size
;
264 if (cramfs_read_super (info
))
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
);
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
, "/"));
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);
288 /* It's a directory. List files within */
289 offset
= CRAMFS_GET_OFFSET (inode
) << 2;
290 size
= CRAMFS_24 (inode
->size
);
293 /* List the given directory */
294 while (inodeoffset
< size
) {
295 inode
= (struct cramfs_inode
*) (PART_OFFSET(info
) + offset
+
298 nextoffset
= cramfs_list_inode (info
, offset
+ inodeoffset
);
301 inodeoffset
+= sizeof (struct cramfs_inode
) + nextoffset
;
307 int cramfs_info (struct part_info
*info
)
309 if (cramfs_read_super (info
))
312 printf ("size: 0x%x (%u)\n", super
.size
, super
.size
);
314 if (super
.flags
!= 0) {
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");
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
);
333 int cramfs_check (struct part_info
*info
)
335 struct cramfs_super
*sb
;
337 if (info
->dev
->id
->type
!= MTD_DEV_TYPE_NOR
)
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
))