diff options
Diffstat (limited to 'fs/hfsplus')
| -rw-r--r-- | fs/hfsplus/acl.h | 9 | ||||
| -rw-r--r-- | fs/hfsplus/attributes.c | 38 | ||||
| -rw-r--r-- | fs/hfsplus/bnode.c | 49 | ||||
| -rw-r--r-- | fs/hfsplus/btree.c | 114 | ||||
| -rw-r--r-- | fs/hfsplus/catalog.c | 41 | ||||
| -rw-r--r-- | fs/hfsplus/dir.c | 34 | ||||
| -rw-r--r-- | fs/hfsplus/extents.c | 33 | ||||
| -rw-r--r-- | fs/hfsplus/hfsplus_fs.h | 216 | ||||
| -rw-r--r-- | fs/hfsplus/hfsplus_raw.h | 18 | ||||
| -rw-r--r-- | fs/hfsplus/inode.c | 88 | ||||
| -rw-r--r-- | fs/hfsplus/options.c | 11 | ||||
| -rw-r--r-- | fs/hfsplus/posix_acl.c | 168 | ||||
| -rw-r--r-- | fs/hfsplus/super.c | 8 | ||||
| -rw-r--r-- | fs/hfsplus/wrapper.c | 29 | ||||
| -rw-r--r-- | fs/hfsplus/xattr.c | 365 | ||||
| -rw-r--r-- | fs/hfsplus/xattr.h | 4 | ||||
| -rw-r--r-- | fs/hfsplus/xattr_security.c | 49 | ||||
| -rw-r--r-- | fs/hfsplus/xattr_trusted.c | 32 | ||||
| -rw-r--r-- | fs/hfsplus/xattr_user.c | 32 | 
19 files changed, 735 insertions, 603 deletions
diff --git a/fs/hfsplus/acl.h b/fs/hfsplus/acl.h index 07c0d494752..95c8ed9ec17 100644 --- a/fs/hfsplus/acl.h +++ b/fs/hfsplus/acl.h @@ -12,16 +12,13 @@  /* posix_acl.c */  struct posix_acl *hfsplus_get_posix_acl(struct inode *inode, int type); -extern int hfsplus_posix_acl_chmod(struct inode *); +int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl, +		int type);  extern int hfsplus_init_posix_acl(struct inode *, struct inode *);  #else  /* CONFIG_HFSPLUS_FS_POSIX_ACL */  #define hfsplus_get_posix_acl NULL - -static inline int hfsplus_posix_acl_chmod(struct inode *inode) -{ -	return 0; -} +#define hfsplus_set_posix_acl NULL  static inline int hfsplus_init_posix_acl(struct inode *inode, struct inode *dir)  { diff --git a/fs/hfsplus/attributes.c b/fs/hfsplus/attributes.c index 0f47890299c..e5b221de7de 100644 --- a/fs/hfsplus/attributes.c +++ b/fs/hfsplus/attributes.c @@ -11,7 +11,7 @@  static struct kmem_cache *hfsplus_attr_tree_cachep; -int hfsplus_create_attr_tree_cache(void) +int __init hfsplus_create_attr_tree_cache(void)  {  	if (hfsplus_attr_tree_cachep)  		return -EEXIST; @@ -54,14 +54,11 @@ int hfsplus_attr_build_key(struct super_block *sb, hfsplus_btree_key *key,  	memset(key, 0, sizeof(struct hfsplus_attr_key));  	key->attr.cnid = cpu_to_be32(cnid);  	if (name) { -		len = strlen(name); -		if (len > HFSPLUS_ATTR_MAX_STRLEN) { -			pr_err("invalid xattr name's length\n"); -			return -EINVAL; -		} -		hfsplus_asc2uni(sb, +		int res = hfsplus_asc2uni(sb,  				(struct hfsplus_unistr *)&key->attr.key_name, -				HFSPLUS_ATTR_MAX_STRLEN, name, len); +				HFSPLUS_ATTR_MAX_STRLEN, name, strlen(name)); +		if (res) +			return res;  		len = be16_to_cpu(key->attr.key_name.length);  	} else {  		key->attr.key_name.length = 0; @@ -82,31 +79,6 @@ int hfsplus_attr_build_key(struct super_block *sb, hfsplus_btree_key *key,  	return 0;  } -void hfsplus_attr_build_key_uni(hfsplus_btree_key *key, -					u32 cnid, -					struct hfsplus_attr_unistr *name) -{ -	int ustrlen; - -	memset(key, 0, sizeof(struct hfsplus_attr_key)); -	ustrlen = be16_to_cpu(name->length); -	key->attr.cnid = cpu_to_be32(cnid); -	key->attr.key_name.length = cpu_to_be16(ustrlen); -	ustrlen *= 2; -	memcpy(key->attr.key_name.unicode, name->unicode, ustrlen); - -	/* The length of the key, as stored in key_len field, does not include -	 * the size of the key_len field itself. -	 * So, offsetof(hfsplus_attr_key, key_name) is a trick because -	 * it takes into consideration key_len field (__be16) of -	 * hfsplus_attr_key structure instead of length field (__be16) of -	 * hfsplus_attr_unistr structure. -	 */ -	key->key_len = -		cpu_to_be16(offsetof(struct hfsplus_attr_key, key_name) + -				ustrlen); -} -  hfsplus_attr_entry *hfsplus_alloc_attr_entry(void)  {  	return kmem_cache_alloc(hfsplus_attr_tree_cachep, GFP_KERNEL); diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c index 11c86020452..759708fd933 100644 --- a/fs/hfsplus/bnode.c +++ b/fs/hfsplus/bnode.c @@ -27,13 +27,13 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)  	pagep = node->page + (off >> PAGE_CACHE_SHIFT);  	off &= ~PAGE_CACHE_MASK; -	l = min(len, (int)PAGE_CACHE_SIZE - off); +	l = min_t(int, len, PAGE_CACHE_SIZE - off);  	memcpy(buf, kmap(*pagep) + off, l);  	kunmap(*pagep);  	while ((len -= l) != 0) {  		buf += l; -		l = min(len, (int)PAGE_CACHE_SIZE); +		l = min_t(int, len, PAGE_CACHE_SIZE);  		memcpy(buf, kmap(*++pagep), l);  		kunmap(*pagep);  	} @@ -80,14 +80,14 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)  	pagep = node->page + (off >> PAGE_CACHE_SHIFT);  	off &= ~PAGE_CACHE_MASK; -	l = min(len, (int)PAGE_CACHE_SIZE - off); +	l = min_t(int, len, PAGE_CACHE_SIZE - off);  	memcpy(kmap(*pagep) + off, buf, l);  	set_page_dirty(*pagep);  	kunmap(*pagep);  	while ((len -= l) != 0) {  		buf += l; -		l = min(len, (int)PAGE_CACHE_SIZE); +		l = min_t(int, len, PAGE_CACHE_SIZE);  		memcpy(kmap(*++pagep), buf, l);  		set_page_dirty(*pagep);  		kunmap(*pagep); @@ -110,13 +110,13 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)  	pagep = node->page + (off >> PAGE_CACHE_SHIFT);  	off &= ~PAGE_CACHE_MASK; -	l = min(len, (int)PAGE_CACHE_SIZE - off); +	l = min_t(int, len, PAGE_CACHE_SIZE - off);  	memset(kmap(*pagep) + off, 0, l);  	set_page_dirty(*pagep);  	kunmap(*pagep);  	while ((len -= l) != 0) { -		l = min(len, (int)PAGE_CACHE_SIZE); +		l = min_t(int, len, PAGE_CACHE_SIZE);  		memset(kmap(*++pagep), 0, l);  		set_page_dirty(*pagep);  		kunmap(*pagep); @@ -142,14 +142,14 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,  	dst &= ~PAGE_CACHE_MASK;  	if (src == dst) { -		l = min(len, (int)PAGE_CACHE_SIZE - src); +		l = min_t(int, len, PAGE_CACHE_SIZE - src);  		memcpy(kmap(*dst_page) + src, kmap(*src_page) + src, l);  		kunmap(*src_page);  		set_page_dirty(*dst_page);  		kunmap(*dst_page);  		while ((len -= l) != 0) { -			l = min(len, (int)PAGE_CACHE_SIZE); +			l = min_t(int, len, PAGE_CACHE_SIZE);  			memcpy(kmap(*++dst_page), kmap(*++src_page), l);  			kunmap(*src_page);  			set_page_dirty(*dst_page); @@ -251,7 +251,7 @@ void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)  		dst &= ~PAGE_CACHE_MASK;  		if (src == dst) { -			l = min(len, (int)PAGE_CACHE_SIZE - src); +			l = min_t(int, len, PAGE_CACHE_SIZE - src);  			memmove(kmap(*dst_page) + src,  				kmap(*src_page) + src, l);  			kunmap(*src_page); @@ -259,7 +259,7 @@ void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)  			kunmap(*dst_page);  			while ((len -= l) != 0) { -				l = min(len, (int)PAGE_CACHE_SIZE); +				l = min_t(int, len, PAGE_CACHE_SIZE);  				memmove(kmap(*++dst_page),  					kmap(*++src_page), l);  				kunmap(*src_page); @@ -386,9 +386,8 @@ struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid)  	struct hfs_bnode *node;  	if (cnid >= tree->node_count) { -		pr_err("request for non-existent node " -				"%d in B*Tree\n", -			cnid); +		pr_err("request for non-existent node %d in B*Tree\n", +		       cnid);  		return NULL;  	} @@ -409,9 +408,8 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)  	loff_t off;  	if (cnid >= tree->node_count) { -		pr_err("request for non-existent node " -				"%d in B*Tree\n", -			cnid); +		pr_err("request for non-existent node %d in B*Tree\n", +		       cnid);  		return NULL;  	} @@ -602,7 +600,7 @@ struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num)  	pagep = node->page;  	memset(kmap(*pagep) + node->page_offset, 0, -	       min((int)PAGE_CACHE_SIZE, (int)tree->node_size)); +	       min_t(int, PAGE_CACHE_SIZE, tree->node_size));  	set_page_dirty(*pagep);  	kunmap(*pagep);  	for (i = 1; i < tree->pages_per_bnode; i++) { @@ -648,8 +646,8 @@ void hfs_bnode_put(struct hfs_bnode *node)  		if (test_bit(HFS_BNODE_DELETED, &node->flags)) {  			hfs_bnode_unhash(node);  			spin_unlock(&tree->hash_lock); -			hfs_bnode_clear(node, 0, -				PAGE_CACHE_SIZE * tree->pages_per_bnode); +			if (hfs_bnode_need_zeroout(tree)) +				hfs_bnode_clear(node, 0, tree->node_size);  			hfs_bmap_free(node);  			hfs_bnode_free(node);  			return; @@ -658,3 +656,16 @@ void hfs_bnode_put(struct hfs_bnode *node)  	}  } +/* + * Unused nodes have to be zeroed if this is the catalog tree and + * a corresponding flag in the volume header is set. + */ +bool hfs_bnode_need_zeroout(struct hfs_btree *tree) +{ +	struct super_block *sb = tree->inode->i_sb; +	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); +	const u32 volume_attr = be32_to_cpu(sbi->s_vhdr->attributes); + +	return tree->cnid == HFSPLUS_CAT_CNID && +		volume_attr & HFSPLUS_VOL_UNUSED_NODE_FIX; +} diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 0c6540c9116..3345c7553ed 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -15,6 +15,118 @@  #include "hfsplus_fs.h"  #include "hfsplus_raw.h" +/* + * Initial source code of clump size calculation is gotten + * from http://opensource.apple.com/tarballs/diskdev_cmds/ + */ +#define CLUMP_ENTRIES	15 + +static short clumptbl[CLUMP_ENTRIES * 3] = { +/* + *	    Volume	Attributes	 Catalog	 Extents + *	     Size	Clump (MB)	Clump (MB)	Clump (MB) + */ +	/*   1GB */	  4,		  4,		 4, +	/*   2GB */	  6,		  6,		 4, +	/*   4GB */	  8,		  8,		 4, +	/*   8GB */	 11,		 11,		 5, +	/* +	 * For volumes 16GB and larger, we want to make sure that a full OS +	 * install won't require fragmentation of the Catalog or Attributes +	 * B-trees.  We do this by making the clump sizes sufficiently large, +	 * and by leaving a gap after the B-trees for them to grow into. +	 * +	 * For SnowLeopard 10A298, a FullNetInstall with all packages selected +	 * results in: +	 * Catalog B-tree Header +	 *	nodeSize:          8192 +	 *	totalNodes:       31616 +	 *	freeNodes:         1978 +	 * (used = 231.55 MB) +	 * Attributes B-tree Header +	 *	nodeSize:          8192 +	 *	totalNodes:       63232 +	 *	freeNodes:          958 +	 * (used = 486.52 MB) +	 * +	 * We also want Time Machine backup volumes to have a sufficiently +	 * large clump size to reduce fragmentation. +	 * +	 * The series of numbers for Catalog and Attribute form a geometric +	 * series. For Catalog (16GB to 512GB), each term is 8**(1/5) times +	 * the previous term.  For Attributes (16GB to 512GB), each term is +	 * 4**(1/5) times the previous term.  For 1TB to 16TB, each term is +	 * 2**(1/5) times the previous term. +	 */ +	/*  16GB */	 64,		 32,		 5, +	/*  32GB */	 84,		 49,		 6, +	/*  64GB */	111,		 74,		 7, +	/* 128GB */	147,		111,		 8, +	/* 256GB */	194,		169,		 9, +	/* 512GB */	256,		256,		11, +	/*   1TB */	294,		294,		14, +	/*   2TB */	338,		338,		16, +	/*   4TB */	388,		388,		20, +	/*   8TB */	446,		446,		25, +	/*  16TB */	512,		512,		32 +}; + +u32 hfsplus_calc_btree_clump_size(u32 block_size, u32 node_size, +					u64 sectors, int file_id) +{ +	u32 mod = max(node_size, block_size); +	u32 clump_size; +	int column; +	int i; + +	/* Figure out which column of the above table to use for this file. */ +	switch (file_id) { +	case HFSPLUS_ATTR_CNID: +		column = 0; +		break; +	case HFSPLUS_CAT_CNID: +		column = 1; +		break; +	default: +		column = 2; +		break; +	} + +	/* +	 * The default clump size is 0.8% of the volume size. And +	 * it must also be a multiple of the node and block size. +	 */ +	if (sectors < 0x200000) { +		clump_size = sectors << 2;	/*  0.8 %  */ +		if (clump_size < (8 * node_size)) +			clump_size = 8 * node_size; +	} else { +		/* turn exponent into table index... */ +		for (i = 0, sectors = sectors >> 22; +		     sectors && (i < CLUMP_ENTRIES - 1); +		     ++i, sectors = sectors >> 1) { +			/* empty body */ +		} + +		clump_size = clumptbl[column + (i) * 3] * 1024 * 1024; +	} + +	/* +	 * Round the clump size to a multiple of node and block size. +	 * NOTE: This rounds down. +	 */ +	clump_size /= mod; +	clump_size *= mod; + +	/* +	 * Rounding down could have rounded down to 0 if the block size was +	 * greater than the clump size.  If so, just use one block or node. +	 */ +	if (clump_size == 0) +		clump_size = mod; + +	return clump_size; +}  /* Get a reference to a B*Tree and do some initial checks */  struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) @@ -246,7 +358,7 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)  		u32 count;  		int res; -		res = hfsplus_file_extend(inode); +		res = hfsplus_file_extend(inode, hfs_bnode_need_zeroout(tree));  		if (res)  			return ERR_PTR(res);  		hip->phys_size = inode->i_size = diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c index 968ce411db5..32602c667b4 100644 --- a/fs/hfsplus/catalog.c +++ b/fs/hfsplus/catalog.c @@ -103,6 +103,8 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry,  		folder = &entry->folder;  		memset(folder, 0, sizeof(*folder));  		folder->type = cpu_to_be16(HFSPLUS_FOLDER); +		if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) +			folder->flags |= cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT);  		folder->id = cpu_to_be32(inode->i_ino);  		HFSPLUS_I(inode)->create_date =  			folder->create_date = @@ -203,6 +205,36 @@ int hfsplus_find_cat(struct super_block *sb, u32 cnid,  	return hfs_brec_find(fd, hfs_find_rec_by_key);  } +static void hfsplus_subfolders_inc(struct inode *dir) +{ +	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); + +	if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) { +		/* +		 * Increment subfolder count. Note, the value is only meaningful +		 * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set. +		 */ +		HFSPLUS_I(dir)->subfolders++; +	} +} + +static void hfsplus_subfolders_dec(struct inode *dir) +{ +	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); + +	if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) { +		/* +		 * Decrement subfolder count. Note, the value is only meaningful +		 * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set. +		 * +		 * Check for zero. Some subfolders may have been created +		 * by an implementation ignorant of this counter. +		 */ +		if (HFSPLUS_I(dir)->subfolders) +			HFSPLUS_I(dir)->subfolders--; +	} +} +  int hfsplus_create_cat(u32 cnid, struct inode *dir,  		struct qstr *str, struct inode *inode)  { @@ -247,6 +279,8 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,  		goto err1;  	dir->i_size++; +	if (S_ISDIR(inode->i_mode)) +		hfsplus_subfolders_inc(dir);  	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;  	hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY); @@ -336,6 +370,8 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)  		goto out;  	dir->i_size--; +	if (type == HFSPLUS_FOLDER) +		hfsplus_subfolders_dec(dir);  	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;  	hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY); @@ -380,6 +416,7 @@ int hfsplus_rename_cat(u32 cnid,  	hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,  				src_fd.entrylength); +	type = be16_to_cpu(entry.type);  	/* create new dir entry with the data from the old entry */  	hfsplus_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name); @@ -394,6 +431,8 @@ int hfsplus_rename_cat(u32 cnid,  	if (err)  		goto out;  	dst_dir->i_size++; +	if (type == HFSPLUS_FOLDER) +		hfsplus_subfolders_inc(dst_dir);  	dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC;  	/* finally remove the old entry */ @@ -405,6 +444,8 @@ int hfsplus_rename_cat(u32 cnid,  	if (err)  		goto out;  	src_dir->i_size--; +	if (type == HFSPLUS_FOLDER) +		hfsplus_subfolders_dec(src_dir);  	src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC;  	/* remove old thread entry */ diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 4a4fea00267..610a3260bef 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -12,6 +12,7 @@  #include <linux/fs.h>  #include <linux/slab.h>  #include <linux/random.h> +#include <linux/nls.h>  #include "hfsplus_fs.h"  #include "hfsplus_raw.h" @@ -127,7 +128,7 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx)  	struct inode *inode = file_inode(file);  	struct super_block *sb = inode->i_sb;  	int len, err; -	char strbuf[HFSPLUS_MAX_STRLEN + 1]; +	char *strbuf;  	hfsplus_cat_entry entry;  	struct hfs_find_data fd;  	struct hfsplus_readdir_data *rd; @@ -139,6 +140,11 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx)  	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);  	if (err)  		return err; +	strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN + 1, GFP_KERNEL); +	if (!strbuf) { +		err = -ENOMEM; +		goto out; +	}  	hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);  	err = hfs_brec_find(&fd, hfs_find_rec_by_key);  	if (err) @@ -193,7 +199,7 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx)  		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,  			fd.entrylength);  		type = be16_to_cpu(entry.type); -		len = HFSPLUS_MAX_STRLEN; +		len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN;  		err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);  		if (err)  			goto out; @@ -212,13 +218,31 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx)  				    be32_to_cpu(entry.folder.id), DT_DIR))  				break;  		} else if (type == HFSPLUS_FILE) { +			u16 mode; +			unsigned type = DT_UNKNOWN; +  			if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {  				pr_err("small file entry\n");  				err = -EIO;  				goto out;  			} + +			mode = be16_to_cpu(entry.file.permissions.mode); +			if (S_ISREG(mode)) +				type = DT_REG; +			else if (S_ISLNK(mode)) +				type = DT_LNK; +			else if (S_ISFIFO(mode)) +				type = DT_FIFO; +			else if (S_ISCHR(mode)) +				type = DT_CHR; +			else if (S_ISBLK(mode)) +				type = DT_BLK; +			else if (S_ISSOCK(mode)) +				type = DT_SOCK; +  			if (!dir_emit(ctx, strbuf, len, -				    be32_to_cpu(entry.file.id), DT_REG)) +				      be32_to_cpu(entry.file.id), type))  				break;  		} else {  			pr_err("bad catalog entry type\n"); @@ -246,6 +270,7 @@ next:  	}  	memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));  out: +	kfree(strbuf);  	hfs_find_exit(&fd);  	return err;  } @@ -529,9 +554,10 @@ const struct inode_operations hfsplus_dir_inode_operations = {  	.setxattr		= generic_setxattr,  	.getxattr		= generic_getxattr,  	.listxattr		= hfsplus_listxattr, -	.removexattr		= hfsplus_removexattr, +	.removexattr		= generic_removexattr,  #ifdef CONFIG_HFSPLUS_FS_POSIX_ACL  	.get_acl		= hfsplus_get_posix_acl, +	.set_acl		= hfsplus_set_posix_acl,  #endif  }; diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c index fbb212fbb1e..feca524ce2a 100644 --- a/fs/hfsplus/extents.c +++ b/fs/hfsplus/extents.c @@ -227,17 +227,15 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock,  	u32 ablock, dblock, mask;  	sector_t sector;  	int was_dirty = 0; -	int shift;  	/* Convert inode block to disk allocation block */ -	shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits;  	ablock = iblock >> sbi->fs_shift;  	if (iblock >= hip->fs_blocks) {  		if (iblock > hip->fs_blocks || !create)  			return -EIO;  		if (ablock >= hip->alloc_blocks) { -			res = hfsplus_file_extend(inode); +			res = hfsplus_file_extend(inode, false);  			if (res)  				return res;  		} @@ -427,7 +425,7 @@ int hfsplus_free_fork(struct super_block *sb, u32 cnid,  	return res;  } -int hfsplus_file_extend(struct inode *inode) +int hfsplus_file_extend(struct inode *inode, bool zeroout)  {  	struct super_block *sb = inode->i_sb;  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); @@ -438,10 +436,9 @@ int hfsplus_file_extend(struct inode *inode)  	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); +		pr_err("extend alloc file! (%llu,%u,%u)\n", +		       sbi->alloc_file->i_size * 8, +		       sbi->total_blocks, sbi->free_blocks);  		return -ENOSPC;  	} @@ -465,6 +462,12 @@ int hfsplus_file_extend(struct inode *inode)  		}  	} +	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) { @@ -498,11 +501,13 @@ int hfsplus_file_extend(struct inode *inode)  			goto insert_extent;  	}  out: -	mutex_unlock(&hip->extents_lock);  	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: @@ -556,11 +561,13 @@ void hfsplus_file_truncate(struct inode *inode)  	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; +		goto out_unlock; -	mutex_lock(&hip->extents_lock);  	res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);  	if (res) {  		mutex_unlock(&hip->extents_lock); @@ -592,10 +599,10 @@ void hfsplus_file_truncate(struct inode *inode)  		hfs_brec_remove(&fd);  	}  	hfs_find_exit(&fd); -	mutex_unlock(&hip->extents_lock);  	hip->alloc_blocks = blk_cnt; -out: +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; diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 2b9cd01696e..eb5e059f481 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -127,6 +127,14 @@ struct hfs_bnode {  #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; @@ -233,6 +242,7 @@ struct hfsplus_inode_info {  	 */  	sector_t fs_blocks;  	u8 userflags;		/* BSD user file flags */ +	u32 subfolders;		/* Subfolder count (HFSX only) */  	struct list_head open_dir_list;  	loff_t phys_size; @@ -357,115 +367,121 @@ typedef int (*search_strategy_t)(struct hfs_bnode *,   */  /* attributes.c */ -int hfsplus_create_attr_tree_cache(void); +int __init hfsplus_create_attr_tree_cache(void);  void hfsplus_destroy_attr_tree_cache(void); +int hfsplus_attr_bin_cmp_key(const hfsplus_btree_key *k1, +			     const hfsplus_btree_key *k2); +int hfsplus_attr_build_key(struct super_block *sb, hfsplus_btree_key *key, +			   u32 cnid, const char *name);  hfsplus_attr_entry *hfsplus_alloc_attr_entry(void); -void hfsplus_destroy_attr_entry(hfsplus_attr_entry *entry_p); -int hfsplus_attr_bin_cmp_key(const hfsplus_btree_key *, -		const hfsplus_btree_key *); -int hfsplus_attr_build_key(struct super_block *, hfsplus_btree_key *, -			u32, const char *); -void hfsplus_attr_build_key_uni(hfsplus_btree_key *key, -					u32 cnid, -					struct hfsplus_attr_unistr *name); -int hfsplus_find_attr(struct super_block *, u32, -			const char *, struct hfs_find_data *); +void hfsplus_destroy_attr_entry(hfsplus_attr_entry *entry); +int hfsplus_find_attr(struct super_block *sb, u32 cnid, const char *name, +		      struct hfs_find_data *fd);  int hfsplus_attr_exists(struct inode *inode, const char *name); -int hfsplus_create_attr(struct inode *, const char *, const void *, size_t); -int hfsplus_delete_attr(struct inode *, const char *); +int hfsplus_create_attr(struct inode *inode, const char *name, +			const void *value, size_t size); +int hfsplus_delete_attr(struct inode *inode, const char *name);  int hfsplus_delete_all_attrs(struct inode *dir, u32 cnid);  /* bitmap.c */ -int hfsplus_block_allocate(struct super_block *, u32, u32, u32 *); -int hfsplus_block_free(struct super_block *, u32, u32); +int hfsplus_block_allocate(struct super_block *sb, u32 size, u32 offset, +			   u32 *max); +int hfsplus_block_free(struct super_block *sb, u32 offset, u32 count);  /* btree.c */ -struct hfs_btree *hfs_btree_open(struct super_block *, u32); -void hfs_btree_close(struct hfs_btree *); -int hfs_btree_write(struct hfs_btree *); -struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *); -void hfs_bmap_free(struct hfs_bnode *); +u32 hfsplus_calc_btree_clump_size(u32 block_size, u32 node_size, u64 sectors, +				  int file_id); +struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id); +void hfs_btree_close(struct hfs_btree *tree); +int hfs_btree_write(struct hfs_btree *tree); +struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree); +void hfs_bmap_free(struct hfs_bnode *node);  /* bnode.c */ -void hfs_bnode_read(struct hfs_bnode *, void *, int, int); -u16 hfs_bnode_read_u16(struct hfs_bnode *, int); -u8 hfs_bnode_read_u8(struct hfs_bnode *, int); -void hfs_bnode_read_key(struct hfs_bnode *, void *, int); -void hfs_bnode_write(struct hfs_bnode *, void *, int, int); -void hfs_bnode_write_u16(struct hfs_bnode *, int, u16); -void hfs_bnode_clear(struct hfs_bnode *, int, int); -void hfs_bnode_copy(struct hfs_bnode *, int, -		    struct hfs_bnode *, int, int); -void hfs_bnode_move(struct hfs_bnode *, int, int, int); -void hfs_bnode_dump(struct hfs_bnode *); -void hfs_bnode_unlink(struct hfs_bnode *); -struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *, u32); -struct hfs_bnode *hfs_bnode_find(struct hfs_btree *, u32); -void hfs_bnode_unhash(struct hfs_bnode *); -void hfs_bnode_free(struct hfs_bnode *); -struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32); -void hfs_bnode_get(struct hfs_bnode *); -void hfs_bnode_put(struct hfs_bnode *); +void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len); +u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off); +u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off); +void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off); +void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len); +void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data); +void hfs_bnode_clear(struct hfs_bnode *node, int off, int len); +void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst, +		    struct hfs_bnode *src_node, int src, int len); +void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len); +void hfs_bnode_dump(struct hfs_bnode *node); +void hfs_bnode_unlink(struct hfs_bnode *node); +struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid); +void hfs_bnode_unhash(struct hfs_bnode *node); +struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num); +void hfs_bnode_free(struct hfs_bnode *node); +struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num); +void hfs_bnode_get(struct hfs_bnode *node); +void hfs_bnode_put(struct hfs_bnode *node); +bool hfs_bnode_need_zeroout(struct hfs_btree *tree);  /* brec.c */ -u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *); -u16 hfs_brec_keylen(struct hfs_bnode *, u16); -int hfs_brec_insert(struct hfs_find_data *, void *, int); -int hfs_brec_remove(struct hfs_find_data *); +u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off); +u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec); +int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len); +int hfs_brec_remove(struct hfs_find_data *fd);  /* bfind.c */ -int hfs_find_init(struct hfs_btree *, struct hfs_find_data *); -void hfs_find_exit(struct hfs_find_data *); -int hfs_find_1st_rec_by_cnid(struct hfs_bnode *, -				struct hfs_find_data *, -				int *, int *, int *); -int hfs_find_rec_by_key(struct hfs_bnode *, -				struct hfs_find_data *, -				int *, int *, int *); -int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *, -				search_strategy_t); -int hfs_brec_find(struct hfs_find_data *, search_strategy_t); -int hfs_brec_read(struct hfs_find_data *, void *, int); -int hfs_brec_goto(struct hfs_find_data *, int); +int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd); +void hfs_find_exit(struct hfs_find_data *fd); +int hfs_find_1st_rec_by_cnid(struct hfs_bnode *bnode, struct hfs_find_data *fd, +			     int *begin, int *end, int *cur_rec); +int hfs_find_rec_by_key(struct hfs_bnode *bnode, struct hfs_find_data *fd, +			int *begin, int *end, int *cur_rec); +int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd, +		    search_strategy_t rec_found); +int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare); +int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len); +int hfs_brec_goto(struct hfs_find_data *fd, int cnt);  /* catalog.c */ -int hfsplus_cat_case_cmp_key(const hfsplus_btree_key *, -		const hfsplus_btree_key *); -int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *, -		const hfsplus_btree_key *); -void hfsplus_cat_build_key(struct super_block *sb, -		hfsplus_btree_key *, u32, struct qstr *); -int hfsplus_find_cat(struct super_block *, u32, struct hfs_find_data *); -int hfsplus_create_cat(u32, struct inode *, struct qstr *, struct inode *); -int hfsplus_delete_cat(u32, struct inode *, struct qstr *); -int hfsplus_rename_cat(u32, struct inode *, struct qstr *, -		       struct inode *, struct qstr *); +int hfsplus_cat_case_cmp_key(const hfsplus_btree_key *k1, +			     const hfsplus_btree_key *k2); +int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1, +			    const hfsplus_btree_key *k2); +void hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *key, +			   u32 parent, struct qstr *str);  void hfsplus_cat_set_perms(struct inode *inode, struct hfsplus_perm *perms); +int hfsplus_find_cat(struct super_block *sb, u32 cnid, +		     struct hfs_find_data *fd); +int hfsplus_create_cat(u32 cnid, struct inode *dir, struct qstr *str, +		       struct inode *inode); +int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str); +int hfsplus_rename_cat(u32 cnid, struct inode *src_dir, struct qstr *src_name, +		       struct inode *dst_dir, struct qstr *dst_name);  /* dir.c */  extern const struct inode_operations hfsplus_dir_inode_operations;  extern const struct file_operations hfsplus_dir_operations;  /* extents.c */ -int hfsplus_ext_cmp_key(const hfsplus_btree_key *, const hfsplus_btree_key *); -int hfsplus_ext_write_extent(struct inode *); -int hfsplus_get_block(struct inode *, sector_t, struct buffer_head *, int); -int hfsplus_free_fork(struct super_block *, u32, -		struct hfsplus_fork_raw *, int); -int hfsplus_file_extend(struct inode *); -void hfsplus_file_truncate(struct inode *); +int hfsplus_ext_cmp_key(const hfsplus_btree_key *k1, +			const hfsplus_btree_key *k2); +int hfsplus_ext_write_extent(struct inode *inode); +int hfsplus_get_block(struct inode *inode, sector_t iblock, +		      struct buffer_head *bh_result, int create); +int hfsplus_free_fork(struct super_block *sb, u32 cnid, +		      struct hfsplus_fork_raw *fork, int type); +int hfsplus_file_extend(struct inode *inode, bool zeroout); +void hfsplus_file_truncate(struct inode *inode);  /* inode.c */  extern const struct address_space_operations hfsplus_aops;  extern const struct address_space_operations hfsplus_btree_aops;  extern const struct dentry_operations hfsplus_dentry_operations; -void hfsplus_inode_read_fork(struct inode *, struct hfsplus_fork_raw *); -void hfsplus_inode_write_fork(struct inode *, struct hfsplus_fork_raw *); -int hfsplus_cat_read_inode(struct inode *, struct hfs_find_data *); -int hfsplus_cat_write_inode(struct inode *); -struct inode *hfsplus_new_inode(struct super_block *, umode_t); -void hfsplus_delete_inode(struct inode *); +struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode); +void hfsplus_delete_inode(struct inode *inode); +void hfsplus_inode_read_fork(struct inode *inode, +			     struct hfsplus_fork_raw *fork); +void hfsplus_inode_write_fork(struct inode *inode, +			      struct hfsplus_fork_raw *fork); +int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd); +int hfsplus_cat_write_inode(struct inode *inode);  int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,  		       int datasync); @@ -473,13 +489,17 @@ int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,  long hfsplus_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);  /* options.c */ -int hfsplus_parse_options(char *, struct hfsplus_sb_info *); +void hfsplus_fill_defaults(struct hfsplus_sb_info *opts);  int hfsplus_parse_options_remount(char *input, int *force); -void hfsplus_fill_defaults(struct hfsplus_sb_info *); -int hfsplus_show_options(struct seq_file *, struct dentry *); +int hfsplus_parse_options(char *input, struct hfsplus_sb_info *sbi); +int hfsplus_show_options(struct seq_file *seq, struct dentry *root); + +/* part_tbl.c */ +int hfs_part_find(struct super_block *sb, sector_t *part_start, +		  sector_t *part_size);  /* super.c */ -struct inode *hfsplus_iget(struct super_block *, unsigned long); +struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino);  void hfsplus_mark_mdb_dirty(struct super_block *sb);  /* tables.c */ @@ -488,23 +508,23 @@ extern u16 hfsplus_decompose_table[];  extern u16 hfsplus_compose_table[];  /* unicode.c */ -int hfsplus_strcasecmp(const struct hfsplus_unistr *, -		const struct hfsplus_unistr *); -int hfsplus_strcmp(const struct hfsplus_unistr *, -		const struct hfsplus_unistr *); -int hfsplus_uni2asc(struct super_block *, -		const struct hfsplus_unistr *, char *, int *); -int hfsplus_asc2uni(struct super_block *, -		struct hfsplus_unistr *, int, const char *, int); +int hfsplus_strcasecmp(const struct hfsplus_unistr *s1, +		       const struct hfsplus_unistr *s2); +int hfsplus_strcmp(const struct hfsplus_unistr *s1, +		   const struct hfsplus_unistr *s2); +int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, +		    char *astr, int *len_p); +int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, +		    int max_unistr_len, const char *astr, int len);  int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str); -int hfsplus_compare_dentry(const struct dentry *parent, const struct dentry *dentry, -		unsigned int len, const char *str, const struct qstr *name); +int hfsplus_compare_dentry(const struct dentry *parent, +			   const struct dentry *dentry, unsigned int len, +			   const char *str, const struct qstr *name);  /* wrapper.c */ -int hfsplus_read_wrapper(struct super_block *); -int hfs_part_find(struct super_block *, sector_t *, sector_t *); -int hfsplus_submit_bio(struct super_block *sb, sector_t sector, -		void *buf, void **data, int rw); +int hfsplus_submit_bio(struct super_block *sb, sector_t sector, void *buf, +		       void **data, int rw); +int hfsplus_read_wrapper(struct super_block *sb);  /* time macros */  #define __hfsp_mt2ut(t)		(be32_to_cpu(t) - 2082844800U) diff --git a/fs/hfsplus/hfsplus_raw.h b/fs/hfsplus/hfsplus_raw.h index 452ede01b03..8298d0985f8 100644 --- a/fs/hfsplus/hfsplus_raw.h +++ b/fs/hfsplus/hfsplus_raw.h @@ -144,6 +144,7 @@ struct hfsplus_vh {  #define HFSPLUS_VOL_NODEID_REUSED	(1 << 12)  #define HFSPLUS_VOL_JOURNALED		(1 << 13)  #define HFSPLUS_VOL_SOFTLOCK		(1 << 15) +#define HFSPLUS_VOL_UNUSED_NODE_FIX	(1 << 31)  /* HFS+ BTree node descriptor */  struct hfs_bnode_desc { @@ -156,10 +157,10 @@ struct hfs_bnode_desc {  } __packed;  /* HFS+ BTree node types */ -#define HFS_NODE_INDEX	0x00 -#define HFS_NODE_HEADER	0x01 -#define HFS_NODE_MAP	0x02 -#define HFS_NODE_LEAF	0xFF +#define HFS_NODE_INDEX	0x00	/* An internal (index) node */ +#define HFS_NODE_HEADER	0x01	/* The tree header node (node 0) */ +#define HFS_NODE_MAP	0x02	/* Holds part of the bitmap of used nodes */ +#define HFS_NODE_LEAF	0xFF	/* A leaf (ndNHeight==1) node */  /* HFS+ BTree header */  struct hfs_btree_header_rec { @@ -187,6 +188,9 @@ struct hfs_btree_header_rec {  /* HFS+ BTree misc info */  #define HFSPLUS_TREE_HEAD 0  #define HFSPLUS_NODE_MXSZ 32768 +#define HFSPLUS_ATTR_TREE_NODE_SIZE		8192 +#define HFSPLUS_BTREE_HDR_NODE_RECS_COUNT	3 +#define HFSPLUS_BTREE_HDR_USER_BYTES		128  /* Some special File ID numbers (stolen from hfs.h) */  #define HFSPLUS_POR_CNID		1	/* Parent Of the Root */ @@ -258,7 +262,7 @@ struct hfsplus_cat_folder {  	struct DInfo user_info;  	struct DXInfo finder_info;  	__be32 text_encoding; -	u32 reserved; +	__be32 subfolders;	/* Subfolder count in HFSX. Reserved in HFS+. */  } __packed;  /* HFS file info (stolen from hfs.h) */ @@ -298,11 +302,13 @@ struct hfsplus_cat_file {  	struct hfsplus_fork_raw rsrc_fork;  } __packed; -/* File attribute bits */ +/* File and folder flag bits */  #define HFSPLUS_FILE_LOCKED		0x0001  #define HFSPLUS_FILE_THREAD_EXISTS	0x0002  #define HFSPLUS_XATTR_EXISTS		0x0004  #define HFSPLUS_ACL_EXISTS		0x0008 +#define HFSPLUS_HAS_FOLDER_COUNT	0x0010	/* Folder has subfolder count +						 * (HFSX only) */  /* HFS+ catalog thread (part of a cat_entry) */  struct hfsplus_cat_thread { diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 37213d075f3..0cf786f2d04 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -123,14 +123,15 @@ static int hfsplus_releasepage(struct page *page, gfp_t mask)  }  static ssize_t hfsplus_direct_IO(int rw, struct kiocb *iocb, -		const struct iovec *iov, loff_t offset, unsigned long nr_segs) +		struct iov_iter *iter, loff_t offset)  {  	struct file *file = iocb->ki_filp;  	struct address_space *mapping = file->f_mapping;  	struct inode *inode = file_inode(file)->i_mapping->host; +	size_t count = iov_iter_count(iter);  	ssize_t ret; -	ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, +	ret = blockdev_direct_IO(rw, iocb, inode, iter, offset,   				 hfsplus_get_block);  	/* @@ -139,7 +140,7 @@ static ssize_t hfsplus_direct_IO(int rw, struct kiocb *iocb,  	 */  	if (unlikely((rw & WRITE) && ret < 0)) {  		loff_t isize = i_size_read(inode); -		loff_t end = offset + iov_length(iov, nr_segs); +		loff_t end = offset + count;  		if (end > isize)  			hfsplus_write_failed(mapping, end); @@ -178,64 +179,6 @@ const struct dentry_operations hfsplus_dentry_operations = {  	.d_compare    = hfsplus_compare_dentry,  }; -static struct dentry *hfsplus_file_lookup(struct inode *dir, -		struct dentry *dentry, unsigned int flags) -{ -	struct hfs_find_data fd; -	struct super_block *sb = dir->i_sb; -	struct inode *inode = NULL; -	struct hfsplus_inode_info *hip; -	int err; - -	if (HFSPLUS_IS_RSRC(dir) || strcmp(dentry->d_name.name, "rsrc")) -		goto out; - -	inode = HFSPLUS_I(dir)->rsrc_inode; -	if (inode) -		goto out; - -	inode = new_inode(sb); -	if (!inode) -		return ERR_PTR(-ENOMEM); - -	hip = HFSPLUS_I(inode); -	inode->i_ino = dir->i_ino; -	INIT_LIST_HEAD(&hip->open_dir_list); -	mutex_init(&hip->extents_lock); -	hip->extent_state = 0; -	hip->flags = 0; -	hip->userflags = 0; -	set_bit(HFSPLUS_I_RSRC, &hip->flags); - -	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); -	if (!err) { -		err = hfsplus_find_cat(sb, dir->i_ino, &fd); -		if (!err) -			err = hfsplus_cat_read_inode(inode, &fd); -		hfs_find_exit(&fd); -	} -	if (err) { -		iput(inode); -		return ERR_PTR(err); -	} -	hip->rsrc_inode = dir; -	HFSPLUS_I(dir)->rsrc_inode = inode; -	igrab(dir); - -	/* -	 * __mark_inode_dirty expects inodes to be hashed.  Since we don't -	 * want resource fork inodes in the regular inode space, we make them -	 * appear hashed, but do not put on any lists.  hlist_del() -	 * will work fine and require no locking. -	 */ -	hlist_add_fake(&inode->i_hash); - -	mark_inode_dirty(inode); -out: -	d_add(dentry, inode); -	return NULL; -} -  static void hfsplus_get_perms(struct inode *inode,  		struct hfsplus_perm *perms, int dir)  { @@ -319,7 +262,7 @@ static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr)  	mark_inode_dirty(inode);  	if (attr->ia_valid & ATTR_MODE) { -		error = hfsplus_posix_acl_chmod(inode); +		error = posix_acl_chmod(inode, inode->i_mode);  		if (unlikely(error))  			return error;  	} @@ -385,23 +328,23 @@ int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,  }  static const struct inode_operations hfsplus_file_inode_operations = { -	.lookup		= hfsplus_file_lookup,  	.setattr	= hfsplus_setattr,  	.setxattr	= generic_setxattr,  	.getxattr	= generic_getxattr,  	.listxattr	= hfsplus_listxattr, -	.removexattr	= hfsplus_removexattr, +	.removexattr	= generic_removexattr,  #ifdef CONFIG_HFSPLUS_FS_POSIX_ACL  	.get_acl	= hfsplus_get_posix_acl, +	.set_acl	= hfsplus_set_posix_acl,  #endif  };  static const struct file_operations hfsplus_file_operations = {  	.llseek		= generic_file_llseek, -	.read		= do_sync_read, -	.aio_read	= generic_file_aio_read, -	.write		= do_sync_write, -	.aio_write	= generic_file_aio_write, +	.read		= new_sync_read, +	.read_iter	= generic_file_read_iter, +	.write		= new_sync_write, +	.write_iter	= generic_file_write_iter,  	.mmap		= generic_file_mmap,  	.splice_read	= generic_file_splice_read,  	.fsync		= hfsplus_file_fsync, @@ -433,6 +376,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode)  	hip->extent_state = 0;  	hip->flags = 0;  	hip->userflags = 0; +	hip->subfolders = 0;  	memset(hip->first_extents, 0, sizeof(hfsplus_extent_rec));  	memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec));  	hip->alloc_blocks = 0; @@ -552,6 +496,10 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)  		inode->i_ctime = hfsp_mt2ut(folder->attribute_mod_date);  		HFSPLUS_I(inode)->create_date = folder->create_date;  		HFSPLUS_I(inode)->fs_blocks = 0; +		if (folder->flags & cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT)) { +			HFSPLUS_I(inode)->subfolders = +				be32_to_cpu(folder->subfolders); +		}  		inode->i_op = &hfsplus_dir_inode_operations;  		inode->i_fop = &hfsplus_dir_operations;  	} else if (type == HFSPLUS_FILE) { @@ -624,6 +572,10 @@ int hfsplus_cat_write_inode(struct inode *inode)  		folder->content_mod_date = hfsp_ut2mt(inode->i_mtime);  		folder->attribute_mod_date = hfsp_ut2mt(inode->i_ctime);  		folder->valence = cpu_to_be32(inode->i_size - 2); +		if (folder->flags & cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT)) { +			folder->subfolders = +				cpu_to_be32(HFSPLUS_I(inode)->subfolders); +		}  		hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,  					 sizeof(struct hfsplus_cat_folder));  	} else if (HFSPLUS_IS_RSRC(inode)) { diff --git a/fs/hfsplus/options.c b/fs/hfsplus/options.c index 968eab5bc1f..c90b72ee676 100644 --- a/fs/hfsplus/options.c +++ b/fs/hfsplus/options.c @@ -75,7 +75,7 @@ int hfsplus_parse_options_remount(char *input, int *force)  	int token;  	if (!input) -		return 0; +		return 1;  	while ((p = strsep(&input, ",")) != NULL) {  		if (!*p) @@ -173,9 +173,8 @@ int hfsplus_parse_options(char *input, struct hfsplus_sb_info *sbi)  			if (p)  				sbi->nls = load_nls(p);  			if (!sbi->nls) { -				pr_err("unable to load " -						"nls mapping \"%s\"\n", -					p); +				pr_err("unable to load nls mapping \"%s\"\n", +				       p);  				kfree(p);  				return 0;  			} @@ -232,8 +231,8 @@ int hfsplus_show_options(struct seq_file *seq, struct dentry *root)  	if (sbi->nls)  		seq_printf(seq, ",nls=%s", sbi->nls->charset);  	if (test_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags)) -		seq_printf(seq, ",nodecompose"); +		seq_puts(seq, ",nodecompose");  	if (test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags)) -		seq_printf(seq, ",nobarrier"); +		seq_puts(seq, ",nobarrier");  	return 0;  } diff --git a/fs/hfsplus/posix_acl.c b/fs/hfsplus/posix_acl.c index b609cc14c72..df0c9af68d0 100644 --- a/fs/hfsplus/posix_acl.c +++ b/fs/hfsplus/posix_acl.c @@ -17,9 +17,7 @@ struct posix_acl *hfsplus_get_posix_acl(struct inode *inode, int type)  	char *value = NULL;  	ssize_t size; -	acl = get_cached_acl(inode, type); -	if (acl != ACL_NOT_CACHED) -		return acl; +	hfs_dbg(ACL_MOD, "[%s]: ino %lu\n", __func__, inode->i_ino);  	switch (type) {  	case ACL_TYPE_ACCESS: @@ -56,17 +54,15 @@ struct posix_acl *hfsplus_get_posix_acl(struct inode *inode, int type)  	return acl;  } -static int hfsplus_set_posix_acl(struct inode *inode, -					int type, -					struct posix_acl *acl) +int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl, +		int type)  {  	int err;  	char *xattr_name;  	size_t size = 0;  	char *value = NULL; -	if (S_ISLNK(inode->i_mode)) -		return -EOPNOTSUPP; +	hfs_dbg(ACL_MOD, "[%s]: ino %lu\n", __func__, inode->i_ino);  	switch (type) {  	case ACL_TYPE_ACCESS: @@ -115,7 +111,7 @@ end_set_acl:  int hfsplus_init_posix_acl(struct inode *inode, struct inode *dir)  {  	int err = 0; -	struct posix_acl *acl = NULL; +	struct posix_acl *default_acl, *acl;  	hfs_dbg(ACL_MOD,  		"[%s]: ino %lu, dir->ino %lu\n", @@ -124,151 +120,21 @@ int hfsplus_init_posix_acl(struct inode *inode, struct inode *dir)  	if (S_ISLNK(inode->i_mode))  		return 0; -	acl = hfsplus_get_posix_acl(dir, ACL_TYPE_DEFAULT); -	if (IS_ERR(acl)) -		return PTR_ERR(acl); - -	if (acl) { -		if (S_ISDIR(inode->i_mode)) { -			err = hfsplus_set_posix_acl(inode, -							ACL_TYPE_DEFAULT, -							acl); -			if (unlikely(err)) -				goto init_acl_cleanup; -		} - -		err = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); -		if (unlikely(err < 0)) -			return err; - -		if (err > 0) -			err = hfsplus_set_posix_acl(inode, -							ACL_TYPE_ACCESS, -							acl); -	} else -		inode->i_mode &= ~current_umask(); - -init_acl_cleanup: -	posix_acl_release(acl); -	return err; -} - -int hfsplus_posix_acl_chmod(struct inode *inode) -{ -	int err; -	struct posix_acl *acl; - -	hfs_dbg(ACL_MOD, "[%s]: ino %lu\n", __func__, inode->i_ino); - -	if (S_ISLNK(inode->i_mode)) -		return -EOPNOTSUPP; - -	acl = hfsplus_get_posix_acl(inode, ACL_TYPE_ACCESS); -	if (IS_ERR(acl) || !acl) -		return PTR_ERR(acl); - -	err = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); -	if (unlikely(err)) +	err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); +	if (err)  		return err; -	err = hfsplus_set_posix_acl(inode, ACL_TYPE_ACCESS, acl); -	posix_acl_release(acl); -	return err; -} - -static int hfsplus_xattr_get_posix_acl(struct dentry *dentry, -					const char *name, -					void *buffer, -					size_t size, -					int type) -{ -	int err = 0; -	struct posix_acl *acl; - -	hfs_dbg(ACL_MOD, -		"[%s]: ino %lu, buffer %p, size %zu, type %#x\n", -		__func__, dentry->d_inode->i_ino, buffer, size, type); - -	if (strcmp(name, "") != 0) -		return -EINVAL; - -	acl = hfsplus_get_posix_acl(dentry->d_inode, type); -	if (IS_ERR(acl)) -		return PTR_ERR(acl); -	if (acl == NULL) -		return -ENODATA; - -	err = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); -	posix_acl_release(acl); - -	return err; -} - -static int hfsplus_xattr_set_posix_acl(struct dentry *dentry, -					const char *name, -					const void *value, -					size_t size, -					int flags, -					int type) -{ -	int err = 0; -	struct inode *inode = dentry->d_inode; -	struct posix_acl *acl = NULL; - -	hfs_dbg(ACL_MOD, -		"[%s]: ino %lu, value %p, size %zu, flags %#x, type %#x\n", -		__func__, inode->i_ino, value, size, flags, type); - -	if (strcmp(name, "") != 0) -		return -EINVAL; - -	if (!inode_owner_or_capable(inode)) -		return -EPERM; - -	if (value) { -		acl = posix_acl_from_xattr(&init_user_ns, value, size); -		if (IS_ERR(acl)) -			return PTR_ERR(acl); -		else if (acl) { -			err = posix_acl_valid(acl); -			if (err) -				goto end_xattr_set_acl; -		} +	if (default_acl) { +		err = hfsplus_set_posix_acl(inode, default_acl, +					    ACL_TYPE_DEFAULT); +		posix_acl_release(default_acl);  	} -	err = hfsplus_set_posix_acl(inode, type, acl); - -end_xattr_set_acl: -	posix_acl_release(acl); +	if (acl) { +		if (!err) +			err = hfsplus_set_posix_acl(inode, acl, +						    ACL_TYPE_ACCESS); +		posix_acl_release(acl); +	}  	return err;  } - -static size_t hfsplus_xattr_list_posix_acl(struct dentry *dentry, -						char *list, -						size_t list_size, -						const char *name, -						size_t name_len, -						int type) -{ -	/* -	 * This method is not used. -	 * It is used hfsplus_listxattr() instead of generic_listxattr(). -	 */ -	return -EOPNOTSUPP; -} - -const struct xattr_handler hfsplus_xattr_acl_access_handler = { -	.prefix	= POSIX_ACL_XATTR_ACCESS, -	.flags	= ACL_TYPE_ACCESS, -	.list	= hfsplus_xattr_list_posix_acl, -	.get	= hfsplus_xattr_get_posix_acl, -	.set	= hfsplus_xattr_set_posix_acl, -}; - -const struct xattr_handler hfsplus_xattr_acl_default_handler = { -	.prefix	= POSIX_ACL_XATTR_DEFAULT, -	.flags	= ACL_TYPE_DEFAULT, -	.list	= hfsplus_xattr_list_posix_acl, -	.get	= hfsplus_xattr_get_posix_acl, -	.set	= hfsplus_xattr_set_posix_acl, -}; diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 4c4d142cf89..4cf2024b87d 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -131,9 +131,10 @@ static int hfsplus_system_write_inode(struct inode *inode)  	hfsplus_inode_write_fork(inode, fork);  	if (tree) {  		int err = hfs_btree_write(tree); +  		if (err) {  			pr_err("b-tree write err: %d, ino %lu\n", -					err, inode->i_ino); +			       err, inode->i_ino);  			return err;  		}  	} @@ -161,7 +162,7 @@ static int hfsplus_write_inode(struct inode *inode,  static void hfsplus_evict_inode(struct inode *inode)  {  	hfs_dbg(INODE, "hfsplus_evict_inode: %lu\n", inode->i_ino); -	truncate_inode_pages(&inode->i_data, 0); +	truncate_inode_pages_final(&inode->i_data);  	clear_inode(inode);  	if (HFSPLUS_IS_RSRC(inode)) {  		HFSPLUS_I(HFSPLUS_I(inode)->rsrc_inode)->rsrc_inode = NULL; @@ -323,6 +324,7 @@ static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)  static int hfsplus_remount(struct super_block *sb, int *flags, char *data)  { +	sync_filesystem(sb);  	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))  		return 0;  	if (!(*flags & MS_RDONLY)) { @@ -474,12 +476,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; diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c index b51a6079108..cc623567143 100644 --- a/fs/hfsplus/wrapper.c +++ b/fs/hfsplus/wrapper.c @@ -24,15 +24,8 @@ struct hfsplus_wd {  	u16 embed_count;  }; -static void hfsplus_end_io_sync(struct bio *bio, int err) -{ -	if (err) -		clear_bit(BIO_UPTODATE, &bio->bi_flags); -	complete(bio->bi_private); -} - -/* - * hfsplus_submit_bio - Perfrom block I/O +/** + * hfsplus_submit_bio - Perform block I/O   * @sb: super block of volume for I/O   * @sector: block to read or write, for blocks of HFSPLUS_SECTOR_SIZE bytes   * @buf: buffer for I/O @@ -53,7 +46,6 @@ static void hfsplus_end_io_sync(struct bio *bio, int err)  int hfsplus_submit_bio(struct super_block *sb, sector_t sector,  		void *buf, void **data, int rw)  { -	DECLARE_COMPLETION_ONSTACK(wait);  	struct bio *bio;  	int ret = 0;  	u64 io_size; @@ -71,10 +63,8 @@ int hfsplus_submit_bio(struct super_block *sb, sector_t sector,  	sector &= ~((io_size >> HFSPLUS_SECTOR_SHIFT) - 1);  	bio = bio_alloc(GFP_NOIO, 1); -	bio->bi_sector = sector; +	bio->bi_iter.bi_sector = sector;  	bio->bi_bdev = sb->s_bdev; -	bio->bi_end_io = hfsplus_end_io_sync; -	bio->bi_private = &wait;  	if (!(rw & WRITE) && data)  		*data = (u8 *)buf + offset; @@ -93,12 +83,7 @@ int hfsplus_submit_bio(struct super_block *sb, sector_t sector,  		buf = (u8 *)buf + len;  	} -	submit_bio(rw, bio); -	wait_for_completion(&wait); - -	if (!bio_flagged(bio, BIO_UPTODATE)) -		ret = -EIO; - +	ret = submit_bio_wait(rw, bio);  out:  	bio_put(bio);  	return ret < 0 ? ret : 0; @@ -246,10 +231,8 @@ reread:  	if (blocksize < HFSPLUS_SECTOR_SIZE || ((blocksize - 1) & blocksize))  		goto out_free_backup_vhdr;  	sbi->alloc_blksz = blocksize; -	sbi->alloc_blksz_shift = 0; -	while ((blocksize >>= 1) != 0) -		sbi->alloc_blksz_shift++; -	blocksize = min(sbi->alloc_blksz, (u32)PAGE_SIZE); +	sbi->alloc_blksz_shift = ilog2(blocksize); +	blocksize = min_t(u32, sbi->alloc_blksz, PAGE_SIZE);  	/*  	 * Align block size to block offset. diff --git a/fs/hfsplus/xattr.c b/fs/hfsplus/xattr.c index bd8471fb9a6..d98094a9f47 100644 --- a/fs/hfsplus/xattr.c +++ b/fs/hfsplus/xattr.c @@ -7,16 +7,20 @@   */  #include "hfsplus_fs.h" +#include <linux/posix_acl_xattr.h> +#include <linux/nls.h>  #include "xattr.h"  #include "acl.h" +static int hfsplus_removexattr(struct inode *inode, const char *name); +  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 -	&hfsplus_xattr_acl_access_handler, -	&hfsplus_xattr_acl_default_handler, +	&posix_acl_access_xattr_handler, +	&posix_acl_default_xattr_handler,  #endif  	&hfsplus_xattr_security_handler,  	NULL @@ -51,80 +55,209 @@ static inline int is_known_namespace(const char *name)  	return true;  } -static int can_set_system_xattr(struct inode *inode, const char *name, -				const void *value, size_t size) +static void hfsplus_init_header_node(struct inode *attr_file, +					u32 clump_size, +					char *buf, u16 node_size)  { -#ifdef CONFIG_HFSPLUS_FS_POSIX_ACL -	struct posix_acl *acl; -	int err; +	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; +	u64 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); +	} -	if (!inode_owner_or_capable(inode)) -		return -EPERM; +	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); +} -	/* -	 * POSIX_ACL_XATTR_ACCESS is tied to i_mode -	 */ -	if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0) { -		acl = posix_acl_from_xattr(&init_user_ns, value, size); -		if (IS_ERR(acl)) -			return PTR_ERR(acl); -		if (acl) { -			err = posix_acl_equiv_mode(acl, &inode->i_mode); -			posix_acl_release(acl); -			if (err < 0) -				return err; -			mark_inode_dirty(inode); -		} +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:  		/* -		 * We're changing the ACL.  Get rid of the cached one +		 * 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.  		 */ -		forget_cached_acl(inode, ACL_TYPE_ACCESS); - +		return -EAGAIN; +	case HFSPLUS_VALID_ATTR_TREE:  		return 0; -	} else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0) { -		acl = posix_acl_from_xattr(&init_user_ns, value, size); -		if (IS_ERR(acl)) -			return PTR_ERR(acl); -		posix_acl_release(acl); +	case HFSPLUS_FAILED_ATTR_TREE: +		return -EOPNOTSUPP; +	default: +		BUG(); +	} -		/* -		 * We're changing the default ACL.  Get rid of the cached one -		 */ -		forget_cached_acl(inode, ACL_TYPE_DEFAULT); +	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); +	} -		return 0; +	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;  	} -#endif /* CONFIG_HFSPLUS_FS_POSIX_ACL */ -	return -EOPNOTSUPP; -} -static int can_set_xattr(struct inode *inode, const char *name, -				const void *value, size_t value_len) -{ -	if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN)) -		return can_set_system_xattr(inode, name, value, value_len); +	while (hip->alloc_blocks < hip->clump_blocks) { +		err = hfsplus_file_extend(attr_file, false); +		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); +	} -	if (!strncmp(name, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN)) { -		/* -		 * This makes sure that we aren't trying to set an -		 * attribute in a different namespace by prefixing it -		 * with "osx." -		 */ -		if (is_known_namespace(name + XATTR_MAC_OSX_PREFIX_LEN)) -			return -EOPNOTSUPP; +	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; +	} -		return 0; +	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);  	} -	/* -	 * Don't allow setting an attribute in an unknown namespace. -	 */ -	if (strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) && -	    strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) && -	    strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) -		return -EOPNOTSUPP; +	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); -	return 0; +	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, @@ -144,18 +277,8 @@ int __hfsplus_setxattr(struct inode *inode, const char *name,  				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; -	} +	if (value == NULL) +		return hfsplus_removexattr(inode, name);  	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);  	if (err) { @@ -211,8 +334,9 @@ int __hfsplus_setxattr(struct inode *inode, const char *name,  	}  	if (!HFSPLUS_SB(inode->i_sb)->attr_tree) { -		err = -EOPNOTSUPP; -		goto end_setxattr; +		err = hfsplus_create_attributes_file(inode->i_sb); +		if (unlikely(err)) +			goto end_setxattr;  	}  	if (hfsplus_attr_exists(inode, name)) { @@ -272,16 +396,11 @@ end_setxattr:  	return err;  } -static inline int is_osx_xattr(const char *xattr_name) -{ -	return !is_known_namespace(xattr_name); -} -  static int name_len(const char *xattr_name, int xattr_name_len)  {  	int len = xattr_name_len + 1; -	if (is_osx_xattr(xattr_name)) +	if (!is_known_namespace(xattr_name))  		len += XATTR_MAC_OSX_PREFIX_LEN;  	return len; @@ -292,7 +411,7 @@ static int copy_name(char *buffer, const char *xattr_name, int name_len)  	int len = name_len;  	int offset = 0; -	if (is_osx_xattr(xattr_name)) { +	if (!is_known_namespace(xattr_name)) {  		strncpy(buffer, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN);  		offset += XATTR_MAC_OSX_PREFIX_LEN;  		len += XATTR_MAC_OSX_PREFIX_LEN; @@ -370,18 +489,6 @@ ssize_t __hfsplus_getxattr(struct inode *inode, const char *name,  				HFSPLUS_IS_RSRC(inode))  		return -EOPNOTSUPP; -	if (strncmp(name, XATTR_MAC_OSX_PREFIX, -				XATTR_MAC_OSX_PREFIX_LEN) == 0) { -		/* skip "osx." prefix */ -		name += XATTR_MAC_OSX_PREFIX_LEN; -		/* -		 * Don't allow retrieving properly prefixed attributes -		 * by prepending them with "osx." -		 */ -		if (is_known_namespace(name)) -			return -EOPNOTSUPP; -	} -  	if (!strcmp_xattr_finder_info(name))  		return hfsplus_getxattr_finder_info(inode, value, size); @@ -539,8 +646,7 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)  	struct hfs_find_data fd;  	u16 key_len = 0;  	struct hfsplus_attr_key attr_key; -	char strbuf[HFSPLUS_ATTR_MAX_STRLEN + -			XATTR_MAC_OSX_PREFIX_LEN + 1] = {0}; +	char *strbuf;  	int xattr_name_len;  	if ((!S_ISREG(inode->i_mode) && @@ -560,6 +666,13 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)  		return err;  	} +	strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + +			XATTR_MAC_OSX_PREFIX_LEN + 1, GFP_KERNEL); +	if (!strbuf) { +		res = -ENOMEM; +		goto out; +	} +  	err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd);  	if (err) {  		if (err == -ENOENT) { @@ -586,7 +699,7 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)  		if (be32_to_cpu(attr_key.cnid) != inode->i_ino)  			goto end_listxattr; -		xattr_name_len = HFSPLUS_ATTR_MAX_STRLEN; +		xattr_name_len = NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN;  		if (hfsplus_uni2asc(inode->i_sb,  			(const struct hfsplus_unistr *)&fd.key->attr.key_name,  					strbuf, &xattr_name_len)) { @@ -612,36 +725,24 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)  	}  end_listxattr: +	kfree(strbuf); +out:  	hfs_find_exit(&fd);  	return res;  } -int hfsplus_removexattr(struct dentry *dentry, const char *name) +static int hfsplus_removexattr(struct inode *inode, const char *name)  {  	int err = 0; -	struct inode *inode = dentry->d_inode;  	struct hfs_find_data cat_fd;  	u16 flags;  	u16 cat_entry_type;  	int is_xattr_acl_deleted = 0;  	int is_all_xattrs_deleted = 0; -	if ((!S_ISREG(inode->i_mode) && -			!S_ISDIR(inode->i_mode)) || -				HFSPLUS_IS_RSRC(inode)) -		return -EOPNOTSUPP; -  	if (!HFSPLUS_SB(inode->i_sb)->attr_tree)  		return -EOPNOTSUPP; -	err = can_set_xattr(inode, name, NULL, 0); -	if (err) -		return err; - -	if (strncmp(name, XATTR_MAC_OSX_PREFIX, -				XATTR_MAC_OSX_PREFIX_LEN) == 0) -		name += XATTR_MAC_OSX_PREFIX_LEN; -  	if (!strcmp_xattr_finder_info(name))  		return -EOPNOTSUPP; @@ -705,39 +806,55 @@ end_removexattr:  static int hfsplus_osx_getxattr(struct dentry *dentry, const char *name,  					void *buffer, size_t size, int type)  { -	char xattr_name[HFSPLUS_ATTR_MAX_STRLEN + -				XATTR_MAC_OSX_PREFIX_LEN + 1] = {0}; -	size_t len = strlen(name); +	char *xattr_name; +	int res;  	if (!strcmp(name, ""))  		return -EINVAL; -	if (len > HFSPLUS_ATTR_MAX_STRLEN) +	/* +	 * Don't allow retrieving properly prefixed attributes +	 * by prepending them with "osx." +	 */ +	if (is_known_namespace(name))  		return -EOPNOTSUPP; - +	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN +		+ XATTR_MAC_OSX_PREFIX_LEN + 1, GFP_KERNEL); +	if (!xattr_name) +		return -ENOMEM;  	strcpy(xattr_name, XATTR_MAC_OSX_PREFIX);  	strcpy(xattr_name + XATTR_MAC_OSX_PREFIX_LEN, name); -	return hfsplus_getxattr(dentry, xattr_name, buffer, size); +	res = hfsplus_getxattr(dentry, xattr_name, buffer, size); +	kfree(xattr_name); +	return res;  }  static int hfsplus_osx_setxattr(struct dentry *dentry, const char *name,  		const void *buffer, size_t size, int flags, int type)  { -	char xattr_name[HFSPLUS_ATTR_MAX_STRLEN + -				XATTR_MAC_OSX_PREFIX_LEN + 1] = {0}; -	size_t len = strlen(name); +	char *xattr_name; +	int res;  	if (!strcmp(name, ""))  		return -EINVAL; -	if (len > HFSPLUS_ATTR_MAX_STRLEN) +	/* +	 * Don't allow setting properly prefixed attributes +	 * by prepending them with "osx." +	 */ +	if (is_known_namespace(name))  		return -EOPNOTSUPP; - +	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN +		+ XATTR_MAC_OSX_PREFIX_LEN + 1, GFP_KERNEL); +	if (!xattr_name) +		return -ENOMEM;  	strcpy(xattr_name, XATTR_MAC_OSX_PREFIX);  	strcpy(xattr_name + XATTR_MAC_OSX_PREFIX_LEN, name); -	return hfsplus_setxattr(dentry, xattr_name, buffer, size, flags); +	res = hfsplus_setxattr(dentry, xattr_name, buffer, size, flags); +	kfree(xattr_name); +	return res;  }  static size_t hfsplus_osx_listxattr(struct dentry *dentry, char *list, diff --git a/fs/hfsplus/xattr.h b/fs/hfsplus/xattr.h index 841b5698c0f..288530cf80b 100644 --- a/fs/hfsplus/xattr.h +++ b/fs/hfsplus/xattr.h @@ -14,8 +14,6 @@  extern const struct xattr_handler hfsplus_xattr_osx_handler;  extern const struct xattr_handler hfsplus_xattr_user_handler;  extern const struct xattr_handler hfsplus_xattr_trusted_handler; -extern const struct xattr_handler hfsplus_xattr_acl_access_handler; -extern const struct xattr_handler hfsplus_xattr_acl_default_handler;  extern const struct xattr_handler hfsplus_xattr_security_handler;  extern const struct xattr_handler *hfsplus_xattr_handlers[]; @@ -42,8 +40,6 @@ static inline ssize_t hfsplus_getxattr(struct dentry *dentry,  ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size); -int hfsplus_removexattr(struct dentry *dentry, const char *name); -  int hfsplus_init_security(struct inode *inode, struct inode *dir,  				const struct qstr *qstr); diff --git a/fs/hfsplus/xattr_security.c b/fs/hfsplus/xattr_security.c index 00722765ea7..6ec5e107691 100644 --- a/fs/hfsplus/xattr_security.c +++ b/fs/hfsplus/xattr_security.c @@ -7,6 +7,8 @@   */  #include <linux/security.h> +#include <linux/nls.h> +  #include "hfsplus_fs.h"  #include "xattr.h"  #include "acl.h" @@ -14,37 +16,43 @@  static int hfsplus_security_getxattr(struct dentry *dentry, const char *name,  					void *buffer, size_t size, int type)  { -	char xattr_name[HFSPLUS_ATTR_MAX_STRLEN + 1] = {0}; -	size_t len = strlen(name); +	char *xattr_name; +	int res;  	if (!strcmp(name, ""))  		return -EINVAL; -	if (len + XATTR_SECURITY_PREFIX_LEN > HFSPLUS_ATTR_MAX_STRLEN) -		return -EOPNOTSUPP; - +	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1, +		GFP_KERNEL); +	if (!xattr_name) +		return -ENOMEM;  	strcpy(xattr_name, XATTR_SECURITY_PREFIX);  	strcpy(xattr_name + XATTR_SECURITY_PREFIX_LEN, name); -	return hfsplus_getxattr(dentry, xattr_name, buffer, size); +	res = hfsplus_getxattr(dentry, xattr_name, buffer, size); +	kfree(xattr_name); +	return res;  }  static int hfsplus_security_setxattr(struct dentry *dentry, const char *name,  		const void *buffer, size_t size, int flags, int type)  { -	char xattr_name[HFSPLUS_ATTR_MAX_STRLEN + 1] = {0}; -	size_t len = strlen(name); +	char *xattr_name; +	int res;  	if (!strcmp(name, ""))  		return -EINVAL; -	if (len + XATTR_SECURITY_PREFIX_LEN > HFSPLUS_ATTR_MAX_STRLEN) -		return -EOPNOTSUPP; - +	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1, +		GFP_KERNEL); +	if (!xattr_name) +		return -ENOMEM;  	strcpy(xattr_name, XATTR_SECURITY_PREFIX);  	strcpy(xattr_name + XATTR_SECURITY_PREFIX_LEN, name); -	return hfsplus_setxattr(dentry, xattr_name, buffer, size, flags); +	res = hfsplus_setxattr(dentry, xattr_name, buffer, size, flags); +	kfree(xattr_name); +	return res;  }  static size_t hfsplus_security_listxattr(struct dentry *dentry, char *list, @@ -62,31 +70,30 @@ static int hfsplus_initxattrs(struct inode *inode,  				void *fs_info)  {  	const struct xattr *xattr; -	char xattr_name[HFSPLUS_ATTR_MAX_STRLEN + 1] = {0}; -	size_t xattr_name_len; +	char *xattr_name;  	int err = 0; +	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1, +		GFP_KERNEL); +	if (!xattr_name) +		return -ENOMEM;  	for (xattr = xattr_array; xattr->name != NULL; xattr++) { -		xattr_name_len = strlen(xattr->name); -		if (xattr_name_len == 0) +		if (!strcmp(xattr->name, ""))  			continue; -		if (xattr_name_len + XATTR_SECURITY_PREFIX_LEN > -				HFSPLUS_ATTR_MAX_STRLEN) -			return -EOPNOTSUPP; -  		strcpy(xattr_name, XATTR_SECURITY_PREFIX);  		strcpy(xattr_name +  			XATTR_SECURITY_PREFIX_LEN, xattr->name);  		memset(xattr_name + -			XATTR_SECURITY_PREFIX_LEN + xattr_name_len, 0, 1); +			XATTR_SECURITY_PREFIX_LEN + strlen(xattr->name), 0, 1);  		err = __hfsplus_setxattr(inode, xattr_name,  					xattr->value, xattr->value_len, 0);  		if (err)  			break;  	} +	kfree(xattr_name);  	return err;  } diff --git a/fs/hfsplus/xattr_trusted.c b/fs/hfsplus/xattr_trusted.c index 426cee27754..3c5f27e4746 100644 --- a/fs/hfsplus/xattr_trusted.c +++ b/fs/hfsplus/xattr_trusted.c @@ -6,43 +6,51 @@   * Handler for trusted extended attributes.   */ +#include <linux/nls.h> +  #include "hfsplus_fs.h"  #include "xattr.h"  static int hfsplus_trusted_getxattr(struct dentry *dentry, const char *name,  					void *buffer, size_t size, int type)  { -	char xattr_name[HFSPLUS_ATTR_MAX_STRLEN + 1] = {0}; -	size_t len = strlen(name); +	char *xattr_name; +	int res;  	if (!strcmp(name, ""))  		return -EINVAL; -	if (len + XATTR_TRUSTED_PREFIX_LEN > HFSPLUS_ATTR_MAX_STRLEN) -		return -EOPNOTSUPP; - +	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1, +		GFP_KERNEL); +	if (!xattr_name) +		return -ENOMEM;  	strcpy(xattr_name, XATTR_TRUSTED_PREFIX);  	strcpy(xattr_name + XATTR_TRUSTED_PREFIX_LEN, name); -	return hfsplus_getxattr(dentry, xattr_name, buffer, size); +	res = hfsplus_getxattr(dentry, xattr_name, buffer, size); +	kfree(xattr_name); +	return res;  }  static int hfsplus_trusted_setxattr(struct dentry *dentry, const char *name,  		const void *buffer, size_t size, int flags, int type)  { -	char xattr_name[HFSPLUS_ATTR_MAX_STRLEN + 1] = {0}; -	size_t len = strlen(name); +	char *xattr_name; +	int res;  	if (!strcmp(name, ""))  		return -EINVAL; -	if (len + XATTR_TRUSTED_PREFIX_LEN > HFSPLUS_ATTR_MAX_STRLEN) -		return -EOPNOTSUPP; - +	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1, +		GFP_KERNEL); +	if (!xattr_name) +		return -ENOMEM;  	strcpy(xattr_name, XATTR_TRUSTED_PREFIX);  	strcpy(xattr_name + XATTR_TRUSTED_PREFIX_LEN, name); -	return hfsplus_setxattr(dentry, xattr_name, buffer, size, flags); +	res = hfsplus_setxattr(dentry, xattr_name, buffer, size, flags); +	kfree(xattr_name); +	return res;  }  static size_t hfsplus_trusted_listxattr(struct dentry *dentry, char *list, diff --git a/fs/hfsplus/xattr_user.c b/fs/hfsplus/xattr_user.c index e34016561ae..2b625a538b6 100644 --- a/fs/hfsplus/xattr_user.c +++ b/fs/hfsplus/xattr_user.c @@ -6,43 +6,51 @@   * Handler for user extended attributes.   */ +#include <linux/nls.h> +  #include "hfsplus_fs.h"  #include "xattr.h"  static int hfsplus_user_getxattr(struct dentry *dentry, const char *name,  					void *buffer, size_t size, int type)  { -	char xattr_name[HFSPLUS_ATTR_MAX_STRLEN + 1] = {0}; -	size_t len = strlen(name); +	char *xattr_name; +	int res;  	if (!strcmp(name, ""))  		return -EINVAL; -	if (len + XATTR_USER_PREFIX_LEN > HFSPLUS_ATTR_MAX_STRLEN) -		return -EOPNOTSUPP; - +	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1, +		GFP_KERNEL); +	if (!xattr_name) +		return -ENOMEM;  	strcpy(xattr_name, XATTR_USER_PREFIX);  	strcpy(xattr_name + XATTR_USER_PREFIX_LEN, name); -	return hfsplus_getxattr(dentry, xattr_name, buffer, size); +	res = hfsplus_getxattr(dentry, xattr_name, buffer, size); +	kfree(xattr_name); +	return res;  }  static int hfsplus_user_setxattr(struct dentry *dentry, const char *name,  		const void *buffer, size_t size, int flags, int type)  { -	char xattr_name[HFSPLUS_ATTR_MAX_STRLEN + 1] = {0}; -	size_t len = strlen(name); +	char *xattr_name; +	int res;  	if (!strcmp(name, ""))  		return -EINVAL; -	if (len + XATTR_USER_PREFIX_LEN > HFSPLUS_ATTR_MAX_STRLEN) -		return -EOPNOTSUPP; - +	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1, +		GFP_KERNEL); +	if (!xattr_name) +		return -ENOMEM;  	strcpy(xattr_name, XATTR_USER_PREFIX);  	strcpy(xattr_name + XATTR_USER_PREFIX_LEN, name); -	return hfsplus_setxattr(dentry, xattr_name, buffer, size, flags); +	res = hfsplus_setxattr(dentry, xattr_name, buffer, size, flags); +	kfree(xattr_name); +	return res;  }  static size_t hfsplus_user_listxattr(struct dentry *dentry, char *list,  | 
