|
66 | 66 | #define BFIRST(bh) ENTRY(BHDR(bh)+1)
|
67 | 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 | 69 | #ifdef EXT4_XATTR_DEBUG
|
77 | 70 | # define ea_idebug(inode, f...) do { \
|
78 | 71 | printk(KERN_DEBUG "inode %s:%lu: ", \
|
@@ -508,6 +501,24 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
|
508 | 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 | 522 | struct ext4_xattr_info {
|
512 | 523 | int name_index;
|
513 | 524 | const char *name;
|
@@ -1014,6 +1025,8 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
|
1014 | 1025 | if (!error) {
|
1015 | 1026 | ext4_xattr_update_super_block(handle, inode->i_sb);
|
1016 | 1027 | inode->i_ctime = ext4_current_time(inode);
|
| 1028 | + if (!value) |
| 1029 | + EXT4_I(inode)->i_state &= ~EXT4_STATE_NO_EXPAND; |
1017 | 1030 | error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
|
1018 | 1031 | /*
|
1019 | 1032 | * The bh is consumed by ext4_mark_iloc_dirty, even with
|
@@ -1066,6 +1079,253 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
|
1066 | 1079 | return error;
|
1067 | 1080 | }
|
1068 | 1081 |
|
| 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 | + |
1069 | 1329 | /*
|
1070 | 1330 | * ext4_xattr_delete_inode()
|
1071 | 1331 | *
|
|
0 commit comments