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@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 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->f_dentry->d_inode; 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 = 0, 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 while (dir_count--) { 176 /* 177 * Read directory entry. 178 */ 179 err = squashfs_read_metadata(inode->i_sb, dire, &block, 180 &offset, sizeof(*dire)); 181 if (err < 0) 182 goto failed_read; 183 184 size = le16_to_cpu(dire->size) + 1; 185 186 err = squashfs_read_metadata(inode->i_sb, dire->name, 187 &block, &offset, size); 188 if (err < 0) 189 goto failed_read; 190 191 length += sizeof(*dire) + size; 192 193 if (file->f_pos >= length) 194 continue; 195 196 dire->name[size] = '\0'; 197 inode_number = le32_to_cpu(dirh.inode_number) + 198 ((short) le16_to_cpu(dire->inode_number)); 199 type = le16_to_cpu(dire->type); 200 201 TRACE("Calling filldir(%p, %s, %d, %lld, %x:%x, %d, %d)" 202 "\n", dirent, dire->name, size, 203 file->f_pos, 204 le32_to_cpu(dirh.start_block), 205 le16_to_cpu(dire->offset), 206 inode_number, 207 squashfs_filetype_table[type]); 208 209 if (filldir(dirent, dire->name, size, file->f_pos, 210 inode_number, 211 squashfs_filetype_table[type]) < 0) { 212 TRACE("Filldir returned less than 0\n"); 213 goto finish; 214 } 215 216 file->f_pos = length; 217 } 218 } 219 220 finish: 221 kfree(dire); 222 return 0; 223 224 failed_read: 225 ERROR("Unable to read directory block [%llx:%x]\n", block, offset); 226 kfree(dire); 227 return 0; 228 } 229 230 231 const struct file_operations squashfs_dir_ops = { 232 .read = generic_read_dir, 233 .readdir = squashfs_readdir 234 }; 235