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