cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dir.c (5355B)


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