[JFFS2][XATTR] XATTR support on JFFS2 (version. 5)

This attached patches provide xattr support including POSIX-ACL and
SELinux support on JFFS2 (version.5).

There are some significant differences from previous version posted
at last December.
The biggest change is addition of EBS(Erase Block Summary) support.
Currently, both kernel and usermode utility (sumtool) can recognize
xattr nodes which have JFFS2_NODETYPE_XATTR/_XREF nodetype.

In addition, some bugs are fixed.
- A potential race condition was fixed.
- Unexpected fail when updating a xattr by same name/value pair was fixed.
- A bug when removing xattr name/value pair was fixed.

The fundamental structures (such as using two new nodetypes and exclusion
mechanism by rwsem) are unchanged. But most of implementation were reviewed
and updated if necessary.
Espacially, we had to change several internal implementations related to
load_xattr_datum() to avoid a potential race condition.

[1/2] xattr_on_jffs2.kernel.version-5.patch
[2/2] xattr_on_jffs2.utils.version-5.patch

Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
This commit is contained in:
KaiGai Kohei 2006-05-13 15:09:47 +09:00
parent 4992a9e888
commit aa98d7cf59
29 changed files with 2800 additions and 15 deletions

View file

@ -1075,6 +1075,44 @@ config JFFS2_FS_DEBUG
If reporting bugs, please try to have available a full dump of the
messages at debug level 1 while the misbehaviour was occurring.
config JFFS2_FS_XATTR
bool "JFFS2 XATTR support"
depends on JFFS2_FS
default n
help
Extended attributes are name:value pairs associated with inodes by
the kernel or by users (see the attr(5) manual page, or visit
<http://acl.bestbits.at/> for details).
If unsure, say N.
config JFFS2_FS_POSIX_ACL
bool "JFFS2 POSIX Access Control Lists"
depends on JFFS2_FS_XATTR
default y
select FS_POSIX_ACL
help
Posix Access Control Lists (ACLs) support permissions for users and
groups beyond the owner/group/world scheme.
To learn more about Access Control Lists, visit the Posix ACLs for
Linux website <http://acl.bestbits.at/>.
If you don't know what Access Control Lists are, say N
config JFFS2_FS_SECURITY
bool "JFFS2 Security Labels"
depends on JFFS2_FS_XATTR
default y
help
Security labels support alternative access control models
implemented by security modules like SELinux. This option
enables an extended attribute handler for file security
labels in the jffs2 filesystem.
If you are not using a security module that requires using
extended attributes for file security labels, say N.
config JFFS2_FS_WRITEBUFFER
bool "JFFS2 write-buffering support"
depends on JFFS2_FS

View file

@ -12,6 +12,9 @@ jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o
jffs2-y += super.o debug.o
jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o
jffs2-$(CONFIG_JFFS2_FS_XATTR) += xattr.o xattr_trusted.o xattr_user.o
jffs2-$(CONFIG_JFFS2_FS_SECURITY) += security.o
jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL) += acl.o
jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o

483
fs/jffs2/acl.c Normal file
View file

