http://downloads.netgear.com/files/GPL/DM111PSP_v3.61d_GPL.tar.gz
[bcm963xx.git] / hostTools / squashfs / read_fs.c
1 /*
2  * Read a squashfs filesystem.  This is a highly compressed read only filesystem.
3  *
4  * Copyright (c) 2002, 2003, 2004 Phillip Lougher <plougher@users.sourceforge.net>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2,
9  * or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * read_fs.c
21  */
22
23 extern void read_bytes(int, unsigned int, int, char *);
24 extern int add_file(int, int, unsigned int *, int, unsigned int, int, int);
25
26 #define TRUE 1
27 #define FALSE 0
28 #include <stdio.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <zlib.h>
35 #include <sys/mman.h>
36
37 #include <endian.h>
38 #include "read_fs.h"
39 #include <squashfs_fs.h>
40
41 #include <stdlib.h>
42
43 #ifdef SQUASHFS_TRACE
44 #define TRACE(s, args...)               printf("mksquashfs: "s, ## args)
45 #else
46 #define TRACE(s, args...)
47 #endif
48
49 #define ERROR(s, args...)               fprintf(stderr, s, ## args)
50
51 int swap;
52
53 int read_block(int fd, int start, int *next, unsigned char *block, squashfs_super_block *sBlk)
54 {
55         unsigned short c_byte;
56         int offset = 2;
57         
58         if(swap) {
59                 read_bytes(fd, start, 2, block);
60                 ((unsigned char *) &c_byte)[1] = block[0];
61                 ((unsigned char *) &c_byte)[0] = block[1]; 
62         } else 
63                 read_bytes(fd, start, 2, (char *)&c_byte);
64
65         if(SQUASHFS_CHECK_DATA(sBlk->flags))
66                 offset = 3;
67         if(SQUASHFS_COMPRESSED(c_byte)) {
68                 unsigned char buffer[SQUASHFS_METADATA_SIZE];
69                 int res;
70                 long bytes = SQUASHFS_METADATA_SIZE;
71
72                 c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
73                 read_bytes(fd, start + offset, c_byte, buffer);
74
75                 if((res = uncompress(block, &bytes, (const char *) buffer, c_byte)) != Z_OK) {
76                         if(res == Z_MEM_ERROR)
77                                 ERROR("zlib::uncompress failed, not enough memory\n");
78                         else if(res == Z_BUF_ERROR)
79                                 ERROR("zlib::uncompress failed, not enough room in output buffer\n");
80                         else
81                                 ERROR("zlib::uncompress failed, unknown error %d\n", res);
82                         return 0;
83                 }
84                 if(next)
85                         *next = start + offset + c_byte;
86                 return bytes;
87         } else {
88                 c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
89                 read_bytes(fd, start + offset, c_byte, block);
90                 if(next)
91                         *next = start + offset + c_byte;
92                 return c_byte;
93         }
94 }
95
96
97 int scan_inode_table(int fd, int start, int end, int root_inode_start, squashfs_super_block *sBlk,
98                 squashfs_dir_inode_header *dir_inode, unsigned char **inode_table, unsigned int *root_inode_block,
99                 unsigned int *uncompressed_file, unsigned int *uncompressed_directory, int *file_count, int *sym_count,
100                 int *dev_count, int *dir_count, int *fifo_count, int *sock_count)
101 {
102         unsigned char *cur_ptr;
103         int bytes = 0, size = 0, files = 0;
104         squashfs_reg_inode_header inode;
105         unsigned int directory_start_block;
106
107         TRACE("scan_inode_table: start 0x%x, end 0x%x, root_inode_start 0x%x\n", start, end, root_inode_start);
108         while(start < end) {
109                 if(start == root_inode_start) {
110                         TRACE("scan_inode_table: read compressed block 0x%x containing root inode\n", start);
111                         *root_inode_block = bytes;
112                 }
113                 if((size - bytes < SQUASHFS_METADATA_SIZE) &&
114                                 ((*inode_table = realloc(*inode_table, size += SQUASHFS_METADATA_SIZE)) == NULL))
115                         return FALSE;
116                 TRACE("scan_inode_table: reading block 0x%x\n", start);
117                 if((bytes += read_block(fd, start, &start, *inode_table + bytes, sBlk)) == 0) {
118                         free(*inode_table);
119                         return FALSE;
120                 }
121         }
122
123         /*
124          * Read last inode entry which is the root directory inode, and obtain the last
125          * directory start block index.  This is used when calculating the total uncompressed
126          * directory size.  The directory bytes in the last block will be counted as normal.
127          *
128          * The root inode is ignored in the inode scan.  This ensures there is
129          * always enough bytes left to read a regular file inode entry
130          */
131         bytes -= sizeof(squashfs_dir_inode_header);
132         if(swap) {
133                 squashfs_dir_inode_header sinode;
134                 memcpy((void *)&sinode, (void *)(*inode_table + bytes), sizeof(*dir_inode));
135                 SQUASHFS_SWAP_DIR_INODE_HEADER(dir_inode, &sinode);
136         } else
137                 memcpy((void *)dir_inode, (void *)(*inode_table + bytes), sizeof(*dir_inode));
138         directory_start_block = dir_inode->start_block;
139
140         for(cur_ptr = *inode_table; cur_ptr < *inode_table + bytes; files ++) {
141                 if(swap) {
142                         squashfs_reg_inode_header sinode;
143                         memcpy((void *)&sinode, (void *)cur_ptr, sizeof(inode));
144                         SQUASHFS_SWAP_REG_INODE_HEADER(&inode, &sinode);
145                 } else
146                         memcpy((void *)&inode, (void *)cur_ptr, sizeof(inode));
147
148                 TRACE("scan_inode_table: processing inode @ byte position 0x%x, type 0x%x\n", cur_ptr - *inode_table,
149                                 inode.inode_type);
150                 switch(inode.inode_type) {
151                         case SQUASHFS_FILE_TYPE: {
152                                 int frag_bytes = inode.fragment == SQUASHFS_INVALID_BLK ? 0 : inode.file_size % sBlk->block_size;
153                                 int blocks = inode.fragment == SQUASHFS_INVALID_BLK ? (inode.file_size
154                                         + sBlk->block_size - 1) >> sBlk->block_log : inode.file_size >>
155                                         sBlk->block_log;
156                                 int file_bytes = 0, i, start = inode.start_block;
157                                 unsigned int block_list[blocks];
158
159                                 TRACE("scan_inode_table: regular file, file_size %d, blocks %d\n", inode.file_size, blocks);
160
161                                 cur_ptr += sizeof(inode);
162                                 if(swap) {
163                                         unsigned int sblock_list[blocks];
164                                         memcpy((void *)sblock_list, (void *)cur_ptr, blocks * sizeof(unsigned int));
165                                         SQUASHFS_SWAP_INTS(block_list, sblock_list, blocks);
166                                 } else
167                                         memcpy((void *)block_list, (void *)cur_ptr, blocks * sizeof(unsigned int));
168
169                                 *uncompressed_file += inode.file_size;
170                                 (*file_count) ++;
171
172                                 for(i = 0; i < blocks; i++)
173                                         file_bytes += SQUASHFS_COMPRESSED_SIZE_BLOCK(block_list[i]);
174
175                                 add_file(start, file_bytes, block_list, blocks, inode.fragment, inode.offset, frag_bytes);
176                                 cur_ptr += blocks * sizeof(unsigned int);
177                                 break;
178                         }       
179                         case SQUASHFS_SYMLINK_TYPE: {
180                                 squashfs_symlink_inode_header inodep;
181         
182                                 if(swap) {
183                                         squashfs_symlink_inode_header sinodep;
184                                         memcpy((void *)&sinodep, (void *)cur_ptr, sizeof(sinodep));
185                                         SQUASHFS_SWAP_SYMLINK_INODE_HEADER(&inodep, &sinodep);
186                                 } else
187                                         memcpy((void *)&inodep, (void *)cur_ptr, sizeof(inodep));
188                                 (*sym_count) ++;
189                                 cur_ptr += sizeof(inodep) + inodep.symlink_size;
190                                 break;
191                         }
192                         case SQUASHFS_DIR_TYPE: {
193                                 squashfs_dir_inode_header dir_inode;
194
195                                 if(swap) {
196                                         squashfs_dir_inode_header sinode;
197                                         memcpy((void *)&sinode, (void *)cur_ptr, sizeof(dir_inode));
198                                         SQUASHFS_SWAP_DIR_INODE_HEADER(&dir_inode, &sinode);
199                                 } else
200                                         memcpy((void *)&dir_inode, (void *)cur_ptr, sizeof(dir_inode));
201                                 if(dir_inode.start_block < directory_start_block)
202                                         *uncompressed_directory += dir_inode.file_size;
203                                 (*dir_count) ++;
204                                 cur_ptr += sizeof(squashfs_dir_inode_header);
205                                 break;
206                         }
207                         case SQUASHFS_BLKDEV_TYPE:
208                         case SQUASHFS_CHRDEV_TYPE:
209                                 (*dev_count) ++;
210                                 cur_ptr += sizeof(squashfs_dev_inode_header);
211                                 break;
212
213                         case SQUASHFS_FIFO_TYPE:
214                                 (*fifo_count) ++;
215                                 cur_ptr += sizeof(squashfs_ipc_inode_header);
216                                 break;
217                         case SQUASHFS_SOCKET_TYPE:
218                                 (*sock_count) ++;
219                                 cur_ptr += sizeof(squashfs_ipc_inode_header);
220                                 break;
221                         default:
222                                 ERROR("Unknown inode type %d in scan_inode_table!\n", inode.inode_type);
223                                 free(*inode_table);
224                                 return FALSE;
225                 }
226         }
227         
228         return files;
229 }
230
231
232 int read_super(int fd, squashfs_super_block *sBlk, int *be, char *source)
233 {
234         read_bytes(fd, SQUASHFS_START, sizeof(squashfs_super_block), (char *) sBlk);
235
236         /* Check it is a SQUASHFS superblock */
237         swap = 0;
238         if(sBlk->s_magic != SQUASHFS_MAGIC) {
239                 if(sBlk->s_magic == SQUASHFS_MAGIC_SWAP) {
240                         squashfs_super_block sblk;
241                         ERROR("Reading a different endian SQUASHFS filesystem on %s - ignoring -le/-be options\n", source);
242                         SQUASHFS_SWAP_SUPER_BLOCK(&sblk, sBlk);
243                         memcpy(sBlk, &sblk, sizeof(squashfs_super_block));
244                         swap = 1;
245                 } else  {
246                         ERROR("Can't find a SQUASHFS superblock on %s\n", source);
247                         goto failed_mount;
248                 }
249         }
250
251         /* Check the MAJOR & MINOR versions */
252         if(sBlk->s_major != SQUASHFS_MAJOR || sBlk->s_minor > SQUASHFS_MINOR) {
253                 if(sBlk->s_major == 1)
254                         ERROR("Filesystem on %s is a SQUASHFS 1.x filesystem.  Appending\nto SQUASHFS 1.x filesystems is not supported.  Please convert it to a SQUASHFS 2.0 filesystem...n", source);
255                 else
256                         ERROR("Major/Minor mismatch, filesystem on %s is (%d:%d), I support (%d: <= %d)\n",
257                                 source, sBlk->s_major, sBlk->s_minor, SQUASHFS_MAJOR, SQUASHFS_MINOR);
258                 goto failed_mount;
259         }
260
261 #if __BYTE_ORDER == __BIG_ENDIAN
262         *be = !swap;
263 #else
264         *be = swap;
265 #endif
266
267         printf("Found a valid SQUASHFS superblock on %s.\n", source);
268         printf("\tInodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(sBlk->flags) ? "un" : "");
269         printf("\tData is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(sBlk->flags) ? "un" : "");
270         printf("\tFragments are %scompressed\n", SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk->flags) ? "un" : "");
271         printf("\tCheck data is %s present in the filesystem\n", SQUASHFS_CHECK_DATA(sBlk->flags) ? "" : "not");
272         printf("\tFragments are %s present in the filesystem\n", SQUASHFS_NO_FRAGMENTS(sBlk->flags) ? "not" : "");
273         printf("\tAlways_use_fragments option is %s specified\n", SQUASHFS_ALWAYS_FRAGMENTS(sBlk->flags) ? "" : "not");
274         printf("\tDuplicates are %s removed\n", SQUASHFS_DUPLICATES(sBlk->flags) ? "" : "not");
275         printf("\tFilesystem size %d bytes\n", sBlk->bytes_used);
276         printf("\tBlock size %d\n", sBlk->block_size);
277         printf("\tNumber of fragments %d\n", sBlk->fragments);
278         printf("\tNumber of inodes %d\n", sBlk->inodes);
279         printf("\tNumber of uids %d\n", sBlk->no_uids);
280         printf("\tNumber of gids %d\n", sBlk->no_guids);
281         TRACE("sBlk->inode_table_start %x\n", sBlk->inode_table_start);
282         TRACE("sBlk->directory_table_start %x\n", sBlk->directory_table_start);
283         TRACE("sBlk->uid_start %x\n", sBlk->uid_start);
284         TRACE("sBlk->fragment_table_start %x\n", sBlk->fragment_table_start);
285         printf("\n");
286
287         return TRUE;
288
289 failed_mount:
290         return FALSE;
291 }
292
293
294 unsigned char *squashfs_readdir(int fd, int root_entries, int start, int offset, int size, squashfs_super_block *sBlk,
295                 void (push_directory_entry)(char *, squashfs_inode, int))
296 {
297         squashfs_dir_header dirh;
298         char buffer[sizeof(squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1];
299         squashfs_dir_entry *dire = (squashfs_dir_entry *) buffer;
300         unsigned char *directory_table = NULL;
301         int bytes = 0, dir_count;
302
303         size += offset;
304         if((directory_table = malloc((size + SQUASHFS_METADATA_SIZE * 2 - 1) & ~(SQUASHFS_METADATA_SIZE - 1))) == NULL)
305                 return NULL;
306         while(bytes < size) {
307                 TRACE("squashfs_readdir: reading block 0x%x, bytes read so far %d\n", start, bytes);
308                 if((bytes += read_block(fd, start, &start, directory_table + bytes, sBlk)) == 0) {
309                         free(directory_table);
310                         return NULL;
311                 }
312         }
313
314         if(!root_entries)
315                 goto all_done;
316
317         bytes = offset;
318         while(bytes < size) {                   
319                 if(swap) {
320                         squashfs_dir_header sdirh;
321                         memcpy((void *)&sdirh, directory_table + bytes, sizeof(sdirh));
322                         SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
323                 } else
324                         memcpy((void *)&dirh, directory_table + bytes, sizeof(dirh));
325
326                 dir_count = dirh.count + 1;
327                 TRACE("squashfs_readdir: Read directory header @ byte position 0x%x, 0x%x directory entries\n", bytes, dir_count);
328                 bytes += sizeof(dirh);
329
330                 while(dir_count--) {
331                         if(swap) {
332                                 squashfs_dir_entry sdire;
333                                 memcpy((void *)&sdire, directory_table + bytes, sizeof(sdire));
334                                 SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
335                         } else
336                                 memcpy((void *)dire, directory_table + bytes, sizeof(dire));
337                         bytes += sizeof(*dire);
338
339                         memcpy((void *)dire->name, directory_table + bytes, dire->size + 1);
340                         dire->name[dire->size + 1] = '\0';
341                         TRACE("squashfs_readdir: pushing directory entry %s, inode %x:%x, type 0x%x\n", dire->name, dirh.start_block, dire->offset, dire->type);
342                         push_directory_entry(dire->name, SQUASHFS_MKINODE(dirh.start_block, dire->offset), dire->type);
343                         bytes += dire->size + 1;
344                 }
345         }
346
347 all_done:
348         return directory_table;
349 }
350
351
352 int read_fragment_table(int fd, squashfs_super_block *sBlk, squashfs_fragment_entry **fragment_table)
353 {
354         int i, indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk->fragments);
355         squashfs_fragment_index fragment_table_index[indexes];
356
357         TRACE("read_fragment_table: %d fragments, reading %d fragment indexes from 0x%x\n", sBlk->fragments, indexes, sBlk->fragment_table_start);
358         if(sBlk->fragments == 0)
359                 return 1;
360
361         if((*fragment_table = (squashfs_fragment_entry *) malloc(sBlk->fragments * sizeof(squashfs_fragment_entry))) == NULL) {
362                 ERROR("Failed to allocate fragment table\n");
363                 return 0;
364         }
365
366         if(swap) {
367                 squashfs_fragment_index sfragment_table_index[indexes];
368
369                 read_bytes(fd, sBlk->fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments), (char *) sfragment_table_index);
370                 SQUASHFS_SWAP_FRAGMENT_INDEXES(fragment_table_index, sfragment_table_index, indexes);
371         } else
372                 read_bytes(fd, sBlk->fragment_table_start, SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments), (char *) fragment_table_index);
373
374         for(i = 0; i < indexes; i++) {
375                 int length = read_block(fd, fragment_table_index[i], NULL, ((char *) *fragment_table) + (i * SQUASHFS_METADATA_SIZE), sBlk);
376                 TRACE("Read fragment table block %d, from 0x%x, length %d\n", i, fragment_table_index[i], length);
377         }
378
379
380         if(swap) {
381                 squashfs_fragment_entry sfragment;
382                 for(i = 0; i < sBlk->fragments; i++) {
383                         SQUASHFS_SWAP_FRAGMENT_ENTRY((&sfragment), (&(*fragment_table)[i]));
384                         memcpy((char *) &(*fragment_table)[i], (char *) &sfragment, sizeof(squashfs_fragment_entry));
385                 }
386         }
387
388         return 1;
389 }
390
391
392 int read_filesystem(char *root_name, int fd, squashfs_super_block *sBlk, char **cinode_table, int *inode_bytes,
393                 char **data_cache, int *cache_bytes, int *cache_size,
394                 char **cdirectory_table, int *directory_bytes,
395                 char **directory_data_cache, int *directory_cache_bytes, int *directory_cache_size,
396                 int *file_count, int *sym_count, int *dev_count, int *dir_count, int *fifo_count, int *sock_count,
397                 squashfs_uid *uids, unsigned short *uid_count, squashfs_uid *guids, unsigned short *guid_count,
398                 unsigned int *uncompressed_file, unsigned int *uncompressed_inode, unsigned int *uncompressed_directory,
399                 void (push_directory_entry)(char *, squashfs_inode, int), squashfs_fragment_entry **fragment_table)
400 {
401         unsigned char *inode_table = NULL, *directory_table;
402         unsigned int start = sBlk->inode_table_start, end = sBlk->directory_table_start,
403                 root_inode_start = start + SQUASHFS_INODE_BLK(sBlk->root_inode),
404                 root_inode_offset = SQUASHFS_INODE_OFFSET(sBlk->root_inode), root_directory_offset;
405         squashfs_dir_inode_header inode;
406         unsigned int files, root_inode_block;
407
408         printf("Scanning existing filesystem...\n");
409
410         if(read_fragment_table(fd,  sBlk, fragment_table) == 0)
411                 goto error;
412
413         if((files = scan_inode_table(fd, start, end, root_inode_start, sBlk, &inode, &inode_table, &root_inode_block,
414                         uncompressed_file, uncompressed_directory, file_count, sym_count, dev_count, dir_count, fifo_count, sock_count)) == 0) {
415                 ERROR("read_filesystem: inode table read failed\n");
416                 goto error;
417         }
418
419         *uncompressed_inode = root_inode_block;
420
421         printf("Read existing filesystem, %d inodes scanned\n", files);
422
423         if(inode.inode_type == SQUASHFS_DIR_TYPE) {
424                 if((directory_table = squashfs_readdir(fd, !root_name, sBlk->directory_table_start + inode.start_block,
425                                                 inode.offset, inode.file_size, sBlk, push_directory_entry)) == NULL) {
426                         ERROR("read_filesystem: Could not read root directory\n");
427                         goto error;
428                 }
429
430                 if((*cinode_table = (char *) malloc(root_inode_start - start)) == NULL) {
431                         ERROR("read_filesystem: failed to alloc space for existing filesystem inode table\n");
432                         goto error;
433                 }
434
435                 read_bytes(fd, start, root_inode_start - start, *cinode_table);
436
437                 if((*cdirectory_table = (char *) malloc(inode.start_block)) == NULL) {
438                         ERROR("read_filesystem: failed to alloc space for existing filesystem inode table\n");
439                         goto error;
440                 }
441
442                 read_bytes(fd, sBlk->directory_table_start, inode.start_block, *cdirectory_table);
443
444                 *inode_bytes =  root_inode_start - start;
445                 *directory_bytes = inode.start_block;
446
447                 root_inode_offset += sizeof(inode);
448                 root_directory_offset = inode.offset + inode.file_size;
449                 (*dir_count) ++;
450
451                 if(((*data_cache = (char *) malloc(root_inode_offset)) == NULL) || 
452                                 ((*directory_data_cache = (char *) malloc(root_directory_offset)) == NULL)) {
453                         ERROR("read_filesystem: failed to alloc inode/directory caches\n");
454                         goto error;
455                 }
456
457                 memcpy(*data_cache, inode_table + root_inode_block, root_inode_offset);
458                 memcpy(*directory_data_cache, directory_table, root_directory_offset);
459                 *cache_size = root_inode_offset;
460                 *directory_cache_size = root_directory_offset;
461
462                 if(root_name) {
463                         push_directory_entry(root_name, sBlk->root_inode, SQUASHFS_DIR_TYPE);
464                         *cache_bytes = root_inode_offset;
465                         *directory_cache_bytes = root_directory_offset;
466                 } else {
467                         *cache_bytes = root_inode_offset - sizeof(inode);
468                         *directory_cache_bytes = inode.offset;
469                 }
470
471                 if(!swap)
472                         read_bytes(fd, sBlk->uid_start, sBlk->no_uids * sizeof(squashfs_uid), (char *) uids);
473                 else {
474                         squashfs_uid uids_copy[sBlk->no_uids];
475
476                         read_bytes(fd, sBlk->uid_start, sBlk->no_uids * sizeof(squashfs_uid), (char *) uids_copy);
477                         SQUASHFS_SWAP_DATA(uids, uids_copy, sBlk->no_uids, sizeof(squashfs_uid) * 8);
478                 }
479
480                 if(!swap)
481                         read_bytes(fd, sBlk->guid_start, sBlk->no_guids * sizeof(squashfs_uid), (char *) guids);
482                 else {
483                         squashfs_uid guids_copy[sBlk->no_guids];
484
485                         read_bytes(fd, sBlk->guid_start, sBlk->no_guids * sizeof(squashfs_uid), (char *) guids_copy);
486                         SQUASHFS_SWAP_DATA(guids, guids_copy, sBlk->no_guids, sizeof(squashfs_uid) * 8);
487                 }
488                 *uid_count = sBlk->no_uids;
489                 *guid_count = sBlk->no_guids;
490
491                 free(inode_table);
492                 free(directory_table);
493                 return sBlk->inode_table_start;
494         }
495
496 error:
497         return 0;
498 }