+
+-/* update ".." entry */
++static int ext4_expand_dotdot(struct inode *dir,
++ struct buffer_head *bh,
++ int dlen)
++{
++ struct ext4_dir_entry_2 *dot_de;
++ struct ext4_dir_entry_2 *dotdot_de;
++ int len;
++ unsigned blocksize = dir->i_sb->s_blocksize;
++
++ dot_de = (struct ext4_dir_entry_2 *)bh->b_data;
++ dotdot_de = ext4_next_entry(dot_de, blocksize);
++
++ if (is_dx(dir)) {
++ struct dx_entry *entries;
++ struct dx_root_info *dx_info;
++ int limit, count;
++ int entry_space;
++
++ len = EXT4_DIR_REC_LEN(2 + dlen, NULL) -
++ EXT4_DIR_ENTRY_LEN(dotdot_de, NULL);
++
++ dx_info = dx_get_dx_info(dot_de, NULL);
++ entries = (struct dx_entry *)((char *)dx_info +
++ sizeof(*dx_info));
++ count = dx_get_count(entries);
++
++ /*
++ * figure out new limit with dlen,
++ * check if we have enough space
++ */
++ entry_space = blocksize;
++ entry_space -= (char *)dotdot_de - (char *)dot_de +
++ EXT4_DIR_REC_LEN(2 + dlen, NULL) +
++ sizeof(*dx_info);
++ if (ext4_has_metadata_csum(dir->i_sb))
++ entry_space -= sizeof(struct dx_tail);
++ limit = entry_space / sizeof(struct dx_entry);
++ if (count > limit)
++ return -ENOSPC;
++
++ /* set the new limit, move dx_info and the entries */
++ dx_set_limit(entries, limit);
++ memmove((char *)dx_info + len, dx_info,
++ sizeof(*dx_info) + count * sizeof(struct dx_entry));
++ } else {
++ struct ext4_dir_entry_2 *next, *to, *prev, *de;
++ char *top = (char *)bh->b_data + blocksize;
++ int space = 0;
++ unsigned rec_len = 0;
++
++ len = EXT4_DIR_REC_LEN(2 + dlen, NULL) -
++ ext4_rec_len_from_disk(dotdot_de->rec_len, blocksize);
++
++ if (ext4_has_metadata_csum(dir->i_sb))
++ top -= sizeof(struct ext4_dir_entry_tail);
++
++ de = ext4_next_entry(dotdot_de, blocksize);
++ while ((char *)de < top) {
++ space += ext4_rec_len_from_disk(de->rec_len, blocksize) -
++ EXT4_DIR_ENTRY_LEN(de, dir);
++ de = ext4_next_entry(de, blocksize);
++ }
++
++ if (space < len)
++ return -ENOSPC;
++
++ /* pack all the entries after dotdot */
++ de = ext4_next_entry(dotdot_de, blocksize);
++ prev = to = de;
++ while ((char *)de < top) {
++ next = ext4_next_entry(de, blocksize);
++ if (de->inode && de->name_len) {
++ rec_len = EXT4_DIR_ENTRY_LEN(de, dir);
++ if (de > to)
++ memmove(to, de, rec_len);
++ to->rec_len = ext4_rec_len_to_disk(rec_len,
++ blocksize);
++ prev = to;
++ to = (struct ext4_dir_entry_2 *)
++ (((char *)to) + rec_len);
++ }
++ de = next;
++ }
++ /* fix up rec_len for the last entry */
++ prev->rec_len = ext4_rec_len_to_disk(top - (char *)prev - len,
++ blocksize);
++ /* move all the entries after dotdot to make space */
++ de = ext4_next_entry(dotdot_de, blocksize);
++ memmove((char *)de + len, de, (char *)prev - (char *)de +
++ EXT4_DIR_ENTRY_LEN(prev, dir));
++ /* fix the rec_len for dotdot */
++ dotdot_de->rec_len = ext4_rec_len_to_disk(
++ EXT4_DIR_REC_LEN(2 + dlen, NULL),
++ blocksize);
++ }
++
++ return 0;
++}
++
++/* update ".." entry, try to expand the entry if necessary */
+ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry,
+ struct inode *inode)
+ {
+@@ -2395,6 +2519,8 @@ static int ext4_update_dotdot(handle_t *
+ struct ext4_dir_entry_2 *dot_de, *dotdot_de;
+ unsigned int offset;
+ int retval = 0;