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