diff options
Diffstat (limited to 'fs/xfs/xfs_ialloc.c')
-rw-r--r-- | fs/xfs/xfs_ialloc.c | 146 |
1 files changed, 82 insertions, 64 deletions
diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c index 9d884c127bb9..abf80ae1e95b 100644 --- a/fs/xfs/xfs_ialloc.c +++ b/fs/xfs/xfs_ialloc.c | |||
@@ -24,14 +24,10 @@ | |||
24 | #include "xfs_trans.h" | 24 | #include "xfs_trans.h" |
25 | #include "xfs_sb.h" | 25 | #include "xfs_sb.h" |
26 | #include "xfs_ag.h" | 26 | #include "xfs_ag.h" |
27 | #include "xfs_dir2.h" | ||
28 | #include "xfs_dmapi.h" | ||
29 | #include "xfs_mount.h" | 27 | #include "xfs_mount.h" |
30 | #include "xfs_bmap_btree.h" | 28 | #include "xfs_bmap_btree.h" |
31 | #include "xfs_alloc_btree.h" | 29 | #include "xfs_alloc_btree.h" |
32 | #include "xfs_ialloc_btree.h" | 30 | #include "xfs_ialloc_btree.h" |
33 | #include "xfs_dir2_sf.h" | ||
34 | #include "xfs_attr_sf.h" | ||
35 | #include "xfs_dinode.h" | 31 | #include "xfs_dinode.h" |
36 | #include "xfs_inode.h" | 32 | #include "xfs_inode.h" |
37 | #include "xfs_btree.h" | 33 | #include "xfs_btree.h" |
@@ -1203,6 +1199,63 @@ error0: | |||
1203 | return error; | 1199 | return error; |
1204 | } | 1200 | } |
1205 | 1201 | ||
1202 | STATIC int | ||
1203 | xfs_imap_lookup( | ||
1204 | struct xfs_mount *mp, | ||
1205 | struct xfs_trans *tp, | ||
1206 | xfs_agnumber_t agno, | ||
1207 | xfs_agino_t agino, | ||
1208 | xfs_agblock_t agbno, | ||
1209 | xfs_agblock_t *chunk_agbno, | ||
1210 | xfs_agblock_t *offset_agbno, | ||
1211 | int flags) | ||
1212 | { | ||
1213 | struct xfs_inobt_rec_incore rec; | ||
1214 | struct xfs_btree_cur *cur; | ||
1215 | struct xfs_buf *agbp; | ||
1216 | xfs_agino_t startino; | ||
1217 | int error; | ||
1218 | int i; | ||
1219 | |||
1220 | error = xfs_ialloc_read_agi(mp, tp, agno, &agbp); | ||
1221 | if (error) { | ||
1222 | xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " | ||
1223 | "xfs_ialloc_read_agi() returned " | ||
1224 | "error %d, agno %d", | ||
1225 | error, agno); | ||
1226 | return error; | ||
1227 | } | ||
1228 | |||
1229 | /* | ||
1230 | * derive and lookup the exact inode record for the given agino. If the | ||
1231 | * record cannot be found, then it's an invalid inode number and we | ||
1232 | * should abort. | ||
1233 | */ | ||
1234 | cur = xfs_inobt_init_cursor(mp, tp, agbp, agno); | ||
1235 | startino = agino & ~(XFS_IALLOC_INODES(mp) - 1); | ||
1236 | error = xfs_inobt_lookup(cur, startino, XFS_LOOKUP_EQ, &i); | ||
1237 | if (!error) { | ||
1238 | if (i) | ||
1239 | error = xfs_inobt_get_rec(cur, &rec, &i); | ||
1240 | if (!error && i == 0) | ||
1241 | error = EINVAL; | ||
1242 | } | ||
1243 | |||
1244 | xfs_trans_brelse(tp, agbp); | ||
1245 | xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); | ||
1246 | if (error) | ||
1247 | return error; | ||
1248 | |||
1249 | /* for untrusted inodes check it is allocated first */ | ||
1250 | if ((flags & XFS_IGET_UNTRUSTED) && | ||
1251 | (rec.ir_free & XFS_INOBT_MASK(agino - rec.ir_startino))) | ||
1252 | return EINVAL; | ||
1253 | |||
1254 | *chunk_agbno = XFS_AGINO_TO_AGBNO(mp, rec.ir_startino); | ||
1255 | *offset_agbno = agbno - *chunk_agbno; | ||
1256 | return 0; | ||
1257 | } | ||
1258 | |||
1206 | /* | 1259 | /* |
1207 | * Return the location of the inode in imap, for mapping it into a buffer. | 1260 | * Return the location of the inode in imap, for mapping it into a buffer. |
1208 | */ | 1261 | */ |
@@ -1235,8 +1288,11 @@ xfs_imap( | |||
1235 | if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks || | 1288 | if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks || |
1236 | ino != XFS_AGINO_TO_INO(mp, agno, agino)) { | 1289 | ino != XFS_AGINO_TO_INO(mp, agno, agino)) { |
1237 | #ifdef DEBUG | 1290 | #ifdef DEBUG |
1238 | /* no diagnostics for bulkstat, ino comes from userspace */ | 1291 | /* |
1239 | if (flags & XFS_IGET_BULKSTAT) | 1292 | * Don't output diagnostic information for untrusted inodes |
1293 | * as they can be invalid without implying corruption. | ||
1294 | */ | ||
1295 | if (flags & XFS_IGET_UNTRUSTED) | ||
1240 | return XFS_ERROR(EINVAL); | 1296 | return XFS_ERROR(EINVAL); |
1241 | if (agno >= mp->m_sb.sb_agcount) { | 1297 | if (agno >= mp->m_sb.sb_agcount) { |
1242 | xfs_fs_cmn_err(CE_ALERT, mp, | 1298 | xfs_fs_cmn_err(CE_ALERT, mp, |
@@ -1263,6 +1319,23 @@ xfs_imap( | |||
1263 | return XFS_ERROR(EINVAL); | 1319 | return XFS_ERROR(EINVAL); |
1264 | } | 1320 | } |
1265 | 1321 | ||
1322 | blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog; | ||
1323 | |||
1324 | /* | ||
1325 | * For bulkstat and handle lookups, we have an untrusted inode number | ||
1326 | * that we have to verify is valid. We cannot do this just by reading | ||
1327 | * the inode buffer as it may have been unlinked and removed leaving | ||
1328 | * inodes in stale state on disk. Hence we have to do a btree lookup | ||
1329 | * in all cases where an untrusted inode number is passed. | ||
1330 | */ | ||
1331 | if (flags & XFS_IGET_UNTRUSTED) { | ||
1332 | error = xfs_imap_lookup(mp, tp, agno, agino, agbno, | ||
1333 | &chunk_agbno, &offset_agbno, flags); | ||
1334 | if (error) | ||
1335 | return error; | ||
1336 | goto out_map; | ||
1337 | } | ||
1338 | |||
1266 | /* | 1339 | /* |
1267 | * If the inode cluster size is the same as the blocksize or | 1340 | * If the inode cluster size is the same as the blocksize or |
1268 | * smaller we get to the buffer by simple arithmetics. | 1341 | * smaller we get to the buffer by simple arithmetics. |
@@ -1277,24 +1350,6 @@ xfs_imap( | |||
1277 | return 0; | 1350 | return 0; |
1278 | } | 1351 | } |
1279 | 1352 | ||
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 | /* | 1353 | /* |
1299 | * If the inode chunks are aligned then use simple maths to | 1354 | * If the inode chunks are aligned then use simple maths to |
1300 | * find the location. Otherwise we have to do a btree | 1355 | * find the location. Otherwise we have to do a btree |
@@ -1304,50 +1359,13 @@ xfs_imap( | |||
1304 | offset_agbno = agbno & mp->m_inoalign_mask; | 1359 | offset_agbno = agbno & mp->m_inoalign_mask; |
1305 | chunk_agbno = agbno - offset_agbno; | 1360 | chunk_agbno = agbno - offset_agbno; |
1306 | } else { | 1361 | } else { |
1307 | xfs_btree_cur_t *cur; /* inode btree cursor */ | 1362 | error = xfs_imap_lookup(mp, tp, agno, agino, agbno, |
1308 | xfs_inobt_rec_incore_t chunk_rec; | 1363 | &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) | 1364 | if (error) |
1346 | return error; | 1365 | return error; |
1347 | chunk_agbno = XFS_AGINO_TO_AGBNO(mp, chunk_rec.ir_startino); | ||
1348 | offset_agbno = agbno - chunk_agbno; | ||
1349 | } | 1366 | } |
1350 | 1367 | ||
1368 | out_map: | ||
1351 | ASSERT(agbno >= chunk_agbno); | 1369 | ASSERT(agbno >= chunk_agbno); |
1352 | cluster_agbno = chunk_agbno + | 1370 | cluster_agbno = chunk_agbno + |
1353 | ((offset_agbno / blks_per_cluster) * blks_per_cluster); | 1371 | ((offset_agbno / blks_per_cluster) * blks_per_cluster); |