aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/xattr.c
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/ext4/xattr.c
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/ext4/xattr.c')
-rw-r--r--fs/ext4/xattr.c274
1 files changed, 267 insertions, 7 deletions
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