@ -0,0 +1,483 @@
/*-------------------------------------------------------------------------*
* File: fs/jffs2/acl.c
* POSIX ACL support on JFFS2 FileSystem
*
* Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com>
* Copyright (C) 2006 NEC Corporation
*
* For licensing information, see the file 'LICENCE' in the jffs2 directory.
*-------------------------------------------------------------------------*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/time.h>
#include <linux/crc32.h>
#include <linux/jffs2.h>
#include <linux/xattr.h>
#include <linux/posix_acl_xattr.h>
#include <linux/mtd/mtd.h>
#include "nodelist.h"
static size_t jffs2_acl_size(int count)
{
if (count <= 4) {
return sizeof(jffs2_acl_header)
+ count * sizeof(jffs2_acl_entry_short);
} else {
return sizeof(jffs2_acl_header)
+ 4 * sizeof(jffs2_acl_entry_short)
+ (count - 4) * sizeof(jffs2_acl_entry);
}
}
static int jffs2_acl_count(size_t size)
{
size_t s;
size -= sizeof(jffs2_acl_header);
s = size - 4 * sizeof(jffs2_acl_entry_short);
if (s < 0) {
if (size % sizeof(jffs2_acl_entry_short))
return -1;
return size / sizeof(jffs2_acl_entry_short);
} else {
if (s % sizeof(jffs2_acl_entry))
return -1;
return s / sizeof(jffs2_acl_entry) + 4;
}
}
static struct posix_acl *jffs2_acl_from_medium(const void *value, size_t size)
{
const char *end = (char *)value + size;
struct posix_acl *acl;
uint32_t ver;
int i, count;
if (!value)
return NULL;
if (size < sizeof(jffs2_acl_header))
return ERR_PTR(-EINVAL);
ver = je32_to_cpu(((jffs2_acl_header *)value)->a_version);
if (ver != JFFS2_ACL_VERSION) {
JFFS2_WARNING("Invalid ACL version. (=%u)\n", ver);
return ERR_PTR(-EINVAL);
}
value = (char *)value + sizeof(jffs2_acl_header);
count = jffs2_acl_count(size);
if (count < 0)
return ERR_PTR(-EINVAL);
if (count == 0)
return NULL;
acl = posix_acl_alloc(count, GFP_KERNEL);
if (!acl)
return ERR_PTR(-ENOMEM);
for (i=0; i < count; i++) {
jffs2_acl_entry *entry = (jffs2_acl_entry *)value;
if ((char *)value + sizeof(jffs2_acl_entry_short) > end)
goto fail;
acl->a_entries[i].e_tag = je16_to_cpu(entry->e_tag);
acl->a_entries[i].e_perm = je16_to_cpu(entry->e_perm);
switch (acl->a_entries[i].e_tag) {
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
value = (char *)value + sizeof(jffs2_acl_entry_short);
acl->a_entries[i].e_id = ACL_UNDEFINED_ID;
break;
case ACL_USER:
case ACL_GROUP:
value = (char *)value + sizeof(jffs2_acl_entry);
if ((char *)value > end)
goto fail;
acl->a_entries[i].e_id = je32_to_cpu(entry->e_id);
break;
default:
goto fail;
}
}
if (value != end)
goto fail;
return acl;
fail:
posix_acl_release(acl);
return ERR_PTR(-EINVAL);
}
static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size)
{
jffs2_acl_header *jffs2_acl;
char *e;
size_t i;
*size = jffs2_acl_size(acl->a_count);
jffs2_acl = (jffs2_acl_header *)kmalloc(sizeof(jffs2_acl_header)
+ acl->a_count * sizeof(jffs2_acl_entry),
GFP_KERNEL);
if (!jffs2_acl)
return ERR_PTR(-ENOMEM);
jffs2_acl->a_version = cpu_to_je32(JFFS2_ACL_VERSION);
e = (char *)jffs2_acl + sizeof(jffs2_acl_header);
for (i=0; i < acl->a_count; i++) {
jffs2_acl_entry *entry = (jffs2_acl_entry *)e;
entry->e_tag = cpu_to_je16(acl->a_entries[i].e_tag);
entry->e_perm = cpu_to_je16(acl->a_entries[i].e_perm);
switch(acl->a_entries[i].e_tag) {
case ACL_USER:
case ACL_GROUP:
entry->e_id = cpu_to_je32(acl->a_entries[i].e_id);
e += sizeof(jffs2_acl_entry);
break;
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
e += sizeof(jffs2_acl_entry_short);
break;
default:
goto fail;
}
}
return (char *)jffs2_acl;
fail:
kfree(jffs2_acl);
return ERR_PTR(-EINVAL);
}
static struct posix_acl *jffs2_iget_acl(struct inode *inode, struct posix_acl **i_acl)
{
struct posix_acl *acl = JFFS2_ACL_NOT_CACHED;
spin_lock(&inode->i_lock);
if (*i_acl != JFFS2_ACL_NOT_CACHED)
acl = posix_acl_dup(*i_acl);
spin_unlock(&inode->i_lock);
return acl;
}
static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct posix_acl *acl)
{
spin_lock(&inode->i_lock);
if (*i_acl != JFFS2_ACL_NOT_CACHED)
posix_acl_release(*i_acl);
*i_acl = posix_acl_dup(acl);
spin_unlock(&inode->i_lock);
}
static struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct posix_acl *acl;
char *value = NULL;
int rc, xprefix;
switch (type) {
case ACL_TYPE_ACCESS:
acl = jffs2_iget_acl(inode, &f->i_acl_access);
if (acl != JFFS2_ACL_NOT_CACHED)
return acl;
xprefix = JFFS2_XPREFIX_ACL_ACCESS;
break;
case ACL_TYPE_DEFAULT:
acl = jffs2_iget_acl(inode, &f->i_acl_default);
if (acl != JFFS2_ACL_NOT_CACHED)
return acl;
xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
break;
default:
return ERR_PTR(-EINVAL);
}
rc = do_jffs2_getxattr(inode, xprefix, "", NULL, 0);
if (rc > 0) {
value = kmalloc(rc, GFP_KERNEL);
if (!value)
return ERR_PTR(-ENOMEM);
rc = do_jffs2_getxattr(inode, xprefix, "", value, rc);
}
if (rc > 0) {
acl = jffs2_acl_from_medium(value, rc);
} else if (rc == -ENODATA || rc == -ENOSYS) {
acl = NULL;
} else {
acl = ERR_PTR(rc);
}
if (value)
kfree(value);
if (!IS_ERR(acl)) {
switch (type) {
case ACL_TYPE_ACCESS:
jffs2_iset_acl(inode, &f->i_acl_access, acl);
break;
case ACL_TYPE_DEFAULT:
jffs2_iset_acl(inode, &f->i_acl_default, acl);
break;
}
}
return acl;
}
static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
size_t size = 0;
char *value = NULL;
int rc, xprefix;
if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;
switch (type) {
case ACL_TYPE_ACCESS:
xprefix = JFFS2_XPREFIX_ACL_ACCESS;
if (acl) {
mode_t mode = inode->i_mode;
rc = posix_acl_equiv_mode(acl, &mode);
if (rc < 0)
return rc;
if (inode->i_mode != mode) {
inode->i_mode = mode;
jffs2_dirty_inode(inode);
}
if (rc == 0)
acl = NULL;
}
break;
case ACL_TYPE_DEFAULT:
xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
if (!S_ISDIR(inode->i_mode))
return acl ? -EACCES : 0;
break;
default:
return -EINVAL;
}
if (acl) {
value = jffs2_acl_to_medium(acl, &size);
if (IS_ERR(value))
return PTR_ERR(value);
}
rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0);
if (value)
kfree(value);
if (!rc) {
switch(type) {
case ACL_TYPE_ACCESS:
jffs2_iset_acl(inode, &f->i_acl_access, acl);
break;
case ACL_TYPE_DEFAULT:
jffs2_iset_acl(inode, &f->i_acl_default, acl);
break;
}
}
return rc;
}
static int jffs2_check_acl(struct inode *inode, int mask)
{
struct posix_acl *acl;
int rc;
acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl) {
rc = posix_acl_permission(inode, acl, mask);
posix_acl_release(acl);
return rc;
}
return -EAGAIN;
}
int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd)
{
return generic_permission(inode, mask, jffs2_check_acl);
}
int jffs2_init_acl(struct inode *inode, struct inode *dir)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct posix_acl *acl = NULL, *clone;
mode_t mode;
int rc = 0;
f->i_acl_access = JFFS2_ACL_NOT_CACHED;
f->i_acl_default = JFFS2_ACL_NOT_CACHED;
if (!S_ISLNK(inode->i_mode)) {
acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (!acl)
inode->i_mode &= ~current->fs->umask;
}
if (acl) {
if (S_ISDIR(inode->i_mode)) {
rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
if (rc)
goto cleanup;
}
clone = posix_acl_clone(acl, GFP_KERNEL);
rc = -ENOMEM;
if (!clone)
goto cleanup;
mode = inode->i_mode;
rc = posix_acl_create_masq(clone, &mode);
if (rc >= 0) {
inode->i_mode = mode;
if (rc > 0)
rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
}
posix_acl_release(clone);
}
cleanup:
posix_acl_release(acl);
return rc;
}
void jffs2_clear_acl(struct inode *inode)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
if (f->i_acl_access && f->i_acl_access != JFFS2_ACL_NOT_CACHED) {
posix_acl_release(f->i_acl_access);
f->i_acl_access = JFFS2_ACL_NOT_CACHED;
}
if (f->i_acl_default && f->i_acl_default != JFFS2_ACL_NOT_CACHED) {
posix_acl_release(f->i_acl_default);
f->i_acl_default = JFFS2_ACL_NOT_CACHED;
}
}
int jffs2_acl_chmod(struct inode *inode)
{
struct posix_acl *acl, *clone;
int rc;
if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;
acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl) || !acl)
return PTR_ERR(acl);
clone = posix_acl_clone(acl, GFP_KERNEL);
posix_acl_release(acl);
if (!clone)
return -ENOMEM;
rc = posix_acl_chmod_masq(clone, inode->i_mode);
if (!rc)
rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
posix_acl_release(clone);
return rc;
}
static size_t jffs2_acl_access_listxattr(struct inode *inode, char *list, size_t list_size,
const char *name, size_t name_len)
{
const int retlen = sizeof(POSIX_ACL_XATTR_ACCESS);
if (list && retlen <= list_size)
strcpy(list, POSIX_ACL_XATTR_ACCESS);
return retlen;
}
static size_t jffs2_acl_default_listxattr(struct inode *inode, char *list, size_t list_size,
const char *name, size_t name_len)
{
const int retlen = sizeof(POSIX_ACL_XATTR_DEFAULT);
if (list && retlen <= list_size)
strcpy(list, POSIX_ACL_XATTR_DEFAULT);
return retlen;
}
static int jffs2_acl_getxattr(struct inode *inode, int type, void *buffer, size_t size)
{
struct posix_acl *acl;
int rc;
acl = jffs2_get_acl(inode, type);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (!acl)
return -ENODATA;
rc = posix_acl_to_xattr(acl, buffer, size);
posix_acl_release(acl);
return rc;
}
static int jffs2_acl_access_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
{
if (name[0] != '\0')
return -EINVAL;
return jffs2_acl_getxattr(inode, ACL_TYPE_ACCESS, buffer, size);
}
static int jffs2_acl_default_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
{
if (name[0] != '\0')
return -EINVAL;
return jffs2_acl_getxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
}
static int jffs2_acl_setxattr(struct inode *inode, int type, const void *value, size_t size)
{
struct posix_acl *acl;
int rc;
if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
return -EPERM;
if (value) {
acl = posix_acl_from_xattr(value, size);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl) {
rc = posix_acl_valid(acl);
if (rc)
goto out;
}
} else {
acl = NULL;
}
rc = jffs2_set_acl(inode, type, acl);
out:
posix_acl_release(acl);
return rc;
}
static int jffs2_acl_access_setxattr(struct inode *inode, const char *name,
const void *buffer, size_t size, int flags)
{
if (name[0] != '\0')
return -EINVAL;
return jffs2_acl_setxattr(inode, ACL_TYPE_ACCESS, buffer, size);
}
static int jffs2_acl_default_setxattr(struct inode *inode, const char *name,
const void *buffer, size_t size, int flags)
{
if (name[0] != '\0')
return -EINVAL;
return jffs2_acl_setxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
}
struct xattr_handler jffs2_acl_access_xattr_handler = {
.prefix = POSIX_ACL_XATTR_ACCESS,
.list = jffs2_acl_access_listxattr,
.get = jffs2_acl_access_getxattr,
.set = jffs2_acl_access_setxattr,
};
struct xattr_handler jffs2_acl_default_xattr_handler = {
.prefix = POSIX_ACL_XATTR_DEFAULT,
.list = jffs2_acl_default_listxattr,
.get = jffs2_acl_default_getxattr,
.set = jffs2_acl_default_setxattr,
};

46
fs/jffs2/acl.h Normal file
View file

@ -0,0 +1,46 @@
/*-------------------------------------------------------------------------*
* File: fs/jffs2/acl.h
* POSIX ACL support on JFFS2 FileSystem
*
* Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com>
* Copyright (C) 2006 NEC Corporation
*
* For licensing information, see the file 'LICENCE' in the jffs2 directory.
*-------------------------------------------------------------------------*/
typedef struct {
jint16_t e_tag;
jint16_t e_perm;
jint32_t e_id;
} jffs2_acl_entry;
typedef struct {
jint16_t e_tag;
jint16_t e_perm;
} jffs2_acl_entry_short;
typedef struct {
jint32_t a_version;
} jffs2_acl_header;
#ifdef __KERNEL__
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
#define JFFS2_ACL_NOT_CACHED ((void *)-1)
extern int jffs2_permission(struct inode *, int, struct nameidata *);
extern int jffs2_acl_chmod(struct inode *);
extern int jffs2_init_acl(struct inode *, struct inode *);
extern void jffs2_clear_acl(struct inode *);
extern struct xattr_handler jffs2_acl_access_xattr_handler;
extern struct xattr_handler jffs2_acl_default_xattr_handler;
#else
#define jffs2_permission NULL
#define jffs2_acl_chmod(inode) (0)
#define jffs2_init_acl(inode,dir) (0)
#define jffs2_clear_acl(inode)
#endif /* CONFIG_JFFS2_FS_POSIX_ACL */
#endif /* __KERNEL__ */

