diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/inode.c | 63 | ||||
-rw-r--r-- | fs/ext4/xattr.c | 274 | ||||
-rw-r--r-- | fs/ext4/xattr.h | 17 |
3 files changed, 346 insertions, 8 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index b83f91edebd1..f6d8528c4f55 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -3106,6 +3106,39 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode, | |||
3106 | } | 3106 | } |
3107 | 3107 | ||
3108 | /* | 3108 | /* |
3109 | * Expand an inode by new_extra_isize bytes. | ||
3110 | * Returns 0 on success or negative error number on failure. | ||
3111 | */ | ||
3112 | int ext4_expand_extra_isize(struct inode *inode, unsigned int new_extra_isize, | ||
3113 | struct ext4_iloc iloc, handle_t *handle) | ||
3114 | { | ||
3115 | struct ext4_inode *raw_inode; | ||
3116 | struct ext4_xattr_ibody_header *header; | ||
3117 | struct ext4_xattr_entry *entry; | ||
3118 | |||
3119 | if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) | ||
3120 | return 0; | ||
3121 | |||
3122 | raw_inode = ext4_raw_inode(&iloc); | ||
3123 | |||
3124 | header = IHDR(inode, raw_inode); | ||
3125 | entry = IFIRST(header); | ||
3126 | |||
3127 | /* No extended attributes present */ | ||
3128 | if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR) || | ||
3129 | header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) { | ||
3130 | memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE, 0, | ||
3131 | new_extra_isize); | ||
3132 | EXT4_I(inode)->i_extra_isize = new_extra_isize; | ||
3133 | return 0; | ||
3134 | } | ||
3135 | |||
3136 | /* try to expand with EAs present */ | ||
3137 | return ext4_expand_extra_isize_ea(inode, new_extra_isize, | ||
3138 | raw_inode, handle); | ||
3139 | } | ||
3140 | |||
3141 | /* | ||
3109 | * What we do here is to mark the in-core inode as clean with respect to inode | 3142 | * What we do here is to mark the in-core inode as clean with respect to inode |
3110 | * dirtiness (it may still be data-dirty). | 3143 | * dirtiness (it may still be data-dirty). |
3111 | * This means that the in-core inode may be reaped by prune_icache | 3144 | * This means that the in-core inode may be reaped by prune_icache |
@@ -3129,10 +3162,38 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode, | |||
3129 | int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode) | 3162 | int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode) |
3130 | { | 3163 | { |
3131 | struct ext4_iloc iloc; | 3164 | struct ext4_iloc iloc; |
3132 | int err; | 3165 | struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); |
3166 | static unsigned int mnt_count; | ||
3167 | int err, ret; | ||
3133 | 3168 | ||
3134 | might_sleep(); | 3169 | might_sleep(); |
3135 | err = ext4_reserve_inode_write(handle, inode, &iloc); | 3170 | err = ext4_reserve_inode_write(handle, inode, &iloc); |
3171 | if (EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize && | ||
3172 | !(EXT4_I(inode)->i_state & EXT4_STATE_NO_EXPAND)) { | ||
3173 | /* | ||
3174 | * We need extra buffer credits since we may write into EA block | ||
3175 | * with this same handle. If journal_extend fails, then it will | ||
3176 | * only result in a minor loss of functionality for that inode. | ||
3177 | * If this is felt to be critical, then e2fsck should be run to | ||
3178 | * force a large enough s_min_extra_isize. | ||
3179 | */ | ||
3180 | if ((jbd2_journal_extend(handle, | ||
3181 | EXT4_DATA_TRANS_BLOCKS(inode->i_sb))) == 0) { | ||
3182 | ret = ext4_expand_extra_isize(inode, | ||
3183 | sbi->s_want_extra_isize, | ||
3184 | iloc, handle); | ||
3185 | if (ret) { | ||
3186 | EXT4_I(inode)->i_state |= EXT4_STATE_NO_EXPAND; | ||
3187 | if (mnt_count != sbi->s_es->s_mnt_count) { | ||
3188 | ext4_warning(inode->i_sb, __FUNCTION__, | ||
3189 | "Unable to expand inode %lu. Delete" | ||
3190 | " some EAs or run e2fsck.", | ||
3191 | inode->i_ino); | ||
3192 | mnt_count = sbi->s_es->s_mnt_count; | ||
3193 | } | ||
3194 | } | ||
3195 | } | ||
3196 | } | ||
3136 | if (!err) | 3197 | if (!err) |
3137 | err = ext4_mark_iloc_dirty(handle, inode, &iloc); | 3198 | err = ext4_mark_iloc_dirty(handle, inode, &iloc); |
3138 | return err; | 3199 | return err; |
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index fe16a569d06b..b10d68fffb55 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c | |||
@@ -66,13 +66,6 @@ | |||
66 | #define BFIRST(bh) ENTRY(BHDR(bh)+1) | 66 | #define BFIRST(bh) ENTRY(BHDR(bh)+1) |
67 | #define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0) | 67 | #define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0) |
68 | 68 | ||
69 | #define IHDR(inode, raw_inode) \ | ||
70 | ((struct ext4_xattr_ibody_header *) \ | ||
71 | ((void *)raw_inode + \ | ||
72 | EXT4_GOOD_OLD_INODE_SIZE + \ | ||
73 | EXT4_I(inode)->i_extra_isize)) | ||
74 | #define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1)) | ||
75 | |||
76 | #ifdef EXT4_XATTR_DEBUG | 69 | #ifdef EXT4_XATTR_DEBUG |
77 | # define ea_idebug(inode, f...) do { \ | 70 | # define ea_idebug(inode, f...) do { \ |
78 | printk(KERN_DEBUG "inode %s:%lu: ", \ | 71 | printk(KERN_DEBUG "inode %s:%lu: ", \ |
@@ -508,6 +501,24 @@ out: | |||
508 | return; | 501 | return; |
509 | } | 502 | } |
510 | 503 | ||
504 | /* | ||
505 | * Find the available free space for EAs. This also returns the total number of | ||
506 | * bytes used by EA entries. | ||
507 | */ | ||
508 | static size_t ext4_xattr_free_space(struct ext4_xattr_entry *last, | ||
509 | size_t *min_offs, void *base, int *total) | ||
510 | { | ||
511 | for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { | ||
512 | *total += EXT4_XATTR_LEN(last->e_name_len); | ||
513 | if (!last->e_value_block && last->e_value_size) { | ||
514 | size_t offs = le16_to_cpu(last->e_value_offs); | ||
515 | if (offs < *min_offs) | ||
516 | *min_offs = offs; | ||
517 | } | ||
518 | } | ||
519 | return (*min_offs - ((void *)last - base) - sizeof(__u32)); | ||
520 | } | ||
521 | |||
511 | struct ext4_xattr_info { | 522 | struct ext4_xattr_info { |
512 | int name_index; | 523 | int name_index; |
513 | const char *name; | 524 | const char *name; |
@@ -1014,6 +1025,8 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index, | |||
1014 | if (!error) { | 1025 | if (!error) { |
1015 | ext4_xattr_update_super_block(handle, inode->i_sb); | 1026 | ext4_xattr_update_super_block(handle, inode->i_sb); |
1016 | inode->i_ctime = ext4_current_time(inode); | 1027 | inode->i_ctime = ext4_current_time(inode); |
1028 | if (!value) | ||
1029 | EXT4_I(inode)->i_state &= ~EXT4_STATE_NO_EXPAND; | ||
1017 | error = ext4_mark_iloc_dirty(handle, inode, &is.iloc); | 1030 | error = ext4_mark_iloc_dirty(handle, inode, &is.iloc); |
1018 | /* | 1031 | /* |
1019 | * The bh is consumed by ext4_mark_iloc_dirty, even with | 1032 | * The bh is consumed by ext4_mark_iloc_dirty, even with |
@@ -1067,6 +1080,253 @@ retry: | |||
1067 | } | 1080 | } |
1068 | 1081 | ||
1069 | /* | 1082 | /* |
1083 | * Shift the EA entries in the inode to create space for the increased | ||
1084 | * i_extra_isize. | ||
1085 | */ | ||
1086 | static void ext4_xattr_shift_entries(struct ext4_xattr_entry *entry, | ||
1087 | int value_offs_shift, void *to, | ||
1088 | void *from, size_t n, int blocksize) | ||
1089 | { | ||
1090 | struct ext4_xattr_entry *last = entry; | ||
1091 | int new_offs; | ||
1092 | |||
1093 | /* Adjust the value offsets of the entries */ | ||
1094 | for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { | ||
1095 | if (!last->e_value_block && last->e_value_size) { | ||
1096 | new_offs = le16_to_cpu(last->e_value_offs) + | ||
1097 | value_offs_shift; | ||
1098 | BUG_ON(new_offs + le32_to_cpu(last->e_value_size) | ||
1099 | > blocksize); | ||
1100 | last->e_value_offs = cpu_to_le16(new_offs); | ||
1101 | } | ||
1102 | } | ||
1103 | /* Shift the entries by n bytes */ | ||
1104 | memmove(to, from, n); | ||
1105 | } | ||
1106 | |||
1107 | /* | ||
1108 | * Expand an inode by new_extra_isize bytes when EAs are present. | ||
1109 | * Returns 0 on success or negative error number on failure. | ||
1110 | */ | ||
1111 | int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, | ||
1112 | struct ext4_inode *raw_inode, handle_t *handle) | ||
1113 | { | ||
1114 | struct ext4_xattr_ibody_header *header; | ||
1115 | struct ext4_xattr_entry *entry, *last, *first; | ||
1116 | struct buffer_head *bh = NULL; | ||
1117 | struct ext4_xattr_ibody_find *is = NULL; | ||
1118 | struct ext4_xattr_block_find *bs = NULL; | ||
1119 | char *buffer = NULL, *b_entry_name = NULL; | ||
1120 | size_t min_offs, free; | ||
1121 | int total_ino, total_blk; | ||
1122 | void *base, *start, *end; | ||
1123 | int extra_isize = 0, error = 0, tried_min_extra_isize = 0; | ||
1124 | int s_min_extra_isize = EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize; | ||
1125 | |||
1126 | down_write(&EXT4_I(inode)->xattr_sem); | ||
1127 | retry: | ||
1128 | if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) { | ||
1129 | up_write(&EXT4_I(inode)->xattr_sem); | ||
1130 | return 0; | ||
1131 | } | ||
1132 | |||
1133 | header = IHDR(inode, raw_inode); | ||
1134 | entry = IFIRST(header); | ||
1135 | |||
1136 | /* | ||
1137 | * Check if enough free space is available in the inode to shift the | ||
1138 | * entries ahead by new_extra_isize. | ||
1139 | */ | ||
1140 | |||
1141 | base = start = entry; | ||
1142 | end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; | ||
1143 | min_offs = end - base; | ||
1144 | last = entry; | ||
1145 | total_ino = sizeof(struct ext4_xattr_ibody_header); | ||
1146 | |||
1147 | free = ext4_xattr_free_space(last, &min_offs, base, &total_ino); | ||
1148 | if (free >= new_extra_isize) { | ||
1149 | entry = IFIRST(header); | ||
1150 | ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize | ||
1151 | - new_extra_isize, (void *)raw_inode + | ||
1152 | EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize, | ||
1153 | (void *)header, total_ino, | ||
1154 | inode->i_sb->s_blocksize); | ||
1155 | EXT4_I(inode)->i_extra_isize = new_extra_isize; | ||
1156 | error = 0; | ||
1157 | goto cleanup; | ||
1158 | } | ||
1159 | |||
1160 | /* | ||
1161 | * Enough free space isn't available in the inode, check if | ||
1162 | * EA block can hold new_extra_isize bytes. | ||
1163 | */ | ||
1164 | if (EXT4_I(inode)->i_file_acl) { | ||
1165 | bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl); | ||
1166 | error = -EIO; | ||
1167 | if (!bh) | ||
1168 | goto cleanup; | ||
1169 | if (ext4_xattr_check_block(bh)) { | ||
1170 | ext4_error(inode->i_sb, __FUNCTION__, | ||
1171 | "inode %lu: bad block %llu", inode->i_ino, | ||
1172 | EXT4_I(inode)->i_file_acl); | ||
1173 | error = -EIO; | ||
1174 | goto cleanup; | ||
1175 | } | ||
1176 | base = BHDR(bh); | ||
1177 | first = BFIRST(bh); | ||
1178 | end = bh->b_data + bh->b_size; | ||
1179 | min_offs = end - base; | ||
1180 | free = ext4_xattr_free_space(first, &min_offs, base, | ||
1181 | &total_blk); | ||
1182 | if (free < new_extra_isize) { | ||
1183 | if (!tried_min_extra_isize && s_min_extra_isize) { | ||
1184 | tried_min_extra_isize++; | ||
1185 | new_extra_isize = s_min_extra_isize; | ||
1186 | brelse(bh); | ||
1187 | goto retry; | ||
1188 | } | ||
1189 | error = -1; | ||
1190 | goto cleanup; | ||
1191 | } | ||
1192 | } else { | ||
1193 | free = inode->i_sb->s_blocksize; | ||
1194 | } | ||
1195 | |||
1196 | while (new_extra_isize > 0) { | ||
1197 | size_t offs, size, entry_size; | ||
1198 | struct ext4_xattr_entry *small_entry = NULL; | ||
1199 | struct ext4_xattr_info i = { | ||
1200 | .value = NULL, | ||
1201 | .value_len = 0, | ||
1202 | }; | ||
1203 | unsigned int total_size; /* EA entry size + value size */ | ||
1204 | unsigned int shift_bytes; /* No. of bytes to shift EAs by? */ | ||
1205 | unsigned int min_total_size = ~0U; | ||
1206 | |||
1207 | is = kzalloc(sizeof(struct ext4_xattr_ibody_find), GFP_NOFS); | ||
1208 | bs = kzalloc(sizeof(struct ext4_xattr_block_find), GFP_NOFS); | ||
1209 | if (!is || !bs) { | ||
1210 | error = -ENOMEM; | ||
1211 | goto cleanup; | ||
1212 | } | ||
1213 | |||
1214 | is->s.not_found = -ENODATA; | ||
1215 | bs->s.not_found = -ENODATA; | ||
1216 | is->iloc.bh = NULL; | ||
1217 | bs->bh = NULL; | ||
1218 | |||
1219 | last = IFIRST(header); | ||
1220 | /* Find the entry best suited to be pushed into EA block */ | ||
1221 | entry = NULL; | ||
1222 | for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { | ||
1223 | total_size = | ||
1224 | EXT4_XATTR_SIZE(le32_to_cpu(last->e_value_size)) + | ||
1225 | EXT4_XATTR_LEN(last->e_name_len); | ||
1226 | if (total_size <= free && total_size < min_total_size) { | ||
1227 | if (total_size < new_extra_isize) { | ||
1228 | small_entry = last; | ||
1229 | } else { | ||
1230 | entry = last; | ||
1231 | min_total_size = total_size; | ||
1232 | } | ||
1233 | } | ||
1234 | } | ||
1235 | |||
1236 | if (entry == NULL) { | ||
1237 | if (small_entry) { | ||
1238 | entry = small_entry; | ||
1239 | } else { | ||
1240 | if (!tried_min_extra_isize && | ||
1241 | s_min_extra_isize) { | ||
1242 | tried_min_extra_isize++; | ||
1243 | new_extra_isize = s_min_extra_isize; | ||
1244 | goto retry; | ||
1245 | } | ||
1246 | error = -1; | ||
1247 | goto cleanup; | ||
1248 | } | ||
1249 | } | ||
1250 | offs = le16_to_cpu(entry->e_value_offs); | ||
1251 | size = le32_to_cpu(entry->e_value_size); | ||
1252 | entry_size = EXT4_XATTR_LEN(entry->e_name_len); | ||
1253 | i.name_index = entry->e_name_index, | ||
1254 | buffer = kmalloc(EXT4_XATTR_SIZE(size), GFP_NOFS); | ||
1255 | b_entry_name = kmalloc(entry->e_name_len + 1, GFP_NOFS); | ||
1256 | if (!buffer || !b_entry_name) { | ||
1257 | error = -ENOMEM; | ||
1258 | goto cleanup; | ||
1259 | } | ||
1260 | /* Save the entry name and the entry value */ | ||
1261 | memcpy(buffer, (void *)IFIRST(header) + offs, | ||
1262 | EXT4_XATTR_SIZE(size)); | ||
1263 | memcpy(b_entry_name, entry->e_name, entry->e_name_len); | ||
1264 | b_entry_name[entry->e_name_len] = '\0'; | ||
1265 | i.name = b_entry_name; | ||
1266 | |||
1267 | error = ext4_get_inode_loc(inode, &is->iloc); | ||
1268 | if (error) | ||
1269 | goto cleanup; | ||
1270 | |||
1271 | error = ext4_xattr_ibody_find(inode, &i, is); | ||
1272 | if (error) | ||
1273 | goto cleanup; | ||
1274 | |||
1275 | /* Remove the chosen entry from the inode */ | ||
1276 | error = ext4_xattr_ibody_set(handle, inode, &i, is); | ||
1277 | |||
1278 | entry = IFIRST(header); | ||
1279 | if (entry_size + EXT4_XATTR_SIZE(size) >= new_extra_isize) | ||
1280 | shift_bytes = new_extra_isize; | ||
1281 | else | ||
1282 | shift_bytes = entry_size + size; | ||
1283 | /* Adjust the offsets and shift the remaining entries ahead */ | ||
1284 | ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize - | ||
1285 | shift_bytes, (void *)raw_inode + | ||
1286 | EXT4_GOOD_OLD_INODE_SIZE + extra_isize + shift_bytes, | ||
1287 | (void *)header, total_ino - entry_size, | ||
1288 | inode->i_sb->s_blocksize); | ||
1289 | |||
1290 | extra_isize += shift_bytes; | ||
1291 | new_extra_isize -= shift_bytes; | ||
1292 | EXT4_I(inode)->i_extra_isize = extra_isize; | ||
1293 | |||
1294 | i.name = b_entry_name; | ||
1295 | i.value = buffer; | ||
1296 | i.value_len = cpu_to_le32(size); | ||
1297 | error = ext4_xattr_block_find(inode, &i, bs); | ||
1298 | if (error) | ||
1299 | goto cleanup; | ||
1300 | |||
1301 | /* Add entry which was removed from the inode into the block */ | ||
1302 | error = ext4_xattr_block_set(handle, inode, &i, bs); | ||
1303 | if (error) | ||
1304 | goto cleanup; | ||
1305 | kfree(b_entry_name); | ||
1306 | kfree(buffer); | ||
1307 | brelse(is->iloc.bh); | ||
1308 | kfree(is); | ||
1309 | kfree(bs); | ||
1310 | } | ||
1311 | brelse(bh); | ||
1312 | up_write(&EXT4_I(inode)->xattr_sem); | ||
1313 | return 0; | ||
1314 | |||
1315 | cleanup: | ||
1316 | kfree(b_entry_name); | ||
1317 | kfree(buffer); | ||
1318 | if (is) | ||
1319 | brelse(is->iloc.bh); | ||
1320 | kfree(is); | ||
1321 | kfree(bs); | ||
1322 | brelse(bh); | ||
1323 | up_write(&EXT4_I(inode)->xattr_sem); | ||
1324 | return error; | ||
1325 | } | ||
1326 | |||
1327 | |||
1328 | |||
1329 | /* | ||
1070 | * ext4_xattr_delete_inode() | 1330 | * ext4_xattr_delete_inode() |
1071 | * | 1331 | * |
1072 | * Free extended attribute resources associated with this inode. This | 1332 | * Free extended attribute resources associated with this inode. This |
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index 79432b35398f..d7f5d6a12651 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h | |||
@@ -56,6 +56,13 @@ struct ext4_xattr_entry { | |||
56 | #define EXT4_XATTR_SIZE(size) \ | 56 | #define EXT4_XATTR_SIZE(size) \ |
57 | (((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND) | 57 | (((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND) |
58 | 58 | ||
59 | #define IHDR(inode, raw_inode) \ | ||
60 | ((struct ext4_xattr_ibody_header *) \ | ||
61 | ((void *)raw_inode + \ | ||
62 | EXT4_GOOD_OLD_INODE_SIZE + \ | ||
63 | EXT4_I(inode)->i_extra_isize)) | ||
64 | #define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1)) | ||
65 | |||
59 | # ifdef CONFIG_EXT4DEV_FS_XATTR | 66 | # ifdef CONFIG_EXT4DEV_FS_XATTR |
60 | 67 | ||
61 | extern struct xattr_handler ext4_xattr_user_handler; | 68 | extern struct xattr_handler ext4_xattr_user_handler; |
@@ -74,6 +81,9 @@ extern int ext4_xattr_set_handle(handle_t *, struct inode *, int, const char *, | |||
74 | extern void ext4_xattr_delete_inode(handle_t *, struct inode *); | 81 | extern void ext4_xattr_delete_inode(handle_t *, struct inode *); |
75 | extern void ext4_xattr_put_super(struct super_block *); | 82 | extern void ext4_xattr_put_super(struct super_block *); |
76 | 83 | ||
84 | extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, | ||
85 | struct ext4_inode *raw_inode, handle_t *handle); | ||
86 | |||
77 | extern int init_ext4_xattr(void); | 87 | extern int init_ext4_xattr(void); |
78 | extern void exit_ext4_xattr(void); | 88 | extern void exit_ext4_xattr(void); |
79 | 89 | ||
@@ -129,6 +139,13 @@ exit_ext4_xattr(void) | |||
129 | { | 139 | { |
130 | } | 140 | } |
131 | 141 | ||
142 | static inline int | ||
143 | ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, | ||
144 | struct ext4_inode *raw_inode, handle_t *handle) | ||
145 | { | ||
146 | return -EOPNOTSUPP; | ||
147 | } | ||
148 | |||
132 | #define ext4_xattr_handlers NULL | 149 | #define ext4_xattr_handlers NULL |
133 | 150 | ||
134 | # endif /* CONFIG_EXT4DEV_FS_XATTR */ | 151 | # endif /* CONFIG_EXT4DEV_FS_XATTR */ |