📄 xfs_inode.c
字号:
XFS_BMAP_INIT(&free_list, &first_block); error = XFS_BUNMAPI(mp, ntp, &ip->i_iocore, first_unmap_block, unmap_len, XFS_BMAPI_AFLAG(fork) | (sync ? 0 : XFS_BMAPI_ASYNC), XFS_ITRUNC_MAX_EXTENTS, &first_block, &free_list, NULL, &done); if (error) { /* * If the bunmapi call encounters an error, * return to the caller where the transaction * can be properly aborted. We just need to * make sure we're not holding any resources * that we were not when we came in. */ xfs_bmap_cancel(&free_list); return error; } /* * Duplicate the transaction that has the permanent * reservation and commit the old transaction. */ error = xfs_bmap_finish(tp, &free_list, &committed); ntp = *tp; if (error) { /* * If the bmap finish call encounters an error, * return to the caller where the transaction * can be properly aborted. We just need to * make sure we're not holding any resources * that we were not when we came in. * * Aborting from this point might lose some * blocks in the file system, but oh well. */ xfs_bmap_cancel(&free_list); if (committed) { /* * If the passed in transaction committed * in xfs_bmap_finish(), then we want to * add the inode to this one before returning. * This keeps things simple for the higher * level code, because it always knows that * the inode is locked and held in the * transaction that returns to it whether * errors occur or not. We don't mark the * inode dirty so that this transaction can * be easily aborted if possible. */ xfs_trans_ijoin(ntp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); xfs_trans_ihold(ntp, ip); } return error; } if (committed) { /* * The first xact was committed, * so add the inode to the new one. * Mark it dirty so it will be logged * and moved forward in the log as * part of every commit. */ xfs_trans_ijoin(ntp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); xfs_trans_ihold(ntp, ip); xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE); } ntp = xfs_trans_dup(ntp); (void) xfs_trans_commit(*tp, 0); *tp = ntp; error = xfs_trans_reserve(ntp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0, XFS_TRANS_PERM_LOG_RES, XFS_ITRUNCATE_LOG_COUNT); /* * Add the inode being truncated to the next chained * transaction. */ xfs_trans_ijoin(ntp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); xfs_trans_ihold(ntp, ip); if (error) return (error); } /* * Only update the size in the case of the data fork, but * always re-log the inode so that our permanent transaction * can keep on rolling it forward in the log. */ if (fork == XFS_DATA_FORK) { xfs_isize_check(mp, ip, new_size); /* * If we are not changing the file size then do * not update the on-disk file size - we may be * called from xfs_inactive_free_eofblocks(). If we * update the on-disk file size and then the system * crashes before the contents of the file are * flushed to disk then the files may be full of * holes (ie NULL files bug). */ if (ip->i_size != new_size) { ip->i_d.di_size = new_size; ip->i_size = new_size; } } xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE); ASSERT((new_size != 0) || (fork == XFS_ATTR_FORK) || (ip->i_delayed_blks == 0)); ASSERT((new_size != 0) || (fork == XFS_ATTR_FORK) || (ip->i_d.di_nextents == 0)); xfs_itrunc_trace(XFS_ITRUNC_FINISH2, ip, 0, new_size, 0, 0); return 0;}/* * xfs_igrow_start * * Do the first part of growing a file: zero any data in the last * block that is beyond the old EOF. We need to do this before * the inode is joined to the transaction to modify the i_size. * That way we can drop the inode lock and call into the buffer * cache to get the buffer mapping the EOF. */intxfs_igrow_start( xfs_inode_t *ip, xfs_fsize_t new_size, cred_t *credp){ int error; ASSERT(ismrlocked(&(ip->i_lock), MR_UPDATE) != 0); ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE) != 0); ASSERT(new_size > ip->i_size); /* * Zero any pages that may have been created by * xfs_write_file() beyond the end of the file * and any blocks between the old and new file sizes. */ error = xfs_zero_eof(XFS_ITOV(ip), &ip->i_iocore, new_size, ip->i_size); return error;}/* * xfs_igrow_finish * * This routine is called to extend the size of a file. * The inode must have both the iolock and the ilock locked * for update and it must be a part of the current transaction. * The xfs_igrow_start() function must have been called previously. * If the change_flag is not zero, the inode change timestamp will * be updated. */voidxfs_igrow_finish( xfs_trans_t *tp, xfs_inode_t *ip, xfs_fsize_t new_size, int change_flag){ ASSERT(ismrlocked(&(ip->i_lock), MR_UPDATE) != 0); ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE) != 0); ASSERT(ip->i_transp == tp); ASSERT(new_size > ip->i_size); /* * Update the file size. Update the inode change timestamp * if change_flag set. */ ip->i_d.di_size = new_size; ip->i_size = new_size; if (change_flag) xfs_ichgtime(ip, XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);}/* * This is called when the inode's link count goes to 0. * We place the on-disk inode on a list in the AGI. It * will be pulled from this list when the inode is freed. */intxfs_iunlink( xfs_trans_t *tp, xfs_inode_t *ip){ xfs_mount_t *mp; xfs_agi_t *agi; xfs_dinode_t *dip; xfs_buf_t *agibp; xfs_buf_t *ibp; xfs_agnumber_t agno; xfs_daddr_t agdaddr; xfs_agino_t agino; short bucket_index; int offset; int error; int agi_ok; ASSERT(ip->i_d.di_nlink == 0); ASSERT(ip->i_d.di_mode != 0); ASSERT(ip->i_transp == tp); mp = tp->t_mountp; agno = XFS_INO_TO_AGNO(mp, ip->i_ino); agdaddr = XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)); /* * Get the agi buffer first. It ensures lock ordering * on the list. */ error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, agdaddr, XFS_FSS_TO_BB(mp, 1), 0, &agibp); if (error) return error; /* * Validate the magic number of the agi block. */ agi = XFS_BUF_TO_AGI(agibp); agi_ok = be32_to_cpu(agi->agi_magicnum) == XFS_AGI_MAGIC && XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum)); if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IUNLINK, XFS_RANDOM_IUNLINK))) { XFS_CORRUPTION_ERROR("xfs_iunlink", XFS_ERRLEVEL_LOW, mp, agi); xfs_trans_brelse(tp, agibp); return XFS_ERROR(EFSCORRUPTED); } /* * Get the index into the agi hash table for the * list this inode will go on. */ agino = XFS_INO_TO_AGINO(mp, ip->i_ino); ASSERT(agino != 0); bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS; ASSERT(agi->agi_unlinked[bucket_index]); ASSERT(be32_to_cpu(agi->agi_unlinked[bucket_index]) != agino); error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0, 0); if (error) return error; /* * Clear the on-disk di_nlink. This is to prevent xfs_bulkstat * from picking up this inode when it is reclaimed (its incore state * initialzed but not flushed to disk yet). The in-core di_nlink is * already cleared in xfs_droplink() and a corresponding transaction * logged. The hack here just synchronizes the in-core to on-disk * di_nlink value in advance before the actual inode sync to disk. * This is OK because the inode is already unlinked and would never * change its di_nlink again for this inode generation. * This is a temporary hack that would require a proper fix * in the future. */ dip->di_core.di_nlink = 0; if (be32_to_cpu(agi->agi_unlinked[bucket_index]) != NULLAGINO) { /* * There is already another inode in the bucket we need * to add ourselves to. Add us at the front of the list. * Here we put the head pointer into our next pointer, * and then we fall through to point the head at us. */ ASSERT(be32_to_cpu(dip->di_next_unlinked) == NULLAGINO); /* both on-disk, don't endian flip twice */ dip->di_next_unlinked = agi->agi_unlinked[bucket_index]; offset = ip->i_boffset + offsetof(xfs_dinode_t, di_next_unlinked); xfs_trans_inode_buf(tp, ibp); xfs_trans_log_buf(tp, ibp, offset, (offset + sizeof(xfs_agino_t) - 1)); xfs_inobp_check(mp, ibp); } /* * Point the bucket head pointer at the inode being inserted. */ ASSERT(agino != 0); agi->agi_unlinked[bucket_index] = cpu_to_be32(agino); offset = offsetof(xfs_agi_t, agi_unlinked) + (sizeof(xfs_agino_t) * bucket_index); xfs_trans_log_buf(tp, agibp, offset, (offset + sizeof(xfs_agino_t) - 1)); return 0;}/* * Pull the on-disk inode from the AGI unlinked list. */STATIC intxfs_iunlink_remove( xfs_trans_t *tp, xfs_inode_t *ip){ xfs_ino_t next_ino; xfs_mount_t *mp; xfs_agi_t *agi; xfs_dinode_t *dip; xfs_buf_t *agibp; xfs_buf_t *ibp; xfs_agnumber_t agno; xfs_daddr_t agdaddr; xfs_agino_t agino; xfs_agino_t next_agino; xfs_buf_t *last_ibp; xfs_dinode_t *last_dip = NULL; short bucket_index; int offset, last_offset = 0; int error; int agi_ok; /* * First pull the on-disk inode from the AGI unlinked list. */ mp = tp->t_mountp; agno = XFS_INO_TO_AGNO(mp, ip->i_ino); agdaddr = XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)); /* * Get the agi buffer first. It ensures lock ordering * on the list. */ error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, agdaddr, XFS_FSS_TO_BB(mp, 1), 0, &agibp); if (error) { cmn_err(CE_WARN, "xfs_iunlink_remove: xfs_trans_read_buf() returned an error %d on %s. Returning error.", error, mp->m_fsname); return error; } /* * Validate the magic number of the agi block. */ agi = XFS_BUF_TO_AGI(agibp); agi_ok = be32_to_cpu(agi->agi_magicnum) == XFS_AGI_MAGIC && XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum)); if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IUNLINK_REMOVE, XFS_RANDOM_IUNLINK_REMOVE))) { XFS_CORRUPTION_ERROR("xfs_iunlink_remove", XFS_ERRLEVEL_LOW, mp, agi); xfs_trans_brelse(tp, agibp); cmn_err(CE_WARN, "xfs_iunlink_remove: XFS_TEST_ERROR() returned an error on %s. Returning EFSCORRUPTED.", mp->m_fsname); return XFS_ERROR(EFSCORRUPTED); } /* * Get the index into the agi hash table for the * list this inode will go on. */ agino = XFS_INO_TO_AGINO(mp, ip->i_ino); ASSERT(agino != 0); bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS; ASSERT(be32_to_cpu(agi->agi_unlinked[bucket_index]) != NULLAGINO); ASSERT(agi->agi_unlinked[bucket_index]); if (be32_to_cpu(agi->agi_unlinked[bucket_index]) == agino) { /* * We're at the head of the list. Get the inode's * on-disk buffer to see if there is anyone after us * on the list. Only modify our next pointer if it * is not already NULLAGINO. This saves us the overhead * of dealing with the buffer when there is no need to * change it. */ error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0, 0); if (error) { cmn_err(CE_WARN, "xfs_iunlink_remove: xfs_itobp() returned an error %d on %s. Returning error.", error, mp->m_fsname); return error; } next_agino = be32_to_cpu(dip->di_next_unlinked); ASSERT(next_agino != 0); if (next_agino != NULLAGINO) { dip->di_next_unlinked = cpu_to_be32(NULLAGINO); offset = ip->i_boffset + offsetof(xfs_dinode_t, di_next_unlinked); xfs_trans_inode_buf(tp, ibp); xfs_trans_log_buf(tp, ibp, offset, (offset + sizeof(xfs_agino_t) - 1)); xfs_inobp_check(mp, ibp); } else { xfs_trans_brelse(tp, ibp); } /* * Point the bucket head pointer at the next inode. */ ASSERT(next_agino != 0); ASSERT(next_agino != agino); agi->agi_unlinked[bucket_index] = cpu_to_be32(next_agino); offset = offsetof(xfs_agi_t, agi_unlinked) + (sizeof(xfs_agino_t) * bucket_index); xfs_trans_log_buf(tp, agibp, offset, (offset + sizeof(xfs_agino_t) - 1)); } else { /* * We need to search the list for the inode being freed. */ next_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]); last_ibp = NULL; while (next_agino != agino) { /* * If the last inode wasn't the one pointing to * us, then release its buffer since we're not * going to do anything with it. */ if (last_ibp != NULL) { xfs_trans_brelse(tp, last_ibp); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -