diff options
-rw-r--r-- | fs/xfs/xfs_ialloc.c | 121 |
1 files changed, 78 insertions, 43 deletions
diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c index 9d884c127bb9..0c946c8e05da 100644 --- a/fs/xfs/xfs_ialloc.c +++ b/fs/xfs/xfs_ialloc.c | |||
@@ -1203,6 +1203,63 @@ error0: | |||
1203 | return error; | 1203 | return error; |
1204 | } | 1204 | } |
1205 | 1205 | ||
1206 | STATIC int | ||
1207 | xfs_imap_lookup( | ||
1208 | struct xfs_mount *mp, | ||
1209 | struct xfs_trans *tp, | ||
1210 | xfs_agnumber_t agno, | ||
1211 | xfs_agino_t agino, | ||
1212 | xfs_agblock_t agbno, | ||
1213 | xfs_agblock_t *chunk_agbno, | ||
1214 | xfs_agblock_t *offset_agbno, | ||
1215 | int flags) | ||
1216 | { | ||
1217 | struct xfs_inobt_rec_incore rec; | ||
1218 | struct xfs_btree_cur *cur; | ||
1219 | struct xfs_buf *agbp; | ||
1220 | xfs_agino_t startino; | ||
1221 | int error; | ||
1222 | int i; | ||
1223 | |||
1224 | error = xfs_ialloc_read_agi(mp, tp, agno, &agbp); | ||
1225 | if (error) { | ||
1226 | xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " | ||
1227 | "xfs_ialloc_read_agi() returned " | ||
1228 | "error %d, agno %d", | ||
1229 | error, agno); | ||
1230 | return error; | ||
1231 | } | ||
1232 | |||
1233 | /* | ||
1234 | * derive and lookup the exact inode record for the given agino. If the | ||
1235 | * record cannot be found, then it's an invalid inode number and we | ||
1236 | * should abort. | ||
1237 | */ | ||
1238 | cur = xfs_inobt_init_cursor(mp, tp, agbp, agno); | ||
1239 | startino = agino & ~(XFS_IALLOC_INODES(mp) - 1); | ||
1240 | error = xfs_inobt_lookup(cur, startino, XFS_LOOKUP_EQ, &i); | ||
1241 | if (!error) { | ||
1242 | if (i) | ||
1243 | error = xfs_inobt_get_rec(cur, &rec, &i); | ||
1244 | if (!error && i == 0) | ||
1245 | error = EINVAL; | ||
1246 | } | ||
1247 | |||
1248 | xfs_trans_brelse(tp, agbp); | ||
1249 | xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); | ||
1250 | if (error) | ||
1251 | return error; | ||
1252 | |||
1253 | /* for untrusted inodes check it is allocated first */ | ||
1254 | if ((flags & XFS_IGET_BULKSTAT) && | ||
1255 | (rec.ir_free & XFS_INOBT_MASK(agino - rec.ir_startino))) | ||
1256 | return EINVAL; | ||
1257 | |||
1258 | *chunk_agbno = XFS_AGINO_TO_AGBNO(mp, rec.ir_startino); | ||
1259 | *offset_agbno = agbno - *chunk_agbno; | ||
1260 | return 0; | ||
1261 | } | ||
1262 | |||
1206 | /* | 1263 | /* |
1207 | * Return the location of the inode in imap, for mapping it into a buffer. | 1264 | * Return the location of the inode in imap, for mapping it into a buffer. |
1208 | */ | 1265 | */ |
@@ -1263,6 +1320,23 @@ xfs_imap( | |||
1263 | return XFS_ERROR(EINVAL); | 1320 | return XFS_ERROR(EINVAL); |
1264 | } | 1321 | } |
1265 | 1322 | ||
1323 | blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog; | ||
1324 | |||
1325 | /* | ||
1326 | * For bulkstat and handle lookups, we have an untrusted inode number | ||
1327 | * that we have to verify is valid. We cannot do this just by reading | ||
1328 | * the inode buffer as it may have been unlinked and removed leaving | ||
1329 | * inodes in stale state on disk. Hence we have to do a btree lookup | ||
1330 | * in all cases where an untrusted inode number is passed. | ||
1331 | */ | ||
1332 | if (flags & XFS_IGET_BULKSTAT) { | ||
1333 | error = xfs_imap_lookup(mp, tp, agno, agino, agbno, | ||
1334 | &chunk_agbno, &offset_agbno, flags); | ||
1335 | if (error) | ||
1336 | return error; | ||
1337 | goto out_map; | ||
1338 | } | ||
1339 | |||
1266 | /* | 1340 | /* |
1267 | * If the inode cluster size is the same as the blocksize or | 1341 | * If the inode cluster size is the same as the blocksize or |
1268 | * smaller we get to the buffer by simple arithmetics. | 1342 | * smaller we get to the buffer by simple arithmetics. |
@@ -1277,10 +1351,8 @@ xfs_imap( | |||
1277 | return 0; | 1351 | return 0; |
1278 | } | 1352 | } |
1279 | 1353 | ||
1280 | blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog; | ||
1281 | |||
1282 | /* | 1354 | /* |
1283 | * If we get a block number passed from bulkstat we can use it to | 1355 | * If we get a block number passed we can use it to |
1284 | * find the buffer easily. | 1356 | * find the buffer easily. |
1285 | */ | 1357 | */ |
1286 | if (imap->im_blkno) { | 1358 | if (imap->im_blkno) { |
@@ -1304,50 +1376,13 @@ xfs_imap( | |||
1304 | offset_agbno = agbno & mp->m_inoalign_mask; | 1376 | offset_agbno = agbno & mp->m_inoalign_mask; |
1305 | chunk_agbno = agbno - offset_agbno; | 1377 | chunk_agbno = agbno - offset_agbno; |
1306 | } else { | 1378 | } else { |
1307 | xfs_btree_cur_t *cur; /* inode btree cursor */ | 1379 | error = xfs_imap_lookup(mp, tp, agno, agino, agbno, |
1308 | xfs_inobt_rec_incore_t chunk_rec; | 1380 | &chunk_agbno, &offset_agbno, flags); |
1309 | xfs_buf_t *agbp; /* agi buffer */ | ||
1310 | int i; /* temp state */ | ||
1311 | |||
1312 | error = xfs_ialloc_read_agi(mp, tp, agno, &agbp); | ||
1313 | if (error) { | ||
1314 | xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " | ||
1315 | "xfs_ialloc_read_agi() returned " | ||
1316 | "error %d, agno %d", | ||
1317 | error, agno); | ||
1318 | return error; | ||
1319 | } | ||
1320 | |||
1321 | cur = xfs_inobt_init_cursor(mp, tp, agbp, agno); | ||
1322 | error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_LE, &i); | ||
1323 | if (error) { | ||
1324 | xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " | ||
1325 | "xfs_inobt_lookup() failed"); | ||
1326 | goto error0; | ||
1327 | } | ||
1328 | |||
1329 | error = xfs_inobt_get_rec(cur, &chunk_rec, &i); | ||
1330 | if (error) { | ||
1331 | xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " | ||
1332 | "xfs_inobt_get_rec() failed"); | ||
1333 | goto error0; | ||
1334 | } | ||
1335 | if (i == 0) { | ||
1336 | #ifdef DEBUG | ||
1337 | xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " | ||
1338 | "xfs_inobt_get_rec() failed"); | ||
1339 | #endif /* DEBUG */ | ||
1340 | error = XFS_ERROR(EINVAL); | ||
1341 | } | ||
1342 | error0: | ||
1343 | xfs_trans_brelse(tp, agbp); | ||
1344 | xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); | ||
1345 | if (error) | 1381 | if (error) |
1346 | return error; | 1382 | return error; |
1347 | chunk_agbno = XFS_AGINO_TO_AGBNO(mp, chunk_rec.ir_startino); | ||
1348 | offset_agbno = agbno - chunk_agbno; | ||
1349 | } | 1383 | } |
1350 | 1384 | ||
1385 | out_map: | ||
1351 | ASSERT(agbno >= chunk_agbno); | 1386 | ASSERT(agbno >= chunk_agbno); |
1352 | cluster_agbno = chunk_agbno + | 1387 | cluster_agbno = chunk_agbno + |
1353 | ((offset_agbno / blks_per_cluster) * blks_per_cluster); | 1388 | ((offset_agbno / blks_per_cluster) * blks_per_cluster); |