diff options
-rw-r--r-- | fs/ocfs2/xattr.c | 241 |
1 files changed, 226 insertions, 15 deletions
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 8ae4e5d1f730..945ca697eb25 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c | |||
@@ -137,6 +137,51 @@ struct ocfs2_xattr_search { | |||
137 | int not_found; | 137 | int not_found; |
138 | }; | 138 | }; |
139 | 139 | ||
140 | /* Operations on struct ocfs2_xa_entry */ | ||
141 | struct ocfs2_xa_loc; | ||
142 | struct ocfs2_xa_loc_operations { | ||
143 | /* | ||
144 | * Return a pointer to the appropriate buffer in loc->xl_storage | ||
145 | * at the given offset from loc->xl_header. | ||
146 | */ | ||
147 | void *(*xlo_offset_pointer)(struct ocfs2_xa_loc *loc, int offset); | ||
148 | |||
149 | /* | ||
150 | * Remove the name+value at this location. Do whatever is | ||
151 | * appropriate with the remaining name+value pairs. | ||
152 | */ | ||
153 | void (*xlo_wipe_namevalue)(struct ocfs2_xa_loc *loc); | ||
154 | }; | ||
155 | |||
156 | /* | ||
157 | * Describes an xattr entry location. This is a memory structure | ||
158 | * tracking the on-disk structure. | ||
159 | */ | ||
160 | struct ocfs2_xa_loc { | ||
161 | /* The ocfs2_xattr_header inside the on-disk storage. Not NULL. */ | ||
162 | struct ocfs2_xattr_header *xl_header; | ||
163 | |||
164 | /* Bytes from xl_header to the end of the storage */ | ||
165 | int xl_size; | ||
166 | |||
167 | /* | ||
168 | * The ocfs2_xattr_entry this location describes. If this is | ||
169 | * NULL, this location describes the on-disk structure where it | ||
170 | * would have been. | ||
171 | */ | ||
172 | struct ocfs2_xattr_entry *xl_entry; | ||
173 | |||
174 | /* | ||
175 | * Internal housekeeping | ||
176 | */ | ||
177 | |||
178 | /* Buffer(s) containing this entry */ | ||
179 | void *xl_storage; | ||
180 | |||
181 | /* Operations on the storage backing this location */ | ||
182 | const struct ocfs2_xa_loc_operations *xl_ops; | ||
183 | }; | ||
184 | |||
140 | static int ocfs2_xattr_bucket_get_name_value(struct super_block *sb, | 185 | static int ocfs2_xattr_bucket_get_name_value(struct super_block *sb, |
141 | struct ocfs2_xattr_header *xh, | 186 | struct ocfs2_xattr_header *xh, |
142 | int index, | 187 | int index, |
@@ -1418,6 +1463,170 @@ static int ocfs2_xattr_set_value_outside(struct inode *inode, | |||
1418 | } | 1463 | } |
1419 | 1464 | ||
1420 | /* | 1465 | /* |
1466 | * Wipe the name+value pair and allow the storage to reclaim it. This | ||
1467 | * must be followed by either removal of the entry or a call to | ||
1468 | * ocfs2_xa_add_namevalue(). | ||
1469 | */ | ||
1470 | static void ocfs2_xa_wipe_namevalue(struct ocfs2_xa_loc *loc) | ||
1471 | { | ||
1472 | loc->xl_ops->xlo_wipe_namevalue(loc); | ||
1473 | } | ||
1474 | |||
1475 | static void *ocfs2_xa_block_offset_pointer(struct ocfs2_xa_loc *loc, | ||
1476 | int offset) | ||
1477 | { | ||
1478 | BUG_ON(offset >= loc->xl_size); | ||
1479 | return (char *)loc->xl_header + offset; | ||
1480 | } | ||
1481 | |||
1482 | /* | ||
1483 | * Block storage for xattrs keeps the name+value pairs compacted. When | ||
1484 | * we remove one, we have to shift any that preceded it towards the end. | ||
1485 | */ | ||
1486 | static void ocfs2_xa_block_wipe_namevalue(struct ocfs2_xa_loc *loc) | ||
1487 | { | ||
1488 | int i, offset; | ||
1489 | int namevalue_offset, first_namevalue_offset, namevalue_size; | ||
1490 | struct ocfs2_xattr_entry *entry = loc->xl_entry; | ||
1491 | struct ocfs2_xattr_header *xh = loc->xl_header; | ||
1492 | u64 value_size = le64_to_cpu(entry->xe_value_size); | ||
1493 | int count = le16_to_cpu(xh->xh_count); | ||
1494 | |||
1495 | namevalue_offset = le16_to_cpu(entry->xe_name_offset); | ||
1496 | namevalue_size = OCFS2_XATTR_SIZE(entry->xe_name_len); | ||
1497 | if (value_size > OCFS2_XATTR_INLINE_SIZE) | ||
1498 | namevalue_size += OCFS2_XATTR_ROOT_SIZE; | ||
1499 | else | ||
1500 | namevalue_size += OCFS2_XATTR_SIZE(value_size); | ||
1501 | |||
1502 | for (i = 0, first_namevalue_offset = loc->xl_size; | ||
1503 | i < count; i++) { | ||
1504 | offset = le16_to_cpu(xh->xh_entries[i].xe_name_offset); | ||
1505 | if (offset < first_namevalue_offset) | ||
1506 | first_namevalue_offset = offset; | ||
1507 | } | ||
1508 | |||
1509 | /* Shift the name+value pairs */ | ||
1510 | memmove((char *)xh + first_namevalue_offset + namevalue_size, | ||
1511 | (char *)xh + first_namevalue_offset, | ||
1512 | namevalue_offset - first_namevalue_offset); | ||
1513 | memset((char *)xh + first_namevalue_offset, 0, namevalue_size); | ||
1514 | |||
1515 | /* Now tell xh->xh_entries about it */ | ||
1516 | for (i = 0; i < count; i++) { | ||
1517 | offset = le16_to_cpu(xh->xh_entries[i].xe_name_offset); | ||
1518 | if (offset < namevalue_offset) | ||
1519 | le16_add_cpu(&xh->xh_entries[i].xe_name_offset, | ||
1520 | namevalue_size); | ||
1521 | } | ||
1522 | |||
1523 | /* | ||
1524 | * Note that we don't update xh_free_start or xh_name_value_len | ||
1525 | * because they're not used in block-stored xattrs. | ||
1526 | */ | ||
1527 | } | ||
1528 | |||
1529 | /* | ||
1530 | * Operations for xattrs stored in blocks. This includes inline inode | ||
1531 | * storage and unindexed ocfs2_xattr_blocks. | ||
1532 | */ | ||
1533 | static const struct ocfs2_xa_loc_operations ocfs2_xa_block_loc_ops = { | ||
1534 | .xlo_offset_pointer = ocfs2_xa_block_offset_pointer, | ||
1535 | .xlo_wipe_namevalue = ocfs2_xa_block_wipe_namevalue, | ||
1536 | }; | ||
1537 | |||
1538 | static void *ocfs2_xa_bucket_offset_pointer(struct ocfs2_xa_loc *loc, | ||
1539 | int offset) | ||
1540 | { | ||
1541 | struct ocfs2_xattr_bucket *bucket = loc->xl_storage; | ||
1542 | int block, block_offset; | ||
1543 | |||
1544 | BUG_ON(offset >= OCFS2_XATTR_BUCKET_SIZE); | ||
1545 | |||
1546 | /* The header is at the front of the bucket */ | ||
1547 | block = offset >> bucket->bu_inode->i_sb->s_blocksize_bits; | ||
1548 | block_offset = offset % bucket->bu_inode->i_sb->s_blocksize; | ||
1549 | |||
1550 | return bucket_block(bucket, block) + block_offset; | ||
1551 | } | ||
1552 | |||
1553 | static void ocfs2_xa_bucket_wipe_namevalue(struct ocfs2_xa_loc *loc) | ||
1554 | { | ||
1555 | int namevalue_size; | ||
1556 | struct ocfs2_xattr_entry *entry = loc->xl_entry; | ||
1557 | u64 value_size = le64_to_cpu(entry->xe_value_size); | ||
1558 | |||
1559 | namevalue_size = OCFS2_XATTR_SIZE(entry->xe_name_len); | ||
1560 | if (value_size > OCFS2_XATTR_INLINE_SIZE) | ||
1561 | namevalue_size += OCFS2_XATTR_ROOT_SIZE; | ||
1562 | else | ||
1563 | namevalue_size += OCFS2_XATTR_SIZE(value_size); | ||
1564 | |||
1565 | le16_add_cpu(&loc->xl_header->xh_name_value_len, -namevalue_size); | ||
1566 | } | ||
1567 | |||
1568 | /* Operations for xattrs stored in buckets. */ | ||
1569 | static const struct ocfs2_xa_loc_operations ocfs2_xa_bucket_loc_ops = { | ||
1570 | .xlo_offset_pointer = ocfs2_xa_bucket_offset_pointer, | ||
1571 | .xlo_wipe_namevalue = ocfs2_xa_bucket_wipe_namevalue, | ||
1572 | }; | ||
1573 | |||
1574 | static void ocfs2_xa_remove_entry(struct ocfs2_xa_loc *loc) | ||
1575 | { | ||
1576 | ocfs2_xa_wipe_namevalue(loc); | ||
1577 | } | ||
1578 | |||
1579 | static void ocfs2_init_dinode_xa_loc(struct ocfs2_xa_loc *loc, | ||
1580 | struct inode *inode, | ||
1581 | struct buffer_head *bh, | ||
1582 | struct ocfs2_xattr_entry *entry) | ||
1583 | { | ||
1584 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data; | ||
1585 | |||
1586 | loc->xl_ops = &ocfs2_xa_block_loc_ops; | ||
1587 | loc->xl_storage = bh; | ||
1588 | loc->xl_entry = entry; | ||
1589 | |||
1590 | if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_XATTR_FL) | ||
1591 | loc->xl_size = le16_to_cpu(di->i_xattr_inline_size); | ||
1592 | else { | ||
1593 | BUG_ON(entry); | ||
1594 | loc->xl_size = OCFS2_SB(inode->i_sb)->s_xattr_inline_size; | ||
1595 | } | ||
1596 | loc->xl_header = | ||
1597 | (struct ocfs2_xattr_header *)(bh->b_data + bh->b_size - | ||
1598 | loc->xl_size); | ||
1599 | } | ||
1600 | |||
1601 | static void ocfs2_init_xattr_block_xa_loc(struct ocfs2_xa_loc *loc, | ||
1602 | struct buffer_head *bh, | ||
1603 | struct ocfs2_xattr_entry *entry) | ||
1604 | { | ||
1605 | struct ocfs2_xattr_block *xb = | ||
1606 | (struct ocfs2_xattr_block *)bh->b_data; | ||
1607 | |||
1608 | BUG_ON(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED); | ||
1609 | |||
1610 | loc->xl_ops = &ocfs2_xa_block_loc_ops; | ||
1611 | loc->xl_storage = bh; | ||
1612 | loc->xl_header = &(xb->xb_attrs.xb_header); | ||
1613 | loc->xl_entry = entry; | ||
1614 | loc->xl_size = bh->b_size - offsetof(struct ocfs2_xattr_block, | ||
1615 | xb_attrs.xb_header); | ||
1616 | } | ||
1617 | |||
1618 | static void ocfs2_init_xattr_bucket_xa_loc(struct ocfs2_xa_loc *loc, | ||
1619 | struct ocfs2_xattr_bucket *bucket, | ||
1620 | struct ocfs2_xattr_entry *entry) | ||
1621 | { | ||
1622 | loc->xl_ops = &ocfs2_xa_bucket_loc_ops; | ||
1623 | loc->xl_storage = bucket; | ||
1624 | loc->xl_header = bucket_xh(bucket); | ||
1625 | loc->xl_entry = entry; | ||
1626 | loc->xl_size = OCFS2_XATTR_BUCKET_SIZE; | ||
1627 | } | ||
1628 | |||
1629 | /* | ||
1421 | * ocfs2_xattr_set_entry_local() | 1630 | * ocfs2_xattr_set_entry_local() |
1422 | * | 1631 | * |
1423 | * Set, replace or remove extended attribute in local. | 1632 | * Set, replace or remove extended attribute in local. |
@@ -1430,7 +1639,14 @@ static void ocfs2_xattr_set_entry_local(struct inode *inode, | |||
1430 | { | 1639 | { |
1431 | size_t name_len = strlen(xi->name); | 1640 | size_t name_len = strlen(xi->name); |
1432 | int i; | 1641 | int i; |
1642 | struct ocfs2_xa_loc loc; | ||
1433 | 1643 | ||
1644 | if (xs->xattr_bh == xs->inode_bh) | ||
1645 | ocfs2_init_dinode_xa_loc(&loc, inode, xs->inode_bh, | ||
1646 | xs->not_found ? NULL : xs->here); | ||
1647 | else | ||
1648 | ocfs2_init_xattr_block_xa_loc(&loc, xs->xattr_bh, | ||
1649 | xs->not_found ? NULL : xs->here); | ||
1434 | if (xi->value && xs->not_found) { | 1650 | if (xi->value && xs->not_found) { |
1435 | /* Insert the new xattr entry. */ | 1651 | /* Insert the new xattr entry. */ |
1436 | le16_add_cpu(&xs->header->xh_count, 1); | 1652 | le16_add_cpu(&xs->header->xh_count, 1); |
@@ -1469,9 +1685,9 @@ static void ocfs2_xattr_set_entry_local(struct inode *inode, | |||
1469 | xi->value_len); | 1685 | xi->value_len); |
1470 | return; | 1686 | return; |
1471 | } | 1687 | } |
1688 | |||
1472 | /* Remove the old name+value. */ | 1689 | /* Remove the old name+value. */ |
1473 | memmove(first_val + size, first_val, val - first_val); | 1690 | ocfs2_xa_wipe_namevalue(&loc); |
1474 | memset(first_val, 0, size); | ||
1475 | xs->here->xe_name_hash = 0; | 1691 | xs->here->xe_name_hash = 0; |
1476 | xs->here->xe_name_offset = 0; | 1692 | xs->here->xe_name_offset = 0; |
1477 | ocfs2_xattr_set_local(xs->here, 1); | 1693 | ocfs2_xattr_set_local(xs->here, 1); |
@@ -1479,23 +1695,15 @@ static void ocfs2_xattr_set_entry_local(struct inode *inode, | |||
1479 | 1695 | ||
1480 | min_offs += size; | 1696 | min_offs += size; |
1481 | 1697 | ||
1482 | /* Adjust all value offsets. */ | ||
1483 | last = xs->header->xh_entries; | ||
1484 | for (i = 0 ; i < le16_to_cpu(xs->header->xh_count); i++) { | ||
1485 | size_t o = le16_to_cpu(last->xe_name_offset); | ||
1486 | |||
1487 | if (o < offs) | ||
1488 | last->xe_name_offset = cpu_to_le16(o + size); | ||
1489 | last += 1; | ||
1490 | } | ||
1491 | |||
1492 | if (!xi->value) { | 1698 | if (!xi->value) { |
1493 | /* Remove the old entry. */ | 1699 | /* Remove the old entry. */ |
1494 | last -= 1; | 1700 | i = le16_to_cpu(xs->header->xh_count) - 1; |
1701 | last = &xs->header->xh_entries[i]; | ||
1702 | xs->header->xh_count = cpu_to_le16(i); | ||
1703 | |||
1495 | memmove(xs->here, xs->here + 1, | 1704 | memmove(xs->here, xs->here + 1, |
1496 | (void *)last - (void *)xs->here); | 1705 | (void *)last - (void *)xs->here); |
1497 | memset(last, 0, sizeof(struct ocfs2_xattr_entry)); | 1706 | memset(last, 0, sizeof(struct ocfs2_xattr_entry)); |
1498 | le16_add_cpu(&xs->header->xh_count, -1); | ||
1499 | } | 1707 | } |
1500 | } | 1708 | } |
1501 | if (xi->value) { | 1709 | if (xi->value) { |
@@ -4769,7 +4977,10 @@ static void ocfs2_xattr_set_entry_normal(struct inode *inode, | |||
4769 | size_t blocksize = inode->i_sb->s_blocksize; | 4977 | size_t blocksize = inode->i_sb->s_blocksize; |
4770 | char *val; | 4978 | char *val; |
4771 | size_t offs, size, new_size; | 4979 | size_t offs, size, new_size; |
4980 | struct ocfs2_xa_loc loc; | ||
4772 | 4981 | ||
4982 | ocfs2_init_xattr_bucket_xa_loc(&loc, xs->bucket, | ||
4983 | xs->not_found ? NULL : xs->here); | ||
4773 | last = &xh->xh_entries[count]; | 4984 | last = &xh->xh_entries[count]; |
4774 | if (!xs->not_found) { | 4985 | if (!xs->not_found) { |
4775 | xe = xs->here; | 4986 | xe = xs->here; |
@@ -4790,7 +5001,7 @@ static void ocfs2_xattr_set_entry_normal(struct inode *inode, | |||
4790 | new_size = OCFS2_XATTR_SIZE(name_len) + | 5001 | new_size = OCFS2_XATTR_SIZE(name_len) + |
4791 | OCFS2_XATTR_SIZE(xi->value_len); | 5002 | OCFS2_XATTR_SIZE(xi->value_len); |
4792 | 5003 | ||
4793 | le16_add_cpu(&xh->xh_name_value_len, -size); | 5004 | ocfs2_xa_wipe_namevalue(&loc); |
4794 | if (xi->value) { | 5005 | if (xi->value) { |
4795 | if (new_size > size) | 5006 | if (new_size > size) |
4796 | goto set_new_name_value; | 5007 | goto set_new_name_value; |