Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Squashfs - a compressed read only filesystem for Linux
0004  *
0005  * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
0006  * Phillip Lougher <phillip@squashfs.org.uk>
0007  *
0008  * dir.c
0009  */
0010 
0011 /*
0012  * This file implements code to read directories from disk.
0013  *
0014  * See namei.c for a description of directory organisation on disk.
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  * Lookup offset (f_pos) in the directory index, returning the
0032  * metadata block containing it.
0033  *
0034  * If we get an error reading the index then return the part of the index
0035  * (if any) we have managed to read - the index isn't essential, just
0036  * quicker.
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      * Translate from external f_pos to the internal f_pos.  This
0052      * is offset by 3 because we invent "." and ".." entries which are
0053      * not actually stored in the directory.
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              * Found the index we're looking for.
0069              */
0070             break;
0071 
0072         size = le32_to_cpu(dir_index.size) + 1;
0073 
0074         /* size should never be larger than SQUASHFS_NAME_LEN */
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      * Translate back from internal f_pos to external f_pos.
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      * Return "." and  ".." entries as the first two filenames in the
0117      * directory.  To maximise compression these two entries are not
0118      * stored in the directory, and so we invent them here.
0119      *
0120      * It also means that the external f_pos is offset by 3 from the
0121      * on-disk directory f_pos.
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          * Read directory header
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              * Read directory entry.
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             /* size should never be larger than SQUASHFS_NAME_LEN */
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 };