View file

@ -160,6 +160,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
ic->scan_dents = NULL;
cond_resched();
}
jffs2_build_xattr_subsystem(c);
c->flags &= ~JFFS2_SB_FLAG_BUILDING;
dbg_fsbuild("FS build complete\n");
@ -178,6 +179,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
jffs2_free_full_dirent(fd);
}
}
jffs2_clear_xattr_subsystem(c);
}
return ret;

View file

@ -171,6 +171,12 @@
#define dbg_memalloc(fmt, ...)
#endif
/* Watch the XATTR subsystem */
#ifdef JFFS2_DBG_XATTR_MESSAGES
#define dbg_xattr(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__)
#else
#define dbg_xattr(fmt, ...)
#endif
/* "Sanity" checks */
void

View file

@ -57,7 +57,12 @@ struct inode_operations jffs2_dir_inode_operations =
.rmdir = jffs2_rmdir,
.mknod = jffs2_mknod,
.rename = jffs2_rename,
.permission = jffs2_permission,
.setattr = jffs2_setattr,
.setxattr = jffs2_setxattr,
.getxattr = jffs2_getxattr,
.listxattr = jffs2_listxattr,
.removexattr = jffs2_removexattr
};
/***********************************************************************/
@ -209,12 +214,15 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
ret = jffs2_do_create(c, dir_f, f, ri,
dentry->d_name.name, dentry->d_name.len);
if (ret) {
make_bad_inode(inode);
iput(inode);
jffs2_free_raw_inode(ri);
return ret;
}
if (ret)
goto fail;
ret = jffs2_init_security(inode, dir_i);
if (ret)
goto fail;
ret = jffs2_init_acl(inode, dir_i);
if (ret)
goto fail;
dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime));
@ -224,6 +232,12 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages));
return 0;
fail:
make_bad_inode(inode);
iput(inode);
jffs2_free_raw_inode(ri);
return ret;
}
/***********************************************************************/
@ -374,6 +388,18 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
up(&f->sem);
jffs2_complete_reservation(c);
ret = jffs2_init_security(inode, dir_i);
if (ret) {
jffs2_clear_inode(inode);
return ret;
}
ret = jffs2_init_acl(inode, dir_i);
if (ret) {
jffs2_clear_inode(inode);
return ret;
}
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
if (ret) {
@ -504,6 +530,18 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
up(&f->sem);
jffs2_complete_reservation(c);
ret = jffs2_init_security(inode, dir_i);
if (ret) {
jffs2_clear_inode(inode);
return ret;
}
ret = jffs2_init_acl(inode, dir_i);
if (ret) {
jffs2_clear_inode(inode);
return ret;
}
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
if (ret) {
@ -660,6 +698,18 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
up(&f->sem);
jffs2_complete_reservation(c);
ret = jffs2_init_security(inode, dir_i);
if (ret) {
jffs2_clear_inode(inode);
return ret;
}
ret = jffs2_init_acl(inode, dir_i);
if (ret) {
jffs2_clear_inode(inode);
return ret;
}
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
if (ret) {

View file

@ -54,7 +54,12 @@ const struct file_operations jffs2_file_operations =
struct inode_operations jffs2_file_inode_operations =
{
.setattr = jffs2_setattr
.permission = jffs2_permission,
.setattr = jffs2_setattr,
.setxattr = jffs2_setxattr,
.getxattr = jffs2_getxattr,
.listxattr = jffs2_listxattr,
.removexattr = jffs2_removexattr
};
struct address_space_operations jffs2_file_address_operations =

View file

@ -185,7 +185,12 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
{
return jffs2_do_setattr(dentry->d_inode, iattr);
int rc;
rc = jffs2_do_setattr(dentry->d_inode, iattr);
if (!rc && (iattr->ia_valid & ATTR_MODE))
rc = jffs2_acl_chmod(dentry->d_inode);
return rc;
}
int jffs2_statfs(struct super_block *sb, struct kstatfs *buf)
@ -224,6 +229,7 @@ void jffs2_clear_inode (struct inode *inode)
D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
jffs2_xattr_delete_inode(c, f->inocache);
jffs2_do_clear_inode(c, f);
}
@ -497,6 +503,8 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
}
memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *));
jffs2_init_xattr_subsystem(c);
if ((ret = jffs2_do_mount_fs(c)))
goto out_inohash;
@ -531,6 +539,7 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
else
kfree(c->blocks);
out_inohash:
jffs2_clear_xattr_subsystem(c);
kfree(c->inocache_list);
out_wbuf:
jffs2_flash_cleanup(c);

View file

