Blame view

fs/ufs/dir.c 16.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   *  linux/fs/ufs/ufs_dir.c
   *
   * Copyright (C) 1996
   * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
   * Laboratory for Computer Science Research Computing Facility
   * Rutgers, The State University of New Jersey
   *
   * swab support by Francois-Rene Rideau <fare@tunes.org> 19970406
   *
   * 4.4BSD (FreeBSD) support added on February 1st 1998 by
   * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
   * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
14
15
16
   *
   * Migration to usage of "page cache" on May 2006 by
   * Evgeniy Dushistov <dushistov@mail.ru> based on ext2 code base.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
   */
  
  #include <linux/time.h>
  #include <linux/fs.h>
82b9d1d0d   Nick Piggin   ufs: convert to n...
21
  #include <linux/swap.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22

e54205988   Mike Frysinger   drop linux/ufs_fs...
23
  #include "ufs_fs.h"
bcd6d4ecf   Christoph Hellwig   ufs: move non-lay...
24
  #include "ufs.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
  #include "swab.h"
  #include "util.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
31
32
  /*
   * NOTE! unlike strncmp, ufs_match returns 1 for success, 0 for failure.
   *
   * len <= UFS_MAXNAMLEN and de != NULL are guaranteed by caller.
   */
  static inline int ufs_match(struct super_block *sb, int len,
89031bc79   Al Viro   sanitize const/si...
33
  		const unsigned char *name, struct ufs_dir_entry *de)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
36
37
38
39
40
  {
  	if (len != ufs_get_de_namlen(sb, de))
  		return 0;
  	if (!de->d_ino)
  		return 0;
  	return !memcmp(name, de->d_name, len);
  }
