]>
Commit | Line | Data |
---|---|---|
dd875c76 WD |
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> | |
dd875c76 WD |
29 | #include <asm/byteorder.h> |
30 | #include <linux/stat.h> | |
180d3f74 | 31 | #include <jffs2/jffs2.h> |
dd875c76 | 32 | #include <jffs2/load_kernel.h> |
180d3f74 | 33 | #include <cramfs/cramfs_fs.h> |
dd875c76 WD |
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 | ||
700a0c64 WD |
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. */ | |
62a813bc | 44 | #if !defined(CONFIG_SYS_NO_FLASH) |
e6f2e902 | 45 | extern flash_info_t flash_info[]; |
06503f16 SW |
46 | #define PART_OFFSET(x) ((ulong)x->offset + \ |
47 | flash_info[x->dev->id->num].start[0]) | |
62a813bc | 48 | #else |
06503f16 | 49 | #define PART_OFFSET(x) ((ulong)x->offset) |
62a813bc | 50 | #endif |
700a0c64 | 51 | |
dd875c76 WD |
52 | static int cramfs_read_super (struct part_info *info) |
53 | { | |
54 | unsigned long root_offset; | |
55 | ||
56 | /* Read the first block and get the superblock from it */ | |
700a0c64 | 57 | memcpy (&super, (void *) PART_OFFSET(info), sizeof (super)); |
dd875c76 WD |
58 | |
59 | /* Do sanity checks on the superblock */ | |
60 | if (super.magic != CRAMFS_32 (CRAMFS_MAGIC)) { | |
61 | /* check at 512 byte offset */ | |
700a0c64 | 62 | memcpy (&super, (void *) PART_OFFSET(info) + 512, sizeof (super)); |
dd875c76 WD |
63 | if (super.magic != CRAMFS_32 (CRAMFS_MAGIC)) { |
64 | printf ("cramfs: wrong magic\n"); | |
65 | return -1; | |
66 | } | |
67 | } | |
68 | ||
69 | /* flags is reused several times, so swab it once */ | |
70 | super.flags = CRAMFS_32 (super.flags); | |
71 | super.size = CRAMFS_32 (super.size); | |
72 | ||
73 | /* get feature flags first */ | |
74 | if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) { | |
75 | printf ("cramfs: unsupported filesystem features\n"); | |
76 | return -1; | |
77 | } | |
78 | ||
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"); | |
82 | return -1; | |
83 | } | |
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); | |
91 | return -1; | |
92 | } | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
700a0c64 | 97 | static unsigned long cramfs_resolve (unsigned long begin, unsigned long offset, |
dd875c76 WD |
98 | unsigned long size, int raw, |
99 | char *filename) | |
100 | { | |
101 | unsigned long inodeoffset = 0, nextoffset; | |
102 | ||
103 | while (inodeoffset < size) { | |
104 | struct cramfs_inode *inode; | |
105 | char *name; | |
106 | int namelen; | |
107 | ||
108 | inode = (struct cramfs_inode *) (begin + offset + | |
109 | inodeoffset); | |
110 | ||
111 | /* | |
112 | * Namelengths on disk are shifted by two | |
113 | * and the name padded out to 4-byte boundaries | |
114 | * with zeroes. | |
115 | */ | |
116 | namelen = CRAMFS_GET_NAMELEN (inode) << 2; | |
117 | name = (char *) inode + sizeof (struct cramfs_inode); | |
118 | ||
119 | nextoffset = | |
120 | inodeoffset + sizeof (struct cramfs_inode) + namelen; | |
121 | ||
122 | for (;;) { | |
123 | if (!namelen) | |
124 | return -1; | |
125 | if (name[namelen - 1]) | |
126 | break; | |
127 | namelen--; | |
128 | } | |
129 | ||
457dd025 HB |
130 | if (!strncmp(filename, name, namelen) && |
131 | (namelen == strlen(filename))) { | |
dd875c76 WD |
132 | char *p = strtok (NULL, "/"); |
133 | ||
134 | if (raw && (p == NULL || *p == '\0')) | |
135 | return offset + inodeoffset; | |
136 | ||
137 | if (S_ISDIR (CRAMFS_16 (inode->mode))) { | |
138 | return cramfs_resolve (begin, | |
139 | CRAMFS_GET_OFFSET | |
140 | (inode) << 2, | |
141 | CRAMFS_24 (inode-> | |
142 | size), raw, | |
143 | p); | |
144 | } else if (S_ISREG (CRAMFS_16 (inode->mode))) { | |
145 | return offset + inodeoffset; | |
146 | } else { | |
147 | printf ("%*.*s: unsupported file type (%x)\n", | |
148 | namelen, namelen, name, | |
149 | CRAMFS_16 (inode->mode)); | |
150 | return 0; | |
151 | } | |
152 | } | |
153 | ||
154 | inodeoffset = nextoffset; | |
155 | } | |
156 | ||
157 | printf ("can't find corresponding entry\n"); | |
158 | return 0; | |
159 | } | |
160 | ||
700a0c64 | 161 | static int cramfs_uncompress (unsigned long begin, unsigned long offset, |
dd875c76 WD |
162 | unsigned long loadoffset) |
163 | { | |
164 | struct cramfs_inode *inode = (struct cramfs_inode *) (begin + offset); | |
165 | unsigned long *block_ptrs = (unsigned long *) | |
166 | (begin + (CRAMFS_GET_OFFSET (inode) << 2)); | |
167 | unsigned long curr_block = (CRAMFS_GET_OFFSET (inode) + | |
168 | (((CRAMFS_24 (inode->size)) + | |
169 | 4095) >> 12)) << 2; | |
170 | int size, total_size = 0; | |
171 | int i; | |
172 | ||
173 | cramfs_uncompress_init (); | |
174 | ||
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]) - | |
179 | curr_block)); | |
180 | if (size < 0) | |
181 | return size; | |
182 | loadoffset += size; | |
183 | total_size += size; | |
184 | curr_block = CRAMFS_32 (block_ptrs[i]); | |
185 | } | |
186 | ||
187 | cramfs_uncompress_exit (); | |
188 | return total_size; | |
189 | } | |
190 | ||
191 | int cramfs_load (char *loadoffset, struct part_info *info, char *filename) | |
192 | { | |
193 | unsigned long offset; | |
194 | ||
195 | if (cramfs_read_super (info)) | |
196 | return -1; | |
197 | ||
700a0c64 | 198 | offset = cramfs_resolve (PART_OFFSET(info), |
dd875c76 WD |
199 | CRAMFS_GET_OFFSET (&(super.root)) << 2, |
200 | CRAMFS_24 (super.root.size), 0, | |
201 | strtok (filename, "/")); | |
202 | ||
203 | if (offset <= 0) | |
204 | return offset; | |
205 | ||
700a0c64 | 206 | return cramfs_uncompress (PART_OFFSET(info), offset, |
dd875c76 WD |
207 | (unsigned long) loadoffset); |
208 | } | |
209 | ||
dd875c76 WD |
210 | static int cramfs_list_inode (struct part_info *info, unsigned long offset) |
211 | { | |
212 | struct cramfs_inode *inode = (struct cramfs_inode *) | |
700a0c64 | 213 | (PART_OFFSET(info) + offset); |
dd875c76 WD |
214 | char *name, str[20]; |
215 | int namelen, nextoff; | |
216 | ||
217 | /* | |
218 | * Namelengths on disk are shifted by two | |
219 | * and the name padded out to 4-byte boundaries | |
220 | * with zeroes. | |
221 | */ | |
222 | namelen = CRAMFS_GET_NAMELEN (inode) << 2; | |
223 | name = (char *) inode + sizeof (struct cramfs_inode); | |
224 | nextoff = namelen; | |
225 | ||
226 | for (;;) { | |
227 | if (!namelen) | |
228 | return namelen; | |
229 | if (name[namelen - 1]) | |
230 | break; | |
231 | namelen--; | |
232 | } | |
233 | ||
234 | printf (" %s %8d %*.*s", mkmodestr (CRAMFS_16 (inode->mode), str), | |
235 | CRAMFS_24 (inode->size), namelen, namelen, name); | |
236 | ||
237 | if ((CRAMFS_16 (inode->mode) & S_IFMT) == S_IFLNK) { | |
238 | /* symbolic link. | |
239 | * Unpack the link target, trusting in the inode's size field. | |
240 | */ | |
241 | unsigned long size = CRAMFS_24 (inode->size); | |
242 | char *link = malloc (size); | |
243 | ||
700a0c64 | 244 | if (link != NULL && cramfs_uncompress (PART_OFFSET(info), offset, |
dd875c76 WD |
245 | (unsigned long) link) |
246 | == size) | |
247 | printf (" -> %*.*s\n", (int) size, (int) size, link); | |
248 | else | |
249 | printf (" [Error reading link]\n"); | |
250 | if (link) | |
251 | free (link); | |
252 | } else | |
253 | printf ("\n"); | |
254 | ||
255 | return nextoff; | |
256 | } | |
257 | ||
258 | int cramfs_ls (struct part_info *info, char *filename) | |
259 | { | |
260 | struct cramfs_inode *inode; | |
261 | unsigned long inodeoffset = 0, nextoffset; | |
262 | unsigned long offset, size; | |
263 | ||
264 | if (cramfs_read_super (info)) | |
265 | return -1; | |
266 | ||
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); | |
271 | } else { | |
272 | /* Resolve the path */ | |
700a0c64 | 273 | offset = cramfs_resolve (PART_OFFSET(info), |
dd875c76 WD |
274 | CRAMFS_GET_OFFSET (&(super.root)) << |
275 | 2, CRAMFS_24 (super.root.size), 1, | |
276 | strtok (filename, "/")); | |
277 | ||
278 | if (offset <= 0) | |
279 | return offset; | |
280 | ||
281 | /* Resolving was successful. Examine the inode */ | |
700a0c64 | 282 | inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset); |
dd875c76 WD |
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); | |
286 | } | |
287 | ||
288 | /* It's a directory. List files within */ | |
289 | offset = CRAMFS_GET_OFFSET (inode) << 2; | |
290 | size = CRAMFS_24 (inode->size); | |
291 | } | |
292 | ||
293 | /* List the given directory */ | |
294 | while (inodeoffset < size) { | |
700a0c64 | 295 | inode = (struct cramfs_inode *) (PART_OFFSET(info) + offset + |
dd875c76 WD |
296 | inodeoffset); |
297 | ||
298 | nextoffset = cramfs_list_inode (info, offset + inodeoffset); | |
299 | if (nextoffset == 0) | |
300 | break; | |
301 | inodeoffset += sizeof (struct cramfs_inode) + nextoffset; | |
302 | } | |
303 | ||
304 | return 1; | |
305 | } | |
306 | ||
307 | int cramfs_info (struct part_info *info) | |
308 | { | |
309 | if (cramfs_read_super (info)) | |
310 | return 0; | |
311 | ||
312 | printf ("size: 0x%x (%u)\n", super.size, super.size); | |
313 | ||
314 | if (super.flags != 0) { | |
315 | printf ("flags:\n"); | |
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"); | |
324 | } | |
325 | ||
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); | |
329 | ||
330 | return 1; | |
331 | } | |
332 | ||
333 | int cramfs_check (struct part_info *info) | |
334 | { | |
700a0c64 WD |
335 | struct cramfs_super *sb; |
336 | ||
337 | if (info->dev->id->type != MTD_DEV_TYPE_NOR) | |
338 | return 0; | |
dd875c76 | 339 | |
700a0c64 | 340 | sb = (struct cramfs_super *) PART_OFFSET(info); |
dd875c76 WD |
341 | if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC)) { |
342 | /* check at 512 byte offset */ | |
700a0c64 WD |
343 | sb = (struct cramfs_super *) (PART_OFFSET(info) + 512); |
344 | if (sb->magic != CRAMFS_32 (CRAMFS_MAGIC)) | |
dd875c76 | 345 | return 0; |
dd875c76 WD |
346 | } |
347 | return 1; | |
348 | } |