Beispiel #1
0
int hfsplus_rmxattr(struct dentry *dentry, const char *name)
{
	struct inode *inode = dentry->d_inode;
	struct hfs_btree *btree = HFSPLUS_SB(inode->i_sb)->attr_tree;
	struct hfs_find_data fd;
	hfsplus_attr_entry entry;
	char tmp[32] = {0};
	int res = 0;

	dprint(DBG_XATTR, "hfs: rmattr [%s][%s] [%lu]\n", dentry->d_name.name, name, dentry->d_inode->i_ino);

	if (!strcmp(name, SZ_XATTR_NAME_TYPE) ||
		!strcmp(name, SZ_XATTR_NAME_CREATOR)) {
		return hfsplus_setxattr_buildin(dentry, name, tmp, 4, 0);
	}
	if (!strcmp(name, SZ_XATTR_NAME_FINDRINFO)) {
		return hfsplus_setxattr_buildin(dentry, name, tmp, 32, 0);
	} else if (!strcmp(name, SZ_XATTR_NAME_RFORK)) {
		return -EOPNOTSUPP;
	}

	res = hfs_find_init(btree, &fd);
	if (res) {
		return res;
	}
	hfsplus_attr_build_key(inode->i_sb, fd.search_key, cpu_to_be32((u32)(unsigned long)dentry->d_fsdata), name, 0);
	if ((res = hfs_brec_find(&fd))) {
		hfs_find_exit(&fd);
		goto out;
	}
	hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, sizeof(hfsplus_attr_entry));
	if (be32_to_cpu(entry.type) != kHFSPlusAttrData) {
		res = -EOPNOTSUPP;
		hfs_find_exit(&fd);
		goto out;
	}
	res = hfs_brec_remove(&fd);

	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
	hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY);
	hfs_find_exit(&fd);

	// check xattr after hfs_find_exit (unlock attr btree)
	if (!hfsplus_has_xattr(dentry)) {
		hfsplus_set_cat_flag(inode, HFS_HAS_ATTR_MASK, 0); // here would lock cat tree.
	}
	//if ((res = filemap_write_and_wait(inode->i_mapping))) {
	//	goto out;
	//}
out:
	return res;
}
Beispiel #2
0
// This function would lock the mutex of catalog tree.
static ssize_t hfsplus_set_cat_flag(struct inode *inode, u16 flags, int add)
{
	struct hfs_find_data fd;
	hfsplus_cat_entry entry;
	ssize_t res = 0;
	int is_file = 0;

	res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
	if (res)
		return res;
	res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
	if (res) {
		goto out;
	}
	hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, sizeof(hfsplus_cat_entry));
	switch (be16_to_cpu(entry.type)) {
		case HFSPLUS_FILE:
			is_file = 1;
			break;
		case HFSPLUS_FOLDER:
			is_file = 0;
			break;
		default:
			res = EINVAL;
			goto out;
	}
	if (is_file) {
		if (add) 
			entry.file.flags |= cpu_to_be16(flags);
		else
			entry.file.flags &= ~cpu_to_be16(flags);
	} else {
		if (add) 
			entry.folder.flags |= cpu_to_be16(flags);
		else
			entry.folder.flags &= ~cpu_to_be16(flags);
	}
	res = 0;
	hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
			is_file ? sizeof(struct hfsplus_cat_file) : sizeof(struct hfsplus_cat_folder));
	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
	hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
	
out:
	hfs_find_exit(&fd);
	return res;
}
Beispiel #3
0
int hfsplus_setxattr(struct dentry *dentry, const char *name,
		     const void *value, size_t size, int flags)
{
	struct inode *inode = dentry->d_inode;
	struct hfs_find_data fd;
	hfsplus_cat_entry entry;
	struct hfsplus_cat_file *file;
	int res;

	if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
		return -EOPNOTSUPP;

	res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
	if (res)
		return res;
	res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
	if (res)
		goto out;
	hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
			sizeof(struct hfsplus_cat_file));
	file = &entry.file;

	if (!strcmp(name, "hfs.type")) {
		if (size == 4)
			memcpy(&file->user_info.fdType, value, 4);
		else
			res = -ERANGE;
	} else if (!strcmp(name, "hfs.creator")) {
		if (size == 4)
			memcpy(&file->user_info.fdCreator, value, 4);
		else
			res = -ERANGE;
	} else
		res = -EOPNOTSUPP;
	if (!res) {
		hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
				sizeof(struct hfsplus_cat_file));
		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
	}
