diff options
Diffstat (limited to 'fs/udf/inode.c')
| -rw-r--r-- | fs/udf/inode.c | 161 | 
1 files changed, 76 insertions, 85 deletions
diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 236cd48184c2..08598843288f 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -51,7 +51,6 @@ MODULE_LICENSE("GPL");  static umode_t udf_convert_permissions(struct fileEntry *);  static int udf_update_inode(struct inode *, int); -static void udf_fill_inode(struct inode *, struct buffer_head *);  static int udf_sync_inode(struct inode *inode);  static int udf_alloc_i_data(struct inode *inode, size_t size);  static sector_t inode_getblk(struct inode *, sector_t, int *, int *); @@ -1271,12 +1270,33 @@ update_time:  	return 0;  } -static void __udf_read_inode(struct inode *inode) +/* + * Maximum length of linked list formed by ICB hierarchy. The chosen number is + * arbitrary - just that we hopefully don't limit any real use of rewritten + * inode on write-once media but avoid looping for too long on corrupted media. + */ +#define UDF_MAX_ICB_NESTING 1024 + +static int udf_read_inode(struct inode *inode)  {  	struct buffer_head *bh = NULL;  	struct fileEntry *fe; +	struct extendedFileEntry *efe;  	uint16_t ident;  	struct udf_inode_info *iinfo = UDF_I(inode); +	struct udf_sb_info *sbi = UDF_SB(inode->i_sb); +	struct kernel_lb_addr *iloc = &iinfo->i_location; +	unsigned int link_count; +	unsigned int indirections = 0; +	int ret = -EIO; + +reread: +	if (iloc->logicalBlockNum >= +	    sbi->s_partmaps[iloc->partitionReferenceNum].s_partition_len) { +		udf_debug("block=%d, partition=%d out of range\n", +			  iloc->logicalBlockNum, iloc->partitionReferenceNum); +		return -EIO; +	}  	/*  	 * Set defaults, but the inode is still incomplete! @@ -1290,78 +1310,54 @@ static void __udf_read_inode(struct inode *inode)  	 *      i_nlink = 1  	 *      i_op = NULL;  	 */ -	bh = udf_read_ptagged(inode->i_sb, &iinfo->i_location, 0, &ident); +	bh = udf_read_ptagged(inode->i_sb, iloc, 0, &ident);  	if (!bh) {  		udf_err(inode->i_sb, "(ino %ld) failed !bh\n", inode->i_ino); -		make_bad_inode(inode); -		return; +		return -EIO;  	}  	if (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE &&  	    ident != TAG_IDENT_USE) {  		udf_err(inode->i_sb, "(ino %ld) failed ident=%d\n",  			inode->i_ino, ident); -		brelse(bh); -		make_bad_inode(inode); -		return; +		goto out;  	}  	fe = (struct fileEntry *)bh->b_data; +	efe = (struct extendedFileEntry *)bh->b_data;  	if (fe->icbTag.strategyType == cpu_to_le16(4096)) {  		struct buffer_head *ibh; -		ibh = udf_read_ptagged(inode->i_sb, &iinfo->i_location, 1, -					&ident); +		ibh = udf_read_ptagged(inode->i_sb, iloc, 1, &ident);  		if (ident == TAG_IDENT_IE && ibh) { -			struct buffer_head *nbh = NULL;  			struct kernel_lb_addr loc;  			struct indirectEntry *ie;  			ie = (struct indirectEntry *)ibh->b_data;  			loc = lelb_to_cpu(ie->indirectICB.extLocation); -			if (ie->indirectICB.extLength && -				(nbh = udf_read_ptagged(inode->i_sb, &loc, 0, -							&ident))) { -				if (ident == TAG_IDENT_FE || -					ident == TAG_IDENT_EFE) { -					memcpy(&iinfo->i_location, -						&loc, -						sizeof(struct kernel_lb_addr)); -					brelse(bh); -					brelse(ibh); -					brelse(nbh); -					__udf_read_inode(inode); -					return; +			if (ie->indirectICB.extLength) { +				brelse(ibh); +				memcpy(&iinfo->i_location, &loc, +				       sizeof(struct kernel_lb_addr)); +				if (++indirections > UDF_MAX_ICB_NESTING) { +					udf_err(inode->i_sb, +						"too many ICBs in ICB hierarchy" +						" (max %d supported)\n", +						UDF_MAX_ICB_NESTING); +					goto out;  				} -				brelse(nbh); +				brelse(bh); +				goto reread;  			}  		}  		brelse(ibh);  	} else if (fe->icbTag.strategyType != cpu_to_le16(4)) {  		udf_err(inode->i_sb, "unsupported strategy type: %d\n",  			le16_to_cpu(fe->icbTag.strategyType)); -		brelse(bh); -		make_bad_inode(inode); -		return; +		goto out;  	} -	udf_fill_inode(inode, bh); - -	brelse(bh); -} - -static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) -{ -	struct fileEntry *fe; -	struct extendedFileEntry *efe; -	struct udf_sb_info *sbi = UDF_SB(inode->i_sb); -	struct udf_inode_info *iinfo = UDF_I(inode); -	unsigned int link_count; - -	fe = (struct fileEntry *)bh->b_data; -	efe = (struct extendedFileEntry *)bh->b_data; -  	if (fe->icbTag.strategyType == cpu_to_le16(4))  		iinfo->i_strat4096 = 0;  	else /* if (fe->icbTag.strategyType == cpu_to_le16(4096)) */ @@ -1378,11 +1374,10 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)  	if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_EFE)) {  		iinfo->i_efe = 1;  		iinfo->i_use = 0; -		if (udf_alloc_i_data(inode, inode->i_sb->s_blocksize - -					sizeof(struct extendedFileEntry))) { -			make_bad_inode(inode); -			return; -		} +		ret = udf_alloc_i_data(inode, inode->i_sb->s_blocksize - +					sizeof(struct extendedFileEntry)); +		if (ret) +			goto out;  		memcpy(iinfo->i_ext.i_data,  		       bh->b_data + sizeof(struct extendedFileEntry),  		       inode->i_sb->s_blocksize - @@ -1390,11 +1385,10 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)  	} else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_FE)) {  		iinfo->i_efe = 0;  		iinfo->i_use = 0; -		if (udf_alloc_i_data(inode, inode->i_sb->s_blocksize - -						sizeof(struct fileEntry))) { -			make_bad_inode(inode); -			return; -		} +		ret = udf_alloc_i_data(inode, inode->i_sb->s_blocksize - +						sizeof(struct fileEntry)); +		if (ret) +			goto out;  		memcpy(iinfo->i_ext.i_data,  		       bh->b_data + sizeof(struct fileEntry),  		       inode->i_sb->s_blocksize - sizeof(struct fileEntry)); @@ -1404,18 +1398,18 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)  		iinfo->i_lenAlloc = le32_to_cpu(  				((struct unallocSpaceEntry *)bh->b_data)->  				 lengthAllocDescs); -		if (udf_alloc_i_data(inode, inode->i_sb->s_blocksize - -					sizeof(struct unallocSpaceEntry))) { -			make_bad_inode(inode); -			return; -		} +		ret = udf_alloc_i_data(inode, inode->i_sb->s_blocksize - +					sizeof(struct unallocSpaceEntry)); +		if (ret) +			goto out;  		memcpy(iinfo->i_ext.i_data,  		       bh->b_data + sizeof(struct unallocSpaceEntry),  		       inode->i_sb->s_blocksize -  					sizeof(struct unallocSpaceEntry)); -		return; +		return 0;  	} +	ret = -EIO;  	read_lock(&sbi->s_cred_lock);  	i_uid_write(inode, le32_to_cpu(fe->uid));  	if (!uid_valid(inode->i_uid) || @@ -1441,8 +1435,10 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)  	read_unlock(&sbi->s_cred_lock);  	link_count = le16_to_cpu(fe->fileLinkCount); -	if (!link_count) -		link_count = 1; +	if (!link_count) { +		ret = -ESTALE; +		goto out; +	}  	set_nlink(inode, link_count);  	inode->i_size = le64_to_cpu(fe->informationLength); @@ -1488,6 +1484,7 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)  		iinfo->i_lenAlloc = le32_to_cpu(efe->lengthAllocDescs);  		iinfo->i_checkpoint = le32_to_cpu(efe->checkpoint);  	} +	inode->i_generation = iinfo->i_unique;  	switch (fe->icbTag.fileType) {  	case ICBTAG_FILE_TYPE_DIRECTORY: @@ -1537,8 +1534,7 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)  	default:  		udf_err(inode->i_sb, "(ino %ld) failed unknown file type=%d\n",  			inode->i_ino, fe->icbTag.fileType); -		make_bad_inode(inode); -		return; +		goto out;  	}  	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {  		struct deviceSpec *dsea = @@ -1549,8 +1545,12 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)  				      le32_to_cpu(dsea->minorDeviceIdent)));  			/* Developer ID ??? */  		} else -			make_bad_inode(inode); +			goto out;  	} +	ret = 0; +out: +	brelse(bh); +	return ret;  }  static int udf_alloc_i_data(struct inode *inode, size_t size) @@ -1664,7 +1664,7 @@ static int udf_update_inode(struct inode *inode, int do_sync)  		     FE_PERM_U_DELETE | FE_PERM_U_CHATTR));  	fe->permissions = cpu_to_le32(udfperms); -	if (S_ISDIR(inode->i_mode)) +	if (S_ISDIR(inode->i_mode) && inode->i_nlink > 0)  		fe->fileLinkCount = cpu_to_le16(inode->i_nlink - 1);  	else  		fe->fileLinkCount = cpu_to_le16(inode->i_nlink); @@ -1830,32 +1830,23 @@ struct inode *udf_iget(struct super_block *sb, struct kernel_lb_addr *ino)  {  	unsigned long block = udf_get_lb_pblock(sb, ino, 0);  	struct inode *inode = iget_locked(sb, block); +	int err;  	if (!inode) -		return NULL; - -	if (inode->i_state & I_NEW) { -		memcpy(&UDF_I(inode)->i_location, ino, sizeof(struct kernel_lb_addr)); -		__udf_read_inode(inode); -		unlock_new_inode(inode); -	} +		return ERR_PTR(-ENOMEM); -	if (is_bad_inode(inode)) -		goto out_iput; +	if (!(inode->i_state & I_NEW)) +		return inode; -	if (ino->logicalBlockNum >= UDF_SB(sb)-> -			s_partmaps[ino->partitionReferenceNum].s_partition_len) { -		udf_debug("block=%d, partition=%d out of range\n", -			  ino->logicalBlockNum, ino->partitionReferenceNum); -		make_bad_inode(inode); -		goto out_iput; +	memcpy(&UDF_I(inode)->i_location, ino, sizeof(struct kernel_lb_addr)); +	err = udf_read_inode(inode); +	if (err < 0) { +		iget_failed(inode); +		return ERR_PTR(err);  	} +	unlock_new_inode(inode);  	return inode; - - out_iput: -	iput(inode); -	return NULL;  }  int udf_add_aext(struct inode *inode, struct extent_position *epos,  |