diff options
author | Yoshiji Amagai <amagai.yoshiji@lab.ntt.co.jp> | 2009-04-06 22:01:34 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-07 11:31:15 -0400 |
commit | 2ba466d74ed74f073257f86e61519cb8f8f46184 (patch) | |
tree | 716b990b7ae70198eef9ba6f54dfb0e9fd8547be /fs/nilfs2 | |
parent | f183ff4f05317b7929337455b233496f68217c1a (diff) |
nilfs2: directory entry operations
This adds directory handling functions, most of which comes from the ext2
file system.
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Signed-off-by: Yoshiji Amagai <amagai.yoshiji@lab.ntt.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/nilfs2')
-rw-r--r-- | fs/nilfs2/dir.c | 711 |
1 files changed, 711 insertions, 0 deletions
diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c new file mode 100644 index 000000000000..1b7e6ddabbeb --- /dev/null +++ b/fs/nilfs2/dir.c | |||
@@ -0,0 +1,711 @@ | |||
1 | /* | ||
2 | * dir.c - NILFS directory entry operations | ||
3 | * | ||
4 | * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
19 | * | ||
20 | * Modified for NILFS by Amagai Yoshiji <amagai@osrg.net> | ||
21 | */ | ||
22 | /* | ||
23 | * linux/fs/ext2/dir.c | ||
24 | * | ||
25 | * Copyright (C) 1992, 1993, 1994, 1995 | ||
26 | * Remy Card (card@masi.ibp.fr) | ||
27 | * Laboratoire MASI - Institut Blaise Pascal | ||
28 | * Universite Pierre et Marie Curie (Paris VI) | ||
29 | * | ||
30 | * from | ||
31 | * | ||
32 | * linux/fs/minix/dir.c | ||
33 | * | ||
34 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
35 | * | ||
36 | * ext2 directory handling functions | ||
37 | * | ||
38 | * Big-endian to little-endian byte-swapping/bitmaps by | ||
39 | * David S. Miller (davem@caip.rutgers.edu), 1995 | ||
40 | * | ||
41 | * All code that works with directory layout had been switched to pagecache | ||
42 | * and moved here. AV | ||
43 | */ | ||
44 | |||
45 | #include <linux/pagemap.h> | ||
46 | #include <linux/smp_lock.h> | ||
47 | #include "nilfs.h" | ||
48 | #include "page.h" | ||
49 | |||
50 | /* | ||
51 | * nilfs uses block-sized chunks. Arguably, sector-sized ones would be | ||
52 | * more robust, but we have what we have | ||
53 | */ | ||
54 | static inline unsigned nilfs_chunk_size(struct inode *inode) | ||
55 | { | ||
56 | return inode->i_sb->s_blocksize; | ||
57 | } | ||
58 | |||
59 | static inline void nilfs_put_page(struct page *page) | ||
60 | { | ||
61 | kunmap(page); | ||
62 | page_cache_release(page); | ||
63 | } | ||
64 | |||
65 | static inline unsigned long dir_pages(struct inode *inode) | ||
66 | { | ||
67 | return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * Return the offset into page `page_nr' of the last valid | ||
72 | * byte in that page, plus one. | ||
73 | */ | ||
74 | static unsigned nilfs_last_byte(struct inode *inode, unsigned long page_nr) | ||
75 | { | ||
76 | unsigned last_byte = inode->i_size; | ||
77 | |||
78 | last_byte -= page_nr << PAGE_CACHE_SHIFT; | ||
79 | if (last_byte > PAGE_CACHE_SIZE) | ||
80 | last_byte = PAGE_CACHE_SIZE; | ||
81 | return last_byte; | ||
82 | } | ||
83 | |||
84 | static int nilfs_prepare_chunk_uninterruptible(struct page *page, | ||
85 | struct address_space *mapping, | ||
86 | unsigned from, unsigned to) | ||
87 | { | ||
88 | loff_t pos = page_offset(page) + from; | ||
89 | return block_write_begin(NULL, mapping, pos, to - from, | ||
90 | AOP_FLAG_UNINTERRUPTIBLE, &page, | ||
91 | NULL, nilfs_get_block); | ||
92 | } | ||
93 | |||
94 | static int nilfs_prepare_chunk(struct page *page, | ||
95 | struct address_space *mapping, | ||
96 | unsigned from, unsigned to) | ||
97 | { | ||
98 | loff_t pos = page_offset(page) + from; | ||
99 | return block_write_begin(NULL, mapping, pos, to - from, 0, &page, | ||
100 | NULL, nilfs_get_block); | ||
101 | } | ||
102 | |||
103 | static int nilfs_commit_chunk(struct page *page, | ||
104 | struct address_space *mapping, | ||
105 | unsigned from, unsigned to) | ||
106 | { | ||
107 | struct inode *dir = mapping->host; | ||
108 | struct nilfs_sb_info *sbi = NILFS_SB(dir->i_sb); | ||
109 | loff_t pos = page_offset(page) + from; | ||
110 | unsigned len = to - from; | ||
111 | unsigned nr_dirty, copied; | ||
112 | int err; | ||
113 | |||
114 | nr_dirty = nilfs_page_count_clean_buffers(page, from, to); | ||
115 | copied = block_write_end(NULL, mapping, pos, len, len, page, NULL); | ||
116 | if (pos + copied > dir->i_size) { | ||
117 | i_size_write(dir, pos + copied); | ||
118 | mark_inode_dirty(dir); | ||
119 | } | ||
120 | if (IS_DIRSYNC(dir)) | ||
121 | nilfs_set_transaction_flag(NILFS_TI_SYNC); | ||
122 | err = nilfs_set_file_dirty(sbi, dir, nr_dirty); | ||
123 | unlock_page(page); | ||
124 | return err; | ||
125 | } | ||
126 | |||
127 | static void nilfs_check_page(struct page *page) | ||
128 | { | ||
129 | struct inode *dir = page->mapping->host; | ||
130 | struct super_block *sb = dir->i_sb; | ||
131 | unsigned chunk_size = nilfs_chunk_size(dir); | ||
132 | char *kaddr = page_address(page); | ||
133 | unsigned offs, rec_len; | ||
134 | unsigned limit = PAGE_CACHE_SIZE; | ||
135 | struct nilfs_dir_entry *p; | ||
136 | char *error; | ||
137 | |||
138 | if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) { | ||
139 | limit = dir->i_size & ~PAGE_CACHE_MASK; | ||
140 | if (limit & (chunk_size - 1)) | ||
141 | goto Ebadsize; | ||
142 | if (!limit) | ||
143 | goto out; | ||
144 | } | ||
145 | for (offs = 0; offs <= limit - NILFS_DIR_REC_LEN(1); offs += rec_len) { | ||
146 | p = (struct nilfs_dir_entry *)(kaddr + offs); | ||
147 | rec_len = le16_to_cpu(p->rec_len); | ||
148 | |||
149 | if (rec_len < NILFS_DIR_REC_LEN(1)) | ||
150 | goto Eshort; | ||
151 | if (rec_len & 3) | ||
152 | goto Ealign; | ||
153 | if (rec_len < NILFS_DIR_REC_LEN(p->name_len)) | ||
154 | goto Enamelen; | ||
155 | if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1)) | ||
156 | goto Espan; | ||
157 | } | ||
158 | if (offs != limit) | ||
159 | goto Eend; | ||
160 | out: | ||
161 | SetPageChecked(page); | ||
162 | return; | ||
163 | |||
164 | /* Too bad, we had an error */ | ||
165 | |||
166 | Ebadsize: | ||
167 | nilfs_error(sb, "nilfs_check_page", | ||
168 | "size of directory #%lu is not a multiple of chunk size", | ||
169 | dir->i_ino | ||
170 | ); | ||
171 | goto fail; | ||
172 | Eshort: | ||
173 | error = "rec_len is smaller than minimal"; | ||
174 | goto bad_entry; | ||
175 | Ealign: | ||
176 | error = "unaligned directory entry"; | ||
177 | goto bad_entry; | ||
178 | Enamelen: | ||
179 | error = "rec_len is too small for name_len"; | ||
180 | goto bad_entry; | ||
181 | Espan: | ||
182 | error = "directory entry across blocks"; | ||
183 | bad_entry: | ||
184 | nilfs_error(sb, "nilfs_check_page", "bad entry in directory #%lu: %s - " | ||
185 | "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", | ||
186 | dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs, | ||
187 | (unsigned long) le64_to_cpu(p->inode), | ||
188 | rec_len, p->name_len); | ||
189 | goto fail; | ||
190 | Eend: | ||
191 | p = (struct nilfs_dir_entry *)(kaddr + offs); | ||
192 | nilfs_error(sb, "nilfs_check_page", | ||
193 | "entry in directory #%lu spans the page boundary" | ||
194 | "offset=%lu, inode=%lu", | ||
195 | dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs, | ||
196 | (unsigned long) le64_to_cpu(p->inode)); | ||
197 | fail: | ||
198 | SetPageChecked(page); | ||
199 | SetPageError(page); | ||
200 | } | ||
201 | |||
202 | static struct page *nilfs_get_page(struct inode *dir, unsigned long n) | ||
203 | { | ||
204 | struct address_space *mapping = dir->i_mapping; | ||
205 | struct page *page = read_cache_page(mapping, n, | ||
206 | (filler_t *)mapping->a_ops->readpage, NULL); | ||
207 | if (!IS_ERR(page)) { | ||
208 | wait_on_page_locked(page); | ||
209 | kmap(page); | ||
210 | if (!PageUptodate(page)) | ||
211 | goto fail; | ||
212 | if (!PageChecked(page)) | ||
213 | nilfs_check_page(page); | ||
214 | if (PageError(page)) | ||
215 | goto fail; | ||
216 | } | ||
217 | return page; | ||
218 | |||
219 | fail: | ||
220 | nilfs_put_page(page); | ||
221 | return ERR_PTR(-EIO); | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * NOTE! unlike strncmp, nilfs_match returns 1 for success, 0 for failure. | ||
226 | * | ||
227 | * len <= NILFS_NAME_LEN and de != NULL are guaranteed by caller. | ||
228 | */ | ||
229 | static int | ||
230 | nilfs_match(int len, const char * const name, struct nilfs_dir_entry *de) | ||
231 | { | ||
232 | if (len != de->name_len) | ||
233 | return 0; | ||
234 | if (!de->inode) | ||
235 | return 0; | ||
236 | return !memcmp(name, de->name, len); | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * p is at least 6 bytes before the end of page | ||
241 | */ | ||
242 | static struct nilfs_dir_entry *nilfs_next_entry(struct nilfs_dir_entry *p) | ||
243 | { | ||
244 | return (struct nilfs_dir_entry *)((char *)p + le16_to_cpu(p->rec_len)); | ||
245 | } | ||
246 | |||
247 | static unsigned char | ||
248 | nilfs_filetype_table[NILFS_FT_MAX] = { | ||
249 | [NILFS_FT_UNKNOWN] = DT_UNKNOWN, | ||
250 | [NILFS_FT_REG_FILE] = DT_REG, | ||
251 | [NILFS_FT_DIR] = DT_DIR, | ||
252 | [NILFS_FT_CHRDEV] = DT_CHR, | ||
253 | [NILFS_FT_BLKDEV] = DT_BLK, | ||
254 | [NILFS_FT_FIFO] = DT_FIFO, | ||
255 | [NILFS_FT_SOCK] = DT_SOCK, | ||
256 | [NILFS_FT_SYMLINK] = DT_LNK, | ||
257 | }; | ||
258 | |||
259 | #define S_SHIFT 12 | ||
260 | static unsigned char | ||
261 | nilfs_type_by_mode[S_IFMT >> S_SHIFT] = { | ||
262 | [S_IFREG >> S_SHIFT] = NILFS_FT_REG_FILE, | ||
263 | [S_IFDIR >> S_SHIFT] = NILFS_FT_DIR, | ||
264 | [S_IFCHR >> S_SHIFT] = NILFS_FT_CHRDEV, | ||
265 | [S_IFBLK >> S_SHIFT] = NILFS_FT_BLKDEV, | ||
266 | [S_IFIFO >> S_SHIFT] = NILFS_FT_FIFO, | ||
267 | [S_IFSOCK >> S_SHIFT] = NILFS_FT_SOCK, | ||
268 | [S_IFLNK >> S_SHIFT] = NILFS_FT_SYMLINK, | ||
269 | }; | ||
270 | |||
271 | static void nilfs_set_de_type(struct nilfs_dir_entry *de, struct inode *inode) | ||
272 | { | ||
273 | mode_t mode = inode->i_mode; | ||
274 | |||
275 | de->file_type = nilfs_type_by_mode[(mode & S_IFMT)>>S_SHIFT]; | ||
276 | } | ||
277 | |||
278 | static int nilfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | ||
279 | { | ||
280 | loff_t pos = filp->f_pos; | ||
281 | struct inode *inode = filp->f_dentry->d_inode; | ||
282 | struct super_block *sb = inode->i_sb; | ||
283 | unsigned int offset = pos & ~PAGE_CACHE_MASK; | ||
284 | unsigned long n = pos >> PAGE_CACHE_SHIFT; | ||
285 | unsigned long npages = dir_pages(inode); | ||
286 | /* unsigned chunk_mask = ~(nilfs_chunk_size(inode)-1); */ | ||
287 | unsigned char *types = NULL; | ||
288 | int ret; | ||
289 | |||
290 | if (pos > inode->i_size - NILFS_DIR_REC_LEN(1)) | ||
291 | goto success; | ||
292 | |||
293 | types = nilfs_filetype_table; | ||
294 | |||
295 | for ( ; n < npages; n++, offset = 0) { | ||
296 | char *kaddr, *limit; | ||
297 | struct nilfs_dir_entry *de; | ||
298 | struct page *page = nilfs_get_page(inode, n); | ||
299 | |||
300 | if (IS_ERR(page)) { | ||
301 | nilfs_error(sb, __func__, "bad page in #%lu", | ||
302 | inode->i_ino); | ||
303 | filp->f_pos += PAGE_CACHE_SIZE - offset; | ||
304 | ret = -EIO; | ||
305 | goto done; | ||
306 | } | ||
307 | kaddr = page_address(page); | ||
308 | de = (struct nilfs_dir_entry *)(kaddr + offset); | ||
309 | limit = kaddr + nilfs_last_byte(inode, n) - | ||
310 | NILFS_DIR_REC_LEN(1); | ||
311 | for ( ; (char *)de <= limit; de = nilfs_next_entry(de)) { | ||
312 | if (de->rec_len == 0) { | ||
313 | nilfs_error(sb, __func__, | ||
314 | "zero-length directory entry"); | ||
315 | ret = -EIO; | ||
316 | nilfs_put_page(page); | ||
317 | goto done; | ||
318 | } | ||
319 | if (de->inode) { | ||
320 | int over; | ||
321 | unsigned char d_type = DT_UNKNOWN; | ||
322 | |||
323 | if (types && de->file_type < NILFS_FT_MAX) | ||
324 | d_type = types[de->file_type]; | ||
325 | |||
326 | offset = (char *)de - kaddr; | ||
327 | over = filldir(dirent, de->name, de->name_len, | ||
328 | (n<<PAGE_CACHE_SHIFT) | offset, | ||
329 | le64_to_cpu(de->inode), d_type); | ||
330 | if (over) { | ||
331 | nilfs_put_page(page); | ||
332 | goto success; | ||
333 | } | ||
334 | } | ||
335 | filp->f_pos += le16_to_cpu(de->rec_len); | ||
336 | } | ||
337 | nilfs_put_page(page); | ||
338 | } | ||
339 | |||
340 | success: | ||
341 | ret = 0; | ||
342 | done: | ||
343 | return ret; | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * nilfs_find_entry() | ||
348 | * | ||
349 | * finds an entry in the specified directory with the wanted name. It | ||
350 | * returns the page in which the entry was found, and the entry itself | ||
351 | * (as a parameter - res_dir). Page is returned mapped and unlocked. | ||
352 | * Entry is guaranteed to be valid. | ||
353 | */ | ||
354 | struct nilfs_dir_entry * | ||
355 | nilfs_find_entry(struct inode *dir, struct dentry *dentry, | ||
356 | struct page **res_page) | ||
357 | { | ||
358 | const char *name = dentry->d_name.name; | ||
359 | int namelen = dentry->d_name.len; | ||
360 | unsigned reclen = NILFS_DIR_REC_LEN(namelen); | ||
361 | unsigned long start, n; | ||
362 | unsigned long npages = dir_pages(dir); | ||
363 | struct page *page = NULL; | ||
364 | struct nilfs_inode_info *ei = NILFS_I(dir); | ||
365 | struct nilfs_dir_entry *de; | ||
366 | |||
367 | if (npages == 0) | ||
368 | goto out; | ||
369 | |||
370 | /* OFFSET_CACHE */ | ||
371 | *res_page = NULL; | ||
372 | |||
373 | start = ei->i_dir_start_lookup; | ||
374 | if (start >= npages) | ||
375 | start = 0; | ||
376 | n = start; | ||
377 | do { | ||
378 | char *kaddr; | ||
379 | page = nilfs_get_page(dir, n); | ||
380 | if (!IS_ERR(page)) { | ||
381 | kaddr = page_address(page); | ||
382 | de = (struct nilfs_dir_entry *)kaddr; | ||
383 | kaddr += nilfs_last_byte(dir, n) - reclen; | ||
384 | while ((char *) de <= kaddr) { | ||
385 | if (de->rec_len == 0) { | ||
386 | nilfs_error(dir->i_sb, __func__, | ||
387 | "zero-length directory entry"); | ||
388 | nilfs_put_page(page); | ||
389 | goto out; | ||
390 | } | ||
391 | if (nilfs_match(namelen, name, de)) | ||
392 | goto found; | ||
393 | de = nilfs_next_entry(de); | ||
394 | } | ||
395 | nilfs_put_page(page); | ||
396 | } | ||
397 | if (++n >= npages) | ||
398 | n = 0; | ||
399 | /* next page is past the blocks we've got */ | ||
400 | if (unlikely(n > (dir->i_blocks >> (PAGE_CACHE_SHIFT - 9)))) { | ||
401 | nilfs_error(dir->i_sb, __func__, | ||
402 | "dir %lu size %lld exceeds block cout %llu", | ||
403 | dir->i_ino, dir->i_size, | ||
404 | (unsigned long long)dir->i_blocks); | ||
405 | goto out; | ||
406 | } | ||
407 | } while (n != start); | ||
408 | out: | ||
409 | return NULL; | ||
410 | |||
411 | found: | ||
412 | *res_page = page; | ||
413 | ei->i_dir_start_lookup = n; | ||
414 | return de; | ||
415 | } | ||
416 | |||
417 | struct nilfs_dir_entry *nilfs_dotdot(struct inode *dir, struct page **p) | ||
418 | { | ||
419 | struct page *page = nilfs_get_page(dir, 0); | ||
420 | struct nilfs_dir_entry *de = NULL; | ||
421 | |||
422 | if (!IS_ERR(page)) { | ||
423 | de = nilfs_next_entry( | ||
424 | (struct nilfs_dir_entry *)page_address(page)); | ||
425 | *p = page; | ||
426 | } | ||
427 | return de; | ||
428 | } | ||
429 | |||
430 | ino_t nilfs_inode_by_name(struct inode *dir, struct dentry *dentry) | ||
431 | { | ||
432 | ino_t res = 0; | ||
433 | struct nilfs_dir_entry *de; | ||
434 | struct page *page; | ||
435 | |||
436 | de = nilfs_find_entry(dir, dentry, &page); | ||
437 | if (de) { | ||
438 | res = le64_to_cpu(de->inode); | ||
439 | kunmap(page); | ||
440 | page_cache_release(page); | ||
441 | } | ||
442 | return res; | ||
443 | } | ||
444 | |||
445 | /* Releases the page */ | ||
446 | void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, | ||
447 | struct page *page, struct inode *inode) | ||
448 | { | ||
449 | unsigned from = (char *) de - (char *) page_address(page); | ||
450 | unsigned to = from + le16_to_cpu(de->rec_len); | ||
451 | struct address_space *mapping = page->mapping; | ||
452 | int err; | ||
453 | |||
454 | lock_page(page); | ||
455 | err = nilfs_prepare_chunk_uninterruptible(page, mapping, from, to); | ||
456 | BUG_ON(err); | ||
457 | de->inode = cpu_to_le64(inode->i_ino); | ||
458 | nilfs_set_de_type(de, inode); | ||
459 | err = nilfs_commit_chunk(page, mapping, from, to); | ||
460 | nilfs_put_page(page); | ||
461 | dir->i_mtime = dir->i_ctime = CURRENT_TIME; | ||
462 | /* NILFS_I(dir)->i_flags &= ~NILFS_BTREE_FL; */ | ||
463 | mark_inode_dirty(dir); | ||
464 | } | ||
465 | |||
466 | /* | ||
467 | * Parent is locked. | ||
468 | */ | ||
469 | int nilfs_add_link(struct dentry *dentry, struct inode *inode) | ||
470 | { | ||
471 | struct inode *dir = dentry->d_parent->d_inode; | ||
472 | const char *name = dentry->d_name.name; | ||
473 | int namelen = dentry->d_name.len; | ||
474 | unsigned chunk_size = nilfs_chunk_size(dir); | ||
475 | unsigned reclen = NILFS_DIR_REC_LEN(namelen); | ||
476 | unsigned short rec_len, name_len; | ||
477 | struct page *page = NULL; | ||
478 | struct nilfs_dir_entry *de; | ||
479 | unsigned long npages = dir_pages(dir); | ||
480 | unsigned long n; | ||
481 | char *kaddr; | ||
482 | unsigned from, to; | ||
483 | int err; | ||
484 | |||
485 | /* | ||
486 | * We take care of directory expansion in the same loop. | ||
487 | * This code plays outside i_size, so it locks the page | ||
488 | * to protect that region. | ||
489 | */ | ||
490 | for (n = 0; n <= npages; n++) { | ||
491 | char *dir_end; | ||
492 | |||
493 | page = nilfs_get_page(dir, n); | ||
494 | err = PTR_ERR(page); | ||
495 | if (IS_ERR(page)) | ||
496 | goto out; | ||
497 | lock_page(page); | ||
498 | kaddr = page_address(page); | ||
499 | dir_end = kaddr + nilfs_last_byte(dir, n); | ||
500 | de = (struct nilfs_dir_entry *)kaddr; | ||
501 | kaddr += PAGE_CACHE_SIZE - reclen; | ||
502 | while ((char *)de <= kaddr) { | ||
503 | if ((char *)de == dir_end) { | ||
504 | /* We hit i_size */ | ||
505 | name_len = 0; | ||
506 | rec_len = chunk_size; | ||
507 | de->rec_len = cpu_to_le16(chunk_size); | ||
508 | de->inode = 0; | ||
509 | goto got_it; | ||
510 | } | ||
511 | if (de->rec_len == 0) { | ||
512 | nilfs_error(dir->i_sb, __func__, | ||
513 | "zero-length directory entry"); | ||
514 | err = -EIO; | ||
515 | goto out_unlock; | ||
516 | } | ||
517 | err = -EEXIST; | ||
518 | if (nilfs_match(namelen, name, de)) | ||
519 | goto out_unlock; | ||
520 | name_len = NILFS_DIR_REC_LEN(de->name_len); | ||
521 | rec_len = le16_to_cpu(de->rec_len); | ||
522 | if (!de->inode && rec_len >= reclen) | ||
523 | goto got_it; | ||
524 | if (rec_len >= name_len + reclen) | ||
525 | goto got_it; | ||
526 | de = (struct nilfs_dir_entry *)((char *)de + rec_len); | ||
527 | } | ||
528 | unlock_page(page); | ||
529 | nilfs_put_page(page); | ||
530 | } | ||
531 | BUG(); | ||
532 | return -EINVAL; | ||
533 | |||
534 | got_it: | ||
535 | from = (char *)de - (char *)page_address(page); | ||
536 | to = from + rec_len; | ||
537 | err = nilfs_prepare_chunk(page, page->mapping, from, to); | ||
538 | if (err) | ||
539 | goto out_unlock; | ||
540 | if (de->inode) { | ||
541 | struct nilfs_dir_entry *de1; | ||
542 | |||
543 | de1 = (struct nilfs_dir_entry *)((char *)de + name_len); | ||
544 | de1->rec_len = cpu_to_le16(rec_len - name_len); | ||
545 | de->rec_len = cpu_to_le16(name_len); | ||
546 | de = de1; | ||
547 | } | ||
548 | de->name_len = namelen; | ||
549 | memcpy(de->name, name, namelen); | ||
550 | de->inode = cpu_to_le64(inode->i_ino); | ||
551 | nilfs_set_de_type(de, inode); | ||
552 | err = nilfs_commit_chunk(page, page->mapping, from, to); | ||
553 | dir->i_mtime = dir->i_ctime = CURRENT_TIME; | ||
554 | /* NILFS_I(dir)->i_flags &= ~NILFS_BTREE_FL; */ | ||
555 | mark_inode_dirty(dir); | ||
556 | /* OFFSET_CACHE */ | ||
557 | out_put: | ||
558 | nilfs_put_page(page); | ||
559 | out: | ||
560 | return err; | ||
561 | out_unlock: | ||
562 | unlock_page(page); | ||
563 | goto out_put; | ||
564 | } | ||
565 | |||
566 | /* | ||
567 | * nilfs_delete_entry deletes a directory entry by merging it with the | ||
568 | * previous entry. Page is up-to-date. Releases the page. | ||
569 | */ | ||
570 | int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page) | ||
571 | { | ||
572 | struct address_space *mapping = page->mapping; | ||
573 | struct inode *inode = mapping->host; | ||
574 | char *kaddr = page_address(page); | ||
575 | unsigned from = ((char *)dir - kaddr) & ~(nilfs_chunk_size(inode) - 1); | ||
576 | unsigned to = ((char *)dir - kaddr) + le16_to_cpu(dir->rec_len); | ||
577 | struct nilfs_dir_entry *pde = NULL; | ||
578 | struct nilfs_dir_entry *de = (struct nilfs_dir_entry *)(kaddr + from); | ||
579 | int err; | ||
580 | |||
581 | while ((char *)de < (char *)dir) { | ||
582 | if (de->rec_len == 0) { | ||
583 | nilfs_error(inode->i_sb, __func__, | ||
584 | "zero-length directory entry"); | ||
585 | err = -EIO; | ||
586 | goto out; | ||
587 | } | ||
588 | pde = de; | ||
589 | de = nilfs_next_entry(de); | ||
590 | } | ||
591 | if (pde) | ||
592 | from = (char *)pde - (char *)page_address(page); | ||
593 | lock_page(page); | ||
594 | err = nilfs_prepare_chunk(page, mapping, from, to); | ||
595 | BUG_ON(err); | ||
596 | if (pde) | ||
597 | pde->rec_len = cpu_to_le16(to - from); | ||
598 | dir->inode = 0; | ||
599 | err = nilfs_commit_chunk(page, mapping, from, to); | ||
600 | inode->i_ctime = inode->i_mtime = CURRENT_TIME; | ||
601 | /* NILFS_I(inode)->i_flags &= ~NILFS_BTREE_FL; */ | ||
602 | mark_inode_dirty(inode); | ||
603 | out: | ||
604 | nilfs_put_page(page); | ||
605 | return err; | ||
606 | } | ||
607 | |||
608 | /* | ||
609 | * Set the first fragment of directory. | ||
610 | */ | ||
611 | int nilfs_make_empty(struct inode *inode, struct inode *parent) | ||
612 | { | ||
613 | struct address_space *mapping = inode->i_mapping; | ||
614 | struct page *page = grab_cache_page(mapping, 0); | ||
615 | unsigned chunk_size = nilfs_chunk_size(inode); | ||
616 | struct nilfs_dir_entry *de; | ||
617 | int err; | ||
618 | void *kaddr; | ||
619 | |||
620 | if (!page) | ||
621 | return -ENOMEM; | ||
622 | |||
623 | err = nilfs_prepare_chunk(page, mapping, 0, chunk_size); | ||
624 | if (unlikely(err)) { | ||
625 | unlock_page(page); | ||
626 | goto fail; | ||
627 | } | ||
628 | kaddr = kmap_atomic(page, KM_USER0); | ||
629 | memset(kaddr, 0, chunk_size); | ||
630 | de = (struct nilfs_dir_entry *)kaddr; | ||
631 | de->name_len = 1; | ||
632 | de->rec_len = cpu_to_le16(NILFS_DIR_REC_LEN(1)); | ||
633 | memcpy(de->name, ".\0\0", 4); | ||
634 | de->inode = cpu_to_le64(inode->i_ino); | ||
635 | nilfs_set_de_type(de, inode); | ||
636 | |||
637 | de = (struct nilfs_dir_entry *)(kaddr + NILFS_DIR_REC_LEN(1)); | ||
638 | de->name_len = 2; | ||
639 | de->rec_len = cpu_to_le16(chunk_size - NILFS_DIR_REC_LEN(1)); | ||
640 | de->inode = cpu_to_le64(parent->i_ino); | ||
641 | memcpy(de->name, "..\0", 4); | ||
642 | nilfs_set_de_type(de, inode); | ||
643 | kunmap_atomic(kaddr, KM_USER0); | ||
644 | err = nilfs_commit_chunk(page, mapping, 0, chunk_size); | ||
645 | fail: | ||
646 | page_cache_release(page); | ||
647 | return err; | ||
648 | } | ||
649 | |||
650 | /* | ||
651 | * routine to check that the specified directory is empty (for rmdir) | ||
652 | */ | ||
653 | int nilfs_empty_dir(struct inode *inode) | ||
654 | { | ||
655 | struct page *page = NULL; | ||
656 | unsigned long i, npages = dir_pages(inode); | ||
657 | |||
658 | for (i = 0; i < npages; i++) { | ||
659 | char *kaddr; | ||
660 | struct nilfs_dir_entry *de; | ||
661 | |||
662 | page = nilfs_get_page(inode, i); | ||
663 | if (IS_ERR(page)) | ||
664 | continue; | ||
665 | |||
666 | kaddr = page_address(page); | ||
667 | de = (struct nilfs_dir_entry *)kaddr; | ||
668 | kaddr += nilfs_last_byte(inode, i) - NILFS_DIR_REC_LEN(1); | ||
669 | |||
670 | while ((char *)de <= kaddr) { | ||
671 | if (de->rec_len == 0) { | ||
672 | nilfs_error(inode->i_sb, __func__, | ||
673 | "zero-length directory entry " | ||
674 | "(kaddr=%p, de=%p)\n", kaddr, de); | ||
675 | goto not_empty; | ||
676 | } | ||
677 | if (de->inode != 0) { | ||
678 | /* check for . and .. */ | ||
679 | if (de->name[0] != '.') | ||
680 | goto not_empty; | ||
681 | if (de->name_len > 2) | ||
682 | goto not_empty; | ||
683 | if (de->name_len < 2) { | ||
684 | if (de->inode != | ||
685 | cpu_to_le64(inode->i_ino)) | ||
686 | goto not_empty; | ||
687 | } else if (de->name[1] != '.') | ||
688 | goto not_empty; | ||
689 | } | ||
690 | de = nilfs_next_entry(de); | ||
691 | } | ||
692 | nilfs_put_page(page); | ||
693 | } | ||
694 | return 1; | ||
695 | |||
696 | not_empty: | ||
697 | nilfs_put_page(page); | ||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | struct file_operations nilfs_dir_operations = { | ||
702 | .llseek = generic_file_llseek, | ||
703 | .read = generic_read_dir, | ||
704 | .readdir = nilfs_readdir, | ||
705 | .ioctl = nilfs_ioctl, | ||
706 | #ifdef CONFIG_COMPAT | ||
707 | .compat_ioctl = nilfs_compat_ioctl, | ||
708 | #endif /* CONFIG_COMPAT */ | ||
709 | .fsync = nilfs_sync_file, | ||
710 | |||
711 | }; | ||