aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorKalpak Shah <kalpak@clusterfs.com>2007-07-18 09:19:57 -0400
committerTheodore Ts'o <tytso@mit.edu>2007-07-18 09:19:57 -0400
commit6dd4ee7cab7e3a17c571aebd444f4344c8c4946e (patch)
treeee7b36d3a83ea843746ed3c906a9ad778838b9c6 /fs
parentef7f38359ea8b3e9c7f2cae9a4d4935f55ca9e80 (diff)
ext4: Expand extra_inodes space per the s_{want,min}_extra_isize fields
We need to make sure that existing ext3 filesystems can also avail the new fields that have been added to the ext4 inode. We use s_want_extra_isize and s_min_extra_isize to decide by how much we should expand the inode. If EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE feature is set then we expand the inode by max(s_want_extra_isize, s_min_extra_isize , sizeof(ext4_inode) - EXT4_GOOD_OLD_INODE_SIZE) bytes. Actually it is still an open question about whether users should be able to set s_*_extra_isize smaller than the known fields or not. This patch also adds the functionality to expand inodes to include the newly added fields. We start by trying to expand by s_want_extra_isize bytes and if its fails we try to expand by s_min_extra_isize bytes. This is done by changing the i_extra_isize if enough space is available in the inode and no EAs are present. If EAs are present and there is enough space in the inode then the EAs in the inode are shifted to make space. If enough space is not available in the inode due to the EAs then 1 or more EAs are shifted to the external EA block. In the worst case when even the external EA block does not have enough space we inform the user that some EA would need to be deleted or s_min_extra_isize would have to be reduced. Signed-off-by: Andreas Dilger <adilger@clusterfs.com> Signed-off-by: Kalpak Shah <kalpak@clusterfs.com> Signed-off-by: Mingming Cao <cmm@us.ibm.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/inode.c63
-rw-r--r--fs/ext4/xattr.c274
-rw-r--r--fs/ext4/xattr.h17
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 */
3112int 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,
3129int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode) 3162int 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 */
508static 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
511struct ext4_xattr_info { 522struct 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 */
1086static 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 */
1111int 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);
1127retry:
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
1315cleanup:
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
61extern struct xattr_handler ext4_xattr_user_handler; 68extern struct xattr_handler ext4_xattr_user_handler;
@@ -74,6 +81,9 @@ extern int ext4_xattr_set_handle(handle_t *, struct inode *, int, const char *,
74extern void ext4_xattr_delete_inode(handle_t *, struct inode *); 81extern void ext4_xattr_delete_inode(handle_t *, struct inode *);
75extern void ext4_xattr_put_super(struct super_block *); 82extern void ext4_xattr_put_super(struct super_block *);
76 83
84extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
85 struct ext4_inode *raw_inode, handle_t *handle);
86
77extern int init_ext4_xattr(void); 87extern int init_ext4_xattr(void);
78extern void exit_ext4_xattr(void); 88extern void exit_ext4_xattr(void);
79 89
@@ -129,6 +139,13 @@ exit_ext4_xattr(void)
129{ 139{
130} 140}
131 141
142static inline int
143ext4_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 */