out:
	hfs_find_exit(&fd);
	return res;
}
Beispiel #4
0
void hfsplus_file_truncate(struct inode *inode)
{
	struct super_block *sb = inode->i_sb;
	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
	struct hfs_find_data fd;
	u32 alloc_cnt, blk_cnt, start;
	int res;

	hfs_dbg(INODE, "truncate: %lu, %llu -> %llu\n",
		inode->i_ino, (long long)hip->phys_size, inode->i_size);

	if (inode->i_size > hip->phys_size) {
		struct address_space *mapping = inode->i_mapping;
		struct page *page;
		void *fsdata;
		loff_t size = inode->i_size;

		res = pagecache_write_begin(NULL, mapping, size, 0,
						AOP_FLAG_UNINTERRUPTIBLE,
						&page, &fsdata);
		if (res)
			return;
		res = pagecache_write_end(NULL, mapping, size,
			0, 0, page, fsdata);
		if (res < 0)
			return;
		mark_inode_dirty(inode);
		return;
	} else if (inode->i_size == hip->phys_size)
		return;

	blk_cnt = (inode->i_size + HFSPLUS_SB(sb)->alloc_blksz - 1) >>
			HFSPLUS_SB(sb)->alloc_blksz_shift;

	mutex_lock(&hip->extents_lock);

	alloc_cnt = hip->alloc_blocks;
	if (blk_cnt == alloc_cnt)
		goto out_unlock;

	res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
	if (res) {
		mutex_unlock(&hip->extents_lock);
		/* XXX: We lack error handling of hfsplus_file_truncate() */
		return;
	}
	while (1) {
		if (alloc_cnt == hip->first_blocks) {
			hfsplus_free_extents(sb, hip->first_extents,
					     alloc_cnt, alloc_cnt - blk_cnt);
			hfsplus_dump_extent(hip->first_extents);
			hip->first_blocks = blk_cnt;
			break;
		}
		res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt);
		if (res)
			break;
		start = hip->cached_start;
		hfsplus_free_extents(sb, hip->cached_extents,
				     alloc_cnt - start, alloc_cnt - blk_cnt);
		hfsplus_dump_extent(hip->cached_extents);
		if (blk_cnt > start) {
			hip->extent_state |= HFSPLUS_EXT_DIRTY;
			break;
		}
		alloc_cnt = start;
		hip->cached_start = hip->cached_blocks = 0;
		hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
		hfs_brec_remove(&fd);
	}
	hfs_find_exit(&fd);

	hip->alloc_blocks = blk_cnt;
