| Phillip Lougher | c88da2c | 2009-01-05 08:46:23 +0000 | [diff] [blame] | 1 | /* | 
 | 2 |  * Squashfs - a compressed read only filesystem for Linux | 
 | 3 |  * | 
 | 4 |  * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | 
 | 5 |  * Phillip Lougher <phillip@lougher.demon.co.uk> | 
 | 6 |  * | 
 | 7 |  * This program is free software; you can redistribute it and/or | 
 | 8 |  * modify it under the terms of the GNU General Public License | 
 | 9 |  * as published by the Free Software Foundation; either version 2, | 
 | 10 |  * or (at your option) any later version. | 
 | 11 |  * | 
 | 12 |  * This program is distributed in the hope that it will be useful, | 
 | 13 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 14 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 15 |  * GNU General Public License for more details. | 
 | 16 |  * | 
 | 17 |  * You should have received a copy of the GNU General Public License | 
 | 18 |  * along with this program; if not, write to the Free Software | 
 | 19 |  * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
 | 20 |  * | 
 | 21 |  * namei.c | 
 | 22 |  */ | 
 | 23 |  | 
 | 24 | /* | 
 | 25 |  * This file implements code to do filename lookup in directories. | 
 | 26 |  * | 
 | 27 |  * Like inodes, directories are packed into compressed metadata blocks, stored | 
 | 28 |  * in a directory table.  Directories are accessed using the start address of | 
 | 29 |  * the metablock containing the directory and the offset into the | 
 | 30 |  * decompressed block (<block, offset>). | 
 | 31 |  * | 
 | 32 |  * Directories are organised in a slightly complex way, and are not simply | 
 | 33 |  * a list of file names.  The organisation takes advantage of the | 
 | 34 |  * fact that (in most cases) the inodes of the files will be in the same | 
 | 35 |  * compressed metadata block, and therefore, can share the start block. | 
 | 36 |  * Directories are therefore organised in a two level list, a directory | 
 | 37 |  * header containing the shared start block value, and a sequence of directory | 
 | 38 |  * entries, each of which share the shared start block.  A new directory header | 
 | 39 |  * is written once/if the inode start block changes.  The directory | 
 | 40 |  * header/directory entry list is repeated as many times as necessary. | 
 | 41 |  * | 
 | 42 |  * Directories are sorted, and can contain a directory index to speed up | 
 | 43 |  * file lookup.  Directory indexes store one entry per metablock, each entry | 
 | 44 |  * storing the index/filename mapping to the first directory header | 
 | 45 |  * in each metadata block.  Directories are sorted in alphabetical order, | 
 | 46 |  * and at lookup the index is scanned linearly looking for the first filename | 
 | 47 |  * alphabetically larger than the filename being looked up.  At this point the | 
 | 48 |  * location of the metadata block the filename is in has been found. | 
 | 49 |  * The general idea of the index is ensure only one metadata block needs to be | 
 | 50 |  * decompressed to do a lookup irrespective of the length of the directory. | 
 | 51 |  * This scheme has the advantage that it doesn't require extra memory overhead | 
 | 52 |  * and doesn't require much extra storage on disk. | 
 | 53 |  */ | 
 | 54 |  | 
 | 55 | #include <linux/fs.h> | 
 | 56 | #include <linux/vfs.h> | 
 | 57 | #include <linux/slab.h> | 
 | 58 | #include <linux/string.h> | 
 | 59 | #include <linux/dcache.h> | 
| Phillip Lougher | 67f66cc | 2010-05-17 04:06:56 +0100 | [diff] [blame] | 60 | #include <linux/xattr.h> | 
| Phillip Lougher | c88da2c | 2009-01-05 08:46:23 +0000 | [diff] [blame] | 61 |  | 
 | 62 | #include "squashfs_fs.h" | 
 | 63 | #include "squashfs_fs_sb.h" | 
 | 64 | #include "squashfs_fs_i.h" | 
 | 65 | #include "squashfs.h" | 
