|
@@ -3016,6 +3016,125 @@ struct ext4_renament {
|
|
int dir_inlined;
|
|
int dir_inlined;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static int ext4_rename_dir_prepare(handle_t *handle, struct ext4_renament *ent)
|
|
|
|
+{
|
|
|
|
+ int retval;
|
|
|
|
+
|
|
|
|
+ ent->dir_bh = ext4_get_first_dir_block(handle, ent->inode,
|
|
|
|
+ &retval, &ent->parent_de,
|
|
|
|
+ &ent->dir_inlined);
|
|
|
|
+ if (!ent->dir_bh)
|
|
|
|
+ return retval;
|
|
|
|
+ if (le32_to_cpu(ent->parent_de->inode) != ent->dir->i_ino)
|
|
|
|
+ return -EIO;
|
|
|
|
+ BUFFER_TRACE(ent->dir_bh, "get_write_access");
|
|
|
|
+ return ext4_journal_get_write_access(handle, ent->dir_bh);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int ext4_rename_dir_finish(handle_t *handle, struct ext4_renament *ent,
|
|
|
|
+ unsigned dir_ino)
|
|
|
|
+{
|
|
|
|
+ int retval;
|
|
|
|
+
|
|
|
|
+ ent->parent_de->inode = cpu_to_le32(dir_ino);
|
|
|
|
+ BUFFER_TRACE(ent->dir_bh, "call ext4_handle_dirty_metadata");
|
|
|
|
+ if (!ent->dir_inlined) {
|
|
|
|
+ if (is_dx(ent->inode)) {
|
|
|
|
+ retval = ext4_handle_dirty_dx_node(handle,
|
|
|
|
+ ent->inode,
|
|
|
|
+ ent->dir_bh);
|
|
|
|
+ } else {
|
|
|
|
+ retval = ext4_handle_dirty_dirent_node(handle,
|
|
|
|
+ ent->inode,
|
|
|
|
+ ent->dir_bh);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ retval = ext4_mark_inode_dirty(handle, ent->inode);
|
|
|
|
+ }
|
|
|
|
+ if (retval) {
|
|
|
|
+ ext4_std_error(ent->dir->i_sb, retval);
|
|
|
|
+ return retval;
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int ext4_setent(handle_t *handle, struct ext4_renament *ent,
|
|
|
|
+ unsigned ino, unsigned file_type)
|
|
|
|
+{
|
|
|
|
+ int retval;
|
|
|
|
+
|
|
|
|
+ BUFFER_TRACE(ent->bh, "get write access");
|
|
|
|
+ retval = ext4_journal_get_write_access(handle, ent->bh);
|
|
|
|
+ if (retval)
|
|
|
|
+ return retval;
|
|
|
|
+ ent->de->inode = cpu_to_le32(ino);
|
|
|
|
+ if (EXT4_HAS_INCOMPAT_FEATURE(ent->dir->i_sb,
|
|
|
|
+ EXT4_FEATURE_INCOMPAT_FILETYPE))
|
|
|
|
+ ent->de->file_type = file_type;
|
|
|
|
+ ent->dir->i_version++;
|
|
|
|
+ ent->dir->i_ctime = ent->dir->i_mtime =
|
|
|
|
+ ext4_current_time(ent->dir);
|
|
|
|
+ ext4_mark_inode_dirty(handle, ent->dir);
|
|
|
|
+ BUFFER_TRACE(ent->bh, "call ext4_handle_dirty_metadata");
|
|
|
|
+ if (!ent->inlined) {
|
|
|
|
+ retval = ext4_handle_dirty_dirent_node(handle,
|
|
|
|
+ ent->dir, ent->bh);
|
|
|
|
+ if (unlikely(retval)) {
|
|
|
|
+ ext4_std_error(ent->dir->i_sb, retval);
|
|
|
|
+ return retval;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ brelse(ent->bh);
|
|
|
|
+ ent->bh = NULL;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int ext4_find_delete_entry(handle_t *handle, struct inode *dir,
|
|
|
|
+ const struct qstr *d_name)
|
|
|
|
+{
|
|
|
|
+ int retval = -ENOENT;
|
|
|
|
+ struct buffer_head *bh;
|
|
|
|
+ struct ext4_dir_entry_2 *de;
|
|
|
|
+
|
|
|
|
+ bh = ext4_find_entry(dir, d_name, &de, NULL);
|
|
|
|
+ if (bh) {
|
|
|
|
+ retval = ext4_delete_entry(handle, dir, de, bh);
|
|
|
|
+ brelse(bh);
|
|
|
|
+ }
|
|
|
|
+ return retval;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent)
|
|
|
|
+{
|
|
|
|
+ int retval;
|
|
|
|
+ /*
|
|
|
|
+ * ent->de could have moved from under us during htree split, so make
|
|
|
|
+ * sure that we are deleting the right entry. We might also be pointing
|
|
|
|
+ * to a stale entry in the unused part of ent->bh so just checking inum
|
|
|
|
+ * and the name isn't enough.
|
|
|
|
+ */
|
|
|
|
+ if (le32_to_cpu(ent->de->inode) != ent->inode->i_ino ||
|
|
|
|
+ ent->de->name_len != ent->dentry->d_name.len ||
|
|
|
|
+ strncmp(ent->de->name, ent->dentry->d_name.name,
|
|
|
|
+ ent->de->name_len)) {
|
|
|
|
+ retval = ext4_find_delete_entry(handle, ent->dir,
|
|
|
|
+ &ent->dentry->d_name);
|
|
|
|
+ } else {
|
|
|
|
+ retval = ext4_delete_entry(handle, ent->dir, ent->de, ent->bh);
|
|
|
|
+ if (retval == -ENOENT) {
|
|
|
|
+ retval = ext4_find_delete_entry(handle, ent->dir,
|
|
|
|
+ &ent->dentry->d_name);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (retval) {
|
|
|
|
+ ext4_warning(ent->dir->i_sb,
|
|
|
|
+ "Deleting old file (%lu), %d, error=%d",
|
|
|
|
+ ent->dir->i_ino, ent->dir->i_nlink, retval);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Anybody can rename anything with this: the permission checks are left to the
|
|
* Anybody can rename anything with this: the permission checks are left to the
|
|
* higher-level routines.
|
|
* higher-level routines.
|
|
@@ -3089,16 +3208,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir))
|
|
if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir))
|
|
goto end_rename;
|
|
goto end_rename;
|
|
}
|
|
}
|
|
- retval = -EIO;
|
|
|
|
- old.dir_bh = ext4_get_first_dir_block(handle, old.inode,
|
|
|
|
- &retval, &old.parent_de,
|
|
|
|
- &old.dir_inlined);
|
|
|
|
- if (!old.dir_bh)
|
|
|
|
- goto end_rename;
|
|
|
|
- if (le32_to_cpu(old.parent_de->inode) != old.dir->i_ino)
|
|
|
|
- goto end_rename;
|
|
|
|
- BUFFER_TRACE(old.dir_bh, "get_write_access");
|
|
|
|
- retval = ext4_journal_get_write_access(handle, old.dir_bh);
|
|
|
|
|
|
+ retval = ext4_rename_dir_prepare(handle, &old);
|
|
if (retval)
|
|
if (retval)
|
|
goto end_rename;
|
|
goto end_rename;
|
|
}
|
|
}
|
|
@@ -3107,29 +3217,10 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
if (retval)
|
|
if (retval)
|
|
goto end_rename;
|
|
goto end_rename;
|
|
} else {
|
|
} else {
|
|
- BUFFER_TRACE(new.bh, "get write access");
|
|
|
|
- retval = ext4_journal_get_write_access(handle, new.bh);
|
|
|
|
|
|
+ retval = ext4_setent(handle, &new,
|
|
|
|
+ old.inode->i_ino, old.de->file_type);
|
|
if (retval)
|
|
if (retval)
|
|
goto end_rename;
|
|
goto end_rename;
|
|
- new.de->inode = cpu_to_le32(old.inode->i_ino);
|
|
|
|
- if (EXT4_HAS_INCOMPAT_FEATURE(new.dir->i_sb,
|
|
|
|
- EXT4_FEATURE_INCOMPAT_FILETYPE))
|
|
|
|
- new.de->file_type = old.de->file_type;
|
|
|
|
- new.dir->i_version++;
|
|
|
|
- new.dir->i_ctime = new.dir->i_mtime =
|
|
|
|
- ext4_current_time(new.dir);
|
|
|
|
- ext4_mark_inode_dirty(handle, new.dir);
|
|
|
|
- BUFFER_TRACE(new.bh, "call ext4_handle_dirty_metadata");
|
|
|
|
- if (!new.inlined) {
|
|
|
|
- retval = ext4_handle_dirty_dirent_node(handle,
|
|
|
|
- new.dir, new.bh);
|
|
|
|
- if (unlikely(retval)) {
|
|
|
|
- ext4_std_error(new.dir->i_sb, retval);
|
|
|
|
- goto end_rename;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- brelse(new.bh);
|
|
|
|
- new.bh = NULL;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -3142,31 +3233,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
/*
|
|
/*
|
|
* ok, that's it
|
|
* ok, that's it
|
|
*/
|
|
*/
|
|
- if (le32_to_cpu(old.de->inode) != old.inode->i_ino ||
|
|
|
|
- old.de->name_len != old.dentry->d_name.len ||
|
|
|
|
- strncmp(old.de->name, old.dentry->d_name.name, old.de->name_len) ||
|
|
|
|
- (retval = ext4_delete_entry(handle, old.dir,
|
|
|
|
- old.de, old.bh)) == -ENOENT) {
|
|
|
|
- /* old.de could have moved from under us during htree split, so
|
|
|
|
- * make sure that we are deleting the right entry. We might
|
|
|
|
- * also be pointing to a stale entry in the unused part of
|
|
|
|
- * old.bh so just checking inum and the name isn't enough. */
|
|
|
|
- struct buffer_head *old_bh2;
|
|
|
|
- struct ext4_dir_entry_2 *old_de2;
|
|
|
|
-
|
|
|
|
- old_bh2 = ext4_find_entry(old.dir, &old.dentry->d_name,
|
|
|
|
- &old_de2, NULL);
|
|
|
|
- if (old_bh2) {
|
|
|
|
- retval = ext4_delete_entry(handle, old.dir,
|
|
|
|
- old_de2, old_bh2);
|
|
|
|
- brelse(old_bh2);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if (retval) {
|
|
|
|
- ext4_warning(old.dir->i_sb,
|
|
|
|
- "Deleting old file (%lu), %d, error=%d",
|
|
|
|
- old.dir->i_ino, old.dir->i_nlink, retval);
|
|
|
|
- }
|
|
|
|
|
|
+ ext4_rename_delete(handle, &old);
|
|
|
|
|
|
if (new.inode) {
|
|
if (new.inode) {
|
|
ext4_dec_count(handle, new.inode);
|
|
ext4_dec_count(handle, new.inode);
|
|
@@ -3175,24 +3242,10 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
old.dir->i_ctime = old.dir->i_mtime = ext4_current_time(old.dir);
|
|
old.dir->i_ctime = old.dir->i_mtime = ext4_current_time(old.dir);
|
|
ext4_update_dx_flag(old.dir);
|
|
ext4_update_dx_flag(old.dir);
|
|
if (old.dir_bh) {
|
|
if (old.dir_bh) {
|
|
- old.parent_de->inode = cpu_to_le32(new.dir->i_ino);
|
|
|
|
- BUFFER_TRACE(old.dir_bh, "call ext4_handle_dirty_metadata");
|
|
|
|
- if (!old.dir_inlined) {
|
|
|
|
- if (is_dx(old.inode)) {
|
|
|
|
- retval = ext4_handle_dirty_dx_node(handle,
|
|
|
|
- old.inode,
|
|
|
|
- old.dir_bh);
|
|
|
|
- } else {
|
|
|
|
- retval = ext4_handle_dirty_dirent_node(handle,
|
|
|
|
- old.inode, old.dir_bh);
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- retval = ext4_mark_inode_dirty(handle, old.inode);
|
|
|
|
- }
|
|
|
|
- if (retval) {
|
|
|
|
- ext4_std_error(old.dir->i_sb, retval);
|
|
|
|
|
|
+ retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino);
|
|
|
|
+ if (retval)
|
|
goto end_rename;
|
|
goto end_rename;
|
|
- }
|
|
|
|
|
|
+
|
|
ext4_dec_count(handle, old.dir);
|
|
ext4_dec_count(handle, old.dir);
|
|
if (new.inode) {
|
|
if (new.inode) {
|
|
/* checked empty_dir above, can't have another parent,
|
|
/* checked empty_dir above, can't have another parent,
|