out_unlock:
	mutex_unlock(&hip->extents_lock);
	hip->phys_size = inode->i_size;
	hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >>
		sb->s_blocksize_bits;
	inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits);
	hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
}
Beispiel #5
0
int hfsplus_file_extend(struct inode *inode, bool zeroout)
{
	struct super_block *sb = inode->i_sb;
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
	u32 start, len, goal;
	int res;

	if (sbi->alloc_file->i_size * 8 <
	    sbi->total_blocks - sbi->free_blocks + 8) {
		/* extend alloc file */
		pr_err("extend alloc file! (%llu,%u,%u)\n",
		       sbi->alloc_file->i_size * 8,
		       sbi->total_blocks, sbi->free_blocks);
		return -ENOSPC;
	}

	mutex_lock(&hip->extents_lock);
	if (hip->alloc_blocks == hip->first_blocks)
		goal = hfsplus_ext_lastblock(hip->first_extents);
	else {
		res = hfsplus_ext_read_extent(inode, hip->alloc_blocks);
		if (res)
			goto out;
		goal = hfsplus_ext_lastblock(hip->cached_extents);
	}

	len = hip->clump_blocks;
	start = hfsplus_block_allocate(sb, sbi->total_blocks, goal, &len);
	if (start >= sbi->total_blocks) {
		start = hfsplus_block_allocate(sb, goal, 0, &len);
		if (start >= goal) {
			res = -ENOSPC;
			goto out;
		}
	}

	if (zeroout) {
		res = sb_issue_zeroout(sb, start, len, GFP_NOFS);
		if (res)
			goto out;
	}

	hfs_dbg(EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len);

	if (hip->alloc_blocks <= hip->first_blocks) {
		if (!hip->first_blocks) {
			hfs_dbg(EXTENT, "first extents\n");
			/* no extents yet */
			hip->first_extents[0].start_block = cpu_to_be32(start);
			hip->first_extents[0].block_count = cpu_to_be32(len);
			res = 0;
		} else {
			/* try to append to extents in inode */
			res = hfsplus_add_extent(hip->first_extents,
						 hip->alloc_blocks,
						 start, len);
			if (res == -ENOSPC)
				goto insert_extent;
		}
		if (!res) {
			hfsplus_dump_extent(hip->first_extents);
			hip->first_blocks += len;
		}
	} else {
		res = hfsplus_add_extent(hip->cached_extents,
					 hip->alloc_blocks - hip->cached_start,
					 start, len);
		if (!res) {
			hfsplus_dump_extent(hip->cached_extents);
			hip->extent_state |= HFSPLUS_EXT_DIRTY;
			hip->cached_blocks += len;
		} else if (res == -ENOSPC)
			goto insert_extent;
	}
out:
	if (!res) {
		hip->alloc_blocks += len;
		mutex_unlock(&hip->extents_lock);
		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
		return 0;
	}
	mutex_unlock(&hip->extents_lock);
	return res;

insert_extent:
	hfs_dbg(EXTENT, "insert new extent\n");
	res = hfsplus_ext_write_extent_locked(inode);
	if (res)
		goto out;

	memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec));
	hip->cached_extents[0].start_block = cpu_to_be32(start);
	hip->cached_extents[0].block_count = cpu_to_be32(len);
	hfsplus_dump_extent(hip->cached_extents);
	hip->extent_state |= HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW;
	hip->cached_start = hip->alloc_blocks;
	hip->cached_blocks = len;

	res = 0;
	goto out;
}
int __hfsplus_setxattr(struct inode *inode, const char *name,
			const void *value, size_t size, int flags)
{
	int err = 0;
	struct hfs_find_data cat_fd;
	hfsplus_cat_entry entry;
	u16 cat_entry_flags, cat_entry_type;
	u16 folder_finderinfo_len = sizeof(struct DInfo) +
					sizeof(struct DXInfo);
	u16 file_finderinfo_len = sizeof(struct FInfo) +
					sizeof(struct FXInfo);

	if ((!S_ISREG(inode->i_mode) &&
			!S_ISDIR(inode->i_mode)) ||
				HFSPLUS_IS_RSRC(inode))
		return -EOPNOTSUPP;

	err = can_set_xattr(inode, name, value, size);
	if (err)
		return err;

	if (strncmp(name, XATTR_MAC_OSX_PREFIX,
				XATTR_MAC_OSX_PREFIX_LEN) == 0)
		name += XATTR_MAC_OSX_PREFIX_LEN;

	if (value == NULL) {
		value = "";
		size = 0;
	}

	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
	if (err) {
		pr_err("can't init xattr find struct\n");
		return err;
	}

	err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
	if (err) {
		pr_err("catalog searching failed\n");
		goto end_setxattr;
	}

	if (!strcmp_xattr_finder_info(name)) {
		if (flags & XATTR_CREATE) {
			pr_err("xattr exists yet\n");
			err = -EOPNOTSUPP;
			goto end_setxattr;
		}
		hfs_bnode_read(cat_fd.bnode, &entry, cat_fd.entryoffset,
					sizeof(hfsplus_cat_entry));
		if (be16_to_cpu(entry.type) == HFSPLUS_FOLDER) {
			if (size == folder_finderinfo_len) {
				memcpy(&entry.folder.user_info, value,
						folder_finderinfo_len);
				hfs_bnode_write(cat_fd.bnode, &entry,
					cat_fd.entryoffset,
					sizeof(struct hfsplus_cat_folder));
				hfsplus_mark_inode_dirty(inode,
						HFSPLUS_I_CAT_DIRTY);
			} else {
				err = -ERANGE;
				goto end_setxattr;
			}
		} else if (be16_to_cpu(entry.type) == HFSPLUS_FILE) {
			if (size == file_finderinfo_len) {
				memcpy(&entry.file.user_info, value,
						file_finderinfo_len);
				hfs_bnode_write(cat_fd.bnode, &entry,
					cat_fd.entryoffset,
					sizeof(struct hfsplus_cat_file));
				hfsplus_mark_inode_dirty(inode,
						HFSPLUS_I_CAT_DIRTY);
			} else {
				err = -ERANGE;
				goto end_setxattr;
			}
		} else {
			err = -EOPNOTSUPP;
			goto end_setxattr;
		}
		goto end_setxattr;
	}

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

	if (hfsplus_attr_exists(inode, name)) {
		if (flags & XATTR_CREATE) {
			pr_err("xattr exists yet\n");
			err = -EOPNOTSUPP;
			goto end_setxattr;
		}
		err = hfsplus_delete_attr(inode, name);
		if (err)
			goto end_setxattr;
		err = hfsplus_create_attr(inode, name, value, size);
		if (err)
			goto end_setxattr;
	} else {
		if (flags & XATTR_REPLACE) {
			pr_err("cannot replace xattr\n");
			err = -EOPNOTSUPP;
			goto end_setxattr;
		}
		err = hfsplus_create_attr(inode, name, value, size);
		if (err)
			goto end_setxattr;
	}

	cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
	if (cat_entry_type == HFSPLUS_FOLDER) {
		cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
				    cat_fd.entryoffset +
				    offsetof(struct hfsplus_cat_folder, flags));
		cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
		if (!strcmp_xattr_acl(name))
			cat_entry_flags |= HFSPLUS_ACL_EXISTS;
		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
				offsetof(struct hfsplus_cat_folder, flags),
				cat_entry_flags);
		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
	} else if (cat_entry_type == HFSPLUS_FILE) {
Beispiel #7
0
static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
{
	struct hfsplus_vh *vhdr;
	struct hfsplus_sb_info *sbi;
	hfsplus_cat_entry entry;
	struct hfs_find_data fd;
	struct inode *root, *inode;
	struct qstr str;
	struct nls_table *nls = NULL;
	int err;

	err = -EINVAL;
	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
	if (!sbi)
		goto out;

	sb->s_fs_info = sbi;
	mutex_init(&sbi->alloc_mutex);
	mutex_init(&sbi->vh_mutex);
	hfsplus_fill_defaults(sbi);

	err = -EINVAL;
	if (!hfsplus_parse_options(data, sbi)) {
		printk(KERN_ERR "hfs: unable to parse mount options\n");
		goto out_unload_nls;
	}

	/* temporarily use utf8 to correctly find the hidden dir below */
	nls = sbi->nls;
	sbi->nls = load_nls("utf8");
	if (!sbi->nls) {
		printk(KERN_ERR "hfs: unable to load nls for utf8\n");
		goto out_unload_nls;
	}

	/* Grab the volume header */
	if (hfsplus_read_wrapper(sb)) {
		if (!silent)
			printk(KERN_WARNING "hfs: unable to find HFS+ superblock\n");
		goto out_unload_nls;
	}
	vhdr = sbi->s_vhdr;

	/* Copy parts of the volume header into the superblock */
	sb->s_magic = HFSPLUS_VOLHEAD_SIG;
	if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION ||
	    be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) {
		printk(KERN_ERR "hfs: wrong filesystem version\n");
		goto out_free_vhdr;
	}
	sbi->total_blocks = be32_to_cpu(vhdr->total_blocks);
	sbi->free_blocks = be32_to_cpu(vhdr->free_blocks);
	sbi->next_cnid = be32_to_cpu(vhdr->next_cnid);
	sbi->file_count = be32_to_cpu(vhdr->file_count);
	sbi->folder_count = be32_to_cpu(vhdr->folder_count);
	sbi->data_clump_blocks =
		be32_to_cpu(vhdr->data_clump_sz) >> sbi->alloc_blksz_shift;
	if (!sbi->data_clump_blocks)
		sbi->data_clump_blocks = 1;
	sbi->rsrc_clump_blocks =
		be32_to_cpu(vhdr->rsrc_clump_sz) >> sbi->alloc_blksz_shift;
	if (!sbi->rsrc_clump_blocks)
		sbi->rsrc_clump_blocks = 1;

	/* Set up operations so we can load metadata */
	sb->s_op = &hfsplus_sops;
	sb->s_maxbytes = MAX_LFS_FILESIZE;

	if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
		printk(KERN_WARNING "hfs: Filesystem was "
				"not cleanly unmounted, "
				"running fsck.hfsplus is recommended.  "
				"mounting read-only.\n");
		sb->s_flags |= MS_RDONLY;
	} else if (test_and_clear_bit(HFSPLUS_SB_FORCE, &sbi->flags)) {
		/* nothing */
	} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
		printk(KERN_WARNING "hfs: Filesystem is marked locked, mounting read-only.\n");
		sb->s_flags |= MS_RDONLY;
	} else if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) &&
			!(sb->s_flags & MS_RDONLY)) {
		printk(KERN_WARNING "hfs: write access to "
				"a journaled filesystem is not supported, "
				"use the force option at your own risk, "
				"mounting read-only.\n");
		sb->s_flags |= MS_RDONLY;
	}

	/* Load metadata objects (B*Trees) */
	sbi->ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID);
	if (!sbi->ext_tree) {
		printk(KERN_ERR "hfs: failed to load extents file\n");
		goto out_free_vhdr;
	}
	sbi->cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID);
	if (!sbi->cat_tree) {
		printk(KERN_ERR "hfs: failed to load catalog file\n");
		goto out_close_ext_tree;
	}

	inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID);
	if (IS_ERR(inode)) {
		printk(KERN_ERR "hfs: failed to load allocation file\n");
		err = PTR_ERR(inode);
		goto out_close_cat_tree;
	}
	sbi->alloc_file = inode;

	/* Load the root directory */
	root = hfsplus_iget(sb, HFSPLUS_ROOT_CNID);
	if (IS_ERR(root)) {
		printk(KERN_ERR "hfs: failed to load root directory\n");
		err = PTR_ERR(root);
		goto out_put_alloc_file;
	}

	str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
	str.name = HFSP_HIDDENDIR_NAME;
	hfs_find_init(sbi->cat_tree, &fd);
	hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
	if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
		hfs_find_exit(&fd);
		if (entry.type != cpu_to_be16(HFSPLUS_FOLDER))
			goto out_put_root;
		inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id));
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
			goto out_put_root;
		}
		sbi->hidden_dir = inode;
	} else
		hfs_find_exit(&fd);

	if (!(sb->s_flags & MS_RDONLY)) {
		/*
		 * H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused
		 * all three are registered with Apple for our use
		 */
		vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
		vhdr->modify_date = hfsp_now2mt();
		be32_add_cpu(&vhdr->write_count, 1);
		vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
		vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
		hfsplus_sync_fs(sb, 1);

		if (!sbi->hidden_dir) {
			mutex_lock(&sbi->vh_mutex);
			sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
			hfsplus_create_cat(sbi->hidden_dir->i_ino, root, &str,
					   sbi->hidden_dir);
			mutex_unlock(&sbi->vh_mutex);

			hfsplus_mark_inode_dirty(sbi->hidden_dir,
						 HFSPLUS_I_CAT_DIRTY);
		}
	}

	sb->s_d_op = &hfsplus_dentry_operations;
	sb->s_root = d_alloc_root(root);
	if (!sb->s_root) {
		err = -ENOMEM;
		goto out_put_hidden_dir;
	}

	unload_nls(sbi->nls);
	sbi->nls = nls;
	return 0;

