| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| */ |
| |
| #include "libxfs.h" |
| #include "avl.h" |
| #include "globals.h" |
| #include "agheader.h" |
| #include "incore.h" |
| #include "protos.h" |
| #include "err_protos.h" |
| #include "dinode.h" |
| #include "versions.h" |
| #include "prefetch.h" |
| #include "progress.h" |
| |
| /* |
| * validates inode block or chunk, returns # of good inodes |
| * the dinodes are verified using verify_uncertain_dinode() which |
| * means only the basic inode info is checked, no fork checks. |
| */ |
| static int |
| check_aginode_block( |
| xfs_mount_t *mp, |
| xfs_agnumber_t agno, |
| xfs_agblock_t agbno) |
| { |
| struct xfs_dinode *dino_p; |
| int i; |
| int cnt = 0; |
| struct xfs_buf *bp; |
| int error; |
| |
| /* |
| * it's ok to read these possible inode blocks in one at |
| * a time because they don't belong to known inodes (if |
| * they did, we'd know about them courtesy of the incore inode |
| * tree and we wouldn't be here and we stale the buffers out |
| * so no one else will overlap them. |
| */ |
| error = -libxfs_buf_read(mp->m_dev, XFS_AGB_TO_DADDR(mp, agno, agbno), |
| XFS_FSB_TO_BB(mp, 1), LIBXFS_READBUF_SALVAGE, &bp, |
| NULL); |
| if (error) { |
| do_warn(_("cannot read agbno (%u/%u), disk block %" PRId64 "\n"), |
| agno, agbno, XFS_AGB_TO_DADDR(mp, agno, agbno)); |
| return(0); |
| } |
| |
| for (i = 0; i < mp->m_sb.sb_inopblock; i++) { |
| dino_p = xfs_make_iptr(mp, bp, i); |
| if (!verify_uncertain_dinode(mp, dino_p, agno, |
| XFS_OFFBNO_TO_AGINO(mp, agbno, i))) |
| cnt++; |
| } |
| if (cnt) |
| bp->b_ops = &xfs_inode_buf_ops; |
| |
| libxfs_buf_relse(bp); |
| return(cnt); |
| } |
| |
| /* |
| * tries to establish if the inode really exists in a valid |
| * inode chunk. returns number of new inodes if things are good |
| * and 0 if bad. start is the start of the discovered inode chunk. |
| * routine assumes that ino is a legal inode number |
| * (verified by libxfs_verify_ino()). If the inode chunk turns out |
| * to be good, this routine will put the inode chunk into |
| * the good inode chunk tree if required. |
| * |
| * the verify_(ag)inode* family of routines are utility |
| * routines called by check_uncertain_aginodes() and |
| * process_uncertain_aginodes(). |
| */ |
| static int |
| verify_inode_chunk(xfs_mount_t *mp, |
| xfs_ino_t ino, |
| xfs_ino_t *start_ino) |
| { |
| xfs_agnumber_t agno; |
| xfs_agino_t agino; |
| xfs_agino_t start_agino; |
| xfs_agblock_t agbno; |
| xfs_agblock_t start_agbno = 0; |
| xfs_agblock_t end_agbno; |
| xfs_agblock_t max_agbno; |
| xfs_agblock_t cur_agbno; |
| xfs_agblock_t chunk_start_agbno; |
| xfs_agblock_t chunk_stop_agbno; |
| ino_tree_node_t *irec_before_p = NULL; |
| ino_tree_node_t *irec_after_p = NULL; |
| ino_tree_node_t *irec_p; |
| ino_tree_node_t *irec_next_p; |
| int irec_cnt; |
| int ino_cnt = 0; |
| int num_blks; |
| int i; |
| int j; |
| int state; |
| xfs_extlen_t blen; |
| struct xfs_ino_geometry *igeo = M_IGEO(mp); |
| |
| agno = XFS_INO_TO_AGNO(mp, ino); |
| agino = XFS_INO_TO_AGINO(mp, ino); |
| agbno = XFS_INO_TO_AGBNO(mp, ino); |
| *start_ino = NULLFSINO; |
| |
| ASSERT(igeo->ialloc_blks > 0); |
| |
| if (agno == mp->m_sb.sb_agcount - 1) |
| max_agbno = mp->m_sb.sb_dblocks - |
| (xfs_rfsblock_t) mp->m_sb.sb_agblocks * agno; |
| else |
| max_agbno = mp->m_sb.sb_agblocks; |
| |
| /* |
| * is the inode beyond the end of the AG? |
| */ |
| if (agbno >= max_agbno) |
| return(0); |
| |
| /* |
| * check for the easy case, inodes per block >= XFS_INODES_PER_CHUNK |
| * (multiple chunks per block) |
| */ |
| if (igeo->ialloc_blks == 1) { |
| if (agbno > max_agbno) |
| return 0; |
| if (check_aginode_block(mp, agno, agino) == 0) |
| return 0; |
| |
| pthread_mutex_lock(&ag_locks[agno].lock); |
| |
| state = get_bmap(agno, agbno); |
| switch (state) { |
| case XR_E_INO: |
| do_warn( |
| _("uncertain inode block %d/%d already known\n"), |
| agno, agbno); |
| break; |
| case XR_E_UNKNOWN: |
| case XR_E_FREE1: |
| case XR_E_FREE: |
| set_bmap(agno, agbno, XR_E_INO); |
| break; |
| case XR_E_MULT: |
| case XR_E_INUSE: |
| case XR_E_INUSE_FS: |
| case XR_E_FS_MAP: |
| /* |
| * if block is already claimed, forget it. |
| */ |
| do_warn( |
| _("inode block %d/%d multiply claimed, (state %d)\n"), |
| agno, agbno, state); |
| set_bmap(agno, agbno, XR_E_MULT); |
| pthread_mutex_unlock(&ag_locks[agno].lock); |
| return(0); |
| default: |
| do_warn( |
| _("inode block %d/%d bad state, (state %d)\n"), |
| agno, agbno, state); |
| set_bmap(agno, agbno, XR_E_INO); |
| break; |
| } |
| |
| pthread_mutex_unlock(&ag_locks[agno].lock); |
| |
| start_agino = XFS_AGB_TO_AGINO(mp, agbno); |
| *start_ino = XFS_AGINO_TO_INO(mp, agno, start_agino); |
| |
| /* |
| * put new inode record(s) into inode tree |
| */ |
| for (j = 0; j < chunks_pblock; j++) { |
| if ((irec_p = find_inode_rec(mp, agno, start_agino)) |
| == NULL) { |
| irec_p = set_inode_free_alloc(mp, agno, |
| start_agino); |
| for (i = 1; i < XFS_INODES_PER_CHUNK; i++) |
| set_inode_free(irec_p, i); |
| } |
| if (start_agino <= agino && agino < |
| start_agino + XFS_INODES_PER_CHUNK) |
| set_inode_used(irec_p, agino - start_agino); |
| |
| start_agino += XFS_INODES_PER_CHUNK; |
| ino_cnt += XFS_INODES_PER_CHUNK; |
| } |
| |
| return(ino_cnt); |
| } else if (fs_aligned_inodes) { |
| /* |
| * next easy case -- aligned inode filesystem. |
| * just check out the chunk |
| */ |
| start_agbno = rounddown(XFS_INO_TO_AGBNO(mp, ino), |
| fs_ino_alignment); |
| end_agbno = start_agbno + igeo->ialloc_blks; |
| |
| /* |
| * if this fs has aligned inodes but the end of the |
| * chunk is beyond the end of the ag, this is a bad |
| * chunk |
| */ |
| if (end_agbno > max_agbno) |
| return(0); |
| |
| /* |
| * check out all blocks in chunk |
| */ |
| ino_cnt = 0; |
| for (cur_agbno = start_agbno; cur_agbno < end_agbno; |
| cur_agbno++) { |
| ino_cnt += check_aginode_block(mp, agno, cur_agbno); |
| } |
| |
| /* |
| * if we lose either 2 blocks worth of inodes or >25% of |
| * the chunk, just forget it. |
| */ |
| if (ino_cnt < XFS_INODES_PER_CHUNK - 2 * mp->m_sb.sb_inopblock |
| || ino_cnt < XFS_INODES_PER_CHUNK - 16) |
| return(0); |
| |
| /* |
| * ok, put the record into the tree, if no conflict. |
| */ |
| if (find_inode_rec(mp, agno, XFS_AGB_TO_AGINO(mp, start_agbno))) |
| return(0); |
| |
| start_agino = XFS_AGB_TO_AGINO(mp, start_agbno); |
| *start_ino = XFS_AGINO_TO_INO(mp, agno, start_agino); |
| |
| irec_p = set_inode_free_alloc(mp, agno, |
| XFS_AGB_TO_AGINO(mp, start_agbno)); |
| |
| for (i = 1; i < XFS_INODES_PER_CHUNK; i++) |
| set_inode_free(irec_p, i); |
| |
| ASSERT(start_agino <= agino && |
| start_agino + XFS_INODES_PER_CHUNK > agino); |
| |
| set_inode_used(irec_p, agino - start_agino); |
| |
| return(XFS_INODES_PER_CHUNK); |
| } |
| |
| /* |
| * hard case -- pre-6.3 filesystem. |
| * set default start/end agbnos and ensure agbnos are legal. |
| * we're setting a range [start_agbno, end_agbno) such that |
| * a discovered inode chunk completely within that range |
| * would include the inode passed into us. |
| */ |
| if (igeo->ialloc_blks > 1) { |
| if (agino > igeo->ialloc_inos) |
| start_agbno = agbno - igeo->ialloc_blks + 1; |
| else |
| start_agbno = 1; |
| } |
| |
| end_agbno = agbno + igeo->ialloc_blks; |
| |
| if (end_agbno > max_agbno) |
| end_agbno = max_agbno; |
| |
| /* |
| * search tree for known inodes within +/- 1 inode chunk range |
| */ |
| irec_before_p = irec_after_p = NULL; |
| |
| find_inode_rec_range(mp, agno, XFS_AGB_TO_AGINO(mp, start_agbno), |
| XFS_OFFBNO_TO_AGINO(mp, end_agbno, mp->m_sb.sb_inopblock - 1), |
| &irec_before_p, &irec_after_p); |
| |
| /* |
| * if we have known inode chunks in our search range, establish |
| * their start and end-points to tighten our search range. range |
| * is [start, end) -- e.g. max/end agbno is one beyond the |
| * last block to be examined. the avl routines work this way. |
| */ |
| if (irec_before_p) { |
| /* |
| * only one inode record in the range, move one boundary in |
| */ |
| if (irec_before_p == irec_after_p) { |
| if (irec_before_p->ino_startnum < agino) |
| start_agbno = XFS_AGINO_TO_AGBNO(mp, |
| irec_before_p->ino_startnum + |
| XFS_INODES_PER_CHUNK); |
| else |
| end_agbno = XFS_AGINO_TO_AGBNO(mp, |
| irec_before_p->ino_startnum); |
| } |
| |
| /* |
| * find the start of the gap in the search range (which |
| * should contain our unknown inode). if the only irec |
| * within +/- 1 chunks starts after the inode we're |
| * looking for, skip this stuff since the end_agbno |
| * of the range has already been trimmed in to not |
| * include that irec. |
| */ |
| if (irec_before_p->ino_startnum < agino) { |
| irec_p = irec_before_p; |
| irec_next_p = next_ino_rec(irec_p); |
| |
| while(irec_next_p != NULL && |
| irec_p->ino_startnum + XFS_INODES_PER_CHUNK == |
| irec_next_p->ino_startnum) { |
| irec_p = irec_next_p; |
| irec_next_p = next_ino_rec(irec_next_p); |
| } |
| |
| start_agbno = XFS_AGINO_TO_AGBNO(mp, |
| irec_p->ino_startnum) + |
| igeo->ialloc_blks; |
| |
| /* |
| * we know that the inode we're trying to verify isn't |
| * in an inode chunk so the next ino_rec marks the end |
| * of the gap -- is it within the search range? |
| */ |
| if (irec_next_p != NULL && |
| agino + igeo->ialloc_inos >= |
| irec_next_p->ino_startnum) |
| end_agbno = XFS_AGINO_TO_AGBNO(mp, |
| irec_next_p->ino_startnum); |
| } |
| |
| ASSERT(start_agbno < end_agbno); |
| } |
| |
| /* |
| * if the gap is too small to contain a chunk, we lose. |
| * this means that inode chunks known to be good surround |
| * the inode in question and that the space between them |
| * is too small for a legal inode chunk |
| */ |
| if (end_agbno - start_agbno < igeo->ialloc_blks) |
| return(0); |
| |
| /* |
| * now grunge around the disk, start at the inode block and |
| * go in each direction until you hit a non-inode block or |
| * run into a range boundary. A non-inode block is block |
| * with *no* good inodes in it. Unfortunately, we can't |
| * co-opt bad blocks into inode chunks (which might take |
| * care of disk blocks that turn into zeroes) because the |
| * filesystem could very well allocate two inode chunks |
| * with a one block file in between and we'd zap the file. |
| * We're better off just losing the rest of the |
| * inode chunk instead. |
| */ |
| for (cur_agbno = agbno; cur_agbno >= start_agbno; cur_agbno--) { |
| /* |
| * if the block has no inodes, it's a bad block so |
| * break out now without decrementing cur_agbno so |
| * chunk start blockno will be set to the last good block |
| */ |
| if (!(irec_cnt = check_aginode_block(mp, agno, cur_agbno))) |
| break; |
| ino_cnt += irec_cnt; |
| } |
| |
| chunk_start_agbno = cur_agbno + 1; |
| |
| for (cur_agbno = agbno + 1; cur_agbno < end_agbno; cur_agbno++) { |
| /* |
| * if the block has no inodes, it's a bad block so |
| * break out now without incrementing cur_agbno so |
| * chunk start blockno will be set to the block |
| * immediately after the last good block. |
| */ |
| if (!(irec_cnt = check_aginode_block(mp, agno, cur_agbno))) |
| break; |
| ino_cnt += irec_cnt; |
| } |
| |
| chunk_stop_agbno = cur_agbno; |
| |
| num_blks = chunk_stop_agbno - chunk_start_agbno; |
| |
| if (num_blks < igeo->ialloc_blks || ino_cnt == 0) |
| return 0; |
| |
| /* |
| * XXX - later - if the entire range is selected and they're all |
| * good inodes, keep searching in either direction. |
| * until you the range of inodes end, then split into chunks |
| * for now, just take one chunk's worth starting at the lowest |
| * possible point and hopefully we'll pick the rest up later. |
| * |
| * XXX - if we were going to fix up an inode chunk for |
| * any good inodes in the chunk, this is where we would |
| * do it. For now, keep it simple and lose the rest of |
| * the chunk |
| */ |
| |
| if (num_blks % igeo->ialloc_blks != 0) { |
| num_blks = rounddown(num_blks, igeo->ialloc_blks); |
| chunk_stop_agbno = chunk_start_agbno + num_blks; |
| } |
| |
| /* |
| * ok, we've got a candidate inode chunk. now we have to |
| * verify that we aren't trying to use blocks that are already |
| * in use. If so, mark them as multiply claimed since odds |
| * are very low that we found this chunk by stumbling across |
| * user data -- we're probably here as a result of a directory |
| * entry or an iunlinked pointer |
| */ |
| pthread_mutex_lock(&ag_locks[agno].lock); |
| for (cur_agbno = chunk_start_agbno; |
| cur_agbno < chunk_stop_agbno; |
| cur_agbno += blen) { |
| state = get_bmap_ext(agno, cur_agbno, chunk_stop_agbno, &blen); |
| switch (state) { |
| case XR_E_MULT: |
| case XR_E_INUSE: |
| case XR_E_INUSE_FS: |
| case XR_E_FS_MAP: |
| do_warn( |
| _("inode block %d/%d multiply claimed, (state %d)\n"), |
| agno, cur_agbno, state); |
| set_bmap_ext(agno, cur_agbno, blen, XR_E_MULT); |
| pthread_mutex_unlock(&ag_locks[agno].lock); |
| return 0; |
| case XR_E_INO: |
| do_error( |
| _("uncertain inode block overlap, agbno = %d, ino = %" PRIu64 "\n"), |
| agbno, ino); |
| break; |
| default: |
| break; |
| } |
| } |
| pthread_mutex_unlock(&ag_locks[agno].lock); |
| |
| /* |
| * ok, chunk is good. put the record into the tree if required, |
| * and fill in the bitmap. All inodes will be marked as "free" |
| * except for the one that led us to discover the chunk. That's |
| * ok because we'll override the free setting later if the |
| * contents of the inode indicate it's in use. |
| */ |
| start_agino = XFS_AGB_TO_AGINO(mp, chunk_start_agbno); |
| *start_ino = XFS_AGINO_TO_INO(mp, agno, start_agino); |
| |
| ASSERT(find_inode_rec(mp, agno, start_agino) == NULL); |
| |
| irec_p = set_inode_free_alloc(mp, agno, start_agino); |
| for (i = 1; i < XFS_INODES_PER_CHUNK; i++) |
| set_inode_free(irec_p, i); |
| |
| ASSERT(start_agino <= agino && |
| start_agino + XFS_INODES_PER_CHUNK > agino); |
| |
| set_inode_used(irec_p, agino - start_agino); |
| |
| pthread_mutex_lock(&ag_locks[agno].lock); |
| |
| for (cur_agbno = chunk_start_agbno; |
| cur_agbno < chunk_stop_agbno; |
| cur_agbno += blen) { |
| state = get_bmap_ext(agno, cur_agbno, chunk_stop_agbno, &blen); |
| switch (state) { |
| case XR_E_INO: |
| do_error( |
| _("uncertain inode block %" PRIu64 " already known\n"), |
| XFS_AGB_TO_FSB(mp, agno, cur_agbno)); |
| break; |
| case XR_E_UNKNOWN: |
| case XR_E_FREE1: |
| case XR_E_FREE: |
| set_bmap_ext(agno, cur_agbno, blen, XR_E_INO); |
| break; |
| case XR_E_MULT: |
| case XR_E_INUSE: |
| case XR_E_INUSE_FS: |
| case XR_E_FS_MAP: |
| do_error( |
| _("inode block %d/%d multiply claimed, (state %d)\n"), |
| agno, cur_agbno, state); |
| break; |
| default: |
| do_warn( |
| _("inode block %d/%d bad state, (state %d)\n"), |
| agno, cur_agbno, state); |
| set_bmap_ext(agno, cur_agbno, blen, XR_E_INO); |
| break; |
| } |
| } |
| pthread_mutex_unlock(&ag_locks[agno].lock); |
| |
| return(ino_cnt); |
| } |
| |
| /* |
| * same as above only for ag inode chunks |
| */ |
| static int |
| verify_aginode_chunk(xfs_mount_t *mp, |
| xfs_agnumber_t agno, |
| xfs_agino_t agino, |
| xfs_agino_t *agino_start) |
| { |
| xfs_ino_t ino; |
| int res; |
| |
| res = verify_inode_chunk(mp, XFS_AGINO_TO_INO(mp, agno, agino), &ino); |
| |
| if (res) |
| *agino_start = XFS_INO_TO_AGINO(mp, ino); |
| else |
| *agino_start = NULLAGINO; |
| |
| return(res); |
| } |
| |
| /* |
| * this does the same as the two above only it returns a pointer |
| * to the inode record in the good inode tree |
| */ |
| static ino_tree_node_t * |
| verify_aginode_chunk_irec(xfs_mount_t *mp, |
| xfs_agnumber_t agno, |
| xfs_agino_t agino) |
| { |
| xfs_agino_t start_agino; |
| ino_tree_node_t *irec = NULL; |
| |
| if (verify_aginode_chunk(mp, agno, agino, &start_agino)) |
| irec = find_inode_rec(mp, agno, start_agino); |
| |
| return(irec); |
| } |
| |
| /* |
| * Set the state of an inode block during inode chunk processing. The block is |
| * expected to be in the free or inode state. If free, it transitions to the |
| * inode state. Warn if the block is in neither expected state as this indicates |
| * multiply claimed blocks. |
| */ |
| static void |
| process_inode_agbno_state( |
| struct xfs_mount *mp, |
| xfs_agnumber_t agno, |
| xfs_agblock_t agbno) |
| { |
| int state; |
| |
| pthread_mutex_lock(&ag_locks[agno].lock); |
| state = get_bmap(agno, agbno); |
| switch (state) { |
| case XR_E_INO: /* already marked */ |
| break; |
| case XR_E_UNKNOWN: |
| case XR_E_FREE: |
| case XR_E_FREE1: |
| set_bmap(agno, agbno, XR_E_INO); |
| break; |
| case XR_E_BAD_STATE: |
| do_error(_("bad state in block map %d\n"), state); |
| break; |
| default: |
| set_bmap(agno, agbno, XR_E_MULT); |
| do_warn( |
| _("inode block %" PRIu64 " multiply claimed, state was %d\n"), |
| XFS_AGB_TO_FSB(mp, agno, agbno), state); |
| break; |
| } |
| pthread_mutex_unlock(&ag_locks[agno].lock); |
| } |
| |
| /* |
| * processes an inode allocation chunk/block, returns 1 on I/O errors, |
| * 0 otherwise |
| * |
| * *bogus is set to 1 if the entire set of inodes is bad. |
| */ |
| static int |
| process_inode_chunk( |
| xfs_mount_t *mp, |
| xfs_agnumber_t agno, |
| int num_inos, |
| ino_tree_node_t *first_irec, |
| int ino_discovery, |
| int check_dups, |
| int extra_attr_check, |
| int *bogus) |
| { |
| xfs_ino_t parent; |
| ino_tree_node_t *ino_rec; |
| struct xfs_buf **bplist; |
| struct xfs_dinode *dino; |
| int icnt; |
| int status; |
| int bp_found; |
| int is_used; |
| int ino_dirty; |
| int irec_offset; |
| int ibuf_offset; |
| xfs_agino_t agino; |
| xfs_agblock_t agbno; |
| xfs_ino_t ino; |
| int dirty = 0; |
| int isa_dir = 0; |
| int cluster_count; |
| int bp_index; |
| int cluster_offset; |
| struct xfs_ino_geometry *igeo = M_IGEO(mp); |
| bool can_punch_sparse = false; |
| int error; |
| |
| ASSERT(first_irec != NULL); |
| ASSERT(XFS_AGINO_TO_OFFSET(mp, first_irec->ino_startnum) == 0); |
| |
| *bogus = 0; |
| ASSERT(igeo->ialloc_blks > 0); |
| |
| cluster_count = XFS_INODES_PER_CHUNK / M_IGEO(mp)->inodes_per_cluster; |
| if (cluster_count == 0) |
| cluster_count = 1; |
| |
| if (xfs_has_sparseinodes(mp) && |
| M_IGEO(mp)->inodes_per_cluster >= XFS_INODES_PER_HOLEMASK_BIT) |
| can_punch_sparse = true; |
| |
| /* |
| * get all blocks required to read in this chunk (may wind up |
| * having to process more chunks in a multi-chunk per block fs) |
| */ |
| agbno = XFS_AGINO_TO_AGBNO(mp, first_irec->ino_startnum); |
| |
| /* |
| * set up first irec |
| */ |
| ino_rec = first_irec; |
| irec_offset = 0; |
| |
| bplist = malloc(cluster_count * sizeof(struct xfs_buf *)); |
| if (bplist == NULL) |
| do_error(_("failed to allocate %zd bytes of memory\n"), |
| cluster_count * sizeof(struct xfs_buf *)); |
| |
| for (bp_index = 0; bp_index < cluster_count; bp_index++) { |
| /* |
| * Skip the cluster buffer if the first inode is sparse. The |
| * remaining inodes in the cluster share the same state as |
| * sparse inodes occur at cluster granularity. |
| */ |
| if (is_inode_sparse(ino_rec, irec_offset)) { |
| pftrace("skip sparse inode, startnum 0x%x idx %d", |
| ino_rec->ino_startnum, irec_offset); |
| bplist[bp_index] = NULL; |
| goto next_readbuf; |
| } |
| |
| pftrace("about to read off %llu in AG %d", |
| XFS_AGB_TO_DADDR(mp, agno, agbno), agno); |
| |
| error = -libxfs_buf_read(mp->m_dev, |
| XFS_AGB_TO_DADDR(mp, agno, agbno), |
| XFS_FSB_TO_BB(mp, |
| M_IGEO(mp)->blocks_per_cluster), |
| LIBXFS_READBUF_SALVAGE, &bplist[bp_index], |
| &xfs_inode_buf_ops); |
| if (error) { |
| do_warn(_("cannot read inode %" PRIu64 ", disk block %" PRId64 ", cnt %d\n"), |
| XFS_AGINO_TO_INO(mp, agno, first_irec->ino_startnum), |
| XFS_AGB_TO_DADDR(mp, agno, agbno), |
| XFS_FSB_TO_BB(mp, |
| M_IGEO(mp)->blocks_per_cluster)); |
| while (bp_index > 0) { |
| bp_index--; |
| libxfs_buf_relse(bplist[bp_index]); |
| } |
| free(bplist); |
| return(1); |
| } |
| |
| pftrace("readbuf %p (%llu, %d) in AG %d", bplist[bp_index], |
| (long long)xfs_buf_daddr(bplist[bp_index]), |
| bplist[bp_index]->b_length, agno); |
| |
| bplist[bp_index]->b_ops = &xfs_inode_buf_ops; |
| |
| next_readbuf: |
| irec_offset += mp->m_sb.sb_inopblock * |
| M_IGEO(mp)->blocks_per_cluster; |
| agbno += M_IGEO(mp)->blocks_per_cluster; |
| } |
| agbno = XFS_AGINO_TO_AGBNO(mp, first_irec->ino_startnum); |
| |
| /* |
| * initialize counters |
| */ |
| irec_offset = 0; |
| ibuf_offset = 0; |
| cluster_offset = 0; |
| icnt = 0; |
| status = 0; |
| bp_found = 0; |
| bp_index = 0; |
| |
| /* |
| * verify inode chunk if necessary |
| */ |
| if (ino_discovery) { |
| for (;;) { |
| agino = irec_offset + ino_rec->ino_startnum; |
| |
| /* no buffers for sparse clusters */ |
| if (bplist[bp_index]) { |
| /* make inode pointer */ |
| dino = xfs_make_iptr(mp, bplist[bp_index], |
| cluster_offset); |
| |
| /* |
| * we always think that the root and realtime |
| * inodes are verified even though we may have |
| * to reset them later to keep from losing the |
| * chunk that they're in |
| */ |
| if (verify_dinode(mp, dino, agno, agino) == 0 || |
| (agno == 0 && |
| (mp->m_sb.sb_rootino == agino || |
| mp->m_sb.sb_rsumino == agino || |
| mp->m_sb.sb_rbmino == agino))) { |
| status++; |
| bp_found++; |
| } |
| } |
| |
| irec_offset++; |
| icnt++; |
| cluster_offset++; |
| |
| if (icnt == igeo->ialloc_inos && |
| irec_offset == XFS_INODES_PER_CHUNK) { |
| /* |
| * done! - finished up irec and block |
| * simultaneously |
| */ |
| break; |
| } else if (irec_offset == XFS_INODES_PER_CHUNK) { |
| /* |
| * get new irec (multiple chunks per block fs) |
| */ |
| ino_rec = next_ino_rec(ino_rec); |
| ASSERT(ino_rec->ino_startnum == agino + 1); |
| irec_offset = 0; |
| } |
| if (cluster_offset == M_IGEO(mp)->inodes_per_cluster) { |
| if (can_punch_sparse && |
| bplist[bp_index] != NULL && |
| bp_found == 0) { |
| /* |
| * We didn't find any good inodes in |
| * this cluster, blow it away before |
| * moving on to the next one. |
| */ |
| libxfs_buf_relse(bplist[bp_index]); |
| bplist[bp_index] = NULL; |
| } |
| bp_index++; |
| cluster_offset = 0; |
| bp_found = 0; |
| } |
| } |
| |
| if (can_punch_sparse && |
| bp_index < cluster_count && |
| bplist[bp_index] != NULL && |
| bp_found == 0) { |
| /* |
| * We didn't find any good inodes in this cluster, blow |
| * it away. |
| */ |
| libxfs_buf_relse(bplist[bp_index]); |
| bplist[bp_index] = NULL; |
| } |
| |
| /* |
| * if chunk/block is bad, blow it off. the inode records |
| * will be deleted by the caller if appropriate. |
| */ |
| if (!status) { |
| *bogus = 1; |
| for (bp_index = 0; bp_index < cluster_count; bp_index++) |
| if (bplist[bp_index]) |
| libxfs_buf_relse(bplist[bp_index]); |
| free(bplist); |
| return(0); |
| } |
| |
| /* |
| * reset irec and counters |
| */ |
| ino_rec = first_irec; |
| |
| irec_offset = 0; |
| cluster_offset = 0; |
| bp_index = 0; |
| icnt = 0; |
| status = 0; |
| } |
| |
| /* |
| * mark block as an inode block in the incore bitmap |
| */ |
| if (!is_inode_sparse(ino_rec, irec_offset)) |
| process_inode_agbno_state(mp, agno, agbno); |
| |
| for (;;) { |
| agino = irec_offset + ino_rec->ino_startnum; |
| ino = XFS_AGINO_TO_INO(mp, agno, agino); |
| |
| if (is_inode_sparse(ino_rec, irec_offset)) |
| goto process_next; |
| |
| /* |
| * Repair skips reading the cluster buffer if the first inode |
| * in the cluster is marked as sparse. If subsequent inodes in |
| * the cluster buffer are /not/ marked sparse, there won't be |
| * a buffer, so we need to avoid the null pointer dereference. |
| */ |
| if (bplist[bp_index] == NULL) { |
| do_warn( |
| _("imap claims inode %" PRIu64 " is present, but inode cluster is sparse, "), |
| ino); |
| if (verbose || !no_modify) |
| do_warn(_("correcting imap\n")); |
| else |
| do_warn(_("would correct imap\n")); |
| set_inode_sparse(ino_rec, irec_offset); |
| set_inode_free(ino_rec, irec_offset); |
| goto process_next; |
| } |
| |
| /* make inode pointer */ |
| dino = xfs_make_iptr(mp, bplist[bp_index], cluster_offset); |
| |
| |
| is_used = 3; |
| ino_dirty = 0; |
| parent = 0; |
| |
| status = process_dinode(mp, dino, agno, agino, |
| is_inode_free(ino_rec, irec_offset), |
| &ino_dirty, &is_used,ino_discovery, check_dups, |
| extra_attr_check, &isa_dir, &parent); |
| |
| ASSERT(is_used != 3); |
| if (ino_dirty) { |
| dirty = 1; |
| libxfs_dinode_calc_crc(mp, dino); |
| } |
| |
| /* |
| * XXX - if we want to try and keep |
| * track of whether we need to bang on |
| * the inode maps (instead of just |
| * blindly reconstructing them like |
| * we do now, this is where to start. |
| */ |
| if (is_used) { |
| if (is_inode_free(ino_rec, irec_offset)) { |
| if (verbose || no_modify) { |
| do_warn( |
| _("imap claims in-use inode %" PRIu64 " is free, "), |
| ino); |
| } |
| |
| if (verbose || !no_modify) |
| do_warn(_("correcting imap\n")); |
| else |
| do_warn(_("would correct imap\n")); |
| } |
| set_inode_used(ino_rec, irec_offset); |
| |
| /* |
| * store the on-disk file type for comparing in |
| * phase 6. |
| */ |
| set_inode_ftype(ino_rec, irec_offset, |
| libxfs_mode_to_ftype(be16_to_cpu(dino->di_mode))); |
| |
| /* |
| * store on-disk nlink count for comparing in phase 7 |
| */ |
| set_inode_disk_nlinks(ino_rec, irec_offset, |
| dino->di_version > 1 |
| ? be32_to_cpu(dino->di_nlink) |
| : be16_to_cpu(dino->di_onlink)); |
| |
| } else { |
| set_inode_free(ino_rec, irec_offset); |
| } |
| |
| /* |
| * if we lose the root inode, or it turns into |
| * a non-directory, that allows us to double-check |
| * later whether or not we need to reinitialize it. |
| */ |
| if (isa_dir) { |
| set_inode_isadir(ino_rec, irec_offset); |
| if (ino_discovery) { |
| ASSERT(parent != 0); |
| set_inode_parent(ino_rec, irec_offset, parent); |
| ASSERT(parent == |
| get_inode_parent(ino_rec, irec_offset)); |
| } |
| } else { |
| clear_inode_isadir(ino_rec, irec_offset); |
| } |
| |
| if (status) { |
| if (mp->m_sb.sb_rootino == ino) { |
| need_root_inode = 1; |
| |
| if (!no_modify) { |
| do_warn( |
| _("cleared root inode %" PRIu64 "\n"), |
| ino); |
| } else { |
| do_warn( |
| _("would clear root inode %" PRIu64 "\n"), |
| ino); |
| } |
| } else if (mp->m_sb.sb_rbmino == ino) { |
| need_rbmino = 1; |
| |
| if (!no_modify) { |
| do_warn( |
| _("cleared realtime bitmap inode %" PRIu64 "\n"), |
| ino); |
| } else { |
| do_warn( |
| _("would clear realtime bitmap inode %" PRIu64 "\n"), |
| ino); |
| } |
| } else if (mp->m_sb.sb_rsumino == ino) { |
| need_rsumino = 1; |
| |
| if (!no_modify) { |
| do_warn( |
| _("cleared realtime summary inode %" PRIu64 "\n"), |
| ino); |
| } else { |
| do_warn( |
| _("would clear realtime summary inode %" PRIu64 "\n"), |
| ino); |
| } |
| } else if (!no_modify) { |
| do_warn(_("cleared inode %" PRIu64 "\n"), |
| ino); |
| } else { |
| do_warn(_("would have cleared inode %" PRIu64 "\n"), |
| ino); |
| } |
| clear_inode_was_rl(ino_rec, irec_offset); |
| } |
| |
| process_next: |
| irec_offset++; |
| ibuf_offset++; |
| icnt++; |
| cluster_offset++; |
| |
| if (icnt == igeo->ialloc_inos && |
| irec_offset == XFS_INODES_PER_CHUNK) { |
| /* |
| * done! - finished up irec and block simultaneously |
| */ |
| for (bp_index = 0; bp_index < cluster_count; bp_index++) { |
| if (!bplist[bp_index]) |
| continue; |
| |
| pftrace("put/writebuf %p (%llu) in AG %d", |
| bplist[bp_index], (long long) |
| xfs_buf_daddr(bplist[bp_index]), agno); |
| |
| if (dirty && !no_modify) { |
| libxfs_buf_mark_dirty(bplist[bp_index]); |
| libxfs_buf_relse(bplist[bp_index]); |
| } |
| else |
| libxfs_buf_relse(bplist[bp_index]); |
| } |
| free(bplist); |
| break; |
| } else if (ibuf_offset == mp->m_sb.sb_inopblock) { |
| /* |
| * mark block as an inode block in the incore bitmap |
| * and reset inode buffer offset counter |
| */ |
| ibuf_offset = 0; |
| agbno++; |
| |
| if (!is_inode_sparse(ino_rec, irec_offset)) |
| process_inode_agbno_state(mp, agno, agbno); |
| } else if (irec_offset == XFS_INODES_PER_CHUNK) { |
| /* |
| * get new irec (multiple chunks per block fs) |
| */ |
| ino_rec = next_ino_rec(ino_rec); |
| ASSERT(ino_rec->ino_startnum == agino + 1); |
| irec_offset = 0; |
| } |
| if (cluster_offset == M_IGEO(mp)->inodes_per_cluster) { |
| bp_index++; |
| cluster_offset = 0; |
| } |
| } |
| return(0); |
| } |
| |
| /* |
| * check all inodes mentioned in the ag's incore inode maps. |
| * the map may be incomplete. If so, we'll catch the missing |
| * inodes (hopefully) when we traverse the directory tree. |
| * check_dirs is set to 1 if directory inodes should be |
| * processed for internal consistency, parent setting and |
| * discovery of unknown inodes. this only happens |
| * in phase 3. check_dups is set to 1 if we're looking for |
| * inodes that reference duplicate blocks so we can trash |
| * the inode right then and there. this is set only in |
| * phase 4 after we've run through and set the bitmap once. |
| */ |
| void |
| process_aginodes( |
| xfs_mount_t *mp, |
| prefetch_args_t *pf_args, |
| xfs_agnumber_t agno, |
| int ino_discovery, |
| int check_dups, |
| int extra_attr_check) |
| { |
| int num_inos, bogus; |
| ino_tree_node_t *ino_rec, *first_ino_rec, *prev_ino_rec; |
| struct xfs_ino_geometry *igeo = M_IGEO(mp); |
| #ifdef XR_PF_TRACE |
| int count; |
| #endif |
| first_ino_rec = ino_rec = findfirst_inode_rec(agno); |
| |
| while (ino_rec != NULL) { |
| /* |
| * paranoia - step through inode records until we step |
| * through a full allocation of inodes. this could |
| * be an issue in big-block filesystems where a block |
| * can hold more than one inode chunk. make sure to |
| * grab the record corresponding to the beginning of |
| * the next block before we call the processing routines. |
| */ |
| num_inos = XFS_INODES_PER_CHUNK; |
| while (num_inos < igeo->ialloc_inos && ino_rec != NULL) { |
| /* |
| * inodes chunks will always be aligned and sized |
| * correctly |
| */ |
| if ((ino_rec = next_ino_rec(ino_rec)) != NULL) |
| num_inos += XFS_INODES_PER_CHUNK; |
| } |
| |
| ASSERT(num_inos == igeo->ialloc_inos); |
| |
| if (pf_args) { |
| sem_post(&pf_args->ra_count); |
| #ifdef XR_PF_TRACE |
| sem_getvalue(&pf_args->ra_count, &count); |
| pftrace("processing inode chunk %p in AG %d (sem count = %d)", |
| first_ino_rec, agno, count); |
| #endif |
| } |
| |
| if (process_inode_chunk(mp, agno, num_inos, first_ino_rec, |
| ino_discovery, check_dups, extra_attr_check, |
| &bogus)) { |
| /* XXX - i/o error, we've got a problem */ |
| abort(); |
| } |
| |
| if (!bogus) |
| first_ino_rec = ino_rec = next_ino_rec(ino_rec); |
| else { |
| /* |
| * inodes pointed to by this record are |
| * completely bogus, blow the records for |
| * this chunk out. |
| * the inode block(s) will get reclaimed |
| * in phase 4 when the block map is |
| * reconstructed after inodes claiming |
| * duplicate blocks are deleted. |
| */ |
| num_inos = 0; |
| ino_rec = first_ino_rec; |
| while (num_inos < igeo->ialloc_inos && |
| ino_rec != NULL) { |
| prev_ino_rec = ino_rec; |
| |
| if ((ino_rec = next_ino_rec(ino_rec)) != NULL) |
| num_inos += XFS_INODES_PER_CHUNK; |
| |
| get_inode_rec(mp, agno, prev_ino_rec); |
| free_inode_rec(agno, prev_ino_rec); |
| } |
| |
| first_ino_rec = ino_rec; |
| } |
| PROG_RPT_INC(prog_rpt_done[agno], num_inos); |
| } |
| } |
| |
| /* |
| * verify the uncertain inode list for an ag. |
| * Good inodes get moved into the good inode tree. |
| * returns 0 if there are no uncertain inode records to |
| * be processed, 1 otherwise. This routine destroys the |
| * the entire uncertain inode tree for the ag as a side-effect. |
| */ |
| void |
| check_uncertain_aginodes(xfs_mount_t *mp, xfs_agnumber_t agno) |
| { |
| ino_tree_node_t *irec; |
| ino_tree_node_t *nrec; |
| xfs_agino_t start; |
| xfs_agino_t i; |
| xfs_agino_t agino; |
| int got_some; |
| struct xfs_perag *pag; |
| |
| nrec = NULL; |
| got_some = 0; |
| |
| clear_uncertain_ino_cache(agno); |
| |
| if ((irec = findfirst_uncertain_inode_rec(agno)) == NULL) |
| return; |
| |
| /* |
| * the trick here is to find a contiguous range |
| * of inodes, make sure that it doesn't overlap |
| * with a known to exist chunk, and then make |
| * sure it is a number of entire chunks. |
| * we check on-disk once we have an idea of what's |
| * going on just to double-check. |
| * |
| * process the uncertain inode record list and look |
| * on disk to see if the referenced inodes are good |
| */ |
| |
| do_warn(_("found inodes not in the inode allocation tree\n")); |
| |
| pag = libxfs_perag_get(mp, agno); |
| do { |
| /* |
| * check every confirmed (which in this case means |
| * inode that we really suspect to be an inode) inode |
| */ |
| for (i = 0; i < XFS_INODES_PER_CHUNK; i++) { |
| if (!is_inode_confirmed(irec, i)) |
| continue; |
| |
| agino = i + irec->ino_startnum; |
| |
| if (!libxfs_verify_agino(pag, agino)) |
| continue; |
| |
| if (nrec != NULL && nrec->ino_startnum <= agino && |
| agino < nrec->ino_startnum + |
| XFS_INODES_PER_CHUNK) |
| continue; |
| |
| if ((nrec = find_inode_rec(mp, agno, agino)) == NULL) |
| if (libxfs_verify_agino(pag, agino)) |
| if (verify_aginode_chunk(mp, agno, |
| agino, &start)) |
| got_some = 1; |
| } |
| |
| get_uncertain_inode_rec(mp, agno, irec); |
| free_inode_rec(agno, irec); |
| |
| irec = findfirst_uncertain_inode_rec(agno); |
| } while (irec != NULL); |
| libxfs_perag_put(pag); |
| |
| if (got_some) |
| do_warn(_("found inodes not in the inode allocation tree\n")); |
| |
| return; |
| } |
| |
| /* |
| * verify and process the uncertain inodes for an ag. |
| * this is different from check_ in that we can't just |
| * move the good inodes into the good inode tree and let |
| * process_aginodes() deal with them because this gets called |
| * after process_aginodes() has been run on the ag inode tree. |
| * So we have to process the inodes as well as verify since |
| * we don't want to rerun process_aginodes() on a tree that has |
| * mostly been processed. |
| * |
| * Note that if this routine does process some inodes, it can |
| * add uncertain inodes to any ag which would require that |
| * the routine be called again to process those newly-added |
| * uncertain inodes. |
| * |
| * returns 0 if no inodes were processed and 1 if inodes |
| * were processed (and it is possible that new uncertain |
| * inodes were discovered). |
| * |
| * as a side-effect, this routine tears down the uncertain |
| * inode tree for the ag. |
| */ |
| int |
| process_uncertain_aginodes(xfs_mount_t *mp, xfs_agnumber_t agno) |
| { |
| ino_tree_node_t *irec; |
| ino_tree_node_t *nrec; |
| xfs_agino_t agino; |
| int i; |
| int bogus; |
| int cnt; |
| int got_some; |
| struct xfs_ino_geometry *igeo = M_IGEO(mp); |
| struct xfs_perag *pag; |
| |
| #ifdef XR_INODE_TRACE |
| fprintf(stderr, "in process_uncertain_aginodes, agno = %d\n", agno); |
| #endif |
| |
| got_some = 0; |
| |
| clear_uncertain_ino_cache(agno); |
| |
| if ((irec = findfirst_uncertain_inode_rec(agno)) == NULL) |
| return(0); |
| |
| nrec = NULL; |
| |
| pag = libxfs_perag_get(mp, agno); |
| do { |
| /* |
| * check every confirmed inode |
| */ |
| for (cnt = i = 0; i < XFS_INODES_PER_CHUNK; i++) { |
| if (!is_inode_confirmed(irec, i)) |
| continue; |
| cnt++; |
| agino = i + irec->ino_startnum; |
| #ifdef XR_INODE_TRACE |
| fprintf(stderr, "ag inode = %d (0x%x)\n", agino, agino); |
| #endif |
| /* |
| * skip over inodes already processed (in the |
| * good tree), bad inode numbers, and inode numbers |
| * pointing to bogus inodes |
| */ |
| if (!libxfs_verify_agino(pag, agino)) |
| continue; |
| |
| if (nrec != NULL && nrec->ino_startnum <= agino && |
| agino < nrec->ino_startnum + |
| XFS_INODES_PER_CHUNK) |
| continue; |
| |
| if ((nrec = find_inode_rec(mp, agno, agino)) != NULL) |
| continue; |
| |
| /* |
| * verify the chunk. if good, it will be |
| * added to the good inode tree. |
| */ |
| if ((nrec = verify_aginode_chunk_irec(mp, |
| agno, agino)) == NULL) |
| continue; |
| |
| got_some = 1; |
| |
| /* |
| * process the inode record we just added |
| * to the good inode tree. The inode |
| * processing may add more records to the |
| * uncertain inode lists. |
| */ |
| if (process_inode_chunk(mp, agno, igeo->ialloc_inos, |
| nrec, 1, 0, 0, &bogus)) { |
| /* XXX - i/o error, we've got a problem */ |
| abort(); |
| } |
| } |
| |
| ASSERT(cnt != 0); |
| /* |
| * now return the uncertain inode record to the free pool |
| * and pull another one off the list for processing |
| */ |
| get_uncertain_inode_rec(mp, agno, irec); |
| free_inode_rec(agno, irec); |
| |
| irec = findfirst_uncertain_inode_rec(agno); |
| } while (irec != NULL); |
| libxfs_perag_put(pag); |
| |
| if (got_some) |
| do_warn(_("found inodes not in the inode allocation tree\n")); |
| |
| return(1); |
| } |