|
@@ -308,6 +308,46 @@ xfs_reinit_inode(
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * If we are allocating a new inode, then check what was returned is
|
|
|
+ * actually a free, empty inode. If we are not allocating an inode,
|
|
|
+ * then check we didn't find a free inode.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * 0 if the inode free state matches the lookup context
|
|
|
+ * -ENOENT if the inode is free and we are not allocating
|
|
|
+ * -EFSCORRUPTED if there is any state mismatch at all
|
|
|
+ */
|
|
|
+static int
|
|
|
+xfs_iget_check_free_state(
|
|
|
+ struct xfs_inode *ip,
|
|
|
+ int flags)
|
|
|
+{
|
|
|
+ if (flags & XFS_IGET_CREATE) {
|
|
|
+ /* should be a free inode */
|
|
|
+ if (VFS_I(ip)->i_mode != 0) {
|
|
|
+ xfs_warn(ip->i_mount,
|
|
|
+"Corruption detected! Free inode 0x%llx not marked free! (mode 0x%x)",
|
|
|
+ ip->i_ino, VFS_I(ip)->i_mode);
|
|
|
+ return -EFSCORRUPTED;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ip->i_d.di_nblocks != 0) {
|
|
|
+ xfs_warn(ip->i_mount,
|
|
|
+"Corruption detected! Free inode 0x%llx has blocks allocated!",
|
|
|
+ ip->i_ino);
|
|
|
+ return -EFSCORRUPTED;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* should be an allocated inode */
|
|
|
+ if (VFS_I(ip)->i_mode == 0)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Check the validity of the inode we just found it the cache
|
|
|
*/
|
|
@@ -357,12 +397,12 @@ xfs_iget_cache_hit(
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * If lookup is racing with unlink return an error immediately.
|
|
|
+ * Check the inode free state is valid. This also detects lookup
|
|
|
+ * racing with unlinks.
|
|
|
*/
|
|
|
- if (VFS_I(ip)->i_mode == 0 && !(flags & XFS_IGET_CREATE)) {
|
|
|
- error = -ENOENT;
|
|
|
+ error = xfs_iget_check_free_state(ip, flags);
|
|
|
+ if (error)
|
|
|
goto out_error;
|
|
|
- }
|
|
|
|
|
|
/*
|
|
|
* If IRECLAIMABLE is set, we've torn down the VFS inode already.
|
|
@@ -485,29 +525,12 @@ xfs_iget_cache_miss(
|
|
|
|
|
|
|
|
|
/*
|
|
|
- * If we are allocating a new inode, then check what was returned is
|
|
|
- * actually a free, empty inode. If we are not allocating an inode,
|
|
|
- * the check we didn't find a free inode.
|
|
|
+ * Check the inode free state is valid. This also detects lookup
|
|
|
+ * racing with unlinks.
|
|
|
*/
|
|
|
- if (flags & XFS_IGET_CREATE) {
|
|
|
- if (VFS_I(ip)->i_mode != 0) {
|
|
|
- xfs_warn(mp,
|
|
|
-"Corruption detected! Free inode 0x%llx not marked free on disk",
|
|
|
- ino);
|
|
|
- error = -EFSCORRUPTED;
|
|
|
- goto out_destroy;
|
|
|
- }
|
|
|
- if (ip->i_d.di_nblocks != 0) {
|
|
|
- xfs_warn(mp,
|
|
|
-"Corruption detected! Free inode 0x%llx has blocks allocated!",
|
|
|
- ino);
|
|
|
- error = -EFSCORRUPTED;
|
|
|
- goto out_destroy;
|
|
|
- }
|
|
|
- } else if (VFS_I(ip)->i_mode == 0) {
|
|
|
- error = -ENOENT;
|
|
|
+ error = xfs_iget_check_free_state(ip, flags);
|
|
|
+ if (error)
|
|
|
goto out_destroy;
|
|
|
- }
|
|
|
|
|
|
/*
|
|
|
* Preload the radix tree so we can insert safely under the
|