Esempio n. 1
0
int
inode_a_size(
	void			*obj,
	int			startoff,
	int			idx)
{
	xfs_attr_shortform_t	*asf;
	xfs_dinode_t		*dip;

	ASSERT(startoff == 0);
	ASSERT(idx == 0);
	dip = obj;
	switch (dip->di_aformat) {
	case XFS_DINODE_FMT_LOCAL:
		asf = (xfs_attr_shortform_t *)XFS_DFORK_APTR(dip);
		return bitize(be16_to_cpu(asf->hdr.totsize));
	case XFS_DINODE_FMT_EXTENTS:
		return (int)be16_to_cpu(dip->di_anextents) *
							bitsz(xfs_bmbt_rec_t);
	case XFS_DINODE_FMT_BTREE:
		return bitize((int)XFS_DFORK_ASIZE(dip, mp));
	default:
		return 0;
	}
}
Esempio n. 2
0
/*
 * returns 1 if attributes got cleared
 * and 0 if things are ok.
 */
int
process_attributes(
	xfs_mount_t	*mp,
	xfs_ino_t	ino,
	xfs_dinode_t	*dip,
	blkmap_t	*blkmap,
	int		*repair)  /* returned if we did repair */
{
	int		err;
	__u8		aformat = dip->di_aformat;
#ifdef DEBUG
	xfs_attr_shortform_t *asf;

	asf = (xfs_attr_shortform_t *) XFS_DFORK_APTR(dip);
#endif

	if (aformat == XFS_DINODE_FMT_LOCAL) {
		ASSERT(be16_to_cpu(asf->hdr.totsize) <=
			XFS_DFORK_ASIZE(dip, mp));
		err = process_shortform_attr(mp, ino, dip, repair);
	} else if (aformat == XFS_DINODE_FMT_EXTENTS ||
					aformat == XFS_DINODE_FMT_BTREE)  {
			err = process_longform_attr(mp, ino, dip, blkmap,
				repair);
			/* if err, convert this to shortform and clear it */
			/* if repair and no error, it's taken care of */
	} else  {
		do_warn(_("illegal attribute format %d, ino %" PRIu64 "\n"),
			aformat, ino);
		err = 1;
	}
	return (err);  /* and repair */
}
Esempio n. 3
0
int
xfs_dfork_asize(xfs_dinode_t *dip, xfs_mount_t *mp)
{
    return XFS_DFORK_ASIZE(dip, mp);
}
Esempio n. 4
0
/* Scrub all the ondisk inode fields. */
STATIC void
xchk_dinode(
	struct xfs_scrub	*sc,
	struct xfs_dinode	*dip,
	xfs_ino_t		ino)
{
	struct xfs_mount	*mp = sc->mp;
	size_t			fork_recs;
	unsigned long long	isize;
	uint64_t		flags2;
	uint32_t		nextents;
	uint16_t		flags;
	uint16_t		mode;

	flags = be16_to_cpu(dip->di_flags);
	if (dip->di_version >= 3)
		flags2 = be64_to_cpu(dip->di_flags2);
	else
		flags2 = 0;

	/* di_mode */
	mode = be16_to_cpu(dip->di_mode);
	switch (mode & S_IFMT) {
	case S_IFLNK:
	case S_IFREG:
	case S_IFDIR:
	case S_IFCHR:
	case S_IFBLK:
	case S_IFIFO:
	case S_IFSOCK:
		/* mode is recognized */
		break;
	default:
		xchk_ino_set_corrupt(sc, ino);
		break;
	}

	/* v1/v2 fields */
	switch (dip->di_version) {
	case 1:
		/*
		 * We autoconvert v1 inodes into v2 inodes on writeout,
		 * so just mark this inode for preening.
		 */
		xchk_ino_set_preen(sc, ino);
		break;
	case 2:
	case 3:
		if (dip->di_onlink != 0)
			xchk_ino_set_corrupt(sc, ino);

		if (dip->di_mode == 0 && sc->ip)
			xchk_ino_set_corrupt(sc, ino);

		if (dip->di_projid_hi != 0 &&
		    !xfs_sb_version_hasprojid32bit(&mp->m_sb))
			xchk_ino_set_corrupt(sc, ino);
		break;
	default:
		xchk_ino_set_corrupt(sc, ino);
		return;
	}

	/*
	 * di_uid/di_gid -- -1 isn't invalid, but there's no way that
	 * userspace could have created that.
	 */
	if (dip->di_uid == cpu_to_be32(-1U) ||
	    dip->di_gid == cpu_to_be32(-1U))
		xchk_ino_set_warning(sc, ino);

	/* di_format */
	switch (dip->di_format) {
	case XFS_DINODE_FMT_DEV:
		if (!S_ISCHR(mode) && !S_ISBLK(mode) &&
		    !S_ISFIFO(mode) && !S_ISSOCK(mode))
			xchk_ino_set_corrupt(sc, ino);
		break;
	case XFS_DINODE_FMT_LOCAL:
		if (!S_ISDIR(mode) && !S_ISLNK(mode))
			xchk_ino_set_corrupt(sc, ino);
		break;
	case XFS_DINODE_FMT_EXTENTS:
		if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode))
			xchk_ino_set_corrupt(sc, ino);
		break;
	case XFS_DINODE_FMT_BTREE:
		if (!S_ISREG(mode) && !S_ISDIR(mode))
			xchk_ino_set_corrupt(sc, ino);
		break;
	case XFS_DINODE_FMT_UUID:
	default:
		xchk_ino_set_corrupt(sc, ino);
		break;
	}

	/* di_[amc]time.nsec */
	if (be32_to_cpu(dip->di_atime.t_nsec) >= NSEC_PER_SEC)
		xchk_ino_set_corrupt(sc, ino);
	if (be32_to_cpu(dip->di_mtime.t_nsec) >= NSEC_PER_SEC)
		xchk_ino_set_corrupt(sc, ino);
	if (be32_to_cpu(dip->di_ctime.t_nsec) >= NSEC_PER_SEC)
		xchk_ino_set_corrupt(sc, ino);

	/*
	 * di_size.  xfs_dinode_verify checks for things that screw up
	 * the VFS such as the upper bit being set and zero-length
	 * symlinks/directories, but we can do more here.
	 */
	isize = be64_to_cpu(dip->di_size);
	if (isize & (1ULL << 63))
		xchk_ino_set_corrupt(sc, ino);

	/* Devices, fifos, and sockets must have zero size */
	if (!S_ISDIR(mode) && !S_ISREG(mode) && !S_ISLNK(mode) && isize != 0)
		xchk_ino_set_corrupt(sc, ino);

	/* Directories can't be larger than the data section size (32G) */
	if (S_ISDIR(mode) && (isize == 0 || isize >= XFS_DIR2_SPACE_SIZE))
		xchk_ino_set_corrupt(sc, ino);

	/* Symlinks can't be larger than SYMLINK_MAXLEN */
	if (S_ISLNK(mode) && (isize == 0 || isize >= XFS_SYMLINK_MAXLEN))
		xchk_ino_set_corrupt(sc, ino);

	/*
	 * Warn if the running kernel can't handle the kinds of offsets
	 * needed to deal with the file size.  In other words, if the
	 * pagecache can't cache all the blocks in this file due to
	 * overly large offsets, flag the inode for admin review.
	 */
	if (isize >= mp->m_super->s_maxbytes)
		xchk_ino_set_warning(sc, ino);

	/* di_nblocks */
	if (flags2 & XFS_DIFLAG2_REFLINK) {
		; /* nblocks can exceed dblocks */
	} else if (flags & XFS_DIFLAG_REALTIME) {
		/*
		 * nblocks is the sum of data extents (in the rtdev),
		 * attr extents (in the datadev), and both forks' bmbt
		 * blocks (in the datadev).  This clumsy check is the
		 * best we can do without cross-referencing with the
		 * inode forks.
		 */
		if (be64_to_cpu(dip->di_nblocks) >=
		    mp->m_sb.sb_dblocks + mp->m_sb.sb_rblocks)
			xchk_ino_set_corrupt(sc, ino);
	} else {
		if (be64_to_cpu(dip->di_nblocks) >= mp->m_sb.sb_dblocks)
			xchk_ino_set_corrupt(sc, ino);
	}

	xchk_inode_flags(sc, dip, ino, mode, flags);

	xchk_inode_extsize(sc, dip, ino, mode, flags);

	/* di_nextents */
	nextents = be32_to_cpu(dip->di_nextents);
	fork_recs =  XFS_DFORK_DSIZE(dip, mp) / sizeof(struct xfs_bmbt_rec);
	switch (dip->di_format) {
	case XFS_DINODE_FMT_EXTENTS:
		if (nextents > fork_recs)
			xchk_ino_set_corrupt(sc, ino);
		break;
	case XFS_DINODE_FMT_BTREE:
		if (nextents <= fork_recs)
			xchk_ino_set_corrupt(sc, ino);
		break;
	default:
		if (nextents != 0)
			xchk_ino_set_corrupt(sc, ino);
		break;
	}

	/* di_forkoff */
	if (XFS_DFORK_APTR(dip) >= (char *)dip + mp->m_sb.sb_inodesize)
		xchk_ino_set_corrupt(sc, ino);
	if (dip->di_anextents != 0 && dip->di_forkoff == 0)
		xchk_ino_set_corrupt(sc, ino);
	if (dip->di_forkoff == 0 && dip->di_aformat != XFS_DINODE_FMT_EXTENTS)
		xchk_ino_set_corrupt(sc, ino);

	/* di_aformat */
	if (dip->di_aformat != XFS_DINODE_FMT_LOCAL &&
	    dip->di_aformat != XFS_DINODE_FMT_EXTENTS &&
	    dip->di_aformat != XFS_DINODE_FMT_BTREE)
		xchk_ino_set_corrupt(sc, ino);

	/* di_anextents */
	nextents = be16_to_cpu(dip->di_anextents);
	fork_recs =  XFS_DFORK_ASIZE(dip, mp) / sizeof(struct xfs_bmbt_rec);
	switch (dip->di_aformat) {
	case XFS_DINODE_FMT_EXTENTS:
		if (nextents > fork_recs)
			xchk_ino_set_corrupt(sc, ino);
		break;
	case XFS_DINODE_FMT_BTREE:
		if (nextents <= fork_recs)
			xchk_ino_set_corrupt(sc, ino);
		break;
	default:
		if (nextents != 0)
			xchk_ino_set_corrupt(sc, ino);
	}

	if (dip->di_version >= 3) {
		if (be32_to_cpu(dip->di_crtime.t_nsec) >= NSEC_PER_SEC)
			xchk_ino_set_corrupt(sc, ino);
		xchk_inode_flags2(sc, dip, ino, mode, flags, flags2);
		xchk_inode_cowextsize(sc, dip, ino, mode, flags,
				flags2);
	}
}