Blame view

fs/squashfs/namei.c 6.8 KB
68252eb5f   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
c88da2c97   Phillip Lougher   Squashfs: directo...
2
3
4
5
  /*
   * Squashfs - a compressed read only filesystem for Linux
   *
   * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
d7f2ff671   Phillip Lougher   Squashfs: update ...
6
   * Phillip Lougher <phillip@squashfs.org.uk>
c88da2c97   Phillip Lougher   Squashfs: directo...
7
   *
c88da2c97   Phillip Lougher   Squashfs: directo...
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
   * namei.c
   */
  
  /*
   * This file implements code to do filename lookup in directories.
   *
   * Like inodes, directories are packed into compressed metadata blocks, stored
   * in a directory table.  Directories are accessed using the start address of
   * the metablock containing the directory and the offset into the
   * decompressed block (<block, offset>).
   *
   * Directories are organised in a slightly complex way, and are not simply
   * a list of file names.  The organisation takes advantage of the
   * fact that (in most cases) the inodes of the files will be in the same
   * compressed metadata block, and therefore, can share the start block.
   * Directories are therefore organised in a two level list, a directory
   * header containing the shared start block value, and a sequence of directory
   * entries, each of which share the shared start block.  A new directory header
   * is written once/if the inode start block changes.  The directory
   * header/directory entry list is repeated as many times as necessary.
   *
   * Directories are sorted, and can contain a directory index to speed up
   * file lookup.  Directory indexes store one entry per metablock, each entry
   * storing the index/filename mapping to the first directory header
   * in each metadata block.  Directories are sorted in alphabetical order,
   * and at lookup the index is scanned linearly looking for the first filename
   * alphabetically larger than the filename being looked up.  At this point the
   * location of the metadata block the filename is in has been found.
   * The general idea of the index is ensure only one metadata block needs to be
   * decompressed to do a lookup irrespective of the length of the directory.
   * This scheme has the advantage that it doesn't require extra memory overhead
   * and doesn't require much extra storage on disk.
   */
  
  #include <linux/fs.h>
  #include <linux/vfs.h>
  #include <linux/slab.h>
  #include <linux/string.h>
  #include <linux/dcache.h>
67f66cc6c   Phillip Lougher   squashfs: add new...
47
  #include <linux/xattr.h>
c88da2c97   Phillip Lougher   Squashfs: directo...
48
49
50
51
52
  
  #include "squashfs_fs.h"
  #include "squashfs_fs_sb.h"
  #include "squashfs_fs_i.h"
  #include "squashfs.h"
01e5b4e4e   Phillip Lougher   squashfs: add xat...
53
  #include "xattr.h"