@ -125,6 +125,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
struct jffs2_eraseblock *jeb;
struct jffs2_raw_node_ref *raw;
int ret = 0, inum, nlink;
int xattr = 0;
if (down_interruptible(&c->alloc_sem))
return -EINTR;
@ -138,7 +139,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
the node CRCs etc. Do it now. */
/* checked_ino is protected by the alloc_sem */
if (c->checked_ino > c->highest_ino) {
if (c->checked_ino > c->highest_ino && xattr) {
printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
c->unchecked_size);
jffs2_dbg_dump_block_lists_nolock(c);
@ -148,6 +149,9 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
spin_unlock(&c->erase_completion_lock);
if (!xattr)
xattr = jffs2_verify_xattr(c);
spin_lock(&c->inocache_lock);
ic = jffs2_get_ino_cache(c, c->checked_ino++);
@ -262,6 +266,16 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
ic = jffs2_raw_ref_to_ic(raw);
/* When 'ic' refers xattr_datum/xattr_ref, this node is GCed as xattr.
We can decide whether this node is inode or xattr by ic->class.
ret = 0 : ic is xattr_datum/xattr_ref, and GC was SUCCESSED.
ret < 0 : ic is xattr_datum/xattr_ref, but GC was FAILED.
ret > 0 : ic is NOT xattr_datum/xattr_ref.
*/
ret = jffs2_garbage_collect_xattr(c, ic);
if (ret <= 0)
goto release_sem;
/* We need to hold the inocache. Either the erase_completion_lock or
the inocache_lock are sufficient; we trade down since the inocache_lock
causes less contention. */

View file

@ -5,6 +5,7 @@
#include <linux/version.h>
#include <linux/rbtree.h>
#include <linux/posix_acl.h>
#include <asm/semaphore.h>
struct jffs2_inode_info {
@ -45,6 +46,10 @@ struct jffs2_inode_info {
struct inode vfs_inode;
#endif
#endif
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
struct posix_acl *i_acl_access;
struct posix_acl *i_acl_default;
#endif
};
#endif /* _JFFS2_FS_I */

View file

@ -115,6 +115,16 @@ struct jffs2_sb_info {
struct jffs2_summary *summary; /* Summary information */
#ifdef CONFIG_JFFS2_FS_XATTR
#define XATTRINDEX_HASHSIZE (57)
uint32_t highest_xid;
struct list_head xattrindex[XATTRINDEX_HASHSIZE];
struct list_head xattr_temp;
struct list_head xattr_unchecked;
struct rw_semaphore xattr_sem;
uint32_t xdatum_mem_usage;
uint32_t xdatum_mem_threshold;
#endif
/* OS-private pointer for getting back to master superblock info */
void *os_priv;
};

View file

@ -26,6 +26,10 @@ static kmem_cache_t *tmp_dnode_info_slab;
static kmem_cache_t *raw_node_ref_slab;
static kmem_cache_t *node_frag_slab;
static kmem_cache_t *inode_cache_slab;
#ifdef CONFIG_JFFS2_FS_XATTR
static kmem_cache_t *xattr_datum_cache;
static kmem_cache_t *xattr_ref_cache;
#endif
int __init jffs2_create_slab_caches(void)
{
@ -68,8 +72,24 @@ int __init jffs2_create_slab_caches(void)
inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
sizeof(struct jffs2_inode_cache),
0, 0, NULL, NULL);
if (inode_cache_slab)
return 0;
if (!inode_cache_slab)
goto err;
#ifdef CONFIG_JFFS2_FS_XATTR
xattr_datum_cache = kmem_cache_create("jffs2_xattr_datum",
sizeof(struct jffs2_xattr_datum),
0, 0, NULL, NULL);
if (!xattr_datum_cache)
goto err;
xattr_ref_cache = kmem_cache_create("jffs2_xattr_ref",
sizeof(struct jffs2_xattr_ref),
0, 0, NULL, NULL);
if (!xattr_ref_cache)
goto err;
#endif
return 0;
err:
jffs2_destroy_slab_caches();
return -ENOMEM;
@ -91,6 +111,12 @@ void jffs2_destroy_slab_caches(void)
kmem_cache_destroy(node_frag_slab);
if(inode_cache_slab)
kmem_cache_destroy(inode_cache_slab);
#ifdef CONFIG_JFFS2_FS_XATTR
if (xattr_datum_cache)
kmem_cache_destroy(xattr_datum_cache);
if (xattr_ref_cache)
kmem_cache_destroy(xattr_ref_cache);
#endif
}
struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
@ -205,3 +231,41 @@ void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
dbg_memalloc("%p\n", x);
kmem_cache_free(inode_cache_slab, x);
}
#ifdef CONFIG_JFFS2_FS_XATTR
struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void)
{
struct jffs2_xattr_datum *xd;
xd = kmem_cache_alloc(xattr_datum_cache, GFP_KERNEL);
dbg_memalloc("%p\n", xd);
memset(xd, 0, sizeof(struct jffs2_xattr_datum));
xd->class = RAWNODE_CLASS_XATTR_DATUM;
INIT_LIST_HEAD(&xd->xindex);
return xd;
}
void jffs2_free_xattr_datum(struct jffs2_xattr_datum *xd)
{
dbg_memalloc("%p\n", xd);
kmem_cache_free(xattr_datum_cache, xd);
}
struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void)
{
struct jffs2_xattr_ref *ref;
ref = kmem_cache_alloc(xattr_ref_cache, GFP_KERNEL);
dbg_memalloc("%p\n", ref);
memset(ref, 0, sizeof(struct jffs2_xattr_ref));
ref->class = RAWNODE_CLASS_XATTR_REF;
INIT_LIST_HEAD(&ref->ilist);
return ref;
}
void jffs2_free_xattr_ref(struct jffs2_xattr_ref *ref)
{
dbg_memalloc("%p\n", ref);
kmem_cache_free(xattr_ref_cache, ref);
}
#endif

View file

@ -938,6 +938,7 @@ void jffs2_free_ino_caches(struct jffs2_sb_info *c)
this = c->inocache_list[i];
while (this) {
next = this->next;
jffs2_xattr_free_inode(c, this);
jffs2_free_inode_cache(this);
this = next;
}

View file

@ -20,6 +20,8 @@
#include <linux/jffs2.h>
#include "jffs2_fs_sb.h"
#include "jffs2_fs_i.h"
#include "xattr.h"
#include "acl.h"
#include "summary.h"
#ifdef __ECOS
@ -107,11 +109,16 @@ struct jffs2_inode_cache {
temporary lists of dirents, and later must be set to
NULL to mark the end of the raw_node_ref->next_in_ino
chain. */
u8 class; /* It's used for identification */
u8 flags;
uint16_t state;
struct jffs2_inode_cache *next;
struct jffs2_raw_node_ref *nodes;
uint32_t ino;
int nlink;
int state;
#ifdef CONFIG_JFFS2_FS_XATTR
struct list_head ilist;
#endif
};
/* Inode states for 'state' above. We need the 'GC' state to prevent
@ -125,6 +132,12 @@ struct jffs2_inode_cache {
#define INO_STATE_READING 5 /* In read_inode() */
#define INO_STATE_CLEARING 6 /* In clear_inode() */
#define INO_FLAGS_XATTR_CHECKED 0x01 /* has no duplicate xattr_ref */
#define RAWNODE_CLASS_INODE_CACHE 0
#define RAWNODE_CLASS_XATTR_DATUM 1
#define RAWNODE_CLASS_XATTR_REF 2
#define INOCACHE_HASHSIZE 128
/*
@ -374,6 +387,12 @@ struct jffs2_node_frag *jffs2_alloc_node_frag(void);
void jffs2_free_node_frag(struct jffs2_node_frag *);
struct jffs2_inode_cache *jffs2_alloc_inode_cache(void);
void jffs2_free_inode_cache(struct jffs2_inode_cache *);
#ifdef CONFIG_JFFS2_FS_XATTR
struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void);
void jffs2_free_xattr_datum(struct jffs2_xattr_datum *);
struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void);
void jffs2_free_xattr_ref(struct jffs2_xattr_ref *);
#endif
/* gc.c */
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);

View file

