Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 95e0d7db authored by Vyacheslav Dubeyko's avatar Vyacheslav Dubeyko Committed by Linus Torvalds
Browse files

hfsplus: implement attributes file creation functionality



Implement functionality of creation AttributesFile metadata file on HFS+
volume in the case of absence of it.

It makes trying to open AttributesFile's B-tree during mount of HFS+
volume.  If HFS+ volume hasn't AttributesFile then a pointer on
AttributesFile's B-tree keeps as NULL.  Thereby, when it is discovered
absence of AttributesFile on HFS+ volume in the begin of xattr creation
operation then AttributesFile will be created.

The creation of AttributesFile will have success in the case of
availability (2 * clump) free blocks on HFS+ volume.  Otherwise,
creation operation is ended with error (-ENOSPC).

Signed-off-by: default avatarVyacheslav Dubeyko <slava@dubeyko.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Hellwig <hch@infradead.org>
Acked-by: default avatarHin-Tak Leung <htl10@users.sourceforge.net>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 099e9245
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -126,6 +126,14 @@ struct hfs_bnode {
#define HFS_BNODE_DIRTY		3
#define HFS_BNODE_DELETED	4

/*
 * Attributes file states
 */
#define HFSPLUS_EMPTY_ATTR_TREE		0
#define HFSPLUS_CREATING_ATTR_TREE	1
#define HFSPLUS_VALID_ATTR_TREE		2
#define HFSPLUS_FAILED_ATTR_TREE	3

/*
 * HFS+ superblock info (built from Volume Header on disk)
 */
@@ -141,6 +149,7 @@ struct hfsplus_sb_info {
	struct hfs_btree *ext_tree;
	struct hfs_btree *cat_tree;
	struct hfs_btree *attr_tree;
	atomic_t attr_tree_state;
	struct inode *alloc_file;
	struct inode *hidden_dir;
	struct nls_table *nls;
+2 −0
Original line number Diff line number Diff line
@@ -474,12 +474,14 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
		pr_err("failed to load catalog file\n");
		goto out_close_ext_tree;
	}
	atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE);
	if (vhdr->attr_file.total_blocks != 0) {
		sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID);
		if (!sbi->attr_tree) {
			pr_err("failed to load attributes file\n");
			goto out_close_cat_tree;
		}
		atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE);
	}
	sb->s_xattr = hfsplus_xattr_handlers;

+140 −2
Original line number Diff line number Diff line
@@ -192,6 +192,143 @@ static void hfsplus_init_header_node(struct inode *attr_file,
	*--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,
						HFSPLUS_CREATING_ATTR_TREE))
			goto check_attr_tree_state_again;
		break;
	case HFSPLUS_CREATING_ATTR_TREE:
		/*
		 * This state means that another thread is in process
		 * of AttributesFile creation. Theoretically, it is
		 * possible to be here. But really __setxattr() method
		 * first of all calls hfs_find_init() for lookup in
		 * B-tree of CatalogFile. This method locks mutex of
		 * CatalogFile's B-tree. As a result, if some thread
		 * is inside AttributedFile creation operation then
		 * another threads will be waiting unlocking of
		 * CatalogFile's B-tree's mutex. However, if code will
		 * change then we will return error code (-EAGAIN) from
		 * here. Really, it means that first try to set of xattr
		 * fails with error but second attempt will have success.
		 */
		return -EAGAIN;
	case HFSPLUS_VALID_ATTR_TREE:
		return 0;
	case HFSPLUS_FAILED_ATTR_TREE:
		return -EOPNOTSUPP;
	default:
		BUG();
	}

	attr_file = hfsplus_iget(sb, HFSPLUS_ATTR_CNID);
	if (IS_ERR(attr_file)) {
		pr_err("failed to load attributes file\n");
		return PTR_ERR(attr_file);
	}

	BUG_ON(i_size_read(attr_file) != 0);

	hip = HFSPLUS_I(attr_file);

	clump_size = hfsplus_calc_btree_clump_size(sb->s_blocksize,
						    node_size,
						    sbi->sect_count,
						    HFSPLUS_ATTR_CNID);

	mutex_lock(&hip->extents_lock);
	hip->clump_blocks = clump_size >> sbi->alloc_blksz_shift;
	mutex_unlock(&hip->extents_lock);

	if (sbi->free_blocks <= (hip->clump_blocks << 1)) {
		err = -ENOSPC;
		goto end_attr_file_creation;
	}

	while (hip->alloc_blocks < hip->clump_blocks) {
		err = hfsplus_file_extend(attr_file);
		if (unlikely(err)) {
			pr_err("failed to extend attributes file\n");
			goto end_attr_file_creation;
		}
		hip->phys_size = attr_file->i_size =
			(loff_t)hip->alloc_blocks << sbi->alloc_blksz_shift;
		hip->fs_blocks = hip->alloc_blocks << sbi->fs_shift;
		inode_set_bytes(attr_file, attr_file->i_size);
	}

	buf = kzalloc(node_size, GFP_NOFS);
	if (!buf) {
		pr_err("failed to allocate memory for header node\n");
		err = -ENOMEM;
		goto end_attr_file_creation;
	}

	hfsplus_init_header_node(attr_file, clump_size, buf, node_size);

	mapping = attr_file->i_mapping;

	index = 0;
	written = 0;
	for (; written < node_size; index++, written += PAGE_CACHE_SIZE) {
		void *kaddr;

		page = read_mapping_page(mapping, index, NULL);
		if (IS_ERR(page)) {
			err = PTR_ERR(page);
			goto failed_header_node_init;
		}

		kaddr = kmap_atomic(page);
		memcpy(kaddr, buf + written,
			min_t(size_t, PAGE_CACHE_SIZE, node_size - written));
		kunmap_atomic(kaddr);

		set_page_dirty(page);
		page_cache_release(page);
	}

	hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY);

	sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID);
	if (!sbi->attr_tree)
		pr_err("failed to load attributes file\n");

failed_header_node_init:
	kfree(buf);

end_attr_file_creation:
	iput(attr_file);

	if (!err)
		atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE);
	else if (err == -ENOSPC)
		atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE);
	else
		atomic_set(&sbi->attr_tree_state, HFSPLUS_FAILED_ATTR_TREE);

	return err;
}

int __hfsplus_setxattr(struct inode *inode, const char *name,
			const void *value, size_t size, int flags)
{
@@ -276,7 +413,8 @@ int __hfsplus_setxattr(struct inode *inode, const char *name,
	}

	if (!HFSPLUS_SB(inode->i_sb)->attr_tree) {
		err = -EOPNOTSUPP;
		err = hfsplus_create_attributes_file(inode->i_sb);
		if (unlikely(err))
			goto end_setxattr;
	}