/*
* linux/fs/hfsplus/xattr.c
*
* Vyacheslav Dubeyko <slava@dubeyko.com>
*
* Logic of processing extended attributes
*/
#include "hfsplus_fs.h"
#include <linux/posix_acl_xattr.h>
#include "xattr.h"
#include "acl.h"
const struct xattr_handler *hfsplus_xattr_handlers[] = {
&hfsplus_xattr_osx_handler,
&hfsplus_xattr_user_handler,
&hfsplus_xattr_trusted_handler,
#ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
&posix_acl_access_xattr_handler,
&posix_acl_default_xattr_handler,
#endif
&hfsplus_xattr_security_handler,
NULL
};
static int strcmp_xattr_finder_info(const char *name)
{
if (name) {
return strncmp(name, HFSPLUS_XATTR_FINDER_INFO_NAME,
sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME));
}
return -1;
}
static int strcmp_xattr_acl(const char *name)
{
if (name) {
return strncmp(name, HFSPLUS_XATTR_ACL_NAME,
sizeof(HFSPLUS_XATTR_ACL_NAME));
}
return -1;
}
static inline int is_known_namespace(const char *name)
{
if (strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) &&
strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) &&
strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
return false;
return true;
}
static void hfsplus_init_header_node(struct inode *attr_file,
u32 clump_size,
char *buf, u16 node_size)
{
struct hfs_bnode_desc *desc;
struct hfs_btree_header_rec *head;
u16 offset;
__be16 *rec_offsets;
u32 hdr_node_map_rec_bits;
char *bmp;
u32 used_nodes;
u32 used_bmp_bytes;
loff_t tmp;
hfs_dbg(ATTR_MOD, "init_hdr_attr_file: clump %u, node_size %u\n",
clump_size, node_size);
/* The end of the node contains list of record offsets */
rec_offsets = (__be16 *)(buf + node_size);
desc = (struct hfs_bnode_desc *)buf;
desc->type = HFS_NODE_HEADER;
desc->num_recs = cpu_to_be16(HFSPLUS_BTREE_HDR_NODE_RECS_COUNT);
offset = sizeof(struct hfs_bnode_desc);
*--rec_offsets = cpu_to_be16(offset);
head = (struct hfs_btree_header_rec *)(buf + offset);
head->node_size = cpu_to_be16(node_size);
tmp = i_size_read(attr_file);
do_div(tmp, node_size);
head->node_count = cpu_to_be32(tmp);
head->free_nodes = cpu_to_be32(be32_to_cpu(head->node_count) - 1);
head->clump_size = cpu_to_be32(clump_size);
head->attributes |= cpu_to_be32(HFS_TREE_BIGKEYS | HFS_TREE_VARIDXKEYS);
head->max_key_len = cpu_to_be16(HFSPLUS_ATTR_KEYLEN - sizeof(u16));
offset += sizeof(struct hfs_btree_header_rec);
*--rec_offsets = cpu_to_be16(offset);
offset += HFSPLUS_BTREE_HDR_USER_BYTES;
*--rec_offsets = cpu_to_be16(offset);
hdr_node_map_rec_bits = 8 * (node_size - offset - (4 * sizeof(u16)));
if (be32_to_cpu(head->node_count) > hdr_node_map_rec_bits) {
u32 map_node_bits;
u32 map_nodes;
desc->next = cpu_to_be32(be32_to_cpu(head->leaf_tail) + 1);
map_node_bits = 8 * (node_size - sizeof(struct hfs_bnode_desc) -
(2 * sizeof(u16)) - 2);
map_nodes = (be32_to_cpu(head->node_count) -
hdr_node_map_rec_bits +
(map_node_bits - 1)) / map_node_bits;
be32_add_cpu(&head->free_nodes, 0 - map_nodes);
}
bmp = buf + offset;
used_nodes =
be32_to_cpu(head->node_count) - be32_to_cpu(head->free_nodes);
used_bmp_bytes = used_nodes / 8;
if (used_bmp_bytes) {
memset(bmp, 0xFF, used_bmp_bytes);
bmp += used_bmp_bytes;
used_nodes %= 8;
}
*bmp = ~(0xFF >> used_nodes);
offset += hdr_node_map_rec_bits / 8;
*--rec_offsets = cpu_to_be16(offset);
}
static int hfsplus_create_attributes_file(struct super_block *sb)
{
int err = 0;
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
struct inode *attr_file;
struct hfsplus_inode_info *hip;
u32 clump_size;
u16 node_size = HFSPLUS_ATTR_TREE_NODE_SIZE;
char *buf;
int index, written;
struct address_space *mapping;
struct page *page;
int old_state = HFSPLUS_EMPTY_ATTR_TREE;
hfs_dbg(ATTR_MOD, "create_attr_file: ino %d\n", HFSPLUS_ATTR_CNID);
check_attr_tree_state_again:
switch (atomic_read(&sbi->attr_tree_state)) {
case HFSPLUS_EMPTY_ATTR_TREE:
if (old_state != atomic_cmpxchg(&sbi->attr_tree_state,
old_state