@@ -27,6 +27,7 @@
#include "xfs_xattr.h"
#include "xfs_parent.h"
#include "xfs_trans_space.h"
+#include "xfs_health.h"
struct kmem_cache *xfs_parent_args_cache;
@@ -194,6 +195,44 @@ xfs_parent_addname(
return 0;
}
+/* Remove a parent pointer to reflect a dirent removal. */
+int
+xfs_parent_removename(
+ struct xfs_trans *tp,
+ struct xfs_parent_args *ppargs,
+ struct xfs_inode *dp,
+ const struct xfs_name *parent_name,
+ struct xfs_inode *child)
+{
+ struct xfs_da_args *args = &ppargs->args;
+
+ if (XFS_IS_CORRUPT(tp->t_mountp,
+ !xfs_parent_valuecheck(tp->t_mountp, parent_name->name,
+ parent_name->len)))
+ return -EFSCORRUPTED;
+
+ /*
+ * For regular attrs, removing an attr from a !hasattr inode is a nop.
+ * For parent pointers, we require that the pointer must exist if the
+ * caller wants us to remove the pointer.
+ */
+ if (XFS_IS_CORRUPT(child->i_mount, !xfs_inode_hasattr(child))) {
+ xfs_inode_mark_sick(child, XFS_SICK_INO_PARENT);
+ return -EFSCORRUPTED;
+ }
+
+ xfs_init_parent_name_rec(&ppargs->rec, dp, parent_name, child);
+ args->hashval = xfs_parent_hashname(dp, ppargs);
+
+ args->trans = tp;
+ args->dp = child;
+
+ xfs_init_parent_davalue(&ppargs->args, parent_name);
+
+ xfs_attr_defer_add(args, XFS_ATTRI_OP_FLAGS_REMOVE);
+ return 0;
+}
+
/* Free a parent pointer context object. */
void
xfs_parent_args_free(
@@ -79,6 +79,22 @@ xfs_parent_add(struct xfs_trans *tp, struct xfs_parent_args *ppargs,
return 0;
}
+int xfs_parent_removename(struct xfs_trans *tp, struct xfs_parent_args *ppargs,
+ struct xfs_inode *dp, const struct xfs_name *parent_name,
+ struct xfs_inode *child);
+
+/* Schedule a parent pointer removal. */
+static inline int
+xfs_parent_remove(struct xfs_trans *tp, struct xfs_parent_args *ppargs,
+ struct xfs_inode *dp, const struct xfs_name *parent_name,
+ struct xfs_inode *child)
+{
+ if (ppargs)
+ return xfs_parent_removename(tp, ppargs, dp, parent_name,
+ child);
+ return 0;
+}
+
void xfs_parent_args_free(struct xfs_mount *mp, struct xfs_parent_args *ppargs);
/* Finish a parent pointer update by freeing the context object. */
@@ -81,3 +81,16 @@ xfs_symlink_space_res(
return ret;
}
+
+unsigned int
+xfs_remove_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen)
+{
+ unsigned int ret = XFS_DIRREMOVE_SPACE_RES(mp);
+
+ if (xfs_has_parent(mp))
+ ret += xfs_parent_calc_space_res(mp, namelen);
+
+ return ret;
+}
@@ -91,8 +91,6 @@
XFS_DQUOT_CLUSTER_SIZE_FSB)
#define XFS_QM_QINOCREATE_SPACE_RES(mp) \
XFS_IALLOC_SPACE_RES(mp)
-#define XFS_REMOVE_SPACE_RES(mp) \
- XFS_DIRREMOVE_SPACE_RES(mp)
#define XFS_RENAME_SPACE_RES(mp,nl) \
(XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_IFREE_SPACE_RES(mp) \
@@ -106,5 +104,6 @@ unsigned int xfs_mkdir_space_res(struct xfs_mount *mp, unsigned int namelen);
unsigned int xfs_link_space_res(struct xfs_mount *mp, unsigned int namelen);
unsigned int xfs_symlink_space_res(struct xfs_mount *mp, unsigned int namelen,
unsigned int fsblocks);
+unsigned int xfs_remove_space_res(struct xfs_mount *mp, unsigned int namelen);
#endif /* __XFS_TRANS_SPACE_H__ */
@@ -2718,16 +2718,17 @@ xfs_iunpin_wait(
*/
int
xfs_remove(
- xfs_inode_t *dp,
+ struct xfs_inode *dp,
struct xfs_name *name,
- xfs_inode_t *ip)
+ struct xfs_inode *ip)
{
- xfs_mount_t *mp = dp->i_mount;
- xfs_trans_t *tp = NULL;
+ struct xfs_mount *mp = dp->i_mount;
+ struct xfs_trans *tp = NULL;
int is_dir = S_ISDIR(VFS_I(ip)->i_mode);
int dontcare;
int error = 0;
uint resblks;
+ struct xfs_parent_args *ppargs;
trace_xfs_remove(dp, name);
@@ -2744,6 +2745,10 @@ xfs_remove(
if (error)
goto std_return;
+ error = xfs_parent_start(mp, &ppargs);
+ if (error)
+ goto std_return;
+
/*
* We try to get the real space reservation first, allowing for
* directory btree deletion(s) implying possible bmap insert(s). If we
@@ -2755,12 +2760,12 @@ xfs_remove(
* the directory code can handle a reservationless update and we don't
* want to prevent a user from trying to free space by deleting things.
*/
- resblks = XFS_REMOVE_SPACE_RES(mp);
+ resblks = xfs_remove_space_res(mp, name->len);
error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, ip, &resblks,
&tp, &dontcare);
if (error) {
ASSERT(error != -ENOSPC);
- goto std_return;
+ goto out_parent;
}
/*
@@ -2820,6 +2825,11 @@ xfs_remove(
goto out_trans_cancel;
}
+ /* Remove parent pointer. */
+ error = xfs_parent_remove(tp, ppargs, dp, name, ip);
+ if (error)
+ goto out_trans_cancel;
+
/*
* Drop the link from dp to ip, and if ip was a directory, remove the
* '.' and '..' references since we freed the directory.
@@ -2843,6 +2853,7 @@ xfs_remove(
xfs_iunlock(ip, XFS_ILOCK_EXCL);
xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ xfs_parent_finish(mp, ppargs);
return 0;
out_trans_cancel:
@@ -2850,6 +2861,8 @@ xfs_remove(
out_unlock:
xfs_iunlock(ip, XFS_ILOCK_EXCL);
xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ out_parent:
+ xfs_parent_finish(mp, ppargs);
std_return:
return error;
}