@ -60,6 +60,10 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
f->target = NULL;
f->flags = 0;
f->usercompr = 0;
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
f->i_acl_access = JFFS2_ACL_NOT_CACHED;
f->i_acl_default = JFFS2_ACL_NOT_CACHED;
#endif
}

View file

@ -902,6 +902,7 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
f->inocache->ino = f->inocache->nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
f->inocache->state = INO_STATE_READING;
init_xattr_inode_cache(f->inocache);
jffs2_add_ino_cache(c, f->inocache);
}
if (!f->inocache) {

View file

@ -306,6 +306,136 @@ int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *je
return BLK_STATE_ALLDIRTY;
}
#ifdef CONFIG_JFFS2_FS_XATTR
static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_raw_xattr *rx, uint32_t ofs,
struct jffs2_summary *s)
{
struct jffs2_xattr_datum *xd;
struct jffs2_raw_node_ref *raw;
uint32_t totlen, crc;
crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr) - 4);
if (crc != je32_to_cpu(rx->node_crc)) {
if (je32_to_cpu(rx->node_crc) != 0xffffffff)
JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
ofs, je32_to_cpu(rx->node_crc), crc);
DIRTY_SPACE(je32_to_cpu(rx->totlen));
return 0;
}
totlen = PAD(sizeof(*rx) + rx->name_len + 1 + je16_to_cpu(rx->value_len));
if (totlen != je32_to_cpu(rx->totlen)) {
JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n",
ofs, je32_to_cpu(rx->totlen), totlen);
DIRTY_SPACE(je32_to_cpu(rx->totlen));
return 0;
}
raw = jffs2_alloc_raw_node_ref();
if (!raw)
return -ENOMEM;
xd = jffs2_setup_xattr_datum(c, je32_to_cpu(rx->xid), je32_to_cpu(rx->version));
if (IS_ERR(xd)) {
jffs2_free_raw_node_ref(raw);
if (PTR_ERR(xd) == -EEXIST) {
DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen)));
return 0;
}
return PTR_ERR(xd);
}
xd->xprefix = rx->xprefix;
xd->name_len = rx->name_len;
xd->value_len = je16_to_cpu(rx->value_len);
xd->data_crc = je32_to_cpu(rx->data_crc);
xd->node = raw;
raw->__totlen = totlen;
raw->flash_offset = ofs | REF_PRISTINE;
raw->next_phys = NULL;
raw->next_in_ino = (void *)xd;
if (!jeb->first_node)
jeb->first_node = raw;
if (jeb->last_node)
jeb->last_node->next_phys = raw;
jeb->last_node = raw;
USED_SPACE(PAD(je32_to_cpu(rx->totlen)));
if (jffs2_sum_active())
jffs2_sum_add_xattr_mem(s, rx, ofs - jeb->offset);
dbg_xattr("scaning xdatum at %#08x (xid=%u, version=%u)\n",
ofs, xd->xid, xd->version);
return 0;
}
static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_raw_xref *rr, uint32_t ofs,
struct jffs2_summary *s)
{
struct jffs2_xattr_ref *ref;
struct jffs2_raw_node_ref *raw;
uint32_t crc;
crc = crc32(0, rr, sizeof(*rr) - 4);
if (crc != je32_to_cpu(rr->node_crc)) {
if (je32_to_cpu(rr->node_crc) != 0xffffffff)
JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
ofs, je32_to_cpu(rr->node_crc), crc);
DIRTY_SPACE(PAD(je32_to_cpu(rr->totlen)));
return 0;
}
if (PAD(sizeof(struct jffs2_raw_xref)) != je32_to_cpu(rr->totlen)) {
JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n",
ofs, je32_to_cpu(rr->totlen),
PAD(sizeof(struct jffs2_raw_xref)));
DIRTY_SPACE(je32_to_cpu(rr->totlen));
return 0;
}
ref = jffs2_alloc_xattr_ref();
if (!ref)
return -ENOMEM;
raw = jffs2_alloc_raw_node_ref();
if (!raw) {
jffs2_free_xattr_ref(ref);
return -ENOMEM;
}
/* BEFORE jffs2_build_xattr_subsystem() called,
* ref->xid is used to store 32bit xid, xd is not used
* ref->ino is used to store 32bit inode-number, ic is not used
* Thoes variables are declared as union, thus using those
* are exclusive. In a similar way, ref->ilist is temporarily
* used to chain all xattr_ref object. It's re-chained to
* jffs2_inode_cache in jffs2_build_xattr_subsystem() correctly.
*/
ref->node = raw;
ref->ino = je32_to_cpu(rr->ino);
ref->xid = je32_to_cpu(rr->xid);
list_add_tail(&ref->ilist, &c->xattr_temp);
raw->__totlen = PAD(je32_to_cpu(rr->totlen));
raw->flash_offset = ofs | REF_PRISTINE;
raw->next_phys = NULL;
raw->next_in_ino = (void *)ref;
if (!jeb->first_node)
jeb->first_node = raw;
if (jeb->last_node)
jeb->last_node->next_phys = raw;
jeb->last_node = raw;
USED_SPACE(PAD(je32_to_cpu(rr->totlen)));
if (jffs2_sum_active())
jffs2_sum_add_xref_mem(s, rr, ofs - jeb->offset);
dbg_xattr("scan xref at %#08x (xid=%u, ino=%u)\n",
ofs, ref->xid, ref->ino);
return 0;
}
#endif
static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) {
struct jffs2_unknown_node *node;
@ -614,6 +744,43 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
ofs += PAD(je32_to_cpu(node->totlen));
break;
#ifdef CONFIG_JFFS2_FS_XATTR
case JFFS2_NODETYPE_XATTR:
if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
D1(printk(KERN_DEBUG "Fewer than %d bytes (xattr node)"
" left to end of buf. Reading 0x%x at 0x%08x\n",
je32_to_cpu(node->totlen), buf_len, ofs));
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
return err;
buf_ofs = ofs;
node = (void *)buf;
}
err = jffs2_scan_xattr_node(c, jeb, (void *)node, ofs, s);
if (err)
return err;
ofs += PAD(je32_to_cpu(node->totlen));
break;
case JFFS2_NODETYPE_XREF:
if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
D1(printk(KERN_DEBUG "Fewer than %d bytes (xref node)"
" left to end of buf. Reading 0x%x at 0x%08x\n",
je32_to_cpu(node->totlen), buf_len, ofs));
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
return err;
buf_ofs = ofs;
node = (void *)buf;
}
err = jffs2_scan_xref_node(c, jeb, (void *)node, ofs, s);
if (err)
return err;
ofs += PAD(je32_to_cpu(node->totlen));
break;
#endif /* CONFIG_JFFS2_FS_XATTR */
case JFFS2_NODETYPE_CLEANMARKER:
D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs));
if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
@ -721,6 +888,7 @@ struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uin
ic->ino = ino;
ic->nodes = (void *)ic;
init_xattr_inode_cache(ic);
jffs2_add_ino_cache(c, ic);
if (ino == 1)
ic->nlink = 1;

82
fs/jffs2/security.c Normal file
View file

