diff options
author | Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> | 2010-07-25 07:39:03 -0400 |
---|---|---|
committer | Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> | 2010-07-25 07:46:43 -0400 |
commit | 6cda9fa2575ec0869fe77b0bdf295c0e51868cab (patch) | |
tree | 09f160f79f2ab135cd0c9ce0a2099d96423e5e00 | |
parent | c28e69d9332aab739920082a0a5677d861390824 (diff) |
nilfs2: avoid rec_len overflow with 64KB block size
With 64KB blocksize, a directory entry can have size 64KB which does
not fit into 16 bits we have for entry length. So this patch stores
0xffff instead and converts value when read from / written to disk.
Nilfs derives its directory implementation from ext2 filesystem, and
this draws upon the corresponding change on ext2.
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
-rw-r--r-- | fs/nilfs2/dir.c | 26 | ||||
-rw-r--r-- | include/linux/nilfs2_fs.h | 18 |
2 files changed, 32 insertions, 12 deletions
diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index d8d183e6d095..b60277b44468 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c | |||
@@ -141,7 +141,7 @@ static void nilfs_check_page(struct page *page) | |||
141 | } | 141 | } |
142 | for (offs = 0; offs <= limit - NILFS_DIR_REC_LEN(1); offs += rec_len) { | 142 | for (offs = 0; offs <= limit - NILFS_DIR_REC_LEN(1); offs += rec_len) { |
143 | p = (struct nilfs_dir_entry *)(kaddr + offs); | 143 | p = (struct nilfs_dir_entry *)(kaddr + offs); |
144 | rec_len = le16_to_cpu(p->rec_len); | 144 | rec_len = nilfs_rec_len_from_disk(p->rec_len); |
145 | 145 | ||
146 | if (rec_len < NILFS_DIR_REC_LEN(1)) | 146 | if (rec_len < NILFS_DIR_REC_LEN(1)) |
147 | goto Eshort; | 147 | goto Eshort; |
@@ -235,7 +235,8 @@ nilfs_match(int len, const unsigned char *name, struct nilfs_dir_entry *de) | |||
235 | */ | 235 | */ |
236 | static struct nilfs_dir_entry *nilfs_next_entry(struct nilfs_dir_entry *p) | 236 | static struct nilfs_dir_entry *nilfs_next_entry(struct nilfs_dir_entry *p) |
237 | { | 237 | { |
238 | return (struct nilfs_dir_entry *)((char *)p + le16_to_cpu(p->rec_len)); | 238 | return (struct nilfs_dir_entry *)((char *)p + |
239 | nilfs_rec_len_from_disk(p->rec_len)); | ||
239 | } | 240 | } |
240 | 241 | ||
241 | static unsigned char | 242 | static unsigned char |
@@ -326,7 +327,7 @@ static int nilfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
326 | goto success; | 327 | goto success; |
327 | } | 328 | } |
328 | } | 329 | } |
329 | filp->f_pos += le16_to_cpu(de->rec_len); | 330 | filp->f_pos += nilfs_rec_len_from_disk(de->rec_len); |
330 | } | 331 | } |
331 | nilfs_put_page(page); | 332 | nilfs_put_page(page); |
332 | } | 333 | } |
@@ -441,7 +442,7 @@ void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, | |||
441 | struct page *page, struct inode *inode) | 442 | struct page *page, struct inode *inode) |
442 | { | 443 | { |
443 | unsigned from = (char *) de - (char *) page_address(page); | 444 | unsigned from = (char *) de - (char *) page_address(page); |
444 | unsigned to = from + le16_to_cpu(de->rec_len); | 445 | unsigned to = from + nilfs_rec_len_from_disk(de->rec_len); |
445 | struct address_space *mapping = page->mapping; | 446 | struct address_space *mapping = page->mapping; |
446 | int err; | 447 | int err; |
447 | 448 | ||
@@ -497,7 +498,7 @@ int nilfs_add_link(struct dentry *dentry, struct inode *inode) | |||
497 | /* We hit i_size */ | 498 | /* We hit i_size */ |
498 | name_len = 0; | 499 | name_len = 0; |
499 | rec_len = chunk_size; | 500 | rec_len = chunk_size; |
500 | de->rec_len = cpu_to_le16(chunk_size); | 501 | de->rec_len = nilfs_rec_len_to_disk(chunk_size); |
501 | de->inode = 0; | 502 | de->inode = 0; |
502 | goto got_it; | 503 | goto got_it; |
503 | } | 504 | } |
@@ -511,7 +512,7 @@ int nilfs_add_link(struct dentry *dentry, struct inode *inode) | |||
511 | if (nilfs_match(namelen, name, de)) | 512 | if (nilfs_match(namelen, name, de)) |
512 | goto out_unlock; | 513 | goto out_unlock; |
513 | name_len = NILFS_DIR_REC_LEN(de->name_len); | 514 | name_len = NILFS_DIR_REC_LEN(de->name_len); |
514 | rec_len = le16_to_cpu(de->rec_len); | 515 | rec_len = nilfs_rec_len_from_disk(de->rec_len); |
515 | if (!de->inode && rec_len >= reclen) | 516 | if (!de->inode && rec_len >= reclen) |
516 | goto got_it; | 517 | goto got_it; |
517 | if (rec_len >= name_len + reclen) | 518 | if (rec_len >= name_len + reclen) |
@@ -534,8 +535,8 @@ got_it: | |||
534 | struct nilfs_dir_entry *de1; | 535 | struct nilfs_dir_entry *de1; |
535 | 536 | ||
536 | de1 = (struct nilfs_dir_entry *)((char *)de + name_len); | 537 | de1 = (struct nilfs_dir_entry *)((char *)de + name_len); |
537 | de1->rec_len = cpu_to_le16(rec_len - name_len); | 538 | de1->rec_len = nilfs_rec_len_to_disk(rec_len - name_len); |
538 | de->rec_len = cpu_to_le16(name_len); | 539 | de->rec_len = nilfs_rec_len_to_disk(name_len); |
539 | de = de1; | 540 | de = de1; |
540 | } | 541 | } |
541 | de->name_len = namelen; | 542 | de->name_len = namelen; |
@@ -566,7 +567,8 @@ int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page) | |||
566 | struct inode *inode = mapping->host; | 567 | struct inode *inode = mapping->host; |
567 | char *kaddr = page_address(page); | 568 | char *kaddr = page_address(page); |
568 | unsigned from = ((char *)dir - kaddr) & ~(nilfs_chunk_size(inode) - 1); | 569 | unsigned from = ((char *)dir - kaddr) & ~(nilfs_chunk_size(inode) - 1); |
569 | unsigned to = ((char *)dir - kaddr) + le16_to_cpu(dir->rec_len); | 570 | unsigned to = ((char *)dir - kaddr) + |
571 | nilfs_rec_len_from_disk(dir->rec_len); | ||
570 | struct nilfs_dir_entry *pde = NULL; | 572 | struct nilfs_dir_entry *pde = NULL; |
571 | struct nilfs_dir_entry *de = (struct nilfs_dir_entry *)(kaddr + from); | 573 | struct nilfs_dir_entry *de = (struct nilfs_dir_entry *)(kaddr + from); |
572 | int err; | 574 | int err; |
@@ -587,7 +589,7 @@ int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page) | |||
587 | err = nilfs_prepare_chunk(page, mapping, from, to); | 589 | err = nilfs_prepare_chunk(page, mapping, from, to); |
588 | BUG_ON(err); | 590 | BUG_ON(err); |
589 | if (pde) | 591 | if (pde) |
590 | pde->rec_len = cpu_to_le16(to - from); | 592 | pde->rec_len = nilfs_rec_len_to_disk(to - from); |
591 | dir->inode = 0; | 593 | dir->inode = 0; |
592 | nilfs_commit_chunk(page, mapping, from, to); | 594 | nilfs_commit_chunk(page, mapping, from, to); |
593 | inode->i_ctime = inode->i_mtime = CURRENT_TIME; | 595 | inode->i_ctime = inode->i_mtime = CURRENT_TIME; |
@@ -621,14 +623,14 @@ int nilfs_make_empty(struct inode *inode, struct inode *parent) | |||
621 | memset(kaddr, 0, chunk_size); | 623 | memset(kaddr, 0, chunk_size); |
622 | de = (struct nilfs_dir_entry *)kaddr; | 624 | de = (struct nilfs_dir_entry *)kaddr; |
623 | de->name_len = 1; | 625 | de->name_len = 1; |
624 | de->rec_len = cpu_to_le16(NILFS_DIR_REC_LEN(1)); | 626 | de->rec_len = nilfs_rec_len_to_disk(NILFS_DIR_REC_LEN(1)); |
625 | memcpy(de->name, ".\0\0", 4); | 627 | memcpy(de->name, ".\0\0", 4); |
626 | de->inode = cpu_to_le64(inode->i_ino); | 628 | de->inode = cpu_to_le64(inode->i_ino); |
627 | nilfs_set_de_type(de, inode); | 629 | nilfs_set_de_type(de, inode); |
628 | 630 | ||
629 | de = (struct nilfs_dir_entry *)(kaddr + NILFS_DIR_REC_LEN(1)); | 631 | de = (struct nilfs_dir_entry *)(kaddr + NILFS_DIR_REC_LEN(1)); |
630 | de->name_len = 2; | 632 | de->name_len = 2; |
631 | de->rec_len = cpu_to_le16(chunk_size - NILFS_DIR_REC_LEN(1)); | 633 | de->rec_len = nilfs_rec_len_to_disk(chunk_size - NILFS_DIR_REC_LEN(1)); |
632 | de->inode = cpu_to_le64(parent->i_ino); | 634 | de->inode = cpu_to_le64(parent->i_ino); |
633 | memcpy(de->name, "..\0", 4); | 635 | memcpy(de->name, "..\0", 4); |
634 | nilfs_set_de_type(de, inode); | 636 | nilfs_set_de_type(de, inode); |
diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h index 7dd4cd494490..970828a5ffc5 100644 --- a/include/linux/nilfs2_fs.h +++ b/include/linux/nilfs2_fs.h | |||
@@ -326,7 +326,25 @@ enum { | |||
326 | #define NILFS_DIR_ROUND (NILFS_DIR_PAD - 1) | 326 | #define NILFS_DIR_ROUND (NILFS_DIR_PAD - 1) |
327 | #define NILFS_DIR_REC_LEN(name_len) (((name_len) + 12 + NILFS_DIR_ROUND) & \ | 327 | #define NILFS_DIR_REC_LEN(name_len) (((name_len) + 12 + NILFS_DIR_ROUND) & \ |
328 | ~NILFS_DIR_ROUND) | 328 | ~NILFS_DIR_ROUND) |
329 | #define NILFS_MAX_REC_LEN ((1<<16)-1) | ||
329 | 330 | ||
331 | static inline unsigned nilfs_rec_len_from_disk(__le16 dlen) | ||
332 | { | ||
333 | unsigned len = le16_to_cpu(dlen); | ||
334 | |||
335 | if (len == NILFS_MAX_REC_LEN) | ||
336 | return 1 << 16; | ||
337 | return len; | ||
338 | } | ||
339 | |||
340 | static inline __le16 nilfs_rec_len_to_disk(unsigned len) | ||
341 | { | ||
342 | if (len == (1 << 16)) | ||
343 | return cpu_to_le16(NILFS_MAX_REC_LEN); | ||
344 | else if (len > (1 << 16)) | ||
345 | BUG(); | ||
346 | return cpu_to_le16(len); | ||
347 | } | ||
330 | 348 | ||
331 | /** | 349 | /** |
332 | * struct nilfs_finfo - file information | 350 | * struct nilfs_finfo - file information |