diff options
Diffstat (limited to 'fs/xfs/xfs_ialloc.c')
-rw-r--r-- | fs/xfs/xfs_ialloc.c | 142 |
1 files changed, 82 insertions, 60 deletions
diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c index 9d884c127bb9..c7142a064c48 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_UNTRUSTED) && | ||
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 | */ |
@@ -1235,8 +1292,11 @@ xfs_imap( | |||
1235 | if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks || | 1292 | if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks || |
1236 | ino != XFS_AGINO_TO_INO(mp, agno, agino)) { | 1293 | ino != XFS_AGINO_TO_INO(mp, agno, agino)) { |
1237 | #ifdef DEBUG | 1294 | #ifdef DEBUG |
1238 | /* no diagnostics for bulkstat, ino comes from userspace */ | 1295 | /* |
1239 | if (flags & XFS_IGET_BULKSTAT) | 1296 | * Don't output diagnostic information for untrusted inodes |
1297 | * as they can be invalid without implying corruption. | ||
1298 | */ | ||
1299 | if (flags & XFS_IGET_UNTRUSTED) | ||
1240 | return XFS_ERROR(EINVAL); | 1300 | return XFS_ERROR(EINVAL); |
1241 | if (agno >= mp->m_sb.sb_agcount) { | 1301 | if (agno >= mp->m_sb.sb_agcount) { |
1242 | xfs_fs_cmn_err(CE_ALERT, mp, | 1302 | xfs_fs_cmn_err(CE_ALERT, mp, |
@@ -1263,6 +1323,23 @@ xfs_imap( | |||
1263 | return XFS_ERROR(EINVAL); | 1323 | return XFS_ERROR(EINVAL); |
1264 | } | 1324 | } |
1265 | 1325 | ||
1326 | blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog; | ||
1327 | |||
1328 | /* | ||
1329 | * For bulkstat and handle lookups, we have an untrusted inode number | ||
1330 | * that we have to verify is valid. We cannot do this just by reading | ||
1331 | * the inode buffer as it may have been unlinked and removed leaving | ||
1332 | * inodes in stale state on disk. Hence we have to do a btree lookup | ||
1333 | * in all cases where an untrusted inode number is passed. | ||
1334 | */ | ||
1335 | if (flags & XFS_IGET_UNTRUSTED) { | ||
1336 | error = xfs_imap_lookup(mp, tp, agno, agino, agbno, | ||
1337 | &chunk_agbno, &offset_agbno, flags); | ||
1338 | if (error) | ||
1339 | return error; | ||
1340 | goto out_map; | ||
1341 | } | ||
1342 | |||
1266 | /* | 1343 | /* |
1267 | * If the inode cluster size is the same as the blocksize or | 1344 | * If the inode cluster size is the same as the blocksize or |
1268 | * smaller we get to the buffer by simple arithmetics. | 1345 | * smaller we get to the buffer by simple arithmetics. |
@@ -1277,24 +1354,6 @@ xfs_imap( | |||
1277 | return 0; | 1354 | return 0; |
1278 | } | 1355 | } |
1279 | 1356 | ||
1280 | blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog; | ||
1281 | |||
1282 | /* | ||
1283 | * If we get a block number passed from bulkstat we can use it to | ||
1284 | * find the buffer easily. | ||
1285 | */ | ||
1286 | if (imap->im_blkno) { | ||
1287 | offset = XFS_INO_TO_OFFSET(mp, ino); | ||
1288 | ASSERT(offset < mp->m_sb.sb_inopblock); | ||
1289 | |||
1290 | cluster_agbno = xfs_daddr_to_agbno(mp, imap->im_blkno); | ||
1291 | offset += (agbno - cluster_agbno) * mp->m_sb.sb_inopblock; | ||
1292 | |||
1293 | imap->im_len = XFS_FSB_TO_BB(mp, blks_per_cluster); | ||
1294 | imap->im_boffset = (ushort)(offset << mp->m_sb.sb_inodelog); | ||
1295 | return 0; | ||
1296 | } | ||
1297 | |||
1298 | /* | 1357 | /* |
1299 | * If the inode chunks are aligned then use simple maths to | 1358 | * If the inode chunks are aligned then use simple maths to |
1300 | * find the location. Otherwise we have to do a btree | 1359 | * find the location. Otherwise we have to do a btree |
@@ -1304,50 +1363,13 @@ xfs_imap( | |||
1304 | offset_agbno = agbno & mp->m_inoalign_mask; | 1363 | offset_agbno = agbno & mp->m_inoalign_mask; |
1305 | chunk_agbno = agbno - offset_agbno; | 1364 | chunk_agbno = agbno - offset_agbno; |
1306 | } else { | 1365 | } else { |
1307 | xfs_btree_cur_t *cur; /* inode btree cursor */ | 1366 | error = xfs_imap_lookup(mp, tp, agno, agino, agbno, |
1308 | xfs_inobt_rec_incore_t chunk_rec; | 1367 | &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) | 1368 | if (error) |
1346 | return error; | 1369 | return error; |
1347 | chunk_agbno = XFS_AGINO_TO_AGBNO(mp, chunk_rec.ir_startino); | ||
1348 | offset_agbno = agbno - chunk_agbno; | ||
1349 | } | 1370 | } |
1350 | 1371 | ||
1372 | out_map: | ||
1351 | ASSERT(agbno >= chunk_agbno); | 1373 | ASSERT(agbno >= chunk_agbno); |
1352 | cluster_agbno = chunk_agbno + | 1374 | cluster_agbno = chunk_agbno + |
1353 | ((offset_agbno / blks_per_cluster) * blks_per_cluster); | 1375 | ((offset_agbno / blks_per_cluster) * blks_per_cluster); |