Blame view

fs/ufs/dir.c 16.1 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
21
   */
  
  #include <linux/time.h>
  #include <linux/fs.h>
  #include <linux/ufs_fs.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
  
  #include "swab.h"
  #include "util.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  /*
   * 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,
  		const char * const name, struct ufs_dir_entry * de)
  {
  	if (len != ufs_get_de_namlen(sb, de))
  		return 0;
  	if (!de->d_ino)
  		return 0;
  	return !memcmp(name, de->d_name, len);
  }
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
39
  static int ufs_commit_chunk(struct page *page, unsigned from, unsigned to)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
41
42
43
44
45
46
47
48
49
50
  	struct inode *dir = page->mapping->host;
  	int err = 0;
  	dir->i_version++;
  	page->mapping->a_ops->commit_write(NULL, page, from, to);
  	if (IS_DIRSYNC(dir))
  		err = write_one_page(page, 1);
  	else
  		unlock_page(page);
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
52
53
54
55
56
  static inline void ufs_put_page(struct page *page)
  {
  	kunmap(page);
  	page_cache_release(page);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
58
59
60
61
  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
62

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
63
64
65
66
67
68
69
70
71
72
  ino_t ufs_inode_by_name(struct inode *dir, struct dentry *dentry)
  {
  	ino_t res = 0;
  	struct ufs_dir_entry *de;
  	struct page *page;
  	
  	de = ufs_find_entry(dir, dentry, &page);
  	if (de) {
  		res = fs32_to_cpu(dir->i_sb, de->d_ino);
  		ufs_put_page(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
74
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
77
78
79
  /* 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
80
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
81
82
83
  	unsigned from = (char *) de - (char *) page_address(page);
  	unsigned to = from + fs16_to_cpu(dir->i_sb, de->d_reclen);
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
85
86
87
88
89
90
91
92
93
94
  	lock_page(page);
  	err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
  	BUG_ON(err);
  	de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino);
  	ufs_set_de_type(dir->i_sb, de, inode->i_mode);
  	err = ufs_commit_chunk(page, from, to);
  	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
95

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
97
98
99
100
101
102
103
  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...
104
  	const unsigned chunk_mask = UFS_SB(sb)->s_uspi->s_dirblksize - 1;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
105
106
107
108
109
  	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...
110
  		if (limit & chunk_mask)
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
111
112
113
  			goto Ebadsize;
  		if (!limit)
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
115
116
117
118
119
120
121
122
123
124
  	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...
125
  		if (((offs + rec_len - 1) ^ offs) & ~chunk_mask)
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
126
127
128
129
130
131
132
133
134
135
136
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
  			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);
  	ufs_error (sb, "ext2_check_page",
  		   "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
175

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
176
177
178
  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...
179
  	struct page *page = read_mapping_page(mapping, n, NULL);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
180
  	if (!IS_ERR(page)) {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
181
  		kmap(page);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
182
183
184
185
  		if (!PageChecked(page))
  			ufs_check_page(page);
  		if (PageError(page))
  			goto fail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
187
  	return page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
189
190
191
  fail:
  	ufs_put_page(page);
  	return ERR_PTR(-EIO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
  }
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
193
194
195
196
197
198
  /*
   * 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
199
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
200
201
202
203
204
205
  	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
206
  }
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
207
208
  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
209
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
210
211
  	return (struct ufs_dir_entry *)((char *)p +
  					fs16_to_cpu(sb, p->d_reclen));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
  }
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
213
214
  
  struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct page **p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
216
217
  	struct page *page = ufs_get_page(dir, 0);
  	struct ufs_dir_entry *de = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
219
220
221
222
  	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
223
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
224
  	return de;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
  }
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
226
227
228
229
230
231
232
233
234
235
  /*
   *	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.
   */
  struct ufs_dir_entry *ufs_find_entry(struct inode *dir, struct dentry *dentry,
  				     struct page **res_page)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
237
238
239
240
241
242
243
  	struct super_block *sb = dir->i_sb;
  	const char *name = dentry->d_name.name;
  	int namelen = dentry->d_name.len;
  	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...
244
  	struct ufs_inode_info *ui = UFS_I(dir);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
245
  	struct ufs_dir_entry *de;
abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
246
247
  	UFSD("ENTER, dir_ino %lu, name %s, namlen %u
  ", dir->i_ino, name, namelen);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
248
249
250
251
252
253
  
  	if (npages == 0 || namelen > UFS_MAXNAMLEN)
  		goto out;
  
  	/* OFFSET_CACHE */
  	*res_page = NULL;
dd187a260   Evgeniy Dushistov   [PATCH] ufs: litt...
254
  	start = ui->i_dir_start_lookup;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
  	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) {
  					ufs_error(dir->i_sb, __FUNCTION__,
  						  "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...
286
  	ui->i_dir_start_lookup = n;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
287
  	return de;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
289
290
  }
  
  /*
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
291
   *	Parent is locked.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
   */
  int ufs_add_link(struct dentry *dentry, struct inode *inode)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
296
297
  	struct inode *dir = dentry->d_parent->d_inode;
  	const char *name = dentry->d_name.name;
  	int namelen = dentry->d_name.len;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
298
299
  	struct super_block *sb = dir->i_sb;
  	unsigned reclen = UFS_DIR_REC_LEN(namelen);
f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
300
  	const unsigned int chunk_size = UFS_SB(sb)->s_uspi->s_dirblksize;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
301
302
303
304
305
306
307
  	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;
  	unsigned from, to;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
  	int err;
abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
309
310
  	UFSD("ENTER, name %s, namelen %u
  ", name, namelen);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
  
  	/*
  	 * 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...
333
334
  				rec_len = chunk_size;
  				de->d_reclen = cpu_to_fs16(sb, chunk_size);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
335
336
  				de->d_ino = 0;
  				goto got_it;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
  			}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
  			if (de->d_reclen == 0) {
  				ufs_error(dir->i_sb, __FUNCTION__,
  					  "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
354
  		}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
355
356
  		unlock_page(page);
  		ufs_put_page(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
358
359
360
361
362
363
364
365
366
  	BUG();
  	return -EINVAL;
  
  got_it:
  	from = (char*)de - (char*)page_address(page);
  	to = from + rec_len;
  	err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
  	if (err)
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
  	if (de->d_ino) {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
368
369
370
371
  		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
372
373
  		de = de1;
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
374

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
  	ufs_set_de_namlen(sb, de, namelen);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
376
  	memcpy(de->d_name, name, namelen + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
  	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...
379
380
  
  	err = ufs_commit_chunk(page, from, to);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
  	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
382

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
  	mark_inode_dirty(dir);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
  	/* 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
407

b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
408
409
410
411
412
413
414
415
  
  /*
   * 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...
416
  	struct inode *inode = filp->f_path.dentry->d_inode;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
417
418
419
420
  	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...
421
  	unsigned chunk_mask = ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
422
423
  	int need_revalidate = filp->f_version != inode->i_version;
  	unsigned flags = UFS_SB(sb)->s_flags;
abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
424
425
  	UFSD("BEGIN
  ");
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
  
  	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)) {
  			ufs_error(sb, __FUNCTION__,
  				  "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) {
  				ufs_error(sb, __FUNCTION__,
  					"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...
466
467
468
469
470
  				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...
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
  
  				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
488
489
  	return 0;
  }
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
490

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
491
492
493
494
  /*
   * ufs_delete_entry deletes a directory entry by merging it with the
   * previous entry.
   */
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
495
496
  int ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir,
  		     struct page * page)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
498
499
500
  	struct super_block *sb = inode->i_sb;
  	struct address_space *mapping = page->mapping;
  	char *kaddr = page_address(page);
f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
501
  	unsigned from = ((char*)dir - kaddr) & ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
502
503
504
505
  	unsigned to = ((char*)dir - kaddr) + fs16_to_cpu(sb, dir->d_reclen);
  	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
506

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

abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
510
511
  	UFSD("ino %u, reclen %u, namlen %u, name %s
  ",
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
512
513
  	      fs32_to_cpu(sb, de->d_ino),
  	      fs16_to_cpu(sb, de->d_reclen),
abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
514
  	      ufs_get_de_namlen(sb, de), de->d_name);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
515
516
517
518
519
520
521
  
  	while ((char*)de < (char*)dir) {
  		if (de->d_reclen == 0) {
  			ufs_error(inode->i_sb, __FUNCTION__,
  				  "zero-length directory entry");
  			err = -EIO;
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
  		}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
523
524
  		pde = de;
  		de = ufs_next_entry(sb, de);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
  	}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
526
527
528
529
530
531
532
533
534
535
536
537
538
  	if (pde)
  		from = (char*)pde - (char*)page_address(page);
  	lock_page(page);
  	err = mapping->a_ops->prepare_write(NULL, page, from, to);
  	BUG_ON(err);
  	if (pde)
  		pde->d_reclen = cpu_to_fs16(sb, to-from);
  	dir->d_ino = 0;
  	err = ufs_commit_chunk(page, from, to);
  	inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
  	mark_inode_dirty(inode);
  out:
  	ufs_put_page(page);
abf5d15fd   Evgeniy Dushistov   [PATCH] ufs: easy...
539
540
  	UFSD("EXIT
  ");
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
541
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
543
544
545
546
  }
  
  int ufs_make_empty(struct inode * inode, struct inode *dir)
  {
  	struct super_block * sb = dir->i_sb;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
547
548
  	struct address_space *mapping = inode->i_mapping;
  	struct page *page = grab_cache_page(mapping, 0);
f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
549
  	const unsigned int chunk_size = UFS_SB(sb)->s_uspi->s_dirblksize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
  	struct ufs_dir_entry * de;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
551
  	char *base;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
  	int err;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
553
554
555
  	if (!page)
  		return -ENOMEM;
  	kmap(page);
f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
556
  	err = mapping->a_ops->prepare_write(NULL, page, 0, chunk_size);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
557
558
559
560
561
562
563
564
565
566
  	if (err) {
  		unlock_page(page);
  		goto fail;
  	}
  
  
  	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
567

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
570
571
572
573
574
575
576
  	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...
577
  	de->d_reclen = cpu_to_fs16(sb, chunk_size - UFS_DIR_REC_LEN(1));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
579
  	ufs_set_de_namlen(sb, de, 2);
  	strcpy (de->d_name, "..");
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
580

f336953bf   Evgeniy Dushistov   [PATCH] ufs: rest...
581
  	err = ufs_commit_chunk(page, 0, chunk_size);
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
582
583
584
585
  fail:
  	kunmap(page);
  	page_cache_release(page);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
587
588
589
590
  }
  
  /*
   * routine to check that the specified directory is empty (for rmdir)
   */
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
591
  int ufs_empty_dir(struct inode * inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
  {
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
  	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) {
  				ufs_error(inode->i_sb, __FUNCTION__,
  					"zero-length directory entry: "
  					"kaddr=%p, de=%p
  ", kaddr, de);
  				goto not_empty;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
  			}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
  			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
632
  		}
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
633
  		ufs_put_page(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
  	return 1;
b71034e5e   Evgeniy Dushistov   [PATCH] ufs: dire...
636
637
638
639
  
  not_empty:
  	ufs_put_page(page);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
  }
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
641
  const struct file_operations ufs_dir_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
643
644
645
  	.read		= generic_read_dir,
  	.readdir	= ufs_readdir,
  	.fsync		= file_fsync,
  };