test: rng: Add a UT testcase for the rng command
[platform/kernel/u-boot.git] / fs / cramfs / cramfs.c
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
41 struct 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)
46 extern 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
53 static int cramfs_uncompress (unsigned long begin, unsigned long offset,
54                               unsigned long loadoffset);
55
56 static 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. */
102 static 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
117 static 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
208 static 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
238 int 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
257 static 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
297 int 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
346 int 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
372 int 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 }