diff options
Diffstat (limited to 'fs/ext4/xattr.c')
-rw-r--r-- | fs/ext4/xattr.c | 274 |
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 | */ | ||
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 |