diff options
author | Tao Ma <boyu.mt@taobao.com> | 2012-12-10 14:06:01 -0500 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2012-12-10 14:06:01 -0500 |
commit | 32f7f22c0b52e8189fef83986b16dc7abe95f2c4 (patch) | |
tree | c11441065aa244746f17bf0efd322fe6dd06c04c /fs | |
parent | 61f86638d8a656101bb0f9c41c55d9685f8a2357 (diff) |
ext4: let ext4_rename handle inline dir
In case we rename a directory, ext4_rename has to read the dir block
and change its dotdot's information. The old ext4_rename encapsulated
the dir_block read into itself. So this patch adds a new function
ext4_get_first_dir_block() which gets the dir buffer information so
the ext4_rename can handle it properly. As it will also change the
parent inode number, we return the parent_de so that ext4_rename() can
handle it more easily.
ext4_find_entry is also changed so that the caller(rename) can tell
whether the found entry is an inlined one or not and journaling the
corresponding buffer head.
Signed-off-by: Tao Ma <boyu.mt@taobao.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/inline.c | 15 | ||||
-rw-r--r-- | fs/ext4/namei.c | 109 | ||||
-rw-r--r-- | fs/ext4/xattr.h | 11 |
3 files changed, 100 insertions, 35 deletions
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index e5da458faba..fc362998092 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c | |||
@@ -1424,6 +1424,21 @@ out: | |||
1424 | return ret; | 1424 | return ret; |
1425 | } | 1425 | } |
1426 | 1426 | ||
1427 | struct buffer_head *ext4_get_first_inline_block(struct inode *inode, | ||
1428 | struct ext4_dir_entry_2 **parent_de, | ||
1429 | int *retval) | ||
1430 | { | ||
1431 | struct ext4_iloc iloc; | ||
1432 | |||
1433 | *retval = ext4_get_inode_loc(inode, &iloc); | ||
1434 | if (*retval) | ||
1435 | return NULL; | ||
1436 | |||
1437 | *parent_de = (struct ext4_dir_entry_2 *)ext4_raw_inode(&iloc)->i_block; | ||
1438 | |||
1439 | return iloc.bh; | ||
1440 | } | ||
1441 | |||
1427 | /* | 1442 | /* |
1428 | * Try to create the inline data for the new dir. | 1443 | * Try to create the inline data for the new dir. |
1429 | * If it succeeds, return 0, otherwise return the error. | 1444 | * If it succeeds, return 0, otherwise return the error. |
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index e3e20d0aa29..b37c2183983 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c | |||
@@ -1176,7 +1176,8 @@ static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block, | |||
1176 | */ | 1176 | */ |
1177 | static struct buffer_head * ext4_find_entry (struct inode *dir, | 1177 | static struct buffer_head * ext4_find_entry (struct inode *dir, |
1178 | const struct qstr *d_name, | 1178 | const struct qstr *d_name, |
1179 | struct ext4_dir_entry_2 ** res_dir) | 1179 | struct ext4_dir_entry_2 **res_dir, |
1180 | int *inlined) | ||
1180 | { | 1181 | { |
1181 | struct super_block *sb; | 1182 | struct super_block *sb; |
1182 | struct buffer_head *bh_use[NAMEI_RA_SIZE]; | 1183 | struct buffer_head *bh_use[NAMEI_RA_SIZE]; |
@@ -1202,8 +1203,11 @@ static struct buffer_head * ext4_find_entry (struct inode *dir, | |||
1202 | int has_inline_data = 1; | 1203 | int has_inline_data = 1; |
1203 | ret = ext4_find_inline_entry(dir, d_name, res_dir, | 1204 | ret = ext4_find_inline_entry(dir, d_name, res_dir, |
1204 | &has_inline_data); | 1205 | &has_inline_data); |
1205 | if (has_inline_data) | 1206 | if (has_inline_data) { |
1207 | if (inlined) | ||
1208 | *inlined = 1; | ||
1206 | return ret; | 1209 | return ret; |
1210 | } | ||
1207 | } | 1211 | } |
1208 | 1212 | ||
1209 | if ((namelen <= 2) && (name[0] == '.') && | 1213 | if ((namelen <= 2) && (name[0] == '.') && |
@@ -1390,7 +1394,7 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi | |||
1390 | if (dentry->d_name.len > EXT4_NAME_LEN) | 1394 | if (dentry->d_name.len > EXT4_NAME_LEN) |
1391 | return ERR_PTR(-ENAMETOOLONG); | 1395 | return ERR_PTR(-ENAMETOOLONG); |
1392 | 1396 | ||
1393 | bh = ext4_find_entry(dir, &dentry->d_name, &de); | 1397 | bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL); |
1394 | inode = NULL; | 1398 | inode = NULL; |
1395 | if (bh) { | 1399 | if (bh) { |
1396 | __u32 ino = le32_to_cpu(de->inode); | 1400 | __u32 ino = le32_to_cpu(de->inode); |
@@ -1424,7 +1428,7 @@ struct dentry *ext4_get_parent(struct dentry *child) | |||
1424 | struct ext4_dir_entry_2 * de; | 1428 | struct ext4_dir_entry_2 * de; |
1425 | struct buffer_head *bh; | 1429 | struct buffer_head *bh; |
1426 | 1430 | ||
1427 | bh = ext4_find_entry(child->d_inode, &dotdot, &de); | 1431 | bh = ext4_find_entry(child->d_inode, &dotdot, &de, NULL); |
1428 | if (!bh) | 1432 | if (!bh) |
1429 | return ERR_PTR(-ENOENT); | 1433 | return ERR_PTR(-ENOENT); |
1430 | ino = le32_to_cpu(de->inode); | 1434 | ino = le32_to_cpu(de->inode); |
@@ -2725,7 +2729,7 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry) | |||
2725 | return PTR_ERR(handle); | 2729 | return PTR_ERR(handle); |
2726 | 2730 | ||
2727 | retval = -ENOENT; | 2731 | retval = -ENOENT; |
2728 | bh = ext4_find_entry(dir, &dentry->d_name, &de); | 2732 | bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL); |
2729 | if (!bh) | 2733 | if (!bh) |
2730 | goto end_rmdir; | 2734 | goto end_rmdir; |
2731 | 2735 | ||
@@ -2790,7 +2794,7 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry) | |||
2790 | ext4_handle_sync(handle); | 2794 | ext4_handle_sync(handle); |
2791 | 2795 | ||
2792 | retval = -ENOENT; | 2796 | retval = -ENOENT; |
2793 | bh = ext4_find_entry(dir, &dentry->d_name, &de); | 2797 | bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL); |
2794 | if (!bh) | 2798 | if (!bh) |
2795 | goto end_unlink; | 2799 | goto end_unlink; |
2796 | 2800 | ||
@@ -2972,8 +2976,39 @@ retry: | |||
2972 | return err; | 2976 | return err; |
2973 | } | 2977 | } |
2974 | 2978 | ||
2975 | #define PARENT_INO(buffer, size) \ | 2979 | |
2976 | (ext4_next_entry((struct ext4_dir_entry_2 *)(buffer), size)->inode) | 2980 | /* |
2981 | * Try to find buffer head where contains the parent block. | ||
2982 | * It should be the inode block if it is inlined or the 1st block | ||
2983 | * if it is a normal dir. | ||
2984 | */ | ||
2985 | static struct buffer_head *ext4_get_first_dir_block(handle_t *handle, | ||
2986 | struct inode *inode, | ||
2987 | int *retval, | ||
2988 | struct ext4_dir_entry_2 **parent_de, | ||
2989 | int *inlined) | ||
2990 | { | ||
2991 | struct buffer_head *bh; | ||
2992 | |||
2993 | if (!ext4_has_inline_data(inode)) { | ||
2994 | if (!(bh = ext4_bread(handle, inode, 0, 0, retval))) { | ||
2995 | if (!*retval) { | ||
2996 | *retval = -EIO; | ||
2997 | ext4_error(inode->i_sb, | ||
2998 | "Directory hole detected on inode %lu\n", | ||
2999 | inode->i_ino); | ||
3000 | } | ||
3001 | return NULL; | ||
3002 | } | ||
3003 | *parent_de = ext4_next_entry( | ||
3004 | (struct ext4_dir_entry_2 *)bh->b_data, | ||
3005 | inode->i_sb->s_blocksize); | ||
3006 | return bh; | ||
3007 | } | ||
3008 | |||
3009 | *inlined = 1; | ||
3010 | return ext4_get_first_inline_block(inode, parent_de, retval); | ||
3011 | } | ||
2977 | 3012 | ||
2978 | /* | 3013 | /* |
2979 | * Anybody can rename anything with this: the permission checks are left to the | 3014 | * Anybody can rename anything with this: the permission checks are left to the |
@@ -2987,6 +3022,8 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
2987 | struct buffer_head *old_bh, *new_bh, *dir_bh; | 3022 | struct buffer_head *old_bh, *new_bh, *dir_bh; |
2988 | struct ext4_dir_entry_2 *old_de, *new_de; | 3023 | struct ext4_dir_entry_2 *old_de, *new_de; |
2989 | int retval, force_da_alloc = 0; | 3024 | int retval, force_da_alloc = 0; |
3025 | int inlined = 0, new_inlined = 0; | ||
3026 | struct ext4_dir_entry_2 *parent_de; | ||
2990 | 3027 | ||
2991 | dquot_initialize(old_dir); | 3028 | dquot_initialize(old_dir); |
2992 | dquot_initialize(new_dir); | 3029 | dquot_initialize(new_dir); |
@@ -3006,7 +3043,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
3006 | if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) | 3043 | if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) |
3007 | ext4_handle_sync(handle); | 3044 | ext4_handle_sync(handle); |
3008 | 3045 | ||
3009 | old_bh = ext4_find_entry(old_dir, &old_dentry->d_name, &old_de); | 3046 | old_bh = ext4_find_entry(old_dir, &old_dentry->d_name, &old_de, NULL); |
3010 | /* | 3047 | /* |
3011 | * Check for inode number is _not_ due to possible IO errors. | 3048 | * Check for inode number is _not_ due to possible IO errors. |
3012 | * We might rmdir the source, keep it as pwd of some process | 3049 | * We might rmdir the source, keep it as pwd of some process |
@@ -3019,7 +3056,8 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
3019 | goto end_rename; | 3056 | goto end_rename; |
3020 | 3057 | ||
3021 | new_inode = new_dentry->d_inode; | 3058 | new_inode = new_dentry->d_inode; |
3022 | new_bh = ext4_find_entry(new_dir, &new_dentry->d_name, &new_de); | 3059 | new_bh = ext4_find_entry(new_dir, &new_dentry->d_name, |
3060 | &new_de, &new_inlined); | ||
3023 | if (new_bh) { | 3061 | if (new_bh) { |
3024 | if (!new_inode) { | 3062 | if (!new_inode) { |
3025 | brelse(new_bh); | 3063 | brelse(new_bh); |
@@ -3033,22 +3071,17 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
3033 | goto end_rename; | 3071 | goto end_rename; |
3034 | } | 3072 | } |
3035 | retval = -EIO; | 3073 | retval = -EIO; |
3036 | if (!(dir_bh = ext4_bread(handle, old_inode, 0, 0, &retval))) { | 3074 | dir_bh = ext4_get_first_dir_block(handle, old_inode, |
3037 | if (!retval) { | 3075 | &retval, &parent_de, |
3038 | retval = -EIO; | 3076 | &inlined); |
3039 | ext4_error(old_inode->i_sb, | 3077 | if (!dir_bh) |
3040 | "Directory hole detected on inode %lu\n", | ||
3041 | old_inode->i_ino); | ||
3042 | } | ||
3043 | goto end_rename; | 3078 | goto end_rename; |
3044 | } | 3079 | if (!inlined && !buffer_verified(dir_bh) && |
3045 | if (!buffer_verified(dir_bh) && | ||
3046 | !ext4_dirent_csum_verify(old_inode, | 3080 | !ext4_dirent_csum_verify(old_inode, |
3047 | (struct ext4_dir_entry *)dir_bh->b_data)) | 3081 | (struct ext4_dir_entry *)dir_bh->b_data)) |
3048 | goto end_rename; | 3082 | goto end_rename; |
3049 | set_buffer_verified(dir_bh); | 3083 | set_buffer_verified(dir_bh); |
3050 | if (le32_to_cpu(PARENT_INO(dir_bh->b_data, | 3084 | if (le32_to_cpu(parent_de->inode) != old_dir->i_ino) |
3051 | old_dir->i_sb->s_blocksize)) != old_dir->i_ino) | ||
3052 | goto end_rename; | 3085 | goto end_rename; |
3053 | retval = -EMLINK; | 3086 | retval = -EMLINK; |
3054 | if (!new_inode && new_dir != old_dir && | 3087 | if (!new_inode && new_dir != old_dir && |
@@ -3077,10 +3110,13 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
3077 | ext4_current_time(new_dir); | 3110 | ext4_current_time(new_dir); |
3078 | ext4_mark_inode_dirty(handle, new_dir); | 3111 | ext4_mark_inode_dirty(handle, new_dir); |
3079 | BUFFER_TRACE(new_bh, "call ext4_handle_dirty_metadata"); | 3112 | BUFFER_TRACE(new_bh, "call ext4_handle_dirty_metadata"); |
3080 | retval = ext4_handle_dirty_dirent_node(handle, new_dir, new_bh); | 3113 | if (!new_inlined) { |
3081 | if (unlikely(retval)) { | 3114 | retval = ext4_handle_dirty_dirent_node(handle, |
3082 | ext4_std_error(new_dir->i_sb, retval); | 3115 | new_dir, new_bh); |
3083 | goto end_rename; | 3116 | if (unlikely(retval)) { |
3117 | ext4_std_error(new_dir->i_sb, retval); | ||
3118 | goto end_rename; | ||
3119 | } | ||
3084 | } | 3120 | } |
3085 | brelse(new_bh); | 3121 | brelse(new_bh); |
3086 | new_bh = NULL; | 3122 | new_bh = NULL; |
@@ -3108,7 +3144,8 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
3108 | struct buffer_head *old_bh2; | 3144 | struct buffer_head *old_bh2; |
3109 | struct ext4_dir_entry_2 *old_de2; | 3145 | struct ext4_dir_entry_2 *old_de2; |
3110 | 3146 | ||
3111 | old_bh2 = ext4_find_entry(old_dir, &old_dentry->d_name, &old_de2); | 3147 | old_bh2 = ext4_find_entry(old_dir, &old_dentry->d_name, |
3148 | &old_de2, NULL); | ||
3112 | if (old_bh2) { | 3149 | if (old_bh2) { |
3113 | retval = ext4_delete_entry(handle, old_dir, | 3150 | retval = ext4_delete_entry(handle, old_dir, |
3114 | old_de2, old_bh2); | 3151 | old_de2, old_bh2); |
@@ -3128,17 +3165,19 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
3128 | old_dir->i_ctime = old_dir->i_mtime = ext4_current_time(old_dir); | 3165 | old_dir->i_ctime = old_dir->i_mtime = ext4_current_time(old_dir); |
3129 | ext4_update_dx_flag(old_dir); | 3166 | ext4_update_dx_flag(old_dir); |
3130 | if (dir_bh) { | 3167 | if (dir_bh) { |
3131 | PARENT_INO(dir_bh->b_data, new_dir->i_sb->s_blocksize) = | 3168 | parent_de->inode = cpu_to_le32(new_dir->i_ino); |
3132 | cpu_to_le32(new_dir->i_ino); | ||
3133 | BUFFER_TRACE(dir_bh, "call ext4_handle_dirty_metadata"); | 3169 | BUFFER_TRACE(dir_bh, "call ext4_handle_dirty_metadata"); |
3134 | if (is_dx(old_inode)) { | 3170 | if (!inlined) { |
3135 | retval = ext4_handle_dirty_dx_node(handle, | 3171 | if (is_dx(old_inode)) { |
3136 | old_inode, | 3172 | retval = ext4_handle_dirty_dx_node(handle, |
3137 | dir_bh); | 3173 | old_inode, |
3174 | dir_bh); | ||
3175 | } else { | ||
3176 | retval = ext4_handle_dirty_dirent_node(handle, | ||
3177 | old_inode, dir_bh); | ||
3178 | } | ||
3138 | } else { | 3179 | } else { |
3139 | retval = ext4_handle_dirty_dirent_node(handle, | 3180 | retval = ext4_mark_inode_dirty(handle, old_inode); |
3140 | old_inode, | ||
3141 | dir_bh); | ||
3142 | } | 3181 | } |
3143 | if (retval) { | 3182 | if (retval) { |
3144 | ext4_std_error(old_dir->i_sb, retval); | 3183 | ext4_std_error(old_dir->i_sb, retval); |
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index 7747bbcebb3..f6c3ca6dae4 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h | |||
@@ -181,6 +181,9 @@ extern int ext4_delete_inline_entry(handle_t *handle, | |||
181 | struct buffer_head *bh, | 181 | struct buffer_head *bh, |
182 | int *has_inline_data); | 182 | int *has_inline_data); |
183 | extern int empty_inline_dir(struct inode *dir, int *has_inline_data); | 183 | extern int empty_inline_dir(struct inode *dir, int *has_inline_data); |
184 | extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode, | ||
185 | struct ext4_dir_entry_2 **parent_de, | ||
186 | int *retval); | ||
184 | # else /* CONFIG_EXT4_FS_XATTR */ | 187 | # else /* CONFIG_EXT4_FS_XATTR */ |
185 | 188 | ||
186 | static inline int | 189 | static inline int |
@@ -387,6 +390,14 @@ static inline int empty_inline_dir(struct inode *dir, int *has_inline_data) | |||
387 | { | 390 | { |
388 | return 0; | 391 | return 0; |
389 | } | 392 | } |
393 | |||
394 | static inline struct buffer_head * | ||
395 | ext4_get_first_inline_block(struct inode *inode, | ||
396 | struct ext4_dir_entry_2 **parent_de, | ||
397 | int *retval) | ||
398 | { | ||
399 | return NULL; | ||
400 | } | ||
390 | # endif /* CONFIG_EXT4_FS_XATTR */ | 401 | # endif /* CONFIG_EXT4_FS_XATTR */ |
391 | 402 | ||
392 | #ifdef CONFIG_EXT4_FS_SECURITY | 403 | #ifdef CONFIG_EXT4_FS_SECURITY |