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