f163523a42708e3b169f8050e033e76678ffffb8
[linux-2.6.git] / fs / squashfs / squashfs2_0.c
1 /*
2  * Squashfs - a compressed read only filesystem for Linux
3  *
4  * Copyright (c) 2002, 2003, 2004, 2005, 2006
5  * Phillip Lougher <phillip@lougher.org.uk>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2,
10  * or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  *
21  * squashfs2_0.c
22  */
23
24 #include <linux/types.h>
25 #include <linux/squashfs_fs.h>
26 #include <linux/module.h>
27 #include <linux/errno.h>
28 #include <linux/slab.h>
29 #include <linux/zlib.h>
30 #include <linux/fs.h>
31 #include <linux/smp_lock.h>
32 #include <linux/slab.h>
33 #include <linux/squashfs_fs_sb.h>
34 #include <linux/squashfs_fs_i.h>
35 #include <linux/buffer_head.h>
36 #include <linux/vfs.h>
37 #include <linux/init.h>
38 #include <linux/dcache.h>
39 #include <linux/wait.h>
40 #include <linux/zlib.h>
41 #include <linux/blkdev.h>
42 #include <linux/vmalloc.h>
43 #include <asm/uaccess.h>
44 #include <asm/semaphore.h>
45
46 #include "squashfs.h"
47 static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir);
48 static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *,
49                                 struct nameidata *);
50
51 static struct file_operations squashfs_dir_ops_2 = {
52         .read = generic_read_dir,
53         .readdir = squashfs_readdir_2
54 };
55
56 static struct inode_operations squashfs_dir_inode_ops_2 = {
57         .lookup = squashfs_lookup_2
58 };
59
60 static unsigned char squashfs_filetype_table[] = {
61         DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
62 };
63
64 static int read_fragment_index_table_2(struct super_block *s)
65 {
66         struct squashfs_sb_info *msblk = s->s_fs_info;
67         struct squashfs_super_block *sblk = &msblk->sblk;
68
69         if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2
70                                         (sblk->fragments), GFP_KERNEL))) {
71                 ERROR("Failed to allocate uid/gid table\n");
72                 return 0;
73         }
74    
75         if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) &&
76                         !squashfs_read_data(s, (char *)
77                         msblk->fragment_index_2,
78                         sblk->fragment_table_start,
79                         SQUASHFS_FRAGMENT_INDEX_BYTES_2
80                         (sblk->fragments) |
81                         SQUASHFS_COMPRESSED_BIT_BLOCK, NULL,
82                         SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments))) {
83                 ERROR("unable to read fragment index table\n");
84                 return 0;
85         }
86
87         if (msblk->swap) {
88                 int i;
89                 unsigned int fragment;
90
91                 for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments);
92                                                                         i++) {
93                         SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment),
94                                                 &msblk->fragment_index_2[i], 1);
95                         msblk->fragment_index_2[i] = fragment;
96                 }
97         }
98
99         return 1;
100 }
101
102
103 static int get_fragment_location_2(struct super_block *s, unsigned int fragment,
104                                 long long *fragment_start_block,
105                                 unsigned int *fragment_size)
106 {
107         struct squashfs_sb_info *msblk = s->s_fs_info;
108         long long start_block =
109                 msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)];
110         int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment);
111         struct squashfs_fragment_entry_2 fragment_entry;
112
113         if (msblk->swap) {
114                 struct squashfs_fragment_entry_2 sfragment_entry;
115
116                 if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
117                                         start_block, offset,
118                                         sizeof(sfragment_entry), &start_block,
119                                         &offset))
120                         goto out;
121                 SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry);
122         } else
123                 if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
124                                         start_block, offset,
125                                         sizeof(fragment_entry), &start_block,
126                                         &offset))
127                         goto out;
128
129         *fragment_start_block = fragment_entry.start_block;
130         *fragment_size = fragment_entry.size;
131
132         return 1;
133
134 out:
135         return 0;
136 }
137
138
139 static struct inode *squashfs_new_inode(struct super_block *s,
140                 struct squashfs_base_inode_header_2 *inodeb, unsigned int ino)
141 {
142         struct squashfs_sb_info *msblk = s->s_fs_info;
143         struct squashfs_super_block *sblk = &msblk->sblk;
144         struct inode *i = new_inode(s);
145
146         if (i) {
147                 i->i_ino = ino;
148                 i->i_mtime.tv_sec = sblk->mkfs_time;
149                 i->i_atime.tv_sec = sblk->mkfs_time;
150                 i->i_ctime.tv_sec = sblk->mkfs_time;
151                 i->i_uid = msblk->uid[inodeb->uid];
152                 i->i_mode = inodeb->mode;
153                 i->i_nlink = 1;
154                 i->i_size = 0;
155                 if (inodeb->guid == SQUASHFS_GUIDS)
156                         i->i_gid = i->i_uid;
157                 else
158                         i->i_gid = msblk->guid[inodeb->guid];
159         }
160
161         return i;
162 }
163
164
165 static struct inode *squashfs_iget_2(struct super_block *s, squashfs_inode_t inode)
166 {
167         struct inode *i;
168         struct squashfs_sb_info *msblk = s->s_fs_info;
169         struct squashfs_super_block *sblk = &msblk->sblk;
170         unsigned int block = SQUASHFS_INODE_BLK(inode) +
171                 sblk->inode_table_start;
172         unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
173         unsigned int ino = SQUASHFS_MK_VFS_INODE(block
174                 - sblk->inode_table_start, offset);
175         long long next_block;
176         unsigned int next_offset;
177         union squashfs_inode_header_2 id, sid;
178         struct squashfs_base_inode_header_2 *inodeb = &id.base,
179                                           *sinodeb = &sid.base;
180
181         TRACE("Entered squashfs_iget\n");
182
183         if (msblk->swap) {
184                 if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
185                                         offset, sizeof(*sinodeb), &next_block,
186                                         &next_offset))
187                         goto failed_read;
188                 SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb,
189                                         sizeof(*sinodeb));
190         } else
191                 if (!squashfs_get_cached_block(s, (char *) inodeb, block,
192                                         offset, sizeof(*inodeb), &next_block,
193                                         &next_offset))
194                         goto failed_read;
195
196         switch(inodeb->inode_type) {
197                 case SQUASHFS_FILE_TYPE: {
198                         struct squashfs_reg_inode_header_2 *inodep = &id.reg;
199                         struct squashfs_reg_inode_header_2 *sinodep = &sid.reg;
200                         long long frag_blk;
201                         unsigned int frag_size;
202                                 
203                         if (msblk->swap) {
204                                 if (!squashfs_get_cached_block(s, (char *)
205                                                 sinodep, block, offset,
206                                                 sizeof(*sinodep), &next_block,
207                                                 &next_offset))
208                                         goto failed_read;
209                                 SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep);
210                         } else
211                                 if (!squashfs_get_cached_block(s, (char *)
212                                                 inodep, block, offset,
213                                                 sizeof(*inodep), &next_block,
214                                                 &next_offset))
215                                         goto failed_read;
216
217                         frag_blk = SQUASHFS_INVALID_BLK;
218                         if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
219                                         !get_fragment_location_2(s,
220                                         inodep->fragment, &frag_blk, &frag_size))
221                                 goto failed_read;
222                                 
223                         if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
224                                 goto failed_read1;
225
226                         i->i_size = inodep->file_size;
227                         i->i_fop = &generic_ro_fops;
228                         i->i_mode |= S_IFREG;
229                         i->i_mtime.tv_sec = inodep->mtime;
230                         i->i_atime.tv_sec = inodep->mtime;
231                         i->i_ctime.tv_sec = inodep->mtime;
232                         i->i_blocks = ((i->i_size - 1) >> 9) + 1;
233                         SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
234                         SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
235                         SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
236                         SQUASHFS_I(i)->start_block = inodep->start_block;
237                         SQUASHFS_I(i)->u.s1.block_list_start = next_block;
238                         SQUASHFS_I(i)->offset = next_offset;
239                         if (sblk->block_size > 4096)
240                                 i->i_data.a_ops = &squashfs_aops;
241                         else
242                                 i->i_data.a_ops = &squashfs_aops_4K;
243
244                         TRACE("File inode %x:%x, start_block %x, "
245                                         "block_list_start %llx, offset %x\n",
246                                         SQUASHFS_INODE_BLK(inode), offset,
247                                         inodep->start_block, next_block,
248                                         next_offset);
249                         break;
250                 }
251                 case SQUASHFS_DIR_TYPE: {
252                         struct squashfs_dir_inode_header_2 *inodep = &id.dir;
253                         struct squashfs_dir_inode_header_2 *sinodep = &sid.dir;
254
255                         if (msblk->swap) {
256                                 if (!squashfs_get_cached_block(s, (char *)
257                                                 sinodep, block, offset,
258                                                 sizeof(*sinodep), &next_block,
259                                                 &next_offset))
260                                         goto failed_read;
261                                 SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep);
262                         } else
263                                 if (!squashfs_get_cached_block(s, (char *)
264                                                 inodep, block, offset,
265                                                 sizeof(*inodep), &next_block,
266                                                 &next_offset))
267                                         goto failed_read;
268
269                         if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
270                                 goto failed_read1;
271
272                         i->i_size = inodep->file_size;
273                         i->i_op = &squashfs_dir_inode_ops_2;
274                         i->i_fop = &squashfs_dir_ops_2;
275                         i->i_mode |= S_IFDIR;
276                         i->i_mtime.tv_sec = inodep->mtime;
277                         i->i_atime.tv_sec = inodep->mtime;
278                         i->i_ctime.tv_sec = inodep->mtime;
279                         SQUASHFS_I(i)->start_block = inodep->start_block;
280                         SQUASHFS_I(i)->offset = inodep->offset;
281                         SQUASHFS_I(i)->u.s2.directory_index_count = 0;
282                         SQUASHFS_I(i)->u.s2.parent_inode = 0;
283
284                         TRACE("Directory inode %x:%x, start_block %x, offset "
285                                         "%x\n", SQUASHFS_INODE_BLK(inode),
286                                         offset, inodep->start_block,
287                                         inodep->offset);
288                         break;
289                 }
290                 case SQUASHFS_LDIR_TYPE: {
291                         struct squashfs_ldir_inode_header_2 *inodep = &id.ldir;
292                         struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir;
293
294                         if (msblk->swap) {
295                                 if (!squashfs_get_cached_block(s, (char *)
296                                                 sinodep, block, offset,
297                                                 sizeof(*sinodep), &next_block,
298                                                 &next_offset))
299                                         goto failed_read;
300                                 SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep,
301                                                 sinodep);
302                         } else
303                                 if (!squashfs_get_cached_block(s, (char *)
304                                                 inodep, block, offset,
305                                                 sizeof(*inodep), &next_block,
306                                                 &next_offset))
307                                         goto failed_read;
308
309                         if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
310                                 goto failed_read1;
311
312                         i->i_size = inodep->file_size;
313                         i->i_op = &squashfs_dir_inode_ops_2;
314                         i->i_fop = &squashfs_dir_ops_2;
315                         i->i_mode |= S_IFDIR;
316                         i->i_mtime.tv_sec = inodep->mtime;
317                         i->i_atime.tv_sec = inodep->mtime;
318                         i->i_ctime.tv_sec = inodep->mtime;
319                         SQUASHFS_I(i)->start_block = inodep->start_block;
320                         SQUASHFS_I(i)->offset = inodep->offset;
321                         SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
322                         SQUASHFS_I(i)->u.s2.directory_index_offset =
323                                                                 next_offset;
324                         SQUASHFS_I(i)->u.s2.directory_index_count =
325                                                                 inodep->i_count;
326                         SQUASHFS_I(i)->u.s2.parent_inode = 0;
327
328                         TRACE("Long directory inode %x:%x, start_block %x, "
329                                         "offset %x\n",
330                                         SQUASHFS_INODE_BLK(inode), offset,
331                                         inodep->start_block, inodep->offset);
332                         break;
333                 }
334                 case SQUASHFS_SYMLINK_TYPE: {
335                         struct squashfs_symlink_inode_header_2 *inodep =
336                                                                 &id.symlink;
337                         struct squashfs_symlink_inode_header_2 *sinodep =
338                                                                 &sid.symlink;
339         
340                         if (msblk->swap) {
341                                 if (!squashfs_get_cached_block(s, (char *)
342                                                 sinodep, block, offset,
343                                                 sizeof(*sinodep), &next_block,
344                                                 &next_offset))
345                                         goto failed_read;
346                                 SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
347                                                                 sinodep);
348                         } else
349                                 if (!squashfs_get_cached_block(s, (char *)
350                                                 inodep, block, offset,
351                                                 sizeof(*inodep), &next_block,
352                                                 &next_offset))
353                                         goto failed_read;
354
355                         if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
356                                 goto failed_read1;
357
358                         i->i_size = inodep->symlink_size;
359                         i->i_op = &page_symlink_inode_operations;
360                         i->i_data.a_ops = &squashfs_symlink_aops;
361                         i->i_mode |= S_IFLNK;
362                         SQUASHFS_I(i)->start_block = next_block;
363                         SQUASHFS_I(i)->offset = next_offset;
364
365                         TRACE("Symbolic link inode %x:%x, start_block %llx, "
366                                         "offset %x\n",
367                                         SQUASHFS_INODE_BLK(inode), offset,
368                                         next_block, next_offset);
369                         break;
370                  }
371                  case SQUASHFS_BLKDEV_TYPE:
372                  case SQUASHFS_CHRDEV_TYPE: {
373                         struct squashfs_dev_inode_header_2 *inodep = &id.dev;
374                         struct squashfs_dev_inode_header_2 *sinodep = &sid.dev;
375
376                         if (msblk->swap) {
377                                 if (!squashfs_get_cached_block(s, (char *)
378                                                 sinodep, block, offset,
379                                                 sizeof(*sinodep), &next_block,
380                                                 &next_offset))
381                                         goto failed_read;
382                                 SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep);
383                         } else  
384                                 if (!squashfs_get_cached_block(s, (char *)
385                                                 inodep, block, offset,
386                                                 sizeof(*inodep), &next_block,
387                                                 &next_offset))
388                                         goto failed_read;
389
390                         if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
391                                 goto failed_read1;
392
393                         i->i_mode |= (inodeb->inode_type ==
394                                         SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
395                                         S_IFBLK;
396                         init_special_inode(i, i->i_mode,
397                                         old_decode_dev(inodep->rdev));
398
399                         TRACE("Device inode %x:%x, rdev %x\n",
400                                         SQUASHFS_INODE_BLK(inode), offset,
401                                         inodep->rdev);
402                         break;
403                  }
404                  case SQUASHFS_FIFO_TYPE:
405                  case SQUASHFS_SOCKET_TYPE: {
406                         if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
407                                 goto failed_read1;
408
409                         i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
410                                                         ? S_IFIFO : S_IFSOCK;
411                         init_special_inode(i, i->i_mode, 0);
412                         break;
413                  }
414                  default:
415                         ERROR("Unknown inode type %d in squashfs_iget!\n",
416                                         inodeb->inode_type);
417                         goto failed_read1;
418         }
419         
420         insert_inode_hash(i);
421         return i;
422
423 failed_read:
424         ERROR("Unable to read inode [%x:%x]\n", block, offset);
425
426 failed_read1:
427         return NULL;
428 }
429
430
431 static int get_dir_index_using_offset(struct super_block *s, long long 
432                                 *next_block, unsigned int *next_offset,
433                                 long long index_start,
434                                 unsigned int index_offset, int i_count,
435                                 long long f_pos)
436 {
437         struct squashfs_sb_info *msblk = s->s_fs_info;
438         struct squashfs_super_block *sblk = &msblk->sblk;
439         int i, length = 0;
440         struct squashfs_dir_index_2 index;
441
442         TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
443                                         i_count, (unsigned int) f_pos);
444
445         if (f_pos == 0)
446                 goto finish;
447
448         for (i = 0; i < i_count; i++) {
449                 if (msblk->swap) {
450                         struct squashfs_dir_index_2 sindex;
451                         squashfs_get_cached_block(s, (char *) &sindex,
452                                         index_start, index_offset,
453                                         sizeof(sindex), &index_start,
454                                         &index_offset);
455                         SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex);
456                 } else
457                         squashfs_get_cached_block(s, (char *) &index,
458                                         index_start, index_offset,
459                                         sizeof(index), &index_start,
460                                         &index_offset);
461
462                 if (index.index > f_pos)
463                         break;
464
465                 squashfs_get_cached_block(s, NULL, index_start, index_offset,
466                                         index.size + 1, &index_start,
467                                         &index_offset);
468
469                 length = index.index;
470                 *next_block = index.start_block + sblk->directory_table_start;
471         }
472
473         *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
474
475 finish:
476         return length;
477 }
478
479
480 static int get_dir_index_using_name(struct super_block *s, long long
481                                 *next_block, unsigned int *next_offset,
482                                 long long index_start,
483                                 unsigned int index_offset, int i_count,
484                                 const char *name, int size)
485 {
486         struct squashfs_sb_info *msblk = s->s_fs_info;
487         struct squashfs_super_block *sblk = &msblk->sblk;
488         int i, length = 0;
489         char buffer[sizeof(struct squashfs_dir_index_2) + SQUASHFS_NAME_LEN + 1];
490         struct squashfs_dir_index_2 *index = (struct squashfs_dir_index_2 *) buffer;
491         char str[SQUASHFS_NAME_LEN + 1];
492
493         TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
494
495         strncpy(str, name, size);
496         str[size] = '\0';
497
498         for (i = 0; i < i_count; i++) {
499                 if (msblk->swap) {
500                         struct squashfs_dir_index_2 sindex;
501                         squashfs_get_cached_block(s, (char *) &sindex,
502                                         index_start, index_offset,
503                                         sizeof(sindex), &index_start,
504                                         &index_offset);
505                         SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex);
506                 } else
507                         squashfs_get_cached_block(s, (char *) index,
508                                         index_start, index_offset,
509                                         sizeof(struct squashfs_dir_index_2),
510                                         &index_start, &index_offset);
511
512                 squashfs_get_cached_block(s, index->name, index_start,
513                                         index_offset, index->size + 1,
514                                         &index_start, &index_offset);
515
516                 index->name[index->size + 1] = '\0';
517
518                 if (strcmp(index->name, str) > 0)
519                         break;
520
521                 length = index->index;
522                 *next_block = index->start_block + sblk->directory_table_start;
523         }
524
525         *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
526         return length;
527 }
528
529                 
530 static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir)
531 {
532         struct inode *i = file->f_dentry->d_inode;
533         struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
534         struct squashfs_super_block *sblk = &msblk->sblk;
535         long long next_block = SQUASHFS_I(i)->start_block +
536                 sblk->directory_table_start;
537         int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0,
538                 dir_count;
539         struct squashfs_dir_header_2 dirh;
540         char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1];
541         struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer;
542
543         TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset);
544
545         length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
546                                 SQUASHFS_I(i)->u.s2.directory_index_start,
547                                 SQUASHFS_I(i)->u.s2.directory_index_offset,
548                                 SQUASHFS_I(i)->u.s2.directory_index_count,
549                                 file->f_pos);
550
551         while (length < i_size_read(i)) {
552                 /* read directory header */
553                 if (msblk->swap) {
554                         struct squashfs_dir_header_2 sdirh;
555                         
556                         if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
557                                         next_block, next_offset, sizeof(sdirh),
558                                         &next_block, &next_offset))
559                                 goto failed_read;
560
561                         length += sizeof(sdirh);
562                         SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
563                 } else {
564                         if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
565                                         next_block, next_offset, sizeof(dirh),
566                                         &next_block, &next_offset))
567                                 goto failed_read;
568
569                         length += sizeof(dirh);
570                 }
571
572                 dir_count = dirh.count + 1;
573                 while (dir_count--) {
574                         if (msblk->swap) {
575                                 struct squashfs_dir_entry_2 sdire;
576                                 if (!squashfs_get_cached_block(i->i_sb, (char *)
577                                                 &sdire, next_block, next_offset,
578                                                 sizeof(sdire), &next_block,
579                                                 &next_offset))
580                                         goto failed_read;
581                                 
582                                 length += sizeof(sdire);
583                                 SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
584                         } else {
585                                 if (!squashfs_get_cached_block(i->i_sb, (char *)
586                                                 dire, next_block, next_offset,
587                                                 sizeof(*dire), &next_block,
588                                                 &next_offset))
589                                         goto failed_read;
590
591                                 length += sizeof(*dire);
592                         }
593
594                         if (!squashfs_get_cached_block(i->i_sb, dire->name,
595                                                 next_block, next_offset,
596                                                 dire->size + 1, &next_block,
597                                                 &next_offset))
598                                 goto failed_read;
599
600                         length += dire->size + 1;
601
602                         if (file->f_pos >= length)
603                                 continue;
604
605                         dire->name[dire->size + 1] = '\0';
606
607                         TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n",
608                                         (unsigned int) dirent, dire->name,
609                                         dire->size + 1, (int) file->f_pos,
610                                         dirh.start_block, dire->offset,
611                                         squashfs_filetype_table[dire->type]);
612
613                         if (filldir(dirent, dire->name, dire->size + 1,
614                                         file->f_pos, SQUASHFS_MK_VFS_INODE(
615                                         dirh.start_block, dire->offset),
616                                         squashfs_filetype_table[dire->type])
617                                         < 0) {
618                                 TRACE("Filldir returned less than 0\n");
619                                 goto finish;
620                         }
621                         file->f_pos = length;
622                         dirs_read++;
623                 }
624         }
625
626 finish:
627         return dirs_read;
628
629 failed_read:
630         ERROR("Unable to read directory block [%llx:%x]\n", next_block,
631                 next_offset);
632         return 0;
633 }
634
635
636 static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry,
637                                 struct nameidata *nd)
638 {
639         const unsigned char *name = dentry->d_name.name;
640         int len = dentry->d_name.len;
641         struct inode *inode = NULL;
642         struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
643         struct squashfs_super_block *sblk = &msblk->sblk;
644         long long next_block = SQUASHFS_I(i)->start_block +
645                                 sblk->directory_table_start;
646         int next_offset = SQUASHFS_I(i)->offset, length = 0,
647                                 dir_count;
648         struct squashfs_dir_header_2 dirh;
649         char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN];
650         struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer;
651         int sorted = sblk->s_major == 2 && sblk->s_minor >= 1;
652
653         TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
654
655         if (len > SQUASHFS_NAME_LEN)
656                 goto exit_loop;
657
658         length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
659                                 SQUASHFS_I(i)->u.s2.directory_index_start,
660                                 SQUASHFS_I(i)->u.s2.directory_index_offset,
661                                 SQUASHFS_I(i)->u.s2.directory_index_count, name,
662                                 len);
663
664         while (length < i_size_read(i)) {
665                 /* read directory header */
666                 if (msblk->swap) {
667                         struct squashfs_dir_header_2 sdirh;
668                         if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
669                                         next_block, next_offset, sizeof(sdirh),
670                                         &next_block, &next_offset))
671                                 goto failed_read;
672
673                         length += sizeof(sdirh);
674                         SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
675                 } else {
676                         if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
677                                         next_block, next_offset, sizeof(dirh),
678                                         &next_block, &next_offset))
679                                 goto failed_read;
680
681                         length += sizeof(dirh);
682                 }
683
684                 dir_count = dirh.count + 1;
685                 while (dir_count--) {
686                         if (msblk->swap) {
687                                 struct squashfs_dir_entry_2 sdire;
688                                 if (!squashfs_get_cached_block(i->i_sb, (char *)
689                                                 &sdire, next_block,next_offset,
690                                                 sizeof(sdire), &next_block,
691                                                 &next_offset))
692                                         goto failed_read;
693                                 
694                                 length += sizeof(sdire);
695                                 SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
696                         } else {
697                                 if (!squashfs_get_cached_block(i->i_sb, (char *)
698                                                 dire, next_block,next_offset,
699                                                 sizeof(*dire), &next_block,
700                                                 &next_offset))
701                                         goto failed_read;
702
703                                 length += sizeof(*dire);
704                         }
705
706                         if (!squashfs_get_cached_block(i->i_sb, dire->name,
707                                         next_block, next_offset, dire->size + 1,
708                                         &next_block, &next_offset))
709                                 goto failed_read;
710
711                         length += dire->size + 1;
712
713                         if (sorted && name[0] < dire->name[0])
714                                 goto exit_loop;
715
716                         if ((len == dire->size + 1) && !strncmp(name,
717                                                 dire->name, len)) {
718                                 squashfs_inode_t ino =
719                                         SQUASHFS_MKINODE(dirh.start_block,
720                                         dire->offset);
721
722                                 TRACE("calling squashfs_iget for directory "
723                                         "entry %s, inode %x:%x, %lld\n", name,
724                                         dirh.start_block, dire->offset, ino);
725
726                                 inode = (msblk->iget)(i->i_sb, ino);
727
728                                 goto exit_loop;
729                         }
730                 }
731         }
732
733 exit_loop:
734         d_add(dentry, inode);
735         return ERR_PTR(0);
736
737 failed_read:
738         ERROR("Unable to read directory block [%llx:%x]\n", next_block,
739                 next_offset);
740         goto exit_loop;
741 }
742
743
744 int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
745 {
746         struct squashfs_super_block *sblk = &msblk->sblk;
747
748         msblk->iget = squashfs_iget_2;
749         msblk->read_fragment_index_table = read_fragment_index_table_2;
750
751         sblk->bytes_used = sblk->bytes_used_2;
752         sblk->uid_start = sblk->uid_start_2;
753         sblk->guid_start = sblk->guid_start_2;
754         sblk->inode_table_start = sblk->inode_table_start_2;
755         sblk->directory_table_start = sblk->directory_table_start_2;
756         sblk->fragment_table_start = sblk->fragment_table_start_2;
757
758         return 1;
759 }