@ -0,0 +1,82 @@
/*-------------------------------------------------------------------------*
* File: fs/jffs2/security.c
* Security Labels support on JFFS2 FileSystem
*
* Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com>
* Copyright (C) 2006 NEC Corporation
*
* For licensing information, see the file 'LICENCE' in the jffs2 directory.
*-------------------------------------------------------------------------*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/time.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
#include <linux/crc32.h>
#include <linux/jffs2.h>
#include <linux/xattr.h>
#include <linux/mtd/mtd.h>
#include <linux/security.h>
#include "nodelist.h"
/* ---- Initial Security Label Attachment -------------- */
int jffs2_init_security(struct inode *inode, struct inode *dir)
{
int rc;
size_t len;
void *value;
char *name;
rc = security_inode_init_security(inode, dir, &name, &value, &len);
if (rc) {
if (rc == -EOPNOTSUPP)
return 0;
return rc;
}
rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0);
kfree(name);
kfree(value);
return rc;
}
/* ---- XATTR Handler for "security.*" ----------------- */
static int jffs2_security_getxattr(struct inode *inode, const char *name,
void *buffer, size_t size)
{
if (!strcmp(name, ""))
return -EINVAL;
return do_jffs2_getxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size);
}
static int jffs2_security_setxattr(struct inode *inode, const char *name, const void *buffer,
size_t size, int flags)
{
if (!strcmp(name, ""))
return -EINVAL;
return do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size, flags);
}
static size_t jffs2_security_listxattr(struct inode *inode, char *list, size_t list_size,
const char *name, size_t name_len)
{
size_t retlen = XATTR_SECURITY_PREFIX_LEN + name_len + 1;
if (list && retlen <= list_size) {
strcpy(list, XATTR_SECURITY_PREFIX);
strcpy(list + XATTR_SECURITY_PREFIX_LEN, name);
}
return retlen;
}
struct xattr_handler jffs2_security_xattr_handler = {
.prefix = XATTR_SECURITY_PREFIX,
.list = jffs2_security_listxattr,
.set = jffs2_security_setxattr,
.get = jffs2_security_getxattr
};

View file

@ -5,6 +5,7 @@
* Zoltan Sogor <weth@inf.u-szeged.hu>,
* Patrik Kluba <pajko@halom.u-szeged.hu>,
* University of Szeged, Hungary
* 2005 KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
@ -81,6 +82,19 @@ static int jffs2_sum_add_mem(struct jffs2_summary *s, union jffs2_sum_mem *item)
dbg_summary("dirent (%u) added to summary\n",
je32_to_cpu(item->d.ino));
break;
#ifdef CONFIG_JFFS2_FS_XATTR
case JFFS2_NODETYPE_XATTR:
s->sum_size += JFFS2_SUMMARY_XATTR_SIZE;
s->sum_num++;
dbg_summary("xattr (xid=%u, version=%u) added to summary\n",
je32_to_cpu(item->x.xid), je32_to_cpu(item->x.version));
break;
case JFFS2_NODETYPE_XREF:
s->sum_size += JFFS2_SUMMARY_XREF_SIZE;
s->sum_num++;
dbg_summary("xref added to summary\n");
break;
#endif
default:
JFFS2_WARNING("UNKNOWN node type %u\n",
je16_to_cpu(item->u.nodetype));
@ -141,6 +155,40 @@ int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *r
return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
}
#ifdef CONFIG_JFFS2_FS_XATTR
int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs)
{
struct jffs2_sum_xattr_mem *temp;
temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
if (!temp)
return -ENOMEM;
temp->nodetype = rx->nodetype;
temp->xid = rx->xid;
temp->version = rx->version;
temp->offset = cpu_to_je32(ofs);
temp->totlen = rx->totlen;
temp->next = NULL;
return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
}
int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs)
{
struct jffs2_sum_xref_mem *temp;
temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
if (!temp)
return -ENOMEM;
temp->nodetype = rr->nodetype;
temp->offset = cpu_to_je32(ofs);
temp->next = NULL;
return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
}
#endif
/* Cleanup every collected summary information */
static void jffs2_sum_clean_collected(struct jffs2_summary *s)
@ -259,7 +307,40 @@ int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs,
return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
}
#ifdef CONFIG_JFFS2_FS_XATTR
case JFFS2_NODETYPE_XATTR: {
struct jffs2_sum_xattr_mem *temp;
if (je32_to_cpu(node->x.version) == 0xffffffff)
return 0;
temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
if (!temp)
goto no_mem;
temp->nodetype = node->x.nodetype;
temp->xid = node->x.xid;
temp->version = node->x.version;
temp->totlen = node->x.totlen;
temp->offset = cpu_to_je32(ofs);
temp->next = NULL;
return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
}
case JFFS2_NODETYPE_XREF: {
struct jffs2_sum_xref_mem *temp;
if (je32_to_cpu(node->r.ino) == 0xffffffff
&& je32_to_cpu(node->r.xid) == 0xffffffff)
return 0;
temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
if (!temp)
goto no_mem;
temp->nodetype = node->r.nodetype;
temp->offset = cpu_to_je32(ofs);
temp->next = NULL;
return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
}
#endif
case JFFS2_NODETYPE_PADDING:
dbg_summary("node PADDING\n");
c->summary->sum_padded += je32_to_cpu(node->u.totlen);
@ -408,8 +489,94 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
break;
}
#ifdef CONFIG_JFFS2_FS_XATTR
case JFFS2_NODETYPE_XATTR: {
struct jffs2_xattr_datum *xd;
struct jffs2_sum_xattr_flash *spx;
uint32_t ofs;
spx = (struct jffs2_sum_xattr_flash *)sp;
ofs = jeb->offset + je32_to_cpu(spx->offset);
dbg_summary("xattr at %#08x (xid=%u, version=%u)\n", ofs,
je32_to_cpu(spx->xid), je32_to_cpu(spx->version));
raw = jffs2_alloc_raw_node_ref();
if (!raw) {
JFFS2_NOTICE("allocation of node reference failed\n");
kfree(summary);
return -ENOMEM;
}
xd = jffs2_setup_xattr_datum(c, je32_to_cpu(spx->xid),
je32_to_cpu(spx->version));
if (IS_ERR(xd)) {
JFFS2_NOTICE("allocation of xattr_datum failed\n");
jffs2_free_raw_node_ref(raw);
kfree(summary);
return PTR_ERR(xd);
}
xd->node = raw;
raw->flash_offset = ofs | REF_UNCHECKED;
raw->__totlen = PAD(je32_to_cpu(spx->totlen));
raw->next_phys = NULL;
raw->next_in_ino = (void *)xd;
if (!jeb->first_node)
jeb->first_node = raw;
if (jeb->last_node)
jeb->last_node->next_phys = raw;
jeb->last_node = raw;
*pseudo_random += je32_to_cpu(spx->xid);
UNCHECKED_SPACE(je32_to_cpu(spx->totlen));
sp += JFFS2_SUMMARY_XATTR_SIZE;
break;
}
case JFFS2_NODETYPE_XREF: {
struct jffs2_xattr_ref *ref;
struct jffs2_sum_xref_flash *spr;
uint32_t ofs;
spr = (struct jffs2_sum_xref_flash *)sp;
ofs = jeb->offset + je32_to_cpu(spr->offset);
dbg_summary("xref at %#08x (xid=%u, ino=%u)\n", ofs,
je32_to_cpu(spr->xid), je32_to_cpu(spr->ino));
raw = jffs2_alloc_raw_node_ref();
if (!raw) {
JFFS2_NOTICE("allocation of node reference failed\n");
kfree(summary);
return -ENOMEM;
}
ref = jffs2_alloc_xattr_ref();
if (!ref) {
JFFS2_NOTICE("allocation of xattr_datum failed\n");
jffs2_free_raw_node_ref(raw);
kfree(summary);
return -ENOMEM;
}
ref->ino = 0xfffffffe;
ref->xid = 0xfffffffd;
ref->node = raw;
list_add_tail(&ref->ilist, &c->xattr_temp);
raw->__totlen = PAD(sizeof(struct jffs2_raw_xref));
raw->flash_offset = ofs | REF_UNCHECKED;
raw->next_phys = NULL;
raw->next_in_ino = (void *)ref;
if (!jeb->first_node)
jeb->first_node = raw;
if (jeb->last_node)
jeb->last_node->next_phys = raw;
jeb->last_node = raw;
UNCHECKED_SPACE(PAD(sizeof(struct jffs2_raw_xref)));
*pseudo_random += ofs;
sp += JFFS2_SUMMARY_XREF_SIZE;
break;
}
#endif
default : {
printk("nodetype = %#04x\n",je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype));
JFFS2_WARNING("Unsupported node type found in summary! Exiting...");
kfree(summary);
return -EIO;
@ -617,7 +784,31 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
break;
}
#ifdef CONFIG_JFFS2_FS_XATTR
case JFFS2_NODETYPE_XATTR: {
struct jffs2_sum_xattr_flash *sxattr_ptr = wpage;
temp = c->summary->sum_list_head;
sxattr_ptr->nodetype = temp->x.nodetype;
sxattr_ptr->xid = temp->x.xid;
sxattr_ptr->version = temp->x.version;
sxattr_ptr->offset = temp->x.offset;
sxattr_ptr->totlen = temp->x.totlen;
wpage += JFFS2_SUMMARY_XATTR_SIZE;
break;
}
case JFFS2_NODETYPE_XREF: {
struct jffs2_sum_xref_flash *sxref_ptr = wpage;
temp = c->summary->sum_list_head;
sxref_ptr->nodetype = temp->r.nodetype;
sxref_ptr->offset = temp->r.offset;
wpage += JFFS2_SUMMARY_XREF_SIZE;
break;
}
#endif
default : {
BUG(); /* unknown node in summary information */
}

