index f54e868..14ff68e 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
-@@ -2174,6 +2174,74 @@ out_frames:
+@@ -2174,6 +2174,67 @@ out_frames:
return retval;
}
-+/* update ".." for hash-indexed directory, split the item "." if necessary */
++/* update ".." entry */
+static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry,
+ struct inode *inode)
+{
+ struct inode *dir = dentry->d_parent->d_inode;
-+ struct buffer_head *dir_block;
-+ struct ext4_dir_entry_2 *de;
-+ int len, journal = 0, err = 0;
++ struct buffer_head *bh;
++ struct ext4_dir_entry_2 *dot_de, *dotdot_de;
++ unsigned int offset;
++ int retval = 0;
+
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (IS_DIRSYNC(dir))
+ handle->h_sync = 1;
+
-+ dir_block = ext4_bread(handle, dir, 0, 0);
-+ if (IS_ERR(dir_block)) {
-+ err = PTR_ERR(dir_block);
++ bh = ext4_read_dirblock(dir, 0, DIRENT_HTREE);
++ if (IS_ERR(bh))
++ return PTR_ERR(bh);
++
++ dot_de = (struct ext4_dir_entry_2 *) bh->b_data;
++ if (ext4_check_dir_entry(dir, NULL, dot_de, bh, bh->b_data,
++ bh->b_size, 0) ||
++ le32_to_cpu(dot_de->inode) != dir->i_ino ||
++ strcmp(".", dot_de->name)) {
++ EXT4_ERROR_INODE(dir, "directory missing '.'");
++ retval = -EFSCORRUPTED;
+ goto out;
+ }
-+
-+ de = (struct ext4_dir_entry_2 *)dir_block->b_data;
-+ /* the first item must be "." */
-+ assert(de->name_len == 1 && de->name[0] == '.');
-+ len = le16_to_cpu(de->rec_len);
-+ assert(len >= EXT4_DIR_REC_LEN(1));
-+ if (len > EXT4_DIR_REC_LEN(1)) {
-+ BUFFER_TRACE(dir_block, "get_write_access");
-+ err = ext4_journal_get_write_access(handle, dir_block);
-+ if (err)
-+ goto out_journal;
-+
-+ journal = 1;
-+ de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1));
++ offset = ext4_rec_len_from_disk(dot_de->rec_len,
++ dir->i_sb->s_blocksize);
++ dotdot_de = ext4_next_entry(dot_de, dir->i_sb->s_blocksize);
++ if (ext4_check_dir_entry(dir, NULL, dotdot_de, bh, bh->b_data,
++ bh->b_size, offset) ||
++ le32_to_cpu(dotdot_de->inode) == 0 ||
++ strcmp("..", dotdot_de->name)) {
++ EXT4_ERROR_INODE(dir, "directory missing '..'");
++ retval = -EFSCORRUPTED;
++ goto out;
+ }
+
-+ len -= EXT4_DIR_REC_LEN(1);
-+ assert(len == 0 || len >= EXT4_DIR_REC_LEN(2));
-+ de = (struct ext4_dir_entry_2 *)
-+ ((char *) de + le16_to_cpu(de->rec_len));
-+ if (!journal) {
-+ BUFFER_TRACE(dir_block, "get_write_access");
-+ err = ext4_journal_get_write_access(handle, dir_block);
-+ if (err)
-+ goto out_journal;
-+ }
++ BUFFER_TRACE(dir_block, "get_write_access");
++ retval = ext4_journal_get_write_access(handle, bh);
++ if (retval)
++ goto out;
+
-+ de->inode = cpu_to_le32(inode->i_ino);
-+ if (len > 0)
-+ de->rec_len = cpu_to_le16(len);
-+ else
-+ assert(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2));
-+ de->name_len = 2;
-+ strcpy(de->name, "..");
-+ ext4_set_de_type(dir->i_sb, de, S_IFDIR);
++ dotdot_de->inode = cpu_to_le32(inode->i_ino);
+
-+out_journal:
-+ if (journal) {
-+ BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata");
-+ err = ext4_handle_dirty_dirblock(handle, dir, dir_block);
-+ ext4_mark_inode_dirty(handle, dir);
++ ext4_mark_inode_dirty(handle, dir);
++ BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata");
++ if (is_dx(dir)) {
++ retval = ext4_handle_dirty_dx_node(handle, dir, bh);
++ } else {
++ retval = ext4_handle_dirty_dirblock(handle, dir, bh);
+ }
-+ brelse(dir_block);
+
+out:
-+ return err;
++ brelse(bh);
++ return retval;
+}
+
/*
* ext4_add_entry()
*
-@@ -2229,6 +2297,9 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
+@@ -2228,6 +2296,10 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
+ }
}
++ if (dentry->d_name.len == 2 &&
++ memcmp(dentry->d_name.name, "..", 2) == 0)
++ return ext4_update_dotdot(handle, dentry, inode);
++
if (is_dx(dir)) {
-+ if (dentry->d_name.len == 2 &&
-+ memcmp(dentry->d_name.name, "..", 2) == 0)
-+ return ext4_update_dotdot(handle, dentry, inode);
retval = ext4_dx_add_entry(handle, &fname, dir, inode);
if (!retval || (retval != ERR_BAD_DX_DIR))
- goto out;
--
2.20.1