mirror of
https://github.com/torvalds/linux.git
synced 2024-11-28 23:21:31 +00: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
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);
|
retval = udf_fiiter_find_entry(new_dir, &new_dentry->d_name, &niter);
|
||||||
if (!nfi)
|
if (retval && retval != -ENOENT)
|
||||||
goto end_rename;
|
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
|
* 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 New Issue
Block a user