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