diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index f7a31374ff82e929daeae8fddf124e3b68698355..410265151ad1a65c568c64e9a6476668a125b8c6 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -1669,8 +1669,9 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name, * Returns: 0 on success, error code on failure */ -int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name) +int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry) { + const struct qstr *name = &dentry->d_name; struct gfs2_dirent *dent, *prev = NULL; struct buffer_head *bh; int error; @@ -1711,6 +1712,8 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name) gfs2_trans_add_bh(dip->i_gl, bh, 1); dip->i_entries--; dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME; + if (S_ISDIR(dentry->d_inode->i_mode)) + drop_nlink(&dip->i_inode); gfs2_dinode_out(dip, bh->b_data); brelse(bh); mark_inode_dirty(&dip->i_inode); diff --git a/fs/gfs2/dir.h b/fs/gfs2/dir.h index a98f644bd3df33596cf2382767b89ca0cdd08161..66831f1d23eae2e9fa4dbcf33a0de70d18b70d5a 100644 --- a/fs/gfs2/dir.h +++ b/fs/gfs2/dir.h @@ -23,7 +23,7 @@ extern int gfs2_dir_check(struct inode *dir, const struct qstr *filename, const struct gfs2_inode *ip); extern int gfs2_dir_add(struct inode *inode, const struct qstr *filename, const struct gfs2_inode *ip, unsigned int type); -extern int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *filename); +extern int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry); extern int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, filldir_t filldir); extern int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index acb6f69b02ed9cc6bdb4aa3949cbc7047c4ec867..765da06c8f555e2baba825794e8e08f21c24e251 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,