Blame view

fs/ext2/dir.c 16.9 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  /*
   *  linux/fs/ext2/dir.c
   *
   * Copyright (C) 1992, 1993, 1994, 1995
   * Remy Card (card@masi.ibp.fr)
   * Laboratoire MASI - Institut Blaise Pascal
   * Universite Pierre et Marie Curie (Paris VI)
   *
   *  from
   *
   *  linux/fs/minix/dir.c
   *
   *  Copyright (C) 1991, 1992  Linus Torvalds
   *
   *  ext2 directory handling functions
   *
   *  Big-endian to little-endian byte-swapping/bitmaps by
   *        David S. Miller (davem@caip.rutgers.edu), 1995
   *
   * All code that works with directory layout had been switched to pagecache
   * and moved here. AV
   */
  
  #include "ext2.h"
f34fb6ecc   Nick Piggin   ext2: convert to ...
26
  #include <linux/buffer_head.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
  #include <linux/pagemap.h>
f34fb6ecc   Nick Piggin   ext2: convert to ...
28
  #include <linux/swap.h>
e1d747d9b   Jeff Layton   ext2: convert to ...
29
  #include <linux/iversion.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
  
  typedef struct ext2_dir_entry_2 ext2_dirent;
40a063f66   Eric Sandeen   ext2: speed up fi...
32
33
34
35
36
  /*
   * Tests against MAX_REC_LEN etc were put in place for 64k block
   * sizes; if that is not possible on this arch, we can skip
   * those tests and speed things up.
   */
