mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	udf: Convert udf_rename() to new directory iteration code
Convert udf_rename() to use new directory iteration code. Reported-by: syzbot+0eaad3590d65102b9391@syzkaller.appspotmail.com Reported-by: syzbot+b7fc73213bc2361ab650@syzkaller.appspotmail.com Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
		
							parent
							
								
									dbfb102d16
								
							
						
					
					
						commit
						e9109a92d2
					
				
					 1 changed files with 78 additions and 87 deletions
				
			
		
							
								
								
									
										165
									
								
								fs/udf/namei.c
									
									
									
									
									
								
							
							
						
						
									
										165
									
								
								fs/udf/namei.c
									
									
									
									
									
								
							|  | @ -1257,78 +1257,68 @@ static int udf_rename(struct user_namespace *mnt_userns, struct inode *old_dir, | ||||||
| { | { | ||||||
| 	struct inode *old_inode = d_inode(old_dentry); | 	struct inode *old_inode = d_inode(old_dentry); | ||||||
| 	struct inode *new_inode = d_inode(new_dentry); | 	struct inode *new_inode = d_inode(new_dentry); | ||||||
| 	struct udf_fileident_bh ofibh, nfibh; | 	struct udf_fileident_iter oiter, niter, diriter; | ||||||
| 	struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL; | 	bool has_diriter = false; | ||||||
| 	struct fileIdentDesc ocfi, ncfi; | 	int retval; | ||||||
| 	struct buffer_head *dir_bh = NULL; |  | ||||||
| 	int retval = -ENOENT; |  | ||||||
| 	struct kernel_lb_addr tloc; | 	struct kernel_lb_addr tloc; | ||||||
| 	struct udf_inode_info *old_iinfo = UDF_I(old_inode); |  | ||||||
| 
 | 
 | ||||||
| 	if (flags & ~RENAME_NOREPLACE) | 	if (flags & ~RENAME_NOREPLACE) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi); | 	retval = udf_fiiter_find_entry(old_dir, &old_dentry->d_name, &oiter); | ||||||
| 	if (!ofi || IS_ERR(ofi)) { | 	if (retval) | ||||||
| 		if (IS_ERR(ofi)) | 		return retval; | ||||||
| 			retval = PTR_ERR(ofi); | 
 | ||||||
| 		goto end_rename; | 	tloc = lelb_to_cpu(oiter.fi.icb.extLocation); | ||||||
|  | 	if (udf_get_lb_pblock(old_dir->i_sb, &tloc, 0) != old_inode->i_ino) { | ||||||
|  | 		retval = -ENOENT; | ||||||
|  | 		goto out_oiter; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (ofibh.sbh != ofibh.ebh) |  | ||||||
| 		brelse(ofibh.ebh); |  | ||||||
| 
 |  | ||||||
| 	brelse(ofibh.sbh); |  | ||||||
| 	tloc = lelb_to_cpu(ocfi.icb.extLocation); |  | ||||||
| 	if (udf_get_lb_pblock(old_dir->i_sb, &tloc, 0) != old_inode->i_ino) |  | ||||||
| 		goto end_rename; |  | ||||||
| 
 |  | ||||||
| 	nfi = udf_find_entry(new_dir, &new_dentry->d_name, &nfibh, &ncfi); |  | ||||||
| 	if (IS_ERR(nfi)) { |  | ||||||
| 		retval = PTR_ERR(nfi); |  | ||||||
| 		goto end_rename; |  | ||||||
| 	} |  | ||||||
| 	if (nfi && !new_inode) { |  | ||||||
| 		if (nfibh.sbh != nfibh.ebh) |  | ||||||
| 			brelse(nfibh.ebh); |  | ||||||
| 		brelse(nfibh.sbh); |  | ||||||
| 		nfi = NULL; |  | ||||||
| 	} |  | ||||||
| 	if (S_ISDIR(old_inode->i_mode)) { | 	if (S_ISDIR(old_inode->i_mode)) { | ||||||
| 		int offset = udf_ext0_offset(old_inode); |  | ||||||
| 
 |  | ||||||
| 		if (new_inode) { | 		if (new_inode) { | ||||||
| 			retval = -ENOTEMPTY; | 			retval = -ENOTEMPTY; | ||||||
| 			if (!empty_dir(new_inode)) | 			if (!empty_dir(new_inode)) | ||||||
| 				goto end_rename; | 				goto out_oiter; | ||||||
| 		} | 		} | ||||||
| 		retval = -EIO; | 		retval = udf_fiiter_find_entry(old_inode, &dotdot_name, | ||||||
| 		if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { | 					       &diriter); | ||||||
| 			dir_fi = udf_get_fileident( | 		if (retval == -ENOENT) { | ||||||
| 					old_iinfo->i_data - | 			udf_err(old_inode->i_sb, | ||||||
| 					  (old_iinfo->i_efe ? | 				"directory (ino %lu) has no '..' entry\n", | ||||||
| 					   sizeof(struct extendedFileEntry) : | 				old_inode->i_ino); | ||||||
| 					   sizeof(struct fileEntry)), | 			retval = -EFSCORRUPTED; | ||||||
| 					old_inode->i_sb->s_blocksize, &offset); |  | ||||||
| 		} else { |  | ||||||
| 			dir_bh = udf_bread(old_inode, 0, 0, &retval); |  | ||||||
| 			if (!dir_bh) |  | ||||||
| 				goto end_rename; |  | ||||||
| 			dir_fi = udf_get_fileident(dir_bh->b_data, |  | ||||||
| 					old_inode->i_sb->s_blocksize, &offset); |  | ||||||
| 		} | 		} | ||||||
| 		if (!dir_fi) | 		if (retval) | ||||||
| 			goto end_rename; | 			goto out_oiter; | ||||||
| 		tloc = lelb_to_cpu(dir_fi->icb.extLocation); | 		has_diriter = true; | ||||||
|  | 		tloc = lelb_to_cpu(diriter.fi.icb.extLocation); | ||||||
| 		if (udf_get_lb_pblock(old_inode->i_sb, &tloc, 0) != | 		if (udf_get_lb_pblock(old_inode->i_sb, &tloc, 0) != | ||||||
| 				old_dir->i_ino) | 				old_dir->i_ino) { | ||||||
| 			goto end_rename; | 			retval = -EFSCORRUPTED; | ||||||
|  | 			udf_err(old_inode->i_sb, | ||||||
|  | 				"directory (ino %lu) has parent entry pointing to another inode (%lu != %u)\n", | ||||||
|  | 				old_inode->i_ino, old_dir->i_ino, | ||||||
|  | 				udf_get_lb_pblock(old_inode->i_sb, &tloc, 0)); | ||||||
|  | 			goto out_oiter; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	if (!nfi) { | 
 | ||||||
| 		nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi, | 	retval = udf_fiiter_find_entry(new_dir, &new_dentry->d_name, &niter); | ||||||
| 				    &retval); | 	if (retval && retval != -ENOENT) | ||||||
| 		if (!nfi) | 		goto out_oiter; | ||||||
| 			goto end_rename; | 	/* Entry found but not passed by VFS? */ | ||||||
|  | 	if (!retval && !new_inode) { | ||||||
|  | 		retval = -EFSCORRUPTED; | ||||||
|  | 		udf_fiiter_release(&niter); | ||||||
|  | 		goto out_oiter; | ||||||
|  | 	} | ||||||
|  | 	/* Entry not found? Need to add one... */ | ||||||
|  | 	if (retval) { | ||||||
|  | 		udf_fiiter_release(&niter); | ||||||
|  | 		retval = udf_fiiter_add_entry(new_dir, new_dentry, &niter); | ||||||
|  | 		if (retval) | ||||||
|  | 			goto out_oiter; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
|  | @ -1341,14 +1331,26 @@ static int udf_rename(struct user_namespace *mnt_userns, struct inode *old_dir, | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * ok, that's it | 	 * ok, that's it | ||||||
| 	 */ | 	 */ | ||||||
| 	ncfi.fileVersionNum = ocfi.fileVersionNum; | 	niter.fi.fileVersionNum = oiter.fi.fileVersionNum; | ||||||
| 	ncfi.fileCharacteristics = ocfi.fileCharacteristics; | 	niter.fi.fileCharacteristics = oiter.fi.fileCharacteristics; | ||||||
| 	memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(ocfi.icb)); | 	memcpy(&(niter.fi.icb), &(oiter.fi.icb), sizeof(oiter.fi.icb)); | ||||||
| 	udf_write_fi(new_dir, &ncfi, nfi, &nfibh, NULL, NULL); | 	udf_fiiter_write_fi(&niter, NULL); | ||||||
|  | 	udf_fiiter_release(&niter); | ||||||
| 
 | 
 | ||||||
| 	/* The old fid may have moved - find it again */ | 	/*
 | ||||||
| 	ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi); | 	 * The old entry may have moved due to new entry allocation. Find it | ||||||
| 	udf_delete_entry(old_dir, ofi, &ofibh, &ocfi); | 	 * again. | ||||||
|  | 	 */ | ||||||
|  | 	udf_fiiter_release(&oiter); | ||||||
|  | 	retval = udf_fiiter_find_entry(old_dir, &old_dentry->d_name, &oiter); | ||||||
|  | 	if (retval) { | ||||||
|  | 		udf_err(old_dir->i_sb, | ||||||
|  | 			"failed to find renamed entry again in directory (ino %lu)\n", | ||||||
|  | 			old_dir->i_ino); | ||||||
|  | 	} else { | ||||||
|  | 		udf_fiiter_delete_entry(&oiter); | ||||||
|  | 		udf_fiiter_release(&oiter); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if (new_inode) { | 	if (new_inode) { | ||||||
| 		new_inode->i_ctime = current_time(new_inode); | 		new_inode->i_ctime = current_time(new_inode); | ||||||
|  | @ -1359,13 +1361,13 @@ static int udf_rename(struct user_namespace *mnt_userns, struct inode *old_dir, | ||||||
| 	mark_inode_dirty(old_dir); | 	mark_inode_dirty(old_dir); | ||||||
| 	mark_inode_dirty(new_dir); | 	mark_inode_dirty(new_dir); | ||||||
| 
 | 
 | ||||||
| 	if (dir_fi) { | 	if (has_diriter) { | ||||||
| 		dir_fi->icb.extLocation = cpu_to_lelb(UDF_I(new_dir)->i_location); | 		diriter.fi.icb.extLocation = | ||||||
| 		udf_update_tag((char *)dir_fi, udf_dir_entry_len(dir_fi)); | 					cpu_to_lelb(UDF_I(new_dir)->i_location); | ||||||
| 		if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) | 		udf_update_tag((char *)&diriter.fi, | ||||||
| 			mark_inode_dirty(old_inode); | 			       udf_dir_entry_len(&diriter.fi)); | ||||||
| 		else | 		udf_fiiter_write_fi(&diriter, NULL); | ||||||
| 			mark_buffer_dirty_inode(dir_bh, old_inode); | 		udf_fiiter_release(&diriter); | ||||||
| 
 | 
 | ||||||
| 		inode_dec_link_count(old_dir); | 		inode_dec_link_count(old_dir); | ||||||
| 		if (new_inode) | 		if (new_inode) | ||||||
|  | @ -1375,22 +1377,11 @@ static int udf_rename(struct user_namespace *mnt_userns, struct inode *old_dir, | ||||||
| 			mark_inode_dirty(new_dir); | 			mark_inode_dirty(new_dir); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 	return 0; | ||||||
| 	if (ofi) { | out_oiter: | ||||||
| 		if (ofibh.sbh != ofibh.ebh) | 	if (has_diriter) | ||||||
| 			brelse(ofibh.ebh); | 		udf_fiiter_release(&diriter); | ||||||
| 		brelse(ofibh.sbh); | 	udf_fiiter_release(&oiter); | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	retval = 0; |  | ||||||
| 
 |  | ||||||
| end_rename: |  | ||||||
| 	brelse(dir_bh); |  | ||||||
| 	if (nfi) { |  | ||||||
| 		if (nfibh.sbh != nfibh.ebh) |  | ||||||
| 			brelse(nfibh.ebh); |  | ||||||
| 		brelse(nfibh.sbh); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	return retval; | 	return retval; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Jan Kara
						Jan Kara