Home | History | Annotate | Download | only in squashfs
      1 /*
      2  * Squashfs - a compressed read only filesystem for Linux
      3  *
      4  * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
      5  * Phillip Lougher <phillip (at) lougher.demon.co.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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20  *
     21  * dir.c
     22  */
     23 
     24 /*
     25  * This file implements code to read directories from disk.
     26  *
     27  * See namei.c for a description of directory organisation on disk.
     28  */
     29 
     30 #include <linux/fs.h>
     31 #include <linux/vfs.h>
     32 #include <linux/slab.h>
     33 #include <linux/zlib.h>
     34 
     35 #include "squashfs_fs.h"
     36 #include "squashfs_fs_sb.h"
     37 #include "squashfs_fs_i.h"
     38 #include "squashfs.h"
     39 
     40 static const unsigned char squashfs_filetype_table[] = {
     41 	DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
     42 };
     43 
     44 /*
     45  * Lookup offset (f_pos) in the directory index, returning the
     46  * metadata block containing it.
     47  *
     48  * If we get an error reading the index then return the part of the index
     49  * (if any) we have managed to read - the index isn't essential, just
     50  * quicker.
     51  */
     52 static int get_dir_index_using_offset(struct super_block *sb,
     53 	u64 *next_block, int *next_offset, u64 index_start, int index_offset,
     54 	int i_count, u64 f_pos)
     55 {
     56 	struct squashfs_sb_info *msblk = sb->s_fs_info;
     57 	int err, i, index, length = 0;
     58 	struct squashfs_dir_index dir_index;
     59 
     60 	TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %lld\n",
     61 					i_count, f_pos);
     62 
     63 	/*
     64 	 * Translate from external f_pos to the internal f_pos.  This
     65 	 * is offset by 3 because we invent "." and ".." entries which are
     66 	 * not actually stored in the directory.
     67 	 */
     68 	if (f_pos < 3)
     69 		return f_pos;
     70 	f_pos -= 3;
     71 
     72 	for (i = 0; i < i_count; i++) {
     73 		err = squashfs_read_metadata(sb, &dir_index, &index_start,
     74 				&index_offset, sizeof(dir_index));
     75 		if (err < 0)
     76 			break;
     77 
     78 		index = le32_to_cpu(dir_index.index);
     79 		if (index > f_pos)
     80 			/*
     81 			 * Found the index we're looking for.
     82 			 */
     83 			break;
     84 
     85 		err = squashfs_read_metadata(sb, NULL, &index_start,
     86 				&index_offset, le32_to_cpu(dir_index.size) + 1);
     87 		if (err < 0)
     88 			break;
     89 
     90 		length = index;
     91 		*next_block = le32_to_cpu(dir_index.start_block) +
     92 					msblk->directory_table;
     93 	}
     94 
     95 	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
     96 
     97 	/*
     98 	 * Translate back from internal f_pos to external f_pos.
     99 	 */
    100 	return length + 3;
    101 }
    102 
    103 
    104 static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
    105 {
    106 	struct inode *inode = file->f_dentry->d_inode;
    107 	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
    108 	u64 block = squashfs_i(inode)->start + msblk->directory_table;
    109 	int offset = squashfs_i(inode)->offset, length = 0, dir_count, size,
    110 				type, err;
    111 	unsigned int inode_number;
    112 	struct squashfs_dir_header dirh;
    113 	struct squashfs_dir_entry *dire;
    114 
    115 	TRACE("Entered squashfs_readdir [%llx:%x]\n", block, offset);
    116 
    117 	dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
    118 	if (dire == NULL) {
    119 		ERROR("Failed to allocate squashfs_dir_entry\n");
    120 		goto finish;
    121 	}
    122 
    123 	/*
    124 	 * Return "." and  ".." entries as the first two filenames in the
    125 	 * directory.  To maximise compression these two entries are not
    126 	 * stored in the directory, and so we invent them here.
    127 	 *
    128 	 * It also means that the external f_pos is offset by 3 from the
    129 	 * on-disk directory f_pos.
    130 	 */
    131 	while (file->f_pos < 3) {
    132 		char *name;
    133 		int i_ino;
    134 
    135 		if (file->f_pos == 0) {
    136 			name = ".";
    137 			size = 1;
    138 			i_ino = inode->i_ino;
    139 		} else {
    140 			name = "..";
    141 			size = 2;
    142 			i_ino = squashfs_i(inode)->parent;
    143 		}
    144 
    145 		TRACE("Calling filldir(%p, %s, %d, %lld, %d, %d)\n",
    146 				dirent, name, size, file->f_pos, i_ino,
    147 				squashfs_filetype_table[1]);
    148 
    149 		if (filldir(dirent, name, size, file->f_pos, i_ino,
    150 				squashfs_filetype_table[1]) < 0) {
    151 				TRACE("Filldir returned less than 0\n");
    152 			goto finish;
    153 		}
    154 
    155 		file->f_pos += size;
    156 	}
    157 
    158 	length = get_dir_index_using_offset(inode->i_sb, &block, &offset,
    159 				squashfs_i(inode)->dir_idx_start,
    160 				squashfs_i(inode)->dir_idx_offset,
    161 				squashfs_i(inode)->dir_idx_cnt,
    162 				file->f_pos);
    163 
    164 	while (length < i_size_read(inode)) {
    165 		/*
    166 		 * Read directory header
    167 		 */
    168 		err = squashfs_read_metadata(inode->i_sb, &dirh, &block,
    169 					&offset, sizeof(dirh));
    170 		if (err < 0)
    171 			goto failed_read;
    172 
    173 		length += sizeof(dirh);
    174 
    175 		dir_count = le32_to_cpu(dirh.count) + 1;
    176 		while (dir_count--) {
    177 			/*
    178 			 * Read directory entry.
    179 			 */
    180 			err = squashfs_read_metadata(inode->i_sb, dire, &block,
    181 					&offset, sizeof(*dire));
    182 			if (err < 0)
    183 				goto failed_read;
    184 
    185 			size = le16_to_cpu(dire->size) + 1;
    186 
    187 			err = squashfs_read_metadata(inode->i_sb, dire->name,
    188 					&block, &offset, size);
    189 			if (err < 0)
    190 				goto failed_read;
    191 
    192 			length += sizeof(*dire) + size;
    193 
    194 			if (file->f_pos >= length)
    195 				continue;
    196 
    197 			dire->name[size] = '\0';
    198 			inode_number = le32_to_cpu(dirh.inode_number) +
    199 				((short) le16_to_cpu(dire->inode_number));
    200 			type = le16_to_cpu(dire->type);
    201 
    202 			TRACE("Calling filldir(%p, %s, %d, %lld, %x:%x, %d, %d)"
    203 					"\n", dirent, dire->name, size,
    204 					file->f_pos,
    205 					le32_to_cpu(dirh.start_block),
    206 					le16_to_cpu(dire->offset),
    207 					inode_number,
    208 					squashfs_filetype_table[type]);
    209 
    210 			if (filldir(dirent, dire->name, size, file->f_pos,
    211 					inode_number,
    212 					squashfs_filetype_table[type]) < 0) {
    213 				TRACE("Filldir returned less than 0\n");
    214 				goto finish;
    215 			}
    216 
    217 			file->f_pos = length;
    218 		}
    219 	}
    220 
    221 finish:
    222 	kfree(dire);
    223 	return 0;
    224 
    225 failed_read:
    226 	ERROR("Unable to read directory block [%llx:%x]\n", block, offset);
    227 	kfree(dire);
    228 	return 0;
    229 }
    230 
    231 
    232 const struct file_operations squashfs_dir_ops = {
    233 	.read = generic_read_dir,
    234 	.readdir = squashfs_readdir
    235 };
    236