| Phillip Lougher | 01e5b4e | 2010-05-17 19:39:02 +0100 | [diff] [blame] | 66 | #include "xattr.h" | 
| Phillip Lougher | c88da2c | 2009-01-05 08:46:23 +0000 | [diff] [blame] | 67 |  | 
 | 68 | /* | 
 | 69 |  * Lookup name in the directory index, returning the location of the metadata | 
 | 70 |  * block containing it, and the directory index this represents. | 
 | 71 |  * | 
 | 72 |  * If we get an error reading the index then return the part of the index | 
 | 73 |  * (if any) we have managed to read - the index isn't essential, just | 
 | 74 |  * quicker. | 
 | 75 |  */ | 
 | 76 | static int get_dir_index_using_name(struct super_block *sb, | 
 | 77 | 			u64 *next_block, int *next_offset, u64 index_start, | 
 | 78 | 			int index_offset, int i_count, const char *name, | 
 | 79 | 			int len) | 
 | 80 | { | 
 | 81 | 	struct squashfs_sb_info *msblk = sb->s_fs_info; | 
 | 82 | 	int i, size, length = 0, err; | 
 | 83 | 	struct squashfs_dir_index *index; | 
 | 84 | 	char *str; | 
 | 85 |  | 
 | 86 | 	TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); | 
 | 87 |  | 
 | 88 | 	index = kmalloc(sizeof(*index) + SQUASHFS_NAME_LEN * 2 + 2, GFP_KERNEL); | 
 | 89 | 	if (index == NULL) { | 
 | 90 | 		ERROR("Failed to allocate squashfs_dir_index\n"); | 
 | 91 | 		goto out; | 
 | 92 | 	} | 
 | 93 |  | 
 | 94 | 	str = &index->name[SQUASHFS_NAME_LEN + 1]; | 
 | 95 | 	strncpy(str, name, len); | 
 | 96 | 	str[len] = '\0'; | 
 | 97 |  | 
 | 98 | 	for (i = 0; i < i_count; i++) { | 
 | 99 | 		err = squashfs_read_metadata(sb, index, &index_start, | 
 | 100 | 					&index_offset, sizeof(*index)); | 
 | 101 | 		if (err < 0) | 
 | 102 | 			break; | 
 | 103 |  | 
 | 104 |  | 
 | 105 | 		size = le32_to_cpu(index->size) + 1; | 
 | 106 |  | 
 | 107 | 		err = squashfs_read_metadata(sb, index->name, &index_start, | 
 | 108 | 					&index_offset, size); | 
 | 109 | 		if (err < 0) | 
 | 110 | 			break; | 
 | 111 |  | 
 | 112 | 		index->name[size] = '\0'; | 
 | 113 |  | 
 | 114 | 		if (strcmp(index->name, str) > 0) | 
 | 115 | 			break; | 
 | 116 |  | 
 | 117 | 		length = le32_to_cpu(index->index); | 
 | 118 | 		*next_block = le32_to_cpu(index->start_block) + | 
 | 119 | 					msblk->directory_table; | 
 | 120 | 	} | 
 | 121 |  | 
 | 122 | 	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; | 
 | 123 | 	kfree(index); | 
 | 124 |  | 
 | 125 | out: | 
 | 126 | 	/* | 
 | 127 | 	 * Return index (f_pos) of the looked up metadata block.  Translate | 
 | 128 | 	 * from internal f_pos to external f_pos which is offset by 3 because | 
 | 129 | 	 * we invent "." and ".." entries which are not actually stored in the | 
 | 130 | 	 * directory. | 
 | 131 | 	 */ | 
 | 132 | 	return length + 3; | 
 | 133 | } | 
 | 134 |  | 
 | 135 |  | 
 | 136 | static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry, | 
 | 137 | 				 struct nameidata *nd) | 
 | 138 | { | 
 | 139 | 	const unsigned char *name = dentry->d_name.name; | 
 | 140 | 	int len = dentry->d_name.len; | 
 | 141 | 	struct inode *inode = NULL; | 
 | 142 | 	struct squashfs_sb_info *msblk = dir->i_sb->s_fs_info; | 
 | 143 | 	struct squashfs_dir_header dirh; | 
 | 144 | 	struct squashfs_dir_entry *dire; | 
 | 145 | 	u64 block = squashfs_i(dir)->start + msblk->directory_table; | 
 | 146 | 	int offset = squashfs_i(dir)->offset; | 
 | 147 | 	int err, length = 0, dir_count, size; | 
 | 148 |  | 
 | 149 | 	TRACE("Entered squashfs_lookup [%llx:%x]\n", block, offset); | 
 | 150 |  | 
 | 151 | 	dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL); | 
 | 152 | 	if (dire == NULL) { | 
 | 153 | 		ERROR("Failed to allocate squashfs_dir_entry\n"); | 
 | 154 | 		return ERR_PTR(-ENOMEM); | 
 | 155 | 	} | 
 | 156 |  | 
 | 157 | 	if (len > SQUASHFS_NAME_LEN) { | 
 | 158 | 		err = -ENAMETOOLONG; | 
 | 159 | 		goto failed; | 
 | 160 | 	} | 
 | 161 |  | 
 | 162 | 	length = get_dir_index_using_name(dir->i_sb, &block, &offset, | 
 | 163 | 				squashfs_i(dir)->dir_idx_start, | 
 | 164 | 				squashfs_i(dir)->dir_idx_offset, | 
 | 165 | 				squashfs_i(dir)->dir_idx_cnt, name, len); | 
 | 166 |  | 
 | 167 | 	while (length < i_size_read(dir)) { | 
 | 168 | 		/* | 
 | 169 | 		 * Read directory header. | 
 | 170 | 		 */ | 
 | 171 | 		err = squashfs_read_metadata(dir->i_sb, &dirh, &block, | 
 | 172 | 				&offset, sizeof(dirh)); | 
 | 173 | 		if (err < 0) | 
 | 174 | 			goto read_failure; | 
 | 175 |  | 
 | 176 | 		length += sizeof(dirh); | 
 | 177 |  | 
 | 178 | 		dir_count = le32_to_cpu(dirh.count) + 1; | 
 | 179 | 		while (dir_count--) { | 
 | 180 | 			/* | 
 | 181 | 			 * Read directory entry. | 
 | 182 | 			 */ | 
 | 183 | 			err = squashfs_read_metadata(dir->i_sb, dire, &block, | 
 | 184 | 					&offset, sizeof(*dire)); | 
 | 185 | 			if (err < 0) | 
 | 186 | 				goto read_failure; | 
 | 187 |  | 
 | 188 | 			size = le16_to_cpu(dire->size) + 1; | 
 | 189 |  | 
 | 190 | 			err = squashfs_read_metadata(dir->i_sb, dire->name, | 
 | 191 | 					&block, &offset, size); | 
 | 192 | 			if (err < 0) | 
 | 193 | 				goto read_failure; | 
 | 194 |  | 
 | 195 | 			length += sizeof(*dire) + size; | 
 | 196 |  | 
 | 197 | 			if (name[0] < dire->name[0]) | 
 | 198 | 				goto exit_lookup; | 
 | 199 |  | 
 | 200 | 			if (len == size && !strncmp(name, dire->name, len)) { | 
 | 201 | 				unsigned int blk, off, ino_num; | 
 | 202 | 				long long ino; | 
 | 203 | 				blk = le32_to_cpu(dirh.start_block); | 
 | 204 | 				off = le16_to_cpu(dire->offset); | 
 | 205 | 				ino_num = le32_to_cpu(dirh.inode_number) + | 
 | 206 | 					(short) le16_to_cpu(dire->inode_number); | 
 | 207 | 				ino = SQUASHFS_MKINODE(blk, off); | 
 | 208 |  | 
 | 209 | 				TRACE("calling squashfs_iget for directory " | 
 | 210 | 					"entry %s, inode  %x:%x, %d\n", name, | 
 | 211 | 					blk, off, ino_num); | 
 | 212 |  | 
 | 213 | 				inode = squashfs_iget(dir->i_sb, ino, ino_num); | 
 | 214 | 				if (IS_ERR(inode)) { | 
 | 215 | 					err = PTR_ERR(inode); | 
 | 216 | 					goto failed; | 
 | 217 | 				} | 
 | 218 |  | 
 | 219 | 				goto exit_lookup; | 
 | 220 | 			} | 
 | 221 | 		} | 
 | 222 | 	} | 
 | 223 |  | 
 | 224 | exit_lookup: | 
 | 225 | 	kfree(dire); | 
 | 226 | 	if (inode) | 
 | 227 | 		return d_splice_alias(inode, dentry); | 
 | 228 | 	d_add(dentry, inode); | 
 | 229 | 	return ERR_PTR(0); | 
 | 230 |  | 
 | 231 | read_failure: | 
 | 232 | 	ERROR("Unable to read directory block [%llx:%x]\n", | 
 | 233 | 		squashfs_i(dir)->start + msblk->directory_table, | 
 | 234 | 		squashfs_i(dir)->offset); | 
 | 235 | failed: | 
 | 236 | 	kfree(dire); | 
 | 237 | 	return ERR_PTR(err); | 
 | 238 | } | 
 | 239 |  | 
 | 240 |  | 
 | 241 | const struct inode_operations squashfs_dir_inode_ops = { | 
| Phillip Lougher | 67f66cc | 2010-05-17 04:06:56 +0100 | [diff] [blame] | 242 | 	.lookup = squashfs_lookup, | 
 | 243 | 	.getxattr = generic_getxattr, | 
 | 244 | 	.listxattr = squashfs_listxattr | 
| Phillip Lougher | c88da2c | 2009-01-05 08:46:23 +0000 | [diff] [blame] | 245 | }; |