brintos

brintos / linux-shallow public Read only

0
0
Text · 5.2 KiB · a2ade63 Raw
224 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Squashfs - a compressed read only filesystem for Linux4 *5 * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 20086 * Phillip Lougher <phillip@squashfs.org.uk>7 *8 * dir.c9 */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_SOCK28};29 30/*31 * Lookup offset (f_pos) in the directory index, returning the32 * metadata block containing it.33 *34 * If we get an error reading the index then return the part of the index35 * (if any) we have managed to read - the index isn't essential, just36 * 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.  This52	 * is offset by 3 because we invent "." and ".." entries which are53	 * 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 the117	 * directory.  To maximise compression these two entries are not118	 * 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 the121	 * 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 header153		 */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};224