c88da2c97   Phillip Lougher   Squashfs: directo...
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  
  /*
   * Lookup name in the directory index, returning the location of the metadata
   * block containing it, and the directory index this represents.
   *
   * If we get an error reading the index then return the part of the index
   * (if any) we have managed to read - the index isn't essential, just
   * quicker.
   */
  static int 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)
  {
  	struct squashfs_sb_info *msblk = sb->s_fs_info;
28d7b5684   Dan Carpenter   Squashfs: sanity ...
69
70
  	int i, length = 0, err;
  	unsigned int size;
c88da2c97   Phillip Lougher   Squashfs: directo...
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  	struct squashfs_dir_index *index;
  	char *str;
  
  	TRACE("Entered get_dir_index_using_name, i_count %d
  ", i_count);
  
  	index = kmalloc(sizeof(*index) + SQUASHFS_NAME_LEN * 2 + 2, GFP_KERNEL);
  	if (index == NULL) {
  		ERROR("Failed to allocate squashfs_dir_index
  ");
  		goto out;
  	}
  
  	str = &index->name[SQUASHFS_NAME_LEN + 1];
  	strncpy(str, name, len);
  	str[len] = '\0';
  
  	for (i = 0; i < i_count; i++) {
  		err = squashfs_read_metadata(sb, index, &index_start,
  					&index_offset, sizeof(*index));
  		if (err < 0)
  			break;
  
  
  		size = le32_to_cpu(index->size) + 1;
9dbc41d5d   Phillip Lougher   Squashfs: fix cor...
96
  		if (size > SQUASHFS_NAME_LEN)
28d7b5684   Dan Carpenter   Squashfs: sanity ...
97
  			break;
c88da2c97   Phillip Lougher   Squashfs: directo...
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  
  		err = squashfs_read_metadata(sb, index->name, &index_start,
  					&index_offset, size);
  		if (err < 0)
  			break;
  
  		index->name[size] = '\0';
  
  		if (strcmp(index->name, str) > 0)
  			break;
  
  		length = le32_to_cpu(index->index);
  		*next_block = le32_to_cpu(index->start_block) +
  					msblk->directory_table;
  	}
  
  	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
  	kfree(index);
  
  out:
  	/*
  	 * Return index (f_pos) of the looked up metadata block.  Translate
  	 * from internal f_pos to external f_pos which is offset by 3 because
  	 * we invent "." and ".." entries which are not actually stored in the
  	 * directory.
  	 */
  	return length + 3;
  }
  
  
  static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry,
00cd8dd3b   Al Viro   stop passing name...
129
  				 unsigned int flags)
c88da2c97   Phillip Lougher   Squashfs: directo...
130
131
132
133
134
135
136
137
138
  {
  	const unsigned char *name = dentry->d_name.name;
  	int len = dentry->d_name.len;
  	struct inode *inode = NULL;
  	struct squashfs_sb_info *msblk = dir->i_sb->s_fs_info;
  	struct squashfs_dir_header dirh;
  	struct squashfs_dir_entry *dire;
  	u64 block = squashfs_i(dir)->start + msblk->directory_table;
  	int offset = squashfs_i(dir)->offset;
52e9ce1c0   Phillip Lougher   Squashfs: fix cor...
139
140
  	int err, length;
  	unsigned int dir_count, size;
c88da2c97   Phillip Lougher   Squashfs: directo...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  
  	TRACE("Entered squashfs_lookup [%llx:%x]
  ", block, offset);
  
  	dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
  	if (dire == NULL) {
  		ERROR("Failed to allocate squashfs_dir_entry
  ");
  		return ERR_PTR(-ENOMEM);
  	}
  
  	if (len > SQUASHFS_NAME_LEN) {
  		err = -ENAMETOOLONG;
  		goto failed;
  	}
  
  	length = get_dir_index_using_name(dir->i_sb, &block, &offset,
  				squashfs_i(dir)->dir_idx_start,
  				squashfs_i(dir)->dir_idx_offset,
  				squashfs_i(dir)->dir_idx_cnt, name, len);
  
  	while (length < i_size_read(dir)) {
  		/*
  		 * Read directory header.
  		 */
  		err = squashfs_read_metadata(dir->i_sb, &dirh, &block,
  				&offset, sizeof(dirh));
  		if (err < 0)
  			goto read_failure;
  
  		length += sizeof(dirh);
  
  		dir_count = le32_to_cpu(dirh.count) + 1;
44cff8a9e   Phillip Lougher   Squashfs: handle ...
174

4826d83d9   Ajeet Yadav   Squashfs: use def...
175
  		if (dir_count > SQUASHFS_DIR_COUNT)
44cff8a9e   Phillip Lougher   Squashfs: handle ...
176
  			goto data_error;
c88da2c97   Phillip Lougher   Squashfs: directo...
177
178
179
180
181
182
183
184
185
186
  		while (dir_count--) {
  			/*
  			 * Read directory entry.
  			 */
  			err = squashfs_read_metadata(dir->i_sb, dire, &block,
  					&offset, sizeof(*dire));
  			if (err < 0)
  				goto read_failure;
  
  			size = le16_to_cpu(dire->size) + 1;
44cff8a9e   Phillip Lougher   Squashfs: handle ...
187
188
189
  			/* size should never be larger than SQUASHFS_NAME_LEN */
  			if (size > SQUASHFS_NAME_LEN)
  				goto data_error;
c88da2c97   Phillip Lougher   Squashfs: directo...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
  			err = squashfs_read_metadata(dir->i_sb, dire->name,
  					&block, &offset, size);
  			if (err < 0)
  				goto read_failure;
  
  			length += sizeof(*dire) + size;
  
  			if (name[0] < dire->name[0])
  				goto exit_lookup;
  
  			if (len == size && !strncmp(name, dire->name, len)) {
  				unsigned int blk, off, ino_num;
  				long long ino;
  				blk = le32_to_cpu(dirh.start_block);
  				off = le16_to_cpu(dire->offset);
  				ino_num = le32_to_cpu(dirh.inode_number) +
  					(short) le16_to_cpu(dire->inode_number);
  				ino = SQUASHFS_MKINODE(blk, off);
  
  				TRACE("calling squashfs_iget for directory "
  					"entry %s, inode  %x:%x, %d
  ", name,
  					blk, off, ino_num);
  
  				inode = squashfs_iget(dir->i_sb, ino, ino_num);
c88da2c97   Phillip Lougher   Squashfs: directo...
215
216
217
218
219
220
221
  				goto exit_lookup;
  			}
  		}
  	}
  
  exit_lookup:
  	kfree(dire);
0c1aa9a95   Al Viro   deuglify squashfs...
222
  	return d_splice_alias(inode, dentry);
c88da2c97   Phillip Lougher   Squashfs: directo...
223

44cff8a9e   Phillip Lougher   Squashfs: handle ...
224
225
  data_error:
  	err = -EIO;
c88da2c97   Phillip Lougher   Squashfs: directo...
226
227
228
229
230
231
232
233
234
235
236
237
  read_failure:
  	ERROR("Unable to read directory block [%llx:%x]
  ",
  		squashfs_i(dir)->start + msblk->directory_table,
  		squashfs_i(dir)->offset);
  failed:
  	kfree(dire);
  	return ERR_PTR(err);
  }
  
  
  const struct inode_operations squashfs_dir_inode_ops = {
67f66cc6c   Phillip Lougher   squashfs: add new...
238
  	.lookup = squashfs_lookup,
67f66cc6c   Phillip Lougher   squashfs: add new...
239
  	.listxattr = squashfs_listxattr
c88da2c97   Phillip Lougher   Squashfs: directo...
240
  };