diff options
Diffstat (limited to 'fs/xfs/libxfs')
| -rw-r--r-- | fs/xfs/libxfs/xfs_ag_resv.c | 31 | ||||
| -rw-r--r-- | fs/xfs/libxfs/xfs_alloc.c | 5 | ||||
| -rw-r--r-- | fs/xfs/libxfs/xfs_bmap.c | 26 | ||||
| -rw-r--r-- | fs/xfs/libxfs/xfs_bmap.h | 2 | ||||
| -rw-r--r-- | fs/xfs/libxfs/xfs_format.h | 5 | ||||
| -rw-r--r-- | fs/xfs/libxfs/xfs_inode_buf.c | 82 | ||||
| -rw-r--r-- | fs/xfs/libxfs/xfs_rtbitmap.c | 4 | 
7 files changed, 116 insertions, 39 deletions
diff --git a/fs/xfs/libxfs/xfs_ag_resv.c b/fs/xfs/libxfs/xfs_ag_resv.c index 84db76e0e3e3..fecd187fcf2c 100644 --- a/fs/xfs/libxfs/xfs_ag_resv.c +++ b/fs/xfs/libxfs/xfs_ag_resv.c @@ -157,6 +157,7 @@ __xfs_ag_resv_free(  	error = xfs_mod_fdblocks(pag->pag_mount, oldresv, true);  	resv->ar_reserved = 0;  	resv->ar_asked = 0; +	resv->ar_orig_reserved = 0;  	if (error)  		trace_xfs_ag_resv_free_error(pag->pag_mount, pag->pag_agno, @@ -189,13 +190,34 @@ __xfs_ag_resv_init(  	struct xfs_mount		*mp = pag->pag_mount;  	struct xfs_ag_resv		*resv;  	int				error; -	xfs_extlen_t			reserved; +	xfs_extlen_t			hidden_space;  	if (used > ask)  		ask = used; -	reserved = ask - used; -	error = xfs_mod_fdblocks(mp, -(int64_t)reserved, true); +	switch (type) { +	case XFS_AG_RESV_RMAPBT: +		/* +		 * Space taken by the rmapbt is not subtracted from fdblocks +		 * because the rmapbt lives in the free space.  Here we must +		 * subtract the entire reservation from fdblocks so that we +		 * always have blocks available for rmapbt expansion. +		 */ +		hidden_space = ask; +		break; +	case XFS_AG_RESV_METADATA: +		/* +		 * Space taken by all other metadata btrees are accounted +		 * on-disk as used space.  We therefore only hide the space +		 * that is reserved but not used by the trees. +		 */ +		hidden_space = ask - used; +		break; +	default: +		ASSERT(0); +		return -EINVAL; +	} +	error = xfs_mod_fdblocks(mp, -(int64_t)hidden_space, true);  	if (error) {  		trace_xfs_ag_resv_init_error(pag->pag_mount, pag->pag_agno,  				error, _RET_IP_); @@ -216,7 +238,8 @@ __xfs_ag_resv_init(  	resv = xfs_perag_resv(pag, type);  	resv->ar_asked = ask; -	resv->ar_reserved = resv->ar_orig_reserved = reserved; +	resv->ar_orig_reserved = hidden_space; +	resv->ar_reserved = ask - used;  	trace_xfs_ag_resv_init(pag, type, ask);  	return 0; diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index eef466260d43..75dbdc14c45f 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -223,12 +223,13 @@ xfs_alloc_get_rec(  	error = xfs_btree_get_rec(cur, &rec, stat);  	if (error || !(*stat))  		return error; -	if (rec->alloc.ar_blockcount == 0) -		goto out_bad_rec;  	*bno = be32_to_cpu(rec->alloc.ar_startblock);  	*len = be32_to_cpu(rec->alloc.ar_blockcount); +	if (*len == 0) +		goto out_bad_rec; +  	/* check for valid extent range, including overflow */  	if (!xfs_verify_agbno(mp, agno, *bno))  		goto out_bad_rec; diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index 01628f0c9a0c..7205268b30bc 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -5780,6 +5780,32 @@ del_cursor:  	return error;  } +/* Make sure we won't be right-shifting an extent past the maximum bound. */ +int +xfs_bmap_can_insert_extents( +	struct xfs_inode	*ip, +	xfs_fileoff_t		off, +	xfs_fileoff_t		shift) +{ +	struct xfs_bmbt_irec	got; +	int			is_empty; +	int			error = 0; + +	ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); + +	if (XFS_FORCED_SHUTDOWN(ip->i_mount)) +		return -EIO; + +	xfs_ilock(ip, XFS_ILOCK_EXCL); +	error = xfs_bmap_last_extent(NULL, ip, XFS_DATA_FORK, &got, &is_empty); +	if (!error && !is_empty && got.br_startoff >= off && +	    ((got.br_startoff + shift) & BMBT_STARTOFF_MASK) < got.br_startoff) +		error = -EINVAL; +	xfs_iunlock(ip, XFS_ILOCK_EXCL); + +	return error; +} +  int  xfs_bmap_insert_extents(  	struct xfs_trans	*tp, diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h index 99dddbd0fcc6..9b49ddf99c41 100644 --- a/fs/xfs/libxfs/xfs_bmap.h +++ b/fs/xfs/libxfs/xfs_bmap.h @@ -227,6 +227,8 @@ int	xfs_bmap_collapse_extents(struct xfs_trans *tp, struct xfs_inode *ip,  		xfs_fileoff_t *next_fsb, xfs_fileoff_t offset_shift_fsb,  		bool *done, xfs_fsblock_t *firstblock,  		struct xfs_defer_ops *dfops); +int	xfs_bmap_can_insert_extents(struct xfs_inode *ip, xfs_fileoff_t off, +		xfs_fileoff_t shift);  int	xfs_bmap_insert_extents(struct xfs_trans *tp, struct xfs_inode *ip,  		xfs_fileoff_t *next_fsb, xfs_fileoff_t offset_shift_fsb,  		bool *done, xfs_fileoff_t stop_fsb, xfs_fsblock_t *firstblock, diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h index 1c5a8aaf2bfc..059bc44c27e8 100644 --- a/fs/xfs/libxfs/xfs_format.h +++ b/fs/xfs/libxfs/xfs_format.h @@ -962,6 +962,9 @@ typedef enum xfs_dinode_fmt {  		XFS_DFORK_DSIZE(dip, mp) : \  		XFS_DFORK_ASIZE(dip, mp)) +#define XFS_DFORK_MAXEXT(dip, mp, w) \ +	(XFS_DFORK_SIZE(dip, mp, w) / sizeof(struct xfs_bmbt_rec)) +  /*   * Return pointers to the data or attribute forks.   */ @@ -1526,6 +1529,8 @@ typedef struct xfs_bmdr_block {  #define BMBT_STARTBLOCK_BITLEN	52  #define BMBT_BLOCKCOUNT_BITLEN	21 +#define BMBT_STARTOFF_MASK	((1ULL << BMBT_STARTOFF_BITLEN) - 1) +  typedef struct xfs_bmbt_rec {  	__be64			l0, l1;  } xfs_bmbt_rec_t; diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index d38d724534c4..30d1d60f1d46 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -374,6 +374,47 @@ xfs_log_dinode_to_disk(  	}  } +static xfs_failaddr_t +xfs_dinode_verify_fork( +	struct xfs_dinode	*dip, +	struct xfs_mount	*mp, +	int			whichfork) +{ +	uint32_t		di_nextents = XFS_DFORK_NEXTENTS(dip, whichfork); + +	switch (XFS_DFORK_FORMAT(dip, whichfork)) { +	case XFS_DINODE_FMT_LOCAL: +		/* +		 * no local regular files yet +		 */ +		if (whichfork == XFS_DATA_FORK) { +			if (S_ISREG(be16_to_cpu(dip->di_mode))) +				return __this_address; +			if (be64_to_cpu(dip->di_size) > +					XFS_DFORK_SIZE(dip, mp, whichfork)) +				return __this_address; +		} +		if (di_nextents) +			return __this_address; +		break; +	case XFS_DINODE_FMT_EXTENTS: +		if (di_nextents > XFS_DFORK_MAXEXT(dip, mp, whichfork)) +			return __this_address; +		break; +	case XFS_DINODE_FMT_BTREE: +		if (whichfork == XFS_ATTR_FORK) { +			if (di_nextents > MAXAEXTNUM) +				return __this_address; +		} else if (di_nextents > MAXEXTNUM) { +			return __this_address; +		} +		break; +	default: +		return __this_address; +	} +	return NULL; +} +  xfs_failaddr_t  xfs_dinode_verify(  	struct xfs_mount	*mp, @@ -441,24 +482,9 @@ xfs_dinode_verify(  	case S_IFREG:  	case S_IFLNK:  	case S_IFDIR: -		switch (dip->di_format) { -		case XFS_DINODE_FMT_LOCAL: -			/* -			 * no local regular files yet -			 */ -			if (S_ISREG(mode)) -				return __this_address; -			if (di_size > XFS_DFORK_DSIZE(dip, mp)) -				return __this_address; -			if (dip->di_nextents) -				return __this_address; -			/* fall through */ -		case XFS_DINODE_FMT_EXTENTS: -		case XFS_DINODE_FMT_BTREE: -			break; -		default: -			return __this_address; -		} +		fa = xfs_dinode_verify_fork(dip, mp, XFS_DATA_FORK); +		if (fa) +			return fa;  		break;  	case 0:  		/* Uninitialized inode ok. */ @@ -468,17 +494,9 @@ xfs_dinode_verify(  	}  	if (XFS_DFORK_Q(dip)) { -		switch (dip->di_aformat) { -		case XFS_DINODE_FMT_LOCAL: -			if (dip->di_anextents) -				return __this_address; -		/* fall through */ -		case XFS_DINODE_FMT_EXTENTS: -		case XFS_DINODE_FMT_BTREE: -			break; -		default: -			return __this_address; -		} +		fa = xfs_dinode_verify_fork(dip, mp, XFS_ATTR_FORK); +		if (fa) +			return fa;  	} else {  		/*  		 * If there is no fork offset, this may be a freshly-made inode @@ -713,7 +731,8 @@ xfs_inode_validate_extsize(  	if ((hint_flag || inherit_flag) && extsize == 0)  		return __this_address; -	if (!(hint_flag || inherit_flag) && extsize != 0) +	/* free inodes get flags set to zero but extsize remains */ +	if (mode && !(hint_flag || inherit_flag) && extsize != 0)  		return __this_address;  	if (extsize_bytes % blocksize_bytes) @@ -759,7 +778,8 @@ xfs_inode_validate_cowextsize(  	if (hint_flag && cowextsize == 0)  		return __this_address; -	if (!hint_flag && cowextsize != 0) +	/* free inodes get flags set to zero but cowextsize remains */ +	if (mode && !hint_flag && cowextsize != 0)  		return __this_address;  	if (hint_flag && rt_flag) diff --git a/fs/xfs/libxfs/xfs_rtbitmap.c b/fs/xfs/libxfs/xfs_rtbitmap.c index 65fc4ed2e9a1..b228c821bae6 100644 --- a/fs/xfs/libxfs/xfs_rtbitmap.c +++ b/fs/xfs/libxfs/xfs_rtbitmap.c @@ -1029,8 +1029,8 @@ xfs_rtalloc_query_range(  	if (low_rec->ar_startext >= mp->m_sb.sb_rextents ||  	    low_rec->ar_startext == high_rec->ar_startext)  		return 0; -	if (high_rec->ar_startext >= mp->m_sb.sb_rextents) -		high_rec->ar_startext = mp->m_sb.sb_rextents - 1; +	if (high_rec->ar_startext > mp->m_sb.sb_rextents) +		high_rec->ar_startext = mp->m_sb.sb_rextents;  	/* Iterate the bitmap, looking for discrepancies. */  	rtstart = low_rec->ar_startext;  |