162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * JFFS2 -- Journalling Flash File System, Version 2.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright © 2006  NEC Corporation
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * For licensing information, see the file 'LICENCE' in this directory.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/fs.h>
1762306a36Sopenharmony_ci#include <linux/sched.h>
1862306a36Sopenharmony_ci#include <linux/time.h>
1962306a36Sopenharmony_ci#include <linux/crc32.h>
2062306a36Sopenharmony_ci#include <linux/jffs2.h>
2162306a36Sopenharmony_ci#include <linux/xattr.h>
2262306a36Sopenharmony_ci#include <linux/posix_acl_xattr.h>
2362306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
2462306a36Sopenharmony_ci#include "nodelist.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic size_t jffs2_acl_size(int count)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	if (count <= 4) {
2962306a36Sopenharmony_ci		return sizeof(struct jffs2_acl_header)
3062306a36Sopenharmony_ci		       + count * sizeof(struct jffs2_acl_entry_short);
3162306a36Sopenharmony_ci	} else {
3262306a36Sopenharmony_ci		return sizeof(struct jffs2_acl_header)
3362306a36Sopenharmony_ci		       + 4 * sizeof(struct jffs2_acl_entry_short)
3462306a36Sopenharmony_ci		       + (count - 4) * sizeof(struct jffs2_acl_entry);
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic int jffs2_acl_count(size_t size)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	size_t s;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	size -= sizeof(struct jffs2_acl_header);
4362306a36Sopenharmony_ci	if (size < 4 * sizeof(struct jffs2_acl_entry_short)) {
4462306a36Sopenharmony_ci		if (size % sizeof(struct jffs2_acl_entry_short))
4562306a36Sopenharmony_ci			return -1;
4662306a36Sopenharmony_ci		return size / sizeof(struct jffs2_acl_entry_short);
4762306a36Sopenharmony_ci	} else {
4862306a36Sopenharmony_ci		s = size - 4 * sizeof(struct jffs2_acl_entry_short);
4962306a36Sopenharmony_ci		if (s % sizeof(struct jffs2_acl_entry))
5062306a36Sopenharmony_ci			return -1;
5162306a36Sopenharmony_ci		return s / sizeof(struct jffs2_acl_entry) + 4;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic struct posix_acl *jffs2_acl_from_medium(void *value, size_t size)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	void *end = value + size;
5862306a36Sopenharmony_ci	struct jffs2_acl_header *header = value;
5962306a36Sopenharmony_ci	struct jffs2_acl_entry *entry;
6062306a36Sopenharmony_ci	struct posix_acl *acl;
6162306a36Sopenharmony_ci	uint32_t ver;
6262306a36Sopenharmony_ci	int i, count;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (!value)
6562306a36Sopenharmony_ci		return NULL;
6662306a36Sopenharmony_ci	if (size < sizeof(struct jffs2_acl_header))
6762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
6862306a36Sopenharmony_ci	ver = je32_to_cpu(header->a_version);
6962306a36Sopenharmony_ci	if (ver != JFFS2_ACL_VERSION) {
7062306a36Sopenharmony_ci		JFFS2_WARNING("Invalid ACL version. (=%u)\n", ver);
7162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	value += sizeof(struct jffs2_acl_header);
7562306a36Sopenharmony_ci	count = jffs2_acl_count(size);
7662306a36Sopenharmony_ci	if (count < 0)
7762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
7862306a36Sopenharmony_ci	if (count == 0)
7962306a36Sopenharmony_ci		return NULL;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	acl = posix_acl_alloc(count, GFP_KERNEL);
8262306a36Sopenharmony_ci	if (!acl)
8362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	for (i=0; i < count; i++) {
8662306a36Sopenharmony_ci		entry = value;
8762306a36Sopenharmony_ci		if (value + sizeof(struct jffs2_acl_entry_short) > end)
8862306a36Sopenharmony_ci			goto fail;
8962306a36Sopenharmony_ci		acl->a_entries[i].e_tag = je16_to_cpu(entry->e_tag);
9062306a36Sopenharmony_ci		acl->a_entries[i].e_perm = je16_to_cpu(entry->e_perm);
9162306a36Sopenharmony_ci		switch (acl->a_entries[i].e_tag) {
9262306a36Sopenharmony_ci			case ACL_USER_OBJ:
9362306a36Sopenharmony_ci			case ACL_GROUP_OBJ:
9462306a36Sopenharmony_ci			case ACL_MASK:
9562306a36Sopenharmony_ci			case ACL_OTHER:
9662306a36Sopenharmony_ci				value += sizeof(struct jffs2_acl_entry_short);
9762306a36Sopenharmony_ci				break;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci			case ACL_USER:
10062306a36Sopenharmony_ci				value += sizeof(struct jffs2_acl_entry);
10162306a36Sopenharmony_ci				if (value > end)
10262306a36Sopenharmony_ci					goto fail;
10362306a36Sopenharmony_ci				acl->a_entries[i].e_uid =
10462306a36Sopenharmony_ci					make_kuid(&init_user_ns,
10562306a36Sopenharmony_ci						  je32_to_cpu(entry->e_id));
10662306a36Sopenharmony_ci				break;
10762306a36Sopenharmony_ci			case ACL_GROUP:
10862306a36Sopenharmony_ci				value += sizeof(struct jffs2_acl_entry);
10962306a36Sopenharmony_ci				if (value > end)
11062306a36Sopenharmony_ci					goto fail;
11162306a36Sopenharmony_ci				acl->a_entries[i].e_gid =
11262306a36Sopenharmony_ci					make_kgid(&init_user_ns,
11362306a36Sopenharmony_ci						  je32_to_cpu(entry->e_id));
11462306a36Sopenharmony_ci				break;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci			default:
11762306a36Sopenharmony_ci				goto fail;
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci	if (value != end)
12162306a36Sopenharmony_ci		goto fail;
12262306a36Sopenharmony_ci	return acl;
12362306a36Sopenharmony_ci fail:
12462306a36Sopenharmony_ci	posix_acl_release(acl);
12562306a36Sopenharmony_ci	return ERR_PTR(-EINVAL);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct jffs2_acl_header *header;
13162306a36Sopenharmony_ci	struct jffs2_acl_entry *entry;
13262306a36Sopenharmony_ci	void *e;
13362306a36Sopenharmony_ci	size_t i;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	*size = jffs2_acl_size(acl->a_count);
13662306a36Sopenharmony_ci	header = kmalloc(struct_size(header, a_entries, acl->a_count),
13762306a36Sopenharmony_ci			GFP_KERNEL);
13862306a36Sopenharmony_ci	if (!header)
13962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
14062306a36Sopenharmony_ci	header->a_version = cpu_to_je32(JFFS2_ACL_VERSION);
14162306a36Sopenharmony_ci	e = header + 1;
14262306a36Sopenharmony_ci	for (i=0; i < acl->a_count; i++) {
14362306a36Sopenharmony_ci		const struct posix_acl_entry *acl_e = &acl->a_entries[i];
14462306a36Sopenharmony_ci		entry = e;
14562306a36Sopenharmony_ci		entry->e_tag = cpu_to_je16(acl_e->e_tag);
14662306a36Sopenharmony_ci		entry->e_perm = cpu_to_je16(acl_e->e_perm);
14762306a36Sopenharmony_ci		switch(acl_e->e_tag) {
14862306a36Sopenharmony_ci			case ACL_USER:
14962306a36Sopenharmony_ci				entry->e_id = cpu_to_je32(
15062306a36Sopenharmony_ci					from_kuid(&init_user_ns, acl_e->e_uid));
15162306a36Sopenharmony_ci				e += sizeof(struct jffs2_acl_entry);
15262306a36Sopenharmony_ci				break;
15362306a36Sopenharmony_ci			case ACL_GROUP:
15462306a36Sopenharmony_ci				entry->e_id = cpu_to_je32(
15562306a36Sopenharmony_ci					from_kgid(&init_user_ns, acl_e->e_gid));
15662306a36Sopenharmony_ci				e += sizeof(struct jffs2_acl_entry);
15762306a36Sopenharmony_ci				break;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci			case ACL_USER_OBJ:
16062306a36Sopenharmony_ci			case ACL_GROUP_OBJ:
16162306a36Sopenharmony_ci			case ACL_MASK:
16262306a36Sopenharmony_ci			case ACL_OTHER:
16362306a36Sopenharmony_ci				e += sizeof(struct jffs2_acl_entry_short);
16462306a36Sopenharmony_ci				break;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci			default:
16762306a36Sopenharmony_ci				goto fail;
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci	return header;
17162306a36Sopenharmony_ci fail:
17262306a36Sopenharmony_ci	kfree(header);
17362306a36Sopenharmony_ci	return ERR_PTR(-EINVAL);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistruct posix_acl *jffs2_get_acl(struct inode *inode, int type, bool rcu)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct posix_acl *acl;
17962306a36Sopenharmony_ci	char *value = NULL;
18062306a36Sopenharmony_ci	int rc, xprefix;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (rcu)
18362306a36Sopenharmony_ci		return ERR_PTR(-ECHILD);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	switch (type) {
18662306a36Sopenharmony_ci	case ACL_TYPE_ACCESS:
18762306a36Sopenharmony_ci		xprefix = JFFS2_XPREFIX_ACL_ACCESS;
18862306a36Sopenharmony_ci		break;
18962306a36Sopenharmony_ci	case ACL_TYPE_DEFAULT:
19062306a36Sopenharmony_ci		xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
19162306a36Sopenharmony_ci		break;
19262306a36Sopenharmony_ci	default:
19362306a36Sopenharmony_ci		BUG();
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci	rc = do_jffs2_getxattr(inode, xprefix, "", NULL, 0);
19662306a36Sopenharmony_ci	if (rc > 0) {
19762306a36Sopenharmony_ci		value = kmalloc(rc, GFP_KERNEL);
19862306a36Sopenharmony_ci		if (!value)
19962306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
20062306a36Sopenharmony_ci		rc = do_jffs2_getxattr(inode, xprefix, "", value, rc);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	if (rc > 0) {
20362306a36Sopenharmony_ci		acl = jffs2_acl_from_medium(value, rc);
20462306a36Sopenharmony_ci	} else if (rc == -ENODATA || rc == -ENOSYS) {
20562306a36Sopenharmony_ci		acl = NULL;
20662306a36Sopenharmony_ci	} else {
20762306a36Sopenharmony_ci		acl = ERR_PTR(rc);
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci	kfree(value);
21062306a36Sopenharmony_ci	return acl;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic int __jffs2_set_acl(struct inode *inode, int xprefix, struct posix_acl *acl)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	char *value = NULL;
21662306a36Sopenharmony_ci	size_t size = 0;
21762306a36Sopenharmony_ci	int rc;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (acl) {
22062306a36Sopenharmony_ci		value = jffs2_acl_to_medium(acl, &size);
22162306a36Sopenharmony_ci		if (IS_ERR(value))
22262306a36Sopenharmony_ci			return PTR_ERR(value);
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci	rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0);
22562306a36Sopenharmony_ci	if (!value && rc == -ENODATA)
22662306a36Sopenharmony_ci		rc = 0;
22762306a36Sopenharmony_ci	kfree(value);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	return rc;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ciint jffs2_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
23362306a36Sopenharmony_ci		  struct posix_acl *acl, int type)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	int rc, xprefix;
23662306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	switch (type) {
23962306a36Sopenharmony_ci	case ACL_TYPE_ACCESS:
24062306a36Sopenharmony_ci		xprefix = JFFS2_XPREFIX_ACL_ACCESS;
24162306a36Sopenharmony_ci		if (acl) {
24262306a36Sopenharmony_ci			umode_t mode;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci			rc = posix_acl_update_mode(&nop_mnt_idmap, inode, &mode,
24562306a36Sopenharmony_ci						   &acl);
24662306a36Sopenharmony_ci			if (rc)
24762306a36Sopenharmony_ci				return rc;
24862306a36Sopenharmony_ci			if (inode->i_mode != mode) {
24962306a36Sopenharmony_ci				struct iattr attr;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci				attr.ia_valid = ATTR_MODE | ATTR_CTIME;
25262306a36Sopenharmony_ci				attr.ia_mode = mode;
25362306a36Sopenharmony_ci				attr.ia_ctime = current_time(inode);
25462306a36Sopenharmony_ci				rc = jffs2_do_setattr(inode, &attr);
25562306a36Sopenharmony_ci				if (rc < 0)
25662306a36Sopenharmony_ci					return rc;
25762306a36Sopenharmony_ci			}
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci		break;
26062306a36Sopenharmony_ci	case ACL_TYPE_DEFAULT:
26162306a36Sopenharmony_ci		xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
26262306a36Sopenharmony_ci		if (!S_ISDIR(inode->i_mode))
26362306a36Sopenharmony_ci			return acl ? -EACCES : 0;
26462306a36Sopenharmony_ci		break;
26562306a36Sopenharmony_ci	default:
26662306a36Sopenharmony_ci		return -EINVAL;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci	rc = __jffs2_set_acl(inode, xprefix, acl);
26962306a36Sopenharmony_ci	if (!rc)
27062306a36Sopenharmony_ci		set_cached_acl(inode, type, acl);
27162306a36Sopenharmony_ci	return rc;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ciint jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, umode_t *i_mode)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct posix_acl *default_acl, *acl;
27762306a36Sopenharmony_ci	int rc;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	cache_no_acl(inode);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	rc = posix_acl_create(dir_i, i_mode, &default_acl, &acl);
28262306a36Sopenharmony_ci	if (rc)
28362306a36Sopenharmony_ci		return rc;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (default_acl) {
28662306a36Sopenharmony_ci		set_cached_acl(inode, ACL_TYPE_DEFAULT, default_acl);
28762306a36Sopenharmony_ci		posix_acl_release(default_acl);
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci	if (acl) {
29062306a36Sopenharmony_ci		set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
29162306a36Sopenharmony_ci		posix_acl_release(acl);
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci	return 0;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ciint jffs2_init_acl_post(struct inode *inode)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	int rc;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (inode->i_default_acl) {
30162306a36Sopenharmony_ci		rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_DEFAULT, inode->i_default_acl);
30262306a36Sopenharmony_ci		if (rc)
30362306a36Sopenharmony_ci			return rc;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	if (inode->i_acl) {
30762306a36Sopenharmony_ci		rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_ACCESS, inode->i_acl);
30862306a36Sopenharmony_ci		if (rc)
30962306a36Sopenharmony_ci			return rc;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
314