Commit 34017472 authored by Benjamin Marzinski's avatar Benjamin Marzinski Committed by Bob Peterson

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: default avatarBenjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: default avatarBob Peterson <rpeterso@redhat.com>
parent 2aba1b5b
......@@ -443,6 +443,27 @@ static int gfs2_dirent_last(const struct gfs2_dirent *dent,
return 0;
}
/* Look for the dirent that contains the offset specified in data. Once we
* find that dirent, there must be space available there for the new dirent */
static int gfs2_dirent_find_offset(const struct gfs2_dirent *dent,
const struct qstr *name,
void *ptr)
{
unsigned required = GFS2_DIRENT_SIZE(name->len);
unsigned actual = GFS2_DIRENT_SIZE(be16_to_cpu(dent->de_name_len));
unsigned totlen = be16_to_cpu(dent->de_rec_len);
if (ptr < (void *)dent || ptr >= (void *)dent + totlen)
return 0;
if (gfs2_dirent_sentinel(dent))
actual = 0;
if (ptr < (void *)dent + actual)
return -1;
if ((void *)dent + totlen >= ptr + required)
return 1;
return -1;
}
static int gfs2_dirent_find_space(const struct gfs2_dirent *dent,
const struct qstr *name,
void *opaque)
......@@ -682,6 +703,27 @@ static void dirent_del(struct gfs2_inode *dip, struct buffer_head *bh,
prev->de_rec_len = cpu_to_be16(prev_rec_len);
}
static struct gfs2_dirent *do_init_dirent(struct inode *inode,
struct gfs2_dirent *dent,
const struct qstr *name,
struct buffer_head *bh,
unsigned offset)
{
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_dirent *ndent;
unsigned totlen;
totlen = be16_to_cpu(dent->de_rec_len);
BUG_ON(offset + name->len > totlen);
gfs2_trans_add_meta(ip->i_gl, bh);
ndent = (struct gfs2_dirent *)((char *)dent + offset);
dent->de_rec_len = cpu_to_be16(offset);
gfs2_qstr2dirent(name, totlen - offset, ndent);
return ndent;
}
/*
* Takes a dent from which to grab space as an argument. Returns the
* newly created dent.
......@@ -691,31 +733,25 @@ static struct gfs2_dirent *gfs2_init_dirent(struct inode *inode,
const struct qstr *name,
struct buffer_head *bh)
{
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_dirent *ndent;
unsigned offset = 0, totlen;
unsigned offset = 0;
if (!gfs2_dirent_sentinel(dent))
offset = GFS2_DIRENT_SIZE(be16_to_cpu(dent->de_name_len));
totlen = be16_to_cpu(dent->de_rec_len);
BUG_ON(offset + name->len > totlen);
gfs2_trans_add_meta(ip->i_gl, bh);
ndent = (struct gfs2_dirent *)((char *)dent + offset);
dent->de_rec_len = cpu_to_be16(offset);
gfs2_qstr2dirent(name, totlen - offset, ndent);
return ndent;
return do_init_dirent(inode, dent, name, bh, offset);
}
static struct gfs2_dirent *gfs2_dirent_alloc(struct inode *inode,
struct buffer_head *bh,
const struct qstr *name)
static struct gfs2_dirent *gfs2_dirent_split_alloc(struct inode *inode,
struct buffer_head *bh,
const struct qstr *name,
void *ptr)
{
struct gfs2_dirent *dent;
dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size,
gfs2_dirent_find_space, name, NULL);
gfs2_dirent_find_offset, name, ptr);
if (!dent || IS_ERR(dent))
return dent;
return gfs2_init_dirent(inode, dent, name, bh);
return do_init_dirent(inode, dent, name, bh,
(unsigned)(ptr - (void *)dent));
}
static 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)
if (!gfs2_dirent_sentinel(dent) &&
be32_to_cpu(dent->de_hash) < divider) {
struct qstr str;
void *ptr = ((char *)dent - obh->b_data) + nbh->b_data;
str.name = (char*)(dent+1);
str.len = be16_to_cpu(dent->de_name_len);
str.hash = be32_to_cpu(dent->de_hash);
new = gfs2_dirent_alloc(inode, nbh, &str);
new = gfs2_dirent_split_alloc(inode, nbh, &str, ptr);
if (IS_ERR(new)) {
error = PTR_ERR(new);
break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment