diff options
Diffstat (limited to 'fs/xfs/xfs_dfrag.c')
| -rw-r--r-- | fs/xfs/xfs_dfrag.c | 106 |
1 files changed, 90 insertions, 16 deletions
diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c index d1483a4f71b8..84ca1cf16a1e 100644 --- a/fs/xfs/xfs_dfrag.c +++ b/fs/xfs/xfs_dfrag.c | |||
| @@ -114,10 +114,82 @@ xfs_swapext( | |||
| 114 | return error; | 114 | return error; |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | /* | ||
| 118 | * We need to check that the format of the data fork in the temporary inode is | ||
| 119 | * valid for the target inode before doing the swap. This is not a problem with | ||
| 120 | * attr1 because of the fixed fork offset, but attr2 has a dynamically sized | ||
| 121 | * data fork depending on the space the attribute fork is taking so we can get | ||
| 122 | * invalid formats on the target inode. | ||
| 123 | * | ||
| 124 | * E.g. target has space for 7 extents in extent format, temp inode only has | ||
| 125 | * space for 6. If we defragment down to 7 extents, then the tmp format is a | ||
| 126 | * btree, but when swapped it needs to be in extent format. Hence we can't just | ||
| 127 | * blindly swap data forks on attr2 filesystems. | ||
| 128 | * | ||
| 129 | * Note that we check the swap in both directions so that we don't end up with | ||
| 130 | * a corrupt temporary inode, either. | ||
| 131 | * | ||
| 132 | * Note that fixing the way xfs_fsr sets up the attribute fork in the source | ||
| 133 | * inode will prevent this situation from occurring, so all we do here is | ||
| 134 | * reject and log the attempt. basically we are putting the responsibility on | ||
| 135 | * userspace to get this right. | ||
| 136 | */ | ||
| 137 | static int | ||
| 138 | xfs_swap_extents_check_format( | ||
| 139 | xfs_inode_t *ip, /* target inode */ | ||
| 140 | xfs_inode_t *tip) /* tmp inode */ | ||
| 141 | { | ||
| 142 | |||
| 143 | /* Should never get a local format */ | ||
| 144 | if (ip->i_d.di_format == XFS_DINODE_FMT_LOCAL || | ||
| 145 | tip->i_d.di_format == XFS_DINODE_FMT_LOCAL) | ||
| 146 | return EINVAL; | ||
| 147 | |||
| 148 | /* | ||
| 149 | * if the target inode has less extents that then temporary inode then | ||
| 150 | * why did userspace call us? | ||
| 151 | */ | ||
| 152 | if (ip->i_d.di_nextents < tip->i_d.di_nextents) | ||
| 153 | return EINVAL; | ||
| 154 | |||
| 155 | /* | ||
| 156 | * if the target inode is in extent form and the temp inode is in btree | ||
| 157 | * form then we will end up with the target inode in the wrong format | ||
| 158 | * as we already know there are less extents in the temp inode. | ||
| 159 | */ | ||
| 160 | if (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS && | ||
| 161 | tip->i_d.di_format == XFS_DINODE_FMT_BTREE) | ||
| 162 | return EINVAL; | ||
| 163 | |||
| 164 | /* Check temp in extent form to max in target */ | ||
| 165 | if (tip->i_d.di_format == XFS_DINODE_FMT_EXTENTS && | ||
| 166 | XFS_IFORK_NEXTENTS(tip, XFS_DATA_FORK) > ip->i_df.if_ext_max) | ||
| 167 | return EINVAL; | ||
| 168 | |||
| 169 | /* Check target in extent form to max in temp */ | ||
| 170 | if (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS && | ||
| 171 | XFS_IFORK_NEXTENTS(ip, XFS_DATA_FORK) > tip->i_df.if_ext_max) | ||
| 172 | return EINVAL; | ||
| 173 | |||
| 174 | /* Check root block of temp in btree form to max in target */ | ||
| 175 | if (tip->i_d.di_format == XFS_DINODE_FMT_BTREE && | ||
| 176 | XFS_IFORK_BOFF(ip) && | ||
| 177 | tip->i_df.if_broot_bytes > XFS_IFORK_BOFF(ip)) | ||
| 178 | return EINVAL; | ||
| 179 | |||
| 180 | /* Check root block of target in btree form to max in temp */ | ||
| 181 | if (ip->i_d.di_format == XFS_DINODE_FMT_BTREE && | ||
| 182 | XFS_IFORK_BOFF(tip) && | ||
| 183 | ip->i_df.if_broot_bytes > XFS_IFORK_BOFF(tip)) | ||
| 184 | return EINVAL; | ||
| 185 | |||
| 186 | return 0; | ||
| 187 | } | ||
| 188 | |||
| 117 | int | 189 | int |
| 118 | xfs_swap_extents( | 190 | xfs_swap_extents( |
| 119 | xfs_inode_t *ip, | 191 | xfs_inode_t *ip, /* target inode */ |
| 120 | xfs_inode_t *tip, | 192 | xfs_inode_t *tip, /* tmp inode */ |
| 121 | xfs_swapext_t *sxp) | 193 | xfs_swapext_t *sxp) |
| 122 | { | 194 | { |
| 123 | xfs_mount_t *mp; | 195 | xfs_mount_t *mp; |
| @@ -161,13 +233,6 @@ xfs_swap_extents( | |||
| 161 | goto out_unlock; | 233 | goto out_unlock; |
| 162 | } | 234 | } |
| 163 | 235 | ||
| 164 | /* Should never get a local format */ | ||
| 165 | if (ip->i_d.di_format == XFS_DINODE_FMT_LOCAL || | ||
| 166 | tip->i_d.di_format == XFS_DINODE_FMT_LOCAL) { | ||
| 167 | error = XFS_ERROR(EINVAL); | ||
| 168 | goto out_unlock; | ||
| 169 | } | ||
| 170 | |||
| 171 | if (VN_CACHED(VFS_I(tip)) != 0) { | 236 | if (VN_CACHED(VFS_I(tip)) != 0) { |
| 172 | error = xfs_flushinval_pages(tip, 0, -1, | 237 | error = xfs_flushinval_pages(tip, 0, -1, |
| 173 | FI_REMAPF_LOCKED); | 238 | FI_REMAPF_LOCKED); |
| @@ -189,13 +254,12 @@ xfs_swap_extents( | |||
| 189 | goto out_unlock; | 254 | goto out_unlock; |
| 190 | } | 255 | } |
| 191 | 256 | ||
| 192 | /* | 257 | /* check inode formats now that data is flushed */ |
| 193 | * If the target has extended attributes, the tmp file | 258 | error = xfs_swap_extents_check_format(ip, tip); |
| 194 | * must also in order to ensure the correct data fork | 259 | if (error) { |
| 195 | * format. | 260 | xfs_fs_cmn_err(CE_NOTE, mp, |
| 196 | */ | 261 | "%s: inode 0x%llx format is incompatible for exchanging.", |
| 197 | if ( XFS_IFORK_Q(ip) != XFS_IFORK_Q(tip) ) { | 262 | __FILE__, ip->i_ino); |
| 198 | error = XFS_ERROR(EINVAL); | ||
| 199 | goto out_unlock; | 263 | goto out_unlock; |
| 200 | } | 264 | } |
| 201 | 265 | ||
| @@ -276,6 +340,16 @@ xfs_swap_extents( | |||
| 276 | *tifp = *tempifp; /* struct copy */ | 340 | *tifp = *tempifp; /* struct copy */ |
| 277 | 341 | ||
| 278 | /* | 342 | /* |
| 343 | * Fix the in-memory data fork values that are dependent on the fork | ||
| 344 | * offset in the inode. We can't assume they remain the same as attr2 | ||
| 345 | * has dynamic fork offsets. | ||
| 346 | */ | ||
| 347 | ifp->if_ext_max = XFS_IFORK_SIZE(ip, XFS_DATA_FORK) / | ||
| 348 | (uint)sizeof(xfs_bmbt_rec_t); | ||
| 349 | tifp->if_ext_max = XFS_IFORK_SIZE(tip, XFS_DATA_FORK) / | ||
| 350 | (uint)sizeof(xfs_bmbt_rec_t); | ||
| 351 | |||
| 352 | /* | ||
| 279 | * Fix the on-disk inode values | 353 | * Fix the on-disk inode values |
| 280 | */ | 354 | */ |
| 281 | tmp = (__uint64_t)ip->i_d.di_nblocks; | 355 | tmp = (__uint64_t)ip->i_d.di_nblocks; |
