From d192a8e5c6fec4fe8cdafebccc415db4074dee88 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Thu, 5 May 2011 12:35:40 +0100 Subject: GFS2: Double check link count under glock To avoid any possible races relating to the link count, we need to recheck it under the inode's glock in all cases where it matters. Also to ensure we never get any nasty surprises, this patch also ensures that once the link count has hit zero it can never be elevated by rereading in data from disk. The only place we cannot provide a proper solution is in rename in the case where we are removing a target inode and we discover that the target inode has been already unlinked on another node. The race window is very small, and we return EAGAIN in this case to indicate what has happened. The proper solution would be to move the lookup parts of rename from the vfs into library calls which the fs could call directly, but that is potentially a very big job and this fix should cover most cases for now. Signed-off-by: Steven Whitehouse --- fs/gfs2/ops_inode.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'fs/gfs2/ops_inode.c') diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 09e436a50723..1005f9eb456e 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -162,6 +162,10 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, if (error) goto out_child; + error = -ENOENT; + if (inode->i_nlink == 0) + goto out_gunlock; + error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC, 0); if (error) goto out_gunlock; @@ -335,6 +339,10 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) if (error) goto out_child; + error = -ENOENT; + if (ip->i_inode.i_nlink == 0) + goto out_rgrp; + error = gfs2_glock_nq(ghs + 2); /* rgrp */ if (error) goto out_rgrp; @@ -589,6 +597,10 @@ static int gfs2_rmdir(struct inode *dir, struct dentry *dentry) if (error) goto out_child; + error = -ENOENT; + if (ip->i_inode.i_nlink == 0) + goto out_rgrp; + error = gfs2_glock_nq(ghs + 2); /* rgrp */ if (error) goto out_rgrp; @@ -792,6 +804,10 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, goto out_gunlock; } + error = -ENOENT; + if (ip->i_inode.i_nlink == 0) + goto out_gunlock; + /* Check out the old directory */ error = gfs2_unlink_ok(odip, &odentry->d_name, ip); @@ -805,6 +821,11 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (error) goto out_gunlock; + if (nip->i_inode.i_nlink == 0) { + error = -EAGAIN; + goto out_gunlock; + } + if (S_ISDIR(nip->i_inode.i_mode)) { if (nip->i_entries < 2) { if (gfs2_consist_inode(nip)) @@ -835,7 +856,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (odip != ndip) { if (!ndip->i_inode.i_nlink) { - error = -EINVAL; + error = -ENOENT; goto out_gunlock; } if (ndip->i_entries == (u32)-1) { -- cgit v1.2.3 From 2baee03fb916563d7cc597e5460e4cb938815c52 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 9 May 2011 12:08:36 +0100 Subject: GFS2: Don't use gfs2_change_nlink in link syscall There are three users of gfs2_change_nlink which add to the link count. Two of these are about to be removed in later patches, so this means that there will no callers, when that happens allowing removal of that function, also in a later patch. Signed-off-by: Steven Whitehouse --- fs/gfs2/ops_inode.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'fs/gfs2/ops_inode.c') diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 1005f9eb456e..acb6f69b02ed 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -145,6 +145,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, struct inode *inode = old_dentry->d_inode; struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_holder ghs[2]; + struct buffer_head *dibh; int alloc_required; int error; @@ -230,12 +231,22 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, goto out_ipres; } - error = gfs2_dir_add(dir, &dentry->d_name, ip, IF2DT(inode->i_mode)); + error = gfs2_meta_inode_buffer(ip, &dibh); if (error) goto out_end_trans; - error = gfs2_change_nlink(ip, +1); + error = gfs2_dir_add(dir, &dentry->d_name, ip, IF2DT(inode->i_mode)); + if (error) + goto out_brelse; + + gfs2_trans_add_bh(ip->i_gl, dibh, 1); + inc_nlink(&ip->i_inode); + ip->i_inode.i_ctime = CURRENT_TIME; + gfs2_dinode_out(ip, dibh->b_data); + mark_inode_dirty(&ip->i_inode); +out_brelse: + brelse(dibh); out_end_trans: gfs2_trans_end(sdp); out_ipres: -- cgit v1.2.3 From 855d23ce2665c56437bd88fa6a0d45b6713bd194 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 9 May 2011 16:42:37 +0100 Subject: GFS2: Make gfs2_dir_del update link count when required When we remove an entry from a directory, we can save ourselves some trouble if we know the type of the entry in question, since if it is itself a directory, we can update the link count of the parent at the same time as removing the directory entry. In addition this patch also merges the rmdir and unlink code which was almost identical anyway. This eliminates the calls to remove the . and .. directory entries on each rmdir (not needed since the directory will be deallocated, anyway) which was the only thing preventing passing the dentry to gfs2_dir_del(). The passing of the dentry rather than just the name allows us to figure out the type of the entry which is being removed, and thus adjust the link count when required. Signed-off-by: Steven Whitehouse --- fs/gfs2/ops_inode.c | 221 ++++++++++++++++------------------------------------ 1 file changed, 66 insertions(+), 155 deletions(-) (limited to 'fs/gfs2/ops_inode.c') diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index acb6f69b02ed..765da06c8f55 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -312,11 +312,52 @@ static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name, } /** - * gfs2_unlink - Unlink a file - * @dir: The inode of the directory containing the file to unlink + * gfs2_unlink_inode - Removes an inode from its parent dir and unlinks it + * @dip: The parent directory + * @name: The name of the entry in the parent directory + * @bh: The inode buffer for the inode to be removed + * @inode: The inode to be removed + * + * Called with all the locks and in a transaction. This will only be + * called for a directory after it has been checked to ensure it is empty. + * + * Returns: 0 on success, or an error + */ + +static int gfs2_unlink_inode(struct gfs2_inode *dip, + const struct dentry *dentry, + struct buffer_head *bh) +{ + struct inode *inode = dentry->d_inode; + struct gfs2_inode *ip = GFS2_I(inode); + int error; + + error = gfs2_dir_del(dip, dentry); + if (error) + return error; + + ip->i_entries = 0; + inode->i_ctime = CURRENT_TIME; + if (S_ISDIR(inode->i_mode)) + clear_nlink(inode); + else + drop_nlink(inode); + gfs2_trans_add_bh(ip->i_gl, bh, 1); + gfs2_dinode_out(ip, bh->b_data); + mark_inode_dirty(inode); + if (inode->i_nlink == 0) + gfs2_unlink_di(inode); + return 0; +} + + +/** + * gfs2_unlink - Unlink an inode (this does rmdir as well) + * @dir: The inode of the directory containing the inode to unlink * @dentry: The file itself * - * Unlink a file. Call gfs2_unlinki() + * This routine uses the type of the inode as a flag to figure out + * whether this is an unlink or an rmdir. * * Returns: errno */ @@ -325,7 +366,9 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) { struct gfs2_inode *dip = GFS2_I(dir); struct gfs2_sbd *sdp = GFS2_SB(dir); - struct gfs2_inode *ip = GFS2_I(dentry->d_inode); + struct inode *inode = dentry->d_inode; + struct gfs2_inode *ip = GFS2_I(inode); + struct buffer_head *bh; struct gfs2_holder ghs[3]; struct gfs2_rgrpd *rgd; struct gfs2_holder ri_gh; @@ -351,9 +394,15 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) goto out_child; error = -ENOENT; - if (ip->i_inode.i_nlink == 0) + if (inode->i_nlink == 0) goto out_rgrp; + if (S_ISDIR(inode->i_mode)) { + error = -ENOTEMPTY; + if (ip->i_entries > 2 || inode->i_nlink > 2) + goto out_rgrp; + } + error = gfs2_glock_nq(ghs + 2); /* rgrp */ if (error) goto out_rgrp; @@ -362,15 +411,16 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) if (error) goto out_gunlock; - error = gfs2_trans_begin(sdp, 2*RES_DINODE + RES_LEAF + RES_RG_BIT, 0); + error = gfs2_trans_begin(sdp, 2*RES_DINODE + 3*RES_LEAF + RES_RG_BIT, 0); if (error) goto out_gunlock; - error = gfs2_dir_del(dip, &dentry->d_name); - if (error) - goto out_end_trans; + error = gfs2_meta_inode_buffer(ip, &bh); + if (error) + goto out_end_trans; - error = gfs2_change_nlink(ip, -1); + error = gfs2_unlink_inode(dip, dentry, bh); + brelse(bh); out_end_trans: gfs2_trans_end(sdp); @@ -521,138 +571,6 @@ static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, int mode) return 0; } -/** - * gfs2_rmdiri - Remove a directory - * @dip: The parent directory of the directory to be removed - * @name: The name of the directory to be removed - * @ip: The GFS2 inode of the directory to be removed - * - * Assumes Glocks on dip and ip are held - * - * Returns: errno - */ - -static int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name, - struct gfs2_inode *ip) -{ - int error; - - if (ip->i_entries != 2) { - if (gfs2_consist_inode(ip)) - gfs2_dinode_print(ip); - return -EIO; - } - - error = gfs2_dir_del(dip, name); - if (error) - return error; - - error = gfs2_change_nlink(dip, -1); - if (error) - return error; - - error = gfs2_dir_del(ip, &gfs2_qdot); - if (error) - return error; - - error = gfs2_dir_del(ip, &gfs2_qdotdot); - if (error) - return error; - - /* It looks odd, but it really should be done twice */ - error = gfs2_change_nlink(ip, -1); - if (error) - return error; - - error = gfs2_change_nlink(ip, -1); - if (error) - return error; - - return error; -} - -/** - * gfs2_rmdir - Remove a directory - * @dir: The parent directory of the directory to be removed - * @dentry: The dentry of the directory to remove - * - * Remove a directory. Call gfs2_rmdiri() - * - * Returns: errno - */ - -static int gfs2_rmdir(struct inode *dir, struct dentry *dentry) -{ - struct gfs2_inode *dip = GFS2_I(dir); - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct gfs2_inode *ip = GFS2_I(dentry->d_inode); - struct gfs2_holder ghs[3]; - struct gfs2_rgrpd *rgd; - struct gfs2_holder ri_gh; - int error; - - error = gfs2_rindex_hold(sdp, &ri_gh); - if (error) - return error; - gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); - gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1); - - rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr); - gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + 2); - - error = gfs2_glock_nq(ghs); /* parent */ - if (error) - goto out_parent; - - error = gfs2_glock_nq(ghs + 1); /* child */ - if (error) - goto out_child; - - error = -ENOENT; - if (ip->i_inode.i_nlink == 0) - goto out_rgrp; - - error = gfs2_glock_nq(ghs + 2); /* rgrp */ - if (error) - goto out_rgrp; - - error = gfs2_unlink_ok(dip, &dentry->d_name, ip); - if (error) - goto out_gunlock; - - if (ip->i_entries < 2) { - if (gfs2_consist_inode(ip)) - gfs2_dinode_print(ip); - error = -EIO; - goto out_gunlock; - } - if (ip->i_entries > 2) { - error = -ENOTEMPTY; - goto out_gunlock; - } - - error = gfs2_trans_begin(sdp, 2 * RES_DINODE + 3 * RES_LEAF + RES_RG_BIT, 0); - if (error) - goto out_gunlock; - - error = gfs2_rmdiri(dip, &dentry->d_name, ip); - - gfs2_trans_end(sdp); - -out_gunlock: - gfs2_glock_dq(ghs + 2); -out_rgrp: - gfs2_holder_uninit(ghs + 2); - gfs2_glock_dq(ghs + 1); -out_child: - gfs2_holder_uninit(ghs + 1); - gfs2_glock_dq(ghs); -out_parent: - gfs2_holder_uninit(ghs); - gfs2_glock_dq_uninit(&ri_gh); - return error; -} - /** * gfs2_mknod - Make a special file * @dir: The directory in which the special file will reside @@ -930,23 +848,16 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, /* Remove the target file, if it exists */ if (nip) { - if (S_ISDIR(nip->i_inode.i_mode)) - error = gfs2_rmdiri(ndip, &ndentry->d_name, nip); - else { - error = gfs2_dir_del(ndip, &ndentry->d_name); - if (error) - goto out_end_trans; - error = gfs2_change_nlink(nip, -1); - } + struct buffer_head *bh; + error = gfs2_meta_inode_buffer(nip, &bh); if (error) goto out_end_trans; + error = gfs2_unlink_inode(ndip, ndentry, bh); + brelse(bh); } if (dir_rename) { error = gfs2_change_nlink(ndip, +1); - if (error) - goto out_end_trans; - error = gfs2_change_nlink(odip, -1); if (error) goto out_end_trans; @@ -964,7 +875,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, brelse(dibh); } - error = gfs2_dir_del(odip, &odentry->d_name); + error = gfs2_dir_del(odip, odentry); if (error) goto out_end_trans; @@ -1347,7 +1258,7 @@ const struct inode_operations gfs2_dir_iops = { .unlink = gfs2_unlink, .symlink = gfs2_symlink, .mkdir = gfs2_mkdir, - .rmdir = gfs2_rmdir, + .rmdir = gfs2_unlink, .mknod = gfs2_mknod, .rename = gfs2_rename, .permission = gfs2_permission, -- cgit v1.2.3 From 3d6ecb7d16fd4248fce58387a982a0756ad3fcc2 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 9 May 2011 13:30:08 +0100 Subject: GFS2: When adding a new dir entry, inc link count if it is a subdir This adds an increment of the link count when we add a new directory entry, if that entry is itself a directory. This means that we no longer need separate code to perform this operation. Now that both adding and removing directory entries automatically update the parent directory's link count if required, that makes the code shorter and simpler than before. Signed-off-by: Steven Whitehouse --- fs/gfs2/ops_inode.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'fs/gfs2/ops_inode.c') diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 765da06c8f55..6b8c2bdb5a0c 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -235,7 +235,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, if (error) goto out_end_trans; - error = gfs2_dir_add(dir, &dentry->d_name, ip, IF2DT(inode->i_mode)); + error = gfs2_dir_add(dir, &dentry->d_name, ip); if (error) goto out_brelse; @@ -554,9 +554,6 @@ static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, int mode) brelse(dibh); } - error = gfs2_change_nlink(dip, +1); - gfs2_assert_withdraw(sdp, !error); /* dip already pinned */ - gfs2_trans_end(sdp); if (dip->i_alloc->al_rgd) gfs2_inplace_release(dip); @@ -857,10 +854,6 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, } if (dir_rename) { - error = gfs2_change_nlink(ndip, +1); - if (error) - goto out_end_trans; - error = gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR); if (error) goto out_end_trans; @@ -879,7 +872,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (error) goto out_end_trans; - error = gfs2_dir_add(ndir, &ndentry->d_name, ip, IF2DT(ip->i_inode.i_mode)); + error = gfs2_dir_add(ndir, &ndentry->d_name, ip); if (error) goto out_end_trans; -- cgit v1.2.3 From 94fb763b1a76a2000ad21f3119b05c90040acaf0 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 9 May 2011 13:36:10 +0100 Subject: GFS2: Remove gfs2_dinode_print() function This function was intended for debugging purposes, but it is not very useful. If we want to know what is on disk then all we need is a block number and gfs2_edit can give us much better information about what is there. Otherwise, if we are interested in what is stored in the in-core inode, it doesn't help us out there either. Signed-off-by: Steven Whitehouse --- fs/gfs2/ops_inode.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs/gfs2/ops_inode.c') diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 6b8c2bdb5a0c..2607c2c6de2b 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -754,8 +754,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (S_ISDIR(nip->i_inode.i_mode)) { if (nip->i_entries < 2) { - if (gfs2_consist_inode(nip)) - gfs2_dinode_print(nip); + gfs2_consist_inode(nip); error = -EIO; goto out_gunlock; } -- cgit v1.2.3 From 194c011fc4650d0dd1eecbc35bc26045108aca51 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 9 May 2011 14:06:38 +0100 Subject: GFS2: Move most of the remaining inode.c into ops_inode.c This is in preparation to remove inode.c and rename ops_inode.c to inode.c. Also most of the functions which were left in inode.c relate to the creation and lookup of inodes. I'm intending to work on consolidating some of that code, and its easier when its all in one place. Signed-off-by: Steven Whitehouse --- fs/gfs2/ops_inode.c | 711 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 711 insertions(+) (limited to 'fs/gfs2/ops_inode.c') diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 2607c2c6de2b..a783a7a8c0ca 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "gfs2.h" @@ -34,7 +35,675 @@ #include "trans.h" #include "util.h" #include "super.h" +#include "glops.h" + +struct gfs2_skip_data { + u64 no_addr; + int skipped; + int non_block; +}; + +static int iget_test(struct inode *inode, void *opaque) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_skip_data *data = opaque; + + if (ip->i_no_addr == data->no_addr) { + if (data->non_block && + inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) { + data->skipped = 1; + return 0; + } + return 1; + } + return 0; +} + +static int iget_set(struct inode *inode, void *opaque) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_skip_data *data = opaque; + + if (data->skipped) + return -ENOENT; + inode->i_ino = (unsigned long)(data->no_addr); + ip->i_no_addr = data->no_addr; + return 0; +} + +struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr, int non_block) +{ + unsigned long hash = (unsigned long)no_addr; + struct gfs2_skip_data data; + + data.no_addr = no_addr; + data.skipped = 0; + data.non_block = non_block; + return ilookup5(sb, hash, iget_test, &data); +} + +static struct inode *gfs2_iget(struct super_block *sb, u64 no_addr, + int non_block) +{ + struct gfs2_skip_data data; + unsigned long hash = (unsigned long)no_addr; + + data.no_addr = no_addr; + data.skipped = 0; + data.non_block = non_block; + return iget5_locked(sb, hash, iget_test, iget_set, &data); +} +/** + * gfs2_set_iop - Sets inode operations + * @inode: The inode with correct i_mode filled in + * + * GFS2 lookup code fills in vfs inode contents based on info obtained + * from directory entry inside gfs2_inode_lookup(). + */ + +static void gfs2_set_iop(struct inode *inode) +{ + struct gfs2_sbd *sdp = GFS2_SB(inode); + umode_t mode = inode->i_mode; + + if (S_ISREG(mode)) { + inode->i_op = &gfs2_file_iops; + if (gfs2_localflocks(sdp)) + inode->i_fop = &gfs2_file_fops_nolock; + else + inode->i_fop = &gfs2_file_fops; + } else if (S_ISDIR(mode)) { + inode->i_op = &gfs2_dir_iops; + if (gfs2_localflocks(sdp)) + inode->i_fop = &gfs2_dir_fops_nolock; + else + inode->i_fop = &gfs2_dir_fops; + } else if (S_ISLNK(mode)) { + inode->i_op = &gfs2_symlink_iops; + } else { + inode->i_op = &gfs2_file_iops; + init_special_inode(inode, inode->i_mode, inode->i_rdev); + } +} + +/** + * gfs2_inode_lookup - Lookup an inode + * @sb: The super block + * @no_addr: The inode number + * @type: The type of the inode + * non_block: Can we block on inodes that are being freed? + * + * Returns: A VFS inode, or an error + */ + +struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type, + u64 no_addr, u64 no_formal_ino, int non_block) +{ + struct inode *inode; + struct gfs2_inode *ip; + struct gfs2_glock *io_gl = NULL; + int error; + + inode = gfs2_iget(sb, no_addr, non_block); + ip = GFS2_I(inode); + + if (!inode) + return ERR_PTR(-ENOBUFS); + + if (inode->i_state & I_NEW) { + struct gfs2_sbd *sdp = GFS2_SB(inode); + ip->i_no_formal_ino = no_formal_ino; + + error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl); + if (unlikely(error)) + goto fail; + ip->i_gl->gl_object = ip; + + error = gfs2_glock_get(sdp, no_addr, &gfs2_iopen_glops, CREATE, &io_gl); + if (unlikely(error)) + goto fail_put; + + set_bit(GIF_INVALID, &ip->i_flags); + error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, GL_EXACT, &ip->i_iopen_gh); + if (unlikely(error)) + goto fail_iopen; + + ip->i_iopen_gh.gh_gl->gl_object = ip; + gfs2_glock_put(io_gl); + io_gl = NULL; + + if (type == DT_UNKNOWN) { + /* Inode glock must be locked already */ + error = gfs2_inode_refresh(GFS2_I(inode)); + if (error) + goto fail_refresh; + } else { + inode->i_mode = DT2IF(type); + } + + gfs2_set_iop(inode); + unlock_new_inode(inode); + } + + return inode; + +fail_refresh: + ip->i_iopen_gh.gh_gl->gl_object = NULL; + gfs2_glock_dq_uninit(&ip->i_iopen_gh); +fail_iopen: + if (io_gl) + gfs2_glock_put(io_gl); +fail_put: + ip->i_gl->gl_object = NULL; + gfs2_glock_put(ip->i_gl); +fail: + iget_failed(inode); + return ERR_PTR(error); +} + +struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr, + u64 *no_formal_ino, unsigned int blktype) +{ + struct super_block *sb = sdp->sd_vfs; + struct gfs2_holder i_gh; + struct inode *inode = NULL; + int error; + + /* Must not read in block until block type is verified */ + error = gfs2_glock_nq_num(sdp, no_addr, &gfs2_inode_glops, + LM_ST_EXCLUSIVE, GL_SKIP, &i_gh); + if (error) + return ERR_PTR(error); + + error = gfs2_check_blk_type(sdp, no_addr, blktype); + if (error) + goto fail; + + inode = gfs2_inode_lookup(sb, DT_UNKNOWN, no_addr, 0, 1); + if (IS_ERR(inode)) + goto fail; + + /* Two extra checks for NFS only */ + if (no_formal_ino) { + error = -ESTALE; + if (GFS2_I(inode)->i_no_formal_ino != *no_formal_ino) + goto fail_iput; + + error = -EIO; + if (GFS2_I(inode)->i_diskflags & GFS2_DIF_SYSTEM) + goto fail_iput; + + error = 0; + } + +fail: + gfs2_glock_dq_uninit(&i_gh); + return error ? ERR_PTR(error) : inode; +fail_iput: + iput(inode); + goto fail; +} + + +struct inode *gfs2_lookup_simple(struct inode *dip, const char *name) +{ + struct qstr qstr; + struct inode *inode; + gfs2_str2qstr(&qstr, name); + inode = gfs2_lookupi(dip, &qstr, 1); + /* gfs2_lookupi has inconsistent callers: vfs + * related routines expect NULL for no entry found, + * gfs2_lookup_simple callers expect ENOENT + * and do not check for NULL. + */ + if (inode == NULL) + return ERR_PTR(-ENOENT); + else + return inode; +} + + +/** + * gfs2_lookupi - Look up a filename in a directory and return its inode + * @d_gh: An initialized holder for the directory glock + * @name: The name of the inode to look for + * @is_root: If 1, ignore the caller's permissions + * @i_gh: An uninitialized holder for the new inode glock + * + * This can be called via the VFS filldir function when NFS is doing + * a readdirplus and the inode which its intending to stat isn't + * already in cache. In this case we must not take the directory glock + * again, since the readdir call will have already taken that lock. + * + * Returns: errno + */ + +struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, + int is_root) +{ + struct super_block *sb = dir->i_sb; + struct gfs2_inode *dip = GFS2_I(dir); + struct gfs2_holder d_gh; + int error = 0; + struct inode *inode = NULL; + int unlock = 0; + + if (!name->len || name->len > GFS2_FNAMESIZE) + return ERR_PTR(-ENAMETOOLONG); + + if ((name->len == 1 && memcmp(name->name, ".", 1) == 0) || + (name->len == 2 && memcmp(name->name, "..", 2) == 0 && + dir == sb->s_root->d_inode)) { + igrab(dir); + return dir; + } + + if (gfs2_glock_is_locked_by_me(dip->i_gl) == NULL) { + error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); + if (error) + return ERR_PTR(error); + unlock = 1; + } + + if (!is_root) { + error = gfs2_permission(dir, MAY_EXEC, 0); + if (error) + goto out; + } + + inode = gfs2_dir_search(dir, name); + if (IS_ERR(inode)) + error = PTR_ERR(inode); +out: + if (unlock) + gfs2_glock_dq_uninit(&d_gh); + if (error == -ENOENT) + return NULL; + return inode ? inode : ERR_PTR(error); +} + +/** + * create_ok - OK to create a new on-disk inode here? + * @dip: Directory in which dinode is to be created + * @name: Name of new dinode + * @mode: + * + * Returns: errno + */ + +static int create_ok(struct gfs2_inode *dip, const struct qstr *name, + unsigned int mode) +{ + int error; + + error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0); + if (error) + return error; + + /* Don't create entries in an unlinked directory */ + if (!dip->i_inode.i_nlink) + return -ENOENT; + + error = gfs2_dir_check(&dip->i_inode, name, NULL); + switch (error) { + case -ENOENT: + error = 0; + break; + case 0: + return -EEXIST; + default: + return error; + } + + if (dip->i_entries == (u32)-1) + return -EFBIG; + if (S_ISDIR(mode) && dip->i_inode.i_nlink == (u32)-1) + return -EMLINK; + + return 0; +} + +static void munge_mode_uid_gid(struct gfs2_inode *dip, unsigned int *mode, + unsigned int *uid, unsigned int *gid) +{ + if (GFS2_SB(&dip->i_inode)->sd_args.ar_suiddir && + (dip->i_inode.i_mode & S_ISUID) && dip->i_inode.i_uid) { + if (S_ISDIR(*mode)) + *mode |= S_ISUID; + else if (dip->i_inode.i_uid != current_fsuid()) + *mode &= ~07111; + *uid = dip->i_inode.i_uid; + } else + *uid = current_fsuid(); + + if (dip->i_inode.i_mode & S_ISGID) { + if (S_ISDIR(*mode)) + *mode |= S_ISGID; + *gid = dip->i_inode.i_gid; + } else + *gid = current_fsgid(); +} + +static int alloc_dinode(struct gfs2_inode *dip, u64 *no_addr, u64 *generation) +{ + struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); + int error; + + if (gfs2_alloc_get(dip) == NULL) + return -ENOMEM; + + dip->i_alloc->al_requested = RES_DINODE; + error = gfs2_inplace_reserve(dip); + if (error) + goto out; + + error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_STATFS, 0); + if (error) + goto out_ipreserv; + + error = gfs2_alloc_di(dip, no_addr, generation); + + gfs2_trans_end(sdp); + +out_ipreserv: + gfs2_inplace_release(dip); +out: + gfs2_alloc_put(dip); + return error; +} + +/** + * init_dinode - Fill in a new dinode structure + * @dip: the directory this inode is being created in + * @gl: The glock covering the new inode + * @inum: the inode number + * @mode: the file permissions + * @uid: + * @gid: + * + */ + +static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, + const struct gfs2_inum_host *inum, unsigned int mode, + unsigned int uid, unsigned int gid, + const u64 *generation, dev_t dev, struct buffer_head **bhp) +{ + struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); + struct gfs2_dinode *di; + struct buffer_head *dibh; + struct timespec tv = CURRENT_TIME; + + dibh = gfs2_meta_new(gl, inum->no_addr); + gfs2_trans_add_bh(gl, dibh, 1); + gfs2_metatype_set(dibh, GFS2_METATYPE_DI, GFS2_FORMAT_DI); + gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); + di = (struct gfs2_dinode *)dibh->b_data; + + di->di_num.no_formal_ino = cpu_to_be64(inum->no_formal_ino); + di->di_num.no_addr = cpu_to_be64(inum->no_addr); + di->di_mode = cpu_to_be32(mode); + di->di_uid = cpu_to_be32(uid); + di->di_gid = cpu_to_be32(gid); + di->di_nlink = 0; + di->di_size = 0; + di->di_blocks = cpu_to_be64(1); + di->di_atime = di->di_mtime = di->di_ctime = cpu_to_be64(tv.tv_sec); + di->di_major = cpu_to_be32(MAJOR(dev)); + di->di_minor = cpu_to_be32(MINOR(dev)); + di->di_goal_meta = di->di_goal_data = cpu_to_be64(inum->no_addr); + di->di_generation = cpu_to_be64(*generation); + di->di_flags = 0; + + if (S_ISREG(mode)) { + if ((dip->i_diskflags & GFS2_DIF_INHERIT_JDATA) || + gfs2_tune_get(sdp, gt_new_files_jdata)) + di->di_flags |= cpu_to_be32(GFS2_DIF_JDATA); + } else if (S_ISDIR(mode)) { + di->di_flags |= cpu_to_be32(dip->i_diskflags & + GFS2_DIF_INHERIT_JDATA); + } + + di->__pad1 = 0; + di->di_payload_format = cpu_to_be32(S_ISDIR(mode) ? GFS2_FORMAT_DE : 0); + di->di_height = 0; + di->__pad2 = 0; + di->__pad3 = 0; + di->di_depth = 0; + di->di_entries = 0; + memset(&di->__pad4, 0, sizeof(di->__pad4)); + di->di_eattr = 0; + di->di_atime_nsec = cpu_to_be32(tv.tv_nsec); + di->di_mtime_nsec = cpu_to_be32(tv.tv_nsec); + di->di_ctime_nsec = cpu_to_be32(tv.tv_nsec); + memset(&di->di_reserved, 0, sizeof(di->di_reserved)); + + set_buffer_uptodate(dibh); + + *bhp = dibh; +} + +static int make_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, + unsigned int mode, const struct gfs2_inum_host *inum, + const u64 *generation, dev_t dev, struct buffer_head **bhp) +{ + struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); + unsigned int uid, gid; + int error; + + munge_mode_uid_gid(dip, &mode, &uid, &gid); + if (!gfs2_alloc_get(dip)) + return -ENOMEM; + + error = gfs2_quota_lock(dip, uid, gid); + if (error) + goto out; + + error = gfs2_quota_check(dip, uid, gid); + if (error) + goto out_quota; + + error = gfs2_trans_begin(sdp, RES_DINODE + RES_QUOTA, 0); + if (error) + goto out_quota; + + init_dinode(dip, gl, inum, mode, uid, gid, generation, dev, bhp); + gfs2_quota_change(dip, +1, uid, gid); + gfs2_trans_end(sdp); + +out_quota: + gfs2_quota_unlock(dip); +out: + gfs2_alloc_put(dip); + return error; +} + +static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, + struct gfs2_inode *ip) +{ + struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); + struct gfs2_alloc *al; + int alloc_required; + struct buffer_head *dibh; + int error; + + al = gfs2_alloc_get(dip); + if (!al) + return -ENOMEM; + + error = gfs2_quota_lock(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + if (error) + goto fail; + + error = alloc_required = gfs2_diradd_alloc_required(&dip->i_inode, name); + if (alloc_required < 0) + goto fail_quota_locks; + if (alloc_required) { + error = gfs2_quota_check(dip, dip->i_inode.i_uid, dip->i_inode.i_gid); + if (error) + goto fail_quota_locks; + + al->al_requested = sdp->sd_max_dirres; + + error = gfs2_inplace_reserve(dip); + if (error) + goto fail_quota_locks; + + error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + + al->al_rgd->rd_length + + 2 * RES_DINODE + + RES_STATFS + RES_QUOTA, 0); + if (error) + goto fail_ipreserv; + } else { + error = gfs2_trans_begin(sdp, RES_LEAF + 2 * RES_DINODE, 0); + if (error) + goto fail_quota_locks; + } + + error = gfs2_dir_add(&dip->i_inode, name, ip); + if (error) + goto fail_end_trans; + + error = gfs2_meta_inode_buffer(ip, &dibh); + if (error) + goto fail_end_trans; + ip->i_inode.i_nlink = 1; + gfs2_trans_add_bh(ip->i_gl, dibh, 1); + gfs2_dinode_out(ip, dibh->b_data); + brelse(dibh); + return 0; + +fail_end_trans: + gfs2_trans_end(sdp); + +fail_ipreserv: + if (dip->i_alloc->al_rgd) + gfs2_inplace_release(dip); + +fail_quota_locks: + gfs2_quota_unlock(dip); + +fail: + gfs2_alloc_put(dip); + return error; +} + +static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip, + const struct qstr *qstr) +{ + int err; + size_t len; + void *value; + char *name; + + err = security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr, + &name, &value, &len); + + if (err) { + if (err == -EOPNOTSUPP) + return 0; + return err; + } + + err = __gfs2_xattr_set(&ip->i_inode, name, value, len, 0, + GFS2_EATYPE_SECURITY); + kfree(value); + kfree(name); + + return err; +} + +/** + * gfs2_createi - Create a new inode + * @ghs: An array of two holders + * @name: The name of the new file + * @mode: the permissions on the new inode + * + * @ghs[0] is an initialized holder for the directory + * @ghs[1] is the holder for the inode lock + * + * If the return value is not NULL, the glocks on both the directory and the new + * file are held. A transaction has been started and an inplace reservation + * is held, as well. + * + * Returns: An inode + */ + +struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, + unsigned int mode, dev_t dev) +{ + struct inode *inode = NULL; + struct gfs2_inode *dip = ghs->gh_gl->gl_object; + struct inode *dir = &dip->i_inode; + struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); + struct gfs2_inum_host inum = { .no_addr = 0, .no_formal_ino = 0 }; + int error; + u64 generation; + struct buffer_head *bh = NULL; + + if (!name->len || name->len > GFS2_FNAMESIZE) + return ERR_PTR(-ENAMETOOLONG); + + gfs2_holder_reinit(LM_ST_EXCLUSIVE, 0, ghs); + error = gfs2_glock_nq(ghs); + if (error) + goto fail; + + error = create_ok(dip, name, mode); + if (error) + goto fail_gunlock; + + error = alloc_dinode(dip, &inum.no_addr, &generation); + if (error) + goto fail_gunlock; + inum.no_formal_ino = generation; + + error = gfs2_glock_nq_num(sdp, inum.no_addr, &gfs2_inode_glops, + LM_ST_EXCLUSIVE, GL_SKIP, ghs + 1); + if (error) + goto fail_gunlock; + + error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation, dev, &bh); + if (error) + goto fail_gunlock2; + + inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr, + inum.no_formal_ino, 0); + if (IS_ERR(inode)) + goto fail_gunlock2; + + error = gfs2_inode_refresh(GFS2_I(inode)); + if (error) + goto fail_gunlock2; + + error = gfs2_acl_create(dip, inode); + if (error) + goto fail_gunlock2; + + error = gfs2_security_init(dip, GFS2_I(inode), name); + if (error) + goto fail_gunlock2; + + error = link_dinode(dip, name, GFS2_I(inode)); + if (error) + goto fail_gunlock2; + + if (bh) + brelse(bh); + return inode; + +fail_gunlock2: + gfs2_glock_dq_uninit(ghs + 1); + if (inode && !IS_ERR(inode)) + iput(inode); +fail_gunlock: + gfs2_glock_dq(ghs); +fail: + if (bh) + brelse(bh); + return ERR_PTR(error); +} /** * gfs2_create - Create a file * @dir: The directory in which to create the file @@ -1000,6 +1669,48 @@ int gfs2_permission(struct inode *inode, int mask, unsigned int flags) return error; } +static int __gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) +{ + struct inode *inode = &ip->i_inode; + struct buffer_head *dibh; + int error; + + error = gfs2_meta_inode_buffer(ip, &dibh); + if (error) + return error; + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + gfs2_trans_add_bh(ip->i_gl, dibh, 1); + gfs2_dinode_out(ip, dibh->b_data); + brelse(dibh); + return 0; +} + +/** + * gfs2_setattr_simple - + * @ip: + * @attr: + * + * Returns: errno + */ + +int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) +{ + int error; + + if (current->journal_info) + return __gfs2_setattr_simple(ip, attr); + + error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), RES_DINODE, 0); + if (error) + return error; + + error = __gfs2_setattr_simple(ip, attr); + gfs2_trans_end(GFS2_SB(&ip->i_inode)); + return error; +} + static int setattr_chown(struct inode *inode, struct iattr *attr) { struct gfs2_inode *ip = GFS2_I(inode); -- cgit v1.2.3 From 2ab9cd1c63b519e37b21b504376822be983badba Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Tue, 10 May 2011 13:12:49 +0100 Subject: GFS2: Rename ops_inode.c to inode.c This is the final part of the ops_inode.c/inode.c reordering. We are left with a single file called inode.c which now contains all the inode operations, as expected. Signed-off-by: Steven Whitehouse --- fs/gfs2/ops_inode.c | 1990 --------------------------------------------------- 1 file changed, 1990 deletions(-) delete mode 100644 fs/gfs2/ops_inode.c (limited to 'fs/gfs2/ops_inode.c') diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c deleted file mode 100644 index a783a7a8c0ca..000000000000 --- a/fs/gfs2/ops_inode.c +++ /dev/null @@ -1,1990 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. - * - * This copyrighted material is made available to anyone wishing to use, - * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU General Public License version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "acl.h" -#include "bmap.h" -#include "dir.h" -#include "xattr.h" -#include "glock.h" -#include "inode.h" -#include "meta_io.h" -#include "quota.h" -#include "rgrp.h" -#include "trans.h" -#include "util.h" -#include "super.h" -#include "glops.h" - -struct gfs2_skip_data { - u64 no_addr; - int skipped; - int non_block; -}; - -static int iget_test(struct inode *inode, void *opaque) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_skip_data *data = opaque; - - if (ip->i_no_addr == data->no_addr) { - if (data->non_block && - inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) { - data->skipped = 1; - return 0; - } - return 1; - } - return 0; -} - -static int iget_set(struct inode *inode, void *opaque) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_skip_data *data = opaque; - - if (data->skipped) - return -ENOENT; - inode->i_ino = (unsigned long)(data->no_addr); - ip->i_no_addr = data->no_addr; - return 0; -} - -struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr, int non_block) -{ - unsigned long hash = (unsigned long)no_addr; - struct gfs2_skip_data data; - - data.no_addr = no_addr; - data.skipped = 0; - data.non_block = non_block; - return ilookup5(sb, hash, iget_test, &data); -} - -static struct inode *gfs2_iget(struct super_block *sb, u64 no_addr, - int non_block) -{ - struct gfs2_skip_data data; - unsigned long hash = (unsigned long)no_addr; - - data.no_addr = no_addr; - data.skipped = 0; - data.non_block = non_block; - return iget5_locked(sb, hash, iget_test, iget_set, &data); -} - -/** - * gfs2_set_iop - Sets inode operations - * @inode: The inode with correct i_mode filled in - * - * GFS2 lookup code fills in vfs inode contents based on info obtained - * from directory entry inside gfs2_inode_lookup(). - */ - -static void gfs2_set_iop(struct inode *inode) -{ - struct gfs2_sbd *sdp = GFS2_SB(inode); - umode_t mode = inode->i_mode; - - if (S_ISREG(mode)) { - inode->i_op = &gfs2_file_iops; - if (gfs2_localflocks(sdp)) - inode->i_fop = &gfs2_file_fops_nolock; - else - inode->i_fop = &gfs2_file_fops; - } else if (S_ISDIR(mode)) { - inode->i_op = &gfs2_dir_iops; - if (gfs2_localflocks(sdp)) - inode->i_fop = &gfs2_dir_fops_nolock; - else - inode->i_fop = &gfs2_dir_fops; - } else if (S_ISLNK(mode)) { - inode->i_op = &gfs2_symlink_iops; - } else { - inode->i_op = &gfs2_file_iops; - init_special_inode(inode, inode->i_mode, inode->i_rdev); - } -} - -/** - * gfs2_inode_lookup - Lookup an inode - * @sb: The super block - * @no_addr: The inode number - * @type: The type of the inode - * non_block: Can we block on inodes that are being freed? - * - * Returns: A VFS inode, or an error - */ - -struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type, - u64 no_addr, u64 no_formal_ino, int non_block) -{ - struct inode *inode; - struct gfs2_inode *ip; - struct gfs2_glock *io_gl = NULL; - int error; - - inode = gfs2_iget(sb, no_addr, non_block); - ip = GFS2_I(inode); - - if (!inode) - return ERR_PTR(-ENOBUFS); - - if (inode->i_state & I_NEW) { - struct gfs2_sbd *sdp = GFS2_SB(inode); - ip->i_no_formal_ino = no_formal_ino; - - error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl); - if (unlikely(error)) - goto fail; - ip->i_gl->gl_object = ip; - - error = gfs2_glock_get(sdp, no_addr, &gfs2_iopen_glops, CREATE, &io_gl); - if (unlikely(error)) - goto fail_put; - - set_bit(GIF_INVALID, &ip->i_flags); - error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, GL_EXACT, &ip->i_iopen_gh); - if (unlikely(error)) - goto fail_iopen; - - ip->i_iopen_gh.gh_gl->gl_object = ip; - gfs2_glock_put(io_gl); - io_gl = NULL; - - if (type == DT_UNKNOWN) { - /* Inode glock must be locked already */ - error = gfs2_inode_refresh(GFS2_I(inode)); - if (error) - goto fail_refresh; - } else { - inode->i_mode = DT2IF(type); - } - - gfs2_set_iop(inode); - unlock_new_inode(inode); - } - - return inode; - -fail_refresh: - ip->i_iopen_gh.gh_gl->gl_object = NULL; - gfs2_glock_dq_uninit(&ip->i_iopen_gh); -fail_iopen: - if (io_gl) - gfs2_glock_put(io_gl); -fail_put: - ip->i_gl->gl_object = NULL; - gfs2_glock_put(ip->i_gl); -fail: - iget_failed(inode); - return ERR_PTR(error); -} - -struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr, - u64 *no_formal_ino, unsigned int blktype) -{ - struct super_block *sb = sdp->sd_vfs; - struct gfs2_holder i_gh; - struct inode *inode = NULL; - int error; - - /* Must not read in block until block type is verified */ - error = gfs2_glock_nq_num(sdp, no_addr, &gfs2_inode_glops, - LM_ST_EXCLUSIVE, GL_SKIP, &i_gh); - if (error) - return ERR_PTR(error); - - error = gfs2_check_blk_type(sdp, no_addr, blktype); - if (error) - goto fail; - - inode = gfs2_inode_lookup(sb, DT_UNKNOWN, no_addr, 0, 1); - if (IS_ERR(inode)) - goto fail; - - /* Two extra checks for NFS only */ - if (no_formal_ino) { - error = -ESTALE; - if (GFS2_I(inode)->i_no_formal_ino != *no_formal_ino) - goto fail_iput; - - error = -EIO; - if (GFS2_I(inode)->i_diskflags & GFS2_DIF_SYSTEM) - goto fail_iput; - - error = 0; - } - -fail: - gfs2_glock_dq_uninit(&i_gh); - return error ? ERR_PTR(error) : inode; -fail_iput: - iput(inode); - goto fail; -} - - -struct inode *gfs2_lookup_simple(struct inode *dip, const char *name) -{ - struct qstr qstr; - struct inode *inode; - gfs2_str2qstr(&qstr, name); - inode = gfs2_lookupi(dip, &qstr, 1); - /* gfs2_lookupi has inconsistent callers: vfs - * related routines expect NULL for no entry found, - * gfs2_lookup_simple callers expect ENOENT - * and do not check for NULL. - */ - if (inode == NULL) - return ERR_PTR(-ENOENT); - else - return inode; -} - - -/** - * gfs2_lookupi - Look up a filename in a directory and return its inode - * @d_gh: An initialized holder for the directory glock - * @name: The name of the inode to look for - * @is_root: If 1, ignore the caller's permissions - * @i_gh: An uninitialized holder for the new inode glock - * - * This can be called via the VFS filldir function when NFS is doing - * a readdirplus and the inode which its intending to stat isn't - * already in cache. In this case we must not take the directory glock - * again, since the readdir call will have already taken that lock. - * - * Returns: errno - */ - -struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, - int is_root) -{ - struct super_block *sb = dir->i_sb; - struct gfs2_inode *dip = GFS2_I(dir); - struct gfs2_holder d_gh; - int error = 0; - struct inode *inode = NULL; - int unlock = 0; - - if (!name->len || name->len > GFS2_FNAMESIZE) - return ERR_PTR(-ENAMETOOLONG); - - if ((name->len == 1 && memcmp(name->name, ".", 1) == 0) || - (name->len == 2 && memcmp(name->name, "..", 2) == 0 && - dir == sb->s_root->d_inode)) { - igrab(dir); - return dir; - } - - if (gfs2_glock_is_locked_by_me(dip->i_gl) == NULL) { - error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); - if (error) - return ERR_PTR(error); - unlock = 1; - } - - if (!is_root) { - error = gfs2_permission(dir, MAY_EXEC, 0); - if (error) - goto out; - } - - inode = gfs2_dir_search(dir, name); - if (IS_ERR(inode)) - error = PTR_ERR(inode); -out: - if (unlock) - gfs2_glock_dq_uninit(&d_gh); - if (error == -ENOENT) - return NULL; - return inode ? inode : ERR_PTR(error); -} - -/** - * create_ok - OK to create a new on-disk inode here? - * @dip: Directory in which dinode is to be created - * @name: Name of new dinode - * @mode: - * - * Returns: errno - */ - -static int create_ok(struct gfs2_inode *dip, const struct qstr *name, - unsigned int mode) -{ - int error; - - error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0); - if (error) - return error; - - /* Don't create entries in an unlinked directory */ - if (!dip->i_inode.i_nlink) - return -ENOENT; - - error = gfs2_dir_check(&dip->i_inode, name, NULL); - switch (error) { - case -ENOENT: - error = 0; - break; - case 0: - return -EEXIST; - default: - return error; - } - - if (dip->i_entries == (u32)-1) - return -EFBIG; - if (S_ISDIR(mode) && dip->i_inode.i_nlink == (u32)-1) - return -EMLINK; - - return 0; -} - -static void munge_mode_uid_gid(struct gfs2_inode *dip, unsigned int *mode, - unsigned int *uid, unsigned int *gid) -{ - if (GFS2_SB(&dip->i_inode)->sd_args.ar_suiddir && - (dip->i_inode.i_mode & S_ISUID) && dip->i_inode.i_uid) { - if (S_ISDIR(*mode)) - *mode |= S_ISUID; - else if (dip->i_inode.i_uid != current_fsuid()) - *mode &= ~07111; - *uid = dip->i_inode.i_uid; - } else - *uid = current_fsuid(); - - if (dip->i_inode.i_mode & S_ISGID) { - if (S_ISDIR(*mode)) - *mode |= S_ISGID; - *gid = dip->i_inode.i_gid; - } else - *gid = current_fsgid(); -} - -static int alloc_dinode(struct gfs2_inode *dip, u64 *no_addr, u64 *generation) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - int error; - - if (gfs2_alloc_get(dip) == NULL) - return -ENOMEM; - - dip->i_alloc->al_requested = RES_DINODE; - error = gfs2_inplace_reserve(dip); - if (error) - goto out; - - error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_STATFS, 0); - if (error) - goto out_ipreserv; - - error = gfs2_alloc_di(dip, no_addr, generation); - - gfs2_trans_end(sdp); - -out_ipreserv: - gfs2_inplace_release(dip); -out: - gfs2_alloc_put(dip); - return error; -} - -/** - * init_dinode - Fill in a new dinode structure - * @dip: the directory this inode is being created in - * @gl: The glock covering the new inode - * @inum: the inode number - * @mode: the file permissions - * @uid: - * @gid: - * - */ - -static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, - const struct gfs2_inum_host *inum, unsigned int mode, - unsigned int uid, unsigned int gid, - const u64 *generation, dev_t dev, struct buffer_head **bhp) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct gfs2_dinode *di; - struct buffer_head *dibh; - struct timespec tv = CURRENT_TIME; - - dibh = gfs2_meta_new(gl, inum->no_addr); - gfs2_trans_add_bh(gl, dibh, 1); - gfs2_metatype_set(dibh, GFS2_METATYPE_DI, GFS2_FORMAT_DI); - gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); - di = (struct gfs2_dinode *)dibh->b_data; - - di->di_num.no_formal_ino = cpu_to_be64(inum->no_formal_ino); - di->di_num.no_addr = cpu_to_be64(inum->no_addr); - di->di_mode = cpu_to_be32(mode); - di->di_uid = cpu_to_be32(uid); - di->di_gid = cpu_to_be32(gid); - di->di_nlink = 0; - di->di_size = 0; - di->di_blocks = cpu_to_be64(1); - di->di_atime = di->di_mtime = di->di_ctime = cpu_to_be64(tv.tv_sec); - di->di_major = cpu_to_be32(MAJOR(dev)); - di->di_minor = cpu_to_be32(MINOR(dev)); - di->di_goal_meta = di->di_goal_data = cpu_to_be64(inum->no_addr); - di->di_generation = cpu_to_be64(*generation); - di->di_flags = 0; - - if (S_ISREG(mode)) { - if ((dip->i_diskflags & GFS2_DIF_INHERIT_JDATA) || - gfs2_tune_get(sdp, gt_new_files_jdata)) - di->di_flags |= cpu_to_be32(GFS2_DIF_JDATA); - } else if (S_ISDIR(mode)) { - di->di_flags |= cpu_to_be32(dip->i_diskflags & - GFS2_DIF_INHERIT_JDATA); - } - - di->__pad1 = 0; - di->di_payload_format = cpu_to_be32(S_ISDIR(mode) ? GFS2_FORMAT_DE : 0); - di->di_height = 0; - di->__pad2 = 0; - di->__pad3 = 0; - di->di_depth = 0; - di->di_entries = 0; - memset(&di->__pad4, 0, sizeof(di->__pad4)); - di->di_eattr = 0; - di->di_atime_nsec = cpu_to_be32(tv.tv_nsec); - di->di_mtime_nsec = cpu_to_be32(tv.tv_nsec); - di->di_ctime_nsec = cpu_to_be32(tv.tv_nsec); - memset(&di->di_reserved, 0, sizeof(di->di_reserved)); - - set_buffer_uptodate(dibh); - - *bhp = dibh; -} - -static int make_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, - unsigned int mode, const struct gfs2_inum_host *inum, - const u64 *generation, dev_t dev, struct buffer_head **bhp) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - unsigned int uid, gid; - int error; - - munge_mode_uid_gid(dip, &mode, &uid, &gid); - if (!gfs2_alloc_get(dip)) - return -ENOMEM; - - error = gfs2_quota_lock(dip, uid, gid); - if (error) - goto out; - - error = gfs2_quota_check(dip, uid, gid); - if (error) - goto out_quota; - - error = gfs2_trans_begin(sdp, RES_DINODE + RES_QUOTA, 0); - if (error) - goto out_quota; - - init_dinode(dip, gl, inum, mode, uid, gid, generation, dev, bhp); - gfs2_quota_change(dip, +1, uid, gid); - gfs2_trans_end(sdp); - -out_quota: - gfs2_quota_unlock(dip); -out: - gfs2_alloc_put(dip); - return error; -} - -static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, - struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct gfs2_alloc *al; - int alloc_required; - struct buffer_head *dibh; - int error; - - al = gfs2_alloc_get(dip); - if (!al) - return -ENOMEM; - - error = gfs2_quota_lock(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto fail; - - error = alloc_required = gfs2_diradd_alloc_required(&dip->i_inode, name); - if (alloc_required < 0) - goto fail_quota_locks; - if (alloc_required) { - error = gfs2_quota_check(dip, dip->i_inode.i_uid, dip->i_inode.i_gid); - if (error) - goto fail_quota_locks; - - al->al_requested = sdp->sd_max_dirres; - - error = gfs2_inplace_reserve(dip); - if (error) - goto fail_quota_locks; - - error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + - al->al_rgd->rd_length + - 2 * RES_DINODE + - RES_STATFS + RES_QUOTA, 0); - if (error) - goto fail_ipreserv; - } else { - error = gfs2_trans_begin(sdp, RES_LEAF + 2 * RES_DINODE, 0); - if (error) - goto fail_quota_locks; - } - - error = gfs2_dir_add(&dip->i_inode, name, ip); - if (error) - goto fail_end_trans; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto fail_end_trans; - ip->i_inode.i_nlink = 1; - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(ip, dibh->b_data); - brelse(dibh); - return 0; - -fail_end_trans: - gfs2_trans_end(sdp); - -fail_ipreserv: - if (dip->i_alloc->al_rgd) - gfs2_inplace_release(dip); - -fail_quota_locks: - gfs2_quota_unlock(dip); - -fail: - gfs2_alloc_put(dip); - return error; -} - -static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip, - const struct qstr *qstr) -{ - int err; - size_t len; - void *value; - char *name; - - err = security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr, - &name, &value, &len); - - if (err) { - if (err == -EOPNOTSUPP) - return 0; - return err; - } - - err = __gfs2_xattr_set(&ip->i_inode, name, value, len, 0, - GFS2_EATYPE_SECURITY); - kfree(value); - kfree(name); - - return err; -} - -/** - * gfs2_createi - Create a new inode - * @ghs: An array of two holders - * @name: The name of the new file - * @mode: the permissions on the new inode - * - * @ghs[0] is an initialized holder for the directory - * @ghs[1] is the holder for the inode lock - * - * If the return value is not NULL, the glocks on both the directory and the new - * file are held. A transaction has been started and an inplace reservation - * is held, as well. - * - * Returns: An inode - */ - -struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, - unsigned int mode, dev_t dev) -{ - struct inode *inode = NULL; - struct gfs2_inode *dip = ghs->gh_gl->gl_object; - struct inode *dir = &dip->i_inode; - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct gfs2_inum_host inum = { .no_addr = 0, .no_formal_ino = 0 }; - int error; - u64 generation; - struct buffer_head *bh = NULL; - - if (!name->len || name->len > GFS2_FNAMESIZE) - return ERR_PTR(-ENAMETOOLONG); - - gfs2_holder_reinit(LM_ST_EXCLUSIVE, 0, ghs); - error = gfs2_glock_nq(ghs); - if (error) - goto fail; - - error = create_ok(dip, name, mode); - if (error) - goto fail_gunlock; - - error = alloc_dinode(dip, &inum.no_addr, &generation); - if (error) - goto fail_gunlock; - inum.no_formal_ino = generation; - - error = gfs2_glock_nq_num(sdp, inum.no_addr, &gfs2_inode_glops, - LM_ST_EXCLUSIVE, GL_SKIP, ghs + 1); - if (error) - goto fail_gunlock; - - error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation, dev, &bh); - if (error) - goto fail_gunlock2; - - inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr, - inum.no_formal_ino, 0); - if (IS_ERR(inode)) - goto fail_gunlock2; - - error = gfs2_inode_refresh(GFS2_I(inode)); - if (error) - goto fail_gunlock2; - - error = gfs2_acl_create(dip, inode); - if (error) - goto fail_gunlock2; - - error = gfs2_security_init(dip, GFS2_I(inode), name); - if (error) - goto fail_gunlock2; - - error = link_dinode(dip, name, GFS2_I(inode)); - if (error) - goto fail_gunlock2; - - if (bh) - brelse(bh); - return inode; - -fail_gunlock2: - gfs2_glock_dq_uninit(ghs + 1); - if (inode && !IS_ERR(inode)) - iput(inode); -fail_gunlock: - gfs2_glock_dq(ghs); -fail: - if (bh) - brelse(bh); - return ERR_PTR(error); -} -/** - * gfs2_create - Create a file - * @dir: The directory in which to create the file - * @dentry: The dentry of the new file - * @mode: The mode of the new file - * - * Returns: errno - */ - -static int gfs2_create(struct inode *dir, struct dentry *dentry, - int mode, struct nameidata *nd) -{ - struct gfs2_inode *dip = GFS2_I(dir); - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct gfs2_holder ghs[2]; - struct inode *inode; - - gfs2_holder_init(dip->i_gl, 0, 0, ghs); - - for (;;) { - inode = gfs2_createi(ghs, &dentry->d_name, S_IFREG | mode, 0); - if (!IS_ERR(inode)) { - gfs2_trans_end(sdp); - if (dip->i_alloc->al_rgd) - gfs2_inplace_release(dip); - gfs2_quota_unlock(dip); - gfs2_alloc_put(dip); - gfs2_glock_dq_uninit_m(2, ghs); - mark_inode_dirty(inode); - break; - } else if (PTR_ERR(inode) != -EEXIST || - (nd && nd->flags & LOOKUP_EXCL)) { - gfs2_holder_uninit(ghs); - return PTR_ERR(inode); - } - - inode = gfs2_lookupi(dir, &dentry->d_name, 0); - if (inode) { - if (!IS_ERR(inode)) { - gfs2_holder_uninit(ghs); - break; - } else { - gfs2_holder_uninit(ghs); - return PTR_ERR(inode); - } - } - } - - d_instantiate(dentry, inode); - - return 0; -} - -/** - * gfs2_lookup - Look up a filename in a directory and return its inode - * @dir: The directory inode - * @dentry: The dentry of the new inode - * @nd: passed from Linux VFS, ignored by us - * - * Called by the VFS layer. Lock dir and call gfs2_lookupi() - * - * Returns: errno - */ - -static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd) -{ - struct inode *inode = NULL; - - inode = gfs2_lookupi(dir, &dentry->d_name, 0); - if (inode && IS_ERR(inode)) - return ERR_CAST(inode); - - if (inode) { - struct gfs2_glock *gl = GFS2_I(inode)->i_gl; - struct gfs2_holder gh; - int error; - error = gfs2_glock_nq_init(gl, LM_ST_SHARED, LM_FLAG_ANY, &gh); - if (error) { - iput(inode); - return ERR_PTR(error); - } - gfs2_glock_dq_uninit(&gh); - return d_splice_alias(inode, dentry); - } - d_add(dentry, inode); - - return NULL; -} - -/** - * gfs2_link - Link to a file - * @old_dentry: The inode to link - * @dir: Add link to this directory - * @dentry: The name of the link - * - * Link the inode in "old_dentry" into the directory "dir" with the - * name in "dentry". - * - * Returns: errno - */ - -static int gfs2_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *dentry) -{ - struct gfs2_inode *dip = GFS2_I(dir); - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct inode *inode = old_dentry->d_inode; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder ghs[2]; - struct buffer_head *dibh; - int alloc_required; - int error; - - if (S_ISDIR(inode->i_mode)) - return -EPERM; - - gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); - gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1); - - error = gfs2_glock_nq(ghs); /* parent */ - if (error) - goto out_parent; - - error = gfs2_glock_nq(ghs + 1); /* child */ - if (error) - goto out_child; - - error = -ENOENT; - if (inode->i_nlink == 0) - goto out_gunlock; - - error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC, 0); - if (error) - goto out_gunlock; - - error = gfs2_dir_check(dir, &dentry->d_name, NULL); - switch (error) { - case -ENOENT: - break; - case 0: - error = -EEXIST; - default: - goto out_gunlock; - } - - error = -EINVAL; - if (!dip->i_inode.i_nlink) - goto out_gunlock; - error = -EFBIG; - if (dip->i_entries == (u32)-1) - goto out_gunlock; - error = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto out_gunlock; - error = -EINVAL; - if (!ip->i_inode.i_nlink) - goto out_gunlock; - error = -EMLINK; - if (ip->i_inode.i_nlink == (u32)-1) - goto out_gunlock; - - alloc_required = error = gfs2_diradd_alloc_required(dir, &dentry->d_name); - if (error < 0) - goto out_gunlock; - error = 0; - - if (alloc_required) { - struct gfs2_alloc *al = gfs2_alloc_get(dip); - if (!al) { - error = -ENOMEM; - goto out_gunlock; - } - - error = gfs2_quota_lock_check(dip); - if (error) - goto out_alloc; - - al->al_requested = sdp->sd_max_dirres; - - error = gfs2_inplace_reserve(dip); - if (error) - goto out_gunlock_q; - - error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + - gfs2_rg_blocks(al) + - 2 * RES_DINODE + RES_STATFS + - RES_QUOTA, 0); - if (error) - goto out_ipres; - } else { - error = gfs2_trans_begin(sdp, 2 * RES_DINODE + RES_LEAF, 0); - if (error) - goto out_ipres; - } - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto out_end_trans; - - error = gfs2_dir_add(dir, &dentry->d_name, ip); - if (error) - goto out_brelse; - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - inc_nlink(&ip->i_inode); - ip->i_inode.i_ctime = CURRENT_TIME; - gfs2_dinode_out(ip, dibh->b_data); - mark_inode_dirty(&ip->i_inode); - -out_brelse: - brelse(dibh); -out_end_trans: - gfs2_trans_end(sdp); -out_ipres: - if (alloc_required) - gfs2_inplace_release(dip); -out_gunlock_q: - if (alloc_required) - gfs2_quota_unlock(dip); -out_alloc: - if (alloc_required) - gfs2_alloc_put(dip); -out_gunlock: - gfs2_glock_dq(ghs + 1); -out_child: - gfs2_glock_dq(ghs); -out_parent: - gfs2_holder_uninit(ghs); - gfs2_holder_uninit(ghs + 1); - if (!error) { - ihold(inode); - d_instantiate(dentry, inode); - mark_inode_dirty(inode); - } - return error; -} - -/* - * gfs2_unlink_ok - check to see that a inode is still in a directory - * @dip: the directory - * @name: the name of the file - * @ip: the inode - * - * Assumes that the lock on (at least) @dip is held. - * - * Returns: 0 if the parent/child relationship is correct, errno if it isn't - */ - -static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name, - const struct gfs2_inode *ip) -{ - int error; - - if (IS_IMMUTABLE(&ip->i_inode) || IS_APPEND(&ip->i_inode)) - return -EPERM; - - if ((dip->i_inode.i_mode & S_ISVTX) && - dip->i_inode.i_uid != current_fsuid() && - ip->i_inode.i_uid != current_fsuid() && !capable(CAP_FOWNER)) - return -EPERM; - - if (IS_APPEND(&dip->i_inode)) - return -EPERM; - - error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0); - if (error) - return error; - - error = gfs2_dir_check(&dip->i_inode, name, ip); - if (error) - return error; - - return 0; -} - -/** - * gfs2_unlink_inode - Removes an inode from its parent dir and unlinks it - * @dip: The parent directory - * @name: The name of the entry in the parent directory - * @bh: The inode buffer for the inode to be removed - * @inode: The inode to be removed - * - * Called with all the locks and in a transaction. This will only be - * called for a directory after it has been checked to ensure it is empty. - * - * Returns: 0 on success, or an error - */ - -static int gfs2_unlink_inode(struct gfs2_inode *dip, - const struct dentry *dentry, - struct buffer_head *bh) -{ - struct inode *inode = dentry->d_inode; - struct gfs2_inode *ip = GFS2_I(inode); - int error; - - error = gfs2_dir_del(dip, dentry); - if (error) - return error; - - ip->i_entries = 0; - inode->i_ctime = CURRENT_TIME; - if (S_ISDIR(inode->i_mode)) - clear_nlink(inode); - else - drop_nlink(inode); - gfs2_trans_add_bh(ip->i_gl, bh, 1); - gfs2_dinode_out(ip, bh->b_data); - mark_inode_dirty(inode); - if (inode->i_nlink == 0) - gfs2_unlink_di(inode); - return 0; -} - - -/** - * gfs2_unlink - Unlink an inode (this does rmdir as well) - * @dir: The inode of the directory containing the inode to unlink - * @dentry: The file itself - * - * This routine uses the type of the inode as a flag to figure out - * whether this is an unlink or an rmdir. - * - * Returns: errno - */ - -static int gfs2_unlink(struct inode *dir, struct dentry *dentry) -{ - struct gfs2_inode *dip = GFS2_I(dir); - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct inode *inode = dentry->d_inode; - struct gfs2_inode *ip = GFS2_I(inode); - struct buffer_head *bh; - struct gfs2_holder ghs[3]; - struct gfs2_rgrpd *rgd; - struct gfs2_holder ri_gh; - int error; - - error = gfs2_rindex_hold(sdp, &ri_gh); - if (error) - return error; - - gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); - gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1); - - rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr); - gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + 2); - - - error = gfs2_glock_nq(ghs); /* parent */ - if (error) - goto out_parent; - - error = gfs2_glock_nq(ghs + 1); /* child */ - if (error) - goto out_child; - - error = -ENOENT; - if (inode->i_nlink == 0) - goto out_rgrp; - - if (S_ISDIR(inode->i_mode)) { - error = -ENOTEMPTY; - if (ip->i_entries > 2 || inode->i_nlink > 2) - goto out_rgrp; - } - - error = gfs2_glock_nq(ghs + 2); /* rgrp */ - if (error) - goto out_rgrp; - - error = gfs2_unlink_ok(dip, &dentry->d_name, ip); - if (error) - goto out_gunlock; - - error = gfs2_trans_begin(sdp, 2*RES_DINODE + 3*RES_LEAF + RES_RG_BIT, 0); - if (error) - goto out_gunlock; - - error = gfs2_meta_inode_buffer(ip, &bh); - if (error) - goto out_end_trans; - - error = gfs2_unlink_inode(dip, dentry, bh); - brelse(bh); - -out_end_trans: - gfs2_trans_end(sdp); -out_gunlock: - gfs2_glock_dq(ghs + 2); -out_rgrp: - gfs2_holder_uninit(ghs + 2); - gfs2_glock_dq(ghs + 1); -out_child: - gfs2_holder_uninit(ghs + 1); - gfs2_glock_dq(ghs); -out_parent: - gfs2_holder_uninit(ghs); - gfs2_glock_dq_uninit(&ri_gh); - return error; -} - -/** - * gfs2_symlink - Create a symlink - * @dir: The directory to create the symlink in - * @dentry: The dentry to put the symlink in - * @symname: The thing which the link points to - * - * Returns: errno - */ - -static int gfs2_symlink(struct inode *dir, struct dentry *dentry, - const char *symname) -{ - struct gfs2_inode *dip = GFS2_I(dir), *ip; - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct gfs2_holder ghs[2]; - struct inode *inode; - struct buffer_head *dibh; - int size; - int error; - - /* Must be stuffed with a null terminator for gfs2_follow_link() */ - size = strlen(symname); - if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode) - 1) - return -ENAMETOOLONG; - - gfs2_holder_init(dip->i_gl, 0, 0, ghs); - - inode = gfs2_createi(ghs, &dentry->d_name, S_IFLNK | S_IRWXUGO, 0); - if (IS_ERR(inode)) { - gfs2_holder_uninit(ghs); - return PTR_ERR(inode); - } - - ip = ghs[1].gh_gl->gl_object; - - i_size_write(inode, size); - - error = gfs2_meta_inode_buffer(ip, &dibh); - - if (!gfs2_assert_withdraw(sdp, !error)) { - gfs2_dinode_out(ip, dibh->b_data); - memcpy(dibh->b_data + sizeof(struct gfs2_dinode), symname, - size); - brelse(dibh); - } - - gfs2_trans_end(sdp); - if (dip->i_alloc->al_rgd) - gfs2_inplace_release(dip); - gfs2_quota_unlock(dip); - gfs2_alloc_put(dip); - - gfs2_glock_dq_uninit_m(2, ghs); - - d_instantiate(dentry, inode); - mark_inode_dirty(inode); - - return 0; -} - -/** - * gfs2_mkdir - Make a directory - * @dir: The parent directory of the new one - * @dentry: The dentry of the new directory - * @mode: The mode of the new directory - * - * Returns: errno - */ - -static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, int mode) -{ - struct gfs2_inode *dip = GFS2_I(dir), *ip; - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct gfs2_holder ghs[2]; - struct inode *inode; - struct buffer_head *dibh; - int error; - - gfs2_holder_init(dip->i_gl, 0, 0, ghs); - - inode = gfs2_createi(ghs, &dentry->d_name, S_IFDIR | mode, 0); - if (IS_ERR(inode)) { - gfs2_holder_uninit(ghs); - return PTR_ERR(inode); - } - - ip = ghs[1].gh_gl->gl_object; - - ip->i_inode.i_nlink = 2; - i_size_write(inode, sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)); - ip->i_diskflags |= GFS2_DIF_JDATA; - ip->i_entries = 2; - - error = gfs2_meta_inode_buffer(ip, &dibh); - - 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); - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_qstr2dirent(&gfs2_qdot, GFS2_DIRENT_SIZE(gfs2_qdot.len), dent); - dent->de_inum = di->di_num; /* already GFS2 endian */ - dent->de_type = cpu_to_be16(DT_DIR); - di->di_entries = cpu_to_be32(1); - - dent = (struct gfs2_dirent *)((char*)dent + GFS2_DIRENT_SIZE(1)); - gfs2_qstr2dirent(&gfs2_qdotdot, dibh->b_size - GFS2_DIRENT_SIZE(1) - sizeof(struct gfs2_dinode), dent); - - gfs2_inum_out(dip, dent); - dent->de_type = cpu_to_be16(DT_DIR); - - gfs2_dinode_out(ip, di); - - brelse(dibh); - } - - gfs2_trans_end(sdp); - if (dip->i_alloc->al_rgd) - gfs2_inplace_release(dip); - gfs2_quota_unlock(dip); - gfs2_alloc_put(dip); - - gfs2_glock_dq_uninit_m(2, ghs); - - d_instantiate(dentry, inode); - mark_inode_dirty(inode); - - return 0; -} - -/** - * gfs2_mknod - Make a special file - * @dir: The directory in which the special file will reside - * @dentry: The dentry of the special file - * @mode: The mode of the special file - * @rdev: The device specification of the special file - * - */ - -static int gfs2_mknod(struct inode *dir, struct dentry *dentry, int mode, - dev_t dev) -{ - struct gfs2_inode *dip = GFS2_I(dir); - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct gfs2_holder ghs[2]; - struct inode *inode; - - gfs2_holder_init(dip->i_gl, 0, 0, ghs); - - inode = gfs2_createi(ghs, &dentry->d_name, mode, dev); - if (IS_ERR(inode)) { - gfs2_holder_uninit(ghs); - return PTR_ERR(inode); - } - - gfs2_trans_end(sdp); - if (dip->i_alloc->al_rgd) - gfs2_inplace_release(dip); - gfs2_quota_unlock(dip); - gfs2_alloc_put(dip); - - gfs2_glock_dq_uninit_m(2, ghs); - - d_instantiate(dentry, inode); - mark_inode_dirty(inode); - - return 0; -} - -/* - * gfs2_ok_to_move - check if it's ok to move a directory to another directory - * @this: move this - * @to: to here - * - * Follow @to back to the root and make sure we don't encounter @this - * Assumes we already hold the rename lock. - * - * Returns: errno - */ - -static int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to) -{ - struct inode *dir = &to->i_inode; - struct super_block *sb = dir->i_sb; - struct inode *tmp; - int error = 0; - - igrab(dir); - - for (;;) { - if (dir == &this->i_inode) { - error = -EINVAL; - break; - } - if (dir == sb->s_root->d_inode) { - error = 0; - break; - } - - tmp = gfs2_lookupi(dir, &gfs2_qdotdot, 1); - if (IS_ERR(tmp)) { - error = PTR_ERR(tmp); - break; - } - - iput(dir); - dir = tmp; - } - - iput(dir); - - return error; -} - -/** - * gfs2_rename - Rename a file - * @odir: Parent directory of old file name - * @odentry: The old dentry of the file - * @ndir: Parent directory of new file name - * @ndentry: The new dentry of the file - * - * Returns: errno - */ - -static int gfs2_rename(struct inode *odir, struct dentry *odentry, - struct inode *ndir, struct dentry *ndentry) -{ - struct gfs2_inode *odip = GFS2_I(odir); - struct gfs2_inode *ndip = GFS2_I(ndir); - struct gfs2_inode *ip = GFS2_I(odentry->d_inode); - struct gfs2_inode *nip = NULL; - struct gfs2_sbd *sdp = GFS2_SB(odir); - struct gfs2_holder ghs[5], r_gh = { .gh_gl = NULL, }, ri_gh; - struct gfs2_rgrpd *nrgd; - unsigned int num_gh; - int dir_rename = 0; - int alloc_required = 0; - unsigned int x; - int error; - - if (ndentry->d_inode) { - nip = GFS2_I(ndentry->d_inode); - if (ip == nip) - return 0; - } - - error = gfs2_rindex_hold(sdp, &ri_gh); - if (error) - return error; - - if (odip != ndip) { - error = gfs2_glock_nq_init(sdp->sd_rename_gl, LM_ST_EXCLUSIVE, - 0, &r_gh); - if (error) - goto out; - - if (S_ISDIR(ip->i_inode.i_mode)) { - dir_rename = 1; - /* don't move a dirctory into it's subdir */ - error = gfs2_ok_to_move(ip, ndip); - if (error) - goto out_gunlock_r; - } - } - - num_gh = 1; - gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); - if (odip != ndip) { - gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); - num_gh++; - } - gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); - num_gh++; - - if (nip) { - gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); - num_gh++; - /* grab the resource lock for unlink flag twiddling - * this is the case of the target file already existing - * so we unlink before doing the rename - */ - nrgd = gfs2_blk2rgrpd(sdp, nip->i_no_addr); - if (nrgd) - gfs2_holder_init(nrgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh++); - } - - for (x = 0; x < num_gh; x++) { - error = gfs2_glock_nq(ghs + x); - if (error) - goto out_gunlock; - } - - error = -ENOENT; - if (ip->i_inode.i_nlink == 0) - goto out_gunlock; - - /* Check out the old directory */ - - error = gfs2_unlink_ok(odip, &odentry->d_name, ip); - if (error) - goto out_gunlock; - - /* Check out the new directory */ - - if (nip) { - error = gfs2_unlink_ok(ndip, &ndentry->d_name, nip); - if (error) - goto out_gunlock; - - if (nip->i_inode.i_nlink == 0) { - error = -EAGAIN; - goto out_gunlock; - } - - if (S_ISDIR(nip->i_inode.i_mode)) { - if (nip->i_entries < 2) { - gfs2_consist_inode(nip); - error = -EIO; - goto out_gunlock; - } - if (nip->i_entries > 2) { - error = -ENOTEMPTY; - goto out_gunlock; - } - } - } else { - error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC, 0); - if (error) - goto out_gunlock; - - error = gfs2_dir_check(ndir, &ndentry->d_name, NULL); - switch (error) { - case -ENOENT: - error = 0; - break; - case 0: - error = -EEXIST; - default: - goto out_gunlock; - }; - - if (odip != ndip) { - if (!ndip->i_inode.i_nlink) { - error = -ENOENT; - goto out_gunlock; - } - if (ndip->i_entries == (u32)-1) { - error = -EFBIG; - goto out_gunlock; - } - if (S_ISDIR(ip->i_inode.i_mode) && - ndip->i_inode.i_nlink == (u32)-1) { - error = -EMLINK; - goto out_gunlock; - } - } - } - - /* Check out the dir to be renamed */ - - if (dir_rename) { - error = gfs2_permission(odentry->d_inode, MAY_WRITE, 0); - if (error) - goto out_gunlock; - } - - if (nip == NULL) - alloc_required = gfs2_diradd_alloc_required(ndir, &ndentry->d_name); - error = alloc_required; - if (error < 0) - goto out_gunlock; - error = 0; - - if (alloc_required) { - struct gfs2_alloc *al = gfs2_alloc_get(ndip); - if (!al) { - error = -ENOMEM; - goto out_gunlock; - } - - error = gfs2_quota_lock_check(ndip); - if (error) - goto out_alloc; - - al->al_requested = sdp->sd_max_dirres; - - error = gfs2_inplace_reserve_ri(ndip); - if (error) - goto out_gunlock_q; - - error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + - gfs2_rg_blocks(al) + - 4 * RES_DINODE + 4 * RES_LEAF + - RES_STATFS + RES_QUOTA + 4, 0); - if (error) - goto out_ipreserv; - } else { - error = gfs2_trans_begin(sdp, 4 * RES_DINODE + - 5 * RES_LEAF + 4, 0); - if (error) - goto out_gunlock; - } - - /* Remove the target file, if it exists */ - - if (nip) { - struct buffer_head *bh; - error = gfs2_meta_inode_buffer(nip, &bh); - if (error) - goto out_end_trans; - error = gfs2_unlink_inode(ndip, ndentry, bh); - brelse(bh); - } - - if (dir_rename) { - error = gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR); - if (error) - goto out_end_trans; - } else { - struct buffer_head *dibh; - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto out_end_trans; - ip->i_inode.i_ctime = CURRENT_TIME; - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(ip, dibh->b_data); - brelse(dibh); - } - - error = gfs2_dir_del(odip, odentry); - if (error) - goto out_end_trans; - - error = gfs2_dir_add(ndir, &ndentry->d_name, ip); - if (error) - goto out_end_trans; - -out_end_trans: - gfs2_trans_end(sdp); -out_ipreserv: - if (alloc_required) - gfs2_inplace_release(ndip); -out_gunlock_q: - if (alloc_required) - gfs2_quota_unlock(ndip); -out_alloc: - if (alloc_required) - gfs2_alloc_put(ndip); -out_gunlock: - while (x--) { - gfs2_glock_dq(ghs + x); - gfs2_holder_uninit(ghs + x); - } -out_gunlock_r: - if (r_gh.gh_gl) - gfs2_glock_dq_uninit(&r_gh); -out: - gfs2_glock_dq_uninit(&ri_gh); - return error; -} - -/** - * gfs2_follow_link - Follow a symbolic link - * @dentry: The dentry of the link - * @nd: Data that we pass to vfs_follow_link() - * - * This can handle symlinks of any size. - * - * Returns: 0 on success or error code - */ - -static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct gfs2_inode *ip = GFS2_I(dentry->d_inode); - struct gfs2_holder i_gh; - struct buffer_head *dibh; - unsigned int x, size; - char *buf; - int error; - - gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh); - error = gfs2_glock_nq(&i_gh); - if (error) { - gfs2_holder_uninit(&i_gh); - nd_set_link(nd, ERR_PTR(error)); - return NULL; - } - - size = (unsigned int)i_size_read(&ip->i_inode); - if (size == 0) { - gfs2_consist_inode(ip); - buf = ERR_PTR(-EIO); - goto out; - } - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) { - buf = ERR_PTR(error); - goto out; - } - - x = size + 1; - buf = kmalloc(x, GFP_NOFS); - if (!buf) - buf = ERR_PTR(-ENOMEM); - else - memcpy(buf, dibh->b_data + sizeof(struct gfs2_dinode), x); - brelse(dibh); -out: - gfs2_glock_dq_uninit(&i_gh); - nd_set_link(nd, buf); - return NULL; -} - -static void gfs2_put_link(struct dentry *dentry, struct nameidata *nd, void *p) -{ - char *s = nd_get_link(nd); - if (!IS_ERR(s)) - kfree(s); -} - -/** - * gfs2_permission - - * @inode: The inode - * @mask: The mask to be tested - * @flags: Indicates whether this is an RCU path walk or not - * - * This may be called from the VFS directly, or from within GFS2 with the - * inode locked, so we look to see if the glock is already locked and only - * lock the glock if its not already been done. - * - * Returns: errno - */ - -int gfs2_permission(struct inode *inode, int mask, unsigned int flags) -{ - struct gfs2_inode *ip; - struct gfs2_holder i_gh; - int error; - int unlock = 0; - - - ip = GFS2_I(inode); - if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) { - if (flags & IPERM_FLAG_RCU) - return -ECHILD; - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh); - if (error) - return error; - unlock = 1; - } - - if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) - error = -EACCES; - else - error = generic_permission(inode, mask, flags, gfs2_check_acl); - if (unlock) - gfs2_glock_dq_uninit(&i_gh); - - return error; -} - -static int __gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) -{ - struct inode *inode = &ip->i_inode; - struct buffer_head *dibh; - int error; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - return error; - - setattr_copy(inode, attr); - mark_inode_dirty(inode); - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(ip, dibh->b_data); - brelse(dibh); - return 0; -} - -/** - * gfs2_setattr_simple - - * @ip: - * @attr: - * - * Returns: errno - */ - -int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) -{ - int error; - - if (current->journal_info) - return __gfs2_setattr_simple(ip, attr); - - error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), RES_DINODE, 0); - if (error) - return error; - - error = __gfs2_setattr_simple(ip, attr); - gfs2_trans_end(GFS2_SB(&ip->i_inode)); - return error; -} - -static int setattr_chown(struct inode *inode, struct iattr *attr) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - u32 ouid, ogid, nuid, ngid; - int error; - - ouid = inode->i_uid; - ogid = inode->i_gid; - nuid = attr->ia_uid; - ngid = attr->ia_gid; - - if (!(attr->ia_valid & ATTR_UID) || ouid == nuid) - ouid = nuid = NO_QUOTA_CHANGE; - if (!(attr->ia_valid & ATTR_GID) || ogid == ngid) - ogid = ngid = NO_QUOTA_CHANGE; - - if (!gfs2_alloc_get(ip)) - return -ENOMEM; - - error = gfs2_quota_lock(ip, nuid, ngid); - if (error) - goto out_alloc; - - if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) { - error = gfs2_quota_check(ip, nuid, ngid); - if (error) - goto out_gunlock_q; - } - - error = gfs2_trans_begin(sdp, RES_DINODE + 2 * RES_QUOTA, 0); - if (error) - goto out_gunlock_q; - - error = gfs2_setattr_simple(ip, attr); - if (error) - goto out_end_trans; - - if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) { - u64 blocks = gfs2_get_inode_blocks(&ip->i_inode); - gfs2_quota_change(ip, -blocks, ouid, ogid); - gfs2_quota_change(ip, blocks, nuid, ngid); - } - -out_end_trans: - gfs2_trans_end(sdp); -out_gunlock_q: - gfs2_quota_unlock(ip); -out_alloc: - gfs2_alloc_put(ip); - return error; -} - -/** - * gfs2_setattr - Change attributes on an inode - * @dentry: The dentry which is changing - * @attr: The structure describing the change - * - * The VFS layer wants to change one or more of an inodes attributes. Write - * that change out to disk. - * - * Returns: errno - */ - -static int gfs2_setattr(struct dentry *dentry, struct iattr *attr) -{ - struct inode *inode = dentry->d_inode; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder i_gh; - int error; - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh); - if (error) - return error; - - error = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto out; - - error = inode_change_ok(inode, attr); - if (error) - goto out; - - if (attr->ia_valid & ATTR_SIZE) - error = gfs2_setattr_size(inode, attr->ia_size); - else if (attr->ia_valid & (ATTR_UID | ATTR_GID)) - error = setattr_chown(inode, attr); - else if ((attr->ia_valid & ATTR_MODE) && IS_POSIXACL(inode)) - error = gfs2_acl_chmod(ip, attr); - else - error = gfs2_setattr_simple(ip, attr); - -out: - gfs2_glock_dq_uninit(&i_gh); - if (!error) - mark_inode_dirty(inode); - return error; -} - -/** - * gfs2_getattr - Read out an inode's attributes - * @mnt: The vfsmount the inode is being accessed from - * @dentry: The dentry to stat - * @stat: The inode's stats - * - * This may be called from the VFS directly, or from within GFS2 with the - * inode locked, so we look to see if the glock is already locked and only - * lock the glock if its not already been done. Note that its the NFS - * readdirplus operation which causes this to be called (from filldir) - * with the glock already held. - * - * Returns: errno - */ - -static int gfs2_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) -{ - struct inode *inode = dentry->d_inode; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder gh; - int error; - int unlock = 0; - - if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) { - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh); - if (error) - return error; - unlock = 1; - } - - generic_fillattr(inode, stat); - if (unlock) - gfs2_glock_dq_uninit(&gh); - - return 0; -} - -static int gfs2_setxattr(struct dentry *dentry, const char *name, - const void *data, size_t size, int flags) -{ - struct inode *inode = dentry->d_inode; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder gh; - int ret; - - gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh); - ret = gfs2_glock_nq(&gh); - if (ret == 0) { - ret = generic_setxattr(dentry, name, data, size, flags); - gfs2_glock_dq(&gh); - } - gfs2_holder_uninit(&gh); - return ret; -} - -static ssize_t gfs2_getxattr(struct dentry *dentry, const char *name, - void *data, size_t size) -{ - struct inode *inode = dentry->d_inode; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder gh; - int ret; - - gfs2_holder_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh); - ret = gfs2_glock_nq(&gh); - if (ret == 0) { - ret = generic_getxattr(dentry, name, data, size); - gfs2_glock_dq(&gh); - } - gfs2_holder_uninit(&gh); - return ret; -} - -static int gfs2_removexattr(struct dentry *dentry, const char *name) -{ - struct inode *inode = dentry->d_inode; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder gh; - int ret; - - gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh); - ret = gfs2_glock_nq(&gh); - if (ret == 0) { - ret = generic_removexattr(dentry, name); - gfs2_glock_dq(&gh); - } - gfs2_holder_uninit(&gh); - return ret; -} - -static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, - u64 start, u64 len) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder gh; - int ret; - - ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC); - if (ret) - return ret; - - mutex_lock(&inode->i_mutex); - - ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &gh); - if (ret) - goto out; - - if (gfs2_is_stuffed(ip)) { - u64 phys = ip->i_no_addr << inode->i_blkbits; - u64 size = i_size_read(inode); - u32 flags = FIEMAP_EXTENT_LAST|FIEMAP_EXTENT_NOT_ALIGNED| - FIEMAP_EXTENT_DATA_INLINE; - phys += sizeof(struct gfs2_dinode); - phys += start; - if (start + len > size) - len = size - start; - if (start < size) - ret = fiemap_fill_next_extent(fieinfo, start, phys, - len, flags); - if (ret == 1) - ret = 0; - } else { - ret = __generic_block_fiemap(inode, fieinfo, start, len, - gfs2_block_map); - } - - gfs2_glock_dq_uninit(&gh); -out: - mutex_unlock(&inode->i_mutex); - return ret; -} - -const struct inode_operations gfs2_file_iops = { - .permission = gfs2_permission, - .setattr = gfs2_setattr, - .getattr = gfs2_getattr, - .setxattr = gfs2_setxattr, - .getxattr = gfs2_getxattr, - .listxattr = gfs2_listxattr, - .removexattr = gfs2_removexattr, - .fiemap = gfs2_fiemap, -}; - -const struct inode_operations gfs2_dir_iops = { - .create = gfs2_create, - .lookup = gfs2_lookup, - .link = gfs2_link, - .unlink = gfs2_unlink, - .symlink = gfs2_symlink, - .mkdir = gfs2_mkdir, - .rmdir = gfs2_unlink, - .mknod = gfs2_mknod, - .rename = gfs2_rename, - .permission = gfs2_permission, - .setattr = gfs2_setattr, - .getattr = gfs2_getattr, - .setxattr = gfs2_setxattr, - .getxattr = gfs2_getxattr, - .listxattr = gfs2_listxattr, - .removexattr = gfs2_removexattr, - .fiemap = gfs2_fiemap, -}; - -const struct inode_operations gfs2_symlink_iops = { - .readlink = generic_readlink, - .follow_link = gfs2_follow_link, - .put_link = gfs2_put_link, - .permission = gfs2_permission, - .setattr = gfs2_setattr, - .getattr = gfs2_getattr, - .setxattr = gfs2_setxattr, - .getxattr = gfs2_getxattr, - .listxattr = gfs2_listxattr, - .removexattr = gfs2_removexattr, - .fiemap = gfs2_fiemap, -}; - -- cgit v1.2.3