#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"
}
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)
}
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);
}
* 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);
}
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)
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:
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;
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);
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);
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;
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;
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);
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;
}
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);
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;
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;
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;
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;
}
* 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) {
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;
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));
}
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
#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>
#include "rgrp.h"
#include "trans.h"
#include "util.h"
+#include "eaops.h"
/* "bad" is for NFS support */
struct filldir_bad_entry {
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;
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);
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;
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);
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
.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,
struct file_operations gfs2_dir_fops = {
.readdir = gfs2_readdir,
+ .ioctl = gfs2_ioctl,
.open = gfs2_open,
.release = gfs2_close,
.fsync = gfs2_fsync,