View file

@ -45,6 +45,8 @@
#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff
#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash))
#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x))
#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash))
#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash))
/* Summary structures used on flash */
@ -75,11 +77,28 @@ struct jffs2_sum_dirent_flash
uint8_t name[0]; /* dirent name */
} __attribute__((packed));
struct jffs2_sum_xattr_flash
{
jint16_t nodetype; /* == JFFS2_NODETYPE_XATR */
jint32_t xid; /* xattr identifier */
jint32_t version; /* version number */
jint32_t offset; /* offset on jeb */
jint32_t totlen; /* node length */
} __attribute__((packed));
struct jffs2_sum_xref_flash
{
jint16_t nodetype; /* == JFFS2_NODETYPE_XREF */
jint32_t offset; /* offset on jeb */
} __attribute__((packed));
union jffs2_sum_flash
{
struct jffs2_sum_unknown_flash u;
struct jffs2_sum_inode_flash i;
struct jffs2_sum_dirent_flash d;
struct jffs2_sum_xattr_flash x;
struct jffs2_sum_xref_flash r;
};
/* Summary structures used in the memory */
@ -114,11 +133,30 @@ struct jffs2_sum_dirent_mem
uint8_t name[0]; /* dirent name */
} __attribute__((packed));
struct jffs2_sum_xattr_mem
{
union jffs2_sum_mem *next;
jint16_t nodetype;
jint32_t xid;
jint32_t version;
jint32_t offset;
jint32_t totlen;
} __attribute__((packed));
struct jffs2_sum_xref_mem
{
union jffs2_sum_mem *next;
jint16_t nodetype;
jint32_t offset;
} __attribute__((packed));
union jffs2_sum_mem
{
struct jffs2_sum_unknown_mem u;
struct jffs2_sum_inode_mem i;
struct jffs2_sum_dirent_mem d;
struct jffs2_sum_xattr_mem x;
struct jffs2_sum_xref_mem r;
};
/* Summary related information stored in superblock */
@ -159,6 +197,8 @@ int jffs2_sum_write_sumnode(struct jffs2_sb_info *c);
int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size);
int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs);
int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs);
int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs);
int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs);
int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
uint32_t ofs, uint32_t *pseudo_random);
@ -176,6 +216,8 @@ int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb
#define jffs2_sum_add_padding_mem(a,b)
#define jffs2_sum_add_inode_mem(a,b,c)
#define jffs2_sum_add_dirent_mem(a,b,c)
#define jffs2_sum_add_xattr_mem(a,b,c)
#define jffs2_sum_add_xref_mem(a,b,c)
#define jffs2_sum_scan_sumnode(a,b,c,d) (0)
#endif /* CONFIG_JFFS2_SUMMARY */

View file

@ -151,7 +151,10 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
sb->s_op = &jffs2_super_operations;
sb->s_flags = flags | MS_NOATIME;
sb->s_xattr = jffs2_xattr_handlers;
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
sb->s_flags |= MS_POSIXACL;
#endif
ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
if (ret) {
@ -293,6 +296,7 @@ static void jffs2_put_super (struct super_block *sb)
kfree(c->blocks);
jffs2_flash_cleanup(c);
kfree(c->inocache_list);
jffs2_clear_xattr_subsystem(c);
if (c->mtd->sync)
c->mtd->sync(c->mtd);

View file

@ -24,7 +24,12 @@ struct inode_operations jffs2_symlink_inode_operations =
{
.readlink = generic_readlink,
.follow_link = jffs2_follow_link,
.setattr = jffs2_setattr
.permission = jffs2_permission,
.setattr = jffs2_setattr,
.setxattr = jffs2_setxattr,
.getxattr = jffs2_getxattr,
.listxattr = jffs2_listxattr,
.removexattr = jffs2_removexattr
};
static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)

View file

@ -36,7 +36,7 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint
f->inocache->nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
f->inocache->state = INO_STATE_PRESENT;
init_xattr_inode_cache(f->inocache);
jffs2_add_ino_cache(c, f->inocache);
D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));

1271
fs/jffs2/xattr.c Normal file

File diff suppressed because it is too large Load diff

120
fs/jffs2/xattr.h Normal file
View file