out_put_hidden_dir:
	iput(sbi->hidden_dir);
out_put_root:
	iput(root);
out_put_alloc_file:
	iput(sbi->alloc_file);
out_close_cat_tree:
	hfs_btree_close(sbi->cat_tree);
out_close_ext_tree:
	hfs_btree_close(sbi->ext_tree);
out_free_vhdr:
	kfree(sbi->s_vhdr);
	kfree(sbi->s_backup_vhdr);
out_unload_nls:
	unload_nls(sbi->nls);
	unload_nls(nls);
	kfree(sbi);
out:
	return err;
}
Beispiel #8
0
/* 
 * The flags XATTR_REPLACE and XATTR_CREATE
 * specify that an extended attribute must exist and must not exist
 * previous to the call, respectively.
 */
int hfsplus_setxattr(struct dentry *dentry, const char *name,
		     const void *value, size_t size, int flags)
{
	struct inode *inode = dentry->d_inode;
	struct hfs_btree *btree = HFSPLUS_SB(inode->i_sb)->attr_tree;
	struct hfs_find_data fd;
	hfsplus_attr_entry *entry = NULL;
	int res = 0, entry_size = 0;

	dprint(DBG_XATTR, "hfs: setattr [%s][%s] [%lu]\n", dentry->d_name.name, name, inode->i_ino);
	
	if (!value) {
		return -EINVAL;
	}
	if (!strcmp(name, SZ_XATTR_NAME_TYPE) ||
		!strcmp(name, SZ_XATTR_NAME_CREATOR) ||
		!strcmp(name, SZ_XATTR_NAME_FINDRINFO)) {
		return hfsplus_setxattr_buildin(dentry, name, value, size, flags);
	} else if (!strcmp(name, SZ_XATTR_NAME_RFORK)) {
		return -EOPNOTSUPP;
	}

#if 0
#define HFSPLUS_MAX_ATTR_LEN (128*1024)
	if (size > HFSPLUS_MAX_ATTR_LEN) {
		return E2BIG;
	}
#endif
	if (size > hfsplus_get_maxinline_attrsize(btree)) {
		return -E2BIG;
	}

	res = hfs_find_init(btree, &fd);
	if (res) {
		return res;
	}
	hfsplus_attr_build_key(inode->i_sb, fd.search_key, cpu_to_be32((u32)(unsigned long)dentry->d_fsdata), name, 0);
	res = hfs_brec_find(&fd);
	if (res == -ENOENT) {
		if (flags & XATTR_REPLACE) {
			res = -ENODATA;
			goto out;
		}
	} else if (res != 0) {
		goto out;
	} else { // res == 0
		if (flags & XATTR_CREATE) {
			res = -EEXIST;
			goto out;
		}
		if ((res = hfs_brec_remove(&fd))) {
			goto out;
		}
		// decrease record count to avoid the next insert worng.
		--fd.record;
		//hfsplus_mark_inode_dirty(btree->inode, HFSPLUS_I_ATTR_DIRTY);
		//if ((res = filemap_write_and_wait(btree->inode->i_mapping))) {
		//	goto out;
		//}
	}
	entry_size = sizeof(struct hfsplus_attr_data) - 2 + size + ((size & 1) ? 1 : 0);
	entry = kmalloc(entry_size * sizeof(char), GFP_NOFS);
	if (!entry) {
		res = -ENOMEM;
		goto out;
	}
	entry->type = cpu_to_be32(kHFSPlusAttrData);
	entry->data.attr_size = cpu_to_be32(size);
	memcpy(entry->data.attr_data, value, size);
	res = hfs_brec_insert(&fd, entry, entry_size);

	// set has attribute bit on file/folder
	hfsplus_set_cat_flag(inode, HFS_HAS_ATTR_MASK, 1);
	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
	hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY);
	//if ((res = filemap_write_and_wait(btree->inode->i_mapping))) {
	//	goto out;
	//}
