summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/gfs2/bmap.c1
-rw-r--r--fs/gfs2/dir.c412
-rw-r--r--fs/gfs2/dir.h15
-rw-r--r--fs/gfs2/incore.h3
-rw-r--r--fs/gfs2/inode.c14
-rw-r--r--fs/gfs2/log.c34
-rw-r--r--fs/gfs2/ops_dentry.c1
-rw-r--r--fs/gfs2/ops_export.c8
-rw-r--r--fs/gfs2/ops_file.c219
-rw-r--r--fs/gfs2/ops_fstype.c4
-rw-r--r--fs/gfs2/ops_inode.c13
-rw-r--r--fs/gfs2/recovery.c2
-rw-r--r--fs/gfs2/super.c1
-rw-r--r--fs/gfs2/util.c5
-rw-r--r--fs/gfs2/util.h2
15 files changed, 416 insertions, 318 deletions
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index cd5e4d863ce2..c7723119acb6 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -13,6 +13,7 @@
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
#include <asm/semaphore.h>
#include "gfs2.h"
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index f31f163da1a1..ba3438553f33 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -60,6 +60,7 @@
#include <linux/buffer_head.h>
#include <linux/sort.h>
#include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
#include <asm/semaphore.h>
#include "gfs2.h"
@@ -344,7 +345,8 @@ fail:
}
typedef int (*gfs2_dscan_t)(const struct gfs2_dirent *dent,
- const struct qstr *name);
+ const struct qstr *name,
+ void *opaque);
static inline int __gfs2_dirent_find(const struct gfs2_dirent *dent,
const struct qstr *name, int ret)
@@ -358,13 +360,15 @@ static inline int __gfs2_dirent_find(const struct gfs2_dirent *dent,
}
static int gfs2_dirent_find(const struct gfs2_dirent *dent,
- const struct qstr *name)
+ const struct qstr *name,
+ void *opaque)
{
return __gfs2_dirent_find(dent, name, 1);
}
static int gfs2_dirent_prev(const struct gfs2_dirent *dent,
- const struct qstr *name)
+ const struct qstr *name,
+ void *opaque)
{
return __gfs2_dirent_find(dent, name, 2);
}
@@ -374,7 +378,8 @@ static int gfs2_dirent_prev(const struct gfs2_dirent *dent,
* name->len holds size of block.
*/
static int gfs2_dirent_last(const struct gfs2_dirent *dent,
- const struct qstr *name)
+ const struct qstr *name,
+ void *opaque)
{
const char *start = name->name;
const char *end = (const char *)dent + be16_to_cpu(dent->de_rec_len);
@@ -384,17 +389,36 @@ static int gfs2_dirent_last(const struct gfs2_dirent *dent,
}
static int gfs2_dirent_find_space(const struct gfs2_dirent *dent,
- const struct qstr *name)
+ const struct qstr *name,
+ void *opaque)
{
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 (!dent->de_inum.no_addr)
+ actual = GFS2_DIRENT_SIZE(0);
if ((totlen - actual) >= required)
return 1;
return 0;
}
+struct dirent_gather {
+ const struct gfs2_dirent **pdent;
+ unsigned offset;
+};
+
+static int gfs2_dirent_gather(const struct gfs2_dirent *dent,
+ const struct qstr *name,
+ void *opaque)
+{
+ struct dirent_gather *g = opaque;
+ if (dent->de_inum.no_addr) {
+ g->pdent[g->offset++] = dent;
+ }
+ return 0;
+}
+
/*
* Other possible things to check:
* - Inode located within filesystem size (and on valid block)
@@ -431,19 +455,12 @@ error:
return -EIO;
}
-static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode,
- void *buf,
- unsigned int len, gfs2_dscan_t scan,
- const struct qstr *name)
+static int gfs2_dirent_offset(const void *buf)
{
- struct gfs2_meta_header *h = buf;
- struct gfs2_dirent *dent, *prev;
- unsigned offset;
- unsigned size;
- int ret = 0;
+ const struct gfs2_meta_header *h = buf;
+ int offset;
BUG_ON(buf == NULL);
- BUG_ON(name == NULL);
switch(be16_to_cpu(h->mh_type)) {
case GFS2_METATYPE_LF:
@@ -455,14 +472,36 @@ static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode,
default:
goto wrong_type;
}
+ return offset;
+wrong_type:
+ printk(KERN_WARNING "gfs2_scan_dirent: wrong block type %u\n",
+ be16_to_cpu(h->mh_type));
+ return -1;
+}
+static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode,
+ void *buf,
+ unsigned int len, gfs2_dscan_t scan,
+ const struct qstr *name,
+ void *opaque)
+{
+ struct gfs2_dirent *dent, *prev;
+ unsigned offset;
+ unsigned size;
+ int ret = 0;
+
+ ret = gfs2_dirent_offset(buf);
+ if (ret < 0)
+ goto consist_inode;
+
+ offset = ret;
prev = NULL;
dent = (struct gfs2_dirent *)(buf + offset);
size = be16_to_cpu(dent->de_rec_len);
if (gfs2_check_dirent(dent, offset, size, len, 1))
goto consist_inode;
do {
- ret = scan(dent, name);
+ ret = scan(dent, name, opaque);
if (ret)
break;
offset += size;
@@ -487,9 +526,6 @@ static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode,
return ERR_PTR(ret);
}
-wrong_type:
- printk(KERN_WARNING "gfs2_scan_dirent: %p wrong block type %u\n", scan,
- be16_to_cpu(h->mh_type));
consist_inode:
gfs2_consist_inode(inode->u.generic_ip);
return ERR_PTR(-EIO);
@@ -651,7 +687,8 @@ static struct gfs2_dirent *gfs2_dirent_alloc(struct inode *inode,
const struct qstr *name)
{
struct gfs2_dirent *dent;
- dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, gfs2_dirent_find_space, name);
+ dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size,
+ gfs2_dirent_find_space, name, NULL);
if (!dent || IS_ERR(dent))
return dent;
return gfs2_init_dirent(inode, dent, name, bh);
@@ -734,7 +771,7 @@ static struct gfs2_dirent *gfs2_dirent_search(struct inode *inode,
return ERR_PTR(error);
do {
dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size,
- scan, name);
+ scan, name, NULL);
if (dent)
goto got_dent;
leaf = (struct gfs2_leaf *)bh->b_data;
@@ -751,7 +788,7 @@ static struct gfs2_dirent *gfs2_dirent_search(struct inode *inode,
error = gfs2_meta_inode_buffer(ip, &bh);
if (error)
return ERR_PTR(error);
- dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, scan, name);
+ dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, scan, name, NULL);
got_dent:
*pbh = bh;
return dent;
@@ -764,6 +801,7 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh,
struct buffer_head *bh = gfs2_meta_new(ip->i_gl, bn);
struct gfs2_leaf *leaf;
struct gfs2_dirent *dent;
+ struct qstr name = { .name = "", .len = 0, .hash = 0 };
if (!bh)
return NULL;
gfs2_trans_add_bh(ip->i_gl, bh, 1);
@@ -775,12 +813,7 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh,
leaf->lf_next = cpu_to_be64(0);
memset(leaf->lf_reserved, 0, sizeof(leaf->lf_reserved));
dent = (struct gfs2_dirent *)(leaf+1);
- dent->de_inum.no_formal_ino = cpu_to_be64(0);
- dent->de_inum.no_addr = cpu_to_be64(0);
- dent->de_hash = cpu_to_be32(0);
- dent->de_rec_len = cpu_to_be16(bh->b_size - sizeof(struct gfs2_leaf));
- dent->de_name_len = cpu_to_be16(0);
- dent->de_type = cpu_to_be16(0);
+ gfs2_qstr2dirent(&name, bh->b_size - sizeof(struct gfs2_leaf), dent);
*pbh = bh;
return leaf;
}
@@ -831,7 +864,7 @@ static int dir_make_exhash(struct inode *inode)
sizeof(struct gfs2_leaf);
args.name = bh->b_data;
dent = gfs2_dirent_scan(dip->i_vnode, bh->b_data, bh->b_size,
- gfs2_dirent_last, &args);
+ gfs2_dirent_last, &args, NULL);
if (!dent) {
brelse(bh);
brelse(dibh);
@@ -939,7 +972,7 @@ static int dir_split_leaf(struct inode *inode, const struct qstr *name)
lp[x] = cpu_to_be64(bn);
error = gfs2_dir_write_data(dip, (char *)lp, start * sizeof(uint64_t),
- half_len * sizeof(uint64_t));
+ half_len * sizeof(uint64_t));
if (error != half_len * sizeof(uint64_t)) {
if (error >= 0)
error = -EIO;
@@ -965,7 +998,7 @@ static int dir_split_leaf(struct inode *inode, const struct qstr *name)
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(dip->i_vnode, nbh, &str);
+ new = gfs2_dirent_alloc(inode, nbh, &str);
if (IS_ERR(new)) {
error = PTR_ERR(new);
break;
@@ -1154,10 +1187,10 @@ static int compare_dents(const void *a, const void *b)
static int do_filldir_main(struct gfs2_inode *dip, uint64_t *offset,
void *opaque, gfs2_filldir_t filldir,
- struct gfs2_dirent **darr, uint32_t entries,
+ const struct gfs2_dirent **darr, uint32_t entries,
int *copied)
{
- struct gfs2_dirent *dent, *dent_next;
+ const struct gfs2_dirent *dent, *dent_next;
struct gfs2_inum inum;
uint64_t off, off_next;
unsigned int x, y;
@@ -1216,189 +1249,74 @@ static int do_filldir_main(struct gfs2_inode *dip, uint64_t *offset,
return 0;
}
-/**
- * do_filldir_single - Read directory entries out of a single block
- * @dip: The GFS2 inode
- * @offset: The offset in the file to read from
- * @opaque: opaque data to pass to filldir
- * @filldir: The function to pass entries to
- * @bh: the block
- * @entries: the number of entries in the block
- * @copied: pointer to int that's non-zero if a entry has been copied out
- *
- * Returns: errno, >0 on exception from filldir
- */
-
-static int do_filldir_single(struct gfs2_inode *dip, uint64_t *offset,
- void *opaque, gfs2_filldir_t filldir,
- struct buffer_head *bh, uint32_t entries,
- int *copied)
+static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque,
+ gfs2_filldir_t filldir, int *copied,
+ unsigned *depth, u64 leaf_no)
{
- struct gfs2_dirent **darr;
- struct gfs2_dirent *de;
- unsigned int e = 0;
- int error;
-
- if (!entries)
- return 0;
-
- darr = kcalloc(entries, sizeof(struct gfs2_dirent *), GFP_KERNEL);
- if (!darr)
- return -ENOMEM;
+ struct gfs2_inode *ip = inode->u.generic_ip;
+ struct buffer_head *bh;
+ struct gfs2_leaf *lf;
+ unsigned entries = 0;
+ unsigned leaves = 0;
+ const struct gfs2_dirent **darr, *dent;
+ struct dirent_gather g;
+ struct buffer_head **larr;
+ int leaf = 0;
+ int error, i;
+ u64 lfn = leaf_no;
- dirent_first(dip, bh, &de);
do {
- if (!de->de_inum.no_addr)
- continue;
- if (e >= entries) {
- gfs2_consist_inode(dip);
- error = -EIO;
- goto out;
- }
- darr[e++] = de;
- } while (dirent_next(dip, bh, &de) == 0);
-
- if (e != entries) {
- gfs2_consist_inode(dip);
- error = -EIO;
- goto out;
- }
-
- error = do_filldir_main(dip, offset, opaque, filldir, darr,
- entries, copied);
-
- out:
- kfree(darr);
-
- return error;
-}
-
-/**
- * do_filldir_multi - Read directory entries out of a linked leaf list
- * @dip: The GFS2 inode
- * @offset: The offset in the file to read from
- * @opaque: opaque data to pass to filldir
- * @filldir: The function to pass entries to
- * @bh: the first leaf in the list
- * @copied: pointer to int that's non-zero if a entry has been copied out
- *
- * Returns: errno, >0 on exception from filldir
- */
-
-static int do_filldir_multi(struct gfs2_inode *dip, uint64_t *offset,
- void *opaque, gfs2_filldir_t filldir,
- struct buffer_head *bh, int *copied)
-{
- struct buffer_head **larr = NULL;
- struct gfs2_dirent **darr;
- struct gfs2_leaf *leaf;
- struct buffer_head *tmp_bh;
- struct gfs2_dirent *de;
- unsigned int entries, e = 0;
- unsigned int leaves = 0, l = 0;
- unsigned int x;
- uint64_t ln;
- int error = 0;
-
- /* Count leaves and entries */
-
- leaf = (struct gfs2_leaf *)bh->b_data;
- entries = be16_to_cpu(leaf->lf_entries);
- ln = be64_to_cpu(leaf->lf_next);
-
- while (ln) {
- error = get_leaf(dip, ln, &tmp_bh);
+ error = get_leaf(ip, lfn, &bh);
if (error)
- return error;
-
- leaf = (struct gfs2_leaf *)tmp_bh->b_data;
- if (leaf->lf_entries) {
- entries += be16_to_cpu(leaf->lf_entries);
- leaves++;
- }
- ln = be64_to_cpu(leaf->lf_next);
-
- brelse(tmp_bh);
- }
+ goto out;
+ lf = (struct gfs2_leaf *)bh->b_data;
+ if (leaves == 0)
+ *depth = be16_to_cpu(lf->lf_depth);
+ entries += be16_to_cpu(lf->lf_entries);
+ leaves++;
+ lfn = be64_to_cpu(lf->lf_next);
+ brelse(bh);
+ } while(lfn);
if (!entries)
return 0;
- if (leaves) {
- larr = kcalloc(leaves, sizeof(struct buffer_head *),GFP_KERNEL);
- if (!larr)
- return -ENOMEM;
- }
-
- darr = kcalloc(entries, sizeof(struct gfs2_dirent *), GFP_KERNEL);
- if (!darr) {
- kfree(larr);
- return -ENOMEM;
- }
-
- leaf = (struct gfs2_leaf *)bh->b_data;
- if (leaf->lf_entries) {
- dirent_first(dip, bh, &de);
- do {
- if (!de->de_inum.no_addr)
- continue;
- if (e >= entries) {
- gfs2_consist_inode(dip);
- error = -EIO;
- goto out;
- }
- darr[e++] = de;
- } while (dirent_next(dip, bh, &de) == 0);
- }
- ln = be64_to_cpu(leaf->lf_next);
+ error = -ENOMEM;
+ larr = kmalloc((leaves + entries) * sizeof(void*), GFP_KERNEL);
+ if (!larr)
+ goto out;
+ darr = (const struct gfs2_dirent **)(larr + leaves);
+ g.pdent = darr;
+ g.offset = 0;
+ lfn = leaf_no;
- while (ln) {
- error = get_leaf(dip, ln, &tmp_bh);
+ do {
+ error = get_leaf(ip, lfn, &bh);
if (error)
- goto out;
-
- leaf = (struct gfs2_leaf *)tmp_bh->b_data;
- if (leaf->lf_entries) {
- dirent_first(dip, tmp_bh, &de);
- do {
- if (!de->de_inum.no_addr)
- continue;
- if (e >= entries) {
- gfs2_consist_inode(dip);
- error = -EIO;
- goto out;
- }
- darr[e++] = de;
- } while (dirent_next(dip, tmp_bh, &de) == 0);
-
- larr[l++] = tmp_bh;
-
- ln = be64_to_cpu(leaf->lf_next);
+ goto out_kfree;
+ lf = (struct gfs2_leaf *)bh->b_data;
+ lfn = be64_to_cpu(lf->lf_next);
+ if (lf->lf_entries) {
+ dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size,
+ gfs2_dirent_gather, NULL, &g);
+ error = PTR_ERR(dent);
+ if (IS_ERR(dent)) {
+ goto out_kfree;
+ }
+ error = 0;
+ larr[leaf++] = bh;
} else {
- ln = be64_to_cpu(leaf->lf_next);
- brelse(tmp_bh);
+ brelse(bh);
}
- }
+ } while(lfn);
- if (gfs2_assert_withdraw(dip->i_sbd, l == leaves)) {
- error = -EIO;
- goto out;
- }
- if (e != entries) {
- gfs2_consist_inode(dip);
- error = -EIO;
- goto out;
- }
-
- error = do_filldir_main(dip, offset, opaque, filldir, darr,
+ error = do_filldir_main(ip, offset, opaque, filldir, darr,
entries, copied);
-
- out:
- kfree(darr);
- for (x = 0; x < l; x++)
- brelse(larr[x]);
+out_kfree:
+ for(i = 0; i < leaf; i++)
+ brelse(larr[i]);
kfree(larr);
-
+out:
return error;
}
@@ -1412,18 +1330,18 @@ static int do_filldir_multi(struct gfs2_inode *dip, uint64_t *offset,
* Returns: errno
*/
-static int dir_e_read(struct gfs2_inode *dip, uint64_t *offset, void *opaque,
+static int dir_e_read(struct inode *inode, uint64_t *offset, void *opaque,
gfs2_filldir_t filldir)
{
+ struct gfs2_inode *dip = inode->u.generic_ip;
struct gfs2_sbd *sdp = dip->i_sbd;
- struct buffer_head *bh;
- struct gfs2_leaf *leaf;
- uint32_t hsize, len;
+ uint32_t hsize, len = 0;
uint32_t ht_offset, lp_offset, ht_offset_cur = -1;
uint32_t hash, index;
uint64_t *lp;
int copied = 0;
int error = 0;
+ unsigned depth;
hsize = 1 << dip->i_di.di_depth;
if (hsize * sizeof(uint64_t) != dip->i_di.di_size) {
@@ -1454,61 +1372,66 @@ static int dir_e_read(struct gfs2_inode *dip, uint64_t *offset, void *opaque,
ht_offset_cur = ht_offset;
}
- error = get_leaf(dip, be64_to_cpu(lp[lp_offset]), &bh);
+ error = gfs2_dir_read_leaf(inode, offset, opaque, filldir,
+ &copied, &depth,
+ be64_to_cpu(lp[lp_offset]));
if (error)
- goto out;
-
- leaf = (struct gfs2_leaf *)bh->b_data;
- if (leaf->lf_next)
- error = do_filldir_multi(dip, offset, opaque, filldir,
- bh, &copied);
- else
- error = do_filldir_single(dip, offset, opaque, filldir,
- bh,
- be16_to_cpu(leaf->lf_entries),
- &copied);
-
- brelse(bh);
-
- if (error) {
- if (error > 0)
- error = 0;
- goto out;
- }
+ break;
- len = 1 << (dip->i_di.di_depth - be16_to_cpu(leaf->lf_depth));
+ len = 1 << (dip->i_di.di_depth - depth);
index = (index & ~(len - 1)) + len;
}
- out:
+out:
kfree(lp);
-
+ if (error > 0)
+ error = 0;
return error;
}
-static int dir_l_read(struct gfs2_inode *dip, uint64_t *offset, void *opaque,
- gfs2_filldir_t filldir)
+int gfs2_dir_read(struct inode *inode, uint64_t *offset, void *opaque,
+ gfs2_filldir_t filldir)
{
+ struct gfs2_inode *dip = inode->u.generic_ip;
+ struct dirent_gather g;
+ const struct gfs2_dirent **darr, *dent;
struct buffer_head *dibh;
int copied = 0;
int error;
+ if (!dip->i_di.di_entries)
+ return 0;
+
+ if (dip->i_di.di_flags & GFS2_DIF_EXHASH)
+ return dir_e_read(inode, offset, opaque, filldir);
+
if (!gfs2_is_stuffed(dip)) {
gfs2_consist_inode(dip);
return -EIO;
}
- if (!dip->i_di.di_entries)
- return 0;
-
error = gfs2_meta_inode_buffer(dip, &dibh);
if (error)
return error;
- error = do_filldir_single(dip, offset,
- opaque, filldir,
- dibh, dip->i_di.di_entries,
- &copied);
+ error = -ENOMEM;
+ darr = kmalloc(dip->i_di.di_entries * sizeof(struct gfs2_dirent *),
+ GFP_KERNEL);
+ if (darr) {
+ g.pdent = darr;
+ g.offset = 0;
+ dent = gfs2_dirent_scan(inode, dibh->b_data, dibh->b_size,
+ gfs2_dirent_gather, NULL, &g);
+ if (IS_ERR(dent)) {
+ error = PTR_ERR(dent);
+ goto out;
+ }
+ error = do_filldir_main(dip, offset, opaque, filldir, darr,
+ dip->i_di.di_entries, &copied);
+out:
+ kfree(darr);
+ }
+
if (error > 0)
error = 0;
@@ -1694,7 +1617,7 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name)
return PTR_ERR(dent);
}
/* If not first in block, adjust pointers accordingly */
- if (gfs2_dirent_find(dent, name) == 0) {
+ if (gfs2_dirent_find(dent, name, NULL) == 0) {
prev = dent;
dent = (struct gfs2_dirent *)((char *)dent + be16_to_cpu(prev->de_rec_len));
}
@@ -1724,19 +1647,6 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name)
return error;
}
-int gfs2_dir_read(struct gfs2_inode *dip, uint64_t *offset, void *opaque,
- gfs2_filldir_t filldir)
-{
- int error;
-
- if (dip->i_di.di_flags & GFS2_DIF_EXHASH)
- error = dir_e_read(dip, offset, opaque, filldir);
- else
- error = dir_l_read(dip, offset, opaque, filldir);
-
- return error;
-}
-
/**
* gfs2_dir_mvino - Change inode number of directory entry
* @dip: The GFS2 inode
diff --git a/fs/gfs2/dir.h b/fs/gfs2/dir.h
index 8fd4dc0f700e..42b3a1f34deb 100644
--- a/fs/gfs2/dir.h
+++ b/fs/gfs2/dir.h
@@ -32,7 +32,7 @@ int gfs2_dir_search(struct inode *dir, const struct qstr *filename,
int gfs2_dir_add(struct inode *inode, const struct qstr *filename,
const struct gfs2_inum *inum, unsigned int type);
int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *filename);
-int gfs2_dir_read(struct gfs2_inode *dip, uint64_t * offset, void *opaque,
+int gfs2_dir_read(struct inode *inode, uint64_t * offset, void *opaque,
gfs2_filldir_t filldir);
int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename,
struct gfs2_inum *new_inum, unsigned int new_type);
@@ -44,6 +44,19 @@ int gfs2_diradd_alloc_required(struct inode *dir,
int gfs2_dir_get_buffer(struct gfs2_inode *ip, uint64_t block, int new,
struct buffer_head **bhp);
+static inline uint32_t gfs2_disk_hash(const char *data, int len)
+{
+ return crc32_le(0xFFFFFFFF, data, len) ^ 0xFFFFFFFF;
+}
+
+
+static inline void gfs2_str2qstr(struct qstr *name, const char *fname)
+{
+ name->name = fname;
+ name->len = strlen(fname);
+ name->hash = gfs2_disk_hash(name->name, name->len);
+}
+
/* N.B. This probably ought to take inum & type as args as well */
static inline void gfs2_qstr2dirent(const struct qstr *name, u16 reclen, struct gfs2_dirent *dent)
{
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 62f109e553c4..be307185f492 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -634,8 +634,7 @@ struct gfs2_sbd {
struct list_head sd_log_le_databuf;
unsigned int sd_log_blks_free;
- struct list_head sd_log_blks_list;
- wait_queue_head_t sd_log_blks_wait;
+ struct mutex sd_log_reserve_mutex;
uint64_t sd_log_sequence;
unsigned int sd_log_head;
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index d403d51d5b0f..6140c2434e85 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -15,6 +15,7 @@
#include <linux/posix_acl.h>
#include <linux/sort.h>
#include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
#include <asm/semaphore.h>
#include "gfs2.h"
@@ -701,9 +702,7 @@ int gfs2_change_nlink(struct gfs2_inode *ip, int diff)
struct inode *gfs2_lookup_simple(struct inode *dip, const char *name)
{
struct qstr qstr;
- qstr.name = name;
- qstr.len = strlen(name);
- qstr.hash = gfs2_disk_hash(qstr.name, qstr.len);
+ gfs2_str2qstr(&qstr, name);
return gfs2_lookupi(dip, &qstr, 1, NULL);
}
@@ -1389,9 +1388,7 @@ int gfs2_rmdiri(struct gfs2_inode *dip, struct qstr *name,
if (error)
return error;
- dotname.len = 1;
- dotname.name = ".";
- dotname.hash = gfs2_disk_hash(dotname.name, dotname.len);
+ gfs2_str2qstr(&dotname, ".");
error = gfs2_dir_del(ip, &dotname);
if (error)
return error;
@@ -1487,10 +1484,7 @@ int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to)
struct qstr dotdot;
int error = 0;
- memset(&dotdot, 0, sizeof(struct qstr));
- dotdot.name = "..";
- dotdot.len = 2;
- dotdot.hash = gfs2_disk_hash(dotdot.name, dotdot.len);
+ gfs2_str2qstr(&dotdot, "..");
igrab(dir);
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index e6a84f7a9b71..16c14441a371 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -13,6 +13,7 @@
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
#include <asm/semaphore.h>
#include "gfs2.h"
@@ -24,18 +25,13 @@
#include "lops.h"
#include "meta_io.h"
#include "util.h"
+#include "dir.h"
#define PULL 1
-static void do_lock_wait(struct gfs2_sbd *sdp, wait_queue_head_t *wq,
- atomic_t *a)
-{
- wait_event(*wq, atomic_read(a) ? 0 : 1);
-}
-
static void lock_for_trans(struct gfs2_sbd *sdp)
{
- do_lock_wait(sdp, &sdp->sd_log_trans_wq, &sdp->sd_log_flush_count);
+ wait_event(sdp->sd_log_trans_wq, atomic_read(&sdp->sd_log_flush_count) ? 0 : 1);
atomic_inc(&sdp->sd_log_trans_count);
}
@@ -49,7 +45,7 @@ static void unlock_from_trans(struct gfs2_sbd *sdp)
static void gfs2_lock_for_flush(struct gfs2_sbd *sdp)
{
atomic_inc(&sdp->sd_log_flush_count);
- do_lock_wait(sdp, &sdp->sd_log_flush_wq, &sdp->sd_log_trans_count);
+ wait_event(sdp->sd_log_flush_wq, atomic_read(&sdp->sd_log_trans_count) ? 0 : 1);
}
static void gfs2_unlock_from_flush(struct gfs2_sbd *sdp)
@@ -191,37 +187,19 @@ static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail)
int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks)
{
- LIST_HEAD(list);
unsigned int try = 0;
if (gfs2_assert_warn(sdp, blks) ||
gfs2_assert_warn(sdp, blks <= sdp->sd_jdesc->jd_blocks))
return -EINVAL;
+ mutex_lock(&sdp->sd_log_reserve_mutex);
for (;;) {
gfs2_log_lock(sdp);
- if (list_empty(&list)) {
- list_add_tail(&list, &sdp->sd_log_blks_list);
- while (sdp->sd_log_blks_list.next != &list) {
- DECLARE_WAITQUEUE(__wait_chan, current);
- set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(&sdp->sd_log_blks_wait,
- &__wait_chan);
- gfs2_log_unlock(sdp);
- schedule();
- gfs2_log_lock(sdp);
- remove_wait_queue(&sdp->sd_log_blks_wait,
- &__wait_chan);
- set_current_state(TASK_RUNNING);
- }
- }
- /* Never give away the last block so we can
- always pull the tail if we need to. */
if (sdp->sd_log_blks_free > blks) {
sdp->sd_log_blks_free -= blks;
- list_del(&list);
gfs2_log_unlock(sdp);
- wake_up(&sdp->sd_log_blks_wait);
+ mutex_unlock(&sdp->sd_log_reserve_mutex);
break;
}
diff --git a/fs/gfs2/ops_dentry.c b/fs/gfs2/ops_dentry.c
index b54608f9df50..958371076093 100644
--- a/fs/gfs2/ops_dentry.c
+++ b/fs/gfs2/ops_dentry.c
@@ -14,6 +14,7 @@
#include <linux/buffer_head.h>
#include <linux/smp_lock.h>
#include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
#include <asm/semaphore.h>
#include "gfs2.h"
diff --git a/fs/gfs2/ops_export.c b/fs/gfs2/ops_export.c
index b27bce74a795..be16c68263d1 100644
--- a/fs/gfs2/ops_export.c
+++ b/fs/gfs2/ops_export.c
@@ -13,6 +13,7 @@
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
#include <asm/semaphore.h>
#include "gfs2.h"
@@ -153,7 +154,7 @@ static int gfs2_get_name(struct dentry *parent, char *name,
if (error)
return error;
- error = gfs2_dir_read(dip, &offset, &gnfd, get_name_filldir);
+ error = gfs2_dir_read(dir, &offset, &gnfd, get_name_filldir);
gfs2_glock_dq_uninit(&gh);
@@ -165,12 +166,11 @@ static int gfs2_get_name(struct dentry *parent, char *name,
static struct dentry *gfs2_get_parent(struct dentry *child)
{
- struct qstr dotdot = { .name = "..", .len = 2 };
+ struct qstr dotdot;
struct inode *inode;
struct dentry *dentry;
- dotdot.hash = gfs2_disk_hash(dotdot.name, dotdot.len);
-
+ gfs2_str2qstr(&dotdot, "..");
inode = gfs2_lookupi(child->d_inode, &dotdot, 1, NULL);
if (!inode)
diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c
index 6333a14cf77a..ac8e1238cb6f 100644
--- a/fs/gfs2/ops_file.c
+++ b/fs/gfs2/ops_file.c
@@ -19,6 +19,8 @@
#include <linux/smp_lock.h>
#include <linux/fs.h>
#include <linux/gfs2_ondisk.h>
+#include <linux/ext2_fs.h>
+#include <linux/crc32.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
@@ -39,6 +41,7 @@
#include "rgrp.h"
#include "trans.h"
#include "util.h"
+#include "eaops.h"
/* "bad" is for NFS support */
struct filldir_bad_entry {
@@ -357,7 +360,8 @@ static int filldir_reg_func(void *opaque, const char *name, unsigned int length,
static int readdir_reg(struct file *file, void *dirent, filldir_t filldir)
{
- struct gfs2_inode *dip = file->f_mapping->host->u.generic_ip;
+ struct inode *dir = file->f_mapping->host;
+ struct gfs2_inode *dip = dir->u.generic_ip;
struct filldir_reg fdr;
struct gfs2_holder d_gh;
uint64_t offset = file->f_pos;
@@ -375,7 +379,7 @@ static int readdir_reg(struct file *file, void *dirent, filldir_t filldir)
return error;
}
- error = gfs2_dir_read(dip, &offset, &fdr, filldir_reg_func);
+ error = gfs2_dir_read(dir, &offset, &fdr, filldir_reg_func);
gfs2_glock_dq_uninit(&d_gh);
@@ -446,7 +450,8 @@ static int filldir_bad_func(void *opaque, const char *name, unsigned int length,
static int readdir_bad(struct file *file, void *dirent, filldir_t filldir)
{
- struct gfs2_inode *dip = file->f_mapping->host->u.generic_ip;
+ struct inode *dir = file->f_mapping->host;
+ struct gfs2_inode *dip = dir->u.generic_ip;
struct gfs2_sbd *sdp = dip->i_sbd;
struct filldir_reg fdr;
unsigned int entries, size;
@@ -479,7 +484,7 @@ static int readdir_bad(struct file *file, void *dirent, filldir_t filldir)
goto out;
}
- error = gfs2_dir_read(dip, &offset, fdb, filldir_bad_func);
+ error = gfs2_dir_read(dir, &offset, fdb, filldir_bad_func);
gfs2_glock_dq_uninit(&d_gh);
@@ -531,6 +536,210 @@ static int gfs2_readdir(struct file *file, void *dirent, filldir_t filldir)
return error;
}
+const struct gfs2_flag_eattr {
+ u32 flag;
+ u32 ext2;
+} gfs2_flag_eattrs[] = {
+ {
+ .flag = GFS2_DIF_IMMUTABLE,
+ .ext2 = EXT2_IMMUTABLE_FL,
+ }, {
+ .flag = GFS2_DIF_APPENDONLY,
+ .ext2 = EXT2_APPEND_FL,
+ }, {
+ .flag = GFS2_DIF_JDATA,
+ .ext2 = EXT2_JOURNAL_DATA_FL,
+ }, {
+ .flag = GFS2_DIF_EXHASH,
+ .ext2 = EXT2_INDEX_FL,
+ }, {
+ .flag = GFS2_DIF_EA_INDIRECT,
+ }, {
+ .flag = GFS2_DIF_DIRECTIO,
+ }, {
+ .flag = GFS2_DIF_NOATIME,
+ .ext2 = EXT2_NOATIME_FL,
+ }, {
+ .flag = GFS2_DIF_SYNC,
+ .ext2 = EXT2_SYNC_FL,
+ }, {
+ .flag = GFS2_DIF_SYSTEM,
+ }, {
+ .flag = GFS2_DIF_TRUNC_IN_PROG,
+ }, {
+ .flag = GFS2_DIF_INHERIT_JDATA,
+ }, {
+ .flag = GFS2_DIF_INHERIT_DIRECTIO,
+ }, {
+ },
+};
+
+static const struct gfs2_flag_eattr *get_by_ext2(u32 ext2)
+{
+ const struct gfs2_flag_eattr *p = gfs2_flag_eattrs;
+ for(; p->flag; p++) {
+ if (ext2 == p->ext2)
+ return p;
+ }
+ return NULL;
+}
+
+static const struct gfs2_flag_eattr *get_by_gfs2(u32 gfs2)
+{
+ const struct gfs2_flag_eattr *p = gfs2_flag_eattrs;
+ for(; p->flag; p++) {
+ if (gfs2 == p->flag)
+ return p;
+ }
+ return NULL;
+}
+
+static u32 gfs2_flags_to_ext2(u32 gfs2)
+{
+ const struct gfs2_flag_eattr *ea;
+ u32 ext2 = 0;
+ u32 mask = 1;
+
+ for(; mask != 0; mask <<=1) {
+ if (mask & gfs2) {
+ ea = get_by_gfs2(mask);
+ if (ea)
+ ext2 |= ea->ext2;
+ }
+ }
+ return ext2;
+}
+
+static int gfs2_flags_from_ext2(u32 *gfs2, u32 ext2)
+{
+ const struct gfs2_flag_eattr *ea;
+ u32 mask = 1;
+
+ for(; mask != 0; mask <<= 1) {
+ if (mask & ext2) {
+ ea = get_by_ext2(mask);
+ if (ea == NULL)
+ return -EINVAL;
+ *gfs2 |= ea->flag;
+ }
+ }
+ return 0;
+}
+
+static int get_ext2_flags(struct inode *inode, u32 __user *ptr)
+{
+ struct gfs2_inode *ip = inode->u.generic_ip;
+ struct gfs2_holder gh;
+ int error;
+ u32 ext2;
+
+ gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME, &gh);
+ error = gfs2_glock_nq_m_atime(1, &gh);
+ if (error)
+ return error;
+
+ ext2 = gfs2_flags_to_ext2(ip->i_di.di_flags);
+ if (put_user(ext2, ptr))
+ error = -EFAULT;
+
+ gfs2_glock_dq_m(1, &gh);
+ gfs2_holder_uninit(&gh);
+ return error;
+}
+
+/* Flags that can be set by user space */
+#define GFS2_FLAGS_USER_SET (GFS2_DIF_JDATA| \
+ GFS2_DIF_DIRECTIO| \
+ GFS2_DIF_IMMUTABLE| \
+ GFS2_DIF_APPENDONLY| \
+ GFS2_DIF_NOATIME| \
+ GFS2_DIF_SYNC| \
+ GFS2_DIF_SYSTEM| \
+ GFS2_DIF_INHERIT_DIRECTIO| \
+ GFS2_DIF_INHERIT_JDATA)
+
+/**
+ * gfs2_set_flags - set flags on an inode
+ * @inode: The inode
+ * @flags: The flags to set
+ * @mask: Indicates which flags are valid
+ *
+ */
+static int gfs2_set_flags(struct inode *inode, u32 flags, u32 mask)
+{
+ struct gfs2_inode *ip = inode->u.generic_ip;
+ struct buffer_head *bh;
+ struct gfs2_holder gh;
+ int error;
+ u32 new_flags;
+
+ gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
+ error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
+ if (error)
+ return error;
+
+ new_flags = (ip->i_di.di_flags & ~mask) | (flags & mask);
+ if ((new_flags ^ flags) == 0)
+ goto out;
+
+ error = -EINVAL;
+ if ((new_flags ^ flags) & ~GFS2_FLAGS_USER_SET)
+ goto out;
+
+ if (S_ISDIR(inode->i_mode)) {
+ if ((new_flags ^ flags) & (GFS2_DIF_JDATA | GFS2_DIF_DIRECTIO))
+ goto out;
+ } else if (S_ISREG(inode->i_mode)) {
+ if ((new_flags ^ flags) & (GFS2_DIF_INHERIT_DIRECTIO|
+ GFS2_DIF_INHERIT_JDATA))
+ goto out;
+ } else
+ goto out;
+
+ error = -EPERM;
+ if (IS_IMMUTABLE(inode) && (new_flags & GFS2_DIF_IMMUTABLE))
+ goto out;
+ if (IS_APPEND(inode) && (new_flags & GFS2_DIF_APPENDONLY))
+ goto out;
+ error = gfs2_repermission(inode, MAY_WRITE, NULL);
+ if (error)
+ goto out;
+
+ error = gfs2_meta_inode_buffer(ip, &bh);
+ if (error)
+ goto out;
+ gfs2_trans_add_bh(ip->i_gl, bh, 1);
+ ip->i_di.di_flags = new_flags;
+ gfs2_dinode_out(&ip->i_di, bh->b_data);
+ brelse(bh);
+out:
+ gfs2_glock_dq_uninit(&gh);
+ return error;
+}
+
+static int set_ext2_flags(struct inode *inode, u32 __user *ptr)
+{
+ u32 ext2, gfs2;
+ if (get_user(ext2, ptr))
+ return -EFAULT;
+ if (gfs2_flags_from_ext2(&gfs2, ext2))
+ return -EINVAL;
+ return gfs2_set_flags(inode, gfs2, ~0);
+}
+
+int gfs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ switch(cmd) {
+ case EXT2_IOC_GETFLAGS:
+ return get_ext2_flags(inode, (u32 __user *)arg);
+ case EXT2_IOC_SETFLAGS:
+ return set_ext2_flags(inode, (u32 __user *)arg);
+ }
+ return -ENOTTY;
+}
+
+
/**
* gfs2_mmap -
* @file: The file to map
@@ -832,6 +1041,7 @@ struct file_operations gfs2_file_fops = {
.write = generic_file_write,
.writev = generic_file_writev,
.aio_write = generic_file_aio_write,
+ .ioctl = gfs2_ioctl,
.mmap = gfs2_mmap,
.open = gfs2_open,
.release = gfs2_close,
@@ -843,6 +1053,7 @@ struct file_operations gfs2_file_fops = {
struct file_operations gfs2_dir_fops = {
.readdir = gfs2_readdir,
+ .ioctl = gfs2_ioctl,
.open = gfs2_open,
.release = gfs2_close,
.fsync = gfs2_fsync,
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index 8d2c557b3ff4..2628bf326334 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -97,9 +97,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
INIT_LIST_HEAD(&sdp->sd_log_le_rg);
INIT_LIST_HEAD(&sdp->sd_log_le_databuf);
- INIT_LIST_HEAD(&sdp->sd_log_blks_list);
- init_waitqueue_head(&sdp->sd_log_blks_wait);
-
+ mutex_init(&sdp->sd_log_reserve_mutex);
INIT_LIST_HEAD(&sdp->sd_ail1_list);
INIT_LIST_HEAD(&sdp->sd_ail2_list);
diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c
index e8ab9d254b76..1e2b709711ae 100644
--- a/fs/gfs2/ops_inode.c
+++ b/fs/gfs2/ops_inode.c
@@ -18,6 +18,7 @@
#include <linux/xattr.h>
#include <linux/posix_acl.h>
#include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
@@ -417,18 +418,16 @@ static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, int mode)
if (!gfs2_assert_withdraw(sdp, !error)) {
struct gfs2_dinode *di = (struct gfs2_dinode *)dibh->b_data;
struct gfs2_dirent *dent = (struct gfs2_dirent *)(di+1);
- struct qstr str = { .name = ".", .len = 1 };
- str.hash = gfs2_disk_hash(str.name, str.len);
+ struct qstr str;
+ gfs2_str2qstr(&str, ".");
gfs2_trans_add_bh(ip->i_gl, dibh, 1);
gfs2_qstr2dirent(&str, GFS2_DIRENT_SIZE(str.len), dent);
dent->de_inum = di->di_num; /* already GFS2 endian */
dent->de_type = DT_DIR;
di->di_entries = cpu_to_be32(1);
- str.name = "..";
- str.len = 2;
- str.hash = gfs2_disk_hash(str.name, str.len);
+ gfs2_str2qstr(&str, "..");
dent = (struct gfs2_dirent *)((char*)dent + GFS2_DIRENT_SIZE(1));
gfs2_qstr2dirent(&str, dibh->b_size - GFS2_DIRENT_SIZE(1) - sizeof(struct gfs2_dinode), dent);
@@ -772,9 +771,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
if (dir_rename) {
struct qstr name;
- name.len = 2;
- name.name = "..";
- name.hash = gfs2_disk_hash(name.name, name.len);
+ gfs2_str2qstr(&name, "..");
error = gfs2_change_nlink(ndip, +1);
if (error)
diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c
index 2df450e2f433..6c7e2e880e32 100644
--- a/fs/gfs2/recovery.c
+++ b/fs/gfs2/recovery.c
@@ -13,6 +13,7 @@
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
#include <asm/semaphore.h>
#include "gfs2.h"
@@ -27,6 +28,7 @@
#include "recovery.h"
#include "super.h"
#include "util.h"
+#include "dir.h"
int gfs2_replay_read_block(struct gfs2_jdesc *jd, unsigned int blk,
struct buffer_head **bh)
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index 71cca7629403..a4da649d086f 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -12,6 +12,7 @@
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
+#include <linux/crc32.h>
#include <linux/gfs2_ondisk.h>
#include <asm/semaphore.h>
diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c
index 8b22fa91bd14..7cd9e25639c4 100644
--- a/fs/gfs2/util.c
+++ b/fs/gfs2/util.c
@@ -28,11 +28,6 @@ kmem_cache_t *gfs2_glock_cachep __read_mostly;
kmem_cache_t *gfs2_inode_cachep __read_mostly;
kmem_cache_t *gfs2_bufdata_cachep __read_mostly;
-uint32_t gfs2_disk_hash(const char *data, int len)
-{
- return crc32_le(0xFFFFFFFF, data, len) ^ 0xFFFFFFFF;
-}
-
void gfs2_assert_i(struct gfs2_sbd *sdp)
{
printk(KERN_EMERG "GFS2: fsid=%s: fatal assertion failed\n",
diff --git a/fs/gfs2/util.h b/fs/gfs2/util.h
index 8d6eba3bdf0a..addbe304993e 100644
--- a/fs/gfs2/util.h
+++ b/fs/gfs2/util.h
@@ -10,8 +10,6 @@
#ifndef __UTIL_DOT_H__
#define __UTIL_DOT_H__
-uint32_t gfs2_disk_hash(const char *data, int len);
-
#define fs_printk(level, fs, fmt, arg...) \
printk(level "GFS2: fsid=%s: " fmt , (fs)->sd_fsname , ## arg)