0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #include <linux/fs.h>
0018 #include <linux/vfs.h>
0019 #include <linux/slab.h>
0020
0021 #include "squashfs_fs.h"
0022 #include "squashfs_fs_sb.h"
0023 #include "squashfs_fs_i.h"
0024 #include "squashfs.h"
0025
0026 static const unsigned char squashfs_filetype_table[] = {
0027 DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
0028 };
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038 static int get_dir_index_using_offset(struct super_block *sb,
0039 u64 *next_block, int *next_offset, u64 index_start, int index_offset,
0040 int i_count, u64 f_pos)
0041 {
0042 struct squashfs_sb_info *msblk = sb->s_fs_info;
0043 int err, i, index, length = 0;
0044 unsigned int size;
0045 struct squashfs_dir_index dir_index;
0046
0047 TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %lld\n",
0048 i_count, f_pos);
0049
0050
0051
0052
0053
0054
0055 if (f_pos <= 3)
0056 return f_pos;
0057 f_pos -= 3;
0058
0059 for (i = 0; i < i_count; i++) {
0060 err = squashfs_read_metadata(sb, &dir_index, &index_start,
0061 &index_offset, sizeof(dir_index));
0062 if (err < 0)
0063 break;
0064
0065 index = le32_to_cpu(dir_index.index);
0066 if (index > f_pos)
0067
0068
0069
0070 break;
0071
0072 size = le32_to_cpu(dir_index.size) + 1;
0073
0074
0075 if (size > SQUASHFS_NAME_LEN)
0076 break;
0077
0078 err = squashfs_read_metadata(sb, NULL, &index_start,
0079 &index_offset, size);
0080 if (err < 0)
0081 break;
0082
0083 length = index;
0084 *next_block = le32_to_cpu(dir_index.start_block) +
0085 msblk->directory_table;
0086 }
0087
0088 *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
0089
0090
0091
0092
0093 return length + 3;
0094 }
0095
0096
0097 static int squashfs_readdir(struct file *file, struct dir_context *ctx)
0098 {
0099 struct inode *inode = file_inode(file);
0100 struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
0101 u64 block = squashfs_i(inode)->start + msblk->directory_table;
0102 int offset = squashfs_i(inode)->offset, length, err;
0103 unsigned int inode_number, dir_count, size, type;
0104 struct squashfs_dir_header dirh;
0105 struct squashfs_dir_entry *dire;
0106
0107 TRACE("Entered squashfs_readdir [%llx:%x]\n", block, offset);
0108
0109 dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
0110 if (dire == NULL) {
0111 ERROR("Failed to allocate squashfs_dir_entry\n");
0112 goto finish;
0113 }
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123 while (ctx->pos < 3) {
0124 char *name;
0125 int i_ino;
0126
0127 if (ctx->pos == 0) {
0128 name = ".";
0129 size = 1;
0130 i_ino = inode->i_ino;
0131 } else {
0132 name = "..";
0133 size = 2;
0134 i_ino = squashfs_i(inode)->parent;
0135 }
0136
0137 if (!dir_emit(ctx, name, size, i_ino,
0138 squashfs_filetype_table[1]))
0139 goto finish;
0140
0141 ctx->pos += size;
0142 }
0143
0144 length = get_dir_index_using_offset(inode->i_sb, &block, &offset,
0145 squashfs_i(inode)->dir_idx_start,
0146 squashfs_i(inode)->dir_idx_offset,
0147 squashfs_i(inode)->dir_idx_cnt,
0148 ctx->pos);
0149
0150 while (length < i_size_read(inode)) {
0151
0152
0153
0154 err = squashfs_read_metadata(inode->i_sb, &dirh, &block,
0155 &offset, sizeof(dirh));
0156 if (err < 0)
0157 goto failed_read;
0158
0159 length += sizeof(dirh);
0160
0161 dir_count = le32_to_cpu(dirh.count) + 1;
0162
0163 if (dir_count > SQUASHFS_DIR_COUNT)
0164 goto failed_read;
0165
0166 while (dir_count--) {
0167
0168
0169
0170 err = squashfs_read_metadata(inode->i_sb, dire, &block,
0171 &offset, sizeof(*dire));
0172 if (err < 0)
0173 goto failed_read;
0174
0175 size = le16_to_cpu(dire->size) + 1;
0176
0177
0178 if (size > SQUASHFS_NAME_LEN)
0179 goto failed_read;
0180
0181 err = squashfs_read_metadata(inode->i_sb, dire->name,
0182 &block, &offset, size);
0183 if (err < 0)
0184 goto failed_read;
0185
0186 length += sizeof(*dire) + size;
0187
0188 if (ctx->pos >= length)
0189 continue;
0190
0191 dire->name[size] = '\0';
0192 inode_number = le32_to_cpu(dirh.inode_number) +
0193 ((short) le16_to_cpu(dire->inode_number));
0194 type = le16_to_cpu(dire->type);
0195
0196 if (type > SQUASHFS_MAX_DIR_TYPE)
0197 goto failed_read;
0198
0199 if (!dir_emit(ctx, dire->name, size,
0200 inode_number,
0201 squashfs_filetype_table[type]))
0202 goto finish;
0203
0204 ctx->pos = length;
0205 }
0206 }
0207
0208 finish:
0209 kfree(dire);
0210 return 0;
0211
0212 failed_read:
0213 ERROR("Unable to read directory block [%llx:%x]\n", block, offset);
0214 kfree(dire);
0215 return 0;
0216 }
0217
0218
0219 const struct file_operations squashfs_dir_ops = {
0220 .read = generic_read_dir,
0221 .iterate_shared = squashfs_readdir,
0222 .llseek = generic_file_llseek,
0223 };