82b9d1d0d   Nick Piggin   ufs: convert to n...
41
  static int ufs_commit_chunk(struct page *page, loff_t pos, unsigned len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
  {
82b9d1d0d   Nick Piggin   ufs: convert to n...
43
44
  	struct address_space *mapping = page->mapping;
  	struct inode *dir = mapping->host;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
45
  	int err = 0;
82b9d1d0d   Nick Piggin   ufs: convert to n...
46

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
47
  	dir->i_version++;
82b9d1d0d   Nick Piggin   ufs: convert to n...
48
49
50
51
52
  	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);
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
53
54
55
56
57
58
  	if (IS_DIRSYNC(dir))
  		err = write_one_page(page, 1);
  	else
  		unlock_page(page);
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
60
61
62
63
64
  static inline void ufs_put_page(struct page *page)
  {
  	kunmap(page);
  	page_cache_release(page);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
66
67
68
69
  static inline unsigned long ufs_dir_pages(struct inode *inode)
  {
  	return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70

89031bc79   Al Viro   sanitize const/si...
71
  ino_t ufs_inode_by_name(struct inode *dir, const struct qstr *qstr)
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
72
73
74
75
76
  {
  	ino_t res = 0;
  	struct ufs_dir_entry *de;
  	struct page *page;
  	
080497079   Alexey Dobriyan   ufs: pass qstr in...
77
  	de = ufs_find_entry(dir, qstr, &page);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
78
79
80
  	if (de) {
  		res = fs32_to_cpu(dir->i_sb, de->d_ino);
  		ufs_put_page(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
82
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
85
86
87
  /* Releases the page */
  void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de,
  		  struct page *page, struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  {
82b9d1d0d   Nick Piggin   ufs: convert to n...
89
90
91
  	loff_t pos = page_offset(page) +
  			(char *) de - (char *) page_address(page);
  	unsigned len = fs16_to_cpu(dir->i_sb, de->d_reclen);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
92
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
94
  	lock_page(page);
f4e420dc4   Christoph Hellwig   clean up write_be...
95
  	err = ufs_prepare_chunk(page, pos, len);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
96
  	BUG_ON(err);
82b9d1d0d   Nick Piggin   ufs: convert to n...
97

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
98
99
  	de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino);
  	ufs_set_de_type(dir->i_sb, de, inode->i_mode);
82b9d1d0d   Nick Piggin   ufs: convert to n...
100
101
  
  	err = ufs_commit_chunk(page, pos, len);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
102
103
104
105
  	ufs_put_page(page);
  	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
  	mark_inode_dirty(dir);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
108
109
110
111
112
113
114
  static void ufs_check_page(struct page *page)
  {
  	struct inode *dir = page->mapping->host;
  	struct super_block *sb = dir->i_sb;
  	char *kaddr = page_address(page);
  	unsigned offs, rec_len;
  	unsigned limit = PAGE_CACHE_SIZE;
f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
115
  	const unsigned chunk_mask = UFS_SB(sb)->s_uspi->s_dirblksize - 1;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
116
117
118
119
120
  	struct ufs_dir_entry *p;
  	char *error;
  
  	if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) {
  		limit = dir->i_size & ~PAGE_CACHE_MASK;
f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
121
  		if (limit & chunk_mask)
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
122
123
124
  			goto Ebadsize;
  		if (!limit)
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
126
127
128
129
130
131
132
133
134
135
  	for (offs = 0; offs <= limit - UFS_DIR_REC_LEN(1); offs += rec_len) {
  		p = (struct ufs_dir_entry *)(kaddr + offs);
  		rec_len = fs16_to_cpu(sb, p->d_reclen);
  
  		if (rec_len < UFS_DIR_REC_LEN(1))
  			goto Eshort;
  		if (rec_len & 3)
  			goto Ealign;
  		if (rec_len < UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, p)))
  			goto Enamelen;
f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
136
  		if (((offs + rec_len - 1) ^ offs) & ~chunk_mask)
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
137
138
139
140
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
174
175
176
177
  			goto Espan;
  		if (fs32_to_cpu(sb, p->d_ino) > (UFS_SB(sb)->s_uspi->s_ipg *
  						  UFS_SB(sb)->s_uspi->s_ncg))
  			goto Einumber;
  	}
  	if (offs != limit)
  		goto Eend;
  out:
  	SetPageChecked(page);
  	return;
  
  	/* Too bad, we had an error */
  
  Ebadsize:
  	ufs_error(sb, "ufs_check_page",
  		  "size of directory #%lu is not a multiple of chunk size",
  		  dir->i_ino
  	);
  	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:
  	ufs_error (sb, "ufs_check_page", "bad entry in directory #%lu: %s - "
  		   "offset=%lu, rec_len=%d, name_len=%d",
  		   dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs,
  		   rec_len, ufs_get_de_namlen(sb, p));
  	goto fail;
  Eend:
  	p = (struct ufs_dir_entry *)(kaddr + offs);
9746077a7   Harvey Harrison   ufs: replace rema...
178
  	ufs_error(sb, __func__,
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
179
180
181
182
183
184
185
  		   "entry in directory #%lu spans the page boundary"
  		   "offset=%lu",
  		   dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs);
  fail:
  	SetPageChecked(page);
  	SetPageError(page);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
187
188
189
  static struct page *ufs_get_page(struct inode *dir, unsigned long n)
  {
  	struct address_space *mapping = dir->i_mapping;
6fe6900e1   Nick Piggin   mm: make read_cac...
190
  	struct page *page = read_mapping_page(mapping, n, NULL);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
191
  	if (!IS_ERR(page)) {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
192
  		kmap(page);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
193
194
195
196
  		if (!PageChecked(page))
  			ufs_check_page(page);
  		if (PageError(page))
  			goto fail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
198
  	return page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
200
201
202
  fail:
  	ufs_put_page(page);
  	return ERR_PTR(-EIO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  }
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
204
205
206
207
208
209
  /*
   * Return the offset into page `page_nr' of the last valid
   * byte in that page, plus one.
   */
  static unsigned
  ufs_last_byte(struct inode *inode, unsigned long page_nr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
211
212
213
214
215
216
  	unsigned last_byte = inode->i_size;
  
  	last_byte -= page_nr << PAGE_CACHE_SHIFT;
  	if (last_byte > PAGE_CACHE_SIZE)
  		last_byte = PAGE_CACHE_SIZE;
  	return last_byte;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
  }
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
218
219
  static inline struct ufs_dir_entry *
  ufs_next_entry(struct super_block *sb, struct ufs_dir_entry *p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
221
222
  	return (struct ufs_dir_entry *)((char *)p +
  					fs16_to_cpu(sb, p->d_reclen));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
  }
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
224
225
  
  struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct page **p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
227
228
  	struct page *page = ufs_get_page(dir, 0);
  	struct ufs_dir_entry *de = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
230
231
232
233
  	if (!IS_ERR(page)) {
  		de = ufs_next_entry(dir->i_sb,
  				    (struct ufs_dir_entry *)page_address(page));
  		*p = page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
235
  	return de;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
  }
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
237
238
239
240
241
242
243
244
  /*
   *	ufs_find_entry()
   *
   * finds an entry in the specified directory with the wanted name. It
   * returns the page in which the entry was found, and the entry itself
   * (as a parameter - res_dir). Page is returned mapped and unlocked.
   * Entry is guaranteed to be valid.
   */
89031bc79   Al Viro   sanitize const/si...
245
  struct ufs_dir_entry *ufs_find_entry(struct inode *dir, const struct qstr *qstr,
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
246
  				     struct page **res_page)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
248
  	struct super_block *sb = dir->i_sb;
89031bc79   Al Viro   sanitize const/si...
249
  	const unsigned char *name = qstr->name;
080497079   Alexey Dobriyan   ufs: pass qstr in...
250
  	int namelen = qstr->len;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
251
252
253
254
  	unsigned reclen = UFS_DIR_REC_LEN(namelen);
  	unsigned long start, n;
  	unsigned long npages = ufs_dir_pages(dir);
  	struct page *page = NULL;
dd187a260   Evgeniy Dushistov   [PATCH] ufs: litt...
255
  	struct ufs_inode_info *ui = UFS_I(dir);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
256
  	struct ufs_dir_entry *de;
abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
257
258
  	UFSD("ENTER, dir_ino %lu, name %s, namlen %u
  ", dir->i_ino, name, namelen);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
259
260
261
262
263
264
  
  	if (npages == 0 || namelen > UFS_MAXNAMLEN)
  		goto out;
  
  	/* OFFSET_CACHE */
  	*res_page = NULL;
dd187a260   Evgeniy Dushistov   [PATCH] ufs: litt...
265
  	start = ui->i_dir_start_lookup;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
266
267
268
269
270
271
272
273
274
275
276
277
  	if (start >= npages)
  		start = 0;
  	n = start;
  	do {
  		char *kaddr;
  		page = ufs_get_page(dir, n);
  		if (!IS_ERR(page)) {
  			kaddr = page_address(page);
  			de = (struct ufs_dir_entry *) kaddr;
  			kaddr += ufs_last_byte(dir, n) - reclen;
  			while ((char *) de <= kaddr) {
  				if (de->d_reclen == 0) {
9746077a7   Harvey Harrison   ufs: replace rema...
278
  					ufs_error(dir->i_sb, __func__,
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
  						  "zero-length directory entry");
  					ufs_put_page(page);
  					goto out;
  				}
  				if (ufs_match(sb, namelen, name, de))
  					goto found;
  				de = ufs_next_entry(sb, de);
  			}
  			ufs_put_page(page);
  		}
  		if (++n >= npages)
  			n = 0;
  	} while (n != start);
  out:
  	return NULL;
  
  found:
  	*res_page = page;
dd187a260   Evgeniy Dushistov   [PATCH] ufs: litt...
297
  	ui->i_dir_start_lookup = n;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
298
  	return de;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
300
301
  }
  
  /*
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
302
   *	Parent is locked.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
   */
  int ufs_add_link(struct dentry *dentry, struct inode *inode)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  	struct inode *dir = dentry->d_parent->d_inode;
89031bc79   Al Viro   sanitize const/si...
307
  	const unsigned char *name = dentry->d_name.name;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
  	int namelen = dentry->d_name.len;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
309
310
  	struct super_block *sb = dir->i_sb;
  	unsigned reclen = UFS_DIR_REC_LEN(namelen);
f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
311
  	const unsigned int chunk_size = UFS_SB(sb)->s_uspi->s_dirblksize;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
312
313
314
315
316
317
  	unsigned short rec_len, name_len;
  	struct page *page = NULL;
  	struct ufs_dir_entry *de;
  	unsigned long npages = ufs_dir_pages(dir);
  	unsigned long n;
  	char *kaddr;
82b9d1d0d   Nick Piggin   ufs: convert to n...
318
  	loff_t pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
  	int err;
abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
320
321
  	UFSD("ENTER, name %s, namelen %u
  ", name, namelen);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
  
  	/*
  	 * 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;
  
  		page = ufs_get_page(dir, n);
  		err = PTR_ERR(page);
  		if (IS_ERR(page))
  			goto out;
  		lock_page(page);
  		kaddr = page_address(page);
  		dir_end = kaddr + ufs_last_byte(dir, n);
  		de = (struct ufs_dir_entry *)kaddr;
  		kaddr += PAGE_CACHE_SIZE - reclen;
  		while ((char *)de <= kaddr) {
  			if ((char *)de == dir_end) {
  				/* We hit i_size */
  				name_len = 0;
f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
344
345
  				rec_len = chunk_size;
  				de->d_reclen = cpu_to_fs16(sb, chunk_size);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
346
347
  				de->d_ino = 0;
  				goto got_it;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
  			}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
349
  			if (de->d_reclen == 0) {
9746077a7   Harvey Harrison   ufs: replace rema...
350
  				ufs_error(dir->i_sb, __func__,
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
351
352
353
354
355
356
357
358
359
360
361
362
363
364
  					  "zero-length directory entry");
  				err = -EIO;
  				goto out_unlock;
  			}
  			err = -EEXIST;
  			if (ufs_match(sb, namelen, name, de))
  				goto out_unlock;
  			name_len = UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de));
  			rec_len = fs16_to_cpu(sb, de->d_reclen);
  			if (!de->d_ino && rec_len >= reclen)
  				goto got_it;
  			if (rec_len >= name_len + reclen)
  				goto got_it;
  			de = (struct ufs_dir_entry *) ((char *) de + rec_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
  		}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
366
367
  		unlock_page(page);
  		ufs_put_page(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
369
370
371
372
  	BUG();
  	return -EINVAL;
  
  got_it:
82b9d1d0d   Nick Piggin   ufs: convert to n...
373
374
  	pos = page_offset(page) +
  			(char*)de - (char*)page_address(page);
f4e420dc4   Christoph Hellwig   clean up write_be...
375
  	err = ufs_prepare_chunk(page, pos, rec_len);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
376
377
  	if (err)
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
  	if (de->d_ino) {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
379
380
381
382
  		struct ufs_dir_entry *de1 =
  			(struct ufs_dir_entry *) ((char *) de + name_len);
  		de1->d_reclen = cpu_to_fs16(sb, rec_len - name_len);
  		de->d_reclen = cpu_to_fs16(sb, name_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
384
  		de = de1;
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
385

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
  	ufs_set_de_namlen(sb, de, namelen);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
387
  	memcpy(de->d_name, name, namelen + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
389
  	de->d_ino = cpu_to_fs32(sb, inode->i_ino);
  	ufs_set_de_type(sb, de, inode->i_mode);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
390

82b9d1d0d   Nick Piggin   ufs: convert to n...
391
  	err = ufs_commit_chunk(page, pos, rec_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
  	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
393

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
  	mark_inode_dirty(dir);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
  	/* OFFSET_CACHE */
  out_put:
  	ufs_put_page(page);
  out:
  	return err;
  out_unlock:
  	unlock_page(page);
  	goto out_put;
  }
  
  static inline unsigned
  ufs_validate_entry(struct super_block *sb, char *base,
  		   unsigned offset, unsigned mask)
  {
  	struct ufs_dir_entry *de = (struct ufs_dir_entry*)(base + offset);
  	struct ufs_dir_entry *p = (struct ufs_dir_entry*)(base + (offset&mask));
  	while ((char*)p < (char*)de) {
  		if (p->d_reclen == 0)
  			break;
  		p = ufs_next_entry(sb, p);
  	}
  	return (char *)p - base;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
419
420
421
422
423
424
425
426
  
  /*
   * This is blatantly stolen from ext2fs
   */
  static int
  ufs_readdir(struct file *filp, void *dirent, filldir_t filldir)
  {
  	loff_t pos = filp->f_pos;
763454d61   Josef Sipek   [PATCH] struct pa...
427
  	struct inode *inode = filp->f_path.dentry->d_inode;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
428
429
430
431
  	struct super_block *sb = inode->i_sb;
  	unsigned int offset = pos & ~PAGE_CACHE_MASK;
  	unsigned long n = pos >> PAGE_CACHE_SHIFT;
  	unsigned long npages = ufs_dir_pages(inode);
f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
432
  	unsigned chunk_mask = ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
433
434
  	int need_revalidate = filp->f_version != inode->i_version;
  	unsigned flags = UFS_SB(sb)->s_flags;
abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
435
436
  	UFSD("BEGIN
  ");
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
437
438
439
440
441
442
443
444
445
446
447
  
  	if (pos > inode->i_size - UFS_DIR_REC_LEN(1))
  		return 0;
  
  	for ( ; n < npages; n++, offset = 0) {
  		char *kaddr, *limit;
  		struct ufs_dir_entry *de;
  
  		struct page *page = ufs_get_page(inode, n);
  
  		if (IS_ERR(page)) {
9746077a7   Harvey Harrison   ufs: replace rema...
448
  			ufs_error(sb, __func__,
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
  				  "bad page in #%lu",
  				  inode->i_ino);
  			filp->f_pos += PAGE_CACHE_SIZE - offset;
  			return -EIO;
  		}
  		kaddr = page_address(page);
  		if (unlikely(need_revalidate)) {
  			if (offset) {
  				offset = ufs_validate_entry(sb, kaddr, offset, chunk_mask);
  				filp->f_pos = (n<<PAGE_CACHE_SHIFT) + offset;
  			}
  			filp->f_version = inode->i_version;
  			need_revalidate = 0;
  		}
  		de = (struct ufs_dir_entry *)(kaddr+offset);
  		limit = kaddr + ufs_last_byte(inode, n) - UFS_DIR_REC_LEN(1);
  		for ( ;(char*)de <= limit; de = ufs_next_entry(sb, de)) {
  			if (de->d_reclen == 0) {
9746077a7   Harvey Harrison   ufs: replace rema...
467
  				ufs_error(sb, __func__,
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
468
469
470
471
472
473
474
475
476
  					"zero-length directory entry");
  				ufs_put_page(page);
  				return -EIO;
  			}
  			if (de->d_ino) {
  				int over;
  				unsigned char d_type = DT_UNKNOWN;
  
  				offset = (char *)de - kaddr;
abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
477
478
479
480
481
  				UFSD("filldir(%s,%u)
  ", de->d_name,
  				      fs32_to_cpu(sb, de->d_ino));
  				UFSD("namlen %u
  ", ufs_get_de_namlen(sb, de));
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
  
  				if ((flags & UFS_DE_MASK) == UFS_DE_44BSD)
  					d_type = de->d_u.d_44.d_type;
  
  				over = filldir(dirent, de->d_name,
  					       ufs_get_de_namlen(sb, de),
  						(n<<PAGE_CACHE_SHIFT) | offset,
  					       fs32_to_cpu(sb, de->d_ino), d_type);
  				if (over) {
  					ufs_put_page(page);
  					return 0;
  				}
  			}
  			filp->f_pos += fs16_to_cpu(sb, de->d_reclen);
  		}
  		ufs_put_page(page);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
500
  	return 0;
  }
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
501

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
503
504
505
  /*
   * ufs_delete_entry deletes a directory entry by merging it with the
   * previous entry.
   */
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
506
507
  int ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir,
  		     struct page * page)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
509
  	struct super_block *sb = inode->i_sb;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
510
  	char *kaddr = page_address(page);
f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
511
  	unsigned from = ((char*)dir - kaddr) & ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
512
  	unsigned to = ((char*)dir - kaddr) + fs16_to_cpu(sb, dir->d_reclen);
82b9d1d0d   Nick Piggin   ufs: convert to n...
513
  	loff_t pos;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
514
515
516
  	struct ufs_dir_entry *pde = NULL;
  	struct ufs_dir_entry *de = (struct ufs_dir_entry *) (kaddr + from);
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517

abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
518
519
  	UFSD("ENTER
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520

abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
521
522
  	UFSD("ino %u, reclen %u, namlen %u, name %s
  ",
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
523
524
  	      fs32_to_cpu(sb, de->d_ino),
  	      fs16_to_cpu(sb, de->d_reclen),
abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
525
  	      ufs_get_de_namlen(sb, de), de->d_name);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
526
527
528
  
  	while ((char*)de < (char*)dir) {
  		if (de->d_reclen == 0) {
9746077a7   Harvey Harrison   ufs: replace rema...
529
  			ufs_error(inode->i_sb, __func__,
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
530
531
532
  				  "zero-length directory entry");
  			err = -EIO;
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
  		}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
534
535
  		pde = de;
  		de = ufs_next_entry(sb, de);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
536
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
537
538
  	if (pde)
  		from = (char*)pde - (char*)page_address(page);
82b9d1d0d   Nick Piggin   ufs: convert to n...
539
540
  
  	pos = page_offset(page) + from;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
541
  	lock_page(page);
f4e420dc4   Christoph Hellwig   clean up write_be...
542
  	err = ufs_prepare_chunk(page, pos, to - from);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
543
544
  	BUG_ON(err);
  	if (pde)
82b9d1d0d   Nick Piggin   ufs: convert to n...
545
  		pde->d_reclen = cpu_to_fs16(sb, to - from);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
546
  	dir->d_ino = 0;
82b9d1d0d   Nick Piggin   ufs: convert to n...
547
  	err = ufs_commit_chunk(page, pos, to - from);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
548
549
550
551
  	inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
  	mark_inode_dirty(inode);
  out:
  	ufs_put_page(page);
abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
552
553
  	UFSD("EXIT
  ");
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
554
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
556
557
558
559
  }
  
  int ufs_make_empty(struct inode * inode, struct inode *dir)
  {
  	struct super_block * sb = dir->i_sb;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
560
561
  	struct address_space *mapping = inode->i_mapping;
  	struct page *page = grab_cache_page(mapping, 0);
f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
562
  	const unsigned int chunk_size = UFS_SB(sb)->s_uspi->s_dirblksize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
  	struct ufs_dir_entry * de;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
564
  	char *base;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
565
  	int err;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
566
567
  	if (!page)
  		return -ENOMEM;
82b9d1d0d   Nick Piggin   ufs: convert to n...
568

f4e420dc4   Christoph Hellwig   clean up write_be...
569
  	err = ufs_prepare_chunk(page, 0, chunk_size);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
570
571
572
573
  	if (err) {
  		unlock_page(page);
  		goto fail;
  	}
82b9d1d0d   Nick Piggin   ufs: convert to n...
574
  	kmap(page);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
575
576
577
578
  	base = (char*)page_address(page);
  	memset(base, 0, PAGE_CACHE_SIZE);
  
  	de = (struct ufs_dir_entry *) base;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
580
581
582
583
584
585
586
587
588
  	de->d_ino = cpu_to_fs32(sb, inode->i_ino);
  	ufs_set_de_type(sb, de, inode->i_mode);
  	ufs_set_de_namlen(sb, de, 1);
  	de->d_reclen = cpu_to_fs16(sb, UFS_DIR_REC_LEN(1));
  	strcpy (de->d_name, ".");
  	de = (struct ufs_dir_entry *)
  		((char *)de + fs16_to_cpu(sb, de->d_reclen));
  	de->d_ino = cpu_to_fs32(sb, dir->i_ino);
  	ufs_set_de_type(sb, de, dir->i_mode);
f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
589
  	de->d_reclen = cpu_to_fs16(sb, chunk_size - UFS_DIR_REC_LEN(1));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
  	ufs_set_de_namlen(sb, de, 2);
  	strcpy (de->d_name, "..");
82b9d1d0d   Nick Piggin   ufs: convert to n...
592
  	kunmap(page);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
593

f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
594
  	err = ufs_commit_chunk(page, 0, chunk_size);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
595
  fail:
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
596
597
  	page_cache_release(page);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
600
601
602
  }
  
  /*
   * routine to check that the specified directory is empty (for rmdir)
   */
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
603
  int ufs_empty_dir(struct inode * inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
  	struct super_block *sb = inode->i_sb;
  	struct page *page = NULL;
  	unsigned long i, npages = ufs_dir_pages(inode);
  
  	for (i = 0; i < npages; i++) {
  		char *kaddr;
  		struct ufs_dir_entry *de;
  		page = ufs_get_page(inode, i);
  
  		if (IS_ERR(page))
  			continue;
  
  		kaddr = page_address(page);
  		de = (struct ufs_dir_entry *)kaddr;
  		kaddr += ufs_last_byte(inode, i) - UFS_DIR_REC_LEN(1);
  
  		while ((char *)de <= kaddr) {
  			if (de->d_reclen == 0) {
9746077a7   Harvey Harrison   ufs: replace rema...
623
  				ufs_error(inode->i_sb, __func__,
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
624
625
626
627
  					"zero-length directory entry: "
  					"kaddr=%p, de=%p
  ", kaddr, de);
  				goto not_empty;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
  			}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
  			if (de->d_ino) {
  				u16 namelen=ufs_get_de_namlen(sb, de);
  				/* check for . and .. */
  				if (de->d_name[0] != '.')
  					goto not_empty;
  				if (namelen > 2)
  					goto not_empty;
  				if (namelen < 2) {
  					if (inode->i_ino !=
  					    fs32_to_cpu(sb, de->d_ino))
  						goto not_empty;
  				} else if (de->d_name[1] != '.')
  					goto not_empty;
  			}
  			de = ufs_next_entry(sb, de);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
644
  		}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
645
  		ufs_put_page(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
646
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
  	return 1;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
648
649
650
651
  
  not_empty:
  	ufs_put_page(page);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652
  }
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
653
  const struct file_operations ufs_dir_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
654
655
  	.read		= generic_read_dir,
  	.readdir	= ufs_readdir,
1b061d924   Christoph Hellwig   rename the generi...
656
  	.fsync		= generic_file_fsync,
3222a3e55   Christoph Hellwig   [PATCH] fix ->lls...
657
  	.llseek		= generic_file_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
  };