out:
	if (entry) {
		kfree(entry);
	}
	hfs_find_exit(&fd);
	return res;
}
Beispiel #9
0
int hfsplus_setxattr_buildin(struct dentry *dentry, const char *name,
		     const void *value, size_t size, int flags)
{
	struct inode *inode = dentry->d_inode;
	struct hfs_btree *btree = HFSPLUS_SB(inode->i_sb)->cat_tree;
	struct hfs_find_data fd;
	hfsplus_cat_entry entry;
	struct hfsplus_cat_file *file;
	char empty_info[32] = {0};
	u8 is_file = 0;
	int res;

	if (HFSPLUS_IS_RSRC(inode))
		return -EOPNOTSUPP;
	res = hfs_find_init(btree, &fd);
	if (res)
		return res;
	res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
	if (res)
		goto out;
	hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
			sizeof(struct hfsplus_cat_file));
	file = &entry.file;
	
	if (entry.type == cpu_to_be16(HFSPLUS_FILE)) {
		is_file = 1;
	}
	res = -ERANGE;
	if (!strcmp(name, SZ_XATTR_NAME_TYPE) && is_file) {
		if (size != 4)
			goto out;
		memcpy(&file->user_info.fdType, value, 4);
	} else if (!strcmp(name, SZ_XATTR_NAME_CREATOR) && is_file) {
		if (size != 4)
			goto out;
		memcpy(&file->user_info.fdCreator, value, 4);
	} else if (!strcmp(name, SZ_XATTR_NAME_FINDRINFO)) {
		if (memcmp(&file->user_info, &empty_info, sizeof(empty_info))) {
			if (flags & XATTR_CREATE) {
				res = -EEXIST;
				goto out;
			}
		} else {
			if (flags & XATTR_REPLACE) {
				res = -ENODATA;
				goto out;
			}
		}
		// reserve the date_added
		memcpy(&((struct FXInfo *)value + sizeof(struct FInfo))->date_added, &file->finder_info.date_added, sizeof(__be32));
		if (size != 32)
			goto out;
		memcpy(&file->user_info, value, 32);
	} else {
		res = -EOPNOTSUPP;
		goto out;
	}
	res = 0;
	hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
			is_file ? sizeof(struct hfsplus_cat_file) : sizeof(struct hfsplus_cat_folder));
	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
	hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
	//if ((res = filemap_write_and_wait(HFSPLUS_SB(inode->i_sb)->cat_tree->inode->i_mapping))) {
	//	goto out;
	//}
out:
	hfs_find_exit(&fd);
	return res;
	
}