89910cccb   Jan Kara   ext2: avoid rec_l...
37
38
39
  static inline unsigned ext2_rec_len_from_disk(__le16 dlen)
  {
  	unsigned len = le16_to_cpu(dlen);
ea1754a08   Kirill A. Shutemov   mm, fs: remove re...
40
  #if (PAGE_SIZE >= 65536)
89910cccb   Jan Kara   ext2: avoid rec_l...
41
42
  	if (len == EXT2_MAX_REC_LEN)
  		return 1 << 16;
40a063f66   Eric Sandeen   ext2: speed up fi...
43
  #endif
89910cccb   Jan Kara   ext2: avoid rec_l...
44
45
46
47
48
  	return len;
  }
  
  static inline __le16 ext2_rec_len_to_disk(unsigned len)
  {
ea1754a08   Kirill A. Shutemov   mm, fs: remove re...
49
  #if (PAGE_SIZE >= 65536)
89910cccb   Jan Kara   ext2: avoid rec_l...
50
51
  	if (len == (1 << 16))
  		return cpu_to_le16(EXT2_MAX_REC_LEN);
2c11619a5   Julia Lawall   fs/ext2: use BUG_ON
52
53
  	else
  		BUG_ON(len > (1 << 16));
40a063f66   Eric Sandeen   ext2: speed up fi...
54
  #endif
89910cccb   Jan Kara   ext2: avoid rec_l...
55
56
  	return cpu_to_le16(len);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
58
59
60
61
62
63
64
65
66
67
68
  /*
   * ext2 uses block-sized chunks. Arguably, sector-sized ones would be
   * more robust, but we have what we have
   */
  static inline unsigned ext2_chunk_size(struct inode *inode)
  {
  	return inode->i_sb->s_blocksize;
  }
  
  static inline void ext2_put_page(struct page *page)
  {
  	kunmap(page);
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
69
  	put_page(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
74
75
76
77
78
  /*
   * Return the offset into page `page_nr' of the last valid
   * byte in that page, plus one.
   */
  static unsigned
  ext2_last_byte(struct inode *inode, unsigned long page_nr)
  {
  	unsigned last_byte = inode->i_size;
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
79
80
81
  	last_byte -= page_nr << PAGE_SHIFT;
  	if (last_byte > PAGE_SIZE)
  		last_byte = PAGE_SIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
  	return last_byte;
  }
f34fb6ecc   Nick Piggin   ext2: convert to ...
84
  static int ext2_commit_chunk(struct page *page, loff_t pos, unsigned len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  {
f34fb6ecc   Nick Piggin   ext2: convert to ...
86
87
  	struct address_space *mapping = page->mapping;
  	struct inode *dir = mapping->host;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  	int err = 0;
f34fb6ecc   Nick Piggin   ext2: convert to ...
89

e1d747d9b   Jeff Layton   ext2: convert to ...
90
  	inode_inc_iversion(dir);
f34fb6ecc   Nick Piggin   ext2: convert to ...
91
92
93
94
95
96
  	block_write_end(NULL, mapping, pos, len, len, page, NULL);
  
  	if (pos+len > dir->i_size) {
  		i_size_write(dir, pos+len);
  		mark_inode_dirty(dir);
  	}
6b7021ef7   Jan Kara   ext2: also update...
97
  	if (IS_DIRSYNC(dir)) {
2b69c8280   Jeff Layton   mm: drop "wait" p...
98
  		err = write_one_page(page);
6b7021ef7   Jan Kara   ext2: also update...
99
  		if (!err)
c37650161   Christoph Hellwig   fs: add sync_inod...
100
  			err = sync_inode_metadata(dir, 1);
6b7021ef7   Jan Kara   ext2: also update...
101
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  		unlock_page(page);
6b7021ef7   Jan Kara   ext2: also update...
103
  	}
f34fb6ecc   Nick Piggin   ext2: convert to ...
104

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
  	return err;
  }
be5b82dbf   Al Viro   make ext2_get_pag...
107
  static bool ext2_check_page(struct page *page, int quiet)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
111
112
113
114
  {
  	struct inode *dir = page->mapping->host;
  	struct super_block *sb = dir->i_sb;
  	unsigned chunk_size = ext2_chunk_size(dir);
  	char *kaddr = page_address(page);
  	u32 max_inumber = le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count);
  	unsigned offs, rec_len;
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
115
  	unsigned limit = PAGE_SIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
  	ext2_dirent *p;
  	char *error;
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
118
119
  	if ((dir->i_size >> PAGE_SHIFT) == page->index) {
  		limit = dir->i_size & ~PAGE_MASK;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
122
123
124
125
126
  		if (limit & (chunk_size - 1))
  			goto Ebadsize;
  		if (!limit)
  			goto out;
  	}
  	for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) {
  		p = (ext2_dirent *)(kaddr + offs);
89910cccb   Jan Kara   ext2: avoid rec_l...
127
  		rec_len = ext2_rec_len_from_disk(p->rec_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128

40a063f66   Eric Sandeen   ext2: speed up fi...
129
  		if (unlikely(rec_len < EXT2_DIR_REC_LEN(1)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
  			goto Eshort;
40a063f66   Eric Sandeen   ext2: speed up fi...
131
  		if (unlikely(rec_len & 3))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  			goto Ealign;
40a063f66   Eric Sandeen   ext2: speed up fi...
133
  		if (unlikely(rec_len < EXT2_DIR_REC_LEN(p->name_len)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
  			goto Enamelen;
40a063f66   Eric Sandeen   ext2: speed up fi...
135
  		if (unlikely(((offs + rec_len - 1) ^ offs) & ~(chunk_size-1)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  			goto Espan;
40a063f66   Eric Sandeen   ext2: speed up fi...
137
  		if (unlikely(le32_to_cpu(p->inode) > max_inumber))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
140
141
142
143
  			goto Einumber;
  	}
  	if (offs != limit)
  		goto Eend;
  out:
  	SetPageChecked(page);
be5b82dbf   Al Viro   make ext2_get_pag...
144
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
147
148
  
  	/* Too bad, we had an error */
  
  Ebadsize:
bd39597cb   Eric Sandeen   ext2: avoid print...
149
150
151
152
  	if (!quiet)
  		ext2_error(sb, __func__,
  			"size of directory #%lu is not a multiple "
  			"of chunk size", dir->i_ino);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
  	goto fail;
  Eshort:
  	error = "rec_len is smaller than minimal";
  	goto bad_entry;
  Ealign:
  	error = "unaligned directory entry";
  	goto bad_entry;
  Enamelen:
  	error = "rec_len is too small for name_len";
  	goto bad_entry;
  Espan:
  	error = "directory entry across blocks";
  	goto bad_entry;
  Einumber:
  	error = "inode out of bounds";
  bad_entry:
bd39597cb   Eric Sandeen   ext2: avoid print...
169
170
171
  	if (!quiet)
  		ext2_error(sb, __func__, "bad entry in directory #%lu: : %s - "
  			"offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
172
  			dir->i_ino, error, (page->index<<PAGE_SHIFT)+offs,
bd39597cb   Eric Sandeen   ext2: avoid print...
173
174
  			(unsigned long) le32_to_cpu(p->inode),
  			rec_len, p->name_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
  	goto fail;
  Eend:
bd39597cb   Eric Sandeen   ext2: avoid print...
177
178
179
180
181
  	if (!quiet) {
  		p = (ext2_dirent *)(kaddr + offs);
  		ext2_error(sb, "ext2_check_page",
  			"entry in directory #%lu spans the page boundary"
  			"offset=%lu, inode=%lu",
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
182
  			dir->i_ino, (page->index<<PAGE_SHIFT)+offs,
bd39597cb   Eric Sandeen   ext2: avoid print...
183
184
  			(unsigned long) le32_to_cpu(p->inode));
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  fail:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
  	SetPageError(page);
be5b82dbf   Al Viro   make ext2_get_pag...
187
  	return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
  }
bd39597cb   Eric Sandeen   ext2: avoid print...
189
190
  static struct page * ext2_get_page(struct inode *dir, unsigned long n,
  				   int quiet)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
  {
  	struct address_space *mapping = dir->i_mapping;
090d2b185   Pekka Enberg   [PATCH] read_mapp...
193
  	struct page *page = read_mapping_page(mapping, n, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  	if (!IS_ERR(page)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
  		kmap(page);
be5b82dbf   Al Viro   make ext2_get_pag...
196
197
198
199
  		if (unlikely(!PageChecked(page))) {
  			if (PageError(page) || !ext2_check_page(page, quiet))
  				goto fail;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
  	}
  	return page;
  
  fail:
  	ext2_put_page(page);
  	return ERR_PTR(-EIO);
  }
  
  /*
   * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure.
   *
   * len <= EXT2_NAME_LEN and de != NULL are guaranteed by caller.
   */
  static inline int ext2_match (int len, const char * const name,
  					struct ext2_dir_entry_2 * de)
  {
  	if (len != de->name_len)
  		return 0;
  	if (!de->inode)
  		return 0;
  	return !memcmp(name, de->name, len);
  }
  
  /*
   * p is at least 6 bytes before the end of page
   */
  static inline ext2_dirent *ext2_next_entry(ext2_dirent *p)
  {
89910cccb   Jan Kara   ext2: avoid rec_l...
228
229
  	return (ext2_dirent *)((char *)p +
  			ext2_rec_len_from_disk(p->rec_len));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  }
  
  static inline unsigned 
  ext2_validate_entry(char *base, unsigned offset, unsigned mask)
  {
  	ext2_dirent *de = (ext2_dirent*)(base + offset);
  	ext2_dirent *p = (ext2_dirent*)(base + (offset&mask));
  	while ((char*)p < (char*)de) {
  		if (p->rec_len == 0)
  			break;
  		p = ext2_next_entry(p);
  	}
  	return (char *)p - base;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
  static inline void ext2_set_de_type(ext2_dirent *de, struct inode *inode)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
  	if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
e10892189   Phillip Potter   ext2: use common ...
247
  		de->file_type = fs_umode_to_ftype(inode->i_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
250
251
252
  	else
  		de->file_type = 0;
  }
  
  static int
80886298c   Al Viro   [readdir] simple ...
253
  ext2_readdir(struct file *file, struct dir_context *ctx)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
  {
80886298c   Al Viro   [readdir] simple ...
255
256
  	loff_t pos = ctx->pos;
  	struct inode *inode = file_inode(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
  	struct super_block *sb = inode->i_sb;
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
258
259
  	unsigned int offset = pos & ~PAGE_MASK;
  	unsigned long n = pos >> PAGE_SHIFT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
  	unsigned long npages = dir_pages(inode);
  	unsigned chunk_mask = ~(ext2_chunk_size(inode)-1);
c472c07bf   Goffredo Baroncelli   iversion: Rename ...
262
  	bool need_revalidate = !inode_eq_iversion(inode, file->f_version);
e10892189   Phillip Potter   ext2: use common ...
263
  	bool has_filetype;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
  
  	if (pos > inode->i_size - EXT2_DIR_REC_LEN(1))
2d7f2ea9c   Al Viro   [PATCH] Fix ext2 ...
266
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267

e10892189   Phillip Potter   ext2: use common ...
268
269
  	has_filetype =
  		EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
272
273
  
  	for ( ; n < npages; n++, offset = 0) {
  		char *kaddr, *limit;
  		ext2_dirent *de;
bd39597cb   Eric Sandeen   ext2: avoid print...
274
  		struct page *page = ext2_get_page(inode, n, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
  
  		if (IS_ERR(page)) {
605afd60e   Harvey Harrison   ext2: replace rem...
277
  			ext2_error(sb, __func__,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
279
  				   "bad page in #%lu",
  				   inode->i_ino);
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
280
  			ctx->pos += PAGE_SIZE - offset;
bbff28602   Akinobu Mita   ext2: improve ext...
281
  			return PTR_ERR(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
  		}
  		kaddr = page_address(page);
2d7f2ea9c   Al Viro   [PATCH] Fix ext2 ...
284
285
286
  		if (unlikely(need_revalidate)) {
  			if (offset) {
  				offset = ext2_validate_entry(kaddr, offset, chunk_mask);
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
287
  				ctx->pos = (n<<PAGE_SHIFT) + offset;
2d7f2ea9c   Al Viro   [PATCH] Fix ext2 ...
288
  			}
e1d747d9b   Jeff Layton   ext2: convert to ...
289
290
  			file->f_version = inode_query_iversion(inode);
  			need_revalidate = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
292
293
294
295
  		}
  		de = (ext2_dirent *)(kaddr+offset);
  		limit = kaddr + ext2_last_byte(inode, n) - EXT2_DIR_REC_LEN(1);
  		for ( ;(char*)de <= limit; de = ext2_next_entry(de)) {
  			if (de->rec_len == 0) {
605afd60e   Harvey Harrison   ext2: replace rem...
296
  				ext2_error(sb, __func__,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
  					"zero-length directory entry");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
  				ext2_put_page(page);
2d7f2ea9c   Al Viro   [PATCH] Fix ext2 ...
299
  				return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
  			}
  			if (de->inode) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
  				unsigned char d_type = DT_UNKNOWN;
e10892189   Phillip Potter   ext2: use common ...
303
304
  				if (has_filetype)
  					d_type = fs_ftype_to_dtype(de->file_type);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305

80886298c   Al Viro   [readdir] simple ...
306
307
308
  				if (!dir_emit(ctx, de->name, de->name_len,
  						le32_to_cpu(de->inode),
  						d_type)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
  					ext2_put_page(page);
2d7f2ea9c   Al Viro   [PATCH] Fix ext2 ...
310
  					return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
  				}
  			}
80886298c   Al Viro   [readdir] simple ...
313
  			ctx->pos += ext2_rec_len_from_disk(de->rec_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
  		}
  		ext2_put_page(page);
  	}
2d7f2ea9c   Al Viro   [PATCH] Fix ext2 ...
317
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
320
321
322
323
  }
  
  /*
   *	ext2_find_entry()
   *
   * finds an entry in the specified directory with the wanted name. It
92e128884   Jérémy Cochoy   ext2: fix comment...
324
325
   * returns the page in which the entry was found (as a parameter - res_page),
   * and the entry itself. Page is returned mapped and unlocked.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
327
   * Entry is guaranteed to be valid.
   */
ac3ba644b   Al Viro   qstr: constify in...
328
329
  struct ext2_dir_entry_2 *ext2_find_entry (struct inode *dir,
  			const struct qstr *child, struct page **res_page)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
  {
a9885444f   Al Viro   [PATCH] get rid o...
331
332
  	const char *name = child->name;
  	int namelen = child->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
335
336
337
338
  	unsigned reclen = EXT2_DIR_REC_LEN(namelen);
  	unsigned long start, n;
  	unsigned long npages = dir_pages(dir);
  	struct page *page = NULL;
  	struct ext2_inode_info *ei = EXT2_I(dir);
  	ext2_dirent * de;
bd39597cb   Eric Sandeen   ext2: avoid print...
339
  	int dir_has_error = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
342
343
344
345
346
347
348
349
350
351
352
  
  	if (npages == 0)
  		goto out;
  
  	/* OFFSET_CACHE */
  	*res_page = NULL;
  
  	start = ei->i_dir_start_lookup;
  	if (start >= npages)
  		start = 0;
  	n = start;
  	do {
  		char *kaddr;
bd39597cb   Eric Sandeen   ext2: avoid print...
353
  		page = ext2_get_page(dir, n, dir_has_error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
356
357
358
359
  		if (!IS_ERR(page)) {
  			kaddr = page_address(page);
  			de = (ext2_dirent *) kaddr;
  			kaddr += ext2_last_byte(dir, n) - reclen;
  			while ((char *) de <= kaddr) {
  				if (de->rec_len == 0) {
605afd60e   Harvey Harrison   ext2: replace rem...
360
  					ext2_error(dir->i_sb, __func__,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
365
366
367
368
369
  						"zero-length directory entry");
  					ext2_put_page(page);
  					goto out;
  				}
  				if (ext2_match (namelen, name, de))
  					goto found;
  				de = ext2_next_entry(de);
  			}
  			ext2_put_page(page);
bd39597cb   Eric Sandeen   ext2: avoid print...
370
371
  		} else
  			dir_has_error = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
  		if (++n >= npages)
  			n = 0;
d8adb9cef   Eric Sandeen   [PATCH] ext2: ski...
374
  		/* next page is past the blocks we've got */
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
375
  		if (unlikely(n > (dir->i_blocks >> (PAGE_SHIFT - 9)))) {
605afd60e   Harvey Harrison   ext2: replace rem...
376
  			ext2_error(dir->i_sb, __func__,
d8adb9cef   Eric Sandeen   [PATCH] ext2: ski...
377
378
379
380
381
  				"dir %lu size %lld exceeds block count %llu",
  				dir->i_ino, dir->i_size,
  				(unsigned long long)dir->i_blocks);
  			goto out;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
384
385
386
387
388
389
390
391
392
393
  	} while (n != start);
  out:
  	return NULL;
  
  found:
  	*res_page = page;
  	ei->i_dir_start_lookup = n;
  	return de;
  }
  
  struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p)
  {
bd39597cb   Eric Sandeen   ext2: avoid print...
394
  	struct page *page = ext2_get_page(dir, 0, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
395
396
397
398
399
400
401
402
  	ext2_dirent *de = NULL;
  
  	if (!IS_ERR(page)) {
  		de = ext2_next_entry((ext2_dirent *) page_address(page));
  		*p = page;
  	}
  	return de;
  }
ac3ba644b   Al Viro   qstr: constify in...
403
  ino_t ext2_inode_by_name(struct inode *dir, const struct qstr *child)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
405
  {
  	ino_t res = 0;
a9885444f   Al Viro   [PATCH] get rid o...
406
  	struct ext2_dir_entry_2 *de;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
  	struct page *page;
  	
a9885444f   Al Viro   [PATCH] get rid o...
409
  	de = ext2_find_entry (dir, child, &page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
411
  	if (de) {
  		res = le32_to_cpu(de->inode);
7d93a1a53   Evgeniy Dushistov   [PATCH] ext2: cle...
412
  		ext2_put_page(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
  	}
  	return res;
  }
f4e420dc4   Christoph Hellwig   clean up write_be...
416
417
  static int ext2_prepare_chunk(struct page *page, loff_t pos, unsigned len)
  {
6e1db88d5   Christoph Hellwig   introduce __block...
418
  	return __block_write_begin(page, pos, len, ext2_get_block);
f4e420dc4   Christoph Hellwig   clean up write_be...
419
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
421
  /* Releases the page */
  void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
39fe7557b   Jan Kara   ext2: Do not upda...
422
  		   struct page *page, struct inode *inode, int update_times)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
  {
f34fb6ecc   Nick Piggin   ext2: convert to ...
424
425
  	loff_t pos = page_offset(page) +
  			(char *) de - (char *) page_address(page);
89910cccb   Jan Kara   ext2: avoid rec_l...
426
  	unsigned len = ext2_rec_len_from_disk(de->rec_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
428
429
  	int err;
  
  	lock_page(page);
f4e420dc4   Christoph Hellwig   clean up write_be...
430
  	err = ext2_prepare_chunk(page, pos, len);
309be53da   Eric Sesterhenn   BUG_ON() Conversi...
431
  	BUG_ON(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
  	de->inode = cpu_to_le32(inode->i_ino);
f34fb6ecc   Nick Piggin   ext2: convert to ...
433
434
  	ext2_set_de_type(de, inode);
  	err = ext2_commit_chunk(page, pos, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
  	ext2_put_page(page);
39fe7557b   Jan Kara   ext2: Do not upda...
436
  	if (update_times)
02027d42c   Deepa Dinamani   fs: Replace CURRE...
437
  		dir->i_mtime = dir->i_ctime = current_time(dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
439
440
441
442
443
444
445
446
  	EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL;
  	mark_inode_dirty(dir);
  }
  
  /*
   *	Parent is locked.
   */
  int ext2_add_link (struct dentry *dentry, struct inode *inode)
  {
2b0143b5c   David Howells   VFS: normal files...
447
  	struct inode *dir = d_inode(dentry->d_parent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
451
452
453
454
455
456
457
  	const char *name = dentry->d_name.name;
  	int namelen = dentry->d_name.len;
  	unsigned chunk_size = ext2_chunk_size(dir);
  	unsigned reclen = EXT2_DIR_REC_LEN(namelen);
  	unsigned short rec_len, name_len;
  	struct page *page = NULL;
  	ext2_dirent * de;
  	unsigned long npages = dir_pages(dir);
  	unsigned long n;
  	char *kaddr;
f34fb6ecc   Nick Piggin   ext2: convert to ...
458
  	loff_t pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
461
462
463
464
465
466
467
  	int err;
  
  	/*
  	 * We take care of directory expansion in the same loop.
  	 * This code plays outside i_size, so it locks the page
  	 * to protect that region.
  	 */
  	for (n = 0; n <= npages; n++) {
  		char *dir_end;
bd39597cb   Eric Sandeen   ext2: avoid print...
468
  		page = ext2_get_page(dir, n, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
470
471
472
473
474
475
  		err = PTR_ERR(page);
  		if (IS_ERR(page))
  			goto out;
  		lock_page(page);
  		kaddr = page_address(page);
  		dir_end = kaddr + ext2_last_byte(dir, n);
  		de = (ext2_dirent *)kaddr;
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
476
  		kaddr += PAGE_SIZE - reclen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
478
479
480
481
  		while ((char *)de <= kaddr) {
  			if ((char *)de == dir_end) {
  				/* We hit i_size */
  				name_len = 0;
  				rec_len = chunk_size;
89910cccb   Jan Kara   ext2: avoid rec_l...
482
  				de->rec_len = ext2_rec_len_to_disk(chunk_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
484
485
486
  				de->inode = 0;
  				goto got_it;
  			}
  			if (de->rec_len == 0) {
605afd60e   Harvey Harrison   ext2: replace rem...
487
  				ext2_error(dir->i_sb, __func__,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
489
490
491
492
493
494
495
  					"zero-length directory entry");
  				err = -EIO;
  				goto out_unlock;
  			}
  			err = -EEXIST;
  			if (ext2_match (namelen, name, de))
  				goto out_unlock;
  			name_len = EXT2_DIR_REC_LEN(de->name_len);
89910cccb   Jan Kara   ext2: avoid rec_l...
496
  			rec_len = ext2_rec_len_from_disk(de->rec_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
498
499
500
501
502
503
504
505
506
507
508
509
  			if (!de->inode && rec_len >= reclen)
  				goto got_it;
  			if (rec_len >= name_len + reclen)
  				goto got_it;
  			de = (ext2_dirent *) ((char *) de + rec_len);
  		}
  		unlock_page(page);
  		ext2_put_page(page);
  	}
  	BUG();
  	return -EINVAL;
  
  got_it:
f34fb6ecc   Nick Piggin   ext2: convert to ...
510
511
  	pos = page_offset(page) +
  		(char*)de - (char*)page_address(page);
f4e420dc4   Christoph Hellwig   clean up write_be...
512
  	err = ext2_prepare_chunk(page, pos, rec_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
515
516
  	if (err)
  		goto out_unlock;
  	if (de->inode) {
  		ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len);
89910cccb   Jan Kara   ext2: avoid rec_l...
517
518
  		de1->rec_len = ext2_rec_len_to_disk(rec_len - name_len);
  		de->rec_len = ext2_rec_len_to_disk(name_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
521
  		de = de1;
  	}
  	de->name_len = namelen;
f34fb6ecc   Nick Piggin   ext2: convert to ...
522
  	memcpy(de->name, name, namelen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
  	de->inode = cpu_to_le32(inode->i_ino);
  	ext2_set_de_type (de, inode);
f34fb6ecc   Nick Piggin   ext2: convert to ...
525
  	err = ext2_commit_chunk(page, pos, rec_len);
02027d42c   Deepa Dinamani   fs: Replace CURRE...
526
  	dir->i_mtime = dir->i_ctime = current_time(dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
  	EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL;
  	mark_inode_dirty(dir);
  	/* OFFSET_CACHE */
  out_put:
  	ext2_put_page(page);
  out:
  	return err;
  out_unlock:
  	unlock_page(page);
  	goto out_put;
  }
  
  /*
   * ext2_delete_entry deletes a directory entry by merging it with the
   * previous entry. Page is up-to-date. Releases the page.
   */
  int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page )
  {
f4e420dc4   Christoph Hellwig   clean up write_be...
545
  	struct inode *inode = page->mapping->host;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
547
  	char *kaddr = page_address(page);
  	unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1);
89910cccb   Jan Kara   ext2: avoid rec_l...
548
549
  	unsigned to = ((char *)dir - kaddr) +
  				ext2_rec_len_from_disk(dir->rec_len);
f34fb6ecc   Nick Piggin   ext2: convert to ...
550
  	loff_t pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
552
553
554
555
556
  	ext2_dirent * pde = NULL;
  	ext2_dirent * de = (ext2_dirent *) (kaddr + from);
  	int err;
  
  	while ((char*)de < (char*)dir) {
  		if (de->rec_len == 0) {
605afd60e   Harvey Harrison   ext2: replace rem...
557
  			ext2_error(inode->i_sb, __func__,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
560
561
562
563
564
565
566
  				"zero-length directory entry");
  			err = -EIO;
  			goto out;
  		}
  		pde = de;
  		de = ext2_next_entry(de);
  	}
  	if (pde)
  		from = (char*)pde - (char*)page_address(page);
f34fb6ecc   Nick Piggin   ext2: convert to ...
567
  	pos = page_offset(page) + from;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
  	lock_page(page);
f4e420dc4   Christoph Hellwig   clean up write_be...
569
  	err = ext2_prepare_chunk(page, pos, to - from);
309be53da   Eric Sesterhenn   BUG_ON() Conversi...
570
  	BUG_ON(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
  	if (pde)
89910cccb   Jan Kara   ext2: avoid rec_l...
572
  		pde->rec_len = ext2_rec_len_to_disk(to - from);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
  	dir->inode = 0;
f34fb6ecc   Nick Piggin   ext2: convert to ...
574
  	err = ext2_commit_chunk(page, pos, to - from);
02027d42c   Deepa Dinamani   fs: Replace CURRE...
575
  	inode->i_ctime = inode->i_mtime = current_time(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
577
578
579
580
581
582
583
584
585
586
587
  	EXT2_I(inode)->i_flags &= ~EXT2_BTREE_FL;
  	mark_inode_dirty(inode);
  out:
  	ext2_put_page(page);
  	return err;
  }
  
  /*
   * Set the first fragment of directory.
   */
  int ext2_make_empty(struct inode *inode, struct inode *parent)
  {
f4e420dc4   Christoph Hellwig   clean up write_be...
588
  	struct page *page = grab_cache_page(inode->i_mapping, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589
590
591
592
593
594
595
  	unsigned chunk_size = ext2_chunk_size(inode);
  	struct ext2_dir_entry_2 * de;
  	int err;
  	void *kaddr;
  
  	if (!page)
  		return -ENOMEM;
f34fb6ecc   Nick Piggin   ext2: convert to ...
596

f4e420dc4   Christoph Hellwig   clean up write_be...
597
  	err = ext2_prepare_chunk(page, 0, chunk_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
600
601
  	if (err) {
  		unlock_page(page);
  		goto fail;
  	}
d4a23aee2   Cong Wang   ext2: remove the ...
602
  	kaddr = kmap_atomic(page);
7823c7c12   Luiz Fernando Capitulino   ext2: trivial ind...
603
  	memset(kaddr, 0, chunk_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
  	de = (struct ext2_dir_entry_2 *)kaddr;
  	de->name_len = 1;
89910cccb   Jan Kara   ext2: avoid rec_l...
606
  	de->rec_len = ext2_rec_len_to_disk(EXT2_DIR_REC_LEN(1));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
608
609
610
611
612
  	memcpy (de->name, ".\0\0", 4);
  	de->inode = cpu_to_le32(inode->i_ino);
  	ext2_set_de_type (de, inode);
  
  	de = (struct ext2_dir_entry_2 *)(kaddr + EXT2_DIR_REC_LEN(1));
  	de->name_len = 2;
89910cccb   Jan Kara   ext2: avoid rec_l...
613
  	de->rec_len = ext2_rec_len_to_disk(chunk_size - EXT2_DIR_REC_LEN(1));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
614
615
616
  	de->inode = cpu_to_le32(parent->i_ino);
  	memcpy (de->name, "..\0", 4);
  	ext2_set_de_type (de, inode);
d4a23aee2   Cong Wang   ext2: remove the ...
617
  	kunmap_atomic(kaddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
619
  	err = ext2_commit_chunk(page, 0, chunk_size);
  fail:
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
620
  	put_page(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621
622
623
624
625
626
627
628
629
630
  	return err;
  }
  
  /*
   * routine to check that the specified directory is empty (for rmdir)
   */
  int ext2_empty_dir (struct inode * inode)
  {
  	struct page *page = NULL;
  	unsigned long i, npages = dir_pages(inode);
bd39597cb   Eric Sandeen   ext2: avoid print...
631
  	int dir_has_error = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632
633
634
635
  
  	for (i = 0; i < npages; i++) {
  		char *kaddr;
  		ext2_dirent * de;
bd39597cb   Eric Sandeen   ext2: avoid print...
636
  		page = ext2_get_page(inode, i, dir_has_error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637

bd39597cb   Eric Sandeen   ext2: avoid print...
638
639
  		if (IS_ERR(page)) {
  			dir_has_error = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
  			continue;
bd39597cb   Eric Sandeen   ext2: avoid print...
641
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
643
644
645
646
647
648
  
  		kaddr = page_address(page);
  		de = (ext2_dirent *)kaddr;
  		kaddr += ext2_last_byte(inode, i) - EXT2_DIR_REC_LEN(1);
  
  		while ((char *)de <= kaddr) {
  			if (de->rec_len == 0) {
605afd60e   Harvey Harrison   ext2: replace rem...
649
  				ext2_error(inode->i_sb, __func__,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
  					"zero-length directory entry");
  				printk("kaddr=%p, de=%p
  ", kaddr, de);
  				goto not_empty;
  			}
  			if (de->inode != 0) {
  				/* check for . and .. */
  				if (de->name[0] != '.')
  					goto not_empty;
  				if (de->name_len > 2)
  					goto not_empty;
  				if (de->name_len < 2) {
  					if (de->inode !=
  					    cpu_to_le32(inode->i_ino))
  						goto not_empty;
  				} else if (de->name[1] != '.')
  					goto not_empty;
  			}
  			de = ext2_next_entry(de);
  		}
  		ext2_put_page(page);
  	}
  	return 1;
  
  not_empty:
  	ext2_put_page(page);
  	return 0;
  }
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
678
  const struct file_operations ext2_dir_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679
680
  	.llseek		= generic_file_llseek,
  	.read		= generic_read_dir,
3b0a3c1ac   Al Viro   simple local file...
681
  	.iterate_shared	= ext2_readdir,
14f9f7b28   Andi Kleen   BKL-removal: conv...
682
  	.unlocked_ioctl = ext2_ioctl,
e322ff07f   David Howells   [PATCH] BLOCK: Mo...
683
684
685
  #ifdef CONFIG_COMPAT
  	.compat_ioctl	= ext2_compat_ioctl,
  #endif
48bde86df   Jan Kara   ext2: report meta...
686
  	.fsync		= ext2_fsync,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687
  };