aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Marzinski <bmarzins@redhat.com>2015-12-01 09:30:34 -0500
committerBob Peterson <rpeterso@redhat.com>2015-12-14 13:19:34 -0500
commit340174722929d80a107120400bab527cfc7e47f1 (patch)
tree0385ac25db0991a4da1975bac80be03cf8ca64c3
parent2aba1b5b4f78d56a764b92bae58298ad3fffdc4a (diff)
gfs2: keep offset when splitting dir leaf blocks
Currently, when gfs2 splits a directory leaf block, the dirents that need to be copied to the new leaf block are packed into the start of it. This is good for space efficiency. However, if gfs2 were to copy those dirents into the exact same offset in the new leaf block as they had in the old block, it would be able to generate a readdir cookie based on the dirent location, that would be guaranteed to be unique up well past where the current code is statistically almost guaranteed to have collisions. So, gfs2 now keeps the dirent's offset in the block the same when it copies it to the new leaf block. Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com> Signed-off-by: Bob Peterson <rpeterso@redhat.com>
-rw-r--r--fs/gfs2/dir.c69
1 files changed, 53 insertions, 16 deletions
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index c2486598fb87..4ee008c6d64b 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -443,6 +443,27 @@ static int gfs2_dirent_last(const struct gfs2_dirent *dent,
443 return 0; 443 return 0;
444} 444}
445 445
446/* Look for the dirent that contains the offset specified in data. Once we
447 * find that dirent, there must be space available there for the new dirent */
448static int gfs2_dirent_find_offset(const struct gfs2_dirent *dent,
449 const struct qstr *name,
450 void *ptr)
451{
452 unsigned required = GFS2_DIRENT_SIZE(name->len);
453 unsigned actual = GFS2_DIRENT_SIZE(be16_to_cpu(dent->de_name_len));
454 unsigned totlen = be16_to_cpu(dent->de_rec_len);
455
456 if (ptr < (void *)dent || ptr >= (void *)dent + totlen)
457 return 0;
458 if (gfs2_dirent_sentinel(dent))
459 actual = 0;
460 if (ptr < (void *)dent + actual)
461 return -1;
462 if ((void *)dent + totlen >= ptr + required)
463 return 1;
464 return -1;
465}
466
446static int gfs2_dirent_find_space(const struct gfs2_dirent *dent, 467static int gfs2_dirent_find_space(const struct gfs2_dirent *dent,
447 const struct qstr *name, 468 const struct qstr *name,
448 void *opaque) 469 void *opaque)
@@ -682,6 +703,27 @@ static void dirent_del(struct gfs2_inode *dip, struct buffer_head *bh,
682 prev->de_rec_len = cpu_to_be16(prev_rec_len); 703 prev->de_rec_len = cpu_to_be16(prev_rec_len);
683} 704}
684 705
706
707static struct gfs2_dirent *do_init_dirent(struct inode *inode,
708 struct gfs2_dirent *dent,
709 const struct qstr *name,
710 struct buffer_head *bh,
711 unsigned offset)
712{
713 struct gfs2_inode *ip = GFS2_I(inode);
714 struct gfs2_dirent *ndent;
715 unsigned totlen;
716
717 totlen = be16_to_cpu(dent->de_rec_len);
718 BUG_ON(offset + name->len > totlen);
719 gfs2_trans_add_meta(ip->i_gl, bh);
720 ndent = (struct gfs2_dirent *)((char *)dent + offset);
721 dent->de_rec_len = cpu_to_be16(offset);
722 gfs2_qstr2dirent(name, totlen - offset, ndent);
723 return ndent;
724}
725
726
685/* 727/*
686 * Takes a dent from which to grab space as an argument. Returns the 728 * Takes a dent from which to grab space as an argument. Returns the
687 * newly created dent. 729 * newly created dent.
@@ -691,31 +733,25 @@ static struct gfs2_dirent *gfs2_init_dirent(struct inode *inode,
691 const struct qstr *name, 733 const struct qstr *name,
692 struct buffer_head *bh) 734 struct buffer_head *bh)
693{ 735{
694 struct gfs2_inode *ip = GFS2_I(inode); 736 unsigned offset = 0;
695 struct gfs2_dirent *ndent;
696 unsigned offset = 0, totlen;
697 737
698 if (!gfs2_dirent_sentinel(dent)) 738 if (!gfs2_dirent_sentinel(dent))
699 offset = GFS2_DIRENT_SIZE(be16_to_cpu(dent->de_name_len)); 739 offset = GFS2_DIRENT_SIZE(be16_to_cpu(dent->de_name_len));
700 totlen = be16_to_cpu(dent->de_rec_len); 740 return do_init_dirent(inode, dent, name, bh, offset);
701 BUG_ON(offset + name->len > totlen);
702 gfs2_trans_add_meta(ip->i_gl, bh);
703 ndent = (struct gfs2_dirent *)((char *)dent + offset);
704 dent->de_rec_len = cpu_to_be16(offset);
705 gfs2_qstr2dirent(name, totlen - offset, ndent);
706 return ndent;
707} 741}
708 742
709static struct gfs2_dirent *gfs2_dirent_alloc(struct inode *inode, 743static struct gfs2_dirent *gfs2_dirent_split_alloc(struct inode *inode,
710 struct buffer_head *bh, 744 struct buffer_head *bh,
711 const struct qstr *name) 745 const struct qstr *name,
746 void *ptr)
712{ 747{
713 struct gfs2_dirent *dent; 748 struct gfs2_dirent *dent;
714 dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, 749 dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size,
715 gfs2_dirent_find_space, name, NULL); 750 gfs2_dirent_find_offset, name, ptr);
716 if (!dent || IS_ERR(dent)) 751 if (!dent || IS_ERR(dent))
717 return dent; 752 return dent;
718 return gfs2_init_dirent(inode, dent, name, bh); 753 return do_init_dirent(inode, dent, name, bh,
754 (unsigned)(ptr - (void *)dent));
719} 755}
720 756
721static int get_leaf(struct gfs2_inode *dip, u64 leaf_no, 757static int get_leaf(struct gfs2_inode *dip, u64 leaf_no,
@@ -1051,10 +1087,11 @@ static int dir_split_leaf(struct inode *inode, const struct qstr *name)
1051 if (!gfs2_dirent_sentinel(dent) && 1087 if (!gfs2_dirent_sentinel(dent) &&
1052 be32_to_cpu(dent->de_hash) < divider) { 1088 be32_to_cpu(dent->de_hash) < divider) {
1053 struct qstr str; 1089 struct qstr str;
1090 void *ptr = ((char *)dent - obh->b_data) + nbh->b_data;
1054 str.name = (char*)(dent+1); 1091 str.name = (char*)(dent+1);
1055 str.len = be16_to_cpu(dent->de_name_len); 1092 str.len = be16_to_cpu(dent->de_name_len);
1056 str.hash = be32_to_cpu(dent->de_hash); 1093 str.hash = be32_to_cpu(dent->de_hash);
1057 new = gfs2_dirent_alloc(inode, nbh, &str); 1094 new = gfs2_dirent_split_alloc(inode, nbh, &str, ptr);
1058 if (IS_ERR(new)) { 1095 if (IS_ERR(new)) {
1059 error = PTR_ERR(new); 1096 error = PTR_ERR(new);
1060 break; 1097 break;