From: Li Dongyang Date: Wed, 17 Apr 2024 05:36:55 +0000 (+1000) Subject: LU-17711 osd-ldiskfs: do not delete dotdot during rename X-Git-Tag: 2.15.64~112 X-Git-Url: https://git.whamcloud.com/?a=commitdiff_plain;h=0536b2a26992f5d2ef9e3537a196afac81281f60;p=fs%2Flustre-release.git LU-17711 osd-ldiskfs: do not delete dotdot during rename Since upstream kernel commit v5.12-rc4-32-g6c0912739699 ext4_dir_entry_2 after rec_len will be wiped when deleting the entry. This creates a problem with rename, when we delete dotdot first and if it's a dx dir, kernel will wipe entire dx_root in the block after dotdot entry. We can just update the dotdot entry in-place without deleting. For dx dirs, ext4_update_dotdot() takes care of dotdot and inserting dotdot is an update, use it for linear dirs also. Rewrite ext4_update_dotdot() to get a few fixes: *use ext4_read_dirblock to get the first block. *do not assert on data read from disk, we check the dot and dotdot entry and if anything looks wrong, we return -EFSCORRUPTED. *make sure the change is journalled. *set metadata_csum correctly for dx dirs. Update ext4-data-in-dirent.patch, if dotdot entry has no space for dirdata, try to expand the dotdot entry by moving the entries behind it, or move the dx_root for dx dirs. Add conf-sanity/154 to verify that the ".." entry was updated properly after restore, including with an htree split directory with dx_root entry. Signed-off-by: Li Dongyang Change-Id: I33e862739fa44f583aaa4369190d6d80271db13b Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/54723 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Andreas Dilger Reviewed-by: Shaun Tancheff Reviewed-by: Jian Yu Reviewed-by: Oleg Drokin --- diff --git a/ldiskfs/kernel_patches/patches/linux-5.10/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/linux-5.10/ext4-data-in-dirent.patch index e637a68..4ed99b8 100644 --- a/ldiskfs/kernel_patches/patches/linux-5.10/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/linux-5.10/ext4-data-in-dirent.patch @@ -173,7 +173,7 @@ diff -ur a/fs/ext4/ext4.h b/fs/ext4/ext4.h void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); -+ struct ext4_dir_entry_2 **dest_de, int *dlen); ++ struct ext4_dir_entry_2 **dest_de, int dlen); void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -278,7 +278,7 @@ diff -ur a/fs/ext4/inline.c b/fs/ext4/inline.c err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; @@ -434,26 +434,20 @@ diff -ur a/fs/ext4/namei.c b/fs/ext4/namei.c if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -1959,14 +1971,16 @@ +@@ -1959,10 +1971,10 @@ struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) -+ struct ext4_dir_entry_2 **dest_de, int *dlen) ++ struct ext4_dir_entry_2 **dest_de, int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)); -+ unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)) + -+ (dlen ? *dlen : 0); ++ unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname) + dlen); int nlen, rlen; unsigned int offset = 0; char *top; - -+ dlen ? *dlen = 0 : 0; /* default set to 0 */ - de = (struct ext4_dir_entry_2 *)buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -1975,10 +1989,26 @@ +@@ -1975,7 +1989,7 @@ return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; @@ -462,25 +456,6 @@ diff -ur a/fs/ext4/namei.c b/fs/ext4/namei.c rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ EXT4_DIR_REC_LEN(fname_len(fname))) { -+ /* set dlen=1 to indicate not -+ * enough space store fid */ -+ dlen ? *dlen = 1 : 0; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -1992,12 +2022,12 @@ void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, @@ -526,18 +501,15 @@ diff -ur a/fs/ext4/namei.c b/fs/ext4/namei.c + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -2047,7 +2087,10 @@ +@@ -2047,7 +2087,7 @@ } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(inode, de, blocksize, fname, data); /* @@ -552,49 +524,152 @@ diff -ur a/fs/ext4/namei.c b/fs/ext4/namei.c /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2202,6 +2246,8 @@ - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; +@@ -2187,7 +2210,104 @@ out_frames: + return retval; + } + +-/* 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) - ++ EXT4_DIR_ENTRY_LEN(dotdot_de); ++ ++ dx_info = dx_get_dx_info(dot_de); ++ 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) + 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) - ++ 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); ++ 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); ++ 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)); ++ /* fix the rec_len for dotdot */ ++ dotdot_de->rec_len = ext4_rec_len_to_disk( ++ EXT4_DIR_REC_LEN(2 + dlen), 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) + { +@@ -2196,6 +2316,8 @@ static int ext4_update_dotdot(handle_t * + struct ext4_dir_entry_2 *dot_de, *dotdot_de; + unsigned int offset; + int retval = 0; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2227,11 +2273,16 @@ - goto out_journal; +@@ -2235,6 +2357,30 @@ static int ext4_update_dotdot(handle_t * - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de)); - } + dotdot_de->inode = cpu_to_le32(inode->i_ino); -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_ENTRY_LEN(de); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ assert(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2248,7 +2299,12 @@ - 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); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ EXT4_DIR_REC_LEN(2 + dlen)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2286,6 +2342,7 @@ ext4_lblk_t block, blocks; int csum_size = 0; diff --git a/ldiskfs/kernel_patches/patches/linux-5.10/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/linux-5.10/ext4-pdirop.patch index 077e8cc..6047c64 100644 --- a/ldiskfs/kernel_patches/patches/linux-5.10/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/linux-5.10/ext4-pdirop.patch @@ -793,9 +793,9 @@ Reviewed-by: Andreas Dilger struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2375,9 +2714,10 @@ static int ext4_add_entry(handle_t *hand - if (dentry->d_name.len == 2 && - memcmp(dentry->d_name.name, "..", 2) == 0) - return ext4_update_dotdot(handle, dentry, inode); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/linux-5.4/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/linux-5.4/ext4-data-in-dirent.patch index 6f34417..d87a0fa 100644 --- a/ldiskfs/kernel_patches/patches/linux-5.4/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/linux-5.4/ext4-data-in-dirent.patch @@ -167,7 +167,7 @@ index cb649f0..fe35251 100644 void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); -+ struct ext4_dir_entry_2 **dest_de, int *dlen); ++ struct ext4_dir_entry_2 **dest_de, int dlen); void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -251,7 +251,7 @@ index 2fec62d..3f35821 100644 err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; @@ -408,26 +408,20 @@ index 8713671..23bd871 100644 if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -1943,14 +1955,16 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -1943,10 +1955,10 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) -+ struct ext4_dir_entry_2 **dest_de, int *dlen) ++ struct ext4_dir_entry_2 **dest_de, int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)); -+ unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)) + -+ (dlen ? *dlen : 0); ++ unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname) + dlen); int nlen, rlen; unsigned int offset = 0; char *top; - -+ dlen ? *dlen = 0 : 0; /* default set to 0 */ - de = (struct ext4_dir_entry_2 *)buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -1959,10 +1973,26 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -1959,7 +1973,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; @@ -436,25 +430,6 @@ index 8713671..23bd871 100644 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ EXT4_DIR_REC_LEN(fname_len(fname))) { -+ /* set dlen=1 to indicate not -+ * enough space store fid */ -+ dlen ? *dlen = 1 : 0; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -1976,12 +2006,12 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, @@ -500,18 +475,15 @@ index 8713671..23bd871 100644 + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -2031,7 +2071,10 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, +@@ -2031,7 +2071,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(inode, de, blocksize, fname, data); /* @@ -526,49 +498,152 @@ index 8713671..23bd871 100644 /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2186,6 +2230,8 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; +@@ -2197,8 +2220,104 @@ out_frames: + brelse(bh2); + return retval; + } ++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) - ++ EXT4_DIR_ENTRY_LEN(dotdot_de); ++ ++ dx_info = dx_get_dx_info(dot_de); ++ 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) + 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) - ++ 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); ++ 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); ++ 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)); ++ /* fix the rec_len for dotdot */ ++ dotdot_de->rec_len = ext4_rec_len_to_disk( ++ EXT4_DIR_REC_LEN(2 + dlen), blocksize); ++ } ++ ++ return 0; ++} + +-/* update ".." entry */ ++/* update ".." entry, try to expand the entry if necessary */ + static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, + struct inode *inode) + { +@@ -2207,6 +2326,8 @@ static int ext4_update_dotdot(handle_t * + struct ext4_dir_entry_2 *dot_de, *dotdot_de; + unsigned int offset; + int retval = 0; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2211,11 +2257,16 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - goto out_journal; +@@ -2246,6 +2368,30 @@ static int ext4_update_dotdot(handle_t * - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de)); - } + dotdot_de->inode = cpu_to_le32(inode->i_ino); -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_ENTRY_LEN(de); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ assert(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2232,7 +2283,12 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - 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); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ EXT4_DIR_REC_LEN(2 + dlen)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2271,6 +2327,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ext4_lblk_t block, blocks; int csum_size = 0; diff --git a/ldiskfs/kernel_patches/patches/linux-5.4/ext4-hash-indexed-dir-dotdot-update.patch b/ldiskfs/kernel_patches/patches/linux-5.4/ext4-hash-indexed-dir-dotdot-update.patch index ef35db0..2c057b8 100644 --- a/ldiskfs/kernel_patches/patches/linux-5.4/ext4-hash-indexed-dir-dotdot-update.patch +++ b/ldiskfs/kernel_patches/patches/linux-5.4/ext4-hash-indexed-dir-dotdot-update.patch @@ -11,18 +11,19 @@ diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c 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); @@ -30,72 +31,65 @@ index f54e868..14ff68e 100644 + 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 diff --git a/ldiskfs/kernel_patches/patches/linux-5.4/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/linux-5.4/ext4-pdirop.patch index 89a3825..f849a99 100644 --- a/ldiskfs/kernel_patches/patches/linux-5.4/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/linux-5.4/ext4-pdirop.patch @@ -800,9 +800,9 @@ Index: linux-stage/fs/ext4/namei.c struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2375,9 +2714,10 @@ static int ext4_add_entry(handle_t *hand - if (dentry->d_name.len == 2 && - memcmp(dentry->d_name.name, "..", 2) == 0) - return ext4_update_dotdot(handle, dentry, inode); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/linux-5.8/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/linux-5.8/ext4-data-in-dirent.patch index 245ed84..98922e4 100644 --- a/ldiskfs/kernel_patches/patches/linux-5.8/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/linux-5.8/ext4-data-in-dirent.patch @@ -170,7 +170,7 @@ Signed-off-by: Andreas Dilger void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); -+ struct ext4_dir_entry_2 **dest_de, int *dlen); ++ struct ext4_dir_entry_2 **dest_de, int dlen); void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -252,7 +252,7 @@ Signed-off-by: Andreas Dilger err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; @@ -407,26 +407,20 @@ Signed-off-by: Andreas Dilger if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -1950,14 +1962,16 @@ int ext4_find_dest_de(struct inode *dir, +@@ -1950,10 +1962,10 @@ int ext4_find_dest_de(struct inode *dir, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) -+ struct ext4_dir_entry_2 **dest_de, int *dlen) ++ struct ext4_dir_entry_2 **dest_de, int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)); -+ unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)) + -+ (dlen ? *dlen : 0); ++ unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname) + dlen); int nlen, rlen; unsigned int offset = 0; char *top; - -+ dlen ? *dlen = 0 : 0; /* default set to 0 */ - de = (struct ext4_dir_entry_2 *)buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -1966,10 +1980,26 @@ int ext4_find_dest_de(struct inode *dir, +@@ -1966,7 +1980,7 @@ int ext4_find_dest_de(struct inode *dir, return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; @@ -435,25 +429,6 @@ Signed-off-by: Andreas Dilger rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ EXT4_DIR_REC_LEN(fname_len(fname))) { -+ /* set dlen=1 to indicate not -+ * enough space store fid */ -+ dlen ? *dlen = 1 : 0; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -1983,12 +2013,12 @@ int ext4_find_dest_de(struct inode *dir, void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, @@ -499,18 +474,15 @@ Signed-off-by: Andreas Dilger + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -2038,7 +2078,10 @@ static int add_dirent_to_buf(handle_t *h +@@ -2038,7 +2078,7 @@ static int add_dirent_to_buf(handle_t *h } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(inode, de, blocksize, fname, data); /* @@ -525,49 +497,152 @@ Signed-off-by: Andreas Dilger /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2193,6 +2237,8 @@ static int ext4_update_dotdot(handle_t * - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; +@@ -2195,7 +2218,104 @@ out_frames: + return retval; + } + +-/* 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) - ++ EXT4_DIR_ENTRY_LEN(dotdot_de); ++ ++ dx_info = dx_get_dx_info(dot_de); ++ 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) + 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) - ++ 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); ++ 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); ++ 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)); ++ /* fix the rec_len for dotdot */ ++ dotdot_de->rec_len = ext4_rec_len_to_disk( ++ EXT4_DIR_REC_LEN(2 + dlen), 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) + { +@@ -2204,6 +2324,8 @@ static int ext4_update_dotdot(handle_t * + struct ext4_dir_entry_2 *dot_de, *dotdot_de; + unsigned int offset; + int retval = 0; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2218,11 +2264,16 @@ static int ext4_update_dotdot(handle_t * - goto out_journal; +@@ -2243,6 +2365,30 @@ static int ext4_update_dotdot(handle_t * - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de)); - } + dotdot_de->inode = cpu_to_le32(inode->i_ino); -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_ENTRY_LEN(de); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ assert(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2239,7 +2290,12 @@ static int ext4_update_dotdot(handle_t * - 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); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ EXT4_DIR_REC_LEN(2 + dlen)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2280,6 +2336,7 @@ static int ext4_add_entry(handle_t *hand ext4_lblk_t block, blocks; int csum_size = 0; diff --git a/ldiskfs/kernel_patches/patches/linux-6.0/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/linux-6.0/ext4-data-in-dirent.patch index 9c61de5..c6381c8 100644 --- a/ldiskfs/kernel_patches/patches/linux-6.0/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/linux-6.0/ext4-data-in-dirent.patch @@ -169,7 +169,7 @@ index e84d4a7..5e73b80 100644 struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); + struct ext4_dir_entry_2 **dest_de, -+ int *dlen); ++ int dlen); void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -273,7 +273,7 @@ index a4fbe82..6a424df 100644 err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; @@ -492,32 +492,21 @@ index 649dc0a..54bbe22 100644 if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -2106,14 +2117,22 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2106,10 +2117,11 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) + struct ext4_dir_entry_2 **dest_de, -+ int *dlen) ++ int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ unsigned short reclen; ++ unsigned short reclen = ext4_dir_rec_len(fname_len(fname) + dlen, dir); int nlen, rlen; unsigned int offset = 0; char *top; - -+ if (dlen) { -+ reclen = ext4_dir_rec_len(fname_len(fname) + *dlen, dir); -+ *dlen = 0; -+ } else { -+ reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ } -+ - de = buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -2122,10 +2141,31 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2122,7 +2141,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; @@ -526,30 +515,6 @@ index 649dc0a..54bbe22 100644 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID -+ */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ ext4_dir_rec_len(fname_len(fname), dir)) { -+ /* set dlen = 1 to indicate not -+ * enough space store fid -+ */ -+ if (dlen) -+ *dlen = 1; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. -+ */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -2140,12 +2180,13 @@ void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, @@ -597,18 +562,15 @@ index 649dc0a..54bbe22 100644 + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -2203,7 +2255,10 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, +@@ -2203,7 +2255,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(dir, inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(dir, inode, de, blocksize, fname, data); /* @@ -632,7 +594,7 @@ index 649dc0a..54bbe22 100644 /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2361,7 +2417,7 @@ out_frames: +@@ -2361,12 +2386,111 @@ out_frames: */ if (retval) ext4_mark_inode_dirty(handle, dir); @@ -641,66 +603,151 @@ index 649dc0a..54bbe22 100644 brelse(bh2); return retval; } -@@ -2374,6 +2430,8 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; + +-/* 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; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2389,21 +2447,26 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, +@@ -2435,6 +2561,30 @@ static int ext4_update_dotdot(handle_t * - de = (struct ext4_dir_entry_2 *)dir_block->b_data; - /* the first item must be "." */ -- assert(de->name_len == 1 && de->name[0] == '.'); -+ 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)) { -+ ASSERT(len >= EXT4_DIR_REC_LEN(1, dir)); -+ if (len > EXT4_DIR_REC_LEN(1, dir)) { - BUFFER_TRACE(dir_block, "get_write_access"); - err = ext4_journal_get_write_access(handle, dir->i_sb, dir_block, EXT4_JTR_NONE); - if (err) - goto out_journal; + dotdot_de->inode = cpu_to_le32(inode->i_ino); - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de, dir)); - } - -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_ENTRY_LEN(de, NULL); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ ASSERT(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen, dir)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2417,10 +2480,15 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - if (len > 0) - de->rec_len = cpu_to_le16(len); - else -- assert(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2)); -+ ASSERT(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2, dir)); - de->name_len = 2; - strcpy(de->name, ".."); -- ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ EXT4_DIR_REC_LEN(2 + dlen, NULL)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2458,6 +2526,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ext4_lblk_t block, blocks; int csum_size = 0; diff --git a/ldiskfs/kernel_patches/patches/linux-6.0/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/linux-6.0/ext4-pdirop.patch index 28d4bb8..53499be 100644 --- a/ldiskfs/kernel_patches/patches/linux-6.0/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/linux-6.0/ext4-pdirop.patch @@ -834,9 +834,9 @@ index 54bbe22..9b20bc5 100644 struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2562,9 +2910,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); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/oe2203/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/oe2203/ext4-pdirop.patch index 9683e64..09d385a 100644 --- a/ldiskfs/kernel_patches/patches/oe2203/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/oe2203/ext4-pdirop.patch @@ -825,9 +825,9 @@ index 24e1276..ae94c33 100644 struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2443,9 +2792,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); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/oe2203sp1/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/oe2203sp1/ext4-data-in-dirent.patch index df53a43..ac87a8c 100644 --- a/ldiskfs/kernel_patches/patches/oe2203sp1/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/oe2203sp1/ext4-data-in-dirent.patch @@ -175,7 +175,7 @@ index 143ce00..98786d8 100644 void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); -+ struct ext4_dir_entry_2 **dest_de, int *dlen); ++ struct ext4_dir_entry_2 **dest_de, int dlen); void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -282,7 +282,7 @@ index c2c688c..686d14a 100644 err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; @@ -439,26 +439,20 @@ index 1537a76..24e1276 100644 if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -2023,14 +2035,16 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2023,10 +2035,10 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) -+ struct ext4_dir_entry_2 **dest_de, int *dlen) ++ struct ext4_dir_entry_2 **dest_de, int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)); -+ unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)) + -+ (dlen ? *dlen : 0); ++ unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname) + dlen); int nlen, rlen; unsigned int offset = 0; char *top; - -+ dlen ? *dlen = 0 : 0; /* default set to 0 */ - de = (struct ext4_dir_entry_2 *)buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -2039,10 +2053,26 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2039,7 +2053,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; @@ -467,25 +461,6 @@ index 1537a76..24e1276 100644 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ EXT4_DIR_REC_LEN(fname_len(fname))) { -+ /* set dlen=1 to indicate not -+ * enough space store fid */ -+ dlen ? *dlen = 1 : 0; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -2056,12 +2086,12 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, @@ -531,18 +506,15 @@ index 1537a76..24e1276 100644 + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -2111,7 +2151,10 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, +@@ -2111,7 +2151,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(inode, de, blocksize, fname, data); /* @@ -557,49 +529,152 @@ index 1537a76..24e1276 100644 /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2267,6 +2311,8 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; +@@ -2259,7 +2282,104 @@ out_frames: + return retval; + } + +-/* 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) - ++ EXT4_DIR_ENTRY_LEN(dotdot_de); ++ ++ dx_info = dx_get_dx_info(dot_de); ++ 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) + 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) - ++ 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); ++ 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); ++ 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)); ++ /* fix the rec_len for dotdot */ ++ dotdot_de->rec_len = ext4_rec_len_to_disk( ++ EXT4_DIR_REC_LEN(2 + dlen), 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) + { +@@ -2268,6 +2388,8 @@ static int ext4_update_dotdot(handle_t * + struct ext4_dir_entry_2 *dot_de, *dotdot_de; + unsigned int offset; + int retval = 0; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2292,11 +2338,16 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - goto out_journal; +@@ -2307,6 +2429,30 @@ static int ext4_update_dotdot(handle_t * - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de)); - } + dotdot_de->inode = cpu_to_le32(inode->i_ino); -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_ENTRY_LEN(de); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ assert(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2313,7 +2364,12 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - 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); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ EXT4_DIR_REC_LEN(2 + dlen)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2351,6 +2407,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ext4_lblk_t block, blocks; int csum_size = 0; diff --git a/ldiskfs/kernel_patches/patches/rhel7.6/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/rhel7.6/ext4-data-in-dirent.patch index 89ef4ec..184ff0f 100644 --- a/ldiskfs/kernel_patches/patches/rhel7.6/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/rhel7.6/ext4-data-in-dirent.patch @@ -168,7 +168,7 @@ Index: linux-stage/fs/ext4/ext4.h void *buf, int buf_size, const char *name, int namelen, - struct ext4_dir_entry_2 **dest_de); -+ struct ext4_dir_entry_2 **dest_de, int *dlen); ++ struct ext4_dir_entry_2 **dest_de, int dlen); void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -389,26 +389,20 @@ Index: linux-stage/fs/ext4/namei.c if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -1675,14 +1686,16 @@ int ext4_find_dest_de(struct inode *dir, +@@ -1675,10 +1686,10 @@ int ext4_find_dest_de(struct inode *dir, struct buffer_head *bh, void *buf, int buf_size, const char *name, int namelen, - struct ext4_dir_entry_2 **dest_de) -+ struct ext4_dir_entry_2 **dest_de, int *dlen) ++ struct ext4_dir_entry_2 **dest_de, int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = EXT4_DIR_REC_LEN(namelen); -+ unsigned short reclen = __EXT4_DIR_REC_LEN(namelen) + -+ (dlen ? *dlen : 0); ++ unsigned short reclen = __EXT4_DIR_REC_LEN(namelen + dlen); int nlen, rlen; unsigned int offset = 0; char *top; - -+ dlen ? *dlen = 0 : 0; /* default set to 0 */ - de = (struct ext4_dir_entry_2 *)buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -1691,10 +1704,26 @@ int ext4_find_dest_de(struct inode *dir, +@@ -1691,7 +1704,7 @@ int ext4_find_dest_de(struct inode *dir, return -EIO; if (ext4_match(namelen, name, de)) return -EEXIST; @@ -417,25 +411,6 @@ Index: linux-stage/fs/ext4/namei.c rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID */ -+ if (namelen == 2 && memcmp(name, "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ __EXT4_DIR_REC_LEN(namelen)) { -+ /* set dlen=1 to indicate not -+ * enough space store fid */ -+ dlen ? *dlen = 1 : 0; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -1708,12 +1737,12 @@ int ext4_find_dest_de(struct inode *dir, void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, @@ -482,18 +457,15 @@ Index: linux-stage/fs/ext4/namei.c err = ext4_find_dest_de(dir, inode, bh, bh->b_data, blocksize - csum_size, - name, namelen, &de); -+ name, namelen, &de, &dlen); ++ name, namelen, &de, dlen); if (err) return err; } -@@ -1765,7 +1804,10 @@ static int add_dirent_to_buf(handle_t *h +@@ -1765,7 +1804,7 @@ static int add_dirent_to_buf(handle_t *h } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(inode, de, blocksize, name, namelen); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(inode, de, blocksize, name, namelen, data); /* @@ -508,63 +480,153 @@ Index: linux-stage/fs/ext4/namei.c /* Initialize as for dx_probe */ hinfo.hash_version = dx_info->hash_version; -@@ -1927,6 +1970,8 @@ static int ext4_update_dotdot(handle_t * - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; +@@ -1933,7 +1956,105 @@ out_frames: + return retval; + } + +-/* 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) - ++ EXT4_DIR_REC_LEN(dotdot_de); ++ ++ dx_info = dx_get_dx_info(dot_de); ++ 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) + 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) - ++ 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_REC_LEN(de); ++ 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_REC_LEN(de); ++ 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_REC_LEN(prev)); ++ /* fix the rec_len for dotdot */ ++ dotdot_de->rec_len = ext4_rec_len_to_disk( ++ __EXT4_DIR_REC_LEN(2 + dlen), ++ 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) + { +@@ -1942,6 +2063,8 @@ static int ext4_update_dotdot(handle_t * + struct ext4_dir_entry_2 *dot_de, *dotdot_de; + unsigned int offset; + int retval = 0; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -1942,19 +1987,24 @@ static int ext4_update_dotdot(handle_t * - /* 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)) { -+ 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; +@@ -1981,6 +2104,30 @@ static int ext4_update_dotdot(handle_t * - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(de)); - } + dotdot_de->inode = cpu_to_le32(inode->i_ino); -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_REC_LEN(de); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ assert(len == 0 || len >= __EXT4_DIR_REC_LEN(2 + dlen)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -1968,10 +2018,15 @@ static int ext4_update_dotdot(handle_t * - if (len > 0) - de->rec_len = cpu_to_le16(len); - else -- assert(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2)); -+ 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); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ __EXT4_DIR_REC_LEN(2 + dlen)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2445,37 +2500,70 @@ retry: return err; } @@ -713,7 +775,7 @@ Index: linux-stage/fs/ext4/inline.c err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, inline_size, - name, namelen, &de); -+ name, namelen, &de, NULL); ++ name, namelen, &de, 0); if (err) return err; diff --git a/ldiskfs/kernel_patches/patches/rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch b/ldiskfs/kernel_patches/patches/rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch index ff3ab9c..8260c93 100644 --- a/ldiskfs/kernel_patches/patches/rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch +++ b/ldiskfs/kernel_patches/patches/rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch @@ -2,18 +2,19 @@ Index: linux-3.10.0-123.9.3.el7.x86_64/fs/ext4/namei.c =================================================================== --- linux-3.10.0-123.9.3.el7.x86_64.orig/fs/ext4/namei.c +++ linux-3.10.0-123.9.3.el7.x86_64/fs/ext4/namei.c -@@ -1894,6 +1894,72 @@ static int make_indexed_dir(handle_t *ha +@@ -1894,6 +1894,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); @@ -21,67 +22,62 @@ Index: linux-3.10.0-123.9.3.el7.x86_64/fs/ext4/namei.c + if (IS_DIRSYNC(dir)) + handle->h_sync = 1; + -+ dir_block = ext4_bread(handle, dir, 0, 0, &err); -+ if (!dir_block) -+ 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; ++ bh = ext4_read_dirblock(dir, 0, EITHER); ++ if (IS_ERR(bh)) ++ return PTR_ERR(bh); + -+ journal = 1; -+ de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); ++ 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; + } -+ -+ 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; ++ 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; + } + -+ 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); ++ BUFFER_TRACE(dir_block, "get_write_access"); ++ retval = ext4_journal_get_write_access(handle, bh); ++ if (retval) ++ goto out; ++ ++ 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_dirent_node(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_dirent_node(handle, dir, bh); + } -+ brelse(dir_block); + +out: -+ return err; ++ brelse(bh); ++ return retval; +} + /* * ext4_add_entry() * -@@ -1938,6 +2004,9 @@ int ext4_add_entry(handle_t *handle, str +@@ -1937,6 +2003,10 @@ static int ext4_add_entry(handle_t *hand + } } ++ 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, dentry, inode); if (!retval || (retval != ERR_BAD_DX_DIR)) - return retval; diff --git a/ldiskfs/kernel_patches/patches/rhel7.9/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/rhel7.9/ext4-pdirop.patch index 5df59bf..525157a 100644 --- a/ldiskfs/kernel_patches/patches/rhel7.9/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/rhel7.9/ext4-pdirop.patch @@ -757,9 +757,9 @@ Index: linux-3.10.0-1160.2.1.el7.x86_64/fs/ext4/namei.c struct inode *dir = dentry->d_parent->d_inode; struct buffer_head *bh = NULL; @@ -2108,9 +2446,10 @@ static int ext4_add_entry(handle_t *hand - if (dentry->d_name.len == 2 && - memcmp(dentry->d_name.name, "..", 2) == 0) - return ext4_update_dotdot(handle, dentry, inode); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, dentry, inode); + retval = ext4_dx_add_entry(handle, dentry, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/rhel8.4/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/rhel8.4/ext4-data-in-dirent.patch index a705ec1..20386af 100644 --- a/ldiskfs/kernel_patches/patches/rhel8.4/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/rhel8.4/ext4-data-in-dirent.patch @@ -167,7 +167,7 @@ index cb649f0..fe35251 100644 void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); -+ struct ext4_dir_entry_2 **dest_de, int *dlen); ++ struct ext4_dir_entry_2 **dest_de, int dlen); void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -251,7 +251,7 @@ index 2fec62d..3f35821 100644 err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; @@ -408,26 +408,20 @@ index 8713671..23bd871 100644 if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -1943,14 +1955,16 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -1943,10 +1955,10 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) -+ struct ext4_dir_entry_2 **dest_de, int *dlen) ++ struct ext4_dir_entry_2 **dest_de, int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)); -+ unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)) + -+ (dlen ? *dlen : 0); ++ unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname) + dlen); int nlen, rlen; unsigned int offset = 0; char *top; - -+ dlen ? *dlen = 0 : 0; /* default set to 0 */ - de = (struct ext4_dir_entry_2 *)buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -1959,10 +1973,26 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -1959,7 +1973,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; @@ -436,25 +430,6 @@ index 8713671..23bd871 100644 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ EXT4_DIR_REC_LEN(fname_len(fname))) { -+ /* set dlen=1 to indicate not -+ * enough space store fid */ -+ dlen ? *dlen = 1 : 0; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -1976,12 +2006,12 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, @@ -500,18 +475,15 @@ index 8713671..23bd871 100644 + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -2031,7 +2071,10 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, +@@ -2031,7 +2071,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(inode, de, blocksize, fname, data); /* @@ -526,49 +498,152 @@ index 8713671..23bd871 100644 /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2186,6 +2230,8 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; +@@ -2068,7 +2091,104 @@ out_frames: + return retval; + } + +-/* 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) - ++ EXT4_DIR_ENTRY_LEN(dotdot_de); ++ ++ dx_info = dx_get_dx_info(dot_de); ++ 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) + 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) - ++ 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); ++ 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); ++ 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)); ++ /* fix the rec_len for dotdot */ ++ dotdot_de->rec_len = ext4_rec_len_to_disk( ++ EXT4_DIR_REC_LEN(2 + dlen), 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) + { +@@ -2077,6 +2203,8 @@ static int ext4_update_dotdot(handle_t * + struct ext4_dir_entry_2 *dot_de, *dotdot_de; + unsigned int offset; + int retval = 0; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2211,11 +2257,16 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - goto out_journal; +@@ -2116,6 +2244,30 @@ static int ext4_update_dotdot(handle_t * - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de)); - } + dotdot_de->inode = cpu_to_le32(inode->i_ino); -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_ENTRY_LEN(de); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ assert(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2232,7 +2283,12 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - 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); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ EXT4_DIR_REC_LEN(2 + dlen)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2271,6 +2327,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ext4_lblk_t block, blocks; int csum_size = 0; diff --git a/ldiskfs/kernel_patches/patches/rhel8.4/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/rhel8.4/ext4-pdirop.patch index ad22991..4894196 100644 --- a/ldiskfs/kernel_patches/patches/rhel8.4/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/rhel8.4/ext4-pdirop.patch @@ -761,9 +761,9 @@ Index: linux-4.18.0-240.1.1.el8/fs/ext4/namei.c struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2234,9 +2572,10 @@ static int ext4_add_entry(handle_t *hand - if (dentry->d_name.len == 2 && - memcmp(dentry->d_name.name, "..", 2) == 0) - return ext4_update_dotdot(handle, dentry, inode); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/rhel8.7/ext4-hash-indexed-dir-dotdot-update.patch b/ldiskfs/kernel_patches/patches/rhel8.7/ext4-hash-indexed-dir-dotdot-update.patch new file mode 100644 index 0000000..68d4aa2 --- /dev/null +++ b/ldiskfs/kernel_patches/patches/rhel8.7/ext4-hash-indexed-dir-dotdot-update.patch @@ -0,0 +1,83 @@ +Index: linux-4.15.0/fs/ext4/namei.c +=================================================================== +--- linux-4.15.0.orig/fs/ext4/namei.c ++++ linux-4.15.0/fs/ext4/namei.c +@@ -2043,6 +2043,67 @@ out_frames: + return retval; + } + ++/* 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 *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; ++ ++ 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; ++ } ++ 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; ++ } ++ ++ BUFFER_TRACE(dir_block, "get_write_access"); ++ retval = ext4_journal_get_write_access(handle, bh); ++ if (retval) ++ goto out; ++ ++ dotdot_de->inode = cpu_to_le32(inode->i_ino); ++ ++ 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_dirent_node(handle, dir, bh); ++ } ++ ++out: ++ brelse(bh); ++ return retval; ++} ++ + /* + * ext4_add_entry() + * +@@ -2090,6 +2158,10 @@ static int ext4_add_entry(handle_t *hand + } + } + ++ if (dentry->d_name.len == 2 && ++ memcmp(dentry->d_name.name, "..", 2) == 0) ++ return ext4_update_dotdot(handle, dentry, inode); ++ + if (is_dx(dir)) { + retval = ext4_dx_add_entry(handle, &fname, dir, inode); + if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/rhel8.7/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/rhel8.7/ext4-pdirop.patch index 2da3d4d..450e821 100644 --- a/ldiskfs/kernel_patches/patches/rhel8.7/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/rhel8.7/ext4-pdirop.patch @@ -775,9 +775,9 @@ Index: linux-4.18.0-423.el8/fs/ext4/namei.c struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2307,9 +2648,10 @@ static int ext4_add_entry(handle_t *hand - if (dentry->d_name.len == 2 && - memcmp(dentry->d_name.name, "..", 2) == 0) - return ext4_update_dotdot(handle, dentry, inode); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/rhel8/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/rhel8/ext4-pdirop.patch index 35d8c03..c25b777 100644 --- a/ldiskfs/kernel_patches/patches/rhel8/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/rhel8/ext4-pdirop.patch @@ -757,9 +757,9 @@ Index: linux-4.18.0-80.1.2.el8_0/fs/ext4/namei.c struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2226,9 +2564,10 @@ static int ext4_add_entry(handle_t *hand - if (dentry->d_name.len == 2 && - memcmp(dentry->d_name.name, "..", 2) == 0) - return ext4_update_dotdot(handle, dentry, inode); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/linux-5.18/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/rhel9.1/ext4-data-in-dirent.patch similarity index 86% rename from ldiskfs/kernel_patches/patches/linux-5.18/ext4-data-in-dirent.patch rename to ldiskfs/kernel_patches/patches/rhel9.1/ext4-data-in-dirent.patch index 2eee9cf..04ca33b 100644 --- a/ldiskfs/kernel_patches/patches/linux-5.18/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/rhel9.1/ext4-data-in-dirent.patch @@ -170,7 +170,7 @@ index f4a1557..85556ce 100644 struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); + struct ext4_dir_entry_2 **dest_de, -+ int *dlen); ++ int dlen); void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -274,7 +274,7 @@ index e9ef5cf..a23fd25 100644 err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; @@ -493,31 +493,21 @@ index 4d932a2..ee1a058 100644 if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -2078,14 +2089,21 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2078,10 +2089,11 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) + struct ext4_dir_entry_2 **dest_de, -+ int *dlen) ++ int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ unsigned short reclen; ++ unsigned short reclen = ext4_dir_rec_len(fname_len(fname) + dlen, dir); int nlen, rlen; unsigned int offset = 0; char *top; - -+ if (dlen) { -+ reclen = ext4_dir_rec_len(fname_len(fname) + *dlen, dir); -+ *dlen = 0; -+ } else { -+ reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ } - de = (struct ext4_dir_entry_2 *)buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -2094,10 +2112,31 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2094,7 +2112,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; @@ -526,30 +516,6 @@ index 4d932a2..ee1a058 100644 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID -+ */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ ext4_dir_rec_len(fname_len(fname), dir)) { -+ /* set dlen = 1 to indicate not -+ * enough space store fid -+ */ -+ if (dlen) -+ *dlen = 1; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. -+ */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -2112,12 +2151,13 @@ void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, @@ -597,18 +563,15 @@ index 4d932a2..ee1a058 100644 + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -2175,7 +2226,10 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, +@@ -2175,7 +2226,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(dir, inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(dir, inode, de, blocksize, fname, data); /* @@ -632,7 +595,7 @@ index 4d932a2..ee1a058 100644 /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2325,7 +2380,7 @@ out_frames: +@@ -2348,12 +2373,111 @@ out_frames: */ if (retval) ext4_mark_inode_dirty(handle, dir); @@ -641,66 +604,151 @@ index 4d932a2..ee1a058 100644 brelse(bh2); return retval; } -@@ -2338,6 +2393,8 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; + +-/* 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) + { +@@ -2362,6 +2486,8 @@ static int ext4_update_dotdot(handle_t * + struct ext4_dir_entry_2 *dot_de, *dotdot_de; + unsigned int offset; + int retval = 0; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2353,21 +2410,26 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - - de = (struct ext4_dir_entry_2 *)dir_block->b_data; - /* the first item must be "." */ -- assert(de->name_len == 1 && de->name[0] == '.'); -+ 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)) { -+ ASSERT(len >= EXT4_DIR_REC_LEN(1, dir)); -+ if (len > EXT4_DIR_REC_LEN(1, dir)) { - BUFFER_TRACE(dir_block, "get_write_access"); - err = ext4_journal_get_write_access(handle, dir->i_sb, dir_block, EXT4_JTR_NONE); - if (err) - goto out_journal; +@@ -2402,6 +2528,30 @@ static int ext4_update_dotdot(handle_t * - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de, dir)); - } + dotdot_de->inode = cpu_to_le32(inode->i_ino); -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_ENTRY_LEN(de, NULL); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ ASSERT(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen, dir)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2381,10 +2443,15 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - if (len > 0) - de->rec_len = cpu_to_le16(len); - else -- assert(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2)); -+ ASSERT(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2, dir)); - de->name_len = 2; - strcpy(de->name, ".."); -- ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ EXT4_DIR_REC_LEN(2 + dlen, NULL)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2422,6 +2489,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ext4_lblk_t block, blocks; int csum_size = 0; diff --git a/ldiskfs/kernel_patches/patches/rhel9.1/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/rhel9.1/ext4-pdirop.patch index ed4242c..3662f8d 100644 --- a/ldiskfs/kernel_patches/patches/rhel9.1/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/rhel9.1/ext4-pdirop.patch @@ -827,9 +827,9 @@ index 2760dc6..2d14bd2 100644 struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2548,9 +2896,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); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/rhel9.2/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/rhel9.2/ext4-data-in-dirent.patch index 1d269df..5e11425 100644 --- a/ldiskfs/kernel_patches/patches/rhel9.2/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/rhel9.2/ext4-data-in-dirent.patch @@ -170,7 +170,7 @@ index e84d4a7..5e73b80 100644 struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); + struct ext4_dir_entry_2 **dest_de, -+ int *dlen); ++ int dlen); void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -274,7 +274,7 @@ index a4fbe82..6a424df 100644 err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; @@ -493,32 +493,21 @@ index 649dc0a..54bbe22 100644 if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -2106,14 +2117,22 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2106,10 +2117,11 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) + struct ext4_dir_entry_2 **dest_de, -+ int *dlen) ++ int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ unsigned short reclen; ++ unsigned short reclen = ext4_dir_rec_len(fname_len(fname) + dlen, dir); int nlen, rlen; unsigned int offset = 0; char *top; - -+ if (dlen) { -+ reclen = ext4_dir_rec_len(fname_len(fname) + *dlen, dir); -+ *dlen = 0; -+ } else { -+ reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ } -+ - de = buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -2122,10 +2141,31 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2122,7 +2141,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; @@ -527,30 +516,6 @@ index 649dc0a..54bbe22 100644 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID -+ */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ ext4_dir_rec_len(fname_len(fname), dir)) { -+ /* set dlen = 1 to indicate not -+ * enough space store fid -+ */ -+ if (dlen) -+ *dlen = 1; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. -+ */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -2140,12 +2180,13 @@ void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, @@ -598,18 +563,15 @@ index 649dc0a..54bbe22 100644 + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -2203,7 +2255,10 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, +@@ -2203,7 +2255,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(dir, inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(dir, inode, de, blocksize, fname, data); /* @@ -633,7 +595,7 @@ index 649dc0a..54bbe22 100644 /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2361,7 +2417,7 @@ out_frames: +@@ -2362,12 +2387,111 @@ out_frames: */ if (retval) ext4_mark_inode_dirty(handle, dir); @@ -642,66 +604,151 @@ index 649dc0a..54bbe22 100644 brelse(bh2); return retval; } -@@ -2374,6 +2430,8 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; + +-/* 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) + { +@@ -2376,6 +2500,8 @@ static int ext4_update_dotdot(handle_t * + struct ext4_dir_entry_2 *dot_de, *dotdot_de; + unsigned int offset; + int retval = 0; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2389,21 +2447,26 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, +@@ -2416,6 +2542,30 @@ static int ext4_update_dotdot(handle_t * - de = (struct ext4_dir_entry_2 *)dir_block->b_data; - /* the first item must be "." */ -- assert(de->name_len == 1 && de->name[0] == '.'); -+ 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)) { -+ ASSERT(len >= EXT4_DIR_REC_LEN(1, dir)); -+ if (len > EXT4_DIR_REC_LEN(1, dir)) { - BUFFER_TRACE(dir_block, "get_write_access"); - err = ext4_journal_get_write_access(handle, dir->i_sb, dir_block, EXT4_JTR_NONE); - if (err) - goto out_journal; + dotdot_de->inode = cpu_to_le32(inode->i_ino); - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de, dir)); - } - -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_ENTRY_LEN(de, NULL); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ ASSERT(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen, dir)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2417,10 +2480,15 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - if (len > 0) - de->rec_len = cpu_to_le16(len); - else -- assert(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2)); -+ ASSERT(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2, dir)); - de->name_len = 2; - strcpy(de->name, ".."); -- ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ EXT4_DIR_REC_LEN(2 + dlen, NULL)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2458,6 +2526,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ext4_lblk_t block, blocks; int csum_size = 0; diff --git a/ldiskfs/kernel_patches/patches/rhel9.2/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/rhel9.2/ext4-pdirop.patch index 28d4bb8..53499be 100644 --- a/ldiskfs/kernel_patches/patches/rhel9.2/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/rhel9.2/ext4-pdirop.patch @@ -834,9 +834,9 @@ index 54bbe22..9b20bc5 100644 struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2562,9 +2910,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); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/rhel9.3/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/rhel9.3/ext4-data-in-dirent.patch index 4025ae6..3df382c 100644 --- a/ldiskfs/kernel_patches/patches/rhel9.3/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/rhel9.3/ext4-data-in-dirent.patch @@ -170,7 +170,7 @@ index a4af3ec8..930ca3a5 100644 struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); + struct ext4_dir_entry_2 **dest_de, -+ int *dlen); ++ int dlen); void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -274,7 +274,7 @@ index c4475a74..3fc75d80 100644 err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; @@ -493,32 +493,21 @@ index d0afa8f2..839d51ba 100644 if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -2106,14 +2117,22 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2106,10 +2117,11 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) + struct ext4_dir_entry_2 **dest_de, -+ int *dlen) ++ int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ unsigned short reclen; ++ unsigned short reclen = ext4_dir_rec_len(fname_len(fname) + dlen, dir); int nlen, rlen; unsigned int offset = 0; char *top; - -+ if (dlen) { -+ reclen = ext4_dir_rec_len(fname_len(fname) + *dlen, dir); -+ *dlen = 0; -+ } else { -+ reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ } -+ - de = buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -2122,10 +2141,31 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2122,7 +2141,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; @@ -527,30 +516,6 @@ index d0afa8f2..839d51ba 100644 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID -+ */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ ext4_dir_rec_len(fname_len(fname), dir)) { -+ /* set dlen = 1 to indicate not -+ * enough space store fid -+ */ -+ if (dlen) -+ *dlen = 1; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. -+ */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -2140,12 +2180,13 @@ void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, @@ -598,18 +563,15 @@ index d0afa8f2..839d51ba 100644 + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -2203,7 +2255,10 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, +@@ -2203,7 +2255,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(dir, inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(dir, inode, de, blocksize, fname, data); /* @@ -633,7 +595,7 @@ index d0afa8f2..839d51ba 100644 /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2361,7 +2417,7 @@ out_frames: +@@ -2361,12 +2386,111 @@ out_frames: */ if (retval) ext4_mark_inode_dirty(handle, dir); @@ -642,66 +604,151 @@ index d0afa8f2..839d51ba 100644 brelse(bh2); return retval; } -@@ -2374,6 +2430,8 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; + +-/* 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) + { +@@ -2375,6 +2504,8 @@ static int ext4_update_dotdot(handle_t * + struct ext4_dir_entry_2 *dot_de, *dotdot_de; + unsigned int offset; + int retval = 0; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2389,21 +2447,26 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, +@@ -2415,6 +2546,30 @@ static int ext4_update_dotdot(handle_t * - de = (struct ext4_dir_entry_2 *)dir_block->b_data; - /* the first item must be "." */ -- assert(de->name_len == 1 && de->name[0] == '.'); -+ 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)) { -+ ASSERT(len >= EXT4_DIR_REC_LEN(1, dir)); -+ if (len > EXT4_DIR_REC_LEN(1, dir)) { - BUFFER_TRACE(dir_block, "get_write_access"); - err = ext4_journal_get_write_access(handle, dir->i_sb, dir_block, EXT4_JTR_NONE); - if (err) - goto out_journal; + dotdot_de->inode = cpu_to_le32(inode->i_ino); - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de, dir)); - } - -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_ENTRY_LEN(de, NULL); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ ASSERT(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen, dir)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2417,10 +2480,15 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - if (len > 0) - de->rec_len = cpu_to_le16(len); - else -- assert(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2)); -+ ASSERT(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2, dir)); - de->name_len = 2; - strcpy(de->name, ".."); -- ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ EXT4_DIR_REC_LEN(2 + dlen, NULL)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2458,6 +2526,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ext4_lblk_t block, blocks; int csum_size = 0; diff --git a/ldiskfs/kernel_patches/patches/rhel9/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/rhel9/ext4-data-in-dirent.patch index 31ebd76..d2eb65d 100644 --- a/ldiskfs/kernel_patches/patches/rhel9/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/rhel9/ext4-data-in-dirent.patch @@ -170,7 +170,7 @@ index 58645be..8c6864c 100644 struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); + struct ext4_dir_entry_2 **dest_de, -+ int *dlen); ++ int dlen); void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -274,7 +274,7 @@ index 39a1ab1..46f2e1e 100644 err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; @@ -493,31 +493,21 @@ index 1f95773..9edb487 100644 if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -2051,14 +2062,21 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2051,10 +2062,11 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) + struct ext4_dir_entry_2 **dest_de, -+ int *dlen) ++ int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ unsigned short reclen; ++ unsigned short reclen = ext4_dir_rec_len(fname_len(fname) + dlen, dir); int nlen, rlen; unsigned int offset = 0; char *top; - -+ if (dlen) { -+ reclen = ext4_dir_rec_len(fname_len(fname) + *dlen, dir); -+ *dlen = 0; -+ } else { -+ reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ } - de = (struct ext4_dir_entry_2 *)buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -2067,10 +2085,31 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2067,7 +2085,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; @@ -526,30 +516,6 @@ index 1f95773..9edb487 100644 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID -+ */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ ext4_dir_rec_len(fname_len(fname), dir)) { -+ /* set dlen = 1 to indicate not -+ * enough space store fid -+ */ -+ if (dlen) -+ *dlen = 1; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. -+ */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -2085,12 +2124,13 @@ void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, @@ -597,18 +563,15 @@ index 1f95773..9edb487 100644 + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -2148,7 +2199,10 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, +@@ -2148,7 +2199,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(dir, inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(dir, inode, de, blocksize, fname, data); /* @@ -632,7 +595,7 @@ index 1f95773..9edb487 100644 /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2298,7 +2353,7 @@ out_frames: +@@ -2298,12 +2323,111 @@ out_frames: */ if (retval) ext4_mark_inode_dirty(handle, dir); @@ -641,66 +604,151 @@ index 1f95773..9edb487 100644 brelse(bh2); return retval; } -@@ -2311,6 +2366,8 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; + +-/* 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) + { +@@ -2312,6 +2436,8 @@ static int ext4_update_dotdot(handle_t * + struct ext4_dir_entry_2 *dot_de, *dotdot_de; + unsigned int offset; + int retval = 0; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2326,21 +2383,26 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - - de = (struct ext4_dir_entry_2 *)dir_block->b_data; - /* the first item must be "." */ -- assert(de->name_len == 1 && de->name[0] == '.'); -+ 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)) { -+ ASSERT(len >= EXT4_DIR_REC_LEN(1, dir)); -+ if (len > EXT4_DIR_REC_LEN(1, dir)) { - BUFFER_TRACE(dir_block, "get_write_access"); - err = ext4_journal_get_write_access(handle, dir->i_sb, dir_block, EXT4_JTR_NONE); - if (err) - goto out_journal; +@@ -2352,6 +2478,30 @@ static int ext4_update_dotdot(handle_t * - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de, dir)); - } + dotdot_de->inode = cpu_to_le32(inode->i_ino); -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_ENTRY_LEN(de, NULL); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ ASSERT(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen, dir)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2354,10 +2416,15 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - if (len > 0) - de->rec_len = cpu_to_le16(len); - else -- assert(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2)); -+ ASSERT(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2, dir)); - de->name_len = 2; - strcpy(de->name, ".."); -- ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ EXT4_DIR_REC_LEN(2 + dlen, NULL)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2395,6 +2462,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ext4_lblk_t block, blocks; int csum_size = 0; diff --git a/ldiskfs/kernel_patches/patches/rhel9/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/rhel9/ext4-pdirop.patch index 22012f9..1a2791f 100644 --- a/ldiskfs/kernel_patches/patches/rhel9/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/rhel9/ext4-pdirop.patch @@ -798,9 +798,9 @@ index 059bc08..7d25879 100644 struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2493,9 +2832,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); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/sles15sp1/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/sles15sp1/ext4-pdirop.patch index eb07fed..30f6bd5 100644 --- a/ldiskfs/kernel_patches/patches/sles15sp1/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/sles15sp1/ext4-pdirop.patch @@ -760,9 +760,9 @@ Reviewed-by: Andreas Dilger struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2251,7 +2589,7 @@ static int ext4_add_entry(handle_t *hand - if (dentry->d_name.len == 2 && - memcmp(dentry->d_name.name, "..", 2) == 0) - return ext4_update_dotdot(handle, dentry, inode); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/sles15sp3/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/sles15sp3/ext4-data-in-dirent.patch index c016423..6fbbcdc 100644 --- a/ldiskfs/kernel_patches/patches/sles15sp3/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/sles15sp3/ext4-data-in-dirent.patch @@ -182,7 +182,7 @@ index 8577b51..dab4486 100644 void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); -+ struct ext4_dir_entry_2 **dest_de, int *dlen); ++ struct ext4_dir_entry_2 **dest_de, int dlen); void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -266,7 +266,7 @@ index 46151bd..316892b 100644 err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; @@ -423,26 +423,20 @@ index 54cca61..2bc4682 100644 if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -1954,14 +1966,16 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -1954,10 +1966,10 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) -+ struct ext4_dir_entry_2 **dest_de, int *dlen) ++ struct ext4_dir_entry_2 **dest_de, int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)); -+ unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)) + -+ (dlen ? *dlen : 0); ++ unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname) + dlen); int nlen, rlen; unsigned int offset = 0; char *top; - -+ dlen ? *dlen = 0 : 0; /* default set to 0 */ - de = (struct ext4_dir_entry_2 *)buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -1970,10 +1984,26 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -1970,7 +1984,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; @@ -451,25 +445,6 @@ index 54cca61..2bc4682 100644 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ EXT4_DIR_REC_LEN(fname_len(fname))) { -+ /* set dlen=1 to indicate not -+ * enough space store fid */ -+ dlen ? *dlen = 1 : 0; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -1987,12 +2017,12 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, @@ -516,18 +491,15 @@ index 54cca61..2bc4682 100644 + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -2042,7 +2083,10 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, +@@ -2042,7 +2083,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(inode, de, blocksize, fname, data); /* @@ -542,49 +514,152 @@ index 54cca61..2bc4682 100644 /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2197,6 +2242,8 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; +@@ -2189,7 +2213,104 @@ out_frames: + return retval; + } + +-/* 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) - ++ EXT4_DIR_ENTRY_LEN(dotdot_de); ++ ++ dx_info = dx_get_dx_info(dot_de); ++ 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) + 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) - ++ 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); ++ 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); ++ 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)); ++ /* fix the rec_len for dotdot */ ++ dotdot_de->rec_len = ext4_rec_len_to_disk( ++ EXT4_DIR_REC_LEN(2 + dlen), 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) + { +@@ -2198,6 +2319,8 @@ static int ext4_update_dotdot(handle_t * + struct ext4_dir_entry_2 *dot_de, *dotdot_de; + unsigned int offset; + int retval = 0; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2222,11 +2269,16 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - goto out_journal; +@@ -2237,6 +2360,30 @@ static int ext4_update_dotdot(handle_t * - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de)); - } + dotdot_de->inode = cpu_to_le32(inode->i_ino); -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_ENTRY_LEN(de); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ assert(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2243,7 +2295,12 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - 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); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ EXT4_DIR_REC_LEN(2 + dlen)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2282,6 +2339,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ext4_lblk_t block, blocks; int csum_size = 0; diff --git a/ldiskfs/kernel_patches/patches/sles15sp3/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/sles15sp3/ext4-pdirop.patch index 8e1ff81..6c9e1be 100644 --- a/ldiskfs/kernel_patches/patches/sles15sp3/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/sles15sp3/ext4-pdirop.patch @@ -826,9 +826,9 @@ index a7dd2f2..046fc45 100644 struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2426,9 +2774,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); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/sles15sp4/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/sles15sp4/ext4-data-in-dirent.patch index c492420..7928ae2 100644 --- a/ldiskfs/kernel_patches/patches/sles15sp4/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/sles15sp4/ext4-data-in-dirent.patch @@ -171,7 +171,7 @@ index 0791a8b..f1bc21d 100644 struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); + struct ext4_dir_entry_2 **dest_de, -+ int *dlen); ++ int dlen); void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -279,7 +279,7 @@ index 9626c31..ed31b5c 100644 err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; @@ -498,31 +498,21 @@ index 7f00dc3..51c950b 100644 if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -2101,14 +2112,21 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2101,10 +2112,11 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) + struct ext4_dir_entry_2 **dest_de, -+ int *dlen) ++ int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ unsigned short reclen; ++ unsigned short reclen = ext4_dir_rec_len(fname_len(fname) + dlen, dir); int nlen, rlen; unsigned int offset = 0; char *top; - -+ if (dlen) { -+ reclen = ext4_dir_rec_len(fname_len(fname) + *dlen, dir); -+ *dlen = 0; -+ } else { -+ reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ } - de = (struct ext4_dir_entry_2 *)buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -2117,10 +2135,31 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2117,7 +2135,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; @@ -531,30 +521,6 @@ index 7f00dc3..51c950b 100644 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID -+ */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ ext4_dir_rec_len(fname_len(fname), dir)) { -+ /* set dlen = 1 to indicate not -+ * enough space store fid -+ */ -+ if (dlen) -+ *dlen = 1; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. -+ */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -2135,12 +2174,13 @@ void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, @@ -602,18 +568,15 @@ index 7f00dc3..51c950b 100644 + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -2198,7 +2249,10 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, +@@ -2198,7 +2249,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(dir, inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(dir, inode, de, blocksize, fname, data); /* @@ -637,7 +600,7 @@ index 7f00dc3..51c950b 100644 /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2348,7 +2403,7 @@ out_frames: +@@ -2348,12 +2373,111 @@ out_frames: */ if (retval) ext4_mark_inode_dirty(handle, dir); @@ -646,65 +609,151 @@ index 7f00dc3..51c950b 100644 brelse(bh2); return retval; } -@@ -2361,6 +2416,8 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; + +-/* 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) + { +@@ -2362,6 +2486,8 @@ static int ext4_update_dotdot(handle_t * + struct ext4_dir_entry_2 *dot_de, *dotdot_de; + unsigned int offset; + int retval = 0; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2376,21 +2433,25 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - - de = (struct ext4_dir_entry_2 *)dir_block->b_data; - /* the first item must be "." */ -- assert(de->name_len == 1 && de->name[0] == '.'); -+ 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)) { -+ ASSERT(len >= EXT4_DIR_REC_LEN(1, dir)); -+ if (len > EXT4_DIR_REC_LEN(1, dir)) { - BUFFER_TRACE(dir_block, "get_write_access"); - err = ext4_journal_get_write_access(handle, dir->i_sb, dir_block, EXT4_JTR_NONE); - if (err) - goto out_journal; +@@ -2402,6 +2528,30 @@ static int ext4_update_dotdot(handle_t * - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1, dir)); - } + dotdot_de->inode = cpu_to_le32(inode->i_ino); -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_REC_LEN(1, dir); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ ASSERT(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen, dir)); - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2404,10 +2465,15 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - if (len > 0) - de->rec_len = cpu_to_le16(len); - else -- assert(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2)); -+ ASSERT(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2, dir)); - de->name_len = 2; - strcpy(de->name, ".."); -- ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ EXT4_DIR_REC_LEN(2 + dlen, NULL)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2445,6 +2511,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ext4_lblk_t block, blocks; int csum_size = 0; diff --git a/ldiskfs/kernel_patches/patches/sles15sp4/ext4-hash-indexed-dir-dotdot-update.patch b/ldiskfs/kernel_patches/patches/sles15sp4/ext4-hash-indexed-dir-dotdot-update.patch index efc330f..9834755 100644 --- a/ldiskfs/kernel_patches/patches/sles15sp4/ext4-hash-indexed-dir-dotdot-update.patch +++ b/ldiskfs/kernel_patches/patches/sles15sp4/ext4-hash-indexed-dir-dotdot-update.patch @@ -8,18 +8,19 @@ diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 95b21f5..e4514c9 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c -@@ -2301,6 +2301,74 @@ out_frames: +@@ -2364,6 +2364,68 @@ 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); @@ -27,72 +28,66 @@ index 95b21f5..e4514c9 100644 + 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->i_sb, dir_block, EXT4_JTR_NONE); -+ 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->i_sb, dir_block, EXT4_JTR_NONE); -+ if (err) -+ goto out_journal; -+ } ++ BUFFER_TRACE(dir_block, "get_write_access"); ++ retval = ext4_journal_get_write_access(handle, dir->i_sb, bh, ++ EXT4_JTR_NONE); ++ 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() * -@@ -2357,6 +2425,9 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, +@@ -2356,6 +2424,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.31.1 diff --git a/ldiskfs/kernel_patches/patches/sles15sp4/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/sles15sp4/ext4-pdirop.patch index ea3b185..e12ffce 100644 --- a/ldiskfs/kernel_patches/patches/sles15sp4/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/sles15sp4/ext4-pdirop.patch @@ -827,9 +827,9 @@ index 51c950b..1b8c80e 100644 struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2547,9 +2895,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); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/ubuntu18/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/ubuntu18/ext4-data-in-dirent.patch index 978c3b6..7de7ac4 100644 --- a/ldiskfs/kernel_patches/patches/ubuntu18/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/ubuntu18/ext4-data-in-dirent.patch @@ -167,7 +167,7 @@ Index: linux-4.15.0/fs/ext4/ext4.h void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); -+ struct ext4_dir_entry_2 **dest_de, int *dlen); ++ struct ext4_dir_entry_2 **dest_de, int dlen); void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -386,26 +386,20 @@ Index: linux-4.15.0/fs/ext4/namei.c if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -1808,14 +1819,16 @@ int ext4_find_dest_de(struct inode *dir, +@@ -1808,10 +1819,10 @@ int ext4_find_dest_de(struct inode *dir, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) -+ struct ext4_dir_entry_2 **dest_de, int *dlen) ++ struct ext4_dir_entry_2 **dest_de, int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)); -+ unsigned short reclen = __EXT4_DIR_REC_LEN(fname_len(fname)) + -+ (dlen ? *dlen : 0); ++ unsigned short reclen = __EXT4_DIR_REC_LEN(fname_len(fname) + dlen); int nlen, rlen; unsigned int offset = 0; char *top; - -+ dlen ? *dlen = 0 : 0; /* default set to 0 */ - de = (struct ext4_dir_entry_2 *)buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -1824,10 +1837,26 @@ int ext4_find_dest_de(struct inode *dir, +@@ -1824,7 +1837,7 @@ int ext4_find_dest_de(struct inode *dir, return -EFSCORRUPTED; if (ext4_match(fname, de)) return -EEXIST; @@ -414,25 +408,6 @@ Index: linux-4.15.0/fs/ext4/namei.c rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ __EXT4_DIR_REC_LEN(fname_len(fname))) { -+ /* set dlen=1 to indicate not -+ * enough space store fid */ -+ dlen ? *dlen = 1 : 0; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -1841,12 +1870,12 @@ int ext4_find_dest_de(struct inode *dir, void ext4_insert_dentry(struct inode *inode, struct ext4_dir_entry_2 *de, @@ -478,18 +453,15 @@ Index: linux-4.15.0/fs/ext4/namei.c + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -1896,7 +1935,10 @@ static int add_dirent_to_buf(handle_t *h +@@ -1896,7 +1935,7 @@ static int add_dirent_to_buf(handle_t *h } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(inode, de, blocksize, fname, data); /* @@ -504,63 +476,153 @@ Index: linux-4.15.0/fs/ext4/namei.c /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2055,6 +2098,8 @@ static int ext4_update_dotdot(handle_t * - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; +@@ -2047,7 +2070,105 @@ out_frames: + return retval; + } + +-/* 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) - ++ EXT4_DIR_REC_LEN(dotdot_de); ++ ++ dx_info = dx_get_dx_info(dot_de); ++ 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) + 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) - ++ 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_REC_LEN(de); ++ 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_REC_LEN(de); ++ 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_REC_LEN(prev)); ++ /* fix the rec_len for dotdot */ ++ dotdot_de->rec_len = ext4_rec_len_to_disk( ++ __EXT4_DIR_REC_LEN(2 + dlen), ++ 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) + { +@@ -2056,6 +2177,8 @@ static int ext4_update_dotdot(handle_t * + struct ext4_dir_entry_2 *dot_de, *dotdot_de; + unsigned int offset; + int retval = 0; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2072,19 +2117,24 @@ static int ext4_update_dotdot(handle_t * - /* 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)) { -+ 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; +@@ -2095,6 +2218,30 @@ static int ext4_update_dotdot(handle_t * - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(de)); - } + dotdot_de->inode = cpu_to_le32(inode->i_ino); -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_REC_LEN(de); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ assert(len == 0 || len >= __EXT4_DIR_REC_LEN(2 + dlen)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2098,10 +2148,15 @@ static int ext4_update_dotdot(handle_t * - if (len > 0) - de->rec_len = cpu_to_le16(len); - else -- assert(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2)); -+ 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); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ __EXT4_DIR_REC_LEN(2 + dlen)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2140,6 +2195,7 @@ static int ext4_add_entry(handle_t *hand ext4_lblk_t block, blocks; int csum_size = 0; @@ -717,7 +779,7 @@ Index: linux-4.15.0/fs/ext4/inline.c err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; diff --git a/ldiskfs/kernel_patches/patches/ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch b/ldiskfs/kernel_patches/patches/ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch deleted file mode 100644 index 7f0b0e5..0000000 --- a/ldiskfs/kernel_patches/patches/ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +++ /dev/null @@ -1,89 +0,0 @@ -Index: linux-4.15.0/fs/ext4/namei.c -=================================================================== ---- linux-4.15.0.orig/fs/ext4/namei.c -+++ linux-4.15.0/fs/ext4/namei.c -@@ -2043,6 +2043,74 @@ out_frames: - return retval; - } - -+/* update ".." for hash-indexed directory, split the item "." if necessary */ -+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; -+ -+ 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); -+ 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)); -+ } -+ -+ 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; -+ } -+ -+ 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); -+ -+out_journal: -+ if (journal) { -+ BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); -+ err = ext4_handle_dirty_dirent_node(handle, dir, dir_block); -+ ext4_mark_inode_dirty(handle, dir); -+ } -+ brelse(dir_block); -+ -+out: -+ return err; -+} -+ - /* - * ext4_add_entry() - * -@@ -2091,6 +2159,9 @@ static int ext4_add_entry(handle_t *hand - } - - 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; diff --git a/ldiskfs/kernel_patches/patches/ubuntu18/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/ubuntu18/ext4-pdirop.patch index e2351b6..90fb914 100644 --- a/ldiskfs/kernel_patches/patches/ubuntu18/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/ubuntu18/ext4-pdirop.patch @@ -757,9 +757,9 @@ Index: linux-4.15.0/fs/ext4/namei.c struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2222,9 +2560,10 @@ static int ext4_add_entry(handle_t *hand - if (dentry->d_name.len == 2 && - memcmp(dentry->d_name.name, "..", 2) == 0) - return ext4_update_dotdot(handle, dentry, inode); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/ubuntu20.04.5/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/ubuntu20.04.5/ext4-data-in-dirent.patch index 0430b27..674e91f 100644 --- a/ldiskfs/kernel_patches/patches/ubuntu20.04.5/ext4-data-in-dirent.patch +++ b/ldiskfs/kernel_patches/patches/ubuntu20.04.5/ext4-data-in-dirent.patch @@ -162,7 +162,7 @@ index 29db9e17..cf1c0732 100644 struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de); + struct ext4_dir_entry_2 **dest_de, -+ int *dlen); ++ int dlen); void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, @@ -266,7 +266,7 @@ index 6fe665de..e20e7894 100644 err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, - inline_size, fname, &de); -+ inline_size, fname, &de, NULL); ++ inline_size, fname, &de, 0); if (err) return err; @@ -485,31 +485,21 @@ index 25e84f0e..5c068e3a 100644 if (de > to) memmove(to, de, rec_len); to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); -@@ -2121,14 +2132,21 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2121,10 +2132,11 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, void *buf, int buf_size, struct ext4_filename *fname, - struct ext4_dir_entry_2 **dest_de) + struct ext4_dir_entry_2 **dest_de, -+ int *dlen) ++ int dlen) { struct ext4_dir_entry_2 *de; - unsigned short reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ unsigned short reclen; ++ unsigned short reclen = ext4_dir_rec_len(fname_len(fname) + dlen, dir); int nlen, rlen; unsigned int offset = 0; char *top; - -+ if (dlen) { -+ reclen = ext4_dir_rec_len(fname_len(fname) + *dlen, dir); -+ *dlen = 0; -+ } else { -+ reclen = ext4_dir_rec_len(fname_len(fname), dir); -+ } - de = (struct ext4_dir_entry_2 *)buf; - top = buf + buf_size - reclen; - while ((char *) de <= top) { -@@ -2137,10 +2155,31 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, +@@ -2137,7 +2155,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, return -EFSCORRUPTED; if (ext4_match(dir, fname, de)) return -EEXIST; @@ -518,30 +508,6 @@ index 25e84f0e..5c068e3a 100644 rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); if ((de->inode ? rlen - nlen : rlen) >= reclen) break; -+ -+ /* Then for dotdot entries, check for the smaller space -+ * required for just the entry, no FID -+ */ -+ if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) { -+ if ((de->inode ? rlen - nlen : rlen) >= -+ ext4_dir_rec_len(fname_len(fname), dir)) { -+ /* set dlen = 1 to indicate not -+ * enough space store fid -+ */ -+ if (dlen) -+ *dlen = 1; -+ break; -+ } -+ /* The new ".." entry must be written over the -+ * previous ".." entry, which is the first -+ * entry traversed by this scan. If it doesn't -+ * fit, something is badly wrong, so -EIO. -+ */ -+ return -EIO; -+ } - de = (struct ext4_dir_entry_2 *)((char *)de + rlen); - offset += rlen; - } @@ -2155,12 +2194,13 @@ void ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, @@ -589,18 +555,15 @@ index 25e84f0e..5c068e3a 100644 + dlen = (*data) + 1; err = ext4_find_dest_de(dir, inode, bh, bh->b_data, - blocksize - csum_size, fname, &de); -+ blocksize - csum_size, fname, &de, &dlen); ++ blocksize - csum_size, fname, &de, dlen); if (err) return err; } -@@ -2218,7 +2269,10 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, +@@ -2218,7 +2269,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, } /* By now the buffer is marked for journaling */ - ext4_insert_dentry(dir, inode, de, blocksize, fname); -+ /* If writing the short form of "dotdot", don't add the data section */ -+ if (dlen == 1) -+ data = NULL; + ext4_insert_dentry(dir, inode, de, blocksize, fname, data); /* @@ -624,7 +587,7 @@ index 25e84f0e..5c068e3a 100644 /* Initialize as for dx_probe */ fname->hinfo.hash_version = dx_info->hash_version; -@@ -2381,7 +2436,7 @@ out_frames: +@@ -2381,12 +2406,111 @@ out_frames: */ if (retval) ext4_mark_inode_dirty(handle, dir); @@ -633,66 +596,151 @@ index 25e84f0e..5c068e3a 100644 brelse(bh2); return retval; } -@@ -2394,6 +2449,8 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - struct buffer_head *dir_block; - struct ext4_dir_entry_2 *de; - int len, journal = 0, err = 0; + +-/* 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; + int dlen = 0; + char *data; if (IS_ERR(handle)) return PTR_ERR(handle); -@@ -2409,21 +2466,26 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - - de = (struct ext4_dir_entry_2 *)dir_block->b_data; - /* the first item must be "." */ -- assert(de->name_len == 1 && de->name[0] == '.'); -+ 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)) { -+ ASSERT(len >= EXT4_DIR_REC_LEN(1, dir)); -+ if (len > EXT4_DIR_REC_LEN(1, dir)) { - BUFFER_TRACE(dir_block, "get_write_access"); - err = ext4_journal_get_write_access(handle, dir->i_sb, dir_block, EXT4_JTR_NONE); - if (err) - goto out_journal; +@@ -2435,6 +2561,30 @@ static int ext4_update_dotdot(handle_t * - journal = 1; -- de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1)); -+ de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de, dir)); - } + dotdot_de->inode = cpu_to_le32(inode->i_ino); -- len -= EXT4_DIR_REC_LEN(1); -- assert(len == 0 || len >= EXT4_DIR_REC_LEN(2)); -+ len -= EXT4_DIR_ENTRY_LEN(de, NULL); + data = ext4_dentry_get_data(dir->i_sb, + (struct ext4_dentry_param *)dentry->d_fsdata); -+ if (data) ++ if (data != NULL) { + dlen = *data + 1; -+ ASSERT(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen, dir)); -+ - de = (struct ext4_dir_entry_2 *) - ((char *) de + le16_to_cpu(de->rec_len)); - if (!journal) { -@@ -2437,10 +2499,15 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry, - if (len > 0) - de->rec_len = cpu_to_le16(len); - else -- assert(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2)); -+ ASSERT(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2, dir)); - de->name_len = 2; - strcpy(de->name, ".."); -- ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) { -+ de->name[2] = 0; -+ memcpy(&de->name[2 + 1], data, *data); -+ ext4_set_de_type(dir->i_sb, de, S_IFDIR); -+ de->file_type |= EXT4_DIRENT_LUFID; ++ if (is_dx(dir)) { ++ if (ext4_get_dirent_data_len(dotdot_de) < dlen) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } else { ++ if (ext4_rec_len_from_disk(dotdot_de->rec_len, ++ dir->i_sb->s_blocksize) < ++ EXT4_DIR_REC_LEN(2 + dlen, NULL)) { ++ if (ext4_expand_dotdot(dir, bh, dlen) < 0) ++ dlen = 0; ++ } ++ } + } - - out_journal: - if (journal) { ++ if (dlen) { ++ dotdot_de->name[2] = 0; ++ memcpy(&dotdot_de->name[2 + 1], data, *data); ++ dotdot_de->file_type |= LDISKFS_DIRENT_LUFID; ++ } ++ + ext4_mark_inode_dirty(handle, dir); + BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); + if (is_dx(dir)) { @@ -2478,6 +2545,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, ext4_lblk_t block, blocks; int csum_size = 0; diff --git a/ldiskfs/kernel_patches/patches/ubuntu20/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/ubuntu20/ext4-pdirop.patch index 4a9b87e..73eca35 100644 --- a/ldiskfs/kernel_patches/patches/ubuntu20/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/ubuntu20/ext4-pdirop.patch @@ -796,9 +796,9 @@ diff -wur a/fs/ext4/namei.c b/fs/ext4/namei.c struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2362,9 +2701,10 @@ - if (dentry->d_name.len == 2 && - memcmp(dentry->d_name.name, "..", 2) == 0) - return ext4_update_dotdot(handle, dentry, inode); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/patches/ubuntu2004/ext4-pdirop.patch b/ldiskfs/kernel_patches/patches/ubuntu2004/ext4-pdirop.patch index 13c3608..d12b2b3 100644 --- a/ldiskfs/kernel_patches/patches/ubuntu2004/ext4-pdirop.patch +++ b/ldiskfs/kernel_patches/patches/ubuntu2004/ext4-pdirop.patch @@ -797,9 +797,9 @@ diff -wur a/fs/ext4/namei.c b/fs/ext4/namei.c struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; @@ -2362,9 +2701,10 @@ - if (dentry->d_name.len == 2 && - memcmp(dentry->d_name.name, "..", 2) == 0) - return ext4_update_dotdot(handle, dentry, inode); + return ext4_update_dotdot(handle, dentry, inode); + + if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, &fname, dir, inode); + retval = ext4_dx_add_entry(handle, &fname, dir, inode, lck); if (!retval || (retval != ERR_BAD_DX_DIR)) diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15-22.series b/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15-22.series index e8a8420..41e4762 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15-22.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15-22.series @@ -5,7 +5,7 @@ suse15/ext4-prealloc.patch suse15/ext4-osd-iop-common.patch suse15/ext4-misc.patch suse15/ext4-mballoc-extra-checks.patch -ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch suse15/ext4-kill-dx-root.patch rhel7.6/ext4-mballoc-pa-free-mismatch.patch linux-5.4/ext4-data-in-dirent.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15.series b/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15.series index c731d36..0a4eb11 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15.series @@ -5,7 +5,7 @@ suse15/ext4-prealloc.patch suse15/ext4-osd-iop-common.patch suse15/ext4-misc.patch suse15/ext4-mballoc-extra-checks.patch -ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch suse15/ext4-kill-dx-root.patch rhel7.6/ext4-mballoc-pa-free-mismatch.patch linux-5.4/ext4-data-in-dirent.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15sp1-7.series b/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15sp1-7.series index a497b84..0a94c42 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15sp1-7.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15sp1-7.series @@ -5,7 +5,7 @@ suse15/ext4-prealloc.patch suse15/ext4-osd-iop-common.patch suse15/ext4-misc.patch suse15/ext4-mballoc-extra-checks.patch -ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch suse15/ext4-kill-dx-root.patch rhel7.6/ext4-mballoc-pa-free-mismatch.patch linux-5.4/ext4-data-in-dirent.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15sp1.series b/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15sp1.series index 124d2cc..41981bf 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15sp1.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.12-sles15sp1.series @@ -5,7 +5,7 @@ suse15/ext4-prealloc.patch suse15/ext4-osd-iop-common.patch sles15sp1/ext4-misc.patch suse15/ext4-mballoc-extra-checks.patch -ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch suse15/ext4-kill-dx-root.patch rhel7.6/ext4-mballoc-pa-free-mismatch.patch linux-5.4/ext4-data-in-dirent.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.15.0-20-ubuntu18.series b/ldiskfs/kernel_patches/series/ldiskfs-4.15.0-20-ubuntu18.series index 0d4b524..ed6e4fb 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.15.0-20-ubuntu18.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.15.0-20-ubuntu18.series @@ -5,7 +5,7 @@ suse15/ext4-prealloc.patch ubuntu18/ext4-osd-iop-common.patch ubuntu18/ext4-misc.patch ubuntu18/ext4-mballoc-extra-checks.patch -ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch ubuntu18/ext4-kill-dx-root.patch rhel7.6/ext4-mballoc-pa-free-mismatch.patch ubuntu18/ext4-data-in-dirent.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.15.0-24-ubuntu18.series b/ldiskfs/kernel_patches/series/ldiskfs-4.15.0-24-ubuntu18.series index 6a19aa3..e0a935f 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.15.0-24-ubuntu18.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.15.0-24-ubuntu18.series @@ -5,7 +5,7 @@ suse15/ext4-prealloc.patch ubuntu18/ext4-osd-iop-common.patch ubuntu18/ext4-misc.patch ubuntu18/ext4-mballoc-extra-checks.patch -ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch ubuntu18/ext4-kill-dx-root.patch rhel7.6/ext4-mballoc-pa-free-mismatch.patch ubuntu18/ext4-data-in-dirent.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.4.series b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.4.series index 20172f5..bf2aed1 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.4.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.4.series @@ -5,7 +5,7 @@ rhel8/ext4-prealloc.patch ubuntu18/ext4-osd-iop-common.patch rhel8.3/ext4-misc.patch rhel8.3/ext4-mballoc-extra-checks.patch -ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch rhel8.1/ext4-kill-dx-root.patch rhel7.6/ext4-mballoc-pa-free-mismatch.patch rhel8.4/ext4-data-in-dirent.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.5.series b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.5.series index 260b1a9..f228bb1 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.5.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.5.series @@ -5,7 +5,7 @@ rhel8/ext4-prealloc.patch ubuntu18/ext4-osd-iop-common.patch rhel8.3/ext4-misc.patch rhel8.3/ext4-mballoc-extra-checks.patch -ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch rhel8.1/ext4-kill-dx-root.patch rhel7.6/ext4-mballoc-pa-free-mismatch.patch rhel8.4/ext4-data-in-dirent.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.6.series b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.6.series index 47c579e..fd78795 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.6.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.6.series @@ -5,7 +5,7 @@ rhel8/ext4-prealloc.patch ubuntu18/ext4-osd-iop-common.patch rhel8.3/ext4-misc.patch rhel8.3/ext4-mballoc-extra-checks.patch -ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch rhel8.1/ext4-kill-dx-root.patch rhel7.6/ext4-mballoc-pa-free-mismatch.patch rhel8.4/ext4-data-in-dirent.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.7.series b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.7.series index 6630dd5..e07994d 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.7.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.7.series @@ -5,7 +5,7 @@ rhel8/ext4-prealloc.patch ubuntu18/ext4-osd-iop-common.patch rhel8.7/ext4-misc.patch rhel8.7/ext4-mballoc-extra-checks.patch -ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +rhel8.7/ext4-hash-indexed-dir-dotdot-update.patch rhel8.1/ext4-kill-dx-root.patch rhel8.7/ext4-mballoc-pa-free-mismatch.patch rhel8.4/ext4-data-in-dirent.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.8.series b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.8.series index 6630dd5..e07994d 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.8.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.8.series @@ -5,7 +5,7 @@ rhel8/ext4-prealloc.patch ubuntu18/ext4-osd-iop-common.patch rhel8.7/ext4-misc.patch rhel8.7/ext4-mballoc-extra-checks.patch -ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +rhel8.7/ext4-hash-indexed-dir-dotdot-update.patch rhel8.1/ext4-kill-dx-root.patch rhel8.7/ext4-mballoc-pa-free-mismatch.patch rhel8.4/ext4-data-in-dirent.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.9.series b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.9.series index 6630dd5..e07994d 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.9.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.9.series @@ -5,7 +5,7 @@ rhel8/ext4-prealloc.patch ubuntu18/ext4-osd-iop-common.patch rhel8.7/ext4-misc.patch rhel8.7/ext4-mballoc-extra-checks.patch -ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +rhel8.7/ext4-hash-indexed-dir-dotdot-update.patch rhel8.1/ext4-kill-dx-root.patch rhel8.7/ext4-mballoc-pa-free-mismatch.patch rhel8.4/ext4-data-in-dirent.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-5.0.0-13-ubuntu19.series b/ldiskfs/kernel_patches/series/ldiskfs-5.0.0-13-ubuntu19.series index 78ce11d..8265f95 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-5.0.0-13-ubuntu19.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-5.0.0-13-ubuntu19.series @@ -5,7 +5,7 @@ rhel8/ext4-prealloc.patch ubuntu18/ext4-osd-iop-common.patch ubuntu19/ext4-misc.patch rhel8/ext4-mballoc-extra-checks.patch -ubuntu18/ext4-hash-indexed-dir-dotdot-update.patch +rhel7.6/ext4-hash-indexed-dir-dotdot-update.patch ubuntu18/ext4-kill-dx-root.patch rhel7.6/ext4-mballoc-pa-free-mismatch.patch ubuntu18/ext4-data-in-dirent.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-5.14-rhel9.1.series b/ldiskfs/kernel_patches/series/ldiskfs-5.14-rhel9.1.series index 4f9a877..e06b52b 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-5.14-rhel9.1.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-5.14-rhel9.1.series @@ -8,7 +8,7 @@ linux-5.18/ext4-mballoc-extra-checks.patch sles15sp4/ext4-hash-indexed-dir-dotdot-update.patch linux-5.14/ext4-kill-dx-root.patch linux-5.18/ext4-mballoc-pa-free-mismatch.patch -linux-5.18/ext4-data-in-dirent.patch +rhel9.1/ext4-data-in-dirent.patch rhel8/ext4-nocmtime.patch base/ext4-htree-lock.patch rhel9.1/ext4-pdirop.patch diff --git a/lustre/osd-ldiskfs/osd_handler.c b/lustre/osd-ldiskfs/osd_handler.c index a292a4c..be48ac6 100644 --- a/lustre/osd-ldiskfs/osd_handler.c +++ b/lustre/osd-ldiskfs/osd_handler.c @@ -5806,9 +5806,10 @@ static int osd_index_ea_delete(const struct lu_env *env, struct dt_object *dt, * the entry after the agent had been removed, or leave a * dangling entry pointing at a random inode. */ - if (strcmp((char *)key, dotdot) != 0) + if (strcmp((char *)key, dotdot) != 0) { osd_take_care_of_agent(env, osd, oh, de); - rc = ldiskfs_delete_entry(oh->ot_handle, dir, de, bh); + rc = ldiskfs_delete_entry(oh->ot_handle, dir, de, bh); + } brelse(bh); } else { rc = PTR_ERR(bh); diff --git a/lustre/tests/conf-sanity.sh b/lustre/tests/conf-sanity.sh index 2d42f25..8b5c432 100755 --- a/lustre/tests/conf-sanity.sh +++ b/lustre/tests/conf-sanity.sh @@ -11187,6 +11187,111 @@ test_153a() { } run_test 153a "bypass invalid NIDs quickly" +test_154() { + [ "$mds1_FSTYPE" == "ldiskfs" ] || skip "ldiskfs only test" + (( $MDS1_VERSION >= $(version_code 2.15.63.1) )) || + skip "Need MDS version at least 2.15.63.1" + local mdt1=$(mdsdevname 1) + local cmd + local parentfid + local dotdotfid + local ino + + reformat + setupall + + # create rename test dir on MDT0000 to simplify later debugfs checks + test_mkdir -c 1 -i 0 $DIR/$tdir || error "mkdir $tdir failed" + test_mkdir -c 1 -i 0 $DIR/$tdir.tgt || error "mkdir $tdir.tgt failed" + + for name in dx_dir dirent empty; do + test_mkdir -c 1 -i 0 $DIR/$tdir.$name || + error "mkdir $tdir.$name failed" + done + + # make this directory large enough to htree split + createmany -o $DIR/$tdir.dx_dir/$tfile.longfilename 128 + # put 2 entries after dotdot in this directory + createmany -o $DIR/$tdir.dirent/$tfile.longfilename 2 + + for name in dx_dir dirent empty; do + mv $DIR/$tdir.$name $DIR/$tdir || error "mv $tdir.$name failed" + done + + parentfid="fid:$($LFS path2fid $DIR/$tdir)" + echo "==target parent FID: $parentfid==" + + stopall + + # check that ".." FID is updated during normal operation + echo "==debugfs before backup==" + for name in dx_dir dirent empty; do + cmd="debugfs -c -R 'stat ROOT/$tdir/$tdir.$name' $mdt1" + do_facet mds1 "$cmd" || + error "debugfs stat before backup failed" + cmd="debugfs -c -R 'ls -lD ROOT/$tdir/$tdir.$name' $mdt1" + do_facet mds1 "$cmd" || + error "debugfs before backup $name failed" + dotdotfid=$(do_facet mds1 "$cmd" |& awk '/fid:.+\.\./ { print $9 }') + [[ "$dotdotfid" == "$parentfid" ]] || + error "parent '$parentfid' != dotdot '$dotdotfid' on $name" + done + + for ((i = 1; i <= $MDSCOUNT; i++ )); do + mds_backup_restore mds$i || + error "Backup/restore on mds$i failed" + done + + setupall + + # verify that rename of the restored directory updates the + # ".." entry in the directory with the parent FID + echo "==pre-rename parent/.. inodes==" + for name in dx_dir dirent empty; do + ls -dial $DIR/$tdir $DIR/$tdir/$tdir.$name/.. || + error "ls on original dirs failed" + ino=($(stat -c "%i" $DIR/$tdir $DIR/$tdir/$tdir.$name/..)) + (( ${ino[0]} == ${ino[1]} )) || + error "ino $DIR/$tdir ${ino[0]} != $tdir.$name/.. ${ino[1]}" + done + + for name in dx_dir dirent empty; do + mv $DIR/$tdir/$tdir.$name $DIR/$tdir.tgt || + error "mv $tdir.$name failed" + done + + echo "==post-rename parent/.. inodes==" + for name in dx_dir dirent empty; do + ls -dial $DIR/$tdir.tgt $DIR/$tdir.tgt/$tdir.$name/.. || + error "ls on renamed dirs failed" + ino=($(stat -c "%i" $DIR/$tdir.tgt $DIR/$tdir.tgt/$tdir.$name/..)) + (( ${ino[0]} == ${ino[1]} )) || + error "ino $DIR/$tdir.tgt ${ino[0]} != $tdir.$name/.. ${ino[1]}" + done + + parentfid="fid:$($LFS path2fid $DIR/$tdir.tgt)" + echo "==target parent FID: $parentfid==" + + stopall + + for name in dx_dir dirent empty; do + echo "==post-rename .$name should have '$parentfid ..'==" + cmd="debugfs -c -R 'stat ROOT/$tdir.tgt/$tdir.$name' $mdt1" + do_facet mds1 "$cmd" || + error "debugfs stat after rename failed" + cmd="debugfs -c -R 'ls -lD ROOT/$tdir.tgt/$tdir.$name' $mdt1" + do_facet mds1 "$cmd" || + error "debugfs ls .$name after rename failed" + dotdotfid=$(do_facet mds1 "$cmd" |& awk '/fid:.+\.\./ { print $9 }') + [[ "$dotdotfid" == "$parentfid" ]] || + error "parent '$parentfid' != dotdot '$dotdotfid' on $name" + done + + FSCK_MAX_ERR=0 run_e2fsck $(facet_active_host mds1) $mdt1 -n || + error "e2fsck returned $?" +} +run_test 154 "expand .. on rename after MDT backup restore" + # # (This was sanity/802a) # diff --git a/lustre/tests/test-framework.sh b/lustre/tests/test-framework.sh index 655f06d..44d92cf 100755 --- a/lustre/tests/test-framework.sh +++ b/lustre/tests/test-framework.sh @@ -6929,13 +6929,13 @@ debugsave() { debugrestore() { [ -n "$DEBUGSAVE" ] && - do_nodes $CLIENTS "$LCTL set_param debug=\\\"${DEBUGSAVE}\\\""|| + do_nodes $CLIENTS $LCTL set_param -n debug=${DEBUGSAVE// /+} || true DEBUGSAVE="" [ -n "$DEBUGSAVE_SERVER" ] && do_nodes $(comma_list $(all_server_nodes)) \ - "$LCTL set_param debug=\\\"${DEBUGSAVE_SERVER}\\\"" || + $LCTL set_param -n debug=${DEBUGSAVE_SERVER// /+} || true DEBUGSAVE_SERVER="" }