📄 xfs_inode.c
字号:
/* * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */#include "xfs.h"#include "xfs_fs.h"#include "xfs_types.h"#include "xfs_bit.h"#include "xfs_log.h"#include "xfs_inum.h"#include "xfs_imap.h"#include "xfs_trans.h"#include "xfs_trans_priv.h"#include "xfs_sb.h"#include "xfs_ag.h"#include "xfs_dir2.h"#include "xfs_dmapi.h"#include "xfs_mount.h"#include "xfs_bmap_btree.h"#include "xfs_alloc_btree.h"#include "xfs_ialloc_btree.h"#include "xfs_dir2_sf.h"#include "xfs_attr_sf.h"#include "xfs_dinode.h"#include "xfs_inode.h"#include "xfs_buf_item.h"#include "xfs_inode_item.h"#include "xfs_btree.h"#include "xfs_alloc.h"#include "xfs_ialloc.h"#include "xfs_bmap.h"#include "xfs_rw.h"#include "xfs_error.h"#include "xfs_utils.h"#include "xfs_dir2_trace.h"#include "xfs_quota.h"#include "xfs_acl.h"#include "xfs_filestream.h"#include "xfs_vnodeops.h"kmem_zone_t *xfs_ifork_zone;kmem_zone_t *xfs_inode_zone;kmem_zone_t *xfs_icluster_zone;/* * Used in xfs_itruncate(). This is the maximum number of extents * freed from a file in a single transaction. */#define XFS_ITRUNC_MAX_EXTENTS 2STATIC int xfs_iflush_int(xfs_inode_t *, xfs_buf_t *);STATIC int xfs_iformat_local(xfs_inode_t *, xfs_dinode_t *, int, int);STATIC int xfs_iformat_extents(xfs_inode_t *, xfs_dinode_t *, int);STATIC int xfs_iformat_btree(xfs_inode_t *, xfs_dinode_t *, int);#ifdef DEBUG/* * Make sure that the extents in the given memory buffer * are valid. */STATIC voidxfs_validate_extents( xfs_ifork_t *ifp, int nrecs, xfs_exntfmt_t fmt){ xfs_bmbt_irec_t irec; xfs_bmbt_rec_host_t rec; int i; for (i = 0; i < nrecs; i++) { xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i); rec.l0 = get_unaligned(&ep->l0); rec.l1 = get_unaligned(&ep->l1); xfs_bmbt_get_all(&rec, &irec); if (fmt == XFS_EXTFMT_NOSTATE) ASSERT(irec.br_state == XFS_EXT_NORM); }}#else /* DEBUG */#define xfs_validate_extents(ifp, nrecs, fmt)#endif /* DEBUG *//* * Check that none of the inode's in the buffer have a next * unlinked field of 0. */#if defined(DEBUG)voidxfs_inobp_check( xfs_mount_t *mp, xfs_buf_t *bp){ int i; int j; xfs_dinode_t *dip; j = mp->m_inode_cluster_size >> mp->m_sb.sb_inodelog; for (i = 0; i < j; i++) { dip = (xfs_dinode_t *)xfs_buf_offset(bp, i * mp->m_sb.sb_inodesize); if (!dip->di_next_unlinked) { xfs_fs_cmn_err(CE_ALERT, mp, "Detected a bogus zero next_unlinked field in incore inode buffer 0x%p. About to pop an ASSERT.", bp); ASSERT(dip->di_next_unlinked); } }}#endif/* * This routine is called to map an inode number within a file * system to the buffer containing the on-disk version of the * inode. It returns a pointer to the buffer containing the * on-disk inode in the bpp parameter, and in the dip parameter * it returns a pointer to the on-disk inode within that buffer. * * If a non-zero error is returned, then the contents of bpp and * dipp are undefined. * * Use xfs_imap() to determine the size and location of the * buffer to read from disk. */STATIC intxfs_inotobp( xfs_mount_t *mp, xfs_trans_t *tp, xfs_ino_t ino, xfs_dinode_t **dipp, xfs_buf_t **bpp, int *offset){ int di_ok; xfs_imap_t imap; xfs_buf_t *bp; int error; xfs_dinode_t *dip; /* * Call the space management code to find the location of the * inode on disk. */ imap.im_blkno = 0; error = xfs_imap(mp, tp, ino, &imap, XFS_IMAP_LOOKUP); if (error != 0) { cmn_err(CE_WARN, "xfs_inotobp: xfs_imap() returned an " "error %d on %s. Returning error.", error, mp->m_fsname); return error; } /* * If the inode number maps to a block outside the bounds of the * file system then return NULL rather than calling read_buf * and panicing when we get an error from the driver. */ if ((imap.im_blkno + imap.im_len) > XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)) { cmn_err(CE_WARN, "xfs_inotobp: inode number (%llu + %d) maps to a block outside the bounds " "of the file system %s. Returning EINVAL.", (unsigned long long)imap.im_blkno, imap.im_len, mp->m_fsname); return XFS_ERROR(EINVAL); } /* * Read in the buffer. If tp is NULL, xfs_trans_read_buf() will * default to just a read_buf() call. */ error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, imap.im_blkno, (int)imap.im_len, XFS_BUF_LOCK, &bp); if (error) { cmn_err(CE_WARN, "xfs_inotobp: xfs_trans_read_buf() returned an " "error %d on %s. Returning error.", error, mp->m_fsname); return error; } dip = (xfs_dinode_t *)xfs_buf_offset(bp, 0); di_ok = be16_to_cpu(dip->di_core.di_magic) == XFS_DINODE_MAGIC && XFS_DINODE_GOOD_VERSION(dip->di_core.di_version); if (unlikely(XFS_TEST_ERROR(!di_ok, mp, XFS_ERRTAG_ITOBP_INOTOBP, XFS_RANDOM_ITOBP_INOTOBP))) { XFS_CORRUPTION_ERROR("xfs_inotobp", XFS_ERRLEVEL_LOW, mp, dip); xfs_trans_brelse(tp, bp); cmn_err(CE_WARN, "xfs_inotobp: XFS_TEST_ERROR() returned an " "error on %s. Returning EFSCORRUPTED.", mp->m_fsname); return XFS_ERROR(EFSCORRUPTED); } xfs_inobp_check(mp, bp); /* * Set *dipp to point to the on-disk inode in the buffer. */ *dipp = (xfs_dinode_t *)xfs_buf_offset(bp, imap.im_boffset); *bpp = bp; *offset = imap.im_boffset; return 0;}/* * This routine is called to map an inode to the buffer containing * the on-disk version of the inode. It returns a pointer to the * buffer containing the on-disk inode in the bpp parameter, and in * the dip parameter it returns a pointer to the on-disk inode within * that buffer. * * If a non-zero error is returned, then the contents of bpp and * dipp are undefined. * * If the inode is new and has not yet been initialized, use xfs_imap() * to determine the size and location of the buffer to read from disk. * If the inode has already been mapped to its buffer and read in once, * then use the mapping information stored in the inode rather than * calling xfs_imap(). This allows us to avoid the overhead of looking * at the inode btree for small block file systems (see xfs_dilocate()). * We can tell whether the inode has been mapped in before by comparing * its disk block address to 0. Only uninitialized inodes will have * 0 for the disk block address. */intxfs_itobp( xfs_mount_t *mp, xfs_trans_t *tp, xfs_inode_t *ip, xfs_dinode_t **dipp, xfs_buf_t **bpp, xfs_daddr_t bno, uint imap_flags){ xfs_imap_t imap; xfs_buf_t *bp; int error; int i; int ni; if (ip->i_blkno == (xfs_daddr_t)0) { /* * Call the space management code to find the location of the * inode on disk. */ imap.im_blkno = bno; if ((error = xfs_imap(mp, tp, ip->i_ino, &imap, XFS_IMAP_LOOKUP | imap_flags))) return error; /* * If the inode number maps to a block outside the bounds * of the file system then return NULL rather than calling * read_buf and panicing when we get an error from the * driver. */ if ((imap.im_blkno + imap.im_len) > XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)) {#ifdef DEBUG xfs_fs_cmn_err(CE_ALERT, mp, "xfs_itobp: " "(imap.im_blkno (0x%llx) " "+ imap.im_len (0x%llx)) > " " XFS_FSB_TO_BB(mp, " "mp->m_sb.sb_dblocks) (0x%llx)", (unsigned long long) imap.im_blkno, (unsigned long long) imap.im_len, XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks));#endif /* DEBUG */ return XFS_ERROR(EINVAL); } /* * Fill in the fields in the inode that will be used to * map the inode to its buffer from now on. */ ip->i_blkno = imap.im_blkno; ip->i_len = imap.im_len; ip->i_boffset = imap.im_boffset; } else { /* * We've already mapped the inode once, so just use the * mapping that we saved the first time. */ imap.im_blkno = ip->i_blkno; imap.im_len = ip->i_len; imap.im_boffset = ip->i_boffset; } ASSERT(bno == 0 || bno == imap.im_blkno); /* * Read in the buffer. If tp is NULL, xfs_trans_read_buf() will * default to just a read_buf() call. */ error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, imap.im_blkno, (int)imap.im_len, XFS_BUF_LOCK, &bp); if (error) {#ifdef DEBUG xfs_fs_cmn_err(CE_ALERT, mp, "xfs_itobp: " "xfs_trans_read_buf() returned error %d, " "imap.im_blkno 0x%llx, imap.im_len 0x%llx", error, (unsigned long long) imap.im_blkno, (unsigned long long) imap.im_len);#endif /* DEBUG */ return error; } /* * Validate the magic number and version of every inode in the buffer * (if DEBUG kernel) or the first inode in the buffer, otherwise. * No validation is done here in userspace (xfs_repair). */#if !defined(__KERNEL__) ni = 0;#elif defined(DEBUG) ni = BBTOB(imap.im_len) >> mp->m_sb.sb_inodelog;#else /* usual case */ ni = 1;#endif for (i = 0; i < ni; i++) { int di_ok; xfs_dinode_t *dip; dip = (xfs_dinode_t *)xfs_buf_offset(bp, (i << mp->m_sb.sb_inodelog)); di_ok = be16_to_cpu(dip->di_core.di_magic) == XFS_DINODE_MAGIC && XFS_DINODE_GOOD_VERSION(dip->di_core.di_version); if (unlikely(XFS_TEST_ERROR(!di_ok, mp, XFS_ERRTAG_ITOBP_INOTOBP, XFS_RANDOM_ITOBP_INOTOBP))) { if (imap_flags & XFS_IMAP_BULKSTAT) { xfs_trans_brelse(tp, bp); return XFS_ERROR(EINVAL); }#ifdef DEBUG cmn_err(CE_ALERT, "Device %s - bad inode magic/vsn " "daddr %lld #%d (magic=%x)", XFS_BUFTARG_NAME(mp->m_ddev_targp), (unsigned long long)imap.im_blkno, i, be16_to_cpu(dip->di_core.di_magic));#endif XFS_CORRUPTION_ERROR("xfs_itobp", XFS_ERRLEVEL_HIGH, mp, dip); xfs_trans_brelse(tp, bp); return XFS_ERROR(EFSCORRUPTED); } } xfs_inobp_check(mp, bp); /* * Mark the buffer as an inode buffer now that it looks good */ XFS_BUF_SET_VTYPE(bp, B_FS_INO); /* * Set *dipp to point to the on-disk inode in the buffer. */ *dipp = (xfs_dinode_t *)xfs_buf_offset(bp, imap.im_boffset); *bpp = bp; return 0;}/* * Move inode type and inode format specific information from the * on-disk inode to the in-core inode. For fifos, devs, and sockets * this means set if_rdev to the proper value. For files, directories, * and symlinks this means to bring in the in-line data or extent * pointers. For a file in B-tree format, only the root is immediately * brought in-core. The rest will be in-lined in if_extents when it * is first referenced (see xfs_iread_extents()). */STATIC intxfs_iformat( xfs_inode_t *ip, xfs_dinode_t *dip){ xfs_attr_shortform_t *atp; int size; int error; xfs_fsize_t di_size; ip->i_df.if_ext_max = XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t); error = 0; if (unlikely(be32_to_cpu(dip->di_core.di_nextents) + be16_to_cpu(dip->di_core.di_anextents) > be64_to_cpu(dip->di_core.di_nblocks))) { xfs_fs_repair_cmn_err(CE_WARN, ip->i_mount, "corrupt dinode %Lu, extent total = %d, nblocks = %Lu.", (unsigned long long)ip->i_ino, (int)(be32_to_cpu(dip->di_core.di_nextents) + be16_to_cpu(dip->di_core.di_anextents)), (unsigned long long) be64_to_cpu(dip->di_core.di_nblocks)); XFS_CORRUPTION_ERROR("xfs_iformat(1)", XFS_ERRLEVEL_LOW, ip->i_mount, dip); return XFS_ERROR(EFSCORRUPTED); } if (unlikely(dip->di_core.di_forkoff > ip->i_mount->m_sb.sb_inodesize)) { xfs_fs_repair_cmn_err(CE_WARN, ip->i_mount, "corrupt dinode %Lu, forkoff = 0x%x.", (unsigned long long)ip->i_ino, dip->di_core.di_forkoff); XFS_CORRUPTION_ERROR("xfs_iformat(2)", XFS_ERRLEVEL_LOW, ip->i_mount, dip); return XFS_ERROR(EFSCORRUPTED); } switch (ip->i_d.di_mode & S_IFMT) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -