Commit e9109a92 authored by Jan Kara's avatar Jan Kara
Browse files

udf: Convert udf_rename() to new directory iteration code



Convert udf_rename() to use new directory iteration code.

Reported-by: default avatar <syzbot+0eaad3590d65102b9391@syzkaller.appspotmail.com>
Reported-by: default avatar <syzbot+b7fc73213bc2361ab650@syzkaller.appspotmail.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent dbfb102d
Loading
Loading
Loading
Loading
+78 −87
Original line number Diff line number Diff line
@@ -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 *new_inode = d_inode(new_dentry);
	struct udf_fileident_bh ofibh, nfibh;
	struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL;
	struct fileIdentDesc ocfi, ncfi;
	struct buffer_head *dir_bh = NULL;
	int retval = -ENOENT;
	struct udf_fileident_iter oiter, niter, diriter;
	bool has_diriter = false;
	int retval;
	struct kernel_lb_addr tloc;
	struct udf_inode_info *old_iinfo = UDF_I(old_inode);

	if (flags & ~RENAME_NOREPLACE)
		return -EINVAL;

	ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi);
	if (!ofi || IS_ERR(ofi)) {
		if (IS_ERR(ofi))
			retval = PTR_ERR(ofi);
		goto end_rename;
	}

	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;
	retval = udf_fiiter_find_entry(old_dir, &old_dentry->d_name, &oiter);
	if (retval)
		return retval;

	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;
	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 (S_ISDIR(old_inode->i_mode)) {
		int offset = udf_ext0_offset(old_inode);

	if (S_ISDIR(old_inode->i_mode)) {
		if (new_inode) {
			retval = -ENOTEMPTY;
			if (!empty_dir(new_inode))
				goto end_rename;
		}
		retval = -EIO;
		if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
			dir_fi = udf_get_fileident(
					old_iinfo->i_data -
					  (old_iinfo->i_efe ?
					   sizeof(struct extendedFileEntry) :
					   sizeof(struct fileEntry)),
					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)
			goto end_rename;
		tloc = lelb_to_cpu(dir_fi->icb.extLocation);
				goto out_oiter;
		}
		retval = udf_fiiter_find_entry(old_inode, &dotdot_name,
					       &diriter);
		if (retval == -ENOENT) {
			udf_err(old_inode->i_sb,
				"directory (ino %lu) has no '..' entry\n",
				old_inode->i_ino);
			retval = -EFSCORRUPTED;
		}
		if (retval)
			goto out_oiter;
		has_diriter = true;
		tloc = lelb_to_cpu(diriter.fi.icb.extLocation);
		if (udf_get_lb_pblock(old_inode->i_sb, &tloc, 0) !=
				old_dir->i_ino)
			goto end_rename;
				old_dir->i_ino) {
			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);
		if (!nfi)
			goto end_rename;

	retval = udf_fiiter_find_entry(new_dir, &new_dentry->d_name, &niter);
	if (retval && retval != -ENOENT)
		goto out_oiter;
	/* 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
	 */
	ncfi.fileVersionNum = ocfi.fileVersionNum;
	ncfi.fileCharacteristics = ocfi.fileCharacteristics;
	memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(ocfi.icb));
	udf_write_fi(new_dir, &ncfi, nfi, &nfibh, NULL, NULL);
	niter.fi.fileVersionNum = oiter.fi.fileVersionNum;
	niter.fi.fileCharacteristics = oiter.fi.fileCharacteristics;
	memcpy(&(niter.fi.icb), &(oiter.fi.icb), sizeof(oiter.fi.icb));
	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);
	udf_delete_entry(old_dir, ofi, &ofibh, &ocfi);
	/*
	 * The old entry may have moved due to new entry allocation. Find it
	 * 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) {
		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(new_dir);

	if (dir_fi) {
		dir_fi->icb.extLocation = cpu_to_lelb(UDF_I(new_dir)->i_location);
		udf_update_tag((char *)dir_fi, udf_dir_entry_len(dir_fi));
		if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
			mark_inode_dirty(old_inode);
		else
			mark_buffer_dirty_inode(dir_bh, old_inode);
	if (has_diriter) {
		diriter.fi.icb.extLocation =
					cpu_to_lelb(UDF_I(new_dir)->i_location);
		udf_update_tag((char *)&diriter.fi,
			       udf_dir_entry_len(&diriter.fi));
		udf_fiiter_write_fi(&diriter, NULL);
		udf_fiiter_release(&diriter);

		inode_dec_link_count(old_dir);
		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);
		}
	}

	if (ofi) {
		if (ofibh.sbh != ofibh.ebh)
			brelse(ofibh.ebh);
		brelse(ofibh.sbh);
	}

	retval = 0;

end_rename:
	brelse(dir_bh);
	if (nfi) {
		if (nfibh.sbh != nfibh.ebh)
			brelse(nfibh.ebh);
		brelse(nfibh.sbh);
	}
	return 0;
out_oiter:
	if (has_diriter)
		udf_fiiter_release(&diriter);
	udf_fiiter_release(&oiter);

	return retval;
}