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 39static 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 */ 51static 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 unsigned int size; 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 size = le32_to_cpu(dir_index.size) + 1; 86 87 /* size should never be larger than SQUASHFS_NAME_LEN */ 88 if (size > SQUASHFS_NAME_LEN) 89 break; 90 91 err = squashfs_read_metadata(sb, NULL, &index_start, 92 &index_offset, size); 93 if (err < 0) 94 break; 95 96 length = index; 97 *next_block = le32_to_cpu(dir_index.start_block) + 98 msblk->directory_table; 99 } 100 101 *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; 102 103 /* 104 * Translate back from internal f_pos to external f_pos. 105 */ 106 return length + 3; 107} 108 109 110static int squashfs_readdir(struct file *file, struct dir_context *ctx) 111{ 112 struct inode *inode = file_inode(file); 113 struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; 114 u64 block = squashfs_i(inode)->start + msblk->directory_table; 115 int offset = squashfs_i(inode)->offset, length, err; 116 unsigned int inode_number, dir_count, size, type; 117 struct squashfs_dir_header dirh; 118 struct squashfs_dir_entry *dire; 119 120 TRACE("Entered squashfs_readdir [%llx:%x]\n", block, offset); 121 122 dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL); 123 if (dire == NULL) { 124 ERROR("Failed to allocate squashfs_dir_entry\n"); 125 goto finish; 126 } 127 128 /* 129 * Return "." and ".." entries as the first two filenames in the 130 * directory. To maximise compression these two entries are not 131 * stored in the directory, and so we invent them here. 132 * 133 * It also means that the external f_pos is offset by 3 from the 134 * on-disk directory f_pos. 135 */ 136 while (ctx->pos < 3) { 137 char *name; 138 int i_ino; 139 140 if (ctx->pos == 0) { 141 name = "."; 142 size = 1; 143 i_ino = inode->i_ino; 144 } else { 145 name = ".."; 146 size = 2; 147 i_ino = squashfs_i(inode)->parent; 148 } 149 150 if (!dir_emit(ctx, name, size, i_ino, 151 squashfs_filetype_table[1])) 152 goto finish; 153 154 ctx->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 ctx->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 (ctx->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 if (type > SQUASHFS_MAX_DIR_TYPE) 210 goto failed_read; 211 212 if (!dir_emit(ctx, dire->name, size, 213 inode_number, 214 squashfs_filetype_table[type])) 215 goto finish; 216 217 ctx->pos = length; 218 } 219 } 220 221finish: 222 kfree(dire); 223 return 0; 224 225failed_read: 226 ERROR("Unable to read directory block [%llx:%x]\n", block, offset); 227 kfree(dire); 228 return 0; 229} 230 231 232const struct file_operations squashfs_dir_ops = { 233 .read = generic_read_dir, 234 .iterate = squashfs_readdir, 235 .llseek = default_llseek, 236}; 237