@ -0,0 +1,120 @@
/*-------------------------------------------------------------------------*
* File: fs/jffs2/xattr.c
* XATTR support on JFFS2 FileSystem
*
* Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com>
* Copyright (C) 2006 NEC Corporation
*
* For licensing information, see the file 'LICENCE' in the jffs2 directory.
*-------------------------------------------------------------------------*/
#ifndef _JFFS2_FS_XATTR_H_
#define _JFFS2_FS_XATTR_H_
#include <linux/xattr.h>
#define JFFS2_XFLAGS_HOT (0x01) /* This datum is HOT */
#define JFFS2_XFLAGS_BIND (0x02) /* This datum is not reclaimed */
struct jffs2_xattr_datum
{
void *always_null;
u8 class;
u8 flags;
u16 xprefix; /* see JFFS2_XATTR_PREFIX_* */
struct jffs2_raw_node_ref *node;
struct list_head xindex; /* chained from c->xattrindex[n] */
uint32_t refcnt; /* # of xattr_ref refers this */
uint32_t xid;
uint32_t version;
uint32_t data_crc;
uint32_t hashkey;
char *xname; /* XATTR name without prefix */
uint32_t name_len; /* length of xname */
char *xvalue; /* XATTR value */
uint32_t value_len; /* length of xvalue */
};
struct jffs2_inode_cache; /* forward refence */
struct jffs2_xattr_ref
{
void *always_null;
u8 class;
u8 flags; /* Currently unused */
u16 unused;
struct jffs2_raw_node_ref *node;
union {
struct jffs2_inode_cache *ic; /* reference to jffs2_inode_cache */
uint32_t ino; /* only used in scanning/building */
};
union {
struct jffs2_xattr_datum *xd; /* reference to jffs2_xattr_datum */
uint32_t xid; /* only used in sccanning/building */
};
struct list_head ilist; /* chained from ic->ilist */
};
#ifdef CONFIG_JFFS2_FS_XATTR
extern void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c);
extern void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c);
extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c);
extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
uint32_t xid, uint32_t version);
extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
extern int jffs2_garbage_collect_xattr(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
extern int jffs2_verify_xattr(struct jffs2_sb_info *c);
extern int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname,
char *buffer, size_t size);
extern int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
const char *buffer, size_t size, int flags);
extern struct xattr_handler *jffs2_xattr_handlers[];
extern struct xattr_handler jffs2_user_xattr_handler;
extern struct xattr_handler jffs2_trusted_xattr_handler;
extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t);
#define jffs2_getxattr generic_getxattr
#define jffs2_setxattr generic_setxattr
#define jffs2_removexattr generic_removexattr
/*---- Any inline initialize functions ----*/
#define init_xattr_inode_cache(x) INIT_LIST_HEAD(&((x)->ilist))
#else
#define jffs2_init_xattr_subsystem(c)
#define jffs2_build_xattr_subsystem(c)
#define jffs2_clear_xattr_subsystem(c)
#define jffs2_xattr_delete_inode(c, ic)
#define jffs2_xattr_free_inode(c, ic)
#define jffs2_garbage_collect_xattr(c, ic) (1)
#define jffs2_verify_xattr(c) (1)
#define jffs2_xattr_handlers NULL
#define jffs2_listxattr NULL
#define jffs2_getxattr NULL
#define jffs2_setxattr NULL
#define jffs2_removexattr NULL
#define init_xattr_inode_cache(x)
#endif /* CONFIG_JFFS2_FS_XATTR */
#ifdef CONFIG_JFFS2_FS_SECURITY
extern int jffs2_init_security(struct inode *inode, struct inode *dir);
extern struct xattr_handler jffs2_security_xattr_handler;
#else
#define jffs2_init_security(inode,dir) (0)
#endif /* CONFIG_JFFS2_FS_SECURITY */
#endif /* _JFFS2_FS_XATTR_H_ */

51
fs/jffs2/xattr_trusted.c Normal file
View file

@ -0,0 +1,51 @@
/*-------------------------------------------------------------------------*
* File: fs/jffs2/xattr_trusted.c
* XATTR support on JFFS2 FileSystem
*
* Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com>
* Copyright (C) 2006 NEC Corporation
*
* For licensing information, see the file 'LICENCE' in the jffs2 directory.
*-------------------------------------------------------------------------*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/jffs2.h>
#include <linux/xattr.h>
#include <linux/mtd/mtd.h>
#include "nodelist.h"
static int jffs2_trusted_getxattr(struct inode *inode, const char *name,
void *buffer, size_t size)
{
if (!strcmp(name, ""))
return -EINVAL;
return do_jffs2_getxattr(inode, JFFS2_XPREFIX_TRUSTED, name, buffer, size);
}
static int jffs2_trusted_setxattr(struct inode *inode, const char *name, const void *buffer,
size_t size, int flags)
{
if (!strcmp(name, ""))
return -EINVAL;
return do_jffs2_setxattr(inode, JFFS2_XPREFIX_TRUSTED, name, buffer, size, flags);
}
static size_t jffs2_trusted_listxattr(struct inode *inode, char *list, size_t list_size,
const char *name, size_t name_len)
{
size_t retlen = XATTR_TRUSTED_PREFIX_LEN + name_len + 1;
if (list && retlen<=list_size) {
strcpy(list, XATTR_TRUSTED_PREFIX);
strcpy(list + XATTR_TRUSTED_PREFIX_LEN, name);
}
return retlen;
}
struct xattr_handler jffs2_trusted_xattr_handler = {
.prefix = XATTR_TRUSTED_PREFIX,
.list = jffs2_trusted_listxattr,
.set = jffs2_trusted_setxattr,
.get = jffs2_trusted_getxattr
};

51
fs/jffs2/xattr_user.c Normal file
View file

@ -0,0 +1,51 @@
/*-------------------------------------------------------------------------*
* File: fs/jffs2/xattr_user.c
* XATTR support on JFFS2 FileSystem
*
* Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com>
* Copyright (C) 2006 NEC Corporation
*
* For licensing information, see the file 'LICENCE' in the jffs2 directory.
*-------------------------------------------------------------------------*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/jffs2.h>
#include <linux/xattr.h>
#include <linux/mtd/mtd.h>
#include "nodelist.h"
static int jffs2_user_getxattr(struct inode *inode, const char *name,
void *buffer, size_t size)
{
if (!strcmp(name, ""))
return -EINVAL;
return do_jffs2_getxattr(inode, JFFS2_XPREFIX_USER, name, buffer, size);
}
static int jffs2_user_setxattr(struct inode *inode, const char *name, const void *buffer,
size_t size, int flags)
{
if (!strcmp(name, ""))
return -EINVAL;
return do_jffs2_setxattr(inode, JFFS2_XPREFIX_USER, name, buffer, size, flags);
}
static size_t jffs2_user_listxattr(struct inode *inode, char *list, size_t list_size,
const char *name, size_t name_len)
{
size_t retlen = XATTR_USER_PREFIX_LEN + name_len + 1;
if (list && retlen <= list_size) {
strcpy(list, XATTR_USER_PREFIX);
strcpy(list + XATTR_USER_PREFIX_LEN, name);
}
return retlen;
}
struct xattr_handler jffs2_user_xattr_handler = {
.prefix = XATTR_USER_PREFIX,
.list = jffs2_user_listxattr,
.set = jffs2_user_setxattr,
.get = jffs2_user_getxattr
};

View file

@ -65,6 +65,18 @@
#define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6)
#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8)
#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9)
/* XATTR Related */
#define JFFS2_XPREFIX_USER 1 /* for "user." */
#define JFFS2_XPREFIX_SECURITY 2 /* for "security." */
#define JFFS2_XPREFIX_ACL_ACCESS 3 /* for "system.posix_acl_access" */
#define JFFS2_XPREFIX_ACL_DEFAULT 4 /* for "system.posix_acl_default" */
#define JFFS2_XPREFIX_TRUSTED 5 /* for "trusted.*" */
#define JFFS2_ACL_VERSION 0x0001
// Maybe later...
//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
@ -151,6 +163,32 @@ struct jffs2_raw_inode
uint8_t data[0];
} __attribute__((packed));
struct jffs2_raw_xattr {
jint16_t magic;
jint16_t nodetype; /* = JFFS2_NODETYPE_XATTR */
jint32_t totlen;
jint32_t hdr_crc;
jint32_t xid; /* XATTR identifier number */
jint32_t version;
uint8_t xprefix;
uint8_t name_len;
jint16_t value_len;
jint32_t data_crc;
jint32_t node_crc;
uint8_t data[0];
} __attribute__((packed));
struct jffs2_raw_xref
{
jint16_t magic;
jint16_t nodetype; /* = JFFS2_NODETYPE_XREF */
jint32_t totlen;
jint32_t hdr_crc;
jint32_t ino; /* inode number */
jint32_t xid; /* XATTR identifier number */
jint32_t node_crc;
} __attribute__((packed));
struct jffs2_raw_summary
{
jint16_t magic;
@ -169,6 +207,8 @@ union jffs2_node_union
{
struct jffs2_raw_inode i;
struct jffs2_raw_dirent d;
struct jffs2_raw_xattr x;
struct jffs2_raw_xref r;
struct jffs2_raw_summary s;
struct jffs2_unknown_node u;
};