@@ -42,6 +42,7 @@
#include "xfs_quota.h"
#include "xfs_trans_space.h"
#include "xfs_trace.h"
+#include "xfs_qm.h"
/*
* xfs_attr.c
@@ -499,6 +500,81 @@ xfs_attr_set(
return error;
}
+int
+xfs_attr_remove_parent(
+ struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ struct xfs_parent_name_rec *rec,
+ int reclen,
+ struct xfs_defer_ops *dfops,
+ xfs_fsblock_t *firstblock)
+{
+ int flags = ATTR_PARENT;
+ char *name = (char *)rec;
+ struct xfs_mount *mp = dp->i_mount;
+ struct xfs_da_args args;
+ int error;
+ int rsvd = 0;
+
+ XFS_STATS_INC(mp, xs_attr_remove);
+
+ if (XFS_FORCED_SHUTDOWN(dp->i_mount))
+ return -EIO;
+
+ error = xfs_attr_args_init(&args, dp, name, flags);
+ if (error)
+ return error;
+
+ args.firstblock = firstblock;
+ args.dfops = dfops;
+ args.name = (char *)rec;
+ args.namelen = reclen;
+
+ /*
+ * we have no control over the attribute names that userspace passes us
+ * to remove, so we have to allow the name lookup prior to attribute
+ * removal to fail.
+ */
+ args.op_flags = XFS_DA_OP_OKNOENT;
+
+ if (xfs_qm_need_dqattach(dp)) {
+ error = xfs_qm_dqattach_locked(dp, 0);
+ if (error)
+ return error;
+ }
+
+ /*
+ * Root fork attributes can use reserved data blocks for this
+ * operation if necessary
+ */
+ if (flags & (ATTR_ROOT | ATTR_PARENT))
+ rsvd = XFS_TRANS_RESERVE;
+
+ error = xfs_trans_alloc(mp, &M_RES(mp)->tr_attrrm,
+ XFS_ATTRRM_SPACE_RES(mp), 0, rsvd, &args.trans);
+ if (error)
+ return error;
+
+ if (!xfs_inode_hasattr(dp))
+ error = -ENOATTR;
+ else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
+ ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
+ error = xfs_attr_shortform_remove(&args);
+ } else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
+ error = xfs_attr_leaf_removename(&args);
+ else
+ error = xfs_attr_node_removename(&args);
+
+ /*
+ * If this is a synchronous mount, make sure that the
+ * transaction goes to disk before returning to the user.
+ */
+ if (!error && mp->m_flags & XFS_MOUNT_WSYNC)
+ xfs_trans_set_sync(args.trans);
+
+ return error;
+}
+
/*
* Generic handler routine to remove a name from an attribute list.
* Transitions attribute list from Btree to shortform as necessary.
@@ -139,3 +139,25 @@ xfs_parent_add(
return xfs_parent_add_nrec(tp, child, &nrec, dfops, firstblock);
}
+
+/*
+ * Remove a parent record from a child inode.
+ */
+int
+xfs_parent_remove(
+ struct xfs_trans *tp,
+ struct xfs_inode *parent,
+ struct xfs_inode *child,
+ xfs_dir2_dataptr_t diroffset,
+ struct xfs_defer_ops *dfops,
+ xfs_fsblock_t *firstblock)
+{
+ struct xfs_parent_name_rec rec;
+
+ rec.p_ino = cpu_to_be64(parent->i_ino);
+ rec.p_gen = cpu_to_be32(VFS_I(parent)->i_generation);
+ rec.p_diroffset = cpu_to_be32(diroffset);
+
+ return xfs_attr_remove_parent(tp, child, &rec, sizeof(rec),
+ dfops, firstblock);
+}
@@ -177,4 +177,11 @@ int xfs_attr_set_parent(struct xfs_trans *tp, struct xfs_inode *ip,
const char *value, int valuelen,
struct xfs_defer_ops *dfops, xfs_fsblock_t *firstblock);
+int xfs_parent_remove(struct xfs_trans *tp, struct xfs_inode *parent,
+ struct xfs_inode *child, xfs_dir2_dataptr_t diroffset,
+ struct xfs_defer_ops *dfops, xfs_fsblock_t *firstblock);
+int xfs_attr_remove_parent(struct xfs_trans *tp, struct xfs_inode *ip,
+ struct xfs_parent_name_rec *rec, int reclen,
+ struct xfs_defer_ops *dfops, xfs_fsblock_t *firstblock);
+
#endif /* __XFS_ATTR_H__ */
@@ -2594,6 +2594,7 @@ xfs_remove(
struct xfs_defer_ops dfops;
xfs_fsblock_t first_block;
uint resblks;
+ uint32_t dir_offset;
trace_xfs_remove(dp, name);
@@ -2674,12 +2675,19 @@ xfs_remove(
xfs_defer_init(&dfops, &first_block);
error = xfs_dir_removename(tp, dp, name, ip->i_ino, &first_block,
- &dfops, resblks, NULL);
+ &dfops, resblks, &dir_offset);
if (error) {
ASSERT(error != -ENOENT);
goto out_bmap_cancel;
}
+ if (xfs_sb_version_hasparent(&mp->m_sb)) {
+ error = xfs_parent_remove(tp, dp, ip, dir_offset, &dfops,
+ &first_block);
+ if (error)
+ goto out_bmap_cancel;
+ }
+
/*
* If this is a synchronous mount, make sure that the
* remove transaction goes to disk before returning to
@@ -305,7 +305,7 @@ xfs_qm_dqattach_one(
return 0;
}
-static bool
+bool
xfs_qm_need_dqattach(
struct xfs_inode *ip)
{
@@ -176,6 +176,7 @@ extern int xfs_qm_scall_setqlim(struct xfs_mount *, xfs_dqid_t, uint,
struct qc_dqblk *);
extern int xfs_qm_scall_quotaon(struct xfs_mount *, uint);
extern int xfs_qm_scall_quotaoff(struct xfs_mount *, uint);
+extern bool xfs_qm_need_dqattach(struct xfs_inode *ip);
static inline struct xfs_def_quota *
xfs_get_defquota(struct xfs_dquot *dqp, struct xfs_quotainfo *qi)