xref: /openbmc/linux/fs/squashfs/namei.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
1*68252eb5SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2c88da2c9SPhillip Lougher /*
3c88da2c9SPhillip Lougher  * Squashfs - a compressed read only filesystem for Linux
4c88da2c9SPhillip Lougher  *
5c88da2c9SPhillip Lougher  * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
6d7f2ff67SPhillip Lougher  * Phillip Lougher <phillip@squashfs.org.uk>
7c88da2c9SPhillip Lougher  *
8c88da2c9SPhillip Lougher  * namei.c
9c88da2c9SPhillip Lougher  */
10c88da2c9SPhillip Lougher 
11c88da2c9SPhillip Lougher /*
12c88da2c9SPhillip Lougher  * This file implements code to do filename lookup in directories.
13c88da2c9SPhillip Lougher  *
14c88da2c9SPhillip Lougher  * Like inodes, directories are packed into compressed metadata blocks, stored
15c88da2c9SPhillip Lougher  * in a directory table.  Directories are accessed using the start address of
16c88da2c9SPhillip Lougher  * the metablock containing the directory and the offset into the
17c88da2c9SPhillip Lougher  * decompressed block (<block, offset>).
18c88da2c9SPhillip Lougher  *
19c88da2c9SPhillip Lougher  * Directories are organised in a slightly complex way, and are not simply
20c88da2c9SPhillip Lougher  * a list of file names.  The organisation takes advantage of the
21c88da2c9SPhillip Lougher  * fact that (in most cases) the inodes of the files will be in the same
22c88da2c9SPhillip Lougher  * compressed metadata block, and therefore, can share the start block.
23c88da2c9SPhillip Lougher  * Directories are therefore organised in a two level list, a directory
24c88da2c9SPhillip Lougher  * header containing the shared start block value, and a sequence of directory
25c88da2c9SPhillip Lougher  * entries, each of which share the shared start block.  A new directory header
26c88da2c9SPhillip Lougher  * is written once/if the inode start block changes.  The directory
27c88da2c9SPhillip Lougher  * header/directory entry list is repeated as many times as necessary.
28c88da2c9SPhillip Lougher  *
29c88da2c9SPhillip Lougher  * Directories are sorted, and can contain a directory index to speed up
30c88da2c9SPhillip Lougher  * file lookup.  Directory indexes store one entry per metablock, each entry
31c88da2c9SPhillip Lougher  * storing the index/filename mapping to the first directory header
32c88da2c9SPhillip Lougher  * in each metadata block.  Directories are sorted in alphabetical order,
33c88da2c9SPhillip Lougher  * and at lookup the index is scanned linearly looking for the first filename
34c88da2c9SPhillip Lougher  * alphabetically larger than the filename being looked up.  At this point the
35c88da2c9SPhillip Lougher  * location of the metadata block the filename is in has been found.
36c88da2c9SPhillip Lougher  * The general idea of the index is ensure only one metadata block needs to be
37c88da2c9SPhillip Lougher  * decompressed to do a lookup irrespective of the length of the directory.
38c88da2c9SPhillip Lougher  * This scheme has the advantage that it doesn't require extra memory overhead
39c88da2c9SPhillip Lougher  * and doesn't require much extra storage on disk.
40c88da2c9SPhillip Lougher  */
41c88da2c9SPhillip Lougher 
42c88da2c9SPhillip Lougher #include <linux/fs.h>
43c88da2c9SPhillip Lougher #include <linux/vfs.h>
44c88da2c9SPhillip Lougher #include <linux/slab.h>
45c88da2c9SPhillip Lougher #include <linux/string.h>
46c88da2c9SPhillip Lougher #include <linux/dcache.h>
4767f66cc6SPhillip Lougher #include <linux/xattr.h>
48c88da2c9SPhillip Lougher 
49c88da2c9SPhillip Lougher #include "squashfs_fs.h"
50c88da2c9SPhillip Lougher #include "squashfs_fs_sb.h"
51c88da2c9SPhillip Lougher #include "squashfs_fs_i.h"
52c88da2c9SPhillip Lougher #include "squashfs.h"
5301e5b4e4SPhillip Lougher #include "xattr.h"
54c88da2c9SPhillip Lougher 
55c88da2c9SPhillip Lougher /*
56c88da2c9SPhillip Lougher  * Lookup name in the directory index, returning the location of the metadata
57c88da2c9SPhillip Lougher  * block containing it, and the directory index this represents.
58c88da2c9SPhillip Lougher  *
59c88da2c9SPhillip Lougher  * If we get an error reading the index then return the part of the index
60c88da2c9SPhillip Lougher  * (if any) we have managed to read - the index isn't essential, just
61c88da2c9SPhillip Lougher  * quicker.
62c88da2c9SPhillip Lougher  */
get_dir_index_using_name(struct super_block * sb,u64 * next_block,int * next_offset,u64 index_start,int index_offset,int i_count,const char * name,int len)63c88da2c9SPhillip Lougher static int get_dir_index_using_name(struct super_block *sb,
64c88da2c9SPhillip Lougher 			u64 *next_block, int *next_offset, u64 index_start,
65c88da2c9SPhillip Lougher 			int index_offset, int i_count, const char *name,
66c88da2c9SPhillip Lougher 			int len)
67c88da2c9SPhillip Lougher {
68c88da2c9SPhillip Lougher 	struct squashfs_sb_info *msblk = sb->s_fs_info;
6928d7b568SDan Carpenter 	int i, length = 0, err;
7028d7b568SDan Carpenter 	unsigned int size;
71c88da2c9SPhillip Lougher 	struct squashfs_dir_index *index;
72c88da2c9SPhillip Lougher 	char *str;
73c88da2c9SPhillip Lougher 
74c88da2c9SPhillip Lougher 	TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
75c88da2c9SPhillip Lougher 
76c88da2c9SPhillip Lougher 	index = kmalloc(sizeof(*index) + SQUASHFS_NAME_LEN * 2 + 2, GFP_KERNEL);
77c88da2c9SPhillip Lougher 	if (index == NULL) {
78c88da2c9SPhillip Lougher 		ERROR("Failed to allocate squashfs_dir_index\n");
79c88da2c9SPhillip Lougher 		goto out;
80c88da2c9SPhillip Lougher 	}
81c88da2c9SPhillip Lougher 
82c88da2c9SPhillip Lougher 	str = &index->name[SQUASHFS_NAME_LEN + 1];
83c88da2c9SPhillip Lougher 	strncpy(str, name, len);
84c88da2c9SPhillip Lougher 	str[len] = '\0';
85c88da2c9SPhillip Lougher 
86c88da2c9SPhillip Lougher 	for (i = 0; i < i_count; i++) {
87c88da2c9SPhillip Lougher 		err = squashfs_read_metadata(sb, index, &index_start,
88c88da2c9SPhillip Lougher 					&index_offset, sizeof(*index));
89c88da2c9SPhillip Lougher 		if (err < 0)
90c88da2c9SPhillip Lougher 			break;
91c88da2c9SPhillip Lougher 
92c88da2c9SPhillip Lougher 
93c88da2c9SPhillip Lougher 		size = le32_to_cpu(index->size) + 1;
949dbc41d5SPhillip Lougher 		if (size > SQUASHFS_NAME_LEN)
9528d7b568SDan Carpenter 			break;
96c88da2c9SPhillip Lougher 
97c88da2c9SPhillip Lougher 		err = squashfs_read_metadata(sb, index->name, &index_start,
98c88da2c9SPhillip Lougher 					&index_offset, size);
99c88da2c9SPhillip Lougher 		if (err < 0)
100c88da2c9SPhillip Lougher 			break;
101c88da2c9SPhillip Lougher 
102c88da2c9SPhillip Lougher 		index->name[size] = '\0';
103c88da2c9SPhillip Lougher 
104c88da2c9SPhillip Lougher 		if (strcmp(index->name, str) > 0)
105c88da2c9SPhillip Lougher 			break;
106c88da2c9SPhillip Lougher 
107c88da2c9SPhillip Lougher 		length = le32_to_cpu(index->index);
108c88da2c9SPhillip Lougher 		*next_block = le32_to_cpu(index->start_block) +
109c88da2c9SPhillip Lougher 					msblk->directory_table;
110c88da2c9SPhillip Lougher 	}
111c88da2c9SPhillip Lougher 
112c88da2c9SPhillip Lougher 	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
113c88da2c9SPhillip Lougher 	kfree(index);
114c88da2c9SPhillip Lougher 
115c88da2c9SPhillip Lougher out:
116c88da2c9SPhillip Lougher 	/*
117c88da2c9SPhillip Lougher 	 * Return index (f_pos) of the looked up metadata block.  Translate
118c88da2c9SPhillip Lougher 	 * from internal f_pos to external f_pos which is offset by 3 because
119c88da2c9SPhillip Lougher 	 * we invent "." and ".." entries which are not actually stored in the
120c88da2c9SPhillip Lougher 	 * directory.
121c88da2c9SPhillip Lougher 	 */
122c88da2c9SPhillip Lougher 	return length + 3;
123c88da2c9SPhillip Lougher }
124c88da2c9SPhillip Lougher 
125c88da2c9SPhillip Lougher 
squashfs_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)126c88da2c9SPhillip Lougher static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry,
12700cd8dd3SAl Viro 				 unsigned int flags)
128c88da2c9SPhillip Lougher {
129c88da2c9SPhillip Lougher 	const unsigned char *name = dentry->d_name.name;
130c88da2c9SPhillip Lougher 	int len = dentry->d_name.len;
131c88da2c9SPhillip Lougher 	struct inode *inode = NULL;
132c88da2c9SPhillip Lougher 	struct squashfs_sb_info *msblk = dir->i_sb->s_fs_info;
133c88da2c9SPhillip Lougher 	struct squashfs_dir_header dirh;
134c88da2c9SPhillip Lougher 	struct squashfs_dir_entry *dire;
135c88da2c9SPhillip Lougher 	u64 block = squashfs_i(dir)->start + msblk->directory_table;
136c88da2c9SPhillip Lougher 	int offset = squashfs_i(dir)->offset;
13752e9ce1cSPhillip Lougher 	int err, length;
13852e9ce1cSPhillip Lougher 	unsigned int dir_count, size;
139c88da2c9SPhillip Lougher 
140c88da2c9SPhillip Lougher 	TRACE("Entered squashfs_lookup [%llx:%x]\n", block, offset);
141c88da2c9SPhillip Lougher 
142c88da2c9SPhillip Lougher 	dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
143c88da2c9SPhillip Lougher 	if (dire == NULL) {
144c88da2c9SPhillip Lougher 		ERROR("Failed to allocate squashfs_dir_entry\n");
145c88da2c9SPhillip Lougher 		return ERR_PTR(-ENOMEM);
146c88da2c9SPhillip Lougher 	}
147c88da2c9SPhillip Lougher 
148c88da2c9SPhillip Lougher 	if (len > SQUASHFS_NAME_LEN) {
149c88da2c9SPhillip Lougher 		err = -ENAMETOOLONG;
150c88da2c9SPhillip Lougher 		goto failed;
151c88da2c9SPhillip Lougher 	}
152c88da2c9SPhillip Lougher 
153c88da2c9SPhillip Lougher 	length = get_dir_index_using_name(dir->i_sb, &block, &offset,
154c88da2c9SPhillip Lougher 				squashfs_i(dir)->dir_idx_start,
155c88da2c9SPhillip Lougher 				squashfs_i(dir)->dir_idx_offset,
156c88da2c9SPhillip Lougher 				squashfs_i(dir)->dir_idx_cnt, name, len);
157c88da2c9SPhillip Lougher 
158c88da2c9SPhillip Lougher 	while (length < i_size_read(dir)) {
159c88da2c9SPhillip Lougher 		/*
160c88da2c9SPhillip Lougher 		 * Read directory header.
161c88da2c9SPhillip Lougher 		 */
162c88da2c9SPhillip Lougher 		err = squashfs_read_metadata(dir->i_sb, &dirh, &block,
163c88da2c9SPhillip Lougher 				&offset, sizeof(dirh));
164c88da2c9SPhillip Lougher 		if (err < 0)
165c88da2c9SPhillip Lougher 			goto read_failure;
166c88da2c9SPhillip Lougher 
167c88da2c9SPhillip Lougher 		length += sizeof(dirh);
168c88da2c9SPhillip Lougher 
169c88da2c9SPhillip Lougher 		dir_count = le32_to_cpu(dirh.count) + 1;
17044cff8a9SPhillip Lougher 
1714826d83dSAjeet Yadav 		if (dir_count > SQUASHFS_DIR_COUNT)
17244cff8a9SPhillip Lougher 			goto data_error;
17344cff8a9SPhillip Lougher 
174c88da2c9SPhillip Lougher 		while (dir_count--) {
175c88da2c9SPhillip Lougher 			/*
176c88da2c9SPhillip Lougher 			 * Read directory entry.
177c88da2c9SPhillip Lougher 			 */
178c88da2c9SPhillip Lougher 			err = squashfs_read_metadata(dir->i_sb, dire, &block,
179c88da2c9SPhillip Lougher 					&offset, sizeof(*dire));
180c88da2c9SPhillip Lougher 			if (err < 0)
181c88da2c9SPhillip Lougher 				goto read_failure;
182c88da2c9SPhillip Lougher 
183c88da2c9SPhillip Lougher 			size = le16_to_cpu(dire->size) + 1;
184c88da2c9SPhillip Lougher 
18544cff8a9SPhillip Lougher 			/* size should never be larger than SQUASHFS_NAME_LEN */
18644cff8a9SPhillip Lougher 			if (size > SQUASHFS_NAME_LEN)
18744cff8a9SPhillip Lougher 				goto data_error;
18844cff8a9SPhillip Lougher 
189c88da2c9SPhillip Lougher 			err = squashfs_read_metadata(dir->i_sb, dire->name,
190c88da2c9SPhillip Lougher 					&block, &offset, size);
191c88da2c9SPhillip Lougher 			if (err < 0)
192c88da2c9SPhillip Lougher 				goto read_failure;
193c88da2c9SPhillip Lougher 
194c88da2c9SPhillip Lougher 			length += sizeof(*dire) + size;
195c88da2c9SPhillip Lougher 
196c88da2c9SPhillip Lougher 			if (name[0] < dire->name[0])
197c88da2c9SPhillip Lougher 				goto exit_lookup;
198c88da2c9SPhillip Lougher 
199c88da2c9SPhillip Lougher 			if (len == size && !strncmp(name, dire->name, len)) {
200c88da2c9SPhillip Lougher 				unsigned int blk, off, ino_num;
201c88da2c9SPhillip Lougher 				long long ino;
202c88da2c9SPhillip Lougher 				blk = le32_to_cpu(dirh.start_block);
203c88da2c9SPhillip Lougher 				off = le16_to_cpu(dire->offset);
204c88da2c9SPhillip Lougher 				ino_num = le32_to_cpu(dirh.inode_number) +
205c88da2c9SPhillip Lougher 					(short) le16_to_cpu(dire->inode_number);
206c88da2c9SPhillip Lougher 				ino = SQUASHFS_MKINODE(blk, off);
207c88da2c9SPhillip Lougher 
208c88da2c9SPhillip Lougher 				TRACE("calling squashfs_iget for directory "
209c88da2c9SPhillip Lougher 					"entry %s, inode  %x:%x, %d\n", name,
210c88da2c9SPhillip Lougher 					blk, off, ino_num);
211c88da2c9SPhillip Lougher 
212c88da2c9SPhillip Lougher 				inode = squashfs_iget(dir->i_sb, ino, ino_num);
213c88da2c9SPhillip Lougher 				goto exit_lookup;
214c88da2c9SPhillip Lougher 			}
215c88da2c9SPhillip Lougher 		}
216c88da2c9SPhillip Lougher 	}
217c88da2c9SPhillip Lougher 
218c88da2c9SPhillip Lougher exit_lookup:
219c88da2c9SPhillip Lougher 	kfree(dire);
220c88da2c9SPhillip Lougher 	return d_splice_alias(inode, dentry);
221c88da2c9SPhillip Lougher 
22244cff8a9SPhillip Lougher data_error:
22344cff8a9SPhillip Lougher 	err = -EIO;
22444cff8a9SPhillip Lougher 
225c88da2c9SPhillip Lougher read_failure:
226c88da2c9SPhillip Lougher 	ERROR("Unable to read directory block [%llx:%x]\n",
227c88da2c9SPhillip Lougher 		squashfs_i(dir)->start + msblk->directory_table,
228c88da2c9SPhillip Lougher 		squashfs_i(dir)->offset);
229c88da2c9SPhillip Lougher failed:
230c88da2c9SPhillip Lougher 	kfree(dire);
231c88da2c9SPhillip Lougher 	return ERR_PTR(err);
232c88da2c9SPhillip Lougher }
233c88da2c9SPhillip Lougher 
234c88da2c9SPhillip Lougher 
235c88da2c9SPhillip Lougher const struct inode_operations squashfs_dir_inode_ops = {
23667f66cc6SPhillip Lougher 	.lookup = squashfs_lookup,
23767f66cc6SPhillip Lougher 	.listxattr = squashfs_listxattr
238c88da2c